BackupItem.tsx 4.0 KB

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