From 7c0b86a9cdebd4cd712864dad9c1cf52731e7e5f Mon Sep 17 00:00:00 2001 From: Noah Hsu Date: Mon, 27 Jun 2022 19:51:23 +0800 Subject: [PATCH] feat: obj list api --- internal/bootstrap/data/data.go | 5 ++ internal/bootstrap/data/dev.go | 21 ++++++ internal/fs/list.go | 3 +- internal/fs/util.go | 17 ----- internal/operations/account.go | 5 +- internal/operations/account_test.go | 6 +- internal/operations/fs.go | 15 ++++- server/common/req.go | 9 +++ server/controllers/fslist.go | 99 +++++++++++++++++++++++++++++ server/router.go | 1 + 10 files changed, 157 insertions(+), 24 deletions(-) create mode 100644 internal/bootstrap/data/dev.go create mode 100644 server/controllers/fslist.go diff --git a/internal/bootstrap/data/data.go b/internal/bootstrap/data/data.go index 207e40f125f..94a1c727af6 100644 --- a/internal/bootstrap/data/data.go +++ b/internal/bootstrap/data/data.go @@ -1,6 +1,11 @@ package data +import "github.com/alist-org/alist/v3/cmd/args" + func InitData() { initUser() initSettings() + if args.Dev { + initDevData() + } } diff --git a/internal/bootstrap/data/dev.go b/internal/bootstrap/data/dev.go new file mode 100644 index 00000000000..dfc9b969d82 --- /dev/null +++ b/internal/bootstrap/data/dev.go @@ -0,0 +1,21 @@ +package data + +import ( + "context" + "github.com/alist-org/alist/v3/internal/model" + "github.com/alist-org/alist/v3/internal/operations" + log "github.com/sirupsen/logrus" +) + +func initDevData() { + err := operations.CreateAccount(context.Background(), model.Account{ + VirtualPath: "/", + Index: 0, + Driver: "Local", + Status: "", + Addition: `{"root_folder":"."}`, + }) + if err != nil { + log.Fatalf("failed to create account: %+v", err) + } +} diff --git a/internal/fs/list.go b/internal/fs/list.go index 02893aff734..58ffe51a071 100644 --- a/internal/fs/list.go +++ b/internal/fs/list.go @@ -2,7 +2,6 @@ package fs import ( "context" - "github.com/alist-org/alist/v3/internal/db" "github.com/alist-org/alist/v3/internal/model" "github.com/alist-org/alist/v3/internal/operations" "github.com/alist-org/alist/v3/pkg/utils" @@ -13,7 +12,7 @@ import ( // List files // TODO: sort func list(ctx context.Context, path string) ([]model.Obj, error) { - meta, _ := db.GetNearestMeta(path) + meta := ctx.Value("meta").(*model.Meta) user := ctx.Value("user").(*model.User) account, actualPath, err := operations.GetAccountAndActualPath(path) virtualFiles := operations.GetAccountVirtualFilesByPath(path) diff --git a/internal/fs/util.go b/internal/fs/util.go index 8745b8415bf..4e328d6316f 100644 --- a/internal/fs/util.go +++ b/internal/fs/util.go @@ -1,7 +1,6 @@ package fs import ( - "github.com/alist-org/alist/v3/pkg/utils" "io" "mime" "net/http" @@ -64,19 +63,3 @@ func getFileStreamFromLink(file model.Obj, link *model.Link) (model.FileStreamer } return stream, nil } - -func canAccess(user *model.User, meta *model.Meta, path string) bool { - // if is not guest, can access - if user.IsAdmin() || user.IgnorePassword { - return true - } - // if meta is nil or password is empty, can access - if meta == nil || meta.Password == "" { - return true - } - // if meta doesn't apply to sub_folder, can access - if !utils.PathEqual(meta.Path, path) && !meta.SubFolder { - return true - } - return false -} diff --git a/internal/operations/account.go b/internal/operations/account.go index 6efb65c7817..57c9ba740cc 100644 --- a/internal/operations/account.go +++ b/internal/operations/account.go @@ -185,6 +185,9 @@ func GetAccountVirtualFilesByPath(prefix string) []model.Obj { return accounts[i].GetAccount().Index < accounts[j].GetAccount().Index }) prefix = utils.StandardizePath(prefix) + if prefix == "/" { + prefix = "" + } set := make(map[string]interface{}) for _, v := range accounts { // TODO should save a balanced account @@ -197,7 +200,7 @@ func GetAccountVirtualFilesByPath(prefix string) []model.Obj { continue } // not prefixed with `prefix` - if !strings.HasPrefix(virtualPath, prefix+"/") && prefix != "/" { + if !strings.HasPrefix(virtualPath, prefix+"/") { continue } name := strings.Split(strings.TrimPrefix(virtualPath, prefix), "/")[1] diff --git a/internal/operations/account_test.go b/internal/operations/account_test.go index 7a0f5589cb6..462f7016f43 100644 --- a/internal/operations/account_test.go +++ b/internal/operations/account_test.go @@ -25,9 +25,9 @@ func TestCreateAccount(t *testing.T) { account model.Account iserr bool }{ - {account: model.Account{Driver: "Local", VirtualPath: "/local", Addition: "{}"}, iserr: false}, - {account: model.Account{Driver: "Local", VirtualPath: "/local", Addition: "{}"}, iserr: true}, - {account: model.Account{Driver: "None", VirtualPath: "/none", Addition: "{}"}, iserr: true}, + {account: model.Account{Driver: "Local", VirtualPath: "/local", Addition: `{"root_folder":"."}`}, iserr: false}, + {account: model.Account{Driver: "Local", VirtualPath: "/local", Addition: `{"root_folder":"."}`}, iserr: true}, + {account: model.Account{Driver: "None", VirtualPath: "/none", Addition: `{"root_folder":"."}`}, iserr: true}, } for _, account := range accounts { err := operations.CreateAccount(context.Background(), account.account) diff --git a/internal/operations/fs.go b/internal/operations/fs.go index bbf8557c962..8273363c2e8 100644 --- a/internal/operations/fs.go +++ b/internal/operations/fs.go @@ -23,6 +23,7 @@ var filesG singleflight.Group[[]model.Obj] // List files in storage, not contains virtual file func List(ctx context.Context, account driver.Driver, path string, refresh ...bool) ([]model.Obj, error) { + log.Debugf("operations.List %s", path) dir, err := Get(ctx, account, path) if err != nil { return nil, errors.WithMessage(err, "failed get dir") @@ -51,8 +52,20 @@ func List(ctx context.Context, account driver.Driver, path string, refresh ...bo return files, err } +func isRoot(path, rootFolderPath string) bool { + if utils.PathEqual(path, rootFolderPath) { + return true + } + // relative path + if utils.PathEqual(path, "/") && rootFolderPath == "." { + return true + } + return false +} + // Get object from list of files func Get(ctx context.Context, account driver.Driver, path string) (model.Obj, error) { + log.Debugf("operations.Get %s", path) // is root folder if r, ok := account.GetAddition().(driver.IRootFolderId); ok && utils.PathEqual(path, "/") { return model.Object{ @@ -63,7 +76,7 @@ func Get(ctx context.Context, account driver.Driver, path string) (model.Obj, er IsFolder: true, }, nil } - if r, ok := account.GetAddition().(driver.IRootFolderPath); ok && utils.PathEqual(path, r.GetRootFolderPath()) { + if r, ok := account.GetAddition().(driver.IRootFolderPath); ok && isRoot(path, r.GetRootFolderPath()) { return model.Object{ ID: r.GetRootFolderPath(), Name: "root", diff --git a/server/common/req.go b/server/common/req.go index e31a62d970c..315b47f075f 100644 --- a/server/common/req.go +++ b/server/common/req.go @@ -4,3 +4,12 @@ type PageReq struct { PageIndex int `json:"page_index" form:"page_index"` PageSize int `json:"page_size" form:"page_size"` } + +func (p *PageReq) Validate() { + if p.PageIndex < 1 { + p.PageIndex = 1 + } + if p.PageSize < 1 { + p.PageSize = 50 + } +} diff --git a/server/controllers/fslist.go b/server/controllers/fslist.go new file mode 100644 index 00000000000..8dbe480968e --- /dev/null +++ b/server/controllers/fslist.go @@ -0,0 +1,99 @@ +package controllers + +import ( + "github.com/alist-org/alist/v3/internal/db" + "github.com/alist-org/alist/v3/internal/fs" + "github.com/alist-org/alist/v3/internal/model" + "github.com/alist-org/alist/v3/pkg/utils" + "github.com/alist-org/alist/v3/server/common" + "github.com/gin-gonic/gin" + "time" +) + +type ListReq struct { + common.PageReq + Path string `json:"path" form:"path"` + Password string `json:"password" form:"password"` +} + +type ObjResp struct { + Name string `json:"name"` + Size int64 `json:"size"` + IsDir bool `json:"is_dir"` + Modified time.Time `json:"modified"` +} + +type ListResp struct { + Content []ObjResp `json:"content"` + Total int64 `json:"total"` +} + +func List(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) + meta, _ := db.GetNearestMeta(req.Path) + c.Set("meta", meta) + if !canAccess(user, meta, req.Path, req.Password) { + common.ErrorStrResp(c, "password is incorrect", 401) + return + } + objs, err := fs.List(c, req.Path) + if err != nil { + common.ErrorResp(c, err, 500, true) + return + } + total, objs := pagination(objs, &req.PageReq) + common.SuccessResp(c, ListResp{ + Content: toObjResp(objs), + Total: int64(total), + }) +} + +func canAccess(user *model.User, meta *model.Meta, path string, password string) bool { + // if is not guest, can access + if user.IsAdmin() || user.IgnorePassword { + return true + } + // if meta is nil or password is empty, can access + if meta == nil || meta.Password == "" { + return true + } + // if meta doesn't apply to sub_folder, can access + if !utils.PathEqual(meta.Path, path) && !meta.SubFolder { + return true + } + // validate password + return meta.Password == password +} + +func pagination(objs []model.Obj, req *common.PageReq) (int, []model.Obj) { + pageIndex, pageSize := req.PageIndex, req.PageSize + 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 toObjResp(objs []model.Obj) []ObjResp { + var resp []ObjResp + for _, obj := range objs { + resp = append(resp, ObjResp{ + Name: obj.GetName(), + Size: obj.GetSize(), + IsDir: obj.IsDir(), + Modified: obj.ModTime(), + }) + } + return resp +} diff --git a/server/router.go b/server/router.go index 6f81427f086..b4dfde25840 100644 --- a/server/router.go +++ b/server/router.go @@ -49,6 +49,7 @@ func Init(r *gin.Engine) { public := api.Group("/public") public.GET("/settings", controllers.PublicSettings) + public.GET("/list", controllers.List) } func Cors(r *gin.Engine) {