"""Point d'entrée de l'application HomeStock. Application FastAPI pour la gestion d'inventaire domestique. """ from contextlib import asynccontextmanager from typing import Any from fastapi import FastAPI from fastapi.middleware.cors import CORSMiddleware from fastapi.responses import JSONResponse from loguru import logger from app.core.config import settings from app.core.database import close_db, init_db from app.core.logging import setup_logging # Configuration du logging setup_logging() @asynccontextmanager async def lifespan(app: FastAPI) -> Any: """Gère le cycle de vie de l'application. - Startup : Initialise la base de données - Shutdown : Ferme les connexions proprement """ logger.info("Démarrage de l'application HomeStock") logger.info(f"Environnement: {settings.ENVIRONMENT}") logger.info(f"Version: {settings.APP_VERSION}") # Initialisation de la base de données # Note: En production, utiliser Alembic pour les migrations if settings.is_development: logger.debug("Initialisation de la base de données (mode développement)") await init_db() yield # Nettoyage logger.info("Arrêt de l'application HomeStock") await close_db() # Création de l'application FastAPI app = FastAPI( title=settings.APP_NAME, description="API pour la gestion d'inventaire domestique", version=settings.APP_VERSION, docs_url="/api/docs" if settings.is_development else None, # Swagger UI redoc_url="/api/redoc" if settings.is_development else None, # ReDoc openapi_url="/api/openapi.json" if settings.is_development else None, lifespan=lifespan, ) # === Configuration CORS === app.add_middleware( CORSMiddleware, allow_origins=settings.cors_origins_list, allow_credentials=settings.CORS_ALLOW_CREDENTIALS, allow_methods=["*"], # Autorise toutes les méthodes (GET, POST, PUT, DELETE, etc.) allow_headers=["*"], # Autorise tous les headers ) # === Routes de santé === @app.get("/", tags=["Health"]) async def root() -> dict[str, str]: """Route racine - Vérification de l'état de l'API.""" return { "app": settings.APP_NAME, "version": settings.APP_VERSION, "status": "running", } @app.get("/health", tags=["Health"]) async def health() -> dict[str, str]: """Endpoint de santé pour monitoring.""" return { "status": "healthy", "environment": settings.ENVIRONMENT, } # === Gestion globale des erreurs === @app.exception_handler(Exception) async def global_exception_handler(request: Any, exc: Exception) -> JSONResponse: """Gestionnaire global des exceptions non capturées. Log l'erreur et retourne une réponse JSON standardisée. """ logger.error(f"Erreur non gérée: {exc}", exc_info=True) # En production, masquer les détails de l'erreur detail = str(exc) if settings.is_development else "Erreur interne du serveur" return JSONResponse( status_code=500, content={ "detail": detail, "type": "internal_server_error", }, ) # === Enregistrement des routers === from app.routers import categories_router, documents_router, import_router, items_router, locations_router, shops_router app.include_router(categories_router, prefix="/api/v1") app.include_router(locations_router, prefix="/api/v1") app.include_router(items_router, prefix="/api/v1") app.include_router(documents_router, prefix="/api/v1") app.include_router(shops_router, prefix="/api/v1") app.include_router(import_router, prefix="/api/v1") if __name__ == "__main__": import uvicorn uvicorn.run( "app.main:app", host=settings.BACKEND_HOST, port=settings.BACKEND_PORT, reload=settings.BACKEND_RELOAD, log_level=settings.LOG_LEVEL.lower(), )