ConnectTabContent.tsx 5.5 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133
  1. import { useParams } from 'common'
  2. import dynamic from 'next/dynamic'
  3. import { forwardRef, HTMLAttributes, useMemo } from 'react'
  4. import { cn } from 'ui'
  5. import { GenericSkeletonLoader } from 'ui-patterns/ShimmeringLoader'
  6. import { CopyCallbackContext } from 'ui-patterns/SimpleCodeBlock'
  7. import { getAddons } from '../Billing/Subscription/Subscription.utils'
  8. import type { projectKeys } from './Connect.types'
  9. import { getConnectionStrings } from './DatabaseSettings.utils'
  10. import { useProjectSettingsV2Query } from '@/data/config/project-settings-v2-query'
  11. import { usePgbouncerConfigQuery } from '@/data/database/pgbouncer-config-query'
  12. import { useSupavisorConfigurationQuery } from '@/data/database/supavisor-configuration-query'
  13. import { useProjectAddonsQuery } from '@/data/subscriptions/project-addons-query'
  14. import { useCheckEntitlements } from '@/hooks/misc/useCheckEntitlements'
  15. import { pluckObjectFields } from '@/lib/helpers'
  16. import { useTrack } from '@/lib/telemetry/track'
  17. interface ConnectContentTabProps extends HTMLAttributes<HTMLDivElement> {
  18. projectKeys: projectKeys
  19. filePath: string
  20. connectionTab: 'App Frameworks' | 'Mobile Frameworks' | 'ORMs'
  21. selectedFrameworkOrTool: string
  22. connectionStringPooler?: {
  23. transactionShared: string
  24. sessionShared: string
  25. transactionDedicated?: string
  26. sessionDedicated?: string
  27. ipv4SupportedForDedicatedPooler: boolean
  28. direct?: string
  29. }
  30. }
  31. export const ConnectTabContent = forwardRef<HTMLDivElement, ConnectContentTabProps>(
  32. ({ projectKeys, filePath, connectionTab, selectedFrameworkOrTool, ...props }, ref) => {
  33. const { ref: projectRef } = useParams()
  34. const track = useTrack()
  35. const { hasAccess: allowPgBouncerSelection } = useCheckEntitlements('dedicated_pooler')
  36. const handleCopy = () => {
  37. const trackingProperties: {
  38. connectionTab: 'App Frameworks' | 'Mobile Frameworks' | 'ORMs'
  39. selectedItem: string
  40. connectionType?: string
  41. lang?: string
  42. } = {
  43. connectionTab,
  44. selectedItem: selectedFrameworkOrTool,
  45. }
  46. // Only include connectionType and lang for App Frameworks and Mobile Frameworks
  47. if (connectionTab !== 'ORMs') {
  48. trackingProperties.connectionType = 'Framework snippet'
  49. trackingProperties.lang = filePath.split('/').pop() ?? 'unknown'
  50. }
  51. track('connection_string_copied', trackingProperties)
  52. }
  53. const { data: settings } = useProjectSettingsV2Query({ projectRef })
  54. const { data: pgbouncerConfig } = usePgbouncerConfigQuery({ projectRef })
  55. const { data: supavisorConfig } = useSupavisorConfigurationQuery({ projectRef })
  56. const { data: addons } = useProjectAddonsQuery({ projectRef })
  57. const { ipv4: ipv4Addon } = getAddons(addons?.selected_addons ?? [])
  58. const DB_FIELDS = ['db_host', 'db_name', 'db_port', 'db_user', 'inserted_at']
  59. const emptyState = { db_user: '', db_host: '', db_port: '', db_name: '' }
  60. const connectionInfo = pluckObjectFields(settings || emptyState, DB_FIELDS)
  61. const poolingConfigurationShared = supavisorConfig?.find((x) => x.database_type === 'PRIMARY')
  62. const poolingConfigurationDedicated = allowPgBouncerSelection ? pgbouncerConfig : undefined
  63. const connectionStringsShared = getConnectionStrings({
  64. connectionInfo,
  65. poolingInfo: {
  66. connectionString: poolingConfigurationShared?.connection_string ?? '',
  67. db_host: poolingConfigurationShared?.db_host ?? '',
  68. db_name: poolingConfigurationShared?.db_name ?? '',
  69. db_port: poolingConfigurationShared?.db_port ?? 0,
  70. db_user: poolingConfigurationShared?.db_user ?? '',
  71. },
  72. metadata: { projectRef },
  73. })
  74. const connectionStringsDedicated =
  75. poolingConfigurationDedicated !== undefined
  76. ? getConnectionStrings({
  77. connectionInfo,
  78. poolingInfo: {
  79. connectionString: poolingConfigurationDedicated.connection_string,
  80. db_host: poolingConfigurationDedicated.db_host,
  81. db_name: poolingConfigurationDedicated.db_name,
  82. db_port: poolingConfigurationDedicated.db_port,
  83. db_user: poolingConfigurationDedicated.db_user,
  84. },
  85. metadata: { projectRef },
  86. })
  87. : undefined
  88. const ContentFile = useMemo(() => {
  89. return dynamic<ConnectContentTabProps>(() => import(`./content/${filePath}/content`), {
  90. loading: () => (
  91. <div className="p-4 min-h-[331px]">
  92. <GenericSkeletonLoader />
  93. </div>
  94. ),
  95. })
  96. }, [filePath])
  97. return (
  98. <div ref={ref} {...props} className={cn('border rounded-lg', props.className)}>
  99. <CopyCallbackContext.Provider value={handleCopy}>
  100. <ContentFile
  101. projectKeys={projectKeys}
  102. filePath={filePath}
  103. connectionTab={connectionTab}
  104. selectedFrameworkOrTool={selectedFrameworkOrTool}
  105. connectionStringPooler={{
  106. transactionShared: connectionStringsShared.pooler.uri,
  107. sessionShared: connectionStringsShared.pooler.uri.replace('6543', '5432'),
  108. transactionDedicated: connectionStringsDedicated?.pooler.uri,
  109. sessionDedicated: connectionStringsDedicated?.pooler.uri.replace('6543', '5432'),
  110. ipv4SupportedForDedicatedPooler: !!ipv4Addon,
  111. direct: connectionStringsShared.direct.uri,
  112. }}
  113. onCopy={handleCopy}
  114. />
  115. </CopyCallbackContext.Provider>
  116. </div>
  117. )
  118. }
  119. )
  120. ConnectTabContent.displayName = 'ConnectTabContent'