Files
security-alert-center/backend/app/api/v1/dashboards.py
T
PapaTramp 42f31b3bb5 fix: dashboard and hosts count SSH and RDP daily reports
analytics-a4: daily_reports_24h uses report.daily.*; sac-deploy enables timers
Co-authored-by: Cursor <cursoragent@cursor.com>
2026-05-29 16:31:09 +10:00

164 lines
5.0 KiB
Python

from datetime import datetime, timedelta, timezone
from fastapi import APIRouter, Depends, Query
from pydantic import BaseModel
from sqlalchemy import func, select
from sqlalchemy.orm import Session, joinedload
from app.auth.jwt_auth import get_current_user
from app.config import get_settings
from app.database import get_db
from app.models import Event, Host, Problem
from app.schemas.list_models import EventSummary
from app.services.host_health import DAILY_REPORT_TYPES, HEARTBEAT_TYPE, count_stale_hosts
router = APIRouter(prefix="/dashboards", tags=["dashboards"])
def fetch_recent_events(db: Session, *, limit: int = 8) -> list[EventSummary]:
"""Последние события по времени приёма ingest (received_at)."""
rows = db.scalars(
select(Event)
.join(Host)
.options(joinedload(Event.host))
.order_by(Event.received_at.desc())
.limit(limit)
).all()
return [
EventSummary(
id=e.id,
event_id=e.event_id,
host_id=e.host_id,
hostname=e.host.hostname,
occurred_at=e.occurred_at,
received_at=e.received_at,
category=e.category,
type=e.type,
severity=e.severity,
title=e.title,
summary=e.summary,
)
for e in rows
]
class TopHostItem(BaseModel):
host_id: int
hostname: str
count: int
class TopTypeItem(BaseModel):
type: str
count: int
class DashboardSummary(BaseModel):
events_last_24h: int
hosts_total: int
hosts_stale: int
heartbeats_24h: int
daily_reports_24h: int
problems_open: int
problems_opened_24h: int
problems_resolved_24h: int
severity_24h: dict[str, int]
top_hosts: list[TopHostItem]
top_event_types: list[TopTypeItem]
recent_events: list[EventSummary]
@router.get("/summary", response_model=DashboardSummary)
def dashboard_summary(
db: Session = Depends(get_db),
_user: str = Depends(get_current_user),
) -> DashboardSummary:
since = datetime.now(timezone.utc) - timedelta(hours=24)
events_24h = db.scalar(
select(func.count()).select_from(Event).where(Event.received_at >= since)
) or 0
hosts_total = db.scalar(select(func.count()).select_from(Host)) or 0
settings = get_settings()
hosts_stale = count_stale_hosts(db, settings.sac_heartbeat_stale_minutes)
heartbeats_24h = db.scalar(
select(func.count())
.select_from(Event)
.where(Event.received_at >= since, Event.type == HEARTBEAT_TYPE)
) or 0
daily_reports_24h = db.scalar(
select(func.count())
.select_from(Event)
.where(Event.received_at >= since, Event.type.in_(DAILY_REPORT_TYPES))
) or 0
problems_open = (
db.scalar(select(func.count()).select_from(Problem).where(Problem.status == "open")) or 0
)
problems_opened_24h = (
db.scalar(select(func.count()).select_from(Problem).where(Problem.created_at >= since)) or 0
)
problems_resolved_24h = (
db.scalar(
select(func.count()).select_from(Problem).where(
Problem.status == "resolved",
Problem.updated_at >= since,
)
)
or 0
)
top_host_rows = db.execute(
select(Host.id, Host.hostname, func.count())
.select_from(Event)
.join(Host, Event.host_id == Host.id)
.where(Event.received_at >= since)
.group_by(Host.id, Host.hostname)
.order_by(func.count().desc())
.limit(10)
).all()
top_hosts = [
TopHostItem(host_id=row[0], hostname=row[1], count=row[2]) for row in top_host_rows
]
top_type_rows = db.execute(
select(Event.type, func.count())
.where(Event.received_at >= since)
.group_by(Event.type)
.order_by(func.count().desc())
.limit(10)
).all()
top_event_types = [TopTypeItem(type=row[0], count=row[1]) for row in top_type_rows]
severity_rows = db.execute(
select(Event.severity, func.count())
.where(Event.received_at >= since)
.group_by(Event.severity)
).all()
severity_24h = {row[0]: row[1] for row in severity_rows}
recent_events = fetch_recent_events(db, limit=8)
return DashboardSummary(
events_last_24h=events_24h,
hosts_total=hosts_total,
hosts_stale=hosts_stale,
heartbeats_24h=heartbeats_24h,
daily_reports_24h=daily_reports_24h,
problems_open=problems_open,
problems_opened_24h=problems_opened_24h,
problems_resolved_24h=problems_resolved_24h,
severity_24h=severity_24h,
top_hosts=top_hosts,
top_event_types=top_event_types,
recent_events=recent_events,
)
@router.get("/recent-events", response_model=list[EventSummary])
def dashboard_recent_events(
limit: int = Query(8, ge=1, le=50),
db: Session = Depends(get_db),
_user: str = Depends(get_current_user),
) -> list[EventSummary]:
return fetch_recent_events(db, limit=limit)