RealtimeTokensPopover.tsx 3.5 KB

1234567891011121314151617181920212223242526272829303132333435363738394041424344454647484950515253545556575859606162636465666768697071727374757677787980818283848586878889909192939495
  1. // @ts-nocheck
  2. import { PermissionAction } from '@supabase/shared-types/out/constants'
  3. import { useParams } from 'common'
  4. import { Dispatch, SetStateAction, useEffect, useRef } from 'react'
  5. import { toast } from 'sonner'
  6. import { RealtimeConfig } from './useRealtimeMessages'
  7. import { RoleImpersonationPopover } from '@/components/interfaces/RoleImpersonationSelector/RoleImpersonationPopover'
  8. import { getKeys, useAPIKeysQuery } from '@/data/api-keys/api-keys-query'
  9. import { getTemporaryAPIKey } from '@/data/api-keys/temp-api-keys-query'
  10. import { useProjectPostgrestConfigQuery } from '@/data/config/project-postgrest-config-query'
  11. import { useSendEventMutation } from '@/data/telemetry/send-event-mutation'
  12. import { useAsyncCheckPermissions } from '@/hooks/misc/useCheckPermissions'
  13. import { useSelectedOrganizationQuery } from '@/hooks/misc/useSelectedOrganization'
  14. import { IS_PLATFORM } from '@/lib/constants'
  15. import { getRoleImpersonationJWT } from '@/lib/role-impersonation'
  16. import { useRoleImpersonationStateSnapshot } from '@/state/role-impersonation-state'
  17. interface RealtimeTokensPopoverProps {
  18. config: RealtimeConfig
  19. onChangeConfig: Dispatch<SetStateAction<RealtimeConfig>>
  20. }
  21. export const RealtimeTokensPopover = ({ config, onChangeConfig }: RealtimeTokensPopoverProps) => {
  22. const { ref } = useParams()
  23. const { data: org } = useSelectedOrganizationQuery()
  24. const snap = useRoleImpersonationStateSnapshot()
  25. const { can: canReadAPIKeys } = useAsyncCheckPermissions(PermissionAction.SECRETS_READ, '*')
  26. const { data: apiKeys } = useAPIKeysQuery(
  27. {
  28. projectRef: config.projectRef,
  29. reveal: true,
  30. },
  31. { enabled: canReadAPIKeys }
  32. )
  33. const { anonKey, publishableKey } = getKeys(apiKeys)
  34. const { data: postgrestConfig } = useProjectPostgrestConfigQuery(
  35. { projectRef: config.projectRef },
  36. { enabled: IS_PLATFORM }
  37. )
  38. const jwtSecret = postgrestConfig?.jwt_secret
  39. const { mutate: sendEvent } = useSendEventMutation()
  40. // only send a telemetry event if the user changes the role. Don't send an event during initial render.
  41. const isMounted = useRef(false)
  42. useEffect(() => {
  43. if (isMounted.current) {
  44. sendEvent({
  45. action: 'realtime_inspector_database_role_updated',
  46. groups: { project: ref ?? 'Unknown', organization: org?.slug ?? 'Unknown' },
  47. })
  48. }
  49. isMounted.current = true
  50. // eslint-disable-next-line react-hooks/exhaustive-deps
  51. }, [snap.role])
  52. useEffect(() => {
  53. const triggerUpdateTokenBearer = async () => {
  54. let token: string | undefined
  55. let bearer: string | null = null
  56. if (
  57. config.projectRef !== undefined &&
  58. jwtSecret !== undefined &&
  59. snap.role !== undefined &&
  60. snap.role.type === 'postgrest'
  61. ) {
  62. token = publishableKey?.api_key ?? anonKey?.api_key
  63. await getRoleImpersonationJWT(config.projectRef, jwtSecret, snap.role)
  64. .then((b) => (bearer = b))
  65. .catch((err) => toast.error(`Failed to get JWT for role: ${err.message}`))
  66. } else {
  67. try {
  68. const data = await getTemporaryAPIKey({ projectRef: config.projectRef, expiry: 3600 })
  69. token = data.api_key
  70. } catch (error) {
  71. token = publishableKey?.api_key
  72. }
  73. }
  74. if (token) {
  75. onChangeConfig({ ...config, token, bearer })
  76. }
  77. }
  78. triggerUpdateTokenBearer()
  79. // eslint-disable-next-line react-hooks/exhaustive-deps
  80. }, [snap.role, anonKey])
  81. return <RoleImpersonationPopover align="start" variant="connected-on-both" />
  82. }