"""FastAPI application entry point.""" from fastapi import FastAPI, Request, Response from fastapi.middleware.cors import CORSMiddleware from fastapi.middleware.trustedhost import TrustedHostMiddleware from slowapi import _rate_limit_exceeded_handler from slowapi.errors import RateLimitExceeded from backend.app.api.v1.router import api_router from backend.app.core.config import settings from backend.app.core.security import limiter app = FastAPI( title="Debate API", version="1.0.0", description="Backend API for Debate - Linux distribution customization platform", docs_url="/docs" if not settings.is_production else None, redoc_url="/redoc" if not settings.is_production else None, debug=settings.debug, ) # Middleware order matters - first added = outermost layer # See: 01-RESEARCH.md Pattern 3: FastAPI Security Middleware Stack # 1. Trusted Host (reject requests with invalid Host header) app.add_middleware( TrustedHostMiddleware, allowed_hosts=settings.allowed_hosts_list, ) # 2. CORS (handle cross-origin requests) app.add_middleware( CORSMiddleware, allow_origins=settings.allowed_origins_list, allow_credentials=True, allow_methods=["GET", "POST", "PUT", "DELETE", "OPTIONS"], allow_headers=["*"], max_age=600, # Cache preflight requests for 10 minutes ) # 3. Rate limiting app.state.limiter = limiter app.add_exception_handler(RateLimitExceeded, _rate_limit_exceeded_handler) # 4. Custom middleware for security headers @app.middleware("http") async def security_headers_middleware(request: Request, call_next: object) -> Response: """Add security headers to all responses.""" response: Response = await call_next(request) # type: ignore[misc] response.headers["Strict-Transport-Security"] = ( "max-age=31536000; includeSubDomains" ) response.headers["X-Content-Type-Options"] = "nosniff" response.headers["X-Frame-Options"] = "DENY" response.headers["X-XSS-Protection"] = "1; mode=block" response.headers["Referrer-Policy"] = "strict-origin-when-cross-origin" return response # Include API routers app.include_router(api_router, prefix="/api/v1") @app.get("/health") async def root_health() -> dict[str, str]: """Root health check endpoint.""" return {"status": "healthy"}