Home / Function/ SecretsManagement() — supabase Function Reference

SecretsManagement() — supabase Function Reference

Architecture documentation for the SecretsManagement() function in SecretsManagement.tsx from the supabase codebase.

Entity Profile

Dependency Diagram

graph TD
  9be42c55_67a4_6c60_26ec_df4d90e32819["SecretsManagement()"]
  fbf17bcf_c38b_443a_2656_70206e6c0a8f["formatSecretColumns()"]
  9be42c55_67a4_6c60_26ec_df4d90e32819 -->|calls| fbf17bcf_c38b_443a_2656_70206e6c0a8f
  style 9be42c55_67a4_6c60_26ec_df4d90e32819 fill:#6366f1,stroke:#818cf8,color:#fff

Relationship Graph

Source Code

apps/studio/components/interfaces/Integrations/Vault/Secrets/SecretsManagement.tsx lines 34–252

export const SecretsManagement = () => {
  const { search } = useParams()
  const { data: project } = useSelectedProjectQuery()

  // Track the ID being deleted to exclude it from error checking
  const deletingSecretIdRef = useRef<string | null>(null)

  const [searchValue, setSearchValue] = useState<string>('')
  const [showAddSecretModal, setShowAddSecretModal] = useQueryState(
    'new',
    parseAsBoolean.withDefault(false).withOptions({ history: 'push', clearOnDefault: true })
  )
  const [selectedSort, setSelectedSort] = useState<'updated_at' | 'name'>('updated_at')

  const { can: canManageSecrets } = useAsyncCheckPermissions(
    PermissionAction.TENANT_SQL_ADMIN_WRITE,
    'tables'
  )

  const {
    data,
    isPending: isLoading,
    isRefetching,
    refetch,
    error,
    isError,
  } = useVaultSecretsQuery({
    projectRef: project?.ref!,
    connectionString: project?.connectionString,
  })
  const allSecrets = useMemo(() => data || [], [data])

  const { setValue: setSelectedSecretToEdit, value: secretToEdit } = useQueryStateWithSelect({
    urlKey: 'edit',
    select: (id: string) => (id ? allSecrets?.find((secret) => secret.id === id) : undefined),
    enabled: !!allSecrets && !isLoading,
    onError: () => toast.error(`Secret not found`),
  })

  const { setValue: setSelectedSecretToRemove, value: secretToDelete } = useQueryStateWithSelect({
    urlKey: 'delete',
    select: (id: string) => (id ? allSecrets?.find((secret) => secret.id === id) : undefined),
    enabled: !!allSecrets && !isLoading,
    onError: (_error, selectedId) =>
      handleErrorOnDelete(deletingSecretIdRef, selectedId, `Secret not found`),
  })

  const secrets = useMemo(() => {
    const filtered =
      searchValue.length > 0
        ? allSecrets.filter(
            (secret) =>
              (secret?.name ?? '').toLowerCase().includes(searchValue.trim().toLowerCase()) ||
              (secret?.id ?? '').toLowerCase().includes(searchValue.trim().toLowerCase())
          )
        : allSecrets

    if (selectedSort === 'updated_at') {
      return sortBy(filtered, (s) => Number(new Date(s.updated_at))).reverse()
    }
    return sortBy(filtered, (s) => (s.name || '').toLowerCase())
  }, [allSecrets, searchValue, selectedSort])

  useEffect(() => {
    if (search !== undefined) setSearchValue(search)
  }, [search])

  const columns = useMemo(
    () =>
      formatSecretColumns({
        onSelectEdit: (secret) => setSelectedSecretToEdit(secret.id),
        onSelectRemove: (secret) => setSelectedSecretToRemove(secret.id),
      }),
    [setSelectedSecretToEdit, setSelectedSecretToRemove]
  )

  return (
    <>
      <div className="h-full w-full space-y-4">
        <div className="h-full w-full flex flex-col relative">
          <div className="bg-surface-200 py-3 px-10 flex items-center justify-between flex-wrap">
            <div className="flex items-center gap-2">
              <Input
                size="tiny"
                className="w-52"
                placeholder="Search by name or key ID"
                icon={<Search />}
                value={searchValue ?? ''}
                onChange={(e) => setSearchValue(e.target.value)}
                actions={[
                  searchValue && (
                    <Button
                      size="tiny"
                      type="text"
                      icon={<X />}
                      onClick={() => {
                        setSearchValue('')
                      }}
                      className="p-0 h-5 w-5"
                    />
                  ),
                ]}
              />

              <Select_Shadcn_ value={selectedSort} onValueChange={(v) => setSelectedSort(v as any)}>
                <SelectTrigger_Shadcn_ size="tiny" className="w-44">
                  <SelectValue_Shadcn_ asChild>
                    <>Sort by {selectedSort}</>
                  </SelectValue_Shadcn_>
                </SelectTrigger_Shadcn_>
                <SelectContent_Shadcn_>
                  <SelectItem_Shadcn_ value="updated_at" className="text-xs">
                    Updated at
                  </SelectItem_Shadcn_>
                  <SelectItem_Shadcn_ value="name" className="text-xs">
                    Name
                  </SelectItem_Shadcn_>
                </SelectContent_Shadcn_>
              </Select_Shadcn_>
            </div>

            <div className="flex items-center gap-x-2">
              <Button
                type="default"
                icon={<RefreshCw />}
                loading={isRefetching}
                onClick={() => refetch()}
              >
                Refresh
              </Button>
              <DocsButton href={`${DOCS_URL}/guides/database/vault`} />
              <ButtonTooltip
                type="primary"
                disabled={!canManageSecrets}
                onClick={() => setShowAddSecretModal(true)}
                tooltip={{
                  content: {
                    side: 'bottom',
                    text: !canManageSecrets
                      ? 'You need additional permissions to add secrets'
                      : undefined,
                  },
                }}
              >
                Add new secret
              </ButtonTooltip>
            </div>
          </div>

          <LoadingLine loading={isLoading || isRefetching} />

          {isError ? (
            <div className="px-6 py-6 space-x-2 flex items-center justify-center">
              <p className="text-sm text-foreground">Failed to load secrets</p>
            </div>
          ) : (
            <DataGrid
              className="flex-grow border-t-0"
              rowHeight={52}
              headerRowHeight={36}
              columns={columns}
              rows={secrets}
              rowKeyGetter={(row: VaultSecret) => row.id}
              rowClass={() => {
                return cn(
                  'cursor-pointer',
                  '[&>.rdg-cell]:border-box [&>.rdg-cell]:outline-none [&>.rdg-cell]:shadow-none',
                  '[&>.rdg-cell:first-child>div]:pl-8'
                )
              }}
              renderers={{
                renderRow(_, props) {
                  return <Row key={(props.row as VaultSecret).id} {...props} />
                },
              }}
            />
          )}

          {secrets.length === 0 && !isLoading && !isError ? (
            <div className="absolute top-32 px-6 w-full">
              <div className="text-center text-sm flex flex-col gap-y-1">
                <p className="text-foreground">
                  {searchValue ? 'No secrets found' : 'No secrets added yet'}
                </p>
                <p className="text-foreground-light">
                  {searchValue
                    ? `There are currently no secrets based on the search "${searchValue}"`
                    : 'The Vault allows you to store sensitive information like API keys'}
                </p>
              </div>
            </div>
          ) : null}
        </div>
      </div>

      <AddNewSecretModal
        visible={showAddSecretModal}
        onClose={() => setShowAddSecretModal(false)}
      />
      {secretToEdit && (
        <EditSecretModal
          visible={!!secretToEdit}
          secret={secretToEdit}
          onClose={() => setSelectedSecretToEdit(null)}
        />
      )}
      <DeleteSecretModal
        selectedSecret={secretToDelete}
        onDeleteStart={(secretId) => {
          deletingSecretIdRef.current = secretId
        }}
        onClose={() => {
          deletingSecretIdRef.current = null
          setSelectedSecretToRemove(null)
        }}
      />
    </>
  )
}

Subdomains

Frequently Asked Questions

What does SecretsManagement() do?
SecretsManagement() is a function in the supabase codebase.
What does SecretsManagement() call?
SecretsManagement() calls 1 function(s): formatSecretColumns.

Analyze Your Own Codebase

Get architecture documentation, dependency graphs, and domain analysis for your codebase in minutes.

Try Supermodel Free