feat(sp36): wire api_routers/__init__.py as the registration point

Moves the per-router auth gate (Depends(matrix_gate)) from the
include_router() call sites in api.py onto each router's own
APIRouter(dependencies=...) declaration. Each router now owns
its own auth dependency.

api.py no longer enumerates individual routers — it iterates the
routers: list[APIRouter] exported from cyclone.api_routers. This
is the registry that 13 future router extractions will append to.

- new: backend/src/cyclone/api_routers/__init__.py (registry)
- new: backend/src/cyclone/api_routers/_shared.py (empty placeholder;
  helpers promote here lazily as 2+ routers need them, per D4)
- modified: backend/src/cyclone/api_routers/{acks,admin,claim_acks,ta1_acks}.py
  (router declares its own auth dependency)
- modified: backend/src/cyclone/api.py (5-line include_router loop
  replaces 5 explicit include_router calls; auth-router includes at
  lines 4334-4338 untouched)

Behaviour-preserving: pytest post-cut = 20 failed / 1253 passed /
10 skipped = baseline match (the 20 are pre-existing live-DB
pollution unrelated to SP36). Health stays public (no matrix_gate).
This commit is contained in:
Nora
2026-07-06 13:32:10 -06:00
parent a52a85c7a2
commit a963d3ce25
7 changed files with 47 additions and 15 deletions
+3 -6
View File
@@ -301,13 +301,10 @@ app.add_middleware(SecurityHeadersMiddleware)
# and are wired in here. (Kept as a top-level package rather than nested
# under `cyclone.api` so the existing ``cyclone.api`` module path keeps
# working — Python prefers packages over same-named modules.)
from cyclone.api_routers import acks, admin, claim_acks, health, ta1_acks # noqa: E402
from cyclone.api_routers import routers # noqa: E402
app.include_router(health.router)
app.include_router(acks.router, dependencies=[Depends(matrix_gate)])
app.include_router(ta1_acks.router, dependencies=[Depends(matrix_gate)])
app.include_router(claim_acks.router, dependencies=[Depends(matrix_gate)])
app.include_router(admin.router, dependencies=[Depends(matrix_gate)])
for _router in routers:
app.include_router(_router)
@app.exception_handler(HTTPException)
+23 -1
View File
@@ -1 +1,23 @@
"""Resource-group routers. Imported and registered by ``cyclone.api``."""
"""Per-resource FastAPI routers.
`api.py` does `for r in routers: app.include_router(r)`. New
routers register themselves here in alphabetical order. Helpers
shared by 2+ routers live in `_shared.py` (private to the package).
Every router except ``health`` declares its own
``APIRouter(dependencies=[Depends(matrix_gate)])`` — keep that
invariant here so adding a new router doesn't silently miss the gate.
"""
from fastapi import APIRouter
from cyclone.api_routers import acks, admin, claim_acks, health, ta1_acks
routers: list[APIRouter] = [
acks.router, # gated
admin.router, # gated
claim_acks.router, # gated
health.router, # public — health probes must work pre-auth
ta1_acks.router, # gated
]
__all__ = ["routers"]
@@ -0,0 +1,9 @@
"""Cross-router helpers for the api_routers package.
Private to the package (leading underscore). Only routers in this
package import from here. Helpers graduate here when at least two
routers need them — single-router helpers stay in the router that
uses them.
Initially empty; helpers promote here as the SP36 refactor progresses.
"""
+3 -2
View File
@@ -18,7 +18,7 @@ from __future__ import annotations
import logging
from typing import Any, AsyncIterator
from fastapi import APIRouter, HTTPException, Query, Request
from fastapi import APIRouter, Depends, HTTPException, Query, Request
from fastapi.responses import StreamingResponse
from cyclone import db
@@ -28,12 +28,13 @@ from cyclone.api_helpers import (
tail_events,
wants_ndjson,
)
from cyclone.auth.deps import matrix_gate
from cyclone.parsers.models_999 import ParseResult999
from cyclone.parsers.serialize_999 import serialize_999
from cyclone.pubsub import EventBus
from cyclone.store import store, to_ui_ack
router = APIRouter()
router = APIRouter(dependencies=[Depends(matrix_gate)])
log = logging.getLogger(__name__)
+3 -2
View File
@@ -12,12 +12,13 @@ format" from "bad checksum".
"""
from __future__ import annotations
from fastapi import APIRouter, Query
from fastapi import APIRouter, Depends, Query
from cyclone.auth.deps import matrix_gate
from cyclone.npi import is_valid_npi, is_valid_tax_id, normalize_tax_id
router = APIRouter()
router = APIRouter(dependencies=[Depends(matrix_gate)])
@router.get("/api/admin/validate-provider")
@@ -21,16 +21,17 @@ from __future__ import annotations
import logging
from typing import Any, AsyncIterator, Literal
from fastapi import APIRouter, HTTPException, Query, Request
from fastapi import APIRouter, Depends, HTTPException, Query, Request
from fastapi.responses import JSONResponse, StreamingResponse
from pydantic import BaseModel, Field
from cyclone import db
from cyclone.api_helpers import ndjson_line, tail_events
from cyclone.auth.deps import matrix_gate
from cyclone.pubsub import EventBus
from cyclone.store import store, to_ui_claim_ack
router = APIRouter()
router = APIRouter(dependencies=[Depends(matrix_gate)])
log = logging.getLogger(__name__)
+3 -2
View File
@@ -16,15 +16,16 @@ from __future__ import annotations
from typing import Any, AsyncIterator
from fastapi import APIRouter, HTTPException, Query, Request
from fastapi import APIRouter, Depends, HTTPException, Query, Request
from fastapi.responses import StreamingResponse
from cyclone.api_helpers import ndjson_line, tail_events
from cyclone.auth.deps import matrix_gate
from cyclone import db
from cyclone.pubsub import EventBus
from cyclone.store import store, to_ui_ta1_ack
router = APIRouter()
router = APIRouter(dependencies=[Depends(matrix_gate)])
# SP25: ``_ta1_to_ui`` moved to ``cyclone.store.ui.to_ui_ta1_ack`` so