import { PermissionAction } from '@supabase/shared-types/out/constants' import { Loader2 } from 'lucide-react' import Link from 'next/link' import { useEffect, useState } from 'react' import { Alert, AlertDescription, AlertTitle, Button } from 'ui' import { Admonition } from 'ui-patterns/admonition' import { GenericSkeletonLoader } from 'ui-patterns/ShimmeringLoader' import { PreviousRestoreItem } from './PreviousRestoreItem' import { PITRForm } from '@/components/interfaces/Database/Backups/PITR/PITRForm' import { BackupsList } from '@/components/interfaces/Database/Backups/RestoreToNewProject/BackupsList' import { ConfirmRestoreDialog } from '@/components/interfaces/Database/Backups/RestoreToNewProject/ConfirmRestoreDialog' import { CreateNewProjectDialog } from '@/components/interfaces/Database/Backups/RestoreToNewProject/CreateNewProjectDialog' import { projectSpecToMonthlyPrice } from '@/components/interfaces/Database/Backups/RestoreToNewProject/RestoreToNewProject.utils' import { DiskType } from '@/components/interfaces/DiskManagement/ui/DiskManagement.constants' import { Markdown } from '@/components/interfaces/Markdown' import AlertError from '@/components/ui/AlertError' import { InlineLink } from '@/components/ui/InlineLink' import NoPermission from '@/components/ui/NoPermission' import Panel from '@/components/ui/Panel' import { UpgradeToPro } from '@/components/ui/UpgradeToPro' import { useDiskAttributesQuery } from '@/data/config/disk-attributes-query' import { useCloneBackupsQuery } from '@/data/projects/clone-query' import { useCloneStatusQuery } from '@/data/projects/clone-status-query' import { useCheckEntitlements } from '@/hooks/misc/useCheckEntitlements' import { useAsyncCheckPermissions } from '@/hooks/misc/useCheckPermissions' import { useSelectedOrganizationQuery } from '@/hooks/misc/useSelectedOrganization' import { useIsAwsK8sCloudProvider, useIsOrioleDb, useSelectedProjectQuery, } from '@/hooks/misc/useSelectedProject' import { DOCS_URL, PROJECT_STATUS } from '@/lib/constants' import { getDatabaseMajorVersion } from '@/lib/helpers' export const RestoreToNewProject = () => { const { data: project } = useSelectedProjectQuery() const { data: organization } = useSelectedOrganizationQuery() const { hasAccess: hasAccessToRestoreToNewProject, isLoading: isLoadingEntitlement } = useCheckEntitlements('backup.restore_to_new_project') const isOrioleDb = useIsOrioleDb() const isAwsK8s = useIsAwsK8sCloudProvider() const [refetchInterval, setRefetchInterval] = useState(false) const [selectedBackupId, setSelectedBackupId] = useState(null) const [showConfirmationDialog, setShowConfirmationDialog] = useState(false) const [showNewProjectDialog, setShowNewProjectDialog] = useState(false) const [recoveryTimeTarget, setRecoveryTimeTarget] = useState(null) const { data: cloneBackups, error, isPending: cloneBackupsLoading, isError, } = useCloneBackupsQuery( { projectRef: project?.ref }, { enabled: hasAccessToRestoreToNewProject } ) const isActiveHealthy = project?.status === PROJECT_STATUS.ACTIVE_HEALTHY const { can: canReadPhysicalBackups, isSuccess: isPermissionsLoaded } = useAsyncCheckPermissions( PermissionAction.READ, 'physical_backups' ) const { can: canTriggerPhysicalBackups } = useAsyncCheckPermissions( PermissionAction.INFRA_EXECUTE, 'queue_job.restore.prepare' ) const PITR_ENABLED = cloneBackups?.pitr_enabled const PHYSICAL_BACKUPS_ENABLED = project?.is_physical_backups_enabled const dbVersion = getDatabaseMajorVersion(project?.dbVersion ?? '') const IS_PG15_OR_ABOVE = dbVersion >= 15 const targetVolumeSizeGb = cloneBackups?.target_volume_size_gb const targetComputeSize = cloneBackups?.target_compute_size const planId = organization?.plan?.id ?? 'free' const { data } = useDiskAttributesQuery({ projectRef: project?.ref }) const storageType = data?.attributes?.type ?? 'gp3' const { data: cloneStatus, refetch: refetchCloneStatus, isPending: cloneStatusLoading, isSuccess: isCloneStatusSuccess, } = useCloneStatusQuery( { projectRef: project?.ref, }, { refetchInterval, refetchOnWindowFocus: false, enabled: PHYSICAL_BACKUPS_ENABLED || PITR_ENABLED, } ) const isLoading = !isPermissionsLoaded || cloneBackupsLoading || cloneStatusLoading useEffect(() => { if (!isCloneStatusSuccess) return const hasTransientState = cloneStatus.clones.some((c) => c.status === 'IN_PROGRESS') if (!hasTransientState) { setRefetchInterval(false) } }, [cloneStatus?.clones, isCloneStatusSuccess]) const previousClones = cloneStatus?.clones const isRestoring = previousClones?.some((c) => c.status === 'IN_PROGRESS') const restoringClone = previousClones?.find((c) => c.status === 'IN_PROGRESS') if (isLoadingEntitlement) { return } if (!hasAccessToRestoreToNewProject) { return ( ) } if (isOrioleDb) { return ( ) } if (isAwsK8s) { return ( ) } if (!canReadPhysicalBackups) { return } if (!canTriggerPhysicalBackups) { return } if (!IS_PG15_OR_ABOVE) { return ( ) } if (!PHYSICAL_BACKUPS_ENABLED) { return ( Physical backups must be enabled to restore your database to a new project.{' '} Learn more } /> ) } if (isLoading) { return } if (isError) { return } if (!isActiveHealthy) { return ( ) } if ( !isLoading && PITR_ENABLED && !cloneBackups?.physicalBackupData.earliestPhysicalBackupDateUnix ) { return ( ) } if (!isLoading && !PITR_ENABLED && cloneBackups?.backups.length === 0) { return ( <> ) } const additionalMonthlySpend = projectSpecToMonthlyPrice({ targetVolumeSizeGb: targetVolumeSizeGb ?? 0, targetComputeSize: targetComputeSize ?? 'nano', planId: planId ?? 'free', storageType: storageType as DiskType, }) return (
{ setShowConfirmationDialog(false) setShowNewProjectDialog(true) }} additionalMonthlySpend={additionalMonthlySpend} /> { refetchCloneStatus() setRefetchInterval(5000) setShowNewProjectDialog(false) }} /> {isRestoring ? ( Restoration in progress

The new project {(restoringClone?.target_project as any)?.name || ''} is currently being created. You'll be able to restore again once the project is ready.

) : null} {previousClones?.length ? (

Previous restorations

{previousClones?.map((c) => ( ))}
) : null} {PITR_ENABLED ? ( <> { setShowConfirmationDialog(true) setRecoveryTimeTarget(v.recoveryTimeTargetUnix) }} earliestAvailableBackupUnix={ cloneBackups?.physicalBackupData.earliestPhysicalBackupDateUnix || 0 } latestAvailableBackupUnix={ cloneBackups?.physicalBackupData.latestPhysicalBackupDateUnix || 0 } /> ) : ( { setSelectedBackupId(id) setShowConfirmationDialog(true) }} /> )}
) }