"""FastAPI application entry point.""" import uuid from contextlib import asynccontextmanager from typing import AsyncGenerator from fastapi import FastAPI, Request from fastapi.responses import JSONResponse from sqlalchemy.exc import IntegrityError from starlette.middleware.base import BaseHTTPMiddleware from app.config import settings from app.db import engine from app.routes import ( agents_router, health_router, playbooks_router, tasks_router, tenants_router, ) # --- Middleware --- class RequestIDMiddleware(BaseHTTPMiddleware): """Middleware that adds a unique request ID to each request.""" async def dispatch(self, request: Request, call_next): request_id = str(uuid.uuid4()) request.state.request_id = request_id response = await call_next(request) response.headers["X-Request-ID"] = request_id return response # --- Lifespan --- @asynccontextmanager async def lifespan(app: FastAPI) -> AsyncGenerator[None, None]: """Application lifespan handler for startup and shutdown.""" # Startup yield # Shutdown await engine.dispose() # --- Application --- app = FastAPI( title=settings.APP_NAME, version=settings.APP_VERSION, description="Control-plane backend for the LetsBe Cloud platform", lifespan=lifespan, ) # Add middleware app.add_middleware(RequestIDMiddleware) # --- Exception Handlers --- @app.exception_handler(IntegrityError) async def integrity_error_handler(request: Request, exc: IntegrityError) -> JSONResponse: """Handle database integrity errors (unique constraint violations, etc.).""" return JSONResponse( status_code=409, content={ "detail": "Resource conflict: a record with these values already exists", "request_id": getattr(request.state, "request_id", None), }, ) # --- Routers --- app.include_router(health_router) app.include_router(tenants_router, prefix="/api/v1") app.include_router(tasks_router, prefix="/api/v1") app.include_router(agents_router, prefix="/api/v1") app.include_router(playbooks_router, prefix="/api/v1") # --- Root endpoint --- @app.get("/") async def root(): """Root endpoint redirecting to docs.""" return { "message": f"Welcome to {settings.APP_NAME}", "docs": "/docs", "health": "/health", }