mirror of
https://github.com/supabase/supabase.git
synced 2026-10-12 04:45:08 +03:00
Adds the new `dedicated_pooler` entitlement and it's respective entitlement checks in the following components: `ConnectTabContent`, `ConnectStepsSection`, `LogsSidebarMenuV2` and `ConnectionPooling` ## Testing ### ConnectTabContent and ConnectStepsSection - Head to `project/_?showConnect=true` with an Org on the Pro Plan or above - Assert that the `Dedicated Pooler` option is available. <img width="1007" height="641" alt="image" src="https://github.com/user-attachments/assets/b4891544-b84b-4745-9e25-4cbc7c76686c" /> ### IPv4SidePanel - Head to `/project/_/database/settings` with an Org on the Pro Plan and above and assert that the Dedicated Pooler is available <img width="889" height="562" alt="image" src="https://github.com/user-attachments/assets/3150cb3d-18e9-4b34-bc9b-2589d0a33c5f" /> ### Dedicated Pooler Logs - Head to `/project/_/logs/dedicated-pooler-logs` with an Org on the Pro Plan and assert that you are in the `dedicated-poolers-logs` page. - Head to `/project/_/logs/dedicated-pooler-logs` with an Org on the Free Plan and assert that you are redirected to `/logs/pooler-logs` ### Logs Sidebar - Head to `/project/jdvjfujajfyywbaaakje/logs/explorer` with an Org on the Free Plan - Assert that only the "Poolers" collection is shown - Head to `/project/jdvjfujajfyywbaaakje/logs/explorer` with an Org on the Pro Plan - Assert that the Dedicated and Shared Poolers collections are shown <img width="251" height="774" alt="image" src="https://github.com/user-attachments/assets/747bceee-d5e7-4a8f-911d-6b02cdb115eb" />
133 lines
5.4 KiB
TypeScript
133 lines
5.4 KiB
TypeScript
import dynamic from 'next/dynamic'
|
|
import { forwardRef, HTMLAttributes, useMemo } from 'react'
|
|
|
|
import { useParams } from 'common'
|
|
import { useProjectSettingsV2Query } from 'data/config/project-settings-v2-query'
|
|
import { usePgbouncerConfigQuery } from 'data/database/pgbouncer-config-query'
|
|
import { useSupavisorConfigurationQuery } from 'data/database/supavisor-configuration-query'
|
|
import { useProjectAddonsQuery } from 'data/subscriptions/project-addons-query'
|
|
import { useCheckEntitlements } from 'hooks/misc/useCheckEntitlements'
|
|
import { pluckObjectFields } from 'lib/helpers'
|
|
import { useTrack } from 'lib/telemetry/track'
|
|
import { cn, CopyCallbackContext } from 'ui'
|
|
import { GenericSkeletonLoader } from 'ui-patterns/ShimmeringLoader'
|
|
import { getAddons } from '../Billing/Subscription/Subscription.utils'
|
|
import type { projectKeys } from './Connect.types'
|
|
import { getConnectionStrings } from './DatabaseSettings.utils'
|
|
|
|
interface ConnectContentTabProps extends HTMLAttributes<HTMLDivElement> {
|
|
projectKeys: projectKeys
|
|
filePath: string
|
|
connectionTab: 'App Frameworks' | 'Mobile Frameworks' | 'ORMs'
|
|
selectedFrameworkOrTool: string
|
|
connectionStringPooler?: {
|
|
transactionShared: string
|
|
sessionShared: string
|
|
transactionDedicated?: string
|
|
sessionDedicated?: string
|
|
ipv4SupportedForDedicatedPooler: boolean
|
|
direct?: string
|
|
}
|
|
}
|
|
|
|
export const ConnectTabContent = forwardRef<HTMLDivElement, ConnectContentTabProps>(
|
|
({ projectKeys, filePath, connectionTab, selectedFrameworkOrTool, ...props }, ref) => {
|
|
const { ref: projectRef } = useParams()
|
|
const track = useTrack()
|
|
const { hasAccess: allowPgBouncerSelection } = useCheckEntitlements('dedicated_pooler')
|
|
|
|
const handleCopy = () => {
|
|
const trackingProperties: {
|
|
connectionTab: 'App Frameworks' | 'Mobile Frameworks' | 'ORMs'
|
|
selectedItem: string
|
|
connectionType?: string
|
|
lang?: string
|
|
} = {
|
|
connectionTab,
|
|
selectedItem: selectedFrameworkOrTool,
|
|
}
|
|
|
|
// Only include connectionType and lang for App Frameworks and Mobile Frameworks
|
|
if (connectionTab !== 'ORMs') {
|
|
trackingProperties.connectionType = 'Framework snippet'
|
|
trackingProperties.lang = filePath.split('/').pop() ?? 'unknown'
|
|
}
|
|
|
|
track('connection_string_copied', trackingProperties)
|
|
}
|
|
|
|
const { data: settings } = useProjectSettingsV2Query({ projectRef })
|
|
const { data: pgbouncerConfig } = usePgbouncerConfigQuery({ projectRef })
|
|
const { data: supavisorConfig } = useSupavisorConfigurationQuery({ projectRef })
|
|
const { data: addons } = useProjectAddonsQuery({ projectRef })
|
|
const { ipv4: ipv4Addon } = getAddons(addons?.selected_addons ?? [])
|
|
|
|
const DB_FIELDS = ['db_host', 'db_name', 'db_port', 'db_user', 'inserted_at']
|
|
const emptyState = { db_user: '', db_host: '', db_port: '', db_name: '' }
|
|
const connectionInfo = pluckObjectFields(settings || emptyState, DB_FIELDS)
|
|
const poolingConfigurationShared = supavisorConfig?.find((x) => x.database_type === 'PRIMARY')
|
|
const poolingConfigurationDedicated = allowPgBouncerSelection ? pgbouncerConfig : undefined
|
|
|
|
const connectionStringsShared = getConnectionStrings({
|
|
connectionInfo,
|
|
poolingInfo: {
|
|
connectionString: poolingConfigurationShared?.connection_string ?? '',
|
|
db_host: poolingConfigurationShared?.db_host ?? '',
|
|
db_name: poolingConfigurationShared?.db_name ?? '',
|
|
db_port: poolingConfigurationShared?.db_port ?? 0,
|
|
db_user: poolingConfigurationShared?.db_user ?? '',
|
|
},
|
|
metadata: { projectRef },
|
|
})
|
|
|
|
const connectionStringsDedicated =
|
|
poolingConfigurationDedicated !== undefined
|
|
? getConnectionStrings({
|
|
connectionInfo,
|
|
poolingInfo: {
|
|
connectionString: poolingConfigurationDedicated.connection_string,
|
|
db_host: poolingConfigurationDedicated.db_host,
|
|
db_name: poolingConfigurationDedicated.db_name,
|
|
db_port: poolingConfigurationDedicated.db_port,
|
|
db_user: poolingConfigurationDedicated.db_user,
|
|
},
|
|
metadata: { projectRef },
|
|
})
|
|
: undefined
|
|
|
|
const ContentFile = useMemo(() => {
|
|
return dynamic<ConnectContentTabProps>(() => import(`./content/${filePath}/content`), {
|
|
loading: () => (
|
|
<div className="p-4 min-h-[331px]">
|
|
<GenericSkeletonLoader />
|
|
</div>
|
|
),
|
|
})
|
|
}, [filePath])
|
|
|
|
return (
|
|
<div ref={ref} {...props} className={cn('border rounded-lg', props.className)}>
|
|
<CopyCallbackContext.Provider value={handleCopy}>
|
|
<ContentFile
|
|
projectKeys={projectKeys}
|
|
filePath={filePath}
|
|
connectionTab={connectionTab}
|
|
selectedFrameworkOrTool={selectedFrameworkOrTool}
|
|
connectionStringPooler={{
|
|
transactionShared: connectionStringsShared.pooler.uri,
|
|
sessionShared: connectionStringsShared.pooler.uri.replace('6543', '5432'),
|
|
transactionDedicated: connectionStringsDedicated?.pooler.uri,
|
|
sessionDedicated: connectionStringsDedicated?.pooler.uri.replace('6543', '5432'),
|
|
ipv4SupportedForDedicatedPooler: !!ipv4Addon,
|
|
direct: connectionStringsShared.direct.uri,
|
|
}}
|
|
onCopy={handleCopy}
|
|
/>
|
|
</CopyCallbackContext.Provider>
|
|
</div>
|
|
)
|
|
}
|
|
)
|
|
|
|
ConnectTabContent.displayName = 'ConnectTabContent'
|