import logging from contextlib import asynccontextmanager from pathlib import Path from fastapi import FastAPI from fastapi.responses import FileResponse, JSONResponse from fastapi.staticfiles import StaticFiles from starlette.middleware.sessions import SessionMiddleware from starlette.types import Scope from app.api.admin import router as admin_router from app.api.auth import router as auth_router from app.api.jobs import router as jobs_router from app.core.config import settings from app.core.db import init_db from app.core.mac_client import MacClient from app.core.migrate import run_migrations class NoCacheStaticFiles(StaticFiles): """StaticFiles, die Browser zu Revalidierung zwingen (verhindert hängende Caches nach Deploy).""" async def get_response(self, path: str, scope: Scope): response = await super().get_response(path, scope) response.headers["Cache-Control"] = "no-cache, must-revalidate" return response logging.basicConfig(level=logging.INFO, format="%(asctime)s [%(levelname)s] %(name)s: %(message)s") log = logging.getLogger("voice-agent") @asynccontextmanager async def lifespan(app: FastAPI): init_db() run_migrations() if settings.session_secret == "CHANGE-ME-SET-IN-ENV": log.warning("SESSION_SECRET nicht gesetzt — bitte in .env auf einen geheimen Wert ändern!") log.info("DB initialized at %s", settings.db_url) log.info("Upload dir: %s", settings.upload_dir) log.info("Result dir: %s", settings.result_dir) log.info("Mac API: %s", settings.mac_api_url) yield app = FastAPI(title=settings.app_name, lifespan=lifespan) app.add_middleware( SessionMiddleware, secret_key=settings.session_secret, session_cookie=settings.session_cookie_name, max_age=settings.session_max_age, same_site="lax", https_only=False, # via Reverse-Proxy: ggf. auf True wenn HTTPS terminiert ) app.include_router(auth_router) app.include_router(admin_router) app.include_router(jobs_router) STATIC_DIR = Path(__file__).resolve().parent / "static" app.mount("/static", NoCacheStaticFiles(directory=STATIC_DIR), name="static") @app.get("/") def index(): return FileResponse(STATIC_DIR / "index.html", headers={"Cache-Control": "no-cache, must-revalidate"}) @app.get("/health") def health(): return {"status": "ok", "service": settings.app_name} @app.get("/api/mac/health") async def mac_health(): try: data = await MacClient().health() return {"reachable": True, "mac": data} except Exception as e: # noqa: BLE001 return JSONResponse(status_code=502, content={"reachable": False, "error": str(e)})