|
package handles |
|
|
|
import ( |
|
"fmt" |
|
stdpath "path" |
|
"strings" |
|
"time" |
|
|
|
"github.com/alist-org/alist/v3/internal/conf" |
|
"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/setting" |
|
"github.com/alist-org/alist/v3/internal/sign" |
|
"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" |
|
) |
|
|
|
type ListReq struct { |
|
model.PageReq |
|
Path string `json:"path" form:"path"` |
|
Password string `json:"password" form:"password"` |
|
Refresh bool `json:"refresh"` |
|
} |
|
|
|
type DirReq struct { |
|
Path string `json:"path" form:"path"` |
|
Password string `json:"password" form:"password"` |
|
ForceRoot bool `json:"force_root" form:"force_root"` |
|
} |
|
|
|
type ObjResp struct { |
|
Name string `json:"name"` |
|
Size int64 `json:"size"` |
|
IsDir bool `json:"is_dir"` |
|
Modified time.Time `json:"modified"` |
|
Created time.Time `json:"created"` |
|
Sign string `json:"sign"` |
|
Thumb string `json:"thumb"` |
|
Type int `json:"type"` |
|
HashInfoStr string `json:"hashinfo"` |
|
HashInfo map[*utils.HashType]string `json:"hash_info"` |
|
} |
|
|
|
type FsListResp struct { |
|
Content []ObjResp `json:"content"` |
|
Total int64 `json:"total"` |
|
Readme string `json:"readme"` |
|
Header string `json:"header"` |
|
Write bool `json:"write"` |
|
Provider string `json:"provider"` |
|
} |
|
|
|
func FsList(c *gin.Context) { |
|
var req ListReq |
|
if err := c.ShouldBind(&req); err != nil { |
|
common.ErrorResp(c, err, 400) |
|
return |
|
} |
|
req.Validate() |
|
user := c.MustGet("user").(*model.User) |
|
reqPath, err := user.JoinPath(req.Path) |
|
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) |
|
if !common.CanAccess(user, meta, reqPath, req.Password) { |
|
common.ErrorStrResp(c, "password is incorrect or you have no permission", 403) |
|
return |
|
} |
|
if !user.CanWrite() && !common.CanWrite(meta, reqPath) && req.Refresh { |
|
common.ErrorStrResp(c, "Refresh without permission", 403) |
|
return |
|
} |
|
objs, err := fs.List(c, reqPath, &fs.ListArgs{Refresh: req.Refresh}) |
|
if err != nil { |
|
common.ErrorResp(c, err, 500) |
|
return |
|
} |
|
total, objs := pagination(objs, &req.PageReq) |
|
provider := "unknown" |
|
storage, err := fs.GetStorage(reqPath, &fs.GetStoragesArgs{}) |
|
if err == nil { |
|
provider = storage.GetStorage().Driver |
|
} |
|
common.SuccessResp(c, FsListResp{ |
|
Content: toObjsResp(objs, reqPath, isEncrypt(meta, reqPath)), |
|
Total: int64(total), |
|
Readme: getReadme(meta, reqPath), |
|
Header: getHeader(meta, reqPath), |
|
Write: user.CanWrite() || common.CanWrite(meta, reqPath), |
|
Provider: provider, |
|
}) |
|
} |
|
|
|
func FsDirs(c *gin.Context) { |
|
var req DirReq |
|
if err := c.ShouldBind(&req); err != nil { |
|
common.ErrorResp(c, err, 400) |
|
return |
|
} |
|
user := c.MustGet("user").(*model.User) |
|
reqPath := req.Path |
|
if req.ForceRoot { |
|
if !user.IsAdmin() { |
|
common.ErrorStrResp(c, "Permission denied", 403) |
|
return |
|
} |
|
} else { |
|
tmp, err := user.JoinPath(req.Path) |
|
if err != nil { |
|
common.ErrorResp(c, err, 403) |
|
return |
|
} |
|
reqPath = tmp |
|
} |
|
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) |
|
if !common.CanAccess(user, meta, reqPath, req.Password) { |
|
common.ErrorStrResp(c, "password is incorrect or you have no permission", 403) |
|
return |
|
} |
|
objs, err := fs.List(c, reqPath, &fs.ListArgs{}) |
|
if err != nil { |
|
common.ErrorResp(c, err, 500) |
|
return |
|
} |
|
dirs := filterDirs(objs) |
|
common.SuccessResp(c, dirs) |
|
} |
|
|
|
type DirResp struct { |
|
Name string `json:"name"` |
|
Modified time.Time `json:"modified"` |
|
} |
|
|
|
func filterDirs(objs []model.Obj) []DirResp { |
|
var dirs []DirResp |
|
for _, obj := range objs { |
|
if obj.IsDir() { |
|
dirs = append(dirs, DirResp{ |
|
Name: obj.GetName(), |
|
Modified: obj.ModTime(), |
|
}) |
|
} |
|
} |
|
return dirs |
|
} |
|
|
|
func getReadme(meta *model.Meta, path string) string { |
|
if meta != nil && (utils.PathEqual(meta.Path, path) || meta.RSub) { |
|
return meta.Readme |
|
} |
|
return "" |
|
} |
|
|
|
func getHeader(meta *model.Meta, path string) string { |
|
if meta != nil && (utils.PathEqual(meta.Path, path) || meta.HeaderSub) { |
|
return meta.Header |
|
} |
|
return "" |
|
} |
|
|
|
func isEncrypt(meta *model.Meta, path string) bool { |
|
if common.IsStorageSignEnabled(path) { |
|
return true |
|
} |
|
if meta == nil || meta.Password == "" { |
|
return false |
|
} |
|
if !utils.PathEqual(meta.Path, path) && !meta.PSub { |
|
return false |
|
} |
|
return true |
|
} |
|
|
|
func pagination(objs []model.Obj, req *model.PageReq) (int, []model.Obj) { |
|
pageIndex, pageSize := req.Page, req.PerPage |
|
total := len(objs) |
|
start := (pageIndex - 1) * pageSize |
|
if start > total { |
|
return total, []model.Obj{} |
|
} |
|
end := start + pageSize |
|
if end > total { |
|
end = total |
|
} |
|
return total, objs[start:end] |
|
} |
|
|
|
func toObjsResp(objs []model.Obj, parent string, encrypt bool) []ObjResp { |
|
var resp []ObjResp |
|
for _, obj := range objs { |
|
thumb, _ := model.GetThumb(obj) |
|
resp = append(resp, ObjResp{ |
|
Name: obj.GetName(), |
|
Size: obj.GetSize(), |
|
IsDir: obj.IsDir(), |
|
Modified: obj.ModTime(), |
|
Created: obj.CreateTime(), |
|
HashInfoStr: obj.GetHash().String(), |
|
HashInfo: obj.GetHash().Export(), |
|
Sign: common.Sign(obj, parent, encrypt), |
|
Thumb: thumb, |
|
Type: utils.GetObjType(obj.GetName(), obj.IsDir()), |
|
}) |
|
} |
|
return resp |
|
} |
|
|
|
type FsGetReq struct { |
|
Path string `json:"path" form:"path"` |
|
Password string `json:"password" form:"password"` |
|
} |
|
|
|
type FsGetResp struct { |
|
ObjResp |
|
RawURL string `json:"raw_url"` |
|
Readme string `json:"readme"` |
|
Header string `json:"header"` |
|
Provider string `json:"provider"` |
|
Related []ObjResp `json:"related"` |
|
} |
|
|
|
func FsGet(c *gin.Context) { |
|
var req FsGetReq |
|
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 |
|
} |
|
meta, err := op.GetNearestMeta(reqPath) |
|
if err != nil { |
|
if !errors.Is(errors.Cause(err), errs.MetaNotFound) { |
|
common.ErrorResp(c, err, 500) |
|
return |
|
} |
|
} |
|
c.Set("meta", meta) |
|
if !common.CanAccess(user, meta, reqPath, req.Password) { |
|
common.ErrorStrResp(c, "password is incorrect or you have no permission", 403) |
|
return |
|
} |
|
obj, err := fs.Get(c, reqPath, &fs.GetArgs{}) |
|
if err != nil { |
|
common.ErrorResp(c, err, 500) |
|
return |
|
} |
|
var rawURL string |
|
|
|
storage, err := fs.GetStorage(reqPath, &fs.GetStoragesArgs{}) |
|
provider := "unknown" |
|
if err == nil { |
|
provider = storage.Config().Name |
|
} |
|
if !obj.IsDir() { |
|
if err != nil { |
|
common.ErrorResp(c, err, 500) |
|
return |
|
} |
|
if storage.Config().MustProxy() || storage.GetStorage().WebProxy { |
|
query := "" |
|
if isEncrypt(meta, reqPath) || setting.GetBool(conf.SignAll) { |
|
query = "?sign=" + sign.Sign(reqPath) |
|
} |
|
if storage.GetStorage().DownProxyUrl != "" { |
|
rawURL = fmt.Sprintf("%s%s?sign=%s", |
|
strings.Split(storage.GetStorage().DownProxyUrl, "\n")[0], |
|
utils.EncodePath(reqPath, true), |
|
sign.Sign(reqPath)) |
|
} else { |
|
rawURL = fmt.Sprintf("%s/p%s%s", |
|
common.GetApiUrl(c.Request), |
|
utils.EncodePath(reqPath, true), |
|
query) |
|
} |
|
} else { |
|
|
|
if url, ok := model.GetUrl(obj); ok { |
|
rawURL = url |
|
} else { |
|
|
|
link, _, err := fs.Link(c, reqPath, model.LinkArgs{ |
|
IP: c.ClientIP(), |
|
Header: c.Request.Header, |
|
HttpReq: c.Request, |
|
}) |
|
if err != nil { |
|
common.ErrorResp(c, err, 500) |
|
return |
|
} |
|
rawURL = link.URL |
|
} |
|
} |
|
} |
|
var related []model.Obj |
|
parentPath := stdpath.Dir(reqPath) |
|
sameLevelFiles, err := fs.List(c, parentPath, &fs.ListArgs{}) |
|
if err == nil { |
|
related = filterRelated(sameLevelFiles, obj) |
|
} |
|
parentMeta, _ := op.GetNearestMeta(parentPath) |
|
thumb, _ := model.GetThumb(obj) |
|
common.SuccessResp(c, FsGetResp{ |
|
ObjResp: ObjResp{ |
|
Name: obj.GetName(), |
|
Size: obj.GetSize(), |
|
IsDir: obj.IsDir(), |
|
Modified: obj.ModTime(), |
|
Created: obj.CreateTime(), |
|
HashInfoStr: obj.GetHash().String(), |
|
HashInfo: obj.GetHash().Export(), |
|
Sign: common.Sign(obj, parentPath, isEncrypt(meta, reqPath)), |
|
Type: utils.GetFileType(obj.GetName()), |
|
Thumb: thumb, |
|
}, |
|
RawURL: rawURL, |
|
Readme: getReadme(meta, reqPath), |
|
Header: getHeader(meta, reqPath), |
|
Provider: provider, |
|
Related: toObjsResp(related, parentPath, isEncrypt(parentMeta, parentPath)), |
|
}) |
|
} |
|
|
|
func filterRelated(objs []model.Obj, obj model.Obj) []model.Obj { |
|
var related []model.Obj |
|
nameWithoutExt := strings.TrimSuffix(obj.GetName(), stdpath.Ext(obj.GetName())) |
|
for _, o := range objs { |
|
if o.GetName() == obj.GetName() { |
|
continue |
|
} |
|
if strings.HasPrefix(o.GetName(), nameWithoutExt) { |
|
related = append(related, o) |
|
} |
|
} |
|
return related |
|
} |
|
|
|
type FsOtherReq struct { |
|
model.FsOtherArgs |
|
Password string `json:"password" form:"password"` |
|
} |
|
|
|
func FsOther(c *gin.Context) { |
|
var req FsOtherReq |
|
if err := c.ShouldBind(&req); err != nil { |
|
common.ErrorResp(c, err, 400) |
|
return |
|
} |
|
user := c.MustGet("user").(*model.User) |
|
var err error |
|
req.Path, err = user.JoinPath(req.Path) |
|
if err != nil { |
|
common.ErrorResp(c, err, 403) |
|
return |
|
} |
|
meta, err := op.GetNearestMeta(req.Path) |
|
if err != nil { |
|
if !errors.Is(errors.Cause(err), errs.MetaNotFound) { |
|
common.ErrorResp(c, err, 500) |
|
return |
|
} |
|
} |
|
c.Set("meta", meta) |
|
if !common.CanAccess(user, meta, req.Path, req.Password) { |
|
common.ErrorStrResp(c, "password is incorrect or you have no permission", 403) |
|
return |
|
} |
|
res, err := fs.Other(c, req.FsOtherArgs) |
|
if err != nil { |
|
common.ErrorResp(c, err, 500) |
|
return |
|
} |
|
common.SuccessResp(c, res) |
|
} |
|
|