All checks were successful
Deploy Development / deploy (push) Successful in 35s
Test Suite / pytest-backend (push) Successful in 24s
Test Suite / lint-backend (push) Successful in 0s
Test Suite / build-frontend (push) Successful in 6s
Test Suite / playwright-tests (push) Successful in 27s
- Added new documentation for media assets and lifecycle management, establishing a single source of truth in MEDIA_ASSETS_AND_ARCHIVE_SPEC.md. - Updated project status to reflect the addition of media archive and lifecycle governance. - Introduced a new API endpoint for platform media storage, allowing superadmin access for media management. - Enhanced exercise media handling with improved database integration for media assets, including deduplication and effective media root resolution. - Updated frontend API utilities to support new media storage functionalities, ensuring seamless integration with the backend. - Incremented version to 0.8.41, reflecting the latest changes and improvements in media handling.
58 lines
1.7 KiB
Python
58 lines
1.7 KiB
Python
"""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
|