refactor: obj name mapping and internal path processing (#2733)
* refactor:Prepare to remove the get interface * feat:add obj Unwarp interface * refactor:obj name mapping and program internal path processing * chore: fix typo * feat: unwrap get * fix: no use op.Get to get parent id * fix: set the path uniformly Co-authored-by: Noah Hsu <i@nn.ci>
This commit is contained in:
@ -19,7 +19,7 @@ var metaCache = cache.NewMemCache(cache.WithShards[*model.Meta](2))
|
||||
var metaG singleflight.Group[*model.Meta]
|
||||
|
||||
func GetNearestMeta(path string) (*model.Meta, error) {
|
||||
path = utils.StandardizePath(path)
|
||||
path = utils.FixAndCleanPath(path)
|
||||
meta, err := GetMetaByPath(path)
|
||||
if err == nil {
|
||||
return meta, nil
|
||||
|
@ -30,6 +30,7 @@ func BatchCreateSearchNodes(nodes *[]model.SearchNode) error {
|
||||
}
|
||||
|
||||
func DeleteSearchNodesByParent(prefix string) error {
|
||||
prefix = utils.FixAndCleanPath(prefix)
|
||||
err := db.Where(whereInParent(prefix)).Delete(&model.SearchNode{}).Error
|
||||
if err != nil {
|
||||
return err
|
||||
@ -37,7 +38,7 @@ func DeleteSearchNodesByParent(prefix string) error {
|
||||
dir, name := path.Split(prefix)
|
||||
return db.Where(fmt.Sprintf("%s = ? AND %s = ?",
|
||||
columnName("parent"), columnName("name")),
|
||||
utils.StandardizePath(dir), name).Delete(&model.SearchNode{}).Error
|
||||
dir, name).Delete(&model.SearchNode{}).Error
|
||||
}
|
||||
|
||||
func ClearSearchNodes() error {
|
||||
|
@ -39,7 +39,7 @@ type Reader interface {
|
||||
}
|
||||
|
||||
type Getter interface {
|
||||
Get(ctx context.Context, path string) (model.Obj, error)
|
||||
GetRoot(ctx context.Context) (model.Obj, error)
|
||||
}
|
||||
|
||||
type Writer interface {
|
||||
|
@ -80,7 +80,7 @@ func Remove(ctx context.Context, path string) error {
|
||||
return err
|
||||
}
|
||||
|
||||
func PutDirectly(ctx context.Context, dstDirPath string, file model.FileStreamer) error {
|
||||
func PutDirectly(ctx context.Context, dstDirPath string, file *model.FileStream) error {
|
||||
err := putDirectly(ctx, dstDirPath, file)
|
||||
if err != nil {
|
||||
log.Errorf("failed put %s: %+v", dstDirPath, err)
|
||||
@ -88,7 +88,7 @@ func PutDirectly(ctx context.Context, dstDirPath string, file model.FileStreamer
|
||||
return err
|
||||
}
|
||||
|
||||
func PutAsTask(dstDirPath string, file model.FileStreamer) error {
|
||||
func PutAsTask(dstDirPath string, file *model.FileStream) error {
|
||||
err := putAsTask(dstDirPath, file)
|
||||
if err != nil {
|
||||
log.Errorf("failed put %s: %+v", dstDirPath, err)
|
||||
|
@ -12,7 +12,7 @@ import (
|
||||
)
|
||||
|
||||
func get(ctx context.Context, path string) (model.Obj, error) {
|
||||
path = utils.StandardizePath(path)
|
||||
path = utils.FixAndCleanPath(path)
|
||||
// maybe a virtual file
|
||||
if path != "/" {
|
||||
virtualFiles := op.GetStorageVirtualFilesByPath(stdpath.Dir(path))
|
||||
|
@ -18,7 +18,7 @@ var UploadTaskManager = task.NewTaskManager(3, func(tid *uint64) {
|
||||
})
|
||||
|
||||
// putAsTask add as a put task and return immediately
|
||||
func putAsTask(dstDirPath string, file model.FileStreamer) error {
|
||||
func putAsTask(dstDirPath string, file *model.FileStream) error {
|
||||
storage, dstDirActualPath, err := op.GetStorageAndActualPath(dstDirPath)
|
||||
if err != nil {
|
||||
return errors.WithMessage(err, "failed get storage")
|
||||
@ -43,7 +43,7 @@ func putAsTask(dstDirPath string, file model.FileStreamer) error {
|
||||
}
|
||||
|
||||
// putDirect put the file and return after finish
|
||||
func putDirectly(ctx context.Context, dstDirPath string, file model.FileStreamer) error {
|
||||
func putDirectly(ctx context.Context, dstDirPath string, file *model.FileStream) error {
|
||||
storage, dstDirActualPath, err := op.GetStorageAndActualPath(dstDirPath)
|
||||
if err != nil {
|
||||
return errors.WithMessage(err, "failed get storage")
|
||||
|
@ -35,7 +35,7 @@ func containsByName(files []model.Obj, file model.Obj) bool {
|
||||
|
||||
var httpClient = &http.Client{}
|
||||
|
||||
func getFileStreamFromLink(file model.Obj, link *model.Link) (model.FileStreamer, error) {
|
||||
func getFileStreamFromLink(file model.Obj, link *model.Link) (*model.FileStream, error) {
|
||||
var rc io.ReadCloser
|
||||
mimetype := utils.GetMimeType(file.GetName())
|
||||
if link.Data != nil {
|
||||
|
@ -8,11 +8,18 @@ import (
|
||||
"github.com/maruel/natural"
|
||||
)
|
||||
|
||||
type UnwrapObj interface {
|
||||
Unwrap() Obj
|
||||
}
|
||||
|
||||
type Obj interface {
|
||||
GetSize() int64
|
||||
GetName() string
|
||||
ModTime() time.Time
|
||||
IsDir() bool
|
||||
|
||||
// The internal information of the driver.
|
||||
// If you want to use it, please understand what it means
|
||||
GetID() string
|
||||
GetPath() string
|
||||
}
|
||||
@ -24,6 +31,7 @@ type FileStreamer interface {
|
||||
SetReadCloser(io.ReadCloser)
|
||||
NeedStore() bool
|
||||
GetReadCloser() io.ReadCloser
|
||||
GetOld() Obj
|
||||
}
|
||||
|
||||
type URL interface {
|
||||
@ -86,3 +94,9 @@ func ExtractFolder(objs []Obj, extractFolder string) {
|
||||
return false
|
||||
})
|
||||
}
|
||||
|
||||
func WrapObjsName(objs []Obj) {
|
||||
for i := 0; i < len(objs); i++ {
|
||||
objs[i] = &ObjWrapName{Obj: objs[i]}
|
||||
}
|
||||
}
|
||||
|
@ -6,6 +6,22 @@ import (
|
||||
"github.com/alist-org/alist/v3/pkg/utils"
|
||||
)
|
||||
|
||||
type ObjWrapName struct {
|
||||
Name string
|
||||
Obj
|
||||
}
|
||||
|
||||
func (o *ObjWrapName) Unwrap() Obj {
|
||||
return o.Obj
|
||||
}
|
||||
|
||||
func (o *ObjWrapName) GetName() string {
|
||||
if o.Name == "" {
|
||||
o.Name = utils.MappingName(o.Obj.GetName())
|
||||
}
|
||||
return o.Name
|
||||
}
|
||||
|
||||
type Object struct {
|
||||
ID string
|
||||
Path string
|
||||
@ -16,7 +32,7 @@ type Object struct {
|
||||
}
|
||||
|
||||
func (o *Object) GetName() string {
|
||||
return utils.MappingName(o.Name)
|
||||
return o.Name
|
||||
}
|
||||
|
||||
func (o *Object) GetSize() int64 {
|
||||
|
@ -9,6 +9,7 @@ type FileStream struct {
|
||||
io.ReadCloser
|
||||
Mimetype string
|
||||
WebPutAsTask bool
|
||||
Old Obj
|
||||
}
|
||||
|
||||
func (f *FileStream) GetMimetype() string {
|
||||
@ -26,3 +27,7 @@ func (f *FileStream) GetReadCloser() io.ReadCloser {
|
||||
func (f *FileStream) SetReadCloser(rc io.ReadCloser) {
|
||||
f.ReadCloser = rc
|
||||
}
|
||||
|
||||
func (f *FileStream) GetOld() Obj {
|
||||
return f.Old
|
||||
}
|
||||
|
@ -1,3 +1,6 @@
|
||||
package op
|
||||
|
||||
var WORK = "work"
|
||||
const (
|
||||
WORK = "work"
|
||||
RootName = "root"
|
||||
)
|
||||
|
@ -4,7 +4,6 @@ import (
|
||||
"context"
|
||||
"os"
|
||||
stdpath "path"
|
||||
"strings"
|
||||
"time"
|
||||
|
||||
"github.com/Xhofe/go-cache"
|
||||
@ -23,12 +22,11 @@ var listCache = cache.NewMemCache(cache.WithShards[[]model.Obj](64))
|
||||
var listG singleflight.Group[[]model.Obj]
|
||||
|
||||
func ClearCache(storage driver.Driver, path string) {
|
||||
key := stdpath.Join(storage.GetStorage().MountPath, path)
|
||||
listCache.Del(key)
|
||||
listCache.Del(Key(storage, path))
|
||||
}
|
||||
|
||||
func Key(storage driver.Driver, path string) string {
|
||||
return stdpath.Join(storage.GetStorage().MountPath, utils.StandardizePath(path))
|
||||
return stdpath.Join(storage.GetStorage().MountPath, utils.FixAndCleanPath(path))
|
||||
}
|
||||
|
||||
// List files in storage, not contains virtual file
|
||||
@ -36,7 +34,7 @@ func List(ctx context.Context, storage driver.Driver, path string, args model.Li
|
||||
if storage.Config().CheckStatus && storage.GetStorage().Status != WORK {
|
||||
return nil, errors.Errorf("storage not init: %s", storage.GetStorage().Status)
|
||||
}
|
||||
path = utils.StandardizePath(path)
|
||||
path = utils.FixAndCleanPath(path)
|
||||
log.Debugf("op.List %s", path)
|
||||
key := Key(storage, path)
|
||||
if len(refresh) == 0 || !refresh[0] {
|
||||
@ -45,7 +43,7 @@ func List(ctx context.Context, storage driver.Driver, path string, args model.Li
|
||||
return files, nil
|
||||
}
|
||||
}
|
||||
dir, err := Get(ctx, storage, path)
|
||||
dir, err := GetUnwrap(ctx, storage, path)
|
||||
if err != nil {
|
||||
return nil, errors.WithMessage(err, "failed get dir")
|
||||
}
|
||||
@ -58,6 +56,14 @@ func List(ctx context.Context, storage driver.Driver, path string, args model.Li
|
||||
if err != nil {
|
||||
return nil, errors.Wrapf(err, "failed to list objs")
|
||||
}
|
||||
// set path
|
||||
for _, f := range files {
|
||||
if s, ok := f.(model.SetPath); ok && f.GetPath() == "" && dir.GetPath() != "" {
|
||||
s.SetPath(stdpath.Join(dir.GetPath(), f.GetName()))
|
||||
}
|
||||
}
|
||||
// warp obj name
|
||||
model.WrapObjsName(files)
|
||||
// call hooks
|
||||
go func(reqPath string, files []model.Obj) {
|
||||
for _, hook := range objsUpdateHooks {
|
||||
@ -78,48 +84,49 @@ func List(ctx context.Context, storage driver.Driver, path string, args model.Li
|
||||
return objs, err
|
||||
}
|
||||
|
||||
func isRoot(path, rootFolderPath string) bool {
|
||||
if utils.PathEqual(path, rootFolderPath) {
|
||||
return true
|
||||
}
|
||||
rootFolderPath = strings.TrimSuffix(rootFolderPath, "/")
|
||||
rootFolderPath = strings.TrimPrefix(rootFolderPath, "\\")
|
||||
// relative path, this shouldn't happen, because root folder path is absolute
|
||||
if utils.PathEqual(path, "/") && rootFolderPath == "." {
|
||||
return true
|
||||
}
|
||||
return false
|
||||
}
|
||||
|
||||
// Get object from list of files
|
||||
func Get(ctx context.Context, storage driver.Driver, path string) (model.Obj, error) {
|
||||
path = utils.StandardizePath(path)
|
||||
path = utils.FixAndCleanPath(path)
|
||||
log.Debugf("op.Get %s", path)
|
||||
if g, ok := storage.(driver.Getter); ok {
|
||||
obj, err := g.Get(ctx, path)
|
||||
if err == nil {
|
||||
return obj, nil
|
||||
}
|
||||
}
|
||||
|
||||
// is root folder
|
||||
if r, ok := storage.GetAddition().(driver.IRootId); ok && utils.PathEqual(path, "/") {
|
||||
return &model.Object{
|
||||
ID: r.GetRootId(),
|
||||
Name: "root",
|
||||
Size: 0,
|
||||
Modified: storage.GetStorage().Modified,
|
||||
IsFolder: true,
|
||||
}, nil
|
||||
}
|
||||
if r, ok := storage.GetAddition().(driver.IRootPath); ok && isRoot(path, r.GetRootPath()) {
|
||||
return &model.Object{
|
||||
Path: r.GetRootPath(),
|
||||
Name: "root",
|
||||
Size: 0,
|
||||
Modified: storage.GetStorage().Modified,
|
||||
IsFolder: true,
|
||||
if utils.PathEqual(path, "/") {
|
||||
var rootObj model.Obj
|
||||
switch r := storage.GetAddition().(type) {
|
||||
case driver.IRootId:
|
||||
rootObj = &model.Object{
|
||||
ID: r.GetRootId(),
|
||||
Name: RootName,
|
||||
Size: 0,
|
||||
Modified: storage.GetStorage().Modified,
|
||||
IsFolder: true,
|
||||
}
|
||||
case driver.IRootPath:
|
||||
rootObj = &model.Object{
|
||||
Path: r.GetRootPath(),
|
||||
Name: RootName,
|
||||
Size: 0,
|
||||
Modified: storage.GetStorage().Modified,
|
||||
IsFolder: true,
|
||||
}
|
||||
default:
|
||||
if storage, ok := storage.(driver.Getter); ok {
|
||||
obj, err := storage.GetRoot(ctx)
|
||||
if err != nil {
|
||||
return nil, errors.WithMessage(err, "failed get root obj")
|
||||
}
|
||||
rootObj = obj
|
||||
}
|
||||
}
|
||||
if rootObj == nil {
|
||||
return nil, errors.Errorf("please implement IRootPath or IRootId or Getter method")
|
||||
}
|
||||
return &model.ObjWrapName{
|
||||
Name: RootName,
|
||||
Obj: rootObj,
|
||||
}, nil
|
||||
}
|
||||
|
||||
// not root folder
|
||||
dir, name := stdpath.Split(path)
|
||||
files, err := List(ctx, storage, dir, model.ListArgs{})
|
||||
@ -129,13 +136,6 @@ func Get(ctx context.Context, storage driver.Driver, path string) (model.Obj, er
|
||||
for _, f := range files {
|
||||
// TODO maybe copy obj here
|
||||
if f.GetName() == name {
|
||||
// use path as id, why don't set id in List function?
|
||||
// because files maybe cache, set id here can reduce memory usage
|
||||
if f.GetPath() == "" {
|
||||
if s, ok := f.(model.SetPath); ok {
|
||||
s.SetPath(path)
|
||||
}
|
||||
}
|
||||
return f, nil
|
||||
}
|
||||
}
|
||||
@ -143,6 +143,17 @@ func Get(ctx context.Context, storage driver.Driver, path string) (model.Obj, er
|
||||
return nil, errors.WithStack(errs.ObjectNotFound)
|
||||
}
|
||||
|
||||
func GetUnwrap(ctx context.Context, storage driver.Driver, path string) (model.Obj, error) {
|
||||
obj, err := Get(ctx, storage, path)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
if unwrap, ok := obj.(model.UnwrapObj); ok {
|
||||
obj = unwrap.Unwrap()
|
||||
}
|
||||
return obj, err
|
||||
}
|
||||
|
||||
var linkCache = cache.NewMemCache(cache.WithShards[*model.Link](16))
|
||||
var linkG singleflight.Group[*model.Link]
|
||||
|
||||
@ -151,7 +162,7 @@ func Link(ctx context.Context, storage driver.Driver, path string, args model.Li
|
||||
if storage.Config().CheckStatus && storage.GetStorage().Status != WORK {
|
||||
return nil, nil, errors.Errorf("storage not init: %s", storage.GetStorage().Status)
|
||||
}
|
||||
file, err := Get(ctx, storage, path)
|
||||
file, err := GetUnwrap(ctx, storage, path)
|
||||
if err != nil {
|
||||
return nil, nil, errors.WithMessage(err, "failed to get file")
|
||||
}
|
||||
@ -178,7 +189,7 @@ func Link(ctx context.Context, storage driver.Driver, path string, args model.Li
|
||||
|
||||
// Other api
|
||||
func Other(ctx context.Context, storage driver.Driver, args model.FsOtherArgs) (interface{}, error) {
|
||||
obj, err := Get(ctx, storage, args.Path)
|
||||
obj, err := GetUnwrap(ctx, storage, args.Path)
|
||||
if err != nil {
|
||||
return nil, errors.WithMessagef(err, "failed to get obj")
|
||||
}
|
||||
@ -199,11 +210,11 @@ func MakeDir(ctx context.Context, storage driver.Driver, path string) error {
|
||||
if storage.Config().CheckStatus && storage.GetStorage().Status != WORK {
|
||||
return errors.Errorf("storage not init: %s", storage.GetStorage().Status)
|
||||
}
|
||||
path = utils.StandardizePath(path)
|
||||
path = utils.FixAndCleanPath(path)
|
||||
key := Key(storage, path)
|
||||
_, err, _ := mkdirG.Do(key, func() (interface{}, error) {
|
||||
// check if dir exists
|
||||
f, err := Get(ctx, storage, path)
|
||||
f, err := GetUnwrap(ctx, storage, path)
|
||||
if err != nil {
|
||||
if errs.IsObjectNotFound(err) {
|
||||
parentPath, dirName := stdpath.Split(path)
|
||||
@ -211,7 +222,7 @@ func MakeDir(ctx context.Context, storage driver.Driver, path string) error {
|
||||
if err != nil {
|
||||
return nil, errors.WithMessagef(err, "failed to make parent dir [%s]", parentPath)
|
||||
}
|
||||
parentDir, err := Get(ctx, storage, parentPath)
|
||||
parentDir, err := GetUnwrap(ctx, storage, parentPath)
|
||||
// this should not happen
|
||||
if err != nil {
|
||||
return nil, errors.WithMessagef(err, "failed to get parent dir [%s]", parentPath)
|
||||
@ -242,11 +253,11 @@ func Move(ctx context.Context, storage driver.Driver, srcPath, dstDirPath string
|
||||
if storage.Config().CheckStatus && storage.GetStorage().Status != WORK {
|
||||
return errors.Errorf("storage not init: %s", storage.GetStorage().Status)
|
||||
}
|
||||
srcObj, err := Get(ctx, storage, srcPath)
|
||||
srcObj, err := GetUnwrap(ctx, storage, srcPath)
|
||||
if err != nil {
|
||||
return errors.WithMessage(err, "failed to get src object")
|
||||
}
|
||||
dstDir, err := Get(ctx, storage, dstDirPath)
|
||||
dstDir, err := GetUnwrap(ctx, storage, dstDirPath)
|
||||
if err != nil {
|
||||
return errors.WithMessage(err, "failed to get dst dir")
|
||||
}
|
||||
@ -257,7 +268,7 @@ func Rename(ctx context.Context, storage driver.Driver, srcPath, dstName string)
|
||||
if storage.Config().CheckStatus && storage.GetStorage().Status != WORK {
|
||||
return errors.Errorf("storage not init: %s", storage.GetStorage().Status)
|
||||
}
|
||||
srcObj, err := Get(ctx, storage, srcPath)
|
||||
srcObj, err := GetUnwrap(ctx, storage, srcPath)
|
||||
if err != nil {
|
||||
return errors.WithMessage(err, "failed to get src object")
|
||||
}
|
||||
@ -269,11 +280,11 @@ func Copy(ctx context.Context, storage driver.Driver, srcPath, dstDirPath string
|
||||
if storage.Config().CheckStatus && storage.GetStorage().Status != WORK {
|
||||
return errors.Errorf("storage not init: %s", storage.GetStorage().Status)
|
||||
}
|
||||
srcObj, err := Get(ctx, storage, srcPath)
|
||||
srcObj, err := GetUnwrap(ctx, storage, srcPath)
|
||||
if err != nil {
|
||||
return errors.WithMessage(err, "failed to get src object")
|
||||
}
|
||||
dstDir, err := Get(ctx, storage, dstDirPath)
|
||||
dstDir, err := GetUnwrap(ctx, storage, dstDirPath)
|
||||
return errors.WithStack(storage.Copy(ctx, srcObj, dstDir))
|
||||
}
|
||||
|
||||
@ -281,7 +292,7 @@ func Remove(ctx context.Context, storage driver.Driver, path string) error {
|
||||
if storage.Config().CheckStatus && storage.GetStorage().Status != WORK {
|
||||
return errors.Errorf("storage not init: %s", storage.GetStorage().Status)
|
||||
}
|
||||
obj, err := Get(ctx, storage, path)
|
||||
obj, err := GetUnwrap(ctx, storage, path)
|
||||
if err != nil {
|
||||
// if object not found, it's ok
|
||||
if errs.IsObjectNotFound(err) {
|
||||
@ -313,7 +324,7 @@ func Remove(ctx context.Context, storage driver.Driver, path string) error {
|
||||
return errors.WithStack(err)
|
||||
}
|
||||
|
||||
func Put(ctx context.Context, storage driver.Driver, dstDirPath string, file model.FileStreamer, up driver.UpdateProgress) error {
|
||||
func Put(ctx context.Context, storage driver.Driver, dstDirPath string, file *model.FileStream, up driver.UpdateProgress) error {
|
||||
if storage.Config().CheckStatus && storage.GetStorage().Status != WORK {
|
||||
return errors.Errorf("storage not init: %s", storage.GetStorage().Status)
|
||||
}
|
||||
@ -332,21 +343,22 @@ func Put(ctx context.Context, storage driver.Driver, dstDirPath string, file mod
|
||||
}()
|
||||
// if file exist and size = 0, delete it
|
||||
dstPath := stdpath.Join(dstDirPath, file.GetName())
|
||||
fi, err := Get(ctx, storage, dstPath)
|
||||
fi, err := GetUnwrap(ctx, storage, dstPath)
|
||||
if err == nil {
|
||||
if fi.GetSize() == 0 {
|
||||
err = Remove(ctx, storage, dstPath)
|
||||
if err != nil {
|
||||
return errors.WithMessagef(err, "failed remove file that exist and have size 0")
|
||||
}
|
||||
} else {
|
||||
file.Old = fi
|
||||
}
|
||||
}
|
||||
|
||||
err = MakeDir(ctx, storage, dstDirPath)
|
||||
if err != nil {
|
||||
return errors.WithMessagef(err, "failed to make dir [%s]", dstDirPath)
|
||||
}
|
||||
parentDir, err := Get(ctx, storage, dstDirPath)
|
||||
parentDir, err := GetUnwrap(ctx, storage, dstDirPath)
|
||||
// this should not happen
|
||||
if err != nil {
|
||||
return errors.WithMessagef(err, "failed to get dir [%s]", dstDirPath)
|
||||
|
@ -1,7 +1,6 @@
|
||||
package op
|
||||
|
||||
import (
|
||||
stdpath "path"
|
||||
"strings"
|
||||
|
||||
"github.com/alist-org/alist/v3/internal/driver"
|
||||
@ -10,30 +9,17 @@ import (
|
||||
log "github.com/sirupsen/logrus"
|
||||
)
|
||||
|
||||
// ActualPath Get the actual path
|
||||
// !!! maybe and \ in the path when use windows local
|
||||
func ActualPath(storage driver.Additional, rawPath string) string {
|
||||
if i, ok := storage.(driver.IRootPath); ok {
|
||||
rawPath = stdpath.Join(i.GetRootPath(), rawPath)
|
||||
}
|
||||
return utils.StandardizePath(rawPath)
|
||||
}
|
||||
|
||||
// GetStorageAndActualPath Get the corresponding storage and actual path
|
||||
// for path: remove the mount path prefix and join the actual root folder if exists
|
||||
func GetStorageAndActualPath(rawPath string) (driver.Driver, string, error) {
|
||||
rawPath = utils.StandardizePath(rawPath)
|
||||
// why can remove this check? because reqPath has joined the base_path of user, no relative path
|
||||
//if strings.Contains(rawPath, "..") {
|
||||
// return nil, "", errors.WithStack(errs.RelativePath)
|
||||
//}
|
||||
storage := GetBalancedStorage(rawPath)
|
||||
func GetStorageAndActualPath(rawPath string) (storage driver.Driver, actualPath string, err error) {
|
||||
rawPath = utils.FixAndCleanPath(rawPath)
|
||||
storage = GetBalancedStorage(rawPath)
|
||||
if storage == nil {
|
||||
return nil, "", errors.Errorf("can't find storage with rawPath: %s", rawPath)
|
||||
err = errors.Errorf("can't find storage with rawPath: %s", rawPath)
|
||||
return
|
||||
}
|
||||
log.Debugln("use storage: ", storage.GetStorage().MountPath)
|
||||
virtualPath := utils.GetActualVirtualPath(storage.GetStorage().MountPath)
|
||||
actualPath := strings.TrimPrefix(rawPath, virtualPath)
|
||||
actualPath = ActualPath(storage.GetAddition(), actualPath)
|
||||
return storage, actualPath, nil
|
||||
actualPath = utils.FixAndCleanPath(strings.TrimPrefix(rawPath, virtualPath))
|
||||
return
|
||||
}
|
||||
|
@ -11,6 +11,7 @@ import (
|
||||
"github.com/alist-org/alist/v3/internal/model"
|
||||
"github.com/alist-org/alist/v3/pkg/generic_sync"
|
||||
"github.com/alist-org/alist/v3/pkg/utils"
|
||||
mapset "github.com/deckarep/golang-set/v2"
|
||||
"github.com/pkg/errors"
|
||||
log "github.com/sirupsen/logrus"
|
||||
)
|
||||
@ -25,10 +26,11 @@ func GetAllStorages() []driver.Driver {
|
||||
}
|
||||
|
||||
func HasStorage(mountPath string) bool {
|
||||
return storagesMap.Has(mountPath)
|
||||
return storagesMap.Has(utils.FixAndCleanPath(mountPath))
|
||||
}
|
||||
|
||||
func GetStorageByVirtualPath(virtualPath string) (driver.Driver, error) {
|
||||
virtualPath = utils.FixAndCleanPath(virtualPath)
|
||||
storageDriver, ok := storagesMap.Load(virtualPath)
|
||||
if !ok {
|
||||
return nil, errors.Errorf("no mount path for an storage is: %s", virtualPath)
|
||||
@ -40,7 +42,7 @@ func GetStorageByVirtualPath(virtualPath string) (driver.Driver, error) {
|
||||
// then instantiate corresponding driver and save it in memory
|
||||
func CreateStorage(ctx context.Context, storage model.Storage) (uint, error) {
|
||||
storage.Modified = time.Now()
|
||||
storage.MountPath = utils.StandardizePath(storage.MountPath)
|
||||
storage.MountPath = utils.FixAndCleanPath(storage.MountPath)
|
||||
var err error
|
||||
// check driver first
|
||||
driverName := storage.Driver
|
||||
@ -65,7 +67,7 @@ func CreateStorage(ctx context.Context, storage model.Storage) (uint, error) {
|
||||
|
||||
// LoadStorage load exist storage in db to memory
|
||||
func LoadStorage(ctx context.Context, storage model.Storage) error {
|
||||
storage.MountPath = utils.StandardizePath(storage.MountPath)
|
||||
storage.MountPath = utils.FixAndCleanPath(storage.MountPath)
|
||||
// check driver first
|
||||
driverName := storage.Driver
|
||||
driverNew, err := GetDriverNew(driverName)
|
||||
@ -158,7 +160,7 @@ func UpdateStorage(ctx context.Context, storage model.Storage) error {
|
||||
return errors.Errorf("driver cannot be changed")
|
||||
}
|
||||
storage.Modified = time.Now()
|
||||
storage.MountPath = utils.StandardizePath(storage.MountPath)
|
||||
storage.MountPath = utils.FixAndCleanPath(storage.MountPath)
|
||||
err = db.UpdateStorage(&storage)
|
||||
if err != nil {
|
||||
return errors.WithMessage(err, "failed update storage in database")
|
||||
@ -237,25 +239,20 @@ func saveDriverStorage(driver driver.Driver) error {
|
||||
func getStoragesByPath(path string) []driver.Driver {
|
||||
storages := make([]driver.Driver, 0)
|
||||
curSlashCount := 0
|
||||
storagesMap.Range(func(key string, value driver.Driver) bool {
|
||||
virtualPath := utils.GetActualVirtualPath(value.GetStorage().MountPath)
|
||||
if virtualPath == "/" {
|
||||
virtualPath = ""
|
||||
storagesMap.Range(func(mountPath string, value driver.Driver) bool {
|
||||
virtualPath := utils.GetActualVirtualPath(mountPath)
|
||||
// is this path
|
||||
if strings.HasPrefix(path, virtualPath) {
|
||||
slashCount := strings.Count(utils.PathAddSeparatorSuffix(virtualPath), "/")
|
||||
// not the longest match
|
||||
if slashCount > curSlashCount {
|
||||
storages = storages[:0]
|
||||
curSlashCount = slashCount
|
||||
}
|
||||
if slashCount == curSlashCount {
|
||||
storages = append(storages, value)
|
||||
}
|
||||
}
|
||||
// not this
|
||||
if path != virtualPath && !strings.HasPrefix(path, virtualPath+"/") {
|
||||
return true
|
||||
}
|
||||
slashCount := strings.Count(virtualPath, "/")
|
||||
// not the longest match
|
||||
if slashCount < curSlashCount {
|
||||
return true
|
||||
}
|
||||
if slashCount > curSlashCount {
|
||||
storages = storages[:0]
|
||||
curSlashCount = slashCount
|
||||
}
|
||||
storages = append(storages, value)
|
||||
return true
|
||||
})
|
||||
// make sure the order is the same for same input
|
||||
@ -277,36 +274,25 @@ func GetStorageVirtualFilesByPath(prefix string) []model.Obj {
|
||||
}
|
||||
return storages[i].GetStorage().Order < storages[j].GetStorage().Order
|
||||
})
|
||||
prefix = utils.StandardizePath(prefix)
|
||||
if prefix != "/" {
|
||||
prefix += "/"
|
||||
}
|
||||
set := make(map[string]interface{})
|
||||
|
||||
prefix = utils.FixAndCleanPath(prefix)
|
||||
set := mapset.NewSet[string]()
|
||||
for _, v := range storages {
|
||||
// TODO should save a balanced storage
|
||||
// balance storage
|
||||
if utils.IsBalance(v.GetStorage().MountPath) {
|
||||
virtualPath := utils.GetActualVirtualPath(v.GetStorage().MountPath)
|
||||
// Exclude prefix itself and non prefix
|
||||
if len(prefix) >= len(virtualPath) || !strings.HasPrefix(virtualPath, prefix) {
|
||||
continue
|
||||
}
|
||||
virtualPath := v.GetStorage().MountPath
|
||||
if len(virtualPath) <= len(prefix) {
|
||||
continue
|
||||
|
||||
name := strings.SplitN(strings.TrimPrefix(virtualPath[len(prefix):], "/"), "/", 1)[0]
|
||||
if set.Add(name) {
|
||||
files = append(files, &model.Object{
|
||||
Name: name,
|
||||
Size: 0,
|
||||
Modified: v.GetStorage().Modified,
|
||||
IsFolder: true,
|
||||
})
|
||||
}
|
||||
// not prefixed with `prefix`
|
||||
if !strings.HasPrefix(virtualPath, prefix) {
|
||||
continue
|
||||
}
|
||||
name := strings.Split(strings.TrimPrefix(virtualPath, prefix), "/")[0]
|
||||
if _, ok := set[name]; ok {
|
||||
continue
|
||||
}
|
||||
files = append(files, &model.Object{
|
||||
Name: name,
|
||||
Size: 0,
|
||||
Modified: v.GetStorage().Modified,
|
||||
IsFolder: true,
|
||||
})
|
||||
set[name] = nil
|
||||
}
|
||||
return files
|
||||
}
|
||||
@ -315,7 +301,7 @@ var balanceMap generic_sync.MapOf[string, int]
|
||||
|
||||
// GetBalancedStorage get storage by path
|
||||
func GetBalancedStorage(path string) driver.Driver {
|
||||
path = utils.StandardizePath(path)
|
||||
path = utils.FixAndCleanPath(path)
|
||||
storages := getStoragesByPath(path)
|
||||
storageNum := len(storages)
|
||||
switch storageNum {
|
||||
@ -325,15 +311,9 @@ func GetBalancedStorage(path string) driver.Driver {
|
||||
return storages[0]
|
||||
default:
|
||||
virtualPath := utils.GetActualVirtualPath(storages[0].GetStorage().MountPath)
|
||||
cur, ok := balanceMap.Load(virtualPath)
|
||||
i := 0
|
||||
if ok {
|
||||
i = cur
|
||||
i = (i + 1) % storageNum
|
||||
balanceMap.Store(virtualPath, i)
|
||||
} else {
|
||||
balanceMap.Store(virtualPath, i)
|
||||
}
|
||||
i, _ := balanceMap.LoadOrStore(virtualPath, 0)
|
||||
i = (i + 1) % storageNum
|
||||
balanceMap.Store(virtualPath, i)
|
||||
return storages[i]
|
||||
}
|
||||
}
|
||||
|
Reference in New Issue
Block a user