Files
supabase/apps/studio/components/interfaces/Docs/LangSelector.tsx
Joshen Lim 27188c147c Support creating multiple publishable keys, and deleting publishable keys (#41186)
* Support creating multiple publishable keys, and deleting publishable keys

* FIx types

* Smol

* Smol fix

* Address issues

* Update comment

* Replace all usage of useApiKeysVisiblity for checking permissions to just call useAsyncCheckPermissions directly

* Clean up and deprecate useApiKeysVisibility hook

* ADdress
2025-12-12 16:07:36 +08:00

178 lines
6.4 KiB
TypeScript

import { PermissionAction } from '@supabase/shared-types/out/constants'
import { Key } from 'lucide-react'
import { useMemo } from 'react'
import { useParams } from 'common'
import type { showApiKey } from 'components/interfaces/Docs/Docs.types'
import { useAPIKeysQuery } from 'data/api-keys/api-keys-query'
import { useAsyncCheckPermissions } from 'hooks/misc/useCheckPermissions'
import {
Button,
DropdownMenu,
DropdownMenuContent,
DropdownMenuGroup,
DropdownMenuLabel,
DropdownMenuRadioGroup,
DropdownMenuRadioItem,
DropdownMenuSeparator,
DropdownMenuTrigger,
} from 'ui'
const DEFAULT_KEY = { name: 'hide', key: 'SUPABASE_KEY' }
interface LangSelectorProps {
selectedLang: string
selectedApiKey: showApiKey
setSelectedLang: (selectedLang: string) => void
setSelectedApiKey: (showApiKey: showApiKey) => void
}
export const LangSelector = ({
selectedLang,
selectedApiKey,
setSelectedLang,
setSelectedApiKey,
}: LangSelectorProps) => {
const { ref: projectRef } = useParams()
const { can: canReadAPIKeys } = useAsyncCheckPermissions(PermissionAction.SECRETS_READ, '*')
const { data: apiKeys = [], isPending: isLoadingAPIKeys } = useAPIKeysQuery(
{
projectRef,
reveal: false,
},
{ enabled: canReadAPIKeys }
)
const legacyKeys = useMemo(() => apiKeys.filter(({ type }) => type === 'legacy'), [apiKeys])
const publishableKeys = useMemo(
() => apiKeys.filter(({ type }) => type === 'publishable'),
[apiKeys]
)
const secretKeys = useMemo(() => apiKeys.filter(({ type }) => type === 'secret'), [apiKeys])
return (
<div className="p-1 w-1/2 ml-auto">
<div className="z-0 flex justify-end">
<button
type="button"
onClick={() => setSelectedLang('js')}
className={`${
selectedLang == 'js'
? 'bg-surface-100 font-medium text-foreground'
: 'bg-alternative text-foreground-lighter'
} relative inline-flex items-center border-r border-background p-1 px-2 text-sm transition hover:text-foreground focus:outline-none`}
>
JavaScript
</button>
<button
type="button"
onClick={() => setSelectedLang('bash')}
className={`${
selectedLang == 'bash'
? 'bg-surface-100 font-medium text-foreground'
: 'bg-alternative text-foreground-lighter'
} relative inline-flex items-center border-r border-background p-1 px-2 text-sm transition hover:text-foreground focus:outline-none`}
>
Bash
</button>
{selectedLang == 'bash' &&
canReadAPIKeys &&
!isLoadingAPIKeys &&
apiKeys &&
apiKeys.length > 0 && (
<div className="flex gap-x-1">
<div className="flex items-center gap-2 p-1 pl-2 text-xs text-foreground-lighter">
<Key size={12} strokeWidth={1.5} />
<span>Project API key:</span>
</div>
<DropdownMenu>
<DropdownMenuTrigger asChild>
<Button type="outline">
{selectedApiKey.name === 'hide' ? 'Hide keys' : selectedApiKey.name}
</Button>
</DropdownMenuTrigger>
<DropdownMenuContent align="end" side="bottom">
<DropdownMenuRadioGroup value={selectedApiKey.key}>
<DropdownMenuRadioItem
key="hide"
value={DEFAULT_KEY.key}
onClick={() => setSelectedApiKey(DEFAULT_KEY)}
>
Hide keys
</DropdownMenuRadioItem>
{publishableKeys.length > 0 && (
<>
<DropdownMenuSeparator />
<DropdownMenuLabel>Publishable keys</DropdownMenuLabel>
{publishableKeys.map((key) => {
const value = key.api_key
return (
<DropdownMenuRadioItem
key={key.id}
value={value}
onClick={() =>
setSelectedApiKey({
name: `Publishable key: ${key.name}`,
key: value,
})
}
>
{key.name}
</DropdownMenuRadioItem>
)
})}
</>
)}
{secretKeys.length > 0 && (
<>
<DropdownMenuSeparator />
<DropdownMenuLabel>Secret keys</DropdownMenuLabel>
{secretKeys.map((key) => {
const value = key.prefix + '...'
return (
<DropdownMenuRadioItem
key={key.id}
value={value}
onClick={() =>
setSelectedApiKey({ name: `Secret key: ${key.name}`, key: value })
}
>
{key.name}
</DropdownMenuRadioItem>
)
})}
</>
)}
<DropdownMenuSeparator />
<DropdownMenuGroup>
<DropdownMenuLabel>JWT-based legacy keys</DropdownMenuLabel>
{legacyKeys.map((key) => {
const value = key.api_key
return (
<DropdownMenuRadioItem
key={key.id}
value={value}
onClick={() =>
setSelectedApiKey({ name: `Legacy key: ${key.name}`, key: value })
}
>
{key.name}
</DropdownMenuRadioItem>
)
})}
</DropdownMenuGroup>
</DropdownMenuRadioGroup>
</DropdownMenuContent>
</DropdownMenu>
</div>
)}
</div>
</div>
)
}