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