from collections.abc import AsyncIterator from contextlib import asynccontextmanager from pathlib import Path from fastapi import FastAPI, Request from fastapi.responses import JSONResponse, RedirectResponse from fastapi.staticfiles import StaticFiles from slowapi.errors import RateLimitExceeded from slowapi.middleware import SlowAPIMiddleware from app.config import get_settings from app.db import close_pool, get_pool from app.health import router as health_router from app.logging_setup import configure_logging from app.rate_limit import limiter from app.routers.auth import router as auth_router from app.routers.push import router as push_router from app.routers.views import router as views_router WEB_DIR = Path("/srv/web") @asynccontextmanager async def lifespan(_: FastAPI) -> AsyncIterator[None]: configure_logging() await get_pool() try: yield finally: await close_pool() def _rate_limit_handler(request: Request, exc: Exception) -> JSONResponse: _ = request, exc return JSONResponse(status_code=429, content={"detail": "rate limit exceeded"}) def create_app(role: str) -> FastAPI: settings = get_settings() app = FastAPI( title=f"fleet-platform [{role}]", version=settings.app_git_sha, lifespan=lifespan, ) app.state.limiter = limiter app.add_exception_handler(RateLimitExceeded, _rate_limit_handler) app.add_middleware(SlowAPIMiddleware) app.include_router(health_router) if role == "gateway": app.include_router(auth_router) app.include_router(push_router) app.include_router(views_router) @app.get("/", include_in_schema=False) async def _root() -> RedirectResponse: return RedirectResponse(url="/index-live.html", status_code=307) if WEB_DIR.is_dir(): app.mount("/", StaticFiles(directory=str(WEB_DIR), html=True), name="web") return app