()
| 372 | } |
| 373 | |
| 374 | function OrgApiKeysSectionBody() { |
| 375 | const result = useAtomValue(orgApiKeysAtom); |
| 376 | const refresh = useAtomRefresh(orgApiKeysAtom); |
| 377 | const doCreate = useAtomSet(createOrgApiKey, { mode: "promiseExit" }); |
| 378 | const doRevoke = useAtomSet(revokeOrgApiKey, { mode: "promiseExit" }); |
| 379 | const [createOpen, setCreateOpen] = useState(false); |
| 380 | // Same remount-per-open discipline as the personal dialog above. |
| 381 | const [openCount, setOpenCount] = useState(0); |
| 382 | const [confirmRevoke, setConfirmRevoke] = useState<ApiKeySummary | null>(null); |
| 383 | const [revokingId, setRevokingId] = useState<string | null>(null); |
| 384 | |
| 385 | const handleCreate = async (name: string): Promise<CreatedKey | null> => { |
| 386 | const exit = await doCreate({ payload: { name }, reactivityKeys: orgApiKeyWriteKeys }); |
| 387 | trackEvent("org_api_key_created", { success: Exit.isSuccess(exit) }); |
| 388 | if (Exit.isSuccess(exit)) { |
| 389 | toast.success("Organization key created"); |
| 390 | return exit.value; |
| 391 | } |
| 392 | toast.error("Failed to create organization key"); |
| 393 | return null; |
| 394 | }; |
| 395 | |
| 396 | const handleRevoke = async (key: ApiKeySummary) => { |
| 397 | setConfirmRevoke(null); |
| 398 | setRevokingId(key.id); |
| 399 | const exit = await doRevoke({ |
| 400 | params: { apiKeyId: key.id }, |
| 401 | reactivityKeys: orgApiKeyWriteKeys, |
| 402 | }); |
| 403 | setRevokingId(null); |
| 404 | trackEvent("org_api_key_revoked", { success: Exit.isSuccess(exit) }); |
| 405 | if (Exit.isSuccess(exit)) { |
| 406 | toast.success(`Revoked ${key.name}`); |
| 407 | return; |
| 408 | } |
| 409 | toast.error("Failed to revoke organization key"); |
| 410 | }; |
| 411 | |
| 412 | return ( |
| 413 | <section className="mt-10"> |
| 414 | <div className="mb-4 flex items-start justify-between gap-4"> |
| 415 | <div> |
| 416 | <h2 className="text-sm font-medium text-foreground">Organization keys</h2> |
| 417 | <p className="mt-0.5 max-w-2xl text-sm text-muted-foreground"> |
| 418 | Read-only keys owned by the organization, not a member. They authenticate the admin API |
| 419 | (who are my users, what have they connected) and cannot act as anyone or write anything. |
| 420 | Admins only. |
| 421 | </p> |
| 422 | </div> |
| 423 | <Button |
| 424 | size="sm" |
| 425 | onClick={() => { |
| 426 | setOpenCount((count) => count + 1); |
| 427 | setCreateOpen(true); |
| 428 | }} |
| 429 | > |
| 430 | <span aria-hidden="true">+</span> |
| 431 | New org key |
nothing calls this directly
no test coverage detected