feat: Dashboard with summary cards, health distribution, gateway trend, global search

Co-Authored-By: Claude <noreply@anthropic.com>
This commit is contained in:
enzo
2026-07-22 17:01:18 +08:00
co-authored by Claude
parent 10404be62d
commit 76045538bb
8 changed files with 621 additions and 6 deletions
+117
View File
@@ -0,0 +1,117 @@
"""Dashboard API: aggregated stats, health distribution, gateway trend,
kanban overview, cron recent, and global search — all mock data for now."""
from datetime import datetime, timezone
from fastapi import APIRouter, Depends, Query
from app.middleware.auth import require_auth
from app.models.user import User
router = APIRouter(tags=["dashboard"])
@router.get("/dashboard/summary")
async def dashboard_summary(_user: User = Depends(require_auth)):
# TODO: replace with real data — hermes gateway status + kanban + cron
return {
"online_count": 12,
"offline_count": 46,
"total_profiles": 58,
"running_tasks": 3,
"cron_failures_today": 1,
"updated_at": datetime.now(timezone.utc).isoformat(timespec="seconds"),
}
@router.get("/dashboard/health-distribution")
async def health_distribution(_user: User = Depends(require_auth)):
# TODO: replace with real health-score calculation (Phase 4)
return {
"A": 22,
"B": 18,
"C": 10,
"D": 8,
"total": 58,
"d_profiles": ["ops-engineer", "monitor", "visualizer", "hebo", "..."],
}
@router.get("/dashboard/gateway-trend")
async def gateway_trend(_user: User = Depends(require_auth)):
# TODO: replace with real 24h aggregation from event store
return [
{"hour": "2026-07-22T08:00", "restarts": 2, "feishu_disconnects": 1, "heartbeat_timeouts": 0},
{"hour": "2026-07-22T09:00", "restarts": 0, "feishu_disconnects": 0, "heartbeat_timeouts": 1},
{"hour": "2026-07-22T10:00", "restarts": 1, "feishu_disconnects": 0, "heartbeat_timeouts": 0},
{"hour": "2026-07-22T11:00", "restarts": 0, "feishu_disconnects": 2, "heartbeat_timeouts": 0},
{"hour": "2026-07-22T12:00", "restarts": 0, "feishu_disconnects": 0, "heartbeat_timeouts": 0},
{"hour": "2026-07-22T13:00", "restarts": 3, "feishu_disconnects": 1, "heartbeat_timeouts": 2},
{"hour": "2026-07-22T14:00", "restarts": 0, "feishu_disconnects": 0, "heartbeat_timeouts": 1},
{"hour": "2026-07-22T15:00", "restarts": 1, "feishu_disconnects": 0, "heartbeat_timeouts": 0},
]
@router.get("/dashboard/kanban-summary")
async def kanban_summary(_user: User = Depends(require_auth)):
# TODO: replace with real kanban task data
return [
{"board": "europe-briefing", "running": "S4", "ready": "S5"},
{"board": "tech-weekly", "running": "S1", "ready": None},
{"board": "ops-review", "running": "M2", "ready": "M3"},
]
@router.get("/dashboard/cron-recent")
async def cron_recent(_user: User = Depends(require_auth)):
# TODO: replace with real cron execution history
return [
{"job": "taiwan-briefing", "status": "success", "time": "07:15"},
{"job": "hindsight", "status": "failure", "time": "21:13"},
{"job": "guangxi-donation", "status": "paused"},
{"job": "daily-report", "status": "success", "time": "08:00"},
{"job": "weekly-digest", "status": "success", "time": "09:30"},
]
@router.get("/search")
async def global_search(
q: str = Query("", min_length=1),
_user: User = Depends(require_auth),
):
# TODO: replace with real full-text search across profiles, tasks, cron, events
keyword = q.lower()
profiles = []
if "gateway" in keyword or "monitor" in keyword:
profiles.append({"name": "gateway-monitor", "status": "online"})
if "visual" in keyword:
profiles.append({"name": "visualizer", "status": "online"})
if "hebo" in keyword or "ops" in keyword:
profiles.append({"name": "ops-engineer", "status": "online"})
kanban_tasks = []
if "briefing" in keyword or "europe" in keyword:
kanban_tasks.append({"title": "Europe Briefing S4", "column": "RUNNING"})
if "tech" in keyword or "weekly" in keyword:
kanban_tasks.append({"title": "Tech Weekly S1", "column": "RUNNING"})
cron_jobs = []
if "taiwan" in keyword or "briefing" in keyword:
cron_jobs.append({"job": "taiwan-briefing", "status": "active"})
if "hindsight" in keyword:
cron_jobs.append({"job": "hindsight", "status": "active"})
if "donation" in keyword or "guangxi" in keyword:
cron_jobs.append({"job": "guangxi-donation", "status": "paused"})
events = []
if "gateway" in keyword or keyword == "restart":
events.append({"title": "Gateway restart detected", "status": "active"})
if "feishu" in keyword or "disconnect" in keyword:
events.append({"title": "Feishu disconnect alert", "status": "active"})
return {
"profiles": profiles,
"kanban_tasks": kanban_tasks,
"cron_jobs": cron_jobs,
"events": events,
}
+2 -1
View File
@@ -4,7 +4,7 @@ from fastapi import FastAPI
from fastapi.middleware.cors import CORSMiddleware
from app.database import engine, Base
from app.api import auth, users, audit
from app.api import auth, users, audit, dashboard
from app.middleware.audit import AuditMiddleware
@@ -34,6 +34,7 @@ app.add_middleware(AuditMiddleware)
app.include_router(auth.router)
app.include_router(users.router)
app.include_router(audit.router)
app.include_router(dashboard.router, prefix="/api/v1")
@app.get("/api/v1/health")