mirror of https://github.com/Xhofe/alist
feat: obj list api
parent
c6007aa9e6
commit
7c0b86a9cd
|
@ -1,6 +1,11 @@
|
||||||
package data
|
package data
|
||||||
|
|
||||||
|
import "github.com/alist-org/alist/v3/cmd/args"
|
||||||
|
|
||||||
func InitData() {
|
func InitData() {
|
||||||
initUser()
|
initUser()
|
||||||
initSettings()
|
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 (
|
import (
|
||||||
"context"
|
"context"
|
||||||
"github.com/alist-org/alist/v3/internal/db"
|
|
||||||
"github.com/alist-org/alist/v3/internal/model"
|
"github.com/alist-org/alist/v3/internal/model"
|
||||||
"github.com/alist-org/alist/v3/internal/operations"
|
"github.com/alist-org/alist/v3/internal/operations"
|
||||||
"github.com/alist-org/alist/v3/pkg/utils"
|
"github.com/alist-org/alist/v3/pkg/utils"
|
||||||
|
@ -13,7 +12,7 @@ import (
|
||||||
// List files
|
// List files
|
||||||
// TODO: sort
|
// TODO: sort
|
||||||
func list(ctx context.Context, path string) ([]model.Obj, error) {
|
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)
|
user := ctx.Value("user").(*model.User)
|
||||||
account, actualPath, err := operations.GetAccountAndActualPath(path)
|
account, actualPath, err := operations.GetAccountAndActualPath(path)
|
||||||
virtualFiles := operations.GetAccountVirtualFilesByPath(path)
|
virtualFiles := operations.GetAccountVirtualFilesByPath(path)
|
||||||
|
|
|
@ -1,7 +1,6 @@
|
||||||
package fs
|
package fs
|
||||||
|
|
||||||
import (
|
import (
|
||||||
"github.com/alist-org/alist/v3/pkg/utils"
|
|
||||||
"io"
|
"io"
|
||||||
"mime"
|
"mime"
|
||||||
"net/http"
|
"net/http"
|
||||||
|
@ -64,19 +63,3 @@ func getFileStreamFromLink(file model.Obj, link *model.Link) (model.FileStreamer
|
||||||
}
|
}
|
||||||
return stream, nil
|
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
|
return accounts[i].GetAccount().Index < accounts[j].GetAccount().Index
|
||||||
})
|
})
|
||||||
prefix = utils.StandardizePath(prefix)
|
prefix = utils.StandardizePath(prefix)
|
||||||
|
if prefix == "/" {
|
||||||
|
prefix = ""
|
||||||
|
}
|
||||||
set := make(map[string]interface{})
|
set := make(map[string]interface{})
|
||||||
for _, v := range accounts {
|
for _, v := range accounts {
|
||||||
// TODO should save a balanced account
|
// TODO should save a balanced account
|
||||||
|
@ -197,7 +200,7 @@ func GetAccountVirtualFilesByPath(prefix string) []model.Obj {
|
||||||
continue
|
continue
|
||||||
}
|
}
|
||||||
// not prefixed with `prefix`
|
// not prefixed with `prefix`
|
||||||
if !strings.HasPrefix(virtualPath, prefix+"/") && prefix != "/" {
|
if !strings.HasPrefix(virtualPath, prefix+"/") {
|
||||||
continue
|
continue
|
||||||
}
|
}
|
||||||
name := strings.Split(strings.TrimPrefix(virtualPath, prefix), "/")[1]
|
name := strings.Split(strings.TrimPrefix(virtualPath, prefix), "/")[1]
|
||||||
|
|
|
@ -25,9 +25,9 @@ func TestCreateAccount(t *testing.T) {
|
||||||
account model.Account
|
account model.Account
|
||||||
iserr bool
|
iserr bool
|
||||||
}{
|
}{
|
||||||
{account: model.Account{Driver: "Local", VirtualPath: "/local", Addition: "{}"}, iserr: false},
|
{account: model.Account{Driver: "Local", VirtualPath: "/local", Addition: `{"root_folder":"."}`}, iserr: false},
|
||||||
{account: model.Account{Driver: "Local", VirtualPath: "/local", Addition: "{}"}, iserr: true},
|
{account: model.Account{Driver: "Local", VirtualPath: "/local", Addition: `{"root_folder":"."}`}, iserr: true},
|
||||||
{account: model.Account{Driver: "None", VirtualPath: "/none", Addition: "{}"}, iserr: true},
|
{account: model.Account{Driver: "None", VirtualPath: "/none", Addition: `{"root_folder":"."}`}, iserr: true},
|
||||||
}
|
}
|
||||||
for _, account := range accounts {
|
for _, account := range accounts {
|
||||||
err := operations.CreateAccount(context.Background(), account.account)
|
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
|
// List files in storage, not contains virtual file
|
||||||
func List(ctx context.Context, account driver.Driver, path string, refresh ...bool) ([]model.Obj, error) {
|
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)
|
dir, err := Get(ctx, account, path)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return nil, errors.WithMessage(err, "failed get dir")
|
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
|
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
|
// Get object from list of files
|
||||||
func Get(ctx context.Context, account driver.Driver, path string) (model.Obj, error) {
|
func Get(ctx context.Context, account driver.Driver, path string) (model.Obj, error) {
|
||||||
|
log.Debugf("operations.Get %s", path)
|
||||||
// is root folder
|
// is root folder
|
||||||
if r, ok := account.GetAddition().(driver.IRootFolderId); ok && utils.PathEqual(path, "/") {
|
if r, ok := account.GetAddition().(driver.IRootFolderId); ok && utils.PathEqual(path, "/") {
|
||||||
return model.Object{
|
return model.Object{
|
||||||
|
@ -63,7 +76,7 @@ func Get(ctx context.Context, account driver.Driver, path string) (model.Obj, er
|
||||||
IsFolder: true,
|
IsFolder: true,
|
||||||
}, nil
|
}, 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{
|
return model.Object{
|
||||||
ID: r.GetRootFolderPath(),
|
ID: r.GetRootFolderPath(),
|
||||||
Name: "root",
|
Name: "root",
|
||||||
|
|
|
@ -4,3 +4,12 @@ type PageReq struct {
|
||||||
PageIndex int `json:"page_index" form:"page_index"`
|
PageIndex int `json:"page_index" form:"page_index"`
|
||||||
PageSize int `json:"page_size" form:"page_size"`
|
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 := api.Group("/public")
|
||||||
public.GET("/settings", controllers.PublicSettings)
|
public.GET("/settings", controllers.PublicSettings)
|
||||||
|
public.GET("/list", controllers.List)
|
||||||
}
|
}
|
||||||
|
|
||||||
func Cors(r *gin.Engine) {
|
func Cors(r *gin.Engine) {
|
||||||
|
|
Loading…
Reference in New Issue