import { PermissionAction } from '@supabase/shared-types/out/constants' import { useParams } from 'common' import { Search } from 'lucide-react' import { parseAsString, useQueryState } from 'nuqs' import { useEffect, useMemo, useState } from 'react' import { toast } from 'sonner' import { Badge, Card, Table, TableBody, TableCell, TableHead, TableHeader, TableRow } from 'ui' import { Input } from 'ui-patterns/DataInputs/Input' import { ConfirmationModal } from 'ui-patterns/Dialogs/ConfirmationModal' import { GenericSkeletonLoader } from 'ui-patterns/ShimmeringLoader' import { AddNewSecretForm } from './AddNewSecretForm' import { DefaultEdgeFunctionSecrets } from './DefaultEdgeFunctionSecrets' import { getVisibleDefaultEdgeFunctionSecrets, isInternalEdgeFunctionSecret, } from './DefaultEdgeFunctionSecrets.utils' import EdgeFunctionSecret from './EdgeFunctionSecret' import { EditSecretSheet } from './EditSecretSheet' import { AlertError } from '@/components/ui/AlertError' import { DocsButton } from '@/components/ui/DocsButton' import { NoPermission } from '@/components/ui/NoPermission' import { useSecretsDeleteMutation } from '@/data/secrets/secrets-delete-mutation' import { useSecretsQuery } from '@/data/secrets/secrets-query' import { useAsyncCheckPermissions } from '@/hooks/misc/useCheckPermissions' import { DOCS_URL } from '@/lib/constants' export const EdgeFunctionSecrets = () => { const { ref: projectRef } = useParams() const [searchString, setSearchString] = useState('') const { can: canReadSecrets, isLoading: isLoadingSecretsPermissions } = useAsyncCheckPermissions( PermissionAction.FUNCTIONS_SECRET_READ, '*' ) const { can: canUpdateSecrets } = useAsyncCheckPermissions(PermissionAction.SECRETS_WRITE, '*') const { data = [], error, isPending: isLoading, isSuccess, isError, } = useSecretsQuery({ projectRef: projectRef }, { enabled: canReadSecrets }) const customSecrets = useMemo( () => data.filter((secret) => !isInternalEdgeFunctionSecret(secret.name)), [data] ) const visibleDefaultSecrets = useMemo( () => getVisibleDefaultEdgeFunctionSecrets(new Set(data.map((secret) => secret.name))), [data] ) const [selectedIdToEdit, setSelectedIdToEdit] = useQueryState( 'edit', parseAsString.withOptions({ history: 'push', clearOnDefault: true }) ) const selectedSecretToEdit = useMemo( () => customSecrets.find((secret) => secret.name === selectedIdToEdit), [customSecrets, selectedIdToEdit] ) const [selectedIdToDelete, setSelectedIdToDelete] = useQueryState( 'delete', parseAsString.withOptions({ history: 'push', clearOnDefault: true }) ) const selectedSecretToDelete = useMemo( () => customSecrets.find((secret) => secret.name === selectedIdToDelete), [customSecrets, selectedIdToDelete] ) const { mutate: deleteSecret, isPending: isDeleting, isSuccess: isSuccessDelete, } = useSecretsDeleteMutation({ onSuccess: (_, variables) => { toast.success(`Successfully deleted secret “${variables.secrets[0]}”`) setSelectedIdToDelete(null) }, }) const filteredCustomSecrets = useMemo(() => { if (searchString.length === 0) return customSecrets const search = searchString.toLowerCase() return customSecrets.filter((secret) => secret.name.toLowerCase().includes(search)) }, [customSecrets, searchString]) const headers = [ Name, Digest SHA256 , Updated, , ] const showLoadingState = isLoadingSecretsPermissions || (canReadSecrets && isLoading) useEffect(() => { if (!!selectedIdToEdit && isSuccess && !selectedSecretToEdit) { toast(`Secret ${selectedIdToEdit} cannot be found`) setSelectedIdToEdit(null) } }, [isSuccess, selectedIdToEdit, selectedSecretToEdit, setSelectedIdToEdit]) useEffect(() => { if (!!selectedIdToDelete && isSuccess && !selectedSecretToDelete && !isSuccessDelete) { toast(`Secret ${selectedIdToDelete} cannot be found`) setSelectedIdToDelete(null) } }, [ isSuccess, isSuccessDelete, selectedIdToDelete, selectedSecretToDelete, setSelectedIdToDelete, ]) return ( <> {showLoadingState ? ( ) : !canReadSecrets ? ( ) : ( <> {isError && } {isSuccess && (
{canUpdateSecrets ? ( ) : ( )}

Custom secrets

Secrets you have defined for this project

setSearchString(e.target.value)} icon={} />
{headers} {filteredCustomSecrets.length > 0 ? ( filteredCustomSecrets.map((secret) => ( setSelectedIdToEdit(secret.name)} onSelectDelete={() => setSelectedIdToDelete(secret.name)} /> )) ) : customSecrets.length === 0 ? (

No custom secrets created

This project has no custom secrets yet.

) : (

No results found

Your search for "{searchString}" did not return any results

)}

Default secrets

Reserved secrets available in every project

)} )} setSelectedIdToEdit(null)} /> setSelectedIdToDelete(null)} onConfirm={() => { if (selectedSecretToDelete) { deleteSecret({ projectRef, secrets: [selectedSecretToDelete.name] }) } }} >

Ensure none of your edge functions are actively using this secret before deleting it. This action cannot be undone.

) }