chore(home): mirror from kalinamall (9883e6a) with papatramp URLs

This commit is contained in:
2026-07-14 20:43:52 +10:00
commit ed4e78f6c3
312 changed files with 42790 additions and 0 deletions
+167
View File
@@ -0,0 +1,167 @@
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.event_summary import event_to_summary
from app.services.event_type_visibility import get_hidden_event_types, visibility_type_filter
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)."""
hidden = get_hidden_event_types(db)
type_filter = visibility_type_filter(hidden)
stmt = (
select(Event)
.join(Host)
.options(joinedload(Event.host))
.order_by(Event.received_at.desc())
.limit(limit)
)
if type_filter is not None:
stmt = stmt.where(type_filter)
rows = db.scalars(stmt).all()
return [event_to_summary(e, db) for e in rows]
class TopHostItem(BaseModel):
host_id: int
hostname: str
display_name: str | None = None
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)
hidden = get_hidden_event_types(db)
type_filter = visibility_type_filter(hidden)
events_24h_stmt = select(func.count()).select_from(Event).where(Event.received_at >= since)
if type_filter is not None:
events_24h_stmt = events_24h_stmt.where(type_filter)
events_24h = db.scalar(events_24h_stmt) 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,
Problem.status != "resolved",
)
)
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_stmt = (
select(Host.id, Host.hostname, Host.display_name, func.count())
.select_from(Event)
.join(Host, Event.host_id == Host.id)
.where(Event.received_at >= since)
)
if type_filter is not None:
top_host_stmt = top_host_stmt.where(type_filter)
top_host_rows = db.execute(
top_host_stmt.group_by(Host.id, Host.hostname, Host.display_name)
.order_by(func.count().desc())
.limit(10)
).all()
top_hosts = [
TopHostItem(host_id=row[0], hostname=row[1], display_name=row[2], count=row[3]) for row in top_host_rows
]
top_type_stmt = select(Event.type, func.count()).where(Event.received_at >= since)
if type_filter is not None:
top_type_stmt = top_type_stmt.where(type_filter)
top_type_rows = db.execute(
top_type_stmt.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_stmt = select(Event.severity, func.count()).where(Event.received_at >= since)
if type_filter is not None:
severity_stmt = severity_stmt.where(type_filter)
severity_rows = db.execute(severity_stmt.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)