mirror of
https://github.com/supabase/supabase.git
synced 2026-09-11 04:21:47 +08:00
## Description This is the Scoped PAT stacked PRs root branch ## How to test ### With the `scopedPAT` enabled (default on staging) Go to https://studio-staging-git-scopedpat-merge-token-lists-supabase.vercel.app/dashboard/account/tokens. - You shouldn't see two tabs anymore - If you had classic tokens, they should have the _Legacy_ badge - You can create scoped tokens - You have a way to copy newly created tokens before closing the form side panel ### With the `scopedPAT` disabled (use the devtool to override) - You shouldn't see two tabs anymore - If you had classic tokens, they should **not** have the _Legacy_ badge - You can create classic tokens - You have a way to copy newly created tokens above the list upon form submission <!-- This is an auto-generated comment: release notes by coderabbit.ai --> ## Summary by CodeRabbit * **New Features** * Show classic and scoped access tokens together in one list, with classic tokens labeled “Legacy” when the scoped experience is enabled. * Add scoped access token creation with a two-step configure → review → success flow (when enabled). * Add a dismissible migration notice about scoped tokens with a link to API docs. * Show “View permissions” only for scoped tokens. * **Bug Fixes** * Token deletion now supports both classic and scoped tokens with the correct confirmation and success handling. * The scoped tokens page now redirects to the unified access tokens page. * **Accessibility** * Improved accessibility by adding a label to the token “more options” action. <!-- end of auto-generated comment: release notes by coderabbit.ai --> --------- Co-authored-by: Ali Waseem <waseema393@gmail.com> Co-authored-by: kemal.earth <606977+kemaldotearth@users.noreply.github.com>
66 lines
2.1 KiB
TypeScript
66 lines
2.1 KiB
TypeScript
import { useMemo } from 'react'
|
|
|
|
import type { BaseToken } from '../AccessToken.types'
|
|
import { useAccessTokensQuery } from '@/data/access-tokens/access-tokens-query'
|
|
import { useScopedAccessTokensQuery } from '@/data/scoped-access-tokens/scoped-access-token-query'
|
|
|
|
export type TokenKind = 'classic' | 'scoped'
|
|
|
|
export type MergedAccessToken = ClassicAccessToken | ScopedAccessToken
|
|
|
|
export interface ClassicAccessToken extends BaseToken {
|
|
id: number
|
|
kind: 'classic'
|
|
}
|
|
|
|
export interface ScopedAccessToken extends BaseToken {
|
|
id: string
|
|
kind: 'scoped'
|
|
}
|
|
|
|
interface UseMergedAccessTokensOptions {
|
|
/** Whether scoped tokens should be fetched + merged (gated on the scopedPAT flag). */
|
|
scopedTokensEnabled?: boolean
|
|
}
|
|
|
|
/**
|
|
* Merges classic and scoped access tokens into a single list, tagging each row with its `kind`.
|
|
* Classic is the baseline (its loading/error state drives the list); scoped failures degrade
|
|
* gracefully so a missing/undeployed scoped endpoint never hides classic tokens. Sorting is left to
|
|
* the consuming list (via filterAndSortTokens), so this only merges + tags.
|
|
*/
|
|
export const useMergedAccessTokens = ({
|
|
scopedTokensEnabled,
|
|
}: UseMergedAccessTokensOptions = {}) => {
|
|
const classic = useAccessTokensQuery()
|
|
const scoped = useScopedAccessTokensQuery({ enabled: scopedTokensEnabled })
|
|
|
|
return useMemo(() => {
|
|
const classicTokens: MergedAccessToken[] = (classic.data ?? []).map((token) => ({
|
|
...token,
|
|
kind: 'classic',
|
|
}))
|
|
const scopedTokens: MergedAccessToken[] = (scoped.data?.tokens ?? []).map((token) => ({
|
|
...token,
|
|
kind: 'scoped',
|
|
}))
|
|
return {
|
|
tokens: [...classicTokens, ...scopedTokens],
|
|
// Classic drives the primary states; a scoped fetch that is still loading shouldn't block the list.
|
|
isLoading: classic.isPending || (scopedTokensEnabled && scoped.isPending),
|
|
isError: classic.isError,
|
|
error: classic.error,
|
|
isSuccess: classic.isSuccess,
|
|
}
|
|
}, [
|
|
classic.data,
|
|
classic.isPending,
|
|
classic.isError,
|
|
classic.error,
|
|
classic.isSuccess,
|
|
scoped.data,
|
|
scoped.isPending,
|
|
scopedTokensEnabled,
|
|
])
|
|
}
|