import time from pathlib import Path from fastapi import FastAPI, Request from fastapi.staticfiles import StaticFiles from app.metrics import metrics from app.api.health import router as health_router from app.api.chat import router as chat_router from app.api.transcribe import router as transcribe_router from app.api.speak import router as speak_router from app.api.devices import router as devices_router from app.api.sessions import router as sessions_router from app.api.config import router as config_router from app.api.admin import router as admin_router from app.api.me import router as me_router from app.api.metrics import router as metrics_router from app.api.ws import router as ws_router app = FastAPI(title="Voice Assistant Gateway") @app.middleware("http") async def record_metrics(request: Request, call_next): start = time.perf_counter() response = await call_next(request) duration = time.perf_counter() - start # Route-Template (z. B. /api/sessions/{session_id}/route) statt konkreter URL, # um die Label-Kardinalitaet niedrig zu halten. route = request.scope.get("route") path = getattr(route, "path", request.url.path) labels = {"method": request.method, "path": path} metrics.inc("http_requests_total", {**labels, "status": response.status_code}) metrics.observe("http_request_duration_seconds", duration, labels) return response app.include_router(health_router) app.include_router(chat_router, prefix="/api") app.include_router(transcribe_router, prefix="/api") app.include_router(speak_router, prefix="/api") app.include_router(devices_router, prefix="/api") app.include_router(sessions_router, prefix="/api") app.include_router(config_router, prefix="/api") app.include_router(admin_router, prefix="/api") app.include_router(me_router, prefix="/api") app.include_router(metrics_router, prefix="/api") app.include_router(ws_router) # Statische Web-UI (same-origin -> kein CORS). Muss NACH allen API-/WS-Routen # gemountet werden, damit "/" nur die uebrigen Pfade abfaengt. class _NoCacheStaticFiles(StaticFiles): """Liefert die UI ohne Caching aus -> Aenderungen sind sofort sichtbar (kein Safari-Cache).""" def file_response(self, *args, **kwargs): response = super().file_response(*args, **kwargs) response.headers["Cache-Control"] = "no-cache, no-store, must-revalidate" return response _WEB_DIR = Path(__file__).resolve().parent / "web" if _WEB_DIR.is_dir(): app.mount("/", _NoCacheStaticFiles(directory=str(_WEB_DIR), html=True), name="web")