from fastapi import FastAPI, Request from fastapi.middleware.cors import CORSMiddleware from fastapi.responses import JSONResponse from slowapi.middleware import SlowAPIMiddleware from slowapi.errors import RateLimitExceeded import logging from app.api.v1.api import api_router from app.core.config import settings from app.middleware.rate_limit import limiter, custom_rate_limit_exceeded_handler from app.middleware.validation import validation_middleware, validate_request_size # Configure logging logging.basicConfig(level=logging.INFO) logger = logging.getLogger(__name__) app = FastAPI( title=settings.PROJECT_NAME, version=settings.PROJECT_VERSION, openapi_url="/openapi.json", docs_url="/docs", redoc_url="/redoc" ) # Add rate limiting middleware app.state.limiter = limiter app.add_middleware(SlowAPIMiddleware) app.add_exception_handler(RateLimitExceeded, custom_rate_limit_exceeded_handler) # Add CORS middleware app.add_middleware( CORSMiddleware, allow_origins=settings.CORS_ORIGINS, allow_credentials=True, allow_methods=["*"], allow_headers=["*"], ) # Request validation middleware @app.middleware("http") async def security_middleware(request: Request, call_next): # Validate request size if not validate_request_size(request): return JSONResponse( status_code=413, content={"error": "Request payload too large"} ) # Validate headers if not validation_middleware.sanitize_headers(dict(request.headers)): return JSONResponse( status_code=400, content={"error": "Invalid request headers"} ) response = await call_next(request) # Add security headers response.headers["X-Content-Type-Options"] = "nosniff" response.headers["X-Frame-Options"] = "DENY" response.headers["X-XSS-Protection"] = "1; mode=block" response.headers["Strict-Transport-Security"] = "max-age=31536000; includeSubDomains" response.headers["Referrer-Policy"] = "strict-origin-when-cross-origin" return response # Include API routes app.include_router(api_router, prefix=settings.API_V1_STR) # Root endpoint @app.get("/") async def root(): """Root endpoint with service information""" return { "service": settings.PROJECT_NAME, "version": settings.PROJECT_VERSION, "documentation": "/docs", "health_check": "/api/v1/health", "api_version": settings.API_V1_STR, "description": "Multi-Tenant SaaS Platform with External Integrations", "features": [ "Multi-tenant data isolation", "JWT authentication with role management", "RESTful API endpoints", "Audit logging", "API rate limiting", "Webhook processing", "External API integration", "Circuit breaker pattern", "Health monitoring" ] } # Global exception handler @app.exception_handler(Exception) async def global_exception_handler(request: Request, exc: Exception): logger.error(f"Global exception: {str(exc)}", exc_info=True) return JSONResponse( status_code=500, content={"error": "Internal server error", "detail": "An unexpected error occurred"} ) if __name__ == "__main__": import uvicorn uvicorn.run( "main:app", host="0.0.0.0", port=8000, reload=True, log_level="info" )