47 lines
2.5 KiB
Markdown
47 lines
2.5 KiB
Markdown
---
|
|
description: dashboard /api routes, tool-runner, metrics middleware, health routes, HAProxy edge, mount order
|
|
paths:
|
|
- "nike/server.py"
|
|
---
|
|
|
|
# FastAPI dashboard, routing & edge
|
|
|
|
- **Mount order matters and is load-bearing.** `dashboard.mount("/mcp", _mcp_app)`
|
|
goes **before** the SPA `StaticFiles` mount at `/` (which is greedy with
|
|
`html=True` SPA 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** over `db`, `sportsdb`, and `mcp`. `/api/status`
|
|
(health cards), `/api/tools` (derived from `mcp.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/tools` derived, never
|
|
hand-maintained.
|
|
|
|
- **Prometheus middleware skips health/MCP paths.** `_SKIP_METRICS_PREFIXES =
|
|
("/live","/ready","/metrics","/mcp")` — probes and MCP transport must not inflate
|
|
`nike_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.
|
|
|
|
- **`/metrics` is 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/`). `/ready` returns 503 when `db.check_connection()` reports down. `/live`
|
|
is 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/telemetry` stays unprotected on purpose (`sendBeacon` can't set headers).
|
|
|
|
- **Structured logging is re-applied in the lifespan** (`configure_logging` after
|
|
`create_pool`) because uvicorn overwrites handlers on `config.load()`. Don't drop
|
|
the second call, or JSON logging silently reverts to uvicorn's default.
|