Audiofy – Musikwünsche für Partys

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>
This commit is contained in:
2026-10-17 19:16:39 +01:00
co-authored by Claude Opus 5.5
commit 3610de7bae
45 changed files with 5666 additions and 0 deletions
+50
View File
@@ -0,0 +1,50 @@
"""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}