Files
2026-05-11 10:58:46 +02:00

101 lines
3.0 KiB
Python

from __future__ import annotations
import logging
from contextlib import asynccontextmanager
import httpx
from fastapi import FastAPI
from fastapi.middleware.cors import CORSMiddleware
from starlette.middleware.base import BaseHTTPMiddleware
from starlette.requests import Request as StarletteRequest
from starlette.responses import Response as StarletteResponse
from app.core.audit import SharedBase
from app.core.config import settings
from app.core.db import create_postgres_engine, create_session_factory
from app.core.executor import get_executor, shutdown_executor
from app.core.otel import configure_otel, instrument_fastapi, instrument_sqlalchemy, shutdown_otel
from app.domain.aw.models import AWBase
from app.domain.wwi.models import WWIBase
from app.routers import aw, platform, wwi
LOGGER = logging.getLogger(__name__)
class SecurityHeadersMiddleware(BaseHTTPMiddleware):
async def dispatch(self, request: StarletteRequest, call_next) -> StarletteResponse:
response = await call_next(request)
response.headers["X-Content-Type-Options"] = "nosniff"
response.headers["X-Frame-Options"] = "DENY"
response.headers["Referrer-Policy"] = "strict-origin-when-cross-origin"
return response
@asynccontextmanager
async def lifespan(app: FastAPI):
# --- startup ---
providers = configure_otel(settings)
LOGGER.info("OTel configured for %s", settings.otel_service_name)
pg_engine = create_postgres_engine()
instrument_sqlalchemy({"pg": pg_engine})
SharedBase.metadata.create_all(pg_engine)
AWBase.metadata.create_all(pg_engine)
WWIBase.metadata.create_all(pg_engine)
pg_factory = create_session_factory(pg_engine)
analytics_client = httpx.AsyncClient(
base_url=settings.analytics_service_url,
timeout=httpx.Timeout(60.0),
)
executor = get_executor()
app.state.pg_engine = pg_engine
app.state.pg_factory = pg_factory
app.state.analytics_client = analytics_client
LOGGER.info("Ready: analytics_service=%s thread_pool_workers=%d",
settings.analytics_service_url, executor._max_workers) # noqa: SLF001
instrument_fastapi(app)
yield
# --- shutdown ---
LOGGER.info("Shutting down")
await analytics_client.aclose()
shutdown_executor()
pg_engine.dispose()
shutdown_otel(providers)
def create_app() -> FastAPI:
app = FastAPI(
title="otel-bi-backend",
version="1.0.0",
lifespan=lifespan,
docs_url="/docs" if settings.app_env != "prod" else None,
redoc_url=None,
)
app.add_middleware(SecurityHeadersMiddleware)
app.add_middleware(
CORSMiddleware,
allow_origins=settings.cors_origins_list,
allow_credentials=True,
allow_methods=["GET", "POST", "DELETE"],
allow_headers=["Authorization", "Content-Type"],
)
app.include_router(platform.router)
app.include_router(aw.router)
app.include_router(wwi.router)
return app
app = create_app()