()
| 61 | }).format(new Date())}`; |
| 62 | |
| 63 | export function ApiKeysPage() { |
| 64 | useExecutorDocumentTitle("API keys"); |
| 65 | const result = useAtomValue(apiKeysAtom); |
| 66 | const refreshApiKeys = useAtomRefresh(apiKeysAtom); |
| 67 | const doCreate = useAtomSet(createApiKey, { mode: "promiseExit" }); |
| 68 | const doRevoke = useAtomSet(revokeApiKey, { mode: "promiseExit" }); |
| 69 | const [createOpen, setCreateOpen] = useState(false); |
| 70 | const [name, setName] = useState(""); |
| 71 | const [createdKey, setCreatedKey] = useState<CreatedKey | null>(null); |
| 72 | const [creating, setCreating] = useState(false); |
| 73 | const [revokingId, setRevokingId] = useState<string | null>(null); |
| 74 | |
| 75 | const handleCreate = async () => { |
| 76 | const trimmed = name.trim(); |
| 77 | if (!trimmed) return; |
| 78 | setCreating(true); |
| 79 | const exit = await doCreate({ payload: { name: trimmed }, reactivityKeys: apiKeyWriteKeys }); |
| 80 | setCreating(false); |
| 81 | trackEvent("api_key_created", { success: Exit.isSuccess(exit) }); |
| 82 | if (Exit.isSuccess(exit)) { |
| 83 | setCreatedKey(exit.value); |
| 84 | setName(""); |
| 85 | toast.success("API key created"); |
| 86 | return; |
| 87 | } |
| 88 | toast.error("Failed to create API key"); |
| 89 | }; |
| 90 | |
| 91 | const handleRevoke = async (key: ApiKeySummary) => { |
| 92 | setRevokingId(key.id); |
| 93 | const exit = await doRevoke({ params: { apiKeyId: key.id }, reactivityKeys: apiKeyWriteKeys }); |
| 94 | setRevokingId(null); |
| 95 | trackEvent("api_key_revoked", { success: Exit.isSuccess(exit) }); |
| 96 | if (Exit.isSuccess(exit)) { |
| 97 | toast.success(`Revoked ${key.name}`); |
| 98 | return; |
| 99 | } |
| 100 | toast.error("Failed to revoke API key"); |
| 101 | }; |
| 102 | |
| 103 | const closeCreate = (open: boolean) => { |
| 104 | setCreateOpen(open); |
| 105 | if (!open) { |
| 106 | setName(""); |
| 107 | setCreatedKey(null); |
| 108 | setCreating(false); |
| 109 | } |
| 110 | }; |
| 111 | |
| 112 | return ( |
| 113 | <PageContainer> |
| 114 | <PageHeader |
| 115 | title="API keys" |
| 116 | description="User keys for accessing the Executor API and MCP endpoint from scripts and tools." |
| 117 | actions={ |
| 118 | <Button |
| 119 | onClick={() => { |
| 120 | setName(defaultApiKeyName()); |
nothing calls this directly
no test coverage detected