| """ |
| Main FastAPI application module. |
| """ |
| import time |
| import logging |
| import json |
| from datetime import datetime |
| from fastapi import FastAPI |
| from fastapi.middleware.cors import CORSMiddleware |
| from fastapi.responses import JSONResponse |
| from contextlib import asynccontextmanager |
|
|
| from app.page_speed.config import settings |
| from app.page_speed.models import HealthResponse |
| from app.rag.routes import router as rag_router |
| from app.seo import routes as seo_routes |
| from app.page_speed import routes as page_speed_routes |
|
|
|
|
| |
| |
| |
| logger = logging.getLogger("app") |
| logger.setLevel(logging.INFO) |
|
|
| handler = logging.StreamHandler() |
| formatter = logging.Formatter( |
| "%(asctime)s | %(levelname)s | %(name)s | %(message)s", |
| datefmt="%Y-%m-%d %H:%M:%S" |
| ) |
| handler.setFormatter(formatter) |
| logger.addHandler(handler) |
|
|
| |
| startup_time = None |
|
|
| @asynccontextmanager |
| async def lifespan(app: FastAPI): |
| """Application lifespan manager.""" |
| global startup_time |
| startup_time = time.time() |
| logger.info("π Starting %s v%s", settings.app_name, settings.app_version) |
| logger.info("π Server running on %s:%s", settings.host, settings.port) |
| yield |
| logger.info("π Shutting down %s", settings.app_name) |
|
|
| |
| app = FastAPI( |
| title=settings.app_name, |
| description=settings.app_description, |
| version=settings.app_version, |
| lifespan=lifespan, |
| docs_url="/docs", |
| redoc_url="/redoc" |
| ) |
|
|
| |
| app.include_router(rag_router) |
|
|
| app.include_router(seo_routes.router) |
|
|
| |
| app.include_router(page_speed_routes.router) |
|
|
| |
| app.add_middleware( |
| CORSMiddleware, |
| allow_origins=["*"], |
| allow_credentials=True, |
| allow_methods=["*"], |
| allow_headers=["*"], |
| ) |
|
|
| @app.get("/", response_model=dict) |
| async def root(): |
| """Root endpoint with API information.""" |
| return { |
| "message": f"Welcome to {settings.app_name}", |
| "version": settings.app_version, |
| "description": settings.app_description, |
| "docs": "/docs", |
| "health": "/health" |
| } |
|
|
|
|
| @app.get("/health", response_model=HealthResponse) |
| async def health_check(): |
| """Health check endpoint.""" |
| global startup_time |
| |
| if startup_time: |
| uptime_seconds = time.time() - startup_time |
| uptime_str = f"{uptime_seconds:.2f} seconds" |
| else: |
| uptime_str = "Unknown" |
| |
| return HealthResponse( |
| status="healthy", |
| version=settings.app_version, |
| uptime=uptime_str |
| ) |
|
|
|
|
| @app.exception_handler(404) |
| async def not_found_handler(request, exc): |
| """Custom 404 handler.""" |
| logger.warning("404 Not Found: %s %s", request.method, request.url.path) |
| return JSONResponse( |
| status_code=404, |
| content={ |
| "error": "Not Found", |
| "message": "The requested endpoint was not found", |
| "docs": "/docs" |
| } |
| ) |
|
|
| @app.exception_handler(500) |
| async def internal_error_handler(request, exc): |
| """Custom 500 handler.""" |
| logger.error("500 Internal Server Error: %s %s -> %s", request.method, request.url.path, exc, exc_info=True) |
| return JSONResponse( |
| status_code=500, |
| content={ |
| "error": "Internal Server Error", |
| "message": "An unexpected error occurred", |
| "timestamp": datetime.now().isoformat() |
| } |
| ) |
|
|
|
|
| if __name__ == "__main__": |
| import uvicorn |
| |
| uvicorn.run( |
| "app.main:app", |
| host=settings.host, |
| port=settings.port, |
| reload=settings.debug |
| ) |
|
|