from fastapi import APIRouter, Depends, HTTPException from pydantic import BaseModel, Field from auth import require_admin_dep from db import get_db from dialogue_store import StoreError, get_conversation, inventory, list_conversations, list_derived_for_conversation, list_messages from provider_settings import ROLES, SettingsError, public_status, set_role_key, upsert_setting from version import APP_VERSION, BUILD_DATE, MODULE_VERSIONS router = APIRouter(prefix="/api/admin", tags=["admin"]) @router.get("/health") def admin_health(session: dict = Depends(require_admin_dep)): with get_db() as conn: users = conn.execute("SELECT COUNT(*) AS n FROM profiles").fetchone()["n"] prompts = conn.execute("SELECT COUNT(*) AS n FROM ai_prompts").fetchone()["n"] features = conn.execute("SELECT COUNT(*) AS n FROM features").fetchone()["n"] dialogue = inventory() providers = public_status() return { "ok": True, "service": "kansho", "version": APP_VERSION, "build_date": BUILD_DATE, "modules": MODULE_VERSIONS, "inventory": { "users": users, "prompts": prompts, "features": features, "dialogue": dialogue, "providers": { role["role"]: {"ready": role["ready"], "key_present": role["key_present"]} for role in providers["roles"] }, }, "session": { "profile_id": session["profile_id"], "role": session["role"], }, } @router.get("/dialogue") def admin_dialogue(session: dict = Depends(require_admin_dep)): return { "instance": inventory(), "own_conversations": list_conversations(session["profile_id"]), "note": "Fremde Lived Experience ist kein Alltagspfad. Detailansicht nur für das eigene Profil.", } @router.get("/dialogue/conversations/{conversation_id}") def admin_conversation(conversation_id: str, session: dict = Depends(require_admin_dep)): try: conv = get_conversation(session["profile_id"], conversation_id) return { "conversation": conv, "original_messages": list_messages(session["profile_id"], conversation_id), "derived_records": list_derived_for_conversation(session["profile_id"], conversation_id), } except StoreError as exc: raise HTTPException(status_code=exc.status_code, detail={"code": exc.code, "message": exc.message}) from exc class ProviderUpdate(BaseModel): name: str = "" url: str = "" model: str = "" zdr: bool = True no_train: bool = True key: str | None = Field(default=None, description="Write-only. Never returned.") @router.get("/providers") def admin_providers(session: dict = Depends(require_admin_dep)): return public_status() @router.put("/providers/{role}") def admin_update_provider(role: str, body: ProviderUpdate, session: dict = Depends(require_admin_dep)): if role not in ROLES: raise HTTPException(status_code=404, detail={"code": "unknown_provider_role", "message": "Unbekannte Provider-Rolle."}) try: upsert_setting( role, name=body.name, url=body.url, model=body.model, zdr=body.zdr, no_train=body.no_train, ) if body.key is not None and body.key.strip(): set_role_key(role, body.key) except SettingsError as exc: raise HTTPException(status_code=exc.status_code, detail={"code": exc.code, "message": exc.message}) from exc return public_status()