import { SupportCategories } from '@supabase/shared-types/out/constants' import { useFlag, useParams } from 'common' import { getAddons, subscriptionHasHipaaAddon, } from 'components/interfaces/Billing/Subscription/Subscription.utils' import { NoticeBar } from 'components/interfaces/DiskManagement/ui/NoticeBar' import { ProjectUpdateDisabledTooltip } from 'components/interfaces/Organization/BillingSettings/ProjectUpdateDisabledTooltip' import { SupportLink } from 'components/interfaces/Support/SupportLink' import { ScaffoldContainer, ScaffoldDivider, ScaffoldSection, ScaffoldSectionContent, ScaffoldSectionDetail, } from 'components/layouts/Scaffold' import AlertError from 'components/ui/AlertError' import { ButtonTooltip } from 'components/ui/ButtonTooltip' import { mapResponseToAnalyticsData } from 'data/analytics/infra-monitoring-queries' import { useInfraMonitoringAttributesQuery } from 'data/analytics/infra-monitoring-query' import { useProjectSettingsV2Query } from 'data/config/project-settings-v2-query' import { useProjectDetailQuery } from 'data/projects/project-detail-query' import { useOrgSubscriptionQuery } from 'data/subscriptions/org-subscription-query' import { useProjectAddonsQuery } from 'data/subscriptions/project-addons-query' import type { ProjectAddonVariantMeta } from 'data/subscriptions/types' import dayjs from 'dayjs' import { useIsFeatureEnabled } from 'hooks/misc/useIsFeatureEnabled' import { useSelectedOrganizationQuery } from 'hooks/misc/useSelectedOrganization' import { useIsAwsCloudProvider, useIsOrioleDbInAws, useIsProjectActive, useSelectedProjectQuery, } from 'hooks/misc/useSelectedProject' import { getCloudProviderArchitecture } from 'lib/cloudprovider-utils' import { BASE_PATH, DOCS_URL, INSTANCE_MICRO_SPECS, INSTANCE_NANO_SPECS } from 'lib/constants' import { getDatabaseMajorVersion, getSemanticVersion } from 'lib/helpers' import { AlertCircle, ChevronRight, ExternalLink, Info } from 'lucide-react' import { useTheme } from 'next-themes' import Image from 'next/image' import Link from 'next/link' import { useMemo } from 'react' import { useAddonsPagePanel } from 'state/addons-page' import { Alert, Alert_Shadcn_, AlertDescription_Shadcn_, AlertTitle_Shadcn_, Button } from 'ui' import { ComputeBadge } from 'ui-patterns/ComputeBadge' import { GenericSkeletonLoader, ShimmeringLoader } from 'ui-patterns/ShimmeringLoader' import CustomDomainSidePanel from './CustomDomainSidePanel' import IPv4SidePanel from './IPv4SidePanel' import PITRSidePanel from './PITRSidePanel' export const Addons = () => { const { resolvedTheme } = useTheme() const { ref: projectRef } = useParams() const { setPanel } = useAddonsPagePanel() const isAws = useIsAwsCloudProvider() const isProjectActive = useIsProjectActive() const isOrioleDbInAws = useIsOrioleDbInAws() const { projectSettingsCustomDomains, projectAddonsDedicatedIpv4Address } = useIsFeatureEnabled([ 'project_settings:custom_domains', 'project_addons:dedicated_ipv4_address', ]) const { data: selectedOrg } = useSelectedOrganizationQuery() const { data: selectedProject, isPending: isLoadingProject } = useSelectedProjectQuery() const { data: parentProject } = useProjectDetailQuery({ ref: selectedProject?.parent_project_ref, }) const isBranch = parentProject !== undefined const { data: settings } = useProjectSettingsV2Query({ projectRef }) const { data: subscription } = useOrgSubscriptionQuery({ orgSlug: selectedOrg?.slug }) const projectUpdateDisabled = useFlag('disableProjectCreationAndUpdate') const hasHipaaAddon = subscriptionHasHipaaAddon(subscription) && settings?.is_sensitive const cpuArchitecture = getCloudProviderArchitecture(selectedProject?.cloud_provider) // Only projects of version greater than supabase-postgrest-14.1.0.44 can use PITR const sufficientPgVersion = // introduced as generatedSemantic version could be < 141044 even if actual version is indeed past it // eg. 15.1.1.2 leads to 15112 getDatabaseMajorVersion(selectedProject?.dbVersion ?? '') > 14 || getSemanticVersion(selectedProject?.dbVersion ?? '') >= 141044 // [Joshen] We could possibly look into reducing the interval to be more "realtime" // I tried setting the interval to 1m but no data was returned, may need to experiment const startDate = useMemo(() => dayjs().subtract(15, 'minutes').millisecond(0).toISOString(), []) const endDate = useMemo(() => dayjs().millisecond(0).toISOString(), []) const { data: ioBudgetData } = useInfraMonitoringAttributesQuery({ projectRef, attributes: ['disk_io_budget'], interval: '5m', startDate, endDate, }) const mostRecentRemainingIOBudget = useMemo(() => { if (!ioBudgetData) return undefined const mapped = mapResponseToAnalyticsData(ioBudgetData, ['disk_io_budget']) const data = mapped['disk_io_budget']?.data if (!data?.length) return undefined const lastPoint = data[data.length - 1] return { disk_io_budget: lastPoint?.disk_io_budget } }, [ioBudgetData]) const { data: addons, error, isPending: isLoading, isError, isSuccess, } = useProjectAddonsQuery({ projectRef }) const selectedAddons = addons?.selected_addons ?? [] const { computeInstance, pitr, customDomain, ipv4 } = getAddons(selectedAddons) const meta = useMemo(() => { const computeMeta = computeInstance?.variant?.meta as ProjectAddonVariantMeta | undefined if (!computeMeta && selectedProject?.infra_compute_size === 'nano') { return INSTANCE_NANO_SPECS } else if (selectedProject?.infra_compute_size === 'micro') { return INSTANCE_MICRO_SPECS } return computeMeta }, [selectedProject, computeInstance]) const canUpdateIPv4 = settings?.db_ip_addr_config === 'ipv6' return ( <> {isBranch && ( You are currently on a preview branch of your project Updating addons here will only apply to this preview branch. To manage your addons, for your main branch, please visit the{' '} main branch . )} {isLoading && (
)} {isError && (
)} {isSuccess && ( <> {selectedProject?.infra_compute_size === 'nano' && subscription?.plan.id !== 'free' && ( Free compute upgrade to Micro Paid Plans include a free upgrade to Micro compute. Your project is ready to upgrade for no additional charges. )}

Compute Size

More information

About compute add-ons

Connection Pooler

Compute size

Current option:

{isLoading || (computeInstance === undefined && isLoadingProject) ? ( ) : (
)} } /> {Number(mostRecentRemainingIOBudget?.disk_io_budget) === 0 ? (

Your workload is currently running at the baseline disk IO bandwidth at{' '} {meta?.baseline_disk_io_mbs?.toLocaleString() ?? '-'} Mbps and may suffer degradation in performance.

Consider upgrading to a larger compute instance for a higher baseline throughput.

) : Number(mostRecentRemainingIOBudget?.disk_io_budget) <= 10 ? (

If the disk IO budget drops to zero, your workload will run at the baseline disk IO bandwidth at{' '} {meta?.baseline_disk_io_mbs?.toLocaleString() ?? '-'} Mbps and may suffer degradation in performance.

Consider upgrading to a larger compute instance for a higher baseline throughput.

) : null}

Memory

{meta?.memory_gb ?? '-'} GB

CPU

{meta?.cpu_cores ?? '?'}-core {cpuArchitecture}{' '} {meta?.cpu_dedicated ? '(Dedicated)' : '(Shared)'}

No. of direct connections

{meta?.connections_direct ?? '-'}

No. of pooler connections

{meta?.connections_pooler ?? '-'}

{projectAddonsDedicatedIpv4Address && ( <>

Dedicated IPv4 address

More information

About IPv4 deprecation

IPv4

Current option:

{ipv4 !== undefined ? 'Dedicated IPv4 address is enabled' : 'Dedicated IPv4 address is not enabled'}

)}

Point in time recovery

More information

About PITR backups

{hasHipaaAddon && ( PITR cannot be changed with HIPAA All projects should have PITR enabled by default and cannot be changed with HIPAA enabled. Contact support for further assistance.
)}
Point-In-Time-Recovery

Current option:

{pitr !== undefined ? `Point in time recovery of ${(pitr.variant.meta as any)?.backup_duration_days} days is enabled` : 'Point in time recovery is not enabled'}

{!sufficientPgVersion ? ( Your project is too old to enable PITR

Reach out to us via support if you're interested

) : isOrioleDbInAws ? ( Enable point in time recovery ) : ( )}
{projectSettingsCustomDomains && ( <>

Custom domain

More information

About custom domains

Custom Domain

Current option:

{customDomain !== undefined ? 'Custom domain is enabled' : 'Custom domain is not enabled'}

{!!customDomain && ( )}
)} )} ) }