BackupItem.tsx119 lines · main
1import { PermissionAction } from '@supabase/shared-types/out/constants'
2import { useParams } from 'common'
3import { Download } from 'lucide-react'
4import { Badge, Tooltip, TooltipContent, TooltipTrigger } from 'ui'
5import { TimestampInfo } from 'ui-patterns'
6
7import { ButtonTooltip } from '@/components/ui/ButtonTooltip'
8import { InlineLink } from '@/components/ui/InlineLink'
9import { useBackupDownloadMutation } from '@/data/database/backup-download-mutation'
10import type { DatabaseBackup } from '@/data/database/backups-query'
11import { useAsyncCheckPermissions } from '@/hooks/misc/useCheckPermissions'
12
13interface BackupItemProps {
14 index: number
15 isHealthy: boolean
16 backup: DatabaseBackup
17 onSelectBackup: () => void
18}
19
20export const BackupItem = ({ index, isHealthy, backup, onSelectBackup }: BackupItemProps) => {
21 const { ref: projectRef } = useParams()
22 const { can: canTriggerScheduledBackups } = useAsyncCheckPermissions(
23 PermissionAction.INFRA_EXECUTE,
24 'queue_job.restore.prepare'
25 )
26
27 const { mutate: downloadBackup, isPending: isDownloading } = useBackupDownloadMutation({
28 onSuccess: (res) => {
29 const { fileUrl } = res
30
31 // Trigger browser download by create,trigger and remove tempLink
32 const tempLink = document.createElement('a')
33 tempLink.href = fileUrl
34 document.body.appendChild(tempLink)
35 tempLink.click()
36 document.body.removeChild(tempLink)
37 },
38 })
39
40 const generateSideButtons = (backup: DatabaseBackup) => {
41 if (backup.status === 'COMPLETED')
42 return (
43 <div className="flex space-x-4">
44 <ButtonTooltip
45 type="default"
46 disabled={!isHealthy || !canTriggerScheduledBackups}
47 onClick={onSelectBackup}
48 tooltip={{
49 content: {
50 side: 'bottom',
51 text: !isHealthy
52 ? 'Cannot be restored as project is not active'
53 : !canTriggerScheduledBackups
54 ? 'You need additional permissions to trigger a restore'
55 : undefined,
56 },
57 }}
58 >
59 Restore
60 </ButtonTooltip>
61
62 {!backup.isPhysicalBackup && (
63 <ButtonTooltip
64 type="default"
65 icon={<Download />}
66 loading={isDownloading}
67 disabled={!canTriggerScheduledBackups || isDownloading}
68 onClick={() => {
69 if (!projectRef) return console.error('Project ref is required')
70 downloadBackup({ ref: projectRef, backup })
71 }}
72 tooltip={{
73 content: {
74 side: 'bottom',
75 text: !canTriggerScheduledBackups
76 ? 'You need additional permissions to download backups'
77 : undefined,
78 },
79 }}
80 >
81 Download
82 </ButtonTooltip>
83 )}
84 </div>
85 )
86 return <Badge variant="warning">Backup In Progress...</Badge>
87 }
88
89 return (
90 <div
91 className={`flex h-12 items-center justify-between px-6 ${
92 index ? 'border-t border-default' : ''
93 }`}
94 >
95 <div className="flex items-center gap-x-2">
96 <TimestampInfo
97 displayAs="utc"
98 utcTimestamp={backup.inserted_at}
99 labelFormat="DD MMM YYYY HH:mm:ss (ZZ)"
100 className="text-left text-sm! font-mono tracking-tight"
101 />
102 <Tooltip>
103 <TooltipTrigger>
104 <Badge variant="default">{backup.isPhysicalBackup ? 'Physical' : 'Logical'}</Badge>
105 </TooltipTrigger>
106 <TooltipContent side="bottom">
107 {backup.isPhysicalBackup
108 ? 'File-level backups of your entire database.'
109 : 'SQL-based backups of your entire database.'}{' '}
110 <InlineLink href="https://supabase.com/blog/postgresql-physical-logical-backups">
111 Learn more
112 </InlineLink>
113 </TooltipContent>
114 </Tooltip>
115 </div>
116 <div>{generateSideButtons(backup)}</div>
117 </div>
118 )
119}