shinkan-jinkendo/backend/routers/media_assets.py
Lars 8ac723eafe
All checks were successful
Deploy Development / deploy (push) Successful in 34s
Test Suite / pytest-backend (push) Successful in 23s
Test Suite / lint-backend (push) Successful in 0s
Test Suite / build-frontend (push) Successful in 6s
Test Suite / playwright-tests (push) Successful in 24s
feat: enhance media lifecycle management and inline media integration
- Implemented media lifecycle management with new API endpoints for handling asset states (trash_soft, trash_hidden, recover, purge), improving media governance.
- Updated frontend components to filter and display media based on lifecycle states, enhancing user experience and visibility.
- Enhanced documentation in MEDIA_ASSETS_AND_ARCHIVE_SPEC.md to include guidelines for inline media references in exercise texts, establishing a clear implementation plan.
- Incremented version to 0.8.42, reflecting the latest changes in media handling and lifecycle management.
2026-05-07 12:55:50 +02:00

61 lines
2.2 KiB
Python

"""Lifecycle für media_assets (Papierkorb) — MEDIA_ASSETS_AND_ARCHIVE_SPEC §5."""
from __future__ import annotations
from typing import Literal
from fastapi import APIRouter, Depends, HTTPException
from pydantic import BaseModel
from db import get_db, get_cursor
from tenant_context import TenantContext, get_tenant_context
from media_lifecycle import (
assert_can_manage_media_asset_lifecycle,
fetch_media_asset_row,
purge_media_asset,
transition_recover_from_hidden,
transition_to_trash_hidden,
transition_to_trash_soft,
)
router = APIRouter(prefix="/api/media-assets", tags=["media-assets"])
class MediaLifecycleBody(BaseModel):
action: Literal["trash_soft", "trash_hidden", "recover", "purge"]
@router.post("/{asset_id}/lifecycle")
def post_media_asset_lifecycle(
asset_id: int,
body: MediaLifecycleBody,
tenant: TenantContext = Depends(get_tenant_context),
):
"""Papierkorb-Übergänge (manuell). Rechte gemäß Spec §5.2."""
with get_db() as conn:
cur = get_cursor(conn)
asset = fetch_media_asset_row(cur, asset_id)
if not asset:
raise HTTPException(status_code=404, detail="Medium nicht gefunden")
assert_can_manage_media_asset_lifecycle(cur, tenant, asset)
action = body.action
if action == "trash_soft":
return transition_to_trash_soft(cur, conn, asset_id)
if action == "trash_hidden":
return transition_to_trash_hidden(cur, conn, asset_id)
if action == "recover":
return transition_recover_from_hidden(cur, conn, asset_id)
if action == "purge":
state = (asset.get("lifecycle_state") or "").strip().lower()
if state != "trash_hidden":
raise HTTPException(
status_code=400,
detail="Nur ausgeblendete Medien (Stufe 2) dürfen endgültig gelöscht werden",
)
ok = purge_media_asset(cur, conn, asset_id)
if not ok:
raise HTTPException(status_code=400, detail="Löschen nicht möglich")
return {"ok": True, "purged": asset_id}
raise HTTPException(status_code=500, detail="Interner Fehler: lifecycle action")