""" Webber - Multi-Agent AI Development System FastAPI application entry point. NO routes here - all routes delegated to domain routers. """ from contextlib import asynccontextmanager from fastapi import FastAPI, Request from fastapi.middleware.cors import CORSMiddleware from fastapi.responses import JSONResponse from src.domains.router import root_router from src.shared.auth import validate_api_key from src.shared.config import get_settings from src.shared.context import user_provider from src.shared.exceptions import AppException from src.shared.logging import get_logger, setup_logging settings = get_settings() setup_logging(settings.log_level) logger = get_logger(__name__) @asynccontextmanager async def lifespan(app: FastAPI): """Application startup and shutdown.""" logger.info("=" * 60) logger.info(f"Starting {settings.app_name} v{settings.app_version}") logger.info(f"Debug: {settings.debug}") logger.info(f"Port: {settings.port}") logger.info(f"Ollama: {settings.ollama_url}") logger.info(f"Agent model: {settings.ollama_agent_model}") logger.info(f"Database: {settings.database_url}") logger.info("=" * 60) yield # Cleanup from src.db import get_database try: database = get_database() await database.close() except Exception: pass logger.info("Shutting down") app = FastAPI( title=settings.app_name, version=settings.app_version, description=settings.app_description, docs_url="/docs", redoc_url=None, openapi_url="/openapi.json", lifespan=lifespan, debug=settings.debug, ) # === Middleware === app.add_middleware( CORSMiddleware, allow_origins=settings.cors_origins, allow_credentials=settings.cors_credentials, allow_methods=settings.cors_methods, allow_headers=settings.cors_headers, ) @app.middleware("http") async def auth_middleware(request: Request, call_next): """ Extract and validate API key, set user context. Allows unauthenticated requests - individual routes decide if auth is required. """ api_key = request.headers.get("X-API-Key") if api_key: user = await validate_api_key(api_key) if user: user_provider.set_user(user) try: response = await call_next(request) return response finally: user_provider.clear_user() # === Exception Handlers === @app.exception_handler(AppException) async def app_exception_handler(request: Request, exc: AppException): """Handle application exceptions.""" logger.warning(f"AppException: {exc.error_code} - {exc.message}") return JSONResponse( status_code=exc.status_code, content=exc.to_dict(), ) @app.exception_handler(Exception) async def global_exception_handler(request: Request, exc: Exception): """Catch-all exception handler.""" logger.error(f"Unhandled exception: {exc}", exc_info=True) return JSONResponse( status_code=500, content={ "error": "InternalServerError", "message": "Internal server error", "details": {"type": type(exc).__name__}, } ) # === Routes === app.include_router(root_router)