From 1c1cd9b342af882520d01caba558a53cbfce7647 Mon Sep 17 00:00:00 2001 From: Aaron Liu <912394456@qq.com> Date: Tue, 7 Feb 2023 20:07:05 +0800 Subject: [PATCH] feat(dashboard): unlink file while not deleting its physical source (#789) --- assets | 2 +- pkg/crontab/collect.go | 2 +- pkg/filesystem/manage.go | 10 +++++++--- pkg/filesystem/manage_test.go | 6 +++--- pkg/webdav/webdav.go | 15 ++++++++------- service/admin/file.go | 7 ++++--- service/admin/user.go | 2 +- service/explorer/objects.go | 2 +- service/explorer/upload.go | 4 ++-- 9 files changed, 28 insertions(+), 22 deletions(-) diff --git a/assets b/assets index d72688d..09a5168 160000 --- a/assets +++ b/assets @@ -1 +1 @@ -Subproject commit d72688d7fceaa0bc916fc6637e928dfd5b5a1c43 +Subproject commit 09a5168b23349cb1d0201b544ba48ccb583886be diff --git a/pkg/crontab/collect.go b/pkg/crontab/collect.go index 0667570..a5678f6 100644 --- a/pkg/crontab/collect.go +++ b/pkg/crontab/collect.go @@ -88,7 +88,7 @@ func uploadSessionCollect() { continue } - if err = fs.Delete(context.Background(), []uint{}, filesIDs, false); err != nil { + if err = fs.Delete(context.Background(), []uint{}, filesIDs, false, false); err != nil { util.Log().Warning("Failed to delete upload session: %s", err) } diff --git a/pkg/filesystem/manage.go b/pkg/filesystem/manage.go index 34c54c7..670f4cd 100644 --- a/pkg/filesystem/manage.go +++ b/pkg/filesystem/manage.go @@ -120,8 +120,9 @@ func (fs *FileSystem) Move(ctx context.Context, dirs, files []uint, src, dst str return err } -// Delete 递归删除对象, force 为 true 时强制删除文件记录,忽略物理删除是否成功 -func (fs *FileSystem) Delete(ctx context.Context, dirs, files []uint, force bool) error { +// Delete 递归删除对象, force 为 true 时强制删除文件记录,忽略物理删除是否成功; +// unlink 为 true 时只删除虚拟文件系统的文件记录,不删除物理文件。 +func (fs *FileSystem) Delete(ctx context.Context, dirs, files []uint, force, unlink bool) error { // 已删除的文件ID var deletedFiles = make([]*model.File, 0, len(fs.FileTarget)) // 删除失败的文件的父目录ID @@ -155,7 +156,10 @@ func (fs *FileSystem) Delete(ctx context.Context, dirs, files []uint, force bool policyGroup := fs.GroupFileByPolicy(ctx, filesToBeDelete) // 按照存储策略分组删除对象 - failed := fs.deleteGroupedFile(ctx, policyGroup) + failed := make(map[uint][]string) + if !unlink { + failed = fs.deleteGroupedFile(ctx, policyGroup) + } // 整理删除结果 for i := 0; i < len(fs.FileTarget); i++ { diff --git a/pkg/filesystem/manage_test.go b/pkg/filesystem/manage_test.go index 2ec0aec..1a7e45d 100644 --- a/pkg/filesystem/manage_test.go +++ b/pkg/filesystem/manage_test.go @@ -3,13 +3,13 @@ package filesystem import ( "context" "errors" + "github.com/DATA-DOG/go-sqlmock" "os" "testing" "github.com/cloudreve/Cloudreve/v3/pkg/filesystem/response" testMock "github.com/stretchr/testify/mock" - "github.com/DATA-DOG/go-sqlmock" model "github.com/cloudreve/Cloudreve/v3/models" "github.com/cloudreve/Cloudreve/v3/pkg/cache" "github.com/cloudreve/Cloudreve/v3/pkg/conf" @@ -505,7 +505,7 @@ func TestFileSystem_Delete(t *testing.T) { fs.FileTarget = []model.File{} fs.DirTarget = []model.Folder{} - err := fs.Delete(ctx, []uint{1}, []uint{1}, true) + err := fs.Delete(ctx, []uint{1}, []uint{1}, true, false) asserts.NoError(err) } //全部成功 @@ -563,7 +563,7 @@ func TestFileSystem_Delete(t *testing.T) { fs.FileTarget = []model.File{} fs.DirTarget = []model.Folder{} - err = fs.Delete(ctx, []uint{1}, []uint{1}, false) + err = fs.Delete(ctx, []uint{1}, []uint{1}, false, false) asserts.NoError(err) } diff --git a/pkg/webdav/webdav.go b/pkg/webdav/webdav.go index 208bd12..8ee02cf 100644 --- a/pkg/webdav/webdav.go +++ b/pkg/webdav/webdav.go @@ -218,7 +218,7 @@ func (h *Handler) confirmLocks(r *http.Request, src, dst string, fs *filesystem. }, 0, nil } -//OK +// OK func (h *Handler) handleOptions(w http.ResponseWriter, r *http.Request, fs *filesystem.FileSystem) (status int, err error) { reqPath, status, err := h.stripPrefix(r.URL.Path, fs.User.ID) if err != nil { @@ -303,7 +303,7 @@ func (h *Handler) handleDelete(w http.ResponseWriter, r *http.Request, fs *files // 尝试作为文件删除 if ok, file := fs.IsFileExist(reqPath); ok { - if err := fs.Delete(ctx, []uint{}, []uint{file.ID}, false); err != nil { + if err := fs.Delete(ctx, []uint{}, []uint{file.ID}, false, false); err != nil { return http.StatusMethodNotAllowed, err } return http.StatusNoContent, nil @@ -311,7 +311,7 @@ func (h *Handler) handleDelete(w http.ResponseWriter, r *http.Request, fs *files // 尝试作为目录删除 if ok, folder := fs.IsPathExist(reqPath); ok { - if err := fs.Delete(ctx, []uint{folder.ID}, []uint{}, false); err != nil { + if err := fs.Delete(ctx, []uint{folder.ID}, []uint{}, false, false); err != nil { return http.StatusMethodNotAllowed, err } return http.StatusNoContent, nil @@ -783,10 +783,11 @@ const ( // infiniteDepth. Parsing any other string returns invalidDepth. // // Different WebDAV methods have further constraints on valid depths: -// - PROPFIND has no further restrictions, as per section 9.1. -// - COPY accepts only "0" or "infinity", as per section 9.8.3. -// - MOVE accepts only "infinity", as per section 9.9.2. -// - LOCK accepts only "0" or "infinity", as per section 9.10.3. +// - PROPFIND has no further restrictions, as per section 9.1. +// - COPY accepts only "0" or "infinity", as per section 9.8.3. +// - MOVE accepts only "infinity", as per section 9.9.2. +// - LOCK accepts only "0" or "infinity", as per section 9.10.3. +// // These constraints are enforced by the handleXxx methods. func parseDepth(s string) int { switch s { diff --git a/service/admin/file.go b/service/admin/file.go index 8279c78..a029989 100644 --- a/service/admin/file.go +++ b/service/admin/file.go @@ -19,8 +19,9 @@ type FileService struct { // FileBatchService 文件批量操作服务 type FileBatchService struct { - ID []uint `json:"id" binding:"min=1"` - Force bool `json:"force"` + ID []uint `json:"id" binding:"min=1"` + Force bool `json:"force"` + UnlinkOnly bool `json:"unlink"` } // ListFolderService 列目录结构 @@ -128,7 +129,7 @@ func (service *FileBatchService) Delete(c *gin.Context) serializer.Response { } // 执行删除 - fs.Delete(context.Background(), []uint{}, ids, service.Force) + fs.Delete(context.Background(), []uint{}, ids, service.Force, service.UnlinkOnly) fs.Recycle() } }(userFile) diff --git a/service/admin/user.go b/service/admin/user.go index 9ade2ed..0b66deb 100644 --- a/service/admin/user.go +++ b/service/admin/user.go @@ -66,7 +66,7 @@ func (service *UserBatchService) Delete() serializer.Response { if err != nil { return serializer.Err(serializer.CodeInternalSetting, "User's root folder not exist", err) } - fs.Delete(context.Background(), []uint{root.ID}, []uint{}, false) + fs.Delete(context.Background(), []uint{root.ID}, []uint{}, false, false) // 删除相关任务 model.DB.Where("user_id = ?", uid).Delete(&model.Download{}) diff --git a/service/explorer/objects.go b/service/explorer/objects.go index 5099c7e..2aacefd 100644 --- a/service/explorer/objects.go +++ b/service/explorer/objects.go @@ -274,7 +274,7 @@ func (service *ItemIDService) Delete(ctx context.Context, c *gin.Context) serial // 删除对象 items := service.Raw() - err = fs.Delete(ctx, items.Dirs, items.Items, false) + err = fs.Delete(ctx, items.Dirs, items.Items, false, false) if err != nil { return serializer.Err(serializer.CodeNotSet, err.Error(), err) } diff --git a/service/explorer/upload.go b/service/explorer/upload.go index b3501be..0cac8df 100644 --- a/service/explorer/upload.go +++ b/service/explorer/upload.go @@ -237,7 +237,7 @@ func (service *UploadSessionService) Delete(ctx context.Context, c *gin.Context) } // 删除文件 - if err := fs.Delete(ctx, []uint{}, []uint{file.ID}, false); err != nil { + if err := fs.Delete(ctx, []uint{}, []uint{file.ID}, false, false); err != nil { return serializer.Err(serializer.CodeInternalSetting, "Failed to delete upload session", err) } @@ -283,7 +283,7 @@ func DeleteAllUploadSession(ctx context.Context, c *gin.Context) serializer.Resp } // 删除文件 - if err := fs.Delete(ctx, []uint{}, fileIDs, false); err != nil { + if err := fs.Delete(ctx, []uint{}, fileIDs, false, false); err != nil { return serializer.Err(serializer.CodeInternalSetting, "Failed to cleanup upload session", err) }