mirror of
https://github.com/supabase/supabase.git
synced 2026-06-18 05:33:50 +08:00
## Problem Our `<Button>` component breaks the default `button` contract by redefining the `type` prop to set its variant (`primary`, `default`, etc) instead of the button type (`submit`, `button`, etc). This is confusing and forces to write more code when using it with shadcn components that expect/inject the standard button props. ## Solution - rename the `type` prop to `variant` - rename the `htmlType` prop to `type` - propagate the changes where necessary - format code ## How to test As this is just prop renaming, if it builds it's ok --------- Co-authored-by: Ivan Vasilov <vasilov.ivan@gmail.com>
239 lines
8.6 KiB
TypeScript
239 lines
8.6 KiB
TypeScript
import { PermissionAction } from '@supabase/shared-types/out/constants'
|
|
import { useParams } from 'common'
|
|
import { useState } from 'react'
|
|
import { toast } from 'sonner'
|
|
import {
|
|
AlertDialog,
|
|
AlertDialogAction,
|
|
AlertDialogCancel,
|
|
AlertDialogContent,
|
|
AlertDialogDescription,
|
|
AlertDialogFooter,
|
|
AlertDialogHeader,
|
|
AlertDialogTitle,
|
|
} from 'ui'
|
|
|
|
import Panel from '../Panel'
|
|
import { ButtonTooltip } from '@/components/ui/ButtonTooltip'
|
|
import { TextConfirmModal } from '@/components/ui/TextConfirmModalWrapper'
|
|
import { useToggleLegacyAPIKeysMutation } from '@/data/api-keys/legacy-api-key-toggle-mutation'
|
|
import { useLegacyAPIKeysStatusQuery } from '@/data/api-keys/legacy-api-keys-status-query'
|
|
import { useLegacyJWTSigningKeyQuery } from '@/data/jwt-signing-keys/legacy-jwt-signing-key-query'
|
|
import { useAuthorizedAppsQuery } from '@/data/oauth/authorized-apps-query'
|
|
import { useAsyncCheckPermissions } from '@/hooks/misc/useCheckPermissions'
|
|
import { useSelectedOrganizationQuery } from '@/hooks/misc/useSelectedOrganization'
|
|
|
|
export const ToggleLegacyApiKeysPanel = () => {
|
|
const { ref: projectRef } = useParams()
|
|
const { data: org } = useSelectedOrganizationQuery()
|
|
|
|
const [isConfirmOpen, setIsConfirmOpen] = useState(false)
|
|
const [isAppsWarningOpen, setIsAppsWarningOpen] = useState(false)
|
|
|
|
const { can: canReadAPIKeys } = useAsyncCheckPermissions(PermissionAction.SECRETS_READ, '*')
|
|
const { can: canUpdateAPIKeys, isSuccess: isPermissionsSuccess } = useAsyncCheckPermissions(
|
|
PermissionAction.SECRETS_WRITE,
|
|
'*'
|
|
)
|
|
|
|
const { data: legacyAPIKeysStatusData, isSuccess: isLegacyAPIKeysStatusSuccess } =
|
|
useLegacyAPIKeysStatusQuery({ projectRef }, { enabled: canReadAPIKeys })
|
|
|
|
const { data: legacyJWTSecret } = useLegacyJWTSigningKeyQuery(
|
|
{ projectRef },
|
|
{ enabled: canReadAPIKeys }
|
|
)
|
|
|
|
const { data: authorizedApps = [], isError: isAuthorizedAppsError } = useAuthorizedAppsQuery({
|
|
slug: org?.slug,
|
|
})
|
|
|
|
const { enabled: isLegacyKeysEnabled } = legacyAPIKeysStatusData || {}
|
|
|
|
const oauthAppsLink = (
|
|
<a
|
|
href={`/dashboard/org/${org?.slug}/apps`}
|
|
target="_blank"
|
|
rel="noreferrer"
|
|
className="underline"
|
|
>
|
|
OAuth apps
|
|
</a>
|
|
)
|
|
|
|
const appsWarning = isAuthorizedAppsError
|
|
? {
|
|
title: 'Check your OAuth apps before continuing',
|
|
description: (
|
|
<>
|
|
Disabling legacy API keys can break apps that integrate with Supabase. Before
|
|
continuing, check your organization's {oauthAppsLink} to ensure none of them depend on
|
|
the legacy API keys.
|
|
</>
|
|
),
|
|
}
|
|
: {
|
|
title: 'Apps using Supabase may break',
|
|
description: (
|
|
<>
|
|
Your project uses apps that integrate with Supabase. Disabling the legacy API keys is a
|
|
brand new feature and the apps you're using may not have added support for this yet. It
|
|
can cause them to stop functioning. Check your {oauthAppsLink} before continuing.
|
|
</>
|
|
),
|
|
}
|
|
|
|
if (!(isLegacyAPIKeysStatusSuccess && isPermissionsSuccess)) {
|
|
return null
|
|
}
|
|
|
|
return (
|
|
<section>
|
|
<Panel>
|
|
<Panel.Content>
|
|
<div className="flex justify-between">
|
|
<div className="flex flex-col gap-2">
|
|
<p className="text-sm">
|
|
{isLegacyKeysEnabled ? 'Disable legacy API keys' : 'Re-enabling legacy API keys'}
|
|
</p>
|
|
<p className="text-foreground-light text-sm">
|
|
{isLegacyKeysEnabled
|
|
? 'Make sure you are no longer using your legacy API keys before proceeding.'
|
|
: 'We recommend you use the new API keys whenever possible, but re-enabling is an option.'}
|
|
</p>
|
|
</div>
|
|
<div className="flex items-center">
|
|
<ButtonTooltip
|
|
variant="default"
|
|
onClick={
|
|
isLegacyKeysEnabled && (authorizedApps?.length || isAuthorizedAppsError)
|
|
? () => setIsAppsWarningOpen(true)
|
|
: () => setIsConfirmOpen(true)
|
|
}
|
|
disabled={
|
|
!canUpdateAPIKeys ||
|
|
(!isLegacyKeysEnabled && legacyJWTSecret?.status === 'revoked')
|
|
}
|
|
tooltip={{
|
|
content: {
|
|
side: 'bottom',
|
|
text: !canUpdateAPIKeys
|
|
? 'You need additional permissions to enable or disable JWT-based API keys'
|
|
: !isLegacyKeysEnabled && legacyJWTSecret?.status === 'revoked'
|
|
? 'The legacy JWT secret is revoked. Re-enabling is not possible until it is at least moved to previously used.'
|
|
: undefined,
|
|
},
|
|
}}
|
|
>
|
|
{legacyAPIKeysStatusData.enabled
|
|
? 'Disable JWT-based API keys'
|
|
: 'Re-enable JWT-based API keys'}
|
|
</ButtonTooltip>
|
|
</div>
|
|
</div>
|
|
</Panel.Content>
|
|
</Panel>
|
|
|
|
<ToggleApiKeysModal
|
|
visible={isConfirmOpen}
|
|
onClose={() => setIsConfirmOpen(false)}
|
|
legacyAPIKeysStatusData={legacyAPIKeysStatusData}
|
|
/>
|
|
|
|
<AlertDialog open={isAppsWarningOpen} onOpenChange={(value) => setIsAppsWarningOpen(value)}>
|
|
<AlertDialogContent>
|
|
<AlertDialogHeader>
|
|
<AlertDialogTitle>{appsWarning.title}</AlertDialogTitle>
|
|
<AlertDialogDescription>{appsWarning.description}</AlertDialogDescription>
|
|
</AlertDialogHeader>
|
|
<AlertDialogFooter>
|
|
<AlertDialogCancel>Cancel</AlertDialogCancel>
|
|
<AlertDialogAction variant="danger" onClick={() => setIsConfirmOpen(true)}>
|
|
Disable API keys
|
|
</AlertDialogAction>
|
|
</AlertDialogFooter>
|
|
</AlertDialogContent>
|
|
</AlertDialog>
|
|
</section>
|
|
)
|
|
}
|
|
|
|
const ToggleApiKeysModal = ({
|
|
visible,
|
|
onClose,
|
|
legacyAPIKeysStatusData,
|
|
}: {
|
|
visible: boolean
|
|
onClose: () => void
|
|
legacyAPIKeysStatusData: { enabled: boolean }
|
|
}) => {
|
|
const { ref: projectRef } = useParams()
|
|
const { enabled: isLegacyKeysEnabled } = legacyAPIKeysStatusData || {}
|
|
|
|
const { mutate: toggleLegacyAPIKey, isPending: isTogglingLegacyAPIKey } =
|
|
useToggleLegacyAPIKeysMutation()
|
|
|
|
const onToggleLegacyAPIKeysEnabled = () => {
|
|
const enabled = !legacyAPIKeysStatusData?.enabled
|
|
|
|
toggleLegacyAPIKey(
|
|
{ projectRef, enabled },
|
|
{
|
|
onSuccess: () => {
|
|
toast.success(
|
|
enabled
|
|
? 'Your anon and service_role keys have been re-enabled!'
|
|
: 'Your anon and service_role keys have been disabled!'
|
|
)
|
|
onClose()
|
|
},
|
|
}
|
|
)
|
|
}
|
|
|
|
return (
|
|
<TextConfirmModal
|
|
size="medium"
|
|
visible={visible}
|
|
onCancel={() => onClose()}
|
|
onConfirm={onToggleLegacyAPIKeysEnabled}
|
|
title={isLegacyKeysEnabled ? 'Disable JWT-based keys' : 'Re-enable JWT-based keys'}
|
|
confirmString={isLegacyKeysEnabled ? 'disable' : 're-enable'}
|
|
confirmLabel={`Confirm to ${isLegacyKeysEnabled ? 'disable' : 're-enable'} anon and service_role`}
|
|
confirmPlaceholder={isLegacyKeysEnabled ? 'disable' : 're-enable'}
|
|
loading={isTogglingLegacyAPIKey}
|
|
variant={isLegacyKeysEnabled ? 'destructive' : 'default'}
|
|
alert={
|
|
isLegacyKeysEnabled
|
|
? {
|
|
title: 'Ensure legacy keys are no longer in use before disabling',
|
|
description: (
|
|
<span className="prose text-sm">
|
|
Disabling <code>anon</code> and <code>service_role</code> keys while they are in
|
|
use will cause downtime for your application. Ensure they are no longer in use
|
|
before proceeding. If you have not created a publishable and at least one secret
|
|
API key, some dashboard functionality may become unavailable.
|
|
<br />
|
|
<br />
|
|
<span className="text-danger">
|
|
This disables API keys when used in the <code>apikey</code> header. They remain
|
|
valid as a JWT.
|
|
</span>
|
|
</span>
|
|
),
|
|
}
|
|
: {
|
|
title: 'Publishable and secret keys are preferred',
|
|
description: (
|
|
<span className="prose text-sm">
|
|
Re-enabling <code>anon</code> and <code>service_role</code> keys may be
|
|
appropriate in certain cases, but using a publishable and secret key is more
|
|
secure. We recommend against re-enabling legacy API keys.
|
|
</span>
|
|
),
|
|
}
|
|
}
|
|
/>
|
|
)
|
|
}
|