mirror of https://github.com/Xhofe/alist
				
				
				
			feat: obj list api
							parent
							
								
									c6007aa9e6
								
							
						
					
					
						commit
						7c0b86a9cd
					
				|  | @ -1,6 +1,11 @@ | |||
| package data | ||||
| 
 | ||||
| import "github.com/alist-org/alist/v3/cmd/args" | ||||
| 
 | ||||
| func InitData() { | ||||
| 	initUser() | ||||
| 	initSettings() | ||||
| 	if args.Dev { | ||||
| 		initDevData() | ||||
| 	} | ||||
| } | ||||
|  |  | |||
|  | @ -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) | ||||
| 	} | ||||
| } | ||||
|  | @ -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) | ||||
|  |  | |||
|  | @ -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 | ||||
| } | ||||
|  |  | |||
|  | @ -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] | ||||
|  |  | |||
|  | @ -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) | ||||
|  |  | |||
|  | @ -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", | ||||
|  |  | |||
|  | @ -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 | ||||
| 	} | ||||
| } | ||||
|  |  | |||
|  | @ -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 | ||||
| } | ||||
|  | @ -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) { | ||||
|  |  | |||
		Loading…
	
		Reference in New Issue
	
	 Noah Hsu
						Noah Hsu