Spaces:
Running
Running
package handles | |
import ( | |
"fmt" | |
"github.com/xhofe/tache" | |
"io" | |
stdpath "path" | |
"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"` | |
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, 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 | |
} | |
} | |
//fs.ClearCache(req.Dir) | |
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 | |
} | |
// record the file path | |
filePathMap := make(map[model.Obj]string) | |
// record the parent file | |
fileParentMap := make(map[model.Obj]model.Obj) | |
// removing files | |
removingFiles := generic.NewQueue[model.Obj]() | |
// removed files | |
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 { | |
// remove empty directory | |
err = fs.Remove(c, removingFilePath) | |
removedFiles[removingFilePath] = true | |
if err != nil { | |
common.ErrorResp(c, err, 500) | |
return | |
} | |
// recheck parent folder | |
parentFile, exist := fileParentMap[removingFile] | |
if exist { | |
removingFiles.Push(parentFile) | |
} | |
} else { | |
// recursive remove | |
for _, subFile := range subFiles { | |
if !subFile.IsDir() { | |
continue | |
} | |
removingFiles.Push(subFile) | |
filePathMap[subFile] = removingFilePath | |
fileParentMap[subFile] = removingFile | |
} | |
} | |
} | |
common.SuccessResp(c) | |
} | |
// Link return real link, just for proxy program, it may contain cookie, so just allowed for admin | |
func Link(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) | |
//rawPath := stdpath.Join(user.BasePath, req.Path) | |
// why need not join base_path? because it's always the full path | |
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 | |
} | |