| package handles |
|
|
| import ( |
| "fmt" |
| "io" |
| stdpath "path" |
|
|
| "github.com/alist-org/alist/v3/pkg/tache" |
|
|
| "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" |
| "github.com/pkg/errors" |
| log "github.com/sirupsen/logrus" |
| ) |
|
|
| type MkdirOrLinkReq struct { |
| Path string `json:"path" form:"path"` |
| } |
|
|
| func FsMkdir(c *gin.Context) { |
| var req MkdirOrLinkReq |
| if err := c.ShouldBind(&req); err != nil { |
| common.ErrorResp(c, err, 400) |
| return |
| } |
| user := c.MustGet("user").(*model.User) |
| reqPath, err := user.JoinPath(req.Path) |
| if err != nil { |
| common.ErrorResp(c, err, 403) |
| return |
| } |
| if !user.CanWrite() { |
| meta, err := op.GetNearestMeta(stdpath.Dir(reqPath)) |
| if err != nil { |
| if !errors.Is(errors.Cause(err), errs.MetaNotFound) { |
| common.ErrorResp(c, err, 500, true) |
| return |
| } |
| } |
| if !common.CanWrite(meta, reqPath) { |
| common.ErrorResp(c, errs.PermissionDenied, 403) |
| return |
| } |
| } |
| if err := fs.MakeDir(c, reqPath); err != nil { |
| common.ErrorResp(c, err, 500) |
| return |
| } |
| common.SuccessResp(c) |
| } |
|
|
| type MoveCopyReq struct { |
| SrcDir string `json:"src_dir"` |
| DstDir string `json:"dst_dir"` |
| Override bool `json:"override"` |
| Names []string `json:"names"` |
| } |
|
|
| func FsMove(c *gin.Context) { |
| var req MoveCopyReq |
| if err := c.ShouldBind(&req); err != nil { |
| common.ErrorResp(c, err, 400) |
| return |
| } |
| if len(req.Names) == 0 { |
| common.ErrorStrResp(c, "Empty file names", 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 |
| } |
| for i, name := range req.Names { |
| err := fs.Move(c, stdpath.Join(srcDir, name), dstDir, len(req.Names) > i+1) |
| 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 { |
| common.ErrorResp(c, err, 400) |
| return |
| } |
| if len(req.Names) == 0 { |
| common.ErrorStrResp(c, "Empty file names", 400) |
| return |
| } |
| user := c.MustGet("user").(*model.User) |
| if !user.CanCopy() { |
| 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 |
| } |
| var addedTasks []tache.TaskWithInfo |
| for i, name := range req.Names { |
| t, err := fs.Copy(c, stdpath.Join(srcDir, name), dstDir, req.Override, len(req.Names) > i+1) |
| if t != nil { |
| addedTasks = append(addedTasks, t) |
| } |
| if err != nil { |
| common.ErrorResp(c, err, 500) |
| return |
| } |
| } |
| common.SuccessResp(c, gin.H{ |
| "tasks": getTaskInfos(addedTasks), |
| }) |
| } |
|
|
| |
| |
| |
| type CopyItem struct { |
| SrcFile string `json:"src_file"` |
| DstDir string `json:"dst_dir"` |
| } |
| type CopyItemReq struct { |
| Override bool `json:"override"` |
| Names []CopyItem `json:"names"` |
| } |
|
|
| func FsCopyItem(c *gin.Context) { |
| var req CopyItemReq |
| if err := c.ShouldBind(&req); err != nil { |
| common.ErrorResp(c, err, 400) |
| return |
| } |
| if len(req.Names) == 0 { |
| common.ErrorStrResp(c, "Empty file names", 400) |
| return |
| } |
| user := c.MustGet("user").(*model.User) |
| if !user.CanCopy() { |
| common.ErrorResp(c, errs.PermissionDenied, 403) |
| return |
| } |
| |
| |
| |
| |
| |
| |
| |
| |
| |
| |
| var addedTasks []tache.TaskWithInfo |
| for i, name := range req.Names { |
| t, err := fs.Copy(c, name.SrcFile, name.DstDir, req.Override, len(req.Names) > i+1) |
| if t != nil { |
| addedTasks = append(addedTasks, t) |
| } |
| if err != nil { |
| common.ErrorResp(c, err, 500) |
| return |
| } |
| } |
| common.SuccessResp(c, gin.H{ |
| "tasks": getTaskInfos(addedTasks), |
| }) |
| } |
|
|
| type RenameReq struct { |
| Path string `json:"path"` |
| Name string `json:"name"` |
| } |
|
|
| func FsRename(c *gin.Context) { |
| var req RenameReq |
| 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.Path) |
| if err != nil { |
| common.ErrorResp(c, err, 403) |
| return |
| } |
| if err := fs.Rename(c, reqPath, req.Name); err != nil { |
| common.ErrorResp(c, err, 500) |
| return |
| } |
| common.SuccessResp(c) |
| } |
|
|
| type RemoveReq struct { |
| Dir string `json:"dir"` |
| Names []string `json:"names"` |
| } |
|
|
| func FsRemove(c *gin.Context) { |
| var req RemoveReq |
| if err := c.ShouldBind(&req); err != nil { |
| common.ErrorResp(c, err, 400) |
| return |
| } |
| if len(req.Names) == 0 { |
| common.ErrorStrResp(c, "Empty file names", 400) |
| return |
| } |
| user := c.MustGet("user").(*model.User) |
| if !user.CanRemove() { |
| common.ErrorResp(c, errs.PermissionDenied, 403) |
| return |
| } |
| reqDir, err := user.JoinPath(req.Dir) |
| if err != nil { |
| common.ErrorResp(c, err, 403) |
| return |
| } |
| for _, name := range req.Names { |
| err := fs.Remove(c, stdpath.Join(reqDir, name)) |
| if err != nil { |
| common.ErrorResp(c, err, 500) |
| return |
| } |
| } |
| |
| common.SuccessResp(c) |
| } |
|
|
| type RemoveEmptyDirectoryReq struct { |
| SrcDir string `json:"src_dir"` |
| } |
|
|
| func FsRemoveEmptyDirectory(c *gin.Context) { |
| var req RemoveEmptyDirectoryReq |
| if err := c.ShouldBind(&req); err != nil { |
| common.ErrorResp(c, err, 400) |
| return |
| } |
|
|
| user := c.MustGet("user").(*model.User) |
| if !user.CanRemove() { |
| common.ErrorResp(c, errs.PermissionDenied, 403) |
| return |
| } |
| srcDir, err := user.JoinPath(req.SrcDir) |
| 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 |
| } |
|
|
| |
| filePathMap := make(map[model.Obj]string) |
| |
| fileParentMap := make(map[model.Obj]model.Obj) |
| |
| removingFiles := generic.NewQueue[model.Obj]() |
| |
| removedFiles := make(map[string]bool) |
| for _, file := range rootFiles { |
| if !file.IsDir() { |
| continue |
| } |
| removingFiles.Push(file) |
| filePathMap[file] = srcDir |
| } |
|
|
| for !removingFiles.IsEmpty() { |
|
|
| removingFile := removingFiles.Pop() |
| removingFilePath := fmt.Sprintf("%s/%s", filePathMap[removingFile], removingFile.GetName()) |
|
|
| if removedFiles[removingFilePath] { |
| continue |
| } |
|
|
| subFiles, err := fs.List(c, removingFilePath, &fs.ListArgs{Refresh: true}) |
| if err != nil { |
| common.ErrorResp(c, err, 500) |
| return |
| } |
|
|
| if len(subFiles) == 0 { |
| |
| err = fs.Remove(c, removingFilePath) |
| removedFiles[removingFilePath] = true |
| if err != nil { |
| common.ErrorResp(c, err, 500) |
| return |
| } |
| |
| parentFile, exist := fileParentMap[removingFile] |
| if exist { |
| removingFiles.Push(parentFile) |
| } |
|
|
| } else { |
| |
| for _, subFile := range subFiles { |
| if !subFile.IsDir() { |
| continue |
| } |
| removingFiles.Push(subFile) |
| filePathMap[subFile] = removingFilePath |
| fileParentMap[subFile] = removingFile |
| } |
| } |
|
|
| } |
|
|
| common.SuccessResp(c) |
| } |
|
|
| |
| func Link(c *gin.Context) { |
| var req MkdirOrLinkReq |
| if err := c.ShouldBind(&req); err != nil { |
| common.ErrorResp(c, err, 400) |
| return |
| } |
| |
| |
| |
| rawPath := req.Path |
| storage, err := fs.GetStorage(rawPath, &fs.GetStoragesArgs{}) |
| if err != nil { |
| common.ErrorResp(c, err, 500) |
| return |
| } |
| if storage.Config().OnlyLocal { |
| common.SuccessResp(c, model.Link{ |
| URL: fmt.Sprintf("%s/p%s?d&sign=%s", |
| common.GetApiUrl(c.Request), |
| utils.EncodePath(rawPath, true), |
| sign.Sign(rawPath)), |
| }) |
| return |
| } |
| link, _, err := fs.Link(c, rawPath, model.LinkArgs{IP: c.ClientIP(), Header: c.Request.Header, HttpReq: c.Request}) |
| if err != nil { |
| common.ErrorResp(c, err, 500) |
| return |
| } |
| if link.MFile != nil { |
| defer func(ReadSeekCloser io.ReadCloser) { |
| err := ReadSeekCloser.Close() |
| if err != nil { |
| log.Errorf("close link data error: %v", err) |
| } |
| }(link.MFile) |
| } |
| common.SuccessResp(c, link) |
| return |
| } |
|
|