"""FastAPI-Server des Planers (Phase 2). Start: `uvicorn server:app --port 8100` aus backend/ — OHNE --reload: der Reload killt laufende Scans im eigenen Prozess (Creator-Lehre; siehe README, Selbst-Änderungs-Regel). """ import sys from contextlib import asynccontextmanager from pathlib import Path sys.path.insert(0, str(Path(__file__).resolve().parent)) from fastapi import FastAPI # noqa: E402 from fastapi.staticfiles import StaticFiles # noqa: E402 from starlette.responses import Response # noqa: E402 import agents # noqa: E402 import config # noqa: E402 import database as db # noqa: E402 from routes import router # noqa: E402 FRONTEND_DIST = config.PROJECT_ROOT / "frontend" / "dist" @asynccontextmanager async def lifespan(app: FastAPI): await db.init_db() agents.on_event = db.add_event yield await db.close_db() class CachedStatic(StaticFiles): """assets/ = unveränderlich (gehashte Namen), index.html = nie cachen.""" def file_response(self, full_path, stat_result, scope, status_code=200) -> Response: resp = super().file_response(full_path, stat_result, scope, status_code) pfad = str(full_path) if "/assets/" in pfad: resp.headers["Cache-Control"] = "public, max-age=31536000, immutable" elif pfad.endswith("index.html"): resp.headers["Cache-Control"] = "no-cache" return resp app = FastAPI(title="Planer", lifespan=lifespan) app.include_router(router) if FRONTEND_DIST.exists(): app.mount("/", CachedStatic(directory=FRONTEND_DIST, html=True), name="frontend")