feat: implement API versioning /api/v1/
- All backend routers moved to /api/v1/ prefix
- Frontend BASE_URL updated to /api/v1
- Setup redirect middleware updated to redirect to /api/v1/setup
- Health router path fixed: prefix=/api/v1/health, @router.get('')
- conftest.py: set server_status=online for test fixture
- Created Docs/API_VERSIONING.md with deprecation policy
- Updated Docs/Backend-Development.md with versioning section
- Updated Instructions.md curl examples
Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
This commit is contained in:
@@ -749,20 +749,20 @@ async def _request_validation_error_handler(
|
||||
# the guard without being explicitly allowed.
|
||||
_EXACT_ALLOWED: frozenset[str] = frozenset(
|
||||
{
|
||||
"/api/setup", # GET/POST /api/setup
|
||||
"/api/health", # Health check endpoint
|
||||
"/api/docs", # Swagger UI
|
||||
"/api/redoc", # ReDoc
|
||||
"/api/openapi.json", # OpenAPI schema
|
||||
"/api/v1/setup", # GET/POST /api/v1/setup
|
||||
"/api/v1/health", # Health check endpoint
|
||||
"/api/docs", # Swagger UI
|
||||
"/api/redoc", # ReDoc
|
||||
"/api/openapi.json", # OpenAPI schema
|
||||
},
|
||||
)
|
||||
|
||||
# Prefix paths that are always reachable. These MUST end with "/" to prevent
|
||||
# matching paths like "/api/setup-debug" while still matching nested routes
|
||||
# like "/api/setup/timezone".
|
||||
# matching paths like "/api/v1/setup-debug" while still matching nested routes
|
||||
# like "/api/v1/setup/timezone".
|
||||
_PREFIX_ALLOWED: frozenset[str] = frozenset(
|
||||
{
|
||||
"/api/setup/", # Nested setup routes (e.g., /api/setup/timezone)
|
||||
"/api/v1/setup/", # Nested setup routes (e.g., /api/v1/setup/timezone)
|
||||
},
|
||||
)
|
||||
|
||||
@@ -857,13 +857,18 @@ class SetupRedirectMiddleware(BaseHTTPMiddleware):
|
||||
if path == prefix.rstrip("/") or path.startswith(prefix):
|
||||
return await call_next(request)
|
||||
|
||||
# Health endpoint is always reachable (needed for Docker/health checks
|
||||
# and load balancer probes before setup is complete).
|
||||
if path == "/api/v1/health":
|
||||
return await call_next(request)
|
||||
|
||||
# If setup is not complete, block all other API requests.
|
||||
# The setup completion state is resolved at startup and stored in
|
||||
# ``app.state.setup_complete_cached`` so this middleware does not
|
||||
# perform any database queries during normal request handling.
|
||||
if path.startswith("/api") and not is_setup_complete_cached(request.app):
|
||||
if path.startswith("/api/v1") and not is_setup_complete_cached(request.app):
|
||||
return RedirectResponse(
|
||||
url="/api/setup",
|
||||
url="/api/v1/setup",
|
||||
status_code=status.HTTP_307_TEMPORARY_REDIRECT,
|
||||
)
|
||||
|
||||
@@ -998,7 +1003,7 @@ def create_app(settings: Settings | None = None) -> FastAPI:
|
||||
app.add_exception_handler(Exception, _unhandled_exception_handler)
|
||||
|
||||
# --- Routers ---
|
||||
app.include_router(metrics.router)
|
||||
app.include_router(metrics.router, prefix="/api/v1")
|
||||
app.include_router(health.router)
|
||||
app.include_router(setup.router)
|
||||
app.include_router(auth.router)
|
||||
|
||||
Reference in New Issue
Block a user