Server (Website, Admin-Bereich, Player-API) und Player (raspotify + Controller) mit Install-Skripten für Debian-Systeme. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
51 lines
1.7 KiB
Python
51 lines
1.7 KiB
Python
"""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}
|