feat(api-tokens): UI fuer Bearer-Tokens — Modul 3/16 des UI-Backlogs

Backend existierte vollstaendig (POST create mit Einmal-Plaintext-Anzeige,
GET list ohne Hashes, DELETE revoke; mcp:read/mcp:write via mcp_server-
Plugin registriert), Frontend hatte 0% Abdeckung.

- api/apiTokens.ts: TanStack-Hooks (useApiTokens, create mit
  ApiTokenCreated-Response inkl. Einmal-Token, revoke)
- pages/ApiTokens.tsx: Token-Karten (Name, Scope-Badges, Ablauf, zuletzt
  genutzt, Abgelaufen-Badge), Create-Dialog (Name, Scopes als
  Komma-Liste, optionale Gueltigkeit in Tagen), EINMALIGE
  Plaintext-Anzeige mit Copy-Button und Warnung, Revoke mit Confirm —
  Aktionen hinter mcp:write gegated
- Platzierung: Settings-Subpage /settings/api-tokens (statisch, Core-Route)
  + Settings-Nav-Item (true-core-settings-Muster)
- i18n apiTokens.* de/en

Verifikation: Vitest 8/8 (Rendering, Scopes, Ablauf-Badge,
Permission-Gating, Create-Flow mit Reveal-Dialog, Revoke) · tsc exit 0 ·
production build exit 0.
This commit is contained in:
Agent Zero
2026-09-13 09:23:01 +02:00
parent f9ff92bec9
commit 4bdc6c66c7
7 changed files with 583 additions and 0 deletions
+25
View File
@@ -1659,5 +1659,30 @@
"deleteConfirm": "Diese Delegation wirklich loeschen?",
"empty": "Keine Delegationen in dieser Ansicht.",
"loadError": "Delegationen konnten nicht geladen werden."
},
"apiTokens": {
"title": "API-Tokens",
"create": "Token erstellen",
"createTitle": "API-Token erstellen",
"createSubmit": "Erstellen",
"name": "Name",
"namePlaceholder": "z.B. CI-Integration",
"scopes": "Scopes",
"scopesPlaceholder": "z.B. contacts:read, mail:read",
"scopesHelper": "Kommagetrennte Liste. Leer = alle Scopes.",
"expiresInDays": "Gueltigkeit (Tage, optional)",
"expiresPlaceholder": "z.B. 30",
"revealTitle": "Token erstellt",
"revealWarning": "Dies ist der einzige Moment, in dem das Token im Klartext sichtbar ist. Jetzt kopieren und sicher speichern — spaeter nicht mehr abrufbar.",
"copy": "Kopieren",
"done": "Fertig",
"created": "Erstellt:",
"expires": "Laeuft ab:",
"lastUsed": "Zuletzt genutzt:",
"expiredBadge": "Abgelaufen",
"revoke": "Widerrufen",
"revokeConfirm": "Token '{{name}}' wirklich widerrufen? Anwendungen damit verlieren sofort den Zugriff.",
"empty": "Keine API-Tokens vorhanden.",
"loadError": "API-Tokens konnten nicht geladen werden."
}
}
+25
View File
@@ -1659,5 +1659,30 @@
"deleteConfirm": "Really delete this delegation?",
"empty": "No delegations in this view.",
"loadError": "Failed to load delegations."
},
"apiTokens": {
"title": "API Tokens",
"create": "Create token",
"createTitle": "Create API token",
"createSubmit": "Create",
"name": "Name",
"namePlaceholder": "e.g. CI integration",
"scopes": "Scopes",
"scopesPlaceholder": "e.g. contacts:read, mail:read",
"scopesHelper": "Comma-separated list. Empty = all scopes.",
"expiresInDays": "Validity (days, optional)",
"expiresPlaceholder": "e.g. 30",
"revealTitle": "Token created",
"revealWarning": "This is the only moment the plaintext token is visible. Copy and store it safely now — it cannot be retrieved later.",
"copy": "Copy",
"done": "Done",
"created": "Created:",
"expires": "Expires:",
"lastUsed": "Last used:",
"expiredBadge": "Expired",
"revoke": "Revoke",
"revokeConfirm": "Really revoke token '{{name}}'? Applications using it lose access immediately.",
"empty": "No API tokens yet.",
"loadError": "Failed to load API tokens."
}
}