import { SupportCategories } from '@supabase/shared-types/out/constants' import { LOCAL_STORAGE_KEYS, useParams } from 'common' import { CheckCircle, Download, Loader } from 'lucide-react' import { useEffect, useState } from 'react' import { Button } from 'ui' import { Admonition } from 'ui-patterns/Admonition' import { SupportLink } from '@/components/interfaces/Support/SupportLink' import { ButtonTooltip } from '@/components/ui/ButtonTooltip' import { useBackupDownloadMutation } from '@/data/database/backup-download-mutation' import { useDownloadableBackupQuery } from '@/data/database/backup-query' import { useInvalidateProjectDetailsQuery } from '@/data/projects/project-detail-query' import { useProjectStatusQuery } from '@/data/projects/project-status-query' import { useLongRunningTransitionState } from '@/hooks/misc/useLongRunningTransitionState' import { useSelectedProjectQuery } from '@/hooks/misc/useSelectedProject' import { PROJECT_STATUS } from '@/lib/constants' import { clearPersistedTransitionStartTime, minutesToMilliseconds, } from '@/lib/project-transition-state' import { getRestoreLongRunningThresholdMinutes } from '@/lib/restore-estimate' export const POLL_INTERVAL_MS = 4000 export const RestoringState = () => { const { ref } = useParams() const { data: project } = useSelectedProjectQuery() const [isConfirming, setIsConfirming] = useState(false) const [hasLeftHealthyState, setHasLeftHealthyState] = useState(false) const restoreStateStartStorageKey = ref ? LOCAL_STORAGE_KEYS.PROJECT_RESTORING_STARTED_AT(ref) : null const { data } = useDownloadableBackupQuery({ projectRef: ref }) const backups = data?.backups ?? [] const logicalBackups = backups.filter((b) => !b.isPhysicalBackup) const longRunningThresholdMinutes = getRestoreLongRunningThresholdMinutes(project?.volumeSizeGb) const longRunningThresholdMs = minutesToMilliseconds(longRunningThresholdMinutes) const isTakingLongerThanExpected = useLongRunningTransitionState({ storageKey: restoreStateStartStorageKey, thresholdMs: longRunningThresholdMs, }) const { invalidateProjectDetailsQuery } = useInvalidateProjectDetailsQuery() const { data: projectStatusData } = useProjectStatusQuery( { projectRef: ref }, { enabled: project?.status !== PROJECT_STATUS.ACTIVE_HEALTHY, refetchInterval: (query) => { const status = query.state.data?.status if (status === PROJECT_STATUS.RESTORE_FAILED) return false if (status === PROJECT_STATUS.ACTIVE_HEALTHY && hasLeftHealthyState) return false return POLL_INTERVAL_MS }, } ) const projectStatus = projectStatusData?.status // Right after a restore is triggered the status endpoint can still report the stale // pre-restore ACTIVE_HEALTHY, so completion is only trusted once the status has been // observed leaving the healthy state. if ( !hasLeftHealthyState && projectStatus !== undefined && projectStatus !== PROJECT_STATUS.ACTIVE_HEALTHY ) { setHasLeftHealthyState(true) } const hasRestoreFailed = projectStatus === PROJECT_STATUS.RESTORE_FAILED const isCompleted = hasLeftHealthyState && projectStatus === PROJECT_STATUS.ACTIVE_HEALTHY const { mutate: downloadBackup, isPending: isDownloading } = useBackupDownloadMutation({ onSuccess: (res) => { const { fileUrl } = res // Trigger browser download by create,trigger and remove tempLink const tempLink = document.createElement('a') tempLink.href = fileUrl document.body.appendChild(tempLink) tempLink.click() document.body.removeChild(tempLink) }, }) const onClickDownloadBackup = () => { if (!ref) return console.error('Project ref is required') if (logicalBackups.length === 0) return console.error('No available backups to download') downloadBackup({ ref, backup: logicalBackups[0] }) } const onConfirm = async () => { if (!ref) return console.error('Project ref is required') setIsConfirming(true) try { await invalidateProjectDetailsQuery(ref) } finally { setIsConfirming(false) } } useEffect(() => { if (!isCompleted && !hasRestoreFailed) return if (restoreStateStartStorageKey) { clearPersistedTransitionStartTime(restoreStateStartStorageKey) } if (hasRestoreFailed && ref) void invalidateProjectDetailsQuery(ref) }, [ isCompleted, hasRestoreFailed, restoreStateStartStorageKey, ref, invalidateProjectDetailsQuery, ]) return (
Restoration complete!
Your project has been successfully restored and is now back online.
Restoration in progress
Restoration can take from a few minutes up to several hours depending on the size of your database. Your project will be offline while the restoration is running.
{isTakingLongerThanExpected && (