mirror of
https://github.com/supabase/supabase.git
synced 2026-06-20 09:32:15 +08:00
## Summary
Rewrites the secret API key reveal flow in `ApiKeyPill` to remove its
dependency on React Query, replacing it with a lightweight custom hook.
## Changes
- **`useRevealedSecret` (new hook)**
A simple, reusable hook that:
- Fetches the unmasked secret key via `getAPIKeysById`
- Exposes `data`, `isLoading`, `reveal()`, and `clear()`
- Keeps sensitive data in local component state (no global cache)
- **`ApiKeyPill` (refactored)**
- Removes all React Query imports (`useQueryClient`, `useAPIKeyIdQuery`,
`apiKeysKeys`)
- Uses `useRevealedSecret` for reveal / copy operations
- Preserves existing UX:
- 10-second auto-hide timer
- Permission-based gating (`canManageSecretKeys`)
- Loading states on toggle / copy
- **`api-key-id-query.ts` (cleaned up)**
- Removes the now-unused `useAPIKeyIdQuery` hook
- Retains the `getAPIKeysById` fetcher for direct use
## Motivation
The previous React Query–based flow had to aggressively disable caching
(`staleTime: 0`, `gcTime: 0`) and manually purge queries from the cache
on every interaction, which was cumbersome and leaked implementation
details into the component. A plain fetch + local state is simpler and
safer for transient, sensitive data.
## Testing
- [x] Toggle reveal on a secret API key
- [x] Verify 10-second auto-hide
- [x] Copy a secret key (both revealed and unrevealed states)
- [x] Verify restricted users cannot reveal/copy
---
Resolves [FE-3206](https://linear.app/supabase/issue/FE-3206)
<!-- This is an auto-generated comment: release notes by coderabbit.ai
-->
## Summary by CodeRabbit
* **Refactor**
* Improved API key reveal/copy flow: uses a dedicated reveal/clear
mechanism, preserves permission checks and 10s auto-hide, and shows
reveal/copy failures via user-facing toasts. Copy now falls back to
masked key when needed and the reveal toggle behavior is more reliable.
[](https://app.coderabbit.ai/change-stack/supabase/supabase/pull/45792)
<!-- end of auto-generated comment: release notes by coderabbit.ai -->
44 lines
1.2 KiB
TypeScript
44 lines
1.2 KiB
TypeScript
import { useCallback, useRef, useState } from 'react'
|
|
|
|
import { getAPIKeysById } from '@/data/api-keys/api-key-id-query'
|
|
|
|
interface UseRevealedSecretOptions {
|
|
projectRef?: string
|
|
id?: string
|
|
}
|
|
|
|
export function useRevealedSecret({ projectRef, id }: UseRevealedSecretOptions) {
|
|
const [data, setData] = useState<string | undefined | null>()
|
|
const [isLoading, setIsLoading] = useState(false)
|
|
const requestIdRef = useRef(0)
|
|
|
|
const reveal = useCallback(async () => {
|
|
if (!projectRef || !id) return
|
|
|
|
const requestId = ++requestIdRef.current
|
|
setIsLoading(true)
|
|
|
|
try {
|
|
const result = await getAPIKeysById({ projectRef, id, reveal: true })
|
|
if (requestId !== requestIdRef.current) return
|
|
setData(result.api_key)
|
|
return result.api_key
|
|
} catch (error) {
|
|
if (requestId !== requestIdRef.current) return
|
|
console.error('Failed to reveal secret key:', error)
|
|
throw error
|
|
} finally {
|
|
if (requestId === requestIdRef.current) {
|
|
setIsLoading(false)
|
|
}
|
|
}
|
|
}, [projectRef, id])
|
|
|
|
const clear = useCallback(() => {
|
|
requestIdRef.current++
|
|
setData(undefined)
|
|
}, [])
|
|
|
|
return { data, isLoading, reveal, clear }
|
|
}
|