"""Effektives Medien-Wurzelverzeichnis (MEDIA_ROOT + Superadmin-relativer Pfad). Siehe MEDIA_ASSETS_AND_ARCHIVE_SPEC.md §7.1.""" from __future__ import annotations import os import re from pathlib import Path from typing import Any, Optional def _default_media_root() -> Path: return Path(os.getenv("MEDIA_ROOT", str(Path(__file__).resolve().parent / "media"))) def normalize_local_relative_root(raw: str) -> str: s = (raw or "").strip().replace("\\", "/") s = s.strip("/") if not s: return "" if ".." in s.split("/"): raise ValueError("Pfad darf nicht '..' enthalten") if s.startswith("/"): raise ValueError("Nur relativer Pfad erlaubt") return s def get_effective_media_root(cur: Any) -> Path: """ MEDIA_ROOT aus ENV mit optionalem local_relative_root aus platform_media_storage (id=1). """ base = _default_media_root().resolve() rel = "" try: cur.execute( "SELECT local_relative_root FROM platform_media_storage WHERE id = 1", ) row = cur.fetchone() if row is not None: v = row["local_relative_root"] if isinstance(row, dict) else row[0] rel = normalize_local_relative_root(str(v or "")) except Exception: rel = "" if not rel: return base return (base / rel).resolve() def path_under_media_root(media_root: Path, storage_key: str) -> Optional[Path]: """Gibt absoluten Pfad zurück oder None bei Path-Traversal.""" key = (storage_key or "").strip().replace("\\", "/").lstrip("/") if not key or ".." in key.split("/"): return None p = (media_root / key).resolve() try: p.relative_to(media_root.resolve()) except ValueError: return None return p