mirror of https://github.com/Xhofe/alist
parent
21b8e7f6e5
commit
e9c74f9959
|
@ -0,0 +1,211 @@
|
||||||
|
package handles
|
||||||
|
|
||||||
|
import (
|
||||||
|
"fmt"
|
||||||
|
"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/pkg/generic"
|
||||||
|
"github.com/alist-org/alist/v3/server/common"
|
||||||
|
"github.com/gin-gonic/gin"
|
||||||
|
"github.com/pkg/errors"
|
||||||
|
)
|
||||||
|
|
||||||
|
type BatchRenameReq struct {
|
||||||
|
SrcDir string `json:"src_dir"`
|
||||||
|
RenameObjects []struct {
|
||||||
|
SrcName string `json:"src_name"`
|
||||||
|
NewName string `json:"new_name"`
|
||||||
|
} `json:"rename_objects"`
|
||||||
|
}
|
||||||
|
|
||||||
|
func FsBatchRename(c *gin.Context) {
|
||||||
|
var req BatchRenameReq
|
||||||
|
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)
|
||||||
|
for _, renameObject := range req.RenameObjects {
|
||||||
|
if renameObject.SrcName == "" || renameObject.NewName == "" {
|
||||||
|
continue
|
||||||
|
}
|
||||||
|
filePath := fmt.Sprintf("%s/%s", reqPath, renameObject.SrcName)
|
||||||
|
if err := fs.Rename(c, filePath, renameObject.NewName); err != nil {
|
||||||
|
common.ErrorResp(c, err, 500)
|
||||||
|
return
|
||||||
|
}
|
||||||
|
}
|
||||||
|
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, &fs.ListArgs{})
|
||||||
|
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 := filePathMap[movingFile]
|
||||||
|
movingFileName := fmt.Sprintf("%s/%s", movingFilePath, movingFile.GetName())
|
||||||
|
if movingFile.IsDir() {
|
||||||
|
// directory, recursive move
|
||||||
|
subFilePath := movingFileName
|
||||||
|
subFiles, err := fs.List(c, movingFileName, &fs.ListArgs{Refresh: 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, movingFileName, dstDir, movingFiles.IsEmpty())
|
||||||
|
if err != nil {
|
||||||
|
common.ErrorResp(c, err, 500)
|
||||||
|
return
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
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, &fs.ListArgs{})
|
||||||
|
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)
|
||||||
|
}
|
|
@ -4,7 +4,6 @@ import (
|
||||||
"fmt"
|
"fmt"
|
||||||
"io"
|
"io"
|
||||||
stdpath "path"
|
stdpath "path"
|
||||||
"regexp"
|
|
||||||
|
|
||||||
"github.com/alist-org/alist/v3/internal/errs"
|
"github.com/alist-org/alist/v3/internal/errs"
|
||||||
"github.com/alist-org/alist/v3/internal/fs"
|
"github.com/alist-org/alist/v3/internal/fs"
|
||||||
|
@ -96,94 +95,6 @@ func FsMove(c *gin.Context) {
|
||||||
common.SuccessResp(c)
|
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, &fs.ListArgs{})
|
|
||||||
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 := filePathMap[movingFile]
|
|
||||||
movingFileName := fmt.Sprintf("%s/%s", movingFilePath, movingFile.GetName())
|
|
||||||
if movingFile.IsDir() {
|
|
||||||
// directory, recursive move
|
|
||||||
subFilePath := movingFileName
|
|
||||||
subFiles, err := fs.List(c, movingFileName, &fs.ListArgs{Refresh: 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, movingFileName, dstDir, movingFiles.IsEmpty())
|
|
||||||
if err != nil {
|
|
||||||
common.ErrorResp(c, err, 500)
|
|
||||||
return
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
}
|
|
||||||
|
|
||||||
common.SuccessResp(c)
|
|
||||||
}
|
|
||||||
|
|
||||||
func FsCopy(c *gin.Context) {
|
func FsCopy(c *gin.Context) {
|
||||||
var req MoveCopyReq
|
var req MoveCopyReq
|
||||||
if err := c.ShouldBind(&req); err != nil {
|
if err := c.ShouldBind(&req); err != nil {
|
||||||
|
@ -255,67 +166,6 @@ func FsRename(c *gin.Context) {
|
||||||
common.SuccessResp(c)
|
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, &fs.ListArgs{})
|
|
||||||
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 {
|
type RemoveReq struct {
|
||||||
Dir string `json:"dir"`
|
Dir string `json:"dir"`
|
||||||
Names []string `json:"names"`
|
Names []string `json:"names"`
|
||||||
|
|
|
@ -130,6 +130,7 @@ func _fs(g *gin.RouterGroup) {
|
||||||
g.Any("/dirs", handles.FsDirs)
|
g.Any("/dirs", handles.FsDirs)
|
||||||
g.POST("/mkdir", handles.FsMkdir)
|
g.POST("/mkdir", handles.FsMkdir)
|
||||||
g.POST("/rename", handles.FsRename)
|
g.POST("/rename", handles.FsRename)
|
||||||
|
g.POST("/batch_rename", handles.FsBatchRename)
|
||||||
g.POST("/regex_rename", handles.FsRegexRename)
|
g.POST("/regex_rename", handles.FsRegexRename)
|
||||||
g.POST("/move", handles.FsMove)
|
g.POST("/move", handles.FsMove)
|
||||||
g.POST("/recursive_move", handles.FsRecursiveMove)
|
g.POST("/recursive_move", handles.FsRecursiveMove)
|
||||||
|
|
Loading…
Reference in New Issue