Add system alerts log with nav badge and Admin Alerts tab.
Deploy / lint (push) Successful in 8s
Deploy / test (push) Failing after 1m4s
Deploy / deploy (push) Has been skipped

Persist job and ingestion warnings/errors for 7 days, surface a dismissible top-nav badge, treat stale OHLCV as a warning (e.g. ticker renames), and show market bar age on the ticker freshness chip.
This commit is contained in:
2026-07-14 13:38:04 +02:00
parent ed82d0a665
commit f59c0c3484
16 changed files with 825 additions and 7 deletions
+49 -1
View File
@@ -6,7 +6,7 @@ All endpoints require admin role.
from fastapi import APIRouter, Depends, Query
from sqlalchemy.ext.asyncio import AsyncSession
from app.dependencies import get_db, require_admin
from app.dependencies import get_db, require_access, require_admin
from app.models.user import User
from app.schemas.admin import (
ActivationConfigUpdate,
@@ -29,6 +29,7 @@ from app.schemas.common import APIEnvelope
from app.services import admin_service
from app.services import alert_service
from app.services import sentiment_provider_service
from app.services import system_event_service
from app.services import ticker_universe_service
router = APIRouter(tags=["admin"])
@@ -403,3 +404,50 @@ async def toggle_job(
status="success",
data={"key": setting.key, "value": setting.value},
)
# ---------------------------------------------------------------------------
# System events (operational warnings / errors)
# ---------------------------------------------------------------------------
@router.get("/admin/system-events", response_model=APIEnvelope)
async def list_system_events(
days: int = Query(7, ge=1, le=30),
severity: str | None = Query(None, description="warning | error"),
unacknowledged_only: bool = Query(False),
_user: User = Depends(require_access),
db: AsyncSession = Depends(get_db),
):
"""List recent system events (default last 7 days)."""
rows = await system_event_service.list_events(
db,
days=days,
severity=severity,
unacknowledged_only=unacknowledged_only,
)
return APIEnvelope(
status="success",
data=[system_event_service.event_to_dict(r) for r in rows],
)
@router.get("/admin/system-events/summary", response_model=APIEnvelope)
async def system_events_summary(
days: int = Query(7, ge=1, le=30),
_user: User = Depends(require_access),
db: AsyncSession = Depends(get_db),
):
"""Badge counts for the top nav."""
data = await system_event_service.summary(db, days=days)
return APIEnvelope(status="success", data=data)
@router.post("/admin/system-events/acknowledge", response_model=APIEnvelope)
async def acknowledge_system_events(
days: int = Query(7, ge=1, le=30),
_user: User = Depends(require_access),
db: AsyncSession = Depends(get_db),
):
"""Dismiss unacknowledged events in the lookback window (clears the badge)."""
count = await system_event_service.acknowledge_all(db, days=days)
return APIEnvelope(status="success", data={"acknowledged": count})