- Add TrustedHostMiddleware for Host header validation - Add CORSMiddleware with configurable origins - Add rate limiting with RateLimitExceeded handler - Add custom middleware for security headers (HSTS, X-Frame-Options, etc.) - Add /health/db endpoint that checks database connectivity - Mark health endpoints as rate limit exempt - Fix linting issues in migration file (Rule 3 - Blocking)
68 lines
2.3 KiB
Python
68 lines
2.3 KiB
Python
"""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"}
|