DeleteBucketModal.tsx 3.4 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596
  1. import { useParams } from 'common'
  2. import { useRouter } from 'next/router'
  3. import { toast } from 'sonner'
  4. import { extractBucketNameFromDefinition } from './Storage.utils'
  5. import { TextConfirmModal } from '@/components/ui/TextConfirmModalWrapper'
  6. import { useDatabasePoliciesQuery } from '@/data/database-policies/database-policies-query'
  7. import { useDatabasePolicyDeleteMutation } from '@/data/database-policies/database-policy-delete-mutation'
  8. import { useBucketDeleteMutation } from '@/data/storage/bucket-delete-mutation'
  9. import { Bucket } from '@/data/storage/buckets-query'
  10. import { useSelectedProjectQuery } from '@/hooks/misc/useSelectedProject'
  11. export interface DeleteBucketModalProps {
  12. visible: boolean
  13. bucket: Bucket
  14. onClose: () => void
  15. }
  16. export const DeleteBucketModal = ({ visible, bucket, onClose }: DeleteBucketModalProps) => {
  17. const router = useRouter()
  18. const { ref: projectRef, bucketId } = useParams()
  19. const { data: project } = useSelectedProjectQuery()
  20. const { data: policies } = useDatabasePoliciesQuery({
  21. projectRef: project?.ref,
  22. connectionString: project?.connectionString,
  23. schema: 'storage',
  24. })
  25. const { mutateAsync: deletePolicy, isPending: isDeletingPolicies } =
  26. useDatabasePolicyDeleteMutation()
  27. const { mutate: deleteBucket, isPending: isDeletingBucket } = useBucketDeleteMutation({
  28. onSuccess: async () => {
  29. if (!project) return console.error('Project is required')
  30. // Clean up policies from the corresponding bucket that was deleted
  31. const bucketPolicies = (policies ?? []).filter((policy) => {
  32. if (policy.table !== 'objects') return false
  33. const policyBucket = extractBucketNameFromDefinition(policy.definition ?? policy.check)
  34. return policyBucket === bucket.name
  35. })
  36. try {
  37. await Promise.all(
  38. bucketPolicies.map((policy) =>
  39. deletePolicy({
  40. projectRef: project?.ref,
  41. connectionString: project?.connectionString,
  42. originalPolicy: policy,
  43. })
  44. )
  45. )
  46. toast.success(`Successfully deleted bucket ${bucket.id}`)
  47. if (!!bucketId) router.push(`/project/${projectRef}/storage/files`)
  48. onClose()
  49. } catch (error) {
  50. toast.success(
  51. `Successfully deleted bucket ${bucket.id}. However, there was a problem deleting the policies tied to the bucket. Please review them in the storage policies section`
  52. )
  53. }
  54. },
  55. })
  56. const onConfirmDelete = async () => {
  57. if (!projectRef) return console.error('Project ref is required')
  58. if (!bucket) return console.error('No bucket is selected')
  59. deleteBucket({ projectRef, id: bucket.id })
  60. }
  61. return (
  62. <TextConfirmModal
  63. visible={visible}
  64. size="medium"
  65. variant="destructive"
  66. title={`Delete bucket “${bucket.id}”`}
  67. loading={isDeletingBucket || isDeletingPolicies}
  68. confirmPlaceholder="Type bucket name"
  69. confirmString={bucket.id}
  70. confirmLabel="Delete bucket"
  71. onCancel={onClose}
  72. onConfirm={onConfirmDelete}
  73. alert={{
  74. title: 'You cannot recover this bucket once deleted',
  75. description: 'This action cannot be undone',
  76. }}
  77. >
  78. <p className="text-sm">
  79. Your bucket <span className="font-bold text-foreground">{bucket.id}</span> and all of its
  80. contents will be permanently deleted.
  81. </p>
  82. </TextConfirmModal>
  83. )
  84. }