2.5 KiB
description, paths
| description | paths | |
|---|---|---|
| dashboard /api routes, tool-runner, metrics middleware, health routes, HAProxy edge, mount order |
|
FastAPI dashboard, routing & edge
-
Mount order matters and is load-bearing.
dashboard.mount("/mcp", _mcp_app)goes before the SPAStaticFilesmount at/(which is greedy withhtml=TrueSPA fallback). MCP must be mounted first or/swallows/mcp. The MCP ASGI app is built early (mcp.http_app(path="/")) so its lifespan can be nested inside the FastAPI lifespan — keep that wiring. -
/api/*routes are thin façades overdb,sportsdb, andmcp./api/status(health cards),/api/tools(derived frommcp.list_tools()— the single source the dashboard reads),/api/logs(the in-memory request log),/api/cache/invalidate(flush both caches),/api/run(tool-runner:mcp.get_tool→tool.fn(**args)),/api/v1/telemetry(browser error sink). Keep/api/toolsderived, never hand-maintained. -
Prometheus middleware skips health/MCP paths.
_SKIP_METRICS_PREFIXES = ("/live","/ready","/metrics","/mcp")— probes and MCP transport must not inflatenike_http_requests_total. It also normalises UUIDs in paths to{id}to bound label cardinality. Keep both behaviours; add new health-ish prefixes to the skip list. -
/metricsis Prometheus exposition, IP-gated in Python._METRICS_ALLOWED_NETS= the standard four ranges (10.10.0.0/24,172.16.0.0/12,127.0.0.0/8,::1/128), no auth — HAProxy/Prometheus can't authenticate (red_panda_standards.md). Don't widen it or add app-side auth. -
Health routes register both slash forms (
/live+/live/,/ready+/ready/)./readyreturns 503 whendb.check_connection()reports down./liveis a bare{"status": "ok"}. -
HAProxy is the edge.
uvicorn.run(..., proxy_headers=True, forwarded_allow_ips=config.TRUSTED_PROXY_IPS, ws="wsproto"). The write routes (/api/run,/api/cache/invalidate) are not individually authenticated — access control is at HAProxy; the code comment says so. If you add a write route, keep that assumption explicit; if Nike could be exposed directly, it needs auth./api/v1/telemetrystays unprotected on purpose (sendBeaconcan't set headers). -
Structured logging is re-applied in the lifespan (
configure_loggingaftercreate_pool) because uvicorn overwrites handlers onconfig.load(). Don't drop the second call, or JSON logging silently reverts to uvicorn's default.