package handles import ( "fmt" stdpath "path" "strings" "time" "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/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"` Sign string `json:"sign"` Thumb string `json:"thumb"` Type int `json:"type"` } type FsListResp struct { Content []ObjResp `json:"content"` Total int64 `json:"total"` Readme string `json:"readme"` 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, req.Refresh) if err != nil { common.ErrorResp(c, err, 500) return } total, objs := pagination(objs, &req.PageReq) provider := "unknown" storage, err := fs.GetStorage(reqPath) 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), 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) 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 isEncrypt(meta *model.Meta, path string) bool { 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(), 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"` 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) if err != nil { common.ErrorResp(c, err, 500) return } var rawURL string storage, err := fs.GetStorage(reqPath) 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 { 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?sign=%s", common.GetApiUrl(c.Request), utils.EncodePath(reqPath, true), sign.Sign(reqPath)) } } else { // file have raw url if url, ok := model.GetUrl(obj); ok { rawURL = url } else { // if storage is not proxy, use raw url by fs.Link link, _, err := fs.Link(c, reqPath, model.LinkArgs{IP: c.ClientIP(), Header: c.Request.Header}) 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) 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(), Sign: common.Sign(obj, parentPath, isEncrypt(meta, reqPath)), Type: utils.GetFileType(obj.GetName()), Thumb: thumb, }, RawURL: rawURL, Readme: getReadme(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) }