from __future__ import annotations import os from collections.abc import AsyncIterator from contextlib import asynccontextmanager from fastapi import FastAPI from fastapi.middleware.cors import CORSMiddleware from app.api.routes import router from app.config import Settings, get_settings from app.db import init_db from app.runtime import Runtime def create_app( settings: Settings | None = None, runtime: Runtime | None = None, ) -> FastAPI: settings = settings or get_settings() runtime = runtime or Runtime(settings) @asynccontextmanager async def lifespan(app: FastAPI) -> AsyncIterator[None]: init_db() app.state.runtime = runtime yield await runtime.shutdown() app = FastAPI(title=settings.app_name, lifespan=lifespan) # Baked at build time (see Dockerfile); the frontend compares its own # version against this to self-heal from a stale cache. app.state.build_version = os.environ.get("BUILD_VERSION", "unknown") app.add_middleware( CORSMiddleware, allow_origins=settings.cors_origin_list, allow_credentials=True, allow_methods=["*"], allow_headers=["*"], ) app.include_router(router) @app.get("/") def root() -> dict[str, str]: return {"name": settings.app_name, "docs": "/docs"} return app app = create_app()