import { useEffect, useState } from 'react' import { api } from '../api.js' import { useAuth } from '../context/AuthContext.jsx' const emptyRole = () => ({ role: '', name: '', url: '', model: '', zdr: true, no_train: true, key: '' }) function statusLabel(role) { if (role.role === 'detect' && !role.url) return 'nicht konfiguriert, fail-closed' if (role.role === 'detect' && role.url && !role.local && role.ready) { return role.remote_plaintext_allowed === false ? 'extern konfiguriert, Produktiv blockiert' : 'bereit (nur Development/Test, extern)' } if (role.ready) return 'bereit' if (!role.key_present && !role.local) return 'Key fehlt' if (!role.url) return 'URL fehlt' return 'nicht bereit' } export default function AdminProvidersPage() { const { session } = useAuth() const [data, setData] = useState(null) const [forms, setForms] = useState({}) const [error, setError] = useState('') const [notice, setNotice] = useState('') const [saving, setSaving] = useState('') const load = () => api('/api/admin/providers', { token: session.token }) .then((payload) => { setData(payload) const next = {} for (const role of payload.roles) { next[role.role] = { role: role.role, name: role.name || '', url: role.url || '', model: role.model || '', zdr: Boolean(role.zdr), no_train: Boolean(role.no_train), key: '' } } setForms(next) }) .catch((e) => setError(e.message)) useEffect(() => { load() }, [session.token]) const update = (role, field, value) => { setForms((prev) => ({ ...prev, [role]: { ...(prev[role] || emptyRole()), [field]: value } })) } const save = async (role) => { const form = forms[role] if (!form) return setError('') setNotice('') setSaving(role) try { const body = { name: form.name, url: form.url, model: form.model, zdr: form.zdr, no_train: form.no_train } if (form.key.trim()) body.key = form.key.trim() const payload = await api(`/api/admin/providers/${role}`, { token: session.token, method: 'PUT', body }) setData(payload) setForms((prev) => ({ ...prev, [role]: { ...prev[role], key: '' } })) setNotice(form.key.trim() ? `${role}: gespeichert, Key in .env geschrieben.` : `${role}: gespeichert.`) } catch (err) { setError(err.message) } finally { setSaving('') } } return (

Schnittstellen

Zwei eigene Einträge: Maskierung (vollständige semantische Detection) und Sprachmodell (Dialogzug und Journalentwurf). Externes Klartext-Detect ist nur in Development/Test zulässig. Produktiv ist ein lokales Detect-Modell nötig. Ohne Detection kein Generate. Der Secret-Key wird nie angezeigt und nie in der Datenbank gespeichert.

{error &&

{error}

} {notice &&

{notice}

} {data && ( <> {data.roles.map((role) => { const form = forms[role.role] || emptyRole() return (
{ e.preventDefault(); save(role.role) }} >

{role.title || role.role}

{role.task &&

{role.task}

}
Status
{statusLabel(role)}
Key
{role.key_present ? (role.key_source === 'generate' ? 'über Sprachmodell' : 'gesetzt') : 'fehlt'}
Modus
{role.local ? 'lokal' : 'extern'} · {role.mode}
) })}

Ohne ZDR und ohne Trainingsverbot bleibt das Sprachmodell fail-closed. Maskierung wählt keine Operation. Gateway lässt sich darüber nicht umgehen.

)}
) }