t2m / src /app /main.py
thanhkt's picture
implement core api
50a7bf0
"""
FastAPI application entry point.
Main application initialization with middleware, routers, and configuration.
"""
from contextlib import asynccontextmanager
from typing import AsyncGenerator, List, Dict, Any
from fastapi import FastAPI, Request, Response
from fastapi.middleware.trustedhost import TrustedHostMiddleware
from fastapi.responses import JSONResponse
from .core.config import get_settings
from .core.logger import get_logger
from .core.redis import redis_manager
from .core.auth import clerk_manager
from .core.openapi import OpenAPIConfig, setup_openapi_documentation
from .middleware import (
setup_cors_middleware,
setup_logging_middleware,
setup_compression_middleware,
setup_security_middleware,
setup_performance_middleware,
setup_async_middleware,
ClerkAuthMiddleware,
)
# Get settings and logger
settings = get_settings()
logger = get_logger(__name__)
@asynccontextmanager
async def lifespan(app: FastAPI) -> AsyncGenerator[None, None]:
"""
Application lifespan manager.
Handles startup and shutdown events.
"""
# Startup
logger.info(
"Starting FastAPI Video Backend",
app_name=settings.app_name,
version=settings.app_version,
environment=settings.environment,
debug=settings.debug,
)
# Initialize Redis connection pool
try:
await redis_manager.initialize()
logger.info("Redis connection initialized successfully")
except Exception as e:
logger.error(f"Failed to initialize Redis: {e}")
# Don't fail startup for Redis issues in development
if settings.is_production:
raise
# Initialize Clerk authentication
try:
clerk_manager.initialize()
logger.info("Clerk authentication initialized successfully")
except Exception as e:
logger.error(f"Failed to initialize Clerk: {e}")
# Don't fail startup for Clerk issues in development
if settings.is_production:
raise
logger.info("Application startup completed")
yield
# Shutdown
logger.info("Shutting down application")
# Close Redis connections
try:
await redis_manager.close()
logger.info("Redis connections closed")
except Exception as e:
logger.error(f"Error closing Redis connections: {e}")
# TODO: Cleanup background tasks
# TODO: Close file handles
logger.info("Application shutdown completed")
def create_application() -> FastAPI:
"""
Create and configure FastAPI application.
Returns:
FastAPI: Configured application instance
"""
# Create FastAPI app with enhanced OpenAPI configuration
app = FastAPI(
title=settings.app_name,
version=settings.app_version,
description=OpenAPIConfig.get_api_description(),
summary="FastAPI backend for multi-agent video generation system",
terms_of_service="https://example.com/terms/",
contact={
"name": "API Support",
"url": "https://example.com/contact/",
"email": "support@example.com",
},
license_info={
"name": "MIT",
"url": "https://opensource.org/licenses/MIT",
},
docs_url=settings.docs_url if not settings.is_production else None,
redoc_url=settings.redoc_url if not settings.is_production else None,
openapi_url=settings.openapi_url if not settings.is_production else None,
openapi_tags=OpenAPIConfig.get_openapi_tags(),
servers=OpenAPIConfig.get_api_servers(),
lifespan=lifespan,
# Enhanced Swagger UI configuration
swagger_ui_parameters={
"syntaxHighlight.activate": True,
"syntaxHighlight.theme": "agate",
"syntaxHighlight.maxLines": 100,
"syntaxHighlight.wrapLines": True,
"displayRequestDuration": True,
"docExpansion": "list", # Show operations expanded
"deepLinking": True,
"displayOperationId": False,
"defaultModelsExpandDepth": 2,
"defaultModelExpandDepth": 2,
"defaultModelRendering": "example",
"showExtensions": True,
"showCommonExtensions": True,
"tryItOutEnabled": True,
},
)
# Add middleware
setup_middleware(app)
# Add routers
setup_routers(app)
# Add exception handlers
setup_exception_handlers(app)
return app
def setup_middleware(app: FastAPI) -> None:
"""
Configure application middleware.
Args:
app: FastAPI application instance
"""
# Trusted host middleware (security) - should be first
if settings.is_production:
app.add_middleware(
TrustedHostMiddleware,
allowed_hosts=["*"] # TODO: Configure with actual allowed hosts
)
# Security headers middleware - early in the chain
setup_security_middleware(app)
# CORS middleware - before authentication
setup_cors_middleware(app)
# Response compression middleware
setup_compression_middleware(app)
# Performance optimization middleware
setup_performance_middleware(app)
setup_async_middleware(app)
# Request logging middleware with performance metrics
setup_logging_middleware(app)
# Clerk authentication middleware - after logging but before business logic
app.add_middleware(
ClerkAuthMiddleware,
exclude_paths=[
"/",
"/health",
"/docs",
"/redoc",
"/openapi.json",
"/favicon.ico",
"/.well-known/appspecific/com.chrome.devtools.json", # Chrome DevTools
"/api/docs", # Alternative docs path
"/api/v1/system/health",
"/api/v1/auth/health",
"/api/v1/auth/status" # This endpoint handles optional auth internally
]
)
def setup_routers(app: FastAPI) -> None:
"""
Configure application routers.
Args:
app: FastAPI application instance
"""
# Health check endpoint
@app.get("/health")
async def health_check():
"""Basic health check endpoint."""
return {
"status": "healthy",
"app_name": settings.app_name,
"version": settings.app_version,
"environment": settings.environment,
}
# Root endpoint
@app.get("/")
async def root():
"""Root endpoint with basic information."""
return {
"message": f"Welcome to {settings.app_name}",
"version": settings.app_version,
"docs_url": settings.docs_url,
"health_url": "/health",
}
# Test endpoint for Swagger UI
@app.get("/test-swagger", tags=["testing"])
async def test_swagger_ui():
"""Test endpoint to verify Swagger UI functionality."""
return {
"message": "Swagger UI is working correctly!",
"timestamp": "2024-01-15T10:30:00Z",
"features": [
"Interactive API documentation",
"Request/response examples",
"Authentication testing",
"Schema validation"
]
}
# Add API v1 routers
from .api.v1 import auth, videos, jobs, system, files
app.include_router(auth.router, prefix=settings.api_v1_prefix)
app.include_router(videos.router, prefix=settings.api_v1_prefix, tags=["videos"])
app.include_router(jobs.router, prefix=settings.api_v1_prefix, tags=["jobs"])
app.include_router(system.router, prefix=settings.api_v1_prefix, tags=["system"])
app.include_router(files.router, prefix=settings.api_v1_prefix, tags=["files"])
def setup_exception_handlers(app: FastAPI) -> None:
"""
Configure global exception handlers.
Args:
app: FastAPI application instance
"""
@app.exception_handler(Exception)
async def global_exception_handler(request: Request, exc: Exception) -> JSONResponse:
"""Handle unexpected exceptions."""
logger.error(
"Unhandled exception",
error_type=type(exc).__name__,
error_message=str(exc),
url=str(request.url),
method=request.method,
exc_info=True,
)
# Don't expose internal errors in production
if settings.is_production:
return JSONResponse(
status_code=500,
content={
"error": {
"message": "Internal server error",
"error_code": "INTERNAL_ERROR",
}
}
)
else:
return JSONResponse(
status_code=500,
content={
"error": {
"message": str(exc),
"error_code": "INTERNAL_ERROR",
"type": type(exc).__name__,
}
}
)
# Create application instance
app = create_application()
# Set up comprehensive OpenAPI documentation
setup_openapi_documentation(app)
if __name__ == "__main__":
import uvicorn
logger.info(
"Starting development server",
host=settings.host,
port=settings.port,
reload=settings.reload,
)
uvicorn.run(
"src.app.main:app",
host=settings.host,
port=settings.port,
reload=settings.reload,
log_level=settings.log_level.lower(),
)