All checks were successful
Deploy Development / deploy (push) Successful in 44s
Test Suite / pytest-backend (push) Successful in 44s
Test Suite / lint-backend (push) Successful in 0s
Test Suite / build-frontend (push) Successful in 13s
Test Suite / k6 /health Baseline (push) Successful in 34s
Test Suite / playwright-tests (push) Successful in 1m13s
- Added documentation to the `catalog_prompt_slots.py` file to clarify its role as a global admin catalog requiring authentication and admin role, without tenant context. - Updated the `check_access_layer_hints.py` script to include `catalog_prompt_slots.py` in the list of exempt routers, ensuring proper access control for admin functionalities.
98 lines
3.5 KiB
Python
98 lines
3.5 KiB
Python
"""
|
||
API: Katalog-Prompt-Slots (Stammdaten × Slot-Typ).
|
||
|
||
Globaler Admin-Katalog (wie catalogs.py) — require_auth + Admin-Rolle, kein TenantContext.
|
||
Eingetragen in backend/scripts/check_access_layer_hints.py EXEMPT_ROUTERS.
|
||
"""
|
||
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
|