"""FastAPI application factory (SPEC §2 ``app/main.py``).""" from __future__ import annotations from collections.abc import AsyncIterator from contextlib import asynccontextmanager from fastapi import FastAPI from sqlalchemy import text from app.config import get_settings from app.db import dispose_engine, get_engine from app.redis_client import close_redis, get_redis @asynccontextmanager async def lifespan(app: FastAPI) -> AsyncIterator[None]: """Warm shared resources on startup, dispose them on shutdown.""" # Touch settings early so misconfiguration fails fast. get_settings() yield await close_redis() await dispose_engine() def create_app() -> FastAPI: """Construct the FastAPI application.""" app = FastAPI(title="Webmail", version="0.1.0", lifespan=lifespan) from app.api.errors import register_error_handlers register_error_handlers(app) @app.get("/api/healthz") async def healthz() -> dict[str, str]: """Liveness/readiness probe: checks DB and Redis (dev-setup §6).""" async with get_engine().connect() as conn: await conn.execute(text("SELECT 1")) await get_redis().ping() return {"status": "ok"} from app.api import register_routers register_routers(app) return app