From 1091e1b74098e4377feb8b7988dd829485141fb8 Mon Sep 17 00:00:00 2001 From: varg1714 <51254954+varg1714@users.noreply.github.com> Date: Fri, 10 Mar 2023 19:01:49 +0800 Subject: [PATCH] feat: file aggregation and regular rename api (#3788) MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit * 增加文件聚合接口,将给定文件夹下所有文件移动到目标文件夹。 * 增加文件正则重命名接口。 --------- Co-authored-by: varg247 --- server/handles/fsmanage.go | 150 +++++++++++++++++++++++++++++++++++++ server/router.go | 2 + 2 files changed, 152 insertions(+) diff --git a/server/handles/fsmanage.go b/server/handles/fsmanage.go index 7bab2709..27108c8b 100644 --- a/server/handles/fsmanage.go +++ b/server/handles/fsmanage.go @@ -3,12 +3,14 @@ package handles import ( "fmt" stdpath "path" + "regexp" "github.com/alist-org/alist/v3/internal/errs" "github.com/alist-org/alist/v3/internal/fs" "github.com/alist-org/alist/v3/internal/model" "github.com/alist-org/alist/v3/internal/op" "github.com/alist-org/alist/v3/internal/sign" + "github.com/alist-org/alist/v3/pkg/generic" "github.com/alist-org/alist/v3/pkg/utils" "github.com/alist-org/alist/v3/server/common" "github.com/gin-gonic/gin" @@ -92,6 +94,93 @@ func FsMove(c *gin.Context) { common.SuccessResp(c) } +type RecursiveMoveReq struct { + SrcDir string `json:"src_dir"` + DstDir string `json:"dst_dir"` +} + +func FsRecursiveMove(c *gin.Context) { + var req RecursiveMoveReq + if err := c.ShouldBind(&req); err != nil { + common.ErrorResp(c, err, 400) + return + } + + user := c.MustGet("user").(*model.User) + if !user.CanMove() { + common.ErrorResp(c, errs.PermissionDenied, 403) + return + } + srcDir, err := user.JoinPath(req.SrcDir) + if err != nil { + common.ErrorResp(c, err, 403) + return + } + dstDir, err := user.JoinPath(req.DstDir) + if err != nil { + common.ErrorResp(c, err, 403) + return + } + + meta, err := op.GetNearestMeta(srcDir) + if err != nil { + if !errors.Is(errors.Cause(err), errs.MetaNotFound) { + common.ErrorResp(c, err, 500, true) + return + } + } + c.Set("meta", meta) + + rootFiles, err := fs.List(c, srcDir, false) + if err != nil { + common.ErrorResp(c, err, 500) + return + } + + // record the file path + filePathMap := make(map[model.Obj]string) + movingFiles := generic.NewQueue[model.Obj]() + for _, file := range rootFiles { + movingFiles.Push(file) + filePathMap[file] = srcDir + } + + for !movingFiles.IsEmpty() { + + movingFile := movingFiles.Pop() + movingFilePath := fmt.Sprintf("%s/%s", filePathMap[movingFile], movingFile.GetName()) + if movingFile.IsDir() { + // directory, recursive move + subFilePath := movingFilePath + subFiles, err := fs.List(c, subFilePath, true) + if err != nil { + common.ErrorResp(c, err, 500) + return + } + for _, subFile := range subFiles { + movingFiles.Push(subFile) + filePathMap[subFile] = subFilePath + } + } else { + + if movingFilePath == dstDir { + // same directory, don't move + continue + } + + // move + err := fs.Move(c, movingFilePath, dstDir, movingFiles.IsEmpty()) + if err != nil { + common.ErrorResp(c, err, 500) + return + } + } + + } + + common.SuccessResp(c) +} + func FsCopy(c *gin.Context) { var req MoveCopyReq if err := c.ShouldBind(&req); err != nil { @@ -163,6 +252,67 @@ func FsRename(c *gin.Context) { common.SuccessResp(c) } +type RegexRenameReq struct { + SrcDir string `json:"src_dir"` + SrcNameRegex string `json:"src_name_regex"` + NewNameRegex string `json:"new_name_regex"` +} + +func FsRegexRename(c *gin.Context) { + var req RegexRenameReq + if err := c.ShouldBind(&req); err != nil { + common.ErrorResp(c, err, 400) + return + } + user := c.MustGet("user").(*model.User) + if !user.CanRename() { + common.ErrorResp(c, errs.PermissionDenied, 403) + return + } + + reqPath, err := user.JoinPath(req.SrcDir) + if err != nil { + common.ErrorResp(c, err, 403) + return + } + + meta, err := op.GetNearestMeta(reqPath) + if err != nil { + if !errors.Is(errors.Cause(err), errs.MetaNotFound) { + common.ErrorResp(c, err, 500, true) + return + } + } + c.Set("meta", meta) + + srcRegexp, err := regexp.Compile(req.SrcNameRegex) + if err != nil { + common.ErrorResp(c, err, 500) + return + } + + files, err := fs.List(c, reqPath, false) + if err != nil { + common.ErrorResp(c, err, 500) + return + } + + for _, file := range files { + + if srcRegexp.MatchString(file.GetName()) { + filePath := fmt.Sprintf("%s/%s", reqPath, file.GetName()) + newFileName := srcRegexp.ReplaceAllString(file.GetName(), req.NewNameRegex) + if err := fs.Rename(c, filePath, newFileName); err != nil { + common.ErrorResp(c, err, 500) + return + } + } + + } + + common.SuccessResp(c) +} + type RemoveReq struct { Dir string `json:"dir"` Names []string `json:"names"` diff --git a/server/router.go b/server/router.go index b41dcf63..f4246266 100644 --- a/server/router.go +++ b/server/router.go @@ -126,7 +126,9 @@ func _fs(g *gin.RouterGroup) { g.Any("/dirs", handles.FsDirs) g.POST("/mkdir", handles.FsMkdir) g.POST("/rename", handles.FsRename) + g.POST("/regex_rename", handles.FsRegexRename) g.POST("/move", handles.FsMove) + g.POST("/recursive_move", handles.FsRecursiveMove) g.POST("/copy", handles.FsCopy) g.POST("/remove", handles.FsRemove) g.PUT("/put", middlewares.FsUp, handles.FsStream)