From 82f2143dd193db948d48fdb6e691c259b232db78 Mon Sep 17 00:00:00 2001 From: florianfederspiel Date: Tue, 12 May 2026 18:25:30 +0200 Subject: [PATCH] =?UTF-8?q?MCP=20Tokenverwaltung=20in=20Firmeneinstellunge?= =?UTF-8?q?n=20erg=C3=A4nzen?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- frontend/pages/settings/tenant.vue | 211 +++++++++++++++++++++++++++++ 1 file changed, 211 insertions(+) diff --git a/frontend/pages/settings/tenant.vue b/frontend/pages/settings/tenant.vue index 8c63905..3b626d6 100644 --- a/frontend/pages/settings/tenant.vue +++ b/frontend/pages/settings/tenant.vue @@ -5,6 +5,7 @@ import { } from "~/composables/useTaxEvaluation" const auth = useAuthStore() +const toast = useToast() const defaultFeatures = { objects: true, calendar: true, @@ -119,6 +120,17 @@ const itemInfo = ref({ projectTypes: [] }) +const canManageMcpTokens = computed(() => Boolean(auth.user?.is_admin || auth.hasPermission("mcp.tokens.write"))) +const mcpTokens = ref([]) +const mcpTokensLoading = ref(false) +const mcpTokenCreating = ref(false) +const mcpTokenDeletingId = ref(null) +const createdMcpToken = ref("") +const mcpTokenForm = reactive({ + name: "Codex MCP Token", + expiresAt: "" +}) + const setupPage = async () => { itemInfo.value = auth.activeTenantData console.log(itemInfo.value) @@ -153,7 +165,86 @@ const saveFeatures = async () => { await updateTenant({features: features.value}) } +const formatMcpTokenDate = (value) => { + if (!value) return "Nie" + + return new Date(value).toLocaleString("de-DE", { + day: "2-digit", + month: "2-digit", + year: "numeric", + hour: "2-digit", + minute: "2-digit" + }) +} + +const loadMcpTokens = async () => { + if (!canManageMcpTokens.value) return + + mcpTokensLoading.value = true + try { + const res = await useNuxtApp().$api("/api/mcp/tokens") + mcpTokens.value = res?.rows || [] + } catch (error) { + toast.add({ title: "MCP Tokens konnten nicht geladen werden", color: "error" }) + } finally { + mcpTokensLoading.value = false + } +} + +const createMcpToken = async () => { + if (!mcpTokenForm.name?.trim()) { + toast.add({ title: "Name fehlt", description: "Bitte gib einen Namen für den Token an.", color: "orange" }) + return + } + + mcpTokenCreating.value = true + createdMcpToken.value = "" + + try { + const res = await useNuxtApp().$api("/api/mcp/tokens", { + method: "POST", + body: { + name: mcpTokenForm.name.trim(), + expiresAt: mcpTokenForm.expiresAt || null + } + }) + + createdMcpToken.value = res?.token || "" + toast.add({ title: "MCP Token erstellt", color: "success" }) + await loadMcpTokens() + } catch (error) { + toast.add({ title: "MCP Token konnte nicht erstellt werden", color: "error" }) + } finally { + mcpTokenCreating.value = false + } +} + +const copyCreatedMcpToken = async () => { + if (!createdMcpToken.value) return + + await navigator.clipboard.writeText(createdMcpToken.value) + toast.add({ title: "Token kopiert", color: "success" }) +} + +const deactivateMcpToken = async (token) => { + if (!token?.id || !confirm(`MCP Token "${token.name}" deaktivieren?`)) return + + mcpTokenDeletingId.value = token.id + try { + await useNuxtApp().$api(`/api/mcp/tokens/${token.id}`, { + method: "DELETE" + }) + toast.add({ title: "MCP Token deaktiviert", color: "success" }) + await loadMcpTokens() + } catch (error) { + toast.add({ title: "MCP Token konnte nicht deaktiviert werden", color: "error" }) + } finally { + mcpTokenDeletingId.value = null + } +} + setupPage() +onMounted(loadMcpTokens)