""" Bot Detector Dashboard - API Backend FastAPI application pour servir le dashboard web """ import logging from contextlib import asynccontextmanager from fastapi import FastAPI, HTTPException from fastapi.middleware.cors import CORSMiddleware from fastapi.staticfiles import StaticFiles from fastapi.responses import FileResponse import os from .config import settings from .database import db from .routes import metrics, detections, variability, attributes, analysis, entities, incidents, audit # Configuration logging logging.basicConfig( level=logging.INFO, format='%(asctime)s - %(name)s - %(levelname)s - %(message)s' ) logger = logging.getLogger(__name__) @asynccontextmanager async def lifespan(app: FastAPI): """Gestion du cycle de vie de l'application""" # Startup logger.info("Démarrage du Bot Detector Dashboard API...") logger.info(f"ClickHouse: {settings.CLICKHOUSE_HOST}:{settings.CLICKHOUSE_PORT}") logger.info(f"Database: {settings.CLICKHOUSE_DB}") # Tester la connexion ClickHouse try: client = db.connect() client.ping() logger.info("Connexion ClickHouse établie avec succès") except Exception as e: logger.error(f"Échec de connexion ClickHouse: {e}") raise yield # Shutdown logger.info("Arrêt du Bot Detector Dashboard API...") db.close() # Création de l'application FastAPI app = FastAPI( title="Bot Detector Dashboard API", description="API pour le dashboard de visualisation des détections Bot Detector", version="1.0.0", lifespan=lifespan ) # Configuration CORS app.add_middleware( CORSMiddleware, allow_origins=settings.CORS_ORIGINS, allow_credentials=True, allow_methods=["*"], allow_headers=["*"], ) # Enregistrement des routes app.include_router(metrics.router) app.include_router(detections.router) app.include_router(variability.router) app.include_router(attributes.router) app.include_router(analysis.router) app.include_router(entities.router) app.include_router(incidents.router) app.include_router(audit.router) # Route pour servir le frontend @app.get("/") async def serve_frontend(): """Sert l'application React""" frontend_path = os.path.join(os.path.dirname(__file__), "..", "frontend", "dist", "index.html") if os.path.exists(frontend_path): return FileResponse(frontend_path) return {"message": "Dashboard API - Frontend non construit. Voir /docs pour l'API."} # Servir les assets statiques assets_path = os.path.join(os.path.dirname(__file__), "..", "frontend", "dist", "assets") if os.path.exists(assets_path): app.mount("/assets", StaticFiles(directory=assets_path), name="assets") # Health check @app.get("/health") async def health_check(): """Endpoint de santé pour le health check Docker""" try: db.connect().ping() return {"status": "healthy", "clickhouse": "connected"} except Exception as e: return {"status": "unhealthy", "clickhouse": "disconnected", "error": str(e)} # Route catch-all pour le routing SPA (React Router) - DOIT ÊTRE EN DERNIER # Sauf pour /api/* qui doit être géré par les routers @app.get("/{full_path:path}") async def serve_spa(full_path: str): """Redirige toutes les routes vers index.html pour le routing React""" # Ne pas intercepter les routes API if full_path.startswith("api/"): raise HTTPException(status_code=404, detail="API endpoint not found") frontend_path = os.path.join(os.path.dirname(__file__), "..", "frontend", "dist", "index.html") if os.path.exists(frontend_path): return FileResponse(frontend_path) return {"message": "Dashboard API - Frontend non construit"} if __name__ == "__main__": import uvicorn uvicorn.run( "main:app", host=settings.API_HOST, port=settings.API_PORT, reload=True )