Files
ragflow/internal/service/file/file_delete.go
Jack 965590ccbe Refactor: dataset/document/file service (#17071)
### Summary

Refactor dataset.go document.do file.go file2document.go in
internal/service.
2026-07-20 09:48:24 +08:00

131 lines
3.5 KiB
Go

package file
import (
"context"
"fmt"
"ragflow/internal/common"
"ragflow/internal/entity"
"ragflow/internal/storage"
)
// DeleteFiles deletes files by IDs
// Returns (success, message) where success is true if all files were deleted
func (s *FileService) DeleteFiles(ctx context.Context, uid string, fileIDs []string) (bool, string) {
for _, fileID := range fileIDs {
// 1. Get file
file, err := s.fileDAO.GetByID(fileID)
if err != nil || file == nil {
return false, "File or Folder not found!"
}
// 2. Check tenant_id
if file.TenantID == "" {
return false, "Tenant not found!"
}
// Block root-folder deletion (root folders have parent_id == id)
if file.ParentID == file.ID {
return false, "Root folder cannot be deleted."
}
// 3. Permission check
if !s.checkFilePerm(s.fileDAO, file, uid) {
return false, "No authorization."
}
// 4. Skip dataset source files
if file.SourceType == FileSourceDataset {
continue
}
// 5. Delete based on type
if file.Type == FileTypeFolder {
if err := s.deleteFolderRecursive(ctx, file, uid); err != nil {
return false, fmt.Sprintf("Failed to delete folder: %v", err)
}
} else {
if err := s.deleteSingleFile(ctx, file); err != nil {
return false, fmt.Sprintf("Failed to delete file: %v", err)
}
}
}
return true, ""
}
// deleteSingleFile deletes a single file (not folder)
// Matches Python's _delete_single_file function
func (s *FileService) deleteSingleFile(ctx context.Context, file *entity.File) error {
// 1. Delete storage object
if file.Location != nil && *file.Location != "" {
storageImpl := storage.GetStorageFactory().GetStorage()
if storageImpl != nil {
if err := storageImpl.Remove(file.ParentID, *file.Location); err != nil {
common.Logger.Error(fmt.Sprintf("Fail to remove object: %s/%s, error: %v", file.ParentID, *file.Location, err))
}
}
}
// 2. Handle associated documents
informs, err := s.file2DocumentDAO.GetByFileID(file.ID)
if err != nil {
return fmt.Errorf("failed to get file2document mappings: %w", err)
}
if len(informs) > 0 {
for _, inform := range informs {
if inform.DocumentID == nil {
continue
}
docID := *inform.DocumentID
if s.documentService != nil {
if err := s.documentService.RemoveDocumentKeepFile(docID); err != nil {
common.Logger.Error(fmt.Sprintf("Fail to remove document: %s, error: %v", docID, err))
}
}
}
// Delete file2document mapping (outside the loop, called once - matching Python behavior)
if err := s.file2DocumentDAO.DeleteByFileID(file.ID); err != nil {
return fmt.Errorf("failed to delete file2document mapping: %w", err)
}
}
// 3. Delete file record
if err := s.fileDAO.Delete(file.ID); err != nil {
return err
}
return nil
}
// deleteFolderRecursive recursively deletes a folder and its contents
// Matches Python's _delete_folder_recursive function
func (s *FileService) deleteFolderRecursive(ctx context.Context, folder *entity.File, uid string) error {
// Get all sub-files
subFiles, err := s.fileDAO.ListByParentID(folder.ID)
if err != nil {
return err
}
for _, subFile := range subFiles {
if subFile.Type == FileTypeFolder {
// Recursively delete subfolder
if err := s.deleteFolderRecursive(ctx, subFile, uid); err != nil {
return err
}
} else {
// Delete single file
if err := s.deleteSingleFile(ctx, subFile); err != nil {
return err
}
}
}
// Delete the folder itself
if err := s.fileDAO.Delete(folder.ID); err != nil {
return err
}
return nil
}