import { PermissionAction } from '@supabase/shared-types/out/constants' import { useParams } from 'common' import { useEffect, useState } from 'react' import { toast } from 'sonner' import { cn, RadioGroup, RadioGroupLargeItem, SidePanel } from 'ui' import { Admonition } from 'ui-patterns' import { TaxDisclaimer } from '@/components/interfaces/Billing/TaxDisclaimer' import { DocsButton } from '@/components/ui/DocsButton' import { InlineLink } from '@/components/ui/InlineLink' import { UpgradeToPro } from '@/components/ui/UpgradeToPro' import { useProjectAddonRemoveMutation } from '@/data/subscriptions/project-addon-remove-mutation' import { useProjectAddonUpdateMutation } from '@/data/subscriptions/project-addon-update-mutation' import { useProjectAddonsQuery } from '@/data/subscriptions/project-addons-query' import type { AddonVariantId } from '@/data/subscriptions/types' import { useCheckEntitlements } from '@/hooks/misc/useCheckEntitlements' import { useAsyncCheckPermissions } from '@/hooks/misc/useCheckPermissions' import { useIsAwsCloudProvider } from '@/hooks/misc/useSelectedProject' import { DOCS_URL } from '@/lib/constants' import { formatCurrency } from '@/lib/helpers' import { useAddonsPagePanel } from '@/state/addons-page' const IPv4SidePanel = () => { const isAws = useIsAwsCloudProvider() const { ref: projectRef } = useParams() const [selectedOption, setSelectedOption] = useState('ipv4_none') const { can: canUpdateIPv4 } = useAsyncCheckPermissions( PermissionAction.BILLING_WRITE, 'stripe.subscriptions' ) const { panel, closePanel } = useAddonsPagePanel() const visible = panel === 'ipv4' const { data: addons, isPending: isLoading } = useProjectAddonsQuery({ projectRef }) const { mutate: updateAddon, isPending: isUpdating } = useProjectAddonUpdateMutation({ onSuccess: () => { toast.success(`Successfully enabled IPv4`) closePanel() }, onError: (error) => { toast.error(`Unable to enable IPv4: ${error.message}`) }, }) const { mutate: removeAddon, isPending: isRemoving } = useProjectAddonRemoveMutation({ onSuccess: () => { toast.success(`Successfully disabled IPv4.`) closePanel() }, onError: (error) => { toast.error(`Unable to disable IPv4: ${error.message}`) }, }) const isSubmitting = isUpdating || isRemoving const subscriptionIpV4Option = (addons?.selected_addons ?? []).find( (addon) => addon.type === 'ipv4' ) const availableOptions = (addons?.available_addons ?? []).find((addon) => addon.type === 'ipv4')?.variants ?? [] const { hasAccess: hasAccessToIPv4, isLoading: isLoadingEntitlement } = useCheckEntitlements('ipv4') const hasChanges = selectedOption !== (subscriptionIpV4Option?.variant.identifier ?? 'ipv4_none') const selectedIPv4 = availableOptions.find((option) => option.identifier === selectedOption) const ipv4Options = [ { value: 'ipv4_none', id: 'ipv4_none', title: 'No IPv4 address', description: 'Use shared pooler or IPv6 for database connections.', priceContent: ( <>

$0

/ month

), priceRowClassName: 'mt-2', }, ...availableOptions.map((option) => ({ value: option.identifier, id: option.identifier, title: 'Dedicated IPv4 address', description: 'Allow database connections from IPv4 networks.', priceContent: ( <>

{formatCurrency(option.price)}

/ month / database

), priceRowClassName: 'mt-3', })), ] useEffect(() => { if (visible) { if (subscriptionIpV4Option !== undefined) { setSelectedOption(subscriptionIpV4Option.variant.identifier) } else { setSelectedOption('ipv4_none') } } }, [visible, isLoading]) const onConfirm = async () => { if (!projectRef) return console.error('Project ref is required') if (selectedOption === 'ipv4_none' && subscriptionIpV4Option !== undefined) { removeAddon({ projectRef, variant: subscriptionIpV4Option.variant.identifier }) } else { updateAddon({ projectRef, type: 'ipv4', variant: selectedOption as AddonVariantId }) } } return (

Dedicated IPv4 address

} >

Your project’s direct connection endpoint and dedicated pooler are IPv6-only by default. Enable the dedicated IPv4 address add-on to connect from IPv4-only networks.

The shared pooler endpoint accepts IPv4 connections by default and does not require this add-on.

{!isAws && ( )} {isAws && (
{ipv4Options.map((option) => (

{option.title}

{option.description}

{option.priceContent}
))}
)} {hasChanges && ( <> {selectedOption !== 'ipv4_none' && (

By default, this is only applied to the primary database for your project. If{' '} read replicas {' '} are used, each replica also gets its own IPv4 address, with a corresponding{' '} {formatCurrency(selectedIPv4?.price)}{' '} charge.

)}

There are no immediate charges. The add-on is billed at the end of your billing cycle based on your usage and prorated to the hour.

)} {!hasAccessToIPv4 && ( )}
) } export default IPv4SidePanel