ApiKeys.tsx 4.9 KB

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