"use client"; /** * TASK-0133 — API keys tab (workspace-scoped). * * API keys live per-workspace in the API (`/workspaces/:id/api-keys`), so the * console operates on the workspace chosen in the shared picker. This tab * REUSES the same building-block components as the workspace Developers * settings (`ApiKeyTable` / `ApiKeyCreateDialog` / `ApiKeyRevealDialog`) — * no duplicated table/dialog code. * * Authorization: the API keys routes are workspace-scoped and require * `ADMIN` workspace role; an org admin is an OWNER of every org workspace, so * the org console can manage them. A non-admin caller is rejected by the API. */ import { useState } from "react"; import { useMutation, useQuery, useQueryClient } from "@tanstack/react-query"; import { Plus } from "lucide-react"; import { orgWorkspaceApi } from "@/lib/org-admin"; import { API_BASE } from "@/lib/constants"; import { toast } from "@/stores/ui-store"; import { Section } from "@/components/settings/settings-shell"; import { ApiKeyTable } from "@/components/settings/api-keys/ApiKeyTable"; import { ApiKeyCreateDialog } from "@/components/settings/api-keys/ApiKeyCreateDialog"; import { ApiKeyRevealDialog } from "@/components/settings/api-keys/ApiKeyRevealDialog"; export function AdminApiKeysTab({ workspaceId }: { workspaceId: string | null }) { const qc = useQueryClient(); const [createOpen, setCreateOpen] = useState(false); const [reveal, setReveal] = useState<{ rawKey: string; name: string } | null>(null); const [revokingId, setRevokingId] = useState(null); const keys = useQuery({ queryKey: ["workspaces", workspaceId, "api-keys"], queryFn: () => orgWorkspaceApi.listApiKeys(workspaceId!), enabled: Boolean(workspaceId), retry: false, }); const create = useMutation({ mutationFn: (input: { name: string; scopes: string[]; rateLimit?: number; expiresAt?: string }) => orgWorkspaceApi.createApiKey(workspaceId!, input), onSuccess: () => qc.invalidateQueries({ queryKey: ["workspaces", workspaceId, "api-keys"] }), }); const revoke = useMutation({ mutationFn: (id: string) => orgWorkspaceApi.deleteApiKey(workspaceId!, id), onMutate: (id) => setRevokingId(id), onSettled: () => setRevokingId(null), onSuccess: () => qc.invalidateQueries({ queryKey: ["workspaces", workspaceId, "api-keys"] }), onError: (e) => toast("error", "Could not revoke the key", (e as Error).message), }); if (!workspaceId) { return (

No workspace selected.

); } return (
revoke.mutate(row.id)} revokingId={revokingId} onCreate={() => setCreateOpen(true)} /> setCreateOpen(false)} onCreated={(k) => { setCreateOpen(false); setReveal(k); }} create={create.mutateAsync} isPending={create.isPending} /> {reveal ? ( setReveal(null)} /> ) : null}
); }