update fastapi and unpin starlette

fastapi 0.116 capped starlette below 0.49, which kept us on a version with several open advisories, the earliest of which is fixed in 0.49.1. fastapi moves to 0.142 and the starlette pin is removed so the latest (1.7.0 today) is pulled in.

fastapi 0.137 and later stopped flattening included routers into app.routes, so the spec generator found no routes to classify and every endpoint failed validation. The generator now reads routes from the routers directly. The spec is regenerated on the new versions.
This commit is contained in:
Josh Hawkins
2026-10-06 13:11:27 -05:00
parent 6b1e084fdc
commit 502a32a96f
3 changed files with 40 additions and 29 deletions
+1 -2
View File
@@ -2,9 +2,8 @@ aiofiles == 25.1.*
click == 8.5.* click == 8.5.*
# FastAPI # FastAPI
aiohttp == 3.12.* aiohttp == 3.12.*
starlette == 0.47.*
starlette-context == 0.5.* starlette-context == 0.5.*
fastapi[standard-no-fastapi-cloud-cli] == 0.116.* fastapi[standard-no-fastapi-cloud-cli] == 0.142.*
uvicorn == 0.52.* uvicorn == 0.52.*
slowapi == 0.1.* slowapi == 0.1.*
joserfc == 1.6.* joserfc == 1.6.*
+7 -2
View File
@@ -8017,7 +8017,7 @@ components:
properties: properties:
file: file:
type: string type: string
format: binary contentMediaType: application/octet-stream
title: File title: File
type: object type: object
required: required:
@@ -8027,7 +8027,7 @@ components:
properties: properties:
file: file:
type: string type: string
format: binary contentMediaType: application/octet-stream
title: File title: File
type: object type: object
required: required:
@@ -9601,6 +9601,11 @@ components:
type: type:
type: string type: string
title: Error Type title: Error Type
input:
title: Input
ctx:
type: object
title: Context
type: object type: object
required: required:
- loc - loc
+32 -25
View File
@@ -136,33 +136,36 @@ ACCESS_NOTES = {
} }
# Mirrors the router set wired up in frigate.api.fastapi_app.
ROUTERS = [
auth.router,
camera.router,
chat.router,
classification.router,
review.router,
main_app.router,
preview.router,
notification.router,
export.router,
hardware.router,
notices.router,
event.router,
media.router,
motion_search.router,
record.router,
debug_replay.router,
]
def build_app() -> FastAPI: def build_app() -> FastAPI:
"""Build a bare app with every router mounted. """Build a bare app with every router mounted.
This mirrors the router set wired up in frigate.api.fastapi_app. It omits It omits the global admin dependency and all runtime state; the OpenAPI
the global admin dependency and all runtime state; the OpenAPI route table route table and the per-route dependencies are all we need to export and
and the per-route dependencies are all we need to export and classify. classify.
""" """
app = FastAPI() app = FastAPI()
routers = [ for router in ROUTERS:
auth.router,
camera.router,
chat.router,
classification.router,
review.router,
main_app.router,
preview.router,
notification.router,
export.router,
hardware.router,
notices.router,
event.router,
media.router,
motion_search.router,
record.router,
debug_replay.router,
]
for router in routers:
app.include_router(router) app.include_router(router)
return app return app
@@ -318,13 +321,17 @@ def _classify_base(
def build_access_map( def build_access_map(
app: FastAPI,
exempt_paths: set[str], exempt_paths: set[str],
exempt_prefixes: tuple[str, ...], exempt_prefixes: tuple[str, ...],
) -> dict[tuple[str, str], dict]: ) -> dict[tuple[str, str], dict]:
"""Map (path, lowercase method) -> classification details.""" """Map (path, lowercase method) -> classification details."""
access_map: dict[tuple[str, str], dict] = {} access_map: dict[tuple[str, str], dict] = {}
for route in app.routes:
# app.routes holds opaque wrappers for included routers on newer FastAPI.
# The routers mount without a prefix, so their own routes carry final paths.
routes = [route for router in ROUTERS for route in router.routes]
for route in routes:
if not isinstance(route, APIRoute): if not isinstance(route, APIRoute):
continue continue
level, roles, flag = classify_route(route, exempt_paths, exempt_prefixes) level, roles, flag = classify_route(route, exempt_paths, exempt_prefixes)
@@ -515,7 +522,7 @@ def render(spec: dict) -> str:
def build_spec() -> tuple[dict, dict, list, list, list]: def build_spec() -> tuple[dict, dict, list, list, list]:
app = build_app() app = build_app()
exempt_paths, exempt_prefixes = read_exempt_rules() exempt_paths, exempt_prefixes = read_exempt_rules()
access_map = build_access_map(app, exempt_paths, exempt_prefixes) access_map = build_access_map(exempt_paths, exempt_prefixes)
spec = base_document(app.openapi()) spec = base_document(app.openapi())
normalized = strip_volatile_defaults(spec) normalized = strip_volatile_defaults(spec)