Files
supabase/apps/studio/components/interfaces/APIKeys/SecretAPIKeys.tsx
Jonathan Summers-Muir 4649bf911e feat: new api keys [hidden] (#33252)
* feat: add basic api keys ui

* init JWT secrets. rough

* Update JWTSecretKeysTable.tsx

* added some info hover cards.

• found this this is probably the wrong direction
• will create a new page for next iteration.

* init new version

* add illustrations

* Update JWTSecretKeysTablev2.tsx

* chore: delete API key now works

* some style changes

* added better tables

* Update JWTSecretKeysTablev2.tsx

* add public JWT dialog

* moar

* adding sub layout in

* starts adding in a ButtonGroup

* about to make into separate components

* added quick copy to project loading screen

* build state

* basic loading

* confirm dialog and loading states

* switched for better loading experience

* moved styles of Input to InputVariants

* issue with ref type

* loading,error and rest states

* new loading states

* alt l;ayout

* add group

* updated error states for permissions

* copy button behaviour for secret keys

* delete dialog

* Update QuickKeyCopy.tsx

* fix type errors

* Update JWTSecretKeysTablev2.tsx

* update menu to hide pages

* Update SettingsMenu.utils.tsx

* Update resource-query.ts

* remove old file

* moved JWT secrets to use valtio

* Update api-keys-query.ts

* fix typecheck

* rename files

* remove JWT stuff

* revert file

* remove more JWT stuff

* Update package.json

* Update pnpm-lock.yaml

* Update ProjectLayout.tsx

* Update PublishableAPIKeys.tsx

* Update api-keys-query.ts

* refactor api-keys-query

* Update SettingsMenu.utils.tsx

* Some clean up

* more clean up and refactor

* Update APIKeyRow.tsx

* Update LayoutHeader.tsx

* resolve comments

* Update CreateSecretAPIKeyModal.tsx

* Update APIKeyRow.tsx

* Add perms check for delete API keys

* Remove console log

* Delete ConnectDialog.tsx

* use project ref

---------

Co-authored-by: Stojan Dimitrovski <sdimitrovski@gmail.com>
Co-authored-by: Joshen Lim <joshenlimek@gmail.com>
2025-02-05 15:21:10 +01:00

147 lines
4.6 KiB
TypeScript

import { PermissionAction } from '@supabase/shared-types/out/constants'
import { useMemo } from 'react'
import { useParams } from 'common'
import { FormHeader } from 'components/ui/Forms/FormHeader'
import { APIKeysData, useAPIKeysQuery } from 'data/api-keys/api-keys-query'
import { useCheckPermissions, usePermissionsLoaded } from 'hooks/misc/useCheckPermissions'
import { Card, CardContent, EyeOffIcon, Skeleton, WarningIcon, cn } from 'ui'
import {
Table,
TableBody,
TableCell,
TableHead,
TableHeader,
TableRow,
} from 'ui/src/components/shadcn/ui/table'
import { APIKeyRow } from './APIKeyRow'
import CreateSecretAPIKeyModal from './CreateSecretAPIKeyModal'
export const SecretAPIKeys = () => {
const { ref: projectRef } = useParams()
const {
data: apiKeysData,
isLoading: isLoadingApiKeys,
error,
} = useAPIKeysQuery({ projectRef, reveal: false })
const isLoadingPermissions = !usePermissionsLoaded()
const canReadAPIKeys = useCheckPermissions(PermissionAction.TENANT_SQL_ADMIN_WRITE, '*')
const secretApiKeys = useMemo(
() =>
apiKeysData?.filter(
(key): key is Extract<APIKeysData[number], { type: 'secret' }> => key.type === 'secret'
) ?? [],
[apiKeysData]
)
const empty = secretApiKeys?.length === 0 && !isLoadingApiKeys && !isLoadingPermissions
const RowLoading = () => (
<TableRow>
<TableCell>
<Skeleton className="max-w-12 h-4 rounded-full" />
</TableCell>
<TableCell>
<Skeleton className="max-w-60 h-4 rounded-full" />
</TableCell>
<TableCell>
<Skeleton className="w-2 h-4 rounded-full" />
</TableCell>
</TableRow>
)
const TableContainer = ({ children }: { children: React.ReactNode }) => (
<div>
<FormHeader
title="Secret keys"
description="These API keys allow privileged access to your project's APIs. Use in servers, functions, workers or other backend components of your application. Keep secret and never publish."
actions={<CreateSecretAPIKeyModal />}
/>
<Card className={cn('w-full overflow-hidden', !empty && 'bg-surface-100')}>
<CardContent className="p-0">
<Table className="p-5">
<TableHeader>
<TableRow className={cn('bg-200', empty && 'hidden')}>
<TableHead
key=""
className="text-left font-mono uppercase text-xs text-foreground-lighter h-auto py-2 overflow-hidden"
>
Name
</TableHead>
<TableHead className="text-left font-mono uppercase text-xs text-foreground-lighter h-auto py-2 pr-0">
API Key
</TableHead>
<TableHead
className="text-right font-mono uppercase text-xs text-foreground-lighter h-auto py-2"
key="actions"
/>
</TableRow>
</TableHeader>
<TableBody className="">{children}</TableBody>
</Table>
</CardContent>
</Card>
</div>
)
if (isLoadingApiKeys || isLoadingPermissions) {
return (
<TableContainer>
<RowLoading />
<RowLoading />
</TableContainer>
)
}
if (!canReadAPIKeys) {
return (
<TableContainer>
<div className="!rounded-b-md overflow-hidden py-12 flex flex-col gap-1 items-center justify-center">
<EyeOffIcon />
<p className="text-sm text-foreground">
You do not have permission to read API Secret Keys
</p>
<p className="text-foreground-light">
Contact your organization owner/admin to request access.
</p>
</div>
</TableContainer>
)
}
if (error) {
return (
<TableContainer>
<div className="!rounded-b-md overflow-hidden py-12 flex flex-col gap-1 items-center justify-center">
<WarningIcon />
<p className="text-sm text-warning-600">Error loading Secret API Keys</p>
<p className="text-warning/75">{error.message}</p>
</div>
</TableContainer>
)
}
if (empty) {
return (
<TableContainer>
<div className="!rounded-b-md overflow-hidden py-12 flex flex-col gap-1 items-center justify-center">
<p className="text-sm text-foreground">No secret API keys exist</p>
<p className="text-sm text-foreground-light">
Your project can't be accessed using secret API keys.
</p>
</div>
</TableContainer>
)
}
return (
<TableContainer>
{secretApiKeys.map((apiKey) => (
<APIKeyRow key={apiKey.id} apiKey={apiKey} />
))}
</TableContainer>
)
}