import { PermissionAction } from '@supabase/shared-types/out/constants' import { useMemo, useRef } from 'react' import { toast } from 'sonner' import { useParams } from 'common' import { AlertError } from 'components/ui/AlertError' import { FormHeader } from 'components/ui/Forms/FormHeader' import { NoPermission } from 'components/ui/NoPermission' import { useAPIKeyDeleteMutation } from 'data/api-keys/api-key-delete-mutation' import { APIKeysData, useAPIKeysQuery } from 'data/api-keys/api-keys-query' import { useAsyncCheckPermissions } from 'hooks/misc/useCheckPermissions' import { handleErrorOnDelete, useQueryStateWithSelect } from 'hooks/misc/useQueryStateWithSelect' import { Card, Table, TableBody, TableCell, TableFooter, TableHead, TableHeader, TableRow, } from 'ui' import { Admonition, GenericSkeletonLoader } from 'ui-patterns' import { APIKeyRow } from './APIKeyRow' import { CreatePublishableAPIKeyDialog } from './CreatePublishableAPIKeyDialog' export const PublishableAPIKeys = () => { const { ref: projectRef } = useParams() const { can: canReadAPIKeys, isLoading: isLoadingPermissions } = useAsyncCheckPermissions( PermissionAction.SECRETS_READ, '*' ) const { data: apiKeysData = [], error, isPending: isLoadingApiKeys, isError: isErrorApiKeys, } = useAPIKeysQuery({ projectRef, reveal: false }, { enabled: canReadAPIKeys }) const newApiKeys = useMemo( () => apiKeysData.filter(({ type }) => type === 'publishable' || type === 'secret') ?? [], [apiKeysData] ) const hasApiKeys = newApiKeys.length > 0 const publishableApiKeys = useMemo( () => apiKeysData?.filter( (key): key is Extract => key.type === 'publishable' ) ?? [], [apiKeysData] ) // Track the ID being deleted to exclude it from error checking const deletingAPIKeyIdRef = useRef(null) const { value: apiKeyToDelete, setValue: setAPIKeyToDelete } = useQueryStateWithSelect({ urlKey: 'deletePublishableKey', select: (id: string) => (id ? publishableApiKeys?.find((key) => key.id === id) : undefined), enabled: !!publishableApiKeys?.length, onError: (_error, selectedId) => { handleErrorOnDelete(deletingAPIKeyIdRef, selectedId, `API Key not found`) }, }) const { mutate: deleteAPIKey, isPending: isDeletingAPIKey } = useAPIKeyDeleteMutation({ onSuccess: () => { toast.success('Successfully deleted publishable key') setAPIKeyToDelete(null) }, onError: () => { deletingAPIKeyIdRef.current = null }, }) const onDeleteAPIKey = (apiKey: Extract) => { if (!projectRef) return console.error('Project ref is required') if (!apiKey.id) return console.error('API key ID is required') deletingAPIKeyIdRef.current = apiKey.id deleteAPIKey({ projectRef, id: apiKey.id }) } return (
} /> {!canReadAPIKeys && !isLoadingPermissions ? ( ) : isLoadingApiKeys || isLoadingPermissions ? ( ) : isErrorApiKeys ? ( ) : ( Name API Key {hasApiKeys && publishableApiKeys.length === 0 && (

No publishable keys created yet

)} {publishableApiKeys.map((apiKey) => ( onDeleteAPIKey(apiKey)} setKeyToDelete={setAPIKeyToDelete} /> ))}

Publishable keys can be safely shared publicly

)}
) }