2026-06-15 12:13:15 +02:00
|
|
|
|
"""
|
|
|
|
|
|
API: Katalog-Prompt-Slots (Stammdaten × Slot-Typ).
|
2026-06-15 15:27:03 +02:00
|
|
|
|
|
|
|
|
|
|
Globaler Admin-Katalog (wie catalogs.py) — require_auth + Admin-Rolle, kein TenantContext.
|
|
|
|
|
|
Eingetragen in backend/scripts/check_access_layer_hints.py EXEMPT_ROUTERS.
|
2026-06-15 12:13:15 +02:00
|
|
|
|
"""
|
|
|
|
|
|
from __future__ import annotations
|
|
|
|
|
|
|
|
|
|
|
|
from typing import Any, Dict, Optional
|
|
|
|
|
|
|
|
|
|
|
|
from fastapi import APIRouter, Depends, HTTPException
|
|
|
|
|
|
from pydantic import BaseModel, Field
|
|
|
|
|
|
|
|
|
|
|
|
from auth import require_auth
|
|
|
|
|
|
from catalog_prompt_slots import (
|
|
|
|
|
|
CATALOG_KINDS,
|
|
|
|
|
|
get_catalog_entry_slots,
|
|
|
|
|
|
list_slot_type_definitions,
|
|
|
|
|
|
upsert_catalog_entry_slots,
|
|
|
|
|
|
)
|
|
|
|
|
|
from db import get_cursor, get_db
|
|
|
|
|
|
|
|
|
|
|
|
router = APIRouter(prefix="/api", tags=["catalog_prompt_slots"])
|
|
|
|
|
|
|
|
|
|
|
|
_VALID_KINDS = frozenset(c.kind for c in CATALOG_KINDS)
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
class CatalogPromptSlotsBody(BaseModel):
|
|
|
|
|
|
slots: Dict[str, Optional[str]] = Field(default_factory=dict)
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
def _require_admin(session: dict = Depends(require_auth)) -> dict:
|
|
|
|
|
|
role = (session.get("role") or "").strip().lower()
|
|
|
|
|
|
if role not in ("admin", "superadmin"):
|
|
|
|
|
|
raise HTTPException(status_code=403, detail="Nur Admins")
|
|
|
|
|
|
return session
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
def _slots_table_ready(cur) -> bool:
|
|
|
|
|
|
cur.execute("SELECT to_regclass(%s)::text AS t", ("public.catalog_prompt_slots",))
|
|
|
|
|
|
row = cur.fetchone()
|
|
|
|
|
|
if not row:
|
|
|
|
|
|
return False
|
|
|
|
|
|
val = row.get("t") if isinstance(row, dict) else row[0]
|
|
|
|
|
|
return val is not None and str(val).strip() != ""
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
@router.get("/catalog-prompt-slot-types")
|
|
|
|
|
|
def api_list_catalog_prompt_slot_types(session: dict = Depends(_require_admin)):
|
|
|
|
|
|
with get_db() as conn:
|
|
|
|
|
|
cur = get_cursor(conn)
|
|
|
|
|
|
if not _slots_table_ready(cur):
|
|
|
|
|
|
raise HTTPException(status_code=503, detail="Tabelle catalog_prompt_slots fehlt.")
|
|
|
|
|
|
return {"slot_types": list_slot_type_definitions(cur)}
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
@router.get("/catalog-prompt-slots/{catalog_kind}/{catalog_id}")
|
|
|
|
|
|
def api_get_catalog_prompt_slots(
|
|
|
|
|
|
catalog_kind: str,
|
|
|
|
|
|
catalog_id: int,
|
|
|
|
|
|
session: dict = Depends(_require_admin),
|
|
|
|
|
|
):
|
|
|
|
|
|
kind = (catalog_kind or "").strip().lower()
|
|
|
|
|
|
if kind not in _VALID_KINDS:
|
|
|
|
|
|
raise HTTPException(status_code=400, detail=f"Unbekannter catalog_kind: {catalog_kind!r}")
|
|
|
|
|
|
with get_db() as conn:
|
|
|
|
|
|
cur = get_cursor(conn)
|
|
|
|
|
|
if not _slots_table_ready(cur):
|
|
|
|
|
|
raise HTTPException(status_code=503, detail="Tabelle catalog_prompt_slots fehlt.")
|
|
|
|
|
|
try:
|
|
|
|
|
|
return get_catalog_entry_slots(cur, kind, catalog_id)
|
|
|
|
|
|
except LookupError as exc:
|
|
|
|
|
|
raise HTTPException(status_code=404, detail=str(exc)) from exc
|
|
|
|
|
|
except ValueError as exc:
|
|
|
|
|
|
raise HTTPException(status_code=400, detail=str(exc)) from exc
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
@router.put("/catalog-prompt-slots/{catalog_kind}/{catalog_id}")
|
|
|
|
|
|
def api_put_catalog_prompt_slots(
|
|
|
|
|
|
catalog_kind: str,
|
|
|
|
|
|
catalog_id: int,
|
|
|
|
|
|
body: CatalogPromptSlotsBody,
|
|
|
|
|
|
session: dict = Depends(_require_admin),
|
|
|
|
|
|
):
|
|
|
|
|
|
kind = (catalog_kind or "").strip().lower()
|
|
|
|
|
|
if kind not in _VALID_KINDS:
|
|
|
|
|
|
raise HTTPException(status_code=400, detail=f"Unbekannter catalog_kind: {catalog_kind!r}")
|
|
|
|
|
|
with get_db() as conn:
|
|
|
|
|
|
cur = get_cursor(conn)
|
|
|
|
|
|
if not _slots_table_ready(cur):
|
|
|
|
|
|
raise HTTPException(status_code=503, detail="Tabelle catalog_prompt_slots fehlt.")
|
|
|
|
|
|
try:
|
|
|
|
|
|
return upsert_catalog_entry_slots(cur, kind, catalog_id, body.slots or {})
|
|
|
|
|
|
except LookupError as exc:
|
|
|
|
|
|
raise HTTPException(status_code=404, detail=str(exc)) from exc
|
|
|
|
|
|
except ValueError as exc:
|
|
|
|
|
|
raise HTTPException(status_code=400, detail=str(exc)) from exc
|