diff --git a/apps/studio/components/interfaces/ConnectSheet/ConnectStepsSection.tsx b/apps/studio/components/interfaces/ConnectSheet/ConnectStepsSection.tsx index c1dfb5acc5f..0b326b6be0b 100644 --- a/apps/studio/components/interfaces/ConnectSheet/ConnectStepsSection.tsx +++ b/apps/studio/components/interfaces/ConnectSheet/ConnectStepsSection.tsx @@ -35,7 +35,8 @@ import { useProjectAddonsQuery } from '@/data/subscriptions/project-addons-query import { useCheckEntitlements } from '@/hooks/misc/useCheckEntitlements' import { useDeploymentMode } from '@/hooks/misc/useDeploymentMode' import { useIsDataApiEnabled } from '@/hooks/misc/useIsDataApiEnabled' -import { useIsHighAvailability } from '@/hooks/misc/useSelectedProject' +import { useIsFeatureEnabled } from '@/hooks/misc/useIsFeatureEnabled' +import { useIsAwsCloudProvider, useIsHighAvailability } from '@/hooks/misc/useSelectedProject' import { DOCS_URL } from '@/lib/constants' import { pluckObjectFields } from '@/lib/helpers' @@ -208,11 +209,16 @@ function StepContent({ export function ConnectStepsSection({ steps, state, projectKeys }: ConnectStepsSectionProps) { const { ref } = useParams() + const isAws = useIsAwsCloudProvider() const stepsContainerRef = useRef(null) const deploymentMode = useDeploymentMode() const isHighAvailability = useIsHighAvailability() const connectionStringPooler = useConnectionStringPooler(deploymentMode) + const { projectAddonsDedicatedIpv4Address } = useIsFeatureEnabled([ + 'project_addons:dedicated_ipv4_address', + ]) + const { data: ipv4Addon } = useProjectAddonsQuery( { projectRef: ref }, { @@ -223,7 +229,9 @@ export function ConnectStepsSection({ steps, state, projectKeys }: ConnectStepsS } ) const showIpv4AddonNotice = shouldShowIpv4AddonNotice({ + isAws, isPlatform: deploymentMode.isPlatform, + isIpv4Enabled: projectAddonsDedicatedIpv4Address, mode: state.mode, connectionMethod: state.connectionMethod, useSharedPooler: state.useSharedPooler, diff --git a/apps/studio/components/interfaces/ConnectSheet/ConnectStepsSection.utils.ts b/apps/studio/components/interfaces/ConnectSheet/ConnectStepsSection.utils.ts index bba878c92ec..ec97c5a38c4 100644 --- a/apps/studio/components/interfaces/ConnectSheet/ConnectStepsSection.utils.ts +++ b/apps/studio/components/interfaces/ConnectSheet/ConnectStepsSection.utils.ts @@ -23,6 +23,8 @@ export function resolveContentPath(template: string, state: ConnectState): strin } export function shouldShowIpv4AddonNotice({ + isAws, + isIpv4Enabled, isPlatform, mode, connectionMethod, @@ -30,6 +32,8 @@ export function shouldShowIpv4AddonNotice({ hasIpv4Addon, isHighAvailability, }: { + isAws: boolean + isIpv4Enabled: boolean isPlatform: boolean mode: ConnectMode connectionMethod: FieldValue @@ -38,7 +42,15 @@ export function shouldShowIpv4AddonNotice({ isHighAvailability: boolean }): boolean { // The IPv4 add-on does not apply to Multigres connections - if (!isPlatform || mode !== 'direct' || hasIpv4Addon || isHighAvailability) return false + if ( + !isPlatform || + !isAws || + !isIpv4Enabled || + mode !== 'direct' || + hasIpv4Addon || + isHighAvailability + ) + return false return connectionMethod === 'direct' || (connectionMethod === 'transaction' && !useSharedPooler) } diff --git a/apps/studio/components/interfaces/ConnectSheet/__tests__/ConnectStepsSection.utils.test.ts b/apps/studio/components/interfaces/ConnectSheet/__tests__/ConnectStepsSection.utils.test.ts index 7340051e2db..7a3c4fb6072 100644 --- a/apps/studio/components/interfaces/ConnectSheet/__tests__/ConnectStepsSection.utils.test.ts +++ b/apps/studio/components/interfaces/ConnectSheet/__tests__/ConnectStepsSection.utils.test.ts @@ -128,6 +128,8 @@ describe('resolveContentPath', () => { describe('shouldShowIpv4AddonNotice', () => { const BASE = { isPlatform: true, + isAws: true, + isIpv4Enabled: true, mode: 'direct' as const, connectionMethod: 'direct', useSharedPooler: false, @@ -171,6 +173,14 @@ describe('shouldShowIpv4AddonNotice', () => { expect(shouldShowIpv4AddonNotice({ ...BASE, isPlatform: false })).toBe(false) }) + test('returns false when not on AWS (e.g. Multigres/Fly)', () => { + expect(shouldShowIpv4AddonNotice({ ...BASE, isAws: false })).toBe(false) + }) + + test('returns false when the IPv4 add-on is not enabled for the project', () => { + expect(shouldShowIpv4AddonNotice({ ...BASE, isIpv4Enabled: false })).toBe(false) + }) + test('returns false for high-availability projects even with a direct connection and no addon', () => { expect(shouldShowIpv4AddonNotice({ ...BASE, isHighAvailability: true })).toBe(false) }) diff --git a/apps/studio/components/interfaces/ConnectSheet/content/steps/direct-connection/IPv4StatusPanel.tsx b/apps/studio/components/interfaces/ConnectSheet/content/steps/direct-connection/IPv4StatusPanel.tsx deleted file mode 100644 index 669b048adad..00000000000 --- a/apps/studio/components/interfaces/ConnectSheet/content/steps/direct-connection/IPv4StatusPanel.tsx +++ /dev/null @@ -1,174 +0,0 @@ -import { ChevronRight, X } from 'lucide-react' -import Link from 'next/link' -import { ReactNode } from 'react' -import { Button, cn, Collapsible, CollapsibleContent, CollapsibleTrigger, WarningIcon } from 'ui' - -import { IS_PLATFORM } from '@/lib/constants' - -const IPv4StatusIcon = ({ className, active }: { className?: string; active: boolean }) => { - return ( -
- - - - - {!active ? ( -
- -
- ) : ( -
- - - -
- )} -
- ) -} - -export interface IPv4Status { - type: 'error' | 'success' - title: string - description?: string | ReactNode - links?: { text: string; url: string }[] -} - -interface IPv4StatusPanelProps { - method: 'direct' | 'transaction' | 'session' - ipv4Status: IPv4Status - projectRef: string -} - -export function IPv4StatusPanel({ method, ipv4Status, projectRef }: IPv4StatusPanelProps) { - if (!IS_PLATFORM) return null - - const links = ipv4Status.links ?? [] - - return ( -
- {method === 'session' ? ( -
-
- -
-
- Only use on a IPv4 network -
-

Session pooler connections are IPv4 proxied for free.

-

Use Direct Connection if connecting via an IPv6 network.

-
-
-
- ) : ( - <> -
-
- -
-
- {ipv4Status.title} - {ipv4Status.description && - (typeof ipv4Status.description === 'string' ? ( - {ipv4Status.description} - ) : ( - ipv4Status.description - ))} - {links.length > 0 && ( -
- {links.map((link) => ( - - ))} -
- )} -
-
- - {ipv4Status.type === 'error' && ( - - - - - -
-

- A few major platforms are IPv4-only and may not work with a Direct Connection: -

-
-
Vercel
-
GitHub Actions
-
Render
-
Retool
-
-

- If you wish to use a Direct Connection with these, please purchase{' '} - - IPv4 support - - . -

-

- You may also use the{' '} - Session Pooler or{' '} - Transaction Pooler if you are on - a IPv4 network. -

-
-
-
- )} - - )} -
- ) -} diff --git a/apps/studio/components/interfaces/Settings/Database/ConnectionPooling/ConnectionPooling.test.tsx b/apps/studio/components/interfaces/Settings/Database/ConnectionPooling/ConnectionPooling.test.tsx index 48149da7a41..ed09061b26e 100644 --- a/apps/studio/components/interfaces/Settings/Database/ConnectionPooling/ConnectionPooling.test.tsx +++ b/apps/studio/components/interfaces/Settings/Database/ConnectionPooling/ConnectionPooling.test.tsx @@ -46,6 +46,7 @@ vi.mock('@/hooks/misc/useHighAvailability', () => ({ vi.mock('@/hooks/misc/useSelectedProject', () => ({ useSelectedProjectQuery: mockUseSelectedProjectQuery, useIsHighAvailability: () => mockUseHighAvailability().isHighAvailability ?? false, + useIsAwsCloudProvider: () => false, })) vi.mock('@/data/database/max-connections-query', () => ({ diff --git a/apps/studio/components/interfaces/Settings/Database/ConnectionPooling/ConnectionPooling.tsx b/apps/studio/components/interfaces/Settings/Database/ConnectionPooling/ConnectionPooling.tsx index c55ac329c78..09f815555a9 100644 --- a/apps/studio/components/interfaces/Settings/Database/ConnectionPooling/ConnectionPooling.tsx +++ b/apps/studio/components/interfaces/Settings/Database/ConnectionPooling/ConnectionPooling.tsx @@ -48,7 +48,8 @@ import { useProjectAddonsQuery } from '@/data/subscriptions/project-addons-query import { useCheckEntitlements } from '@/hooks/misc/useCheckEntitlements' import { useAsyncCheckPermissions } from '@/hooks/misc/useCheckPermissions' import { useHighAvailability } from '@/hooks/misc/useHighAvailability' -import { useSelectedProjectQuery } from '@/hooks/misc/useSelectedProject' +import { useIsFeatureEnabled } from '@/hooks/misc/useIsFeatureEnabled' +import { useIsAwsCloudProvider, useSelectedProjectQuery } from '@/hooks/misc/useSelectedProject' import { DOCS_URL } from '@/lib/constants' import { preprocessEmptyNumberInput } from '@/lib/forms/zod-number-input' @@ -67,9 +68,15 @@ const PoolingConfigurationFormSchema = z.object({ */ export const ConnectionPooling = () => { const { ref: projectRef } = useParams() + const isAws = useIsAwsCloudProvider() const { data: project } = useSelectedProjectQuery() const { isHighAvailability, isPending: isHighAvailabilityPending } = useHighAvailability() const canLoadPoolingConfig = !isHighAvailability && !isHighAvailabilityPending + + const { projectAddonsDedicatedIpv4Address } = useIsFeatureEnabled([ + 'project_addons:dedicated_ipv4_address', + ]) + const { can: canUpdateConnectionPoolingConfiguration } = useAsyncCheckPermissions( PermissionAction.UPDATE, 'projects', @@ -122,6 +129,14 @@ export const ConnectionPooling = () => { const connectionPoolingUnavailable = pgbouncerConfig?.pool_mode === null const ignoreStartupParameters = pgbouncerConfig?.ignore_startup_parameters + const showIpv4Callout = + isAws && + projectAddonsDedicatedIpv4Address && + isSuccessAddons && + !isHighAvailability && + !disablePoolModeSelection && + !hasIpv4Addon + const onSubmit: SubmitHandler> = async (data) => { if (!projectRef || isHighAvailability) return @@ -187,7 +202,7 @@ export const ConnectionPooling = () => { /> )} - {isSuccessAddons && !isHighAvailability && !disablePoolModeSelection && !hasIpv4Addon && ( + {showIpv4Callout && (