from contextlib import asynccontextmanager from collections.abc import AsyncGenerator from fastapi import FastAPI, Request from fastapi.middleware.cors import CORSMiddleware from fastapi.responses import JSONResponse from prometheus_client import make_asgi_app from src.core.exceptions import PIPException from src.core.logging import configure_logging from src.core.metrics import metrics_middleware from src.core.tracing import setup_tracing from src.infrastructure.cache.redis_cache import redis_cache from src.infrastructure.config.settings import settings from src.infrastructure.messaging.rabbitmq import rabbitmq_client from src.api.middleware.audit import AuditMiddleware from src.api.middleware.logging import RequestLoggingMiddleware from src.api.middleware.rate_limit import RateLimitMiddleware from src.api.v1.router import v1_router from src.infrastructure.database.session import engine @asynccontextmanager async def lifespan(app: FastAPI) -> AsyncGenerator[None, None]: configure_logging() setup_tracing(app, engine=engine) await redis_cache.connect() await rabbitmq_client.connect() yield await rabbitmq_client.disconnect() await redis_cache.disconnect() await engine.dispose() def create_app() -> FastAPI: application = FastAPI( title=settings.APP_NAME, version=settings.APP_VERSION, description="Pharmacy Integration Platform — unified API between pharmacy ERPs and external applications", docs_url="/docs", redoc_url="/redoc", openapi_url="/openapi.json", lifespan=lifespan, ) application.add_middleware( CORSMiddleware, allow_origins=settings.CORS_ORIGINS, allow_credentials=settings.CORS_ALLOW_CREDENTIALS, allow_methods=["*"], allow_headers=["*"], ) application.add_middleware(RateLimitMiddleware) application.add_middleware(AuditMiddleware) application.add_middleware(RequestLoggingMiddleware) application.middleware("http")(metrics_middleware) application.include_router(v1_router) metrics_app = make_asgi_app() application.mount("/metrics", metrics_app) @application.exception_handler(PIPException) async def pip_exception_handler(request: Request, exc: PIPException) -> JSONResponse: return JSONResponse(status_code=exc.status_code, content=exc.to_dict()) @application.exception_handler(Exception) async def generic_exception_handler(request: Request, exc: Exception) -> JSONResponse: return JSONResponse(status_code=500, content={"error": {"code": "INTERNAL_ERROR", "message": "Internal server error"}}) return application app = create_app()