ApiKeys.tsx163 lines · main
1import { PermissionAction } from '@supabase/shared-types/out/constants'
2import { Key } from 'lucide-react'
3import { useMemo } from 'react'
4import { toast } from 'sonner'
5import { Badge, copyToClipboard } from 'ui'
6import type { ICommand } from 'ui-patterns/CommandMenu'
7import {
8 PageType,
9 useRegisterCommands,
10 useRegisterPage,
11 useResetCommandMenu,
12 useSetCommandMenuOpen,
13 useSetPage,
14} from 'ui-patterns/CommandMenu'
15
16import { COMMAND_MENU_SECTIONS } from './CommandMenu.utils'
17import { orderCommandSectionsByPriority } from './ordering'
18import { getKeys, useAPIKeysQuery } from '@/data/api-keys/api-keys-query'
19import { useAsyncCheckPermissions } from '@/hooks/misc/useCheckPermissions'
20import { useSelectedProjectQuery } from '@/hooks/misc/useSelectedProject'
21
22const API_KEYS_PAGE_NAME = 'API Keys'
23
24export function useApiKeysCommands() {
25 const setIsOpen = useSetCommandMenuOpen()
26 const resetCommandMenu = useResetCommandMenu()
27 const setPage = useSetPage()
28
29 const { data: project } = useSelectedProjectQuery()
30 const ref = project?.ref || '_'
31
32 const { can: canReadAPIKeys } = useAsyncCheckPermissions(PermissionAction.SECRETS_READ, '*')
33
34 const { data: apiKeys } = useAPIKeysQuery(
35 { projectRef: project?.ref, reveal: true },
36 { enabled: canReadAPIKeys }
37 )
38 const commands = useMemo(() => {
39 const { anonKey, serviceKey, publishableKey, allSecretKeys } = canReadAPIKeys
40 ? getKeys(apiKeys)
41 : {}
42
43 return [
44 project &&
45 publishableKey && {
46 id: 'publishable-key',
47 name: `Copy publishable key`,
48 action: () => {
49 copyToClipboard(publishableKey.api_key ?? '', () => {
50 toast.success('Publishable key copied to clipboard')
51 })
52 setIsOpen(false)
53 resetCommandMenu()
54 },
55 badge: () => (
56 <span className="flex items-center gap-x-1">
57 <Badge>Project: {project?.name}</Badge>
58 <Badge>{publishableKey.type}</Badge>
59 </span>
60 ),
61 icon: () => <Key />,
62 },
63 ...(project && allSecretKeys
64 ? allSecretKeys.map((key) => ({
65 id: key.id,
66 name: `Copy secret key (${key.name})`,
67 action: () => {
68 copyToClipboard(key.api_key ?? '', () => {
69 toast.success('Secret key copied to clipboard')
70 })
71 setIsOpen(false)
72 resetCommandMenu()
73 },
74 badge: () => (
75 <span className="flex items-center gap-x-1">
76 <Badge>Project: {project?.name}</Badge>
77 <Badge>{key.type}</Badge>
78 </span>
79 ),
80 icon: () => <Key />,
81 }))
82 : []),
83 project &&
84 anonKey && {
85 id: 'anon-key',
86 name: `Copy anonymous API key`,
87 action: () => {
88 copyToClipboard(anonKey.api_key ?? '', () => {
89 toast.success('Anonymous API key copied to clipboard')
90 })
91 setIsOpen(false)
92 resetCommandMenu()
93 },
94 badge: () => (
95 <span className="flex items-center gap-x-1">
96 <Badge>Project: {project?.name}</Badge>
97 <Badge>Public</Badge>
98 <Badge>{anonKey.type}</Badge>
99 </span>
100 ),
101 icon: () => <Key />,
102 },
103 project &&
104 serviceKey && {
105 id: 'service-key',
106 name: `Copy service API key`,
107 action: () => {
108 copyToClipboard(serviceKey.api_key ?? '', () => {
109 toast.success('Service key copied to clipboard')
110 })
111 setIsOpen(false)
112 resetCommandMenu()
113 },
114 badge: () => (
115 <span className="flex items-center gap-x-1">
116 <Badge>Project: {project?.name}</Badge>
117 <Badge variant="destructive">Secret</Badge>
118 <Badge>{serviceKey.type}</Badge>
119 </span>
120 ),
121 icon: () => <Key />,
122 },
123 !(anonKey || serviceKey) && {
124 id: 'api-keys-project-settings',
125 name: 'See API keys in Project Settings',
126 route: `/project/${ref}/settings/api-keys`,
127 icon: () => <Key />,
128 },
129 ].filter(Boolean) as ICommand[]
130 }, [apiKeys, canReadAPIKeys, project, ref, resetCommandMenu, setIsOpen])
131
132 useRegisterPage(
133 API_KEYS_PAGE_NAME,
134 {
135 type: PageType.Commands,
136 sections: [
137 {
138 id: 'api-keys',
139 name: 'API keys',
140 commands,
141 },
142 ],
143 },
144 { deps: [commands], enabled: !!project && commands.length > 0 }
145 )
146
147 useRegisterCommands(
148 COMMAND_MENU_SECTIONS.ACTIONS,
149 [
150 {
151 id: 'api-keys',
152 name: 'Get API keys...',
153 action: () => setPage(API_KEYS_PAGE_NAME),
154 icon: () => <Key />,
155 },
156 ],
157 {
158 enabled: !!project && commands.length > 0,
159 orderSection: orderCommandSectionsByPriority,
160 sectionMeta: { priority: 3 },
161 }
162 )
163}