DeleteVectorBucketModal.tsx 3.4 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106
  1. import { useState } from 'react'
  2. import { toast } from 'sonner'
  3. import { useS3VectorsWrapperInstance } from './useS3VectorsWrapperInstance'
  4. import { TextConfirmModal } from '@/components/ui/TextConfirmModalWrapper'
  5. import { useFDWDeleteMutation } from '@/data/fdw/fdw-delete-mutation'
  6. import { useVectorBucketDeleteMutation } from '@/data/storage/vector-bucket-delete-mutation'
  7. import { deleteVectorBucketIndex } from '@/data/storage/vector-bucket-index-delete-mutation'
  8. import { useVectorBucketsIndexesQuery } from '@/data/storage/vector-buckets-indexes-query'
  9. import { useSelectedProjectQuery } from '@/hooks/misc/useSelectedProject'
  10. export interface DeleteVectorBucketModalProps {
  11. visible: boolean
  12. bucketName?: string
  13. onCancel: () => void
  14. onSuccess: () => void
  15. }
  16. export const DeleteVectorBucketModal = ({
  17. visible,
  18. bucketName,
  19. onCancel,
  20. onSuccess,
  21. }: DeleteVectorBucketModalProps) => {
  22. const { data: project } = useSelectedProjectQuery()
  23. // Has to be a state because we're using a promise.all to delete the indexes
  24. const [isDeletingIndexes, setIsDeletingIndexes] = useState(false)
  25. const { data: vectorBucketWrapper, meta: wrapperMeta } = useS3VectorsWrapperInstance({
  26. bucketId: bucketName,
  27. })
  28. const { mutate: deleteFDW } = useFDWDeleteMutation()
  29. const { mutateAsync: deleteBucket, isPending: isDeletingBucket } = useVectorBucketDeleteMutation({
  30. onSuccess: async () => {
  31. toast.success(`Bucket "${bucketName}" deleted successfully`)
  32. if (vectorBucketWrapper) {
  33. deleteFDW({
  34. projectRef: project?.ref,
  35. connectionString: project?.connectionString,
  36. wrapper: vectorBucketWrapper,
  37. wrapperMeta: wrapperMeta!,
  38. })
  39. }
  40. onSuccess()
  41. },
  42. })
  43. const { data: { indexes = [] } = {}, isPending: isLoadingIndexes } = useVectorBucketsIndexesQuery(
  44. {
  45. projectRef: project?.ref,
  46. vectorBucketName: bucketName,
  47. }
  48. )
  49. const onConfirmDelete = async () => {
  50. if (!project?.ref) return console.error('Project ref is required')
  51. if (!bucketName) return console.error('No bucket is selected')
  52. try {
  53. setIsDeletingIndexes(true)
  54. // delete all indexes from the bucket first
  55. const promises = indexes.map((index) =>
  56. deleteVectorBucketIndex({
  57. projectRef: project?.ref,
  58. bucketName: bucketName,
  59. indexName: index.indexName,
  60. })
  61. )
  62. await Promise.all(promises)
  63. await deleteBucket({ projectRef: project?.ref, bucketName })
  64. } catch (error) {
  65. toast.error(
  66. `Failed to delete bucket: ${error instanceof Error ? error.message : 'Unknown error'}`
  67. )
  68. } finally {
  69. setIsDeletingIndexes(false)
  70. }
  71. }
  72. return (
  73. <TextConfirmModal
  74. visible={visible}
  75. size="medium"
  76. variant="destructive"
  77. title={`Delete bucket “${bucketName}”`}
  78. loading={isDeletingBucket || isDeletingIndexes || isLoadingIndexes}
  79. confirmPlaceholder="Type bucket name"
  80. confirmString={bucketName ?? ''}
  81. confirmLabel="Delete bucket"
  82. onCancel={onCancel}
  83. onConfirm={onConfirmDelete}
  84. alert={{
  85. title: 'You cannot recover this bucket once deleted',
  86. description: 'This action cannot be undone',
  87. }}
  88. >
  89. <p className="text-sm">
  90. Your bucket <span className="font-bold text-foreground">{bucketName}</span> and all of its
  91. contents will be permanently deleted.
  92. </p>
  93. </TextConfirmModal>
  94. )
  95. }