feat: API-Keys-Verwaltung in DB statt appsettings

- Neue Tabelle api_keys (Provider, Name, ApiKey, IsActive)
- Admin-CRUD: GET/POST/PUT/DELETE /api/admin/api-keys
- Aktivieren/Deaktivieren pro Provider (nur einer aktiv)
- Live-Token-Update im AnthropicService ohne Neustart
- Migration: AnthropicToken aus appsettings.Production.json → DB
- AnthropicToken aus ServerSettings-Definitionen + appsettings entfernt
- Frontend: API-Keys-Seite mit Erstellen/Bearbeiten/Löschen/Toggle
- Dashboard: API-Keys-Link für Admins
This commit is contained in:
Meleki committed 2026-03-12 12:07:22 +01:00
1 parent 1228dc9d58
commit fa8deb15b6
11 files changed
+649 -4

No files matched your search

+61
View File
@@ -584,6 +584,67 @@ async function apiSaveServerSettings(data) {
return resp.json();
}
// ── API Keys (Admin) ──
async function apiGetApiKeys() {
const resp = await fetch('/api/admin/api-keys');
if (!resp.ok) throw new Error('Fehler beim Laden der API-Keys');
return resp.json();
}
async function apiCreateApiKey(data) {
const resp = await fetch('/api/admin/api-keys', {
method: 'POST',
headers: { 'Content-Type': 'application/json' },
body: JSON.stringify(data)
});
if (!resp.ok) {
const err = await resp.json().catch(() => ({}));
throw new Error(err.error || 'Fehler beim Erstellen');
}
return resp.json();
}
async function apiUpdateApiKey(keyId, data) {
const resp = await fetch('/api/admin/api-keys/' + keyId, {
method: 'PUT',
headers: { 'Content-Type': 'application/json' },
body: JSON.stringify(data)
});
if (!resp.ok) {
const err = await resp.json().catch(() => ({}));
throw new Error(err.error || 'Fehler beim Aktualisieren');
}
return resp.json();
}
async function apiActivateApiKey(keyId) {
const resp = await fetch('/api/admin/api-keys/' + keyId + '/activate', { method: 'PUT' });
if (!resp.ok) {
const err = await resp.json().catch(() => ({}));
throw new Error(err.error || 'Fehler beim Aktivieren');
}
return resp.json();
}
async function apiDeactivateApiKey(keyId) {
const resp = await fetch('/api/admin/api-keys/' + keyId + '/deactivate', { method: 'PUT' });
if (!resp.ok) {
const err = await resp.json().catch(() => ({}));
throw new Error(err.error || 'Fehler beim Deaktivieren');
}
return resp.json();
}
async function apiDeleteApiKey(keyId) {
const resp = await fetch('/api/admin/api-keys/' + keyId, { method: 'DELETE' });
if (!resp.ok) {
const err = await resp.json().catch(() => ({}));
throw new Error(err.error || 'Fehler beim Löschen');
}
return resp.json();
}
// ── Krea Image API ──
async function apiKreaStatus() {