Files
nike/.claude/rules/fastapi-dashboard.md
Robert Helewka 9f1d85f04b
All checks were successful
CVE Scan & Docker Build / security-scan (push) Successful in 33s
CVE Scan & Docker Build / build-and-push (push) Successful in 1m36s
docs: add Claude rules and workspace configuration
2026-07-14 13:34:05 -04:00

2.5 KiB

description, paths
description paths
dashboard /api routes, tool-runner, metrics middleware, health routes, HAProxy edge, mount order
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_tooltool.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.