mirror of
https://github.com/supabase/supabase.git
synced 2026-10-05 09:25:06 +03:00
Update checks for showing ipv4 callout (#51018)
## Context Updates checks for showing IPv4 add on callouts, should only be visible if the project's provider is AWS Involves updating 3 files: - `ConnectionPooling.tsx` - adds check for cloud provider + enabled features - `ConnectStepsSection.tsx` - adds check for cloud provider + enabled features - `Ipv4StatusPanel.tsx` - realised this is dead code, so deleted <!-- This is an auto-generated comment: release notes by coderabbit.ai --> ## Summary by CodeRabbit * **Updates** * IPv4 add-on notices in connection setup and connection pooling appear only for AWS projects with IPv4 enabled, when the existing connection and configuration requirements are met. * **Removals** * The standalone IPv4 status panel has been removed from the connection setup flow. <!-- end of auto-generated comment: release notes by coderabbit.ai -->
This commit is contained in:
1 parent
247e9d41fb
commit
b1b2cf3e6b
6 files changed
+50
-178
No files matched your search
@@ -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<HTMLDivElement | null>(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,
|
||||
|
||||
@@ -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)
|
||||
}
|
||||
|
||||
|
||||
+10
@@ -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)
|
||||
})
|
||||
|
||||
-174
@@ -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 (
|
||||
<div className={cn('relative inline-flex', className)}>
|
||||
<svg
|
||||
xmlns="http://www.w3.org/2000/svg"
|
||||
fill="none"
|
||||
viewBox="0 0 24 24"
|
||||
strokeWidth="1"
|
||||
stroke="currentColor"
|
||||
className="size-6 stroke-foreground-lighter"
|
||||
>
|
||||
<path
|
||||
strokeLinecap="round"
|
||||
strokeLinejoin="round"
|
||||
d="M12 21a9.004 9.004 0 0 0 8.716-6.747M12 21a9.004 9.004 0 0 1-8.716-6.747M12 21c2.485 0 4.5-4.03 4.5-9S14.485 3 12 3m0 18c-2.485 0-4.5-4.03-4.5-9S9.515 3 12 3m0 0a8.997 8.997 0 0 1 7.843 4.582M12 3a8.997 8.997 0 0 0-7.843 4.582m15.686 0A11.953 11.953 0 0 1 12 10.5c-2.998 0-5.74-1.1-7.843-2.918m15.686 0A8.959 8.959 0 0 1 21 12c0 .778-.099 1.533-.284 2.253m0 0A17.919 17.919 0 0 1 12 16.5c-3.162 0-6.133-.815-8.716-2.247m0 0A9.015 9.015 0 0 1 3 12c0-1.605.42-3.113 1.157-4.418"
|
||||
/>
|
||||
</svg>
|
||||
|
||||
{!active ? (
|
||||
<div className="absolute -right-1.5 -top-1.5 bg-destructive rounded-sm w-4 h-4 flex items-center justify-center">
|
||||
<X size={10} strokeWidth={4} className="text-white rounded-full" />
|
||||
</div>
|
||||
) : (
|
||||
<div className="absolute -right-1.5 -top-1.5 bg-brand-500 rounded-sm w-4 h-4 flex items-center justify-center">
|
||||
<svg
|
||||
width="10"
|
||||
height="10"
|
||||
viewBox="0 0 10 10"
|
||||
fill="none"
|
||||
xmlns="http://www.w3.org/2000/svg"
|
||||
>
|
||||
<path
|
||||
d="M8.33325 2.5L3.74992 7.08333L1.66659 5"
|
||||
stroke="white"
|
||||
strokeWidth="2"
|
||||
strokeLinecap="round"
|
||||
strokeLinejoin="round"
|
||||
/>
|
||||
</svg>
|
||||
</div>
|
||||
)}
|
||||
</div>
|
||||
)
|
||||
}
|
||||
|
||||
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 (
|
||||
<div className="flex flex-col -space-y-px w-full">
|
||||
{method === 'session' ? (
|
||||
<div className="border border-muted px-5 flex gap-7 items-center py-3 rounded-sm bg-alternative/50">
|
||||
<div className="flex w-6 h-6 rounded-sm items-center justify-center gap-2 shrink-0 bg-surface-100">
|
||||
<WarningIcon />
|
||||
</div>
|
||||
<div className="flex flex-col">
|
||||
<span className="text-xs text-foreground">Only use on a IPv4 network</span>
|
||||
<div className="flex flex-col text-xs text-foreground-lighter">
|
||||
<p>Session pooler connections are IPv4 proxied for free.</p>
|
||||
<p>Use Direct Connection if connecting via an IPv6 network.</p>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
) : (
|
||||
<>
|
||||
<div
|
||||
className={cn(
|
||||
'border border-muted px-5 flex gap-7 items-center py-3 first:rounded-t',
|
||||
ipv4Status.type === 'error' ? 'rounded-b-none' : 'last:rounded-b'
|
||||
)}
|
||||
>
|
||||
<div className="flex items-center gap-2">
|
||||
<IPv4StatusIcon active={ipv4Status.type === 'success'} />
|
||||
</div>
|
||||
<div className="flex flex-col">
|
||||
<span className="text-xs text-foreground">{ipv4Status.title}</span>
|
||||
{ipv4Status.description &&
|
||||
(typeof ipv4Status.description === 'string' ? (
|
||||
<span className="text-xs text-foreground-lighter">{ipv4Status.description}</span>
|
||||
) : (
|
||||
ipv4Status.description
|
||||
))}
|
||||
{links.length > 0 && (
|
||||
<div className="flex items-center gap-x-2 mt-2">
|
||||
{links.map((link) => (
|
||||
<Button key={link.text} asChild size="tiny">
|
||||
<Link href={link.url} className="text-xs text-light hover:text-foreground">
|
||||
{link.text}
|
||||
</Link>
|
||||
</Button>
|
||||
))}
|
||||
</div>
|
||||
)}
|
||||
</div>
|
||||
</div>
|
||||
|
||||
{ipv4Status.type === 'error' && (
|
||||
<Collapsible className="group -space-y-px">
|
||||
<CollapsibleTrigger
|
||||
asChild
|
||||
className="group/collapse w-full justify-start rounded-t-none !last:rounded-b group-data-open:rounded-b-none border-muted"
|
||||
>
|
||||
<Button
|
||||
size="tiny"
|
||||
className="text-foreground-lighter bg-dash-sidebar!"
|
||||
icon={
|
||||
<ChevronRight
|
||||
className={cn(
|
||||
'group-data-open/collapse:rotate-90 text-foreground-muted transition-transform'
|
||||
)}
|
||||
/>
|
||||
}
|
||||
>
|
||||
Some platforms are IPv4-only:
|
||||
</Button>
|
||||
</CollapsibleTrigger>
|
||||
<CollapsibleContent className="bg-dash-sidebar rounded-b border px-3 py-2">
|
||||
<div className="flex flex-col gap-2">
|
||||
<p className="text-xs text-foreground-light max-w-xs">
|
||||
A few major platforms are IPv4-only and may not work with a Direct Connection:
|
||||
</p>
|
||||
<div className="flex gap-4">
|
||||
<div className="text-foreground text-xs">Vercel</div>
|
||||
<div className="text-foreground text-xs">GitHub Actions</div>
|
||||
<div className="text-foreground text-xs">Render</div>
|
||||
<div className="text-foreground text-xs">Retool</div>
|
||||
</div>
|
||||
<p className="text-xs text-foreground-lighter max-w-xs">
|
||||
If you wish to use a Direct Connection with these, please purchase{' '}
|
||||
<Link
|
||||
href={`/project/${projectRef}/settings/addons?panel=ipv4`}
|
||||
className="text-xs text-light hover:text-foreground"
|
||||
>
|
||||
IPv4 support
|
||||
</Link>
|
||||
.
|
||||
</p>
|
||||
<p className="text-xs text-foreground-lighter max-w-xs">
|
||||
You may also use the{' '}
|
||||
<span className="text-foreground-light">Session Pooler</span> or{' '}
|
||||
<span className="text-foreground-light">Transaction Pooler</span> if you are on
|
||||
a IPv4 network.
|
||||
</p>
|
||||
</div>
|
||||
</CollapsibleContent>
|
||||
</Collapsible>
|
||||
)}
|
||||
</>
|
||||
)}
|
||||
</div>
|
||||
)
|
||||
}
|
||||
+1
@@ -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', () => ({
|
||||
|
||||
+17
-2
@@ -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<z.infer<typeof PoolingConfigurationFormSchema>> = async (data) => {
|
||||
if (!projectRef || isHighAvailability) return
|
||||
|
||||
@@ -187,7 +202,7 @@ export const ConnectionPooling = () => {
|
||||
/>
|
||||
)}
|
||||
|
||||
{isSuccessAddons && !isHighAvailability && !disablePoolModeSelection && !hasIpv4Addon && (
|
||||
{showIpv4Callout && (
|
||||
<Admonition
|
||||
type="default"
|
||||
layout="responsive"
|
||||
|
||||
Reference in new issue
Block a user