"""Audiofy-Web – FastAPI-Anwendung. Start (Server): uvicorn app.main:app --host 127.0.0.1 --port 8000 --workers 1 --no-access-log Caddy terminiert HTTPS und leitet an 127.0.0.1:8000 weiter. """ from contextlib import asynccontextmanager from fastapi import FastAPI, Request from fastapi.staticfiles import StaticFiles from . import admin, db, gaeste, player_api from .config import konfig # Streng: nur eigene Skripte/Styles, Cover-Bilder vom Spotify-CDN, Turnstile von Cloudflare CSP = ("default-src 'self'; script-src 'self' https://challenges.cloudflare.com; style-src 'self'; " "img-src 'self' https://i.scdn.co data:; frame-src https://challenges.cloudflare.com; " "connect-src 'self'; frame-ancestors 'none'; base-uri 'none'; form-action 'self'") @asynccontextmanager async def lifespan(app: FastAPI): k = konfig() db.initialisieren(k.db_pfad, k.playlist_id) yield # Keine automatische API-Doku öffentlich ausliefern app = FastAPI(title="Audiofy – Musikwünsche", lifespan=lifespan, docs_url=None, redoc_url=None, openapi_url=None) @app.middleware("http") async def sicherheits_header(request: Request, call_next): antwort = await call_next(request) antwort.headers.setdefault("Content-Security-Policy", CSP) if request.url.path.startswith("/api/"): antwort.headers.setdefault("Cache-Control", "no-store") return antwort app.include_router(player_api.router, prefix="/api/player") app.include_router(player_api.router, prefix="/api/pi", include_in_schema=False) # alter Pfad, für ältere Player app.include_router(gaeste.router) app.include_router(admin.router) app.mount("/static", StaticFiles(directory=gaeste.STATIC), name="static") @app.get("/healthz") def healthz() -> dict: return {"ok": True}