"""Schnellzugriff-Links (Jellyfin, Audiobookshelf, Homer) – pro Konto.""" import re from urllib.parse import urlparse from fastapi import APIRouter, Body, Depends from .auth import current_user from .db import transaction router = APIRouter() KEYS = ("jellyfin", "audiobookshelf", "homer") MAX_LEN = 500 HOSTNAME = re.compile(r"[A-Za-z0-9.-]+|\[[0-9A-Fa-f:.]+\]") class ValidationError(ValueError): pass def normalize_url(value, field: str) -> str: """Prüft eine http(s)-Adresse; ergänzt fehlendes "http://". Leer bleibt leer.""" v = value.strip() if isinstance(value, str) else "" if not v: return "" if "://" not in v: v = f"http://{v}" # "jellyfin.local:8096" -> "http://jellyfin.local:8096" try: parsed = urlparse(v) host = parsed.netloc.rsplit("@", 1)[-1] host = host[: host.find("]") + 1] if host.startswith("[") else host.split(":")[0] parsed.port # wirft bei ungültigem Port except ValueError: host, parsed = "", None if ( not parsed or parsed.scheme not in ("http", "https") or not HOSTNAME.fullmatch(host) or re.search(r"\s", v) or len(v) > MAX_LEN ): raise ValidationError(f"Ungültige Adresse für {field}.") return v def _get(conn, user_id: int) -> dict: rows = conn.execute("SELECT key, url FROM user_links WHERE user_id = ?", (user_id,)).fetchall() stored = {r["key"]: r["url"] for r in rows} return {key: stored.get(key, "") for key in KEYS} @router.get("/api/links") def get_links(user: dict = Depends(current_user)): with transaction() as conn: return _get(conn, user["id"]) @router.put("/api/links") def update_links(data: dict = Body(...), user: dict = Depends(current_user)): values = {key: normalize_url(data[key], key) for key in KEYS if key in data} with transaction() as conn: for key, url in values.items(): conn.execute("INSERT OR REPLACE INTO user_links VALUES (?, ?, ?)", (user["id"], key, url)) return _get(conn, user["id"])