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>
pull/2745/head
foxxorcat 2022-12-17 19:49:05 +08:00 committed by GitHub
parent 3d336b328a
commit fb64f00640
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
50 changed files with 297 additions and 381 deletions

View File

@ -58,11 +58,6 @@ func (d *Pan123) List(ctx context.Context, dir model.Obj, args model.ListArgs) (
}) })
} }
//func (d *Pan123) Get(ctx context.Context, path string) (model.Obj, error) {
// // this is optional
// return nil, errs.NotImplement
//}
func (d *Pan123) Link(ctx context.Context, file model.Obj, args model.LinkArgs) (*model.Link, error) { func (d *Pan123) Link(ctx context.Context, file model.Obj, args model.LinkArgs) (*model.Link, error) {
if f, ok := file.(File); ok { if f, ok := file.(File); ok {
//var resp DownResp //var resp DownResp

View File

@ -5,7 +5,6 @@ import (
"time" "time"
"github.com/alist-org/alist/v3/internal/model" "github.com/alist-org/alist/v3/internal/model"
"github.com/alist-org/alist/v3/pkg/utils"
) )
//type BaseResp struct { //type BaseResp struct {
@ -40,7 +39,7 @@ func (f File) GetSize() int64 {
} }
func (f File) GetName() string { func (f File) GetName() string {
return utils.MappingName(f.FileName) return f.FileName
} }
func (f File) ModTime() time.Time { func (f File) ModTime() time.Time {

View File

@ -53,11 +53,6 @@ func (d *Yun139) List(ctx context.Context, dir model.Obj, args model.ListArgs) (
} }
} }
//func (d *Yun139) Get(ctx context.Context, path string) (model.Obj, error) {
// // this is optional
// return nil, errs.NotImplement
//}
func (d *Yun139) Link(ctx context.Context, file model.Obj, args model.LinkArgs) (*model.Link, error) { func (d *Yun139) Link(ctx context.Context, file model.Obj, args model.LinkArgs) (*model.Link, error) {
u, err := d.getLink(file.GetID()) u, err := d.getLink(file.GetID())
if err != nil { if err != nil {

View File

@ -46,11 +46,6 @@ func (d *Cloud189) List(ctx context.Context, dir model.Obj, args model.ListArgs)
return d.getFiles(dir.GetID()) return d.getFiles(dir.GetID())
} }
//func (d *Cloud189) Get(ctx context.Context, path string) (model.Obj, error) {
// // this is optional
// return nil, errs.NotImplement
//}
func (d *Cloud189) Link(ctx context.Context, file model.Obj, args model.LinkArgs) (*model.Link, error) { func (d *Cloud189) Link(ctx context.Context, file model.Obj, args model.LinkArgs) (*model.Link, error) {
var resp DownResp var resp DownResp
u := "https://cloud.189.cn/api/portal/getFileInfo.action" u := "https://cloud.189.cn/api/portal/getFileInfo.action"

View File

@ -6,8 +6,6 @@ import (
"sort" "sort"
"strings" "strings"
"time" "time"
"github.com/alist-org/alist/v3/pkg/utils"
) )
// 居然有四种返回方式 // 居然有四种返回方式
@ -136,7 +134,7 @@ type Cloud189File struct {
} }
func (c *Cloud189File) GetSize() int64 { return c.Size } func (c *Cloud189File) GetSize() int64 { return c.Size }
func (c *Cloud189File) GetName() string { return utils.MappingName(c.Name) } func (c *Cloud189File) GetName() string { return c.Name }
func (c *Cloud189File) ModTime() time.Time { func (c *Cloud189File) ModTime() time.Time {
if c.parseTime == nil { if c.parseTime == nil {
c.parseTime = MustParseTime(c.LastOpTime) c.parseTime = MustParseTime(c.LastOpTime)
@ -168,7 +166,7 @@ type Cloud189Folder struct {
} }
func (c *Cloud189Folder) GetSize() int64 { return 0 } func (c *Cloud189Folder) GetSize() int64 { return 0 }
func (c *Cloud189Folder) GetName() string { return utils.MappingName(c.Name) } func (c *Cloud189Folder) GetName() string { return c.Name }
func (c *Cloud189Folder) ModTime() time.Time { func (c *Cloud189Folder) ModTime() time.Time {
if c.parseTime == nil { if c.parseTime == nil {
c.parseTime = MustParseTime(c.LastOpTime) c.parseTime = MustParseTime(c.LastOpTime)

View File

@ -67,11 +67,6 @@ func (d *AListV2) List(ctx context.Context, dir model.Obj, args model.ListArgs)
return files, nil return files, nil
} }
//func (d *AList) Get(ctx context.Context, path string) (model.Obj, error) {
// // this is optional
// return nil, errs.NotImplement
//}
func (d *AListV2) Link(ctx context.Context, file model.Obj, args model.LinkArgs) (*model.Link, error) { func (d *AListV2) Link(ctx context.Context, file model.Obj, args model.LinkArgs) (*model.Link, error) {
url := d.Address + "/api/public/path" url := d.Address + "/api/public/path"
var resp common.Resp[PathResp] var resp common.Resp[PathResp]

View File

@ -71,11 +71,6 @@ func (d *AListV3) List(ctx context.Context, dir model.Obj, args model.ListArgs)
return files, nil return files, nil
} }
//func (d *AList) Get(ctx context.Context, path string) (model.Obj, error) {
// // this is optional
// return nil, errs.NotImplement
//}
func (d *AListV3) Link(ctx context.Context, file model.Obj, args model.LinkArgs) (*model.Link, error) { func (d *AListV3) Link(ctx context.Context, file model.Obj, args model.LinkArgs) (*model.Link, error) {
url := d.Address + "/api/fs/get" url := d.Address + "/api/fs/get"
var resp common.Resp[FsGetResp] var resp common.Resp[FsGetResp]

View File

@ -81,11 +81,6 @@ func (d *AliDrive) List(ctx context.Context, dir model.Obj, args model.ListArgs)
}) })
} }
//func (d *AliDrive) Get(ctx context.Context, path string) (model.Obj, error) {
// // TODO this is optional
// return nil, errs.NotImplement
//}
func (d *AliDrive) Link(ctx context.Context, file model.Obj, args model.LinkArgs) (*model.Link, error) { func (d *AliDrive) Link(ctx context.Context, file model.Obj, args model.LinkArgs) (*model.Link, error) {
data := base.Json{ data := base.Json{
"drive_id": d.DriveId, "drive_id": d.DriveId,

View File

@ -68,11 +68,6 @@ func (d *AliyundriveShare) List(ctx context.Context, dir model.Obj, args model.L
}) })
} }
//func (d *AliyundriveShare) Get(ctx context.Context, path string) (model.Obj, error) {
// // this is optional
// return nil, errs.NotImplement
//}
func (d *AliyundriveShare) Link(ctx context.Context, file model.Obj, args model.LinkArgs) (*model.Link, error) { func (d *AliyundriveShare) Link(ctx context.Context, file model.Obj, args model.LinkArgs) (*model.Link, error) {
data := base.Json{ data := base.Json{
"drive_id": d.DriveId, "drive_id": d.DriveId,

View File

@ -52,11 +52,6 @@ func (d *BaiduNetdisk) List(ctx context.Context, dir model.Obj, args model.ListA
}) })
} }
//func (d *BaiduNetdisk) Get(ctx context.Context, path string) (model.Obj, error) {
// // this is optional
// return nil, errs.NotImplement
//}
func (d *BaiduNetdisk) Link(ctx context.Context, file model.Obj, args model.LinkArgs) (*model.Link, error) { func (d *BaiduNetdisk) Link(ctx context.Context, file model.Obj, args model.LinkArgs) (*model.Link, error) {
if d.DownloadAPI == "crack" { if d.DownloadAPI == "crack" {
return d.linkCrack(file, args) return d.linkCrack(file, args)

View File

@ -3,8 +3,6 @@ package baiduphoto
import ( import (
"fmt" "fmt"
"time" "time"
"github.com/alist-org/alist/v3/pkg/utils"
) )
type TokenErrResp struct { type TokenErrResp struct {
@ -102,7 +100,7 @@ type (
) )
func (a *Album) GetSize() int64 { return 0 } func (a *Album) GetSize() int64 { return 0 }
func (a *Album) GetName() string { return utils.MappingName(a.Title) } func (a *Album) GetName() string { return a.Title }
func (a *Album) ModTime() time.Time { func (a *Album) ModTime() time.Time {
if a.parseTime == nil { if a.parseTime == nil {
a.parseTime = toTime(a.Mtime) a.parseTime = toTime(a.Mtime)

View File

@ -60,11 +60,6 @@ func (d *FTP) List(ctx context.Context, dir model.Obj, args model.ListArgs) ([]m
return res, nil return res, nil
} }
//func (d *FTP) Get(ctx context.Context, path string) (model.Obj, error) {
// // this is optional
// return nil, errs.NotImplement
//}
func (d *FTP) Link(ctx context.Context, file model.Obj, args model.LinkArgs) (*model.Link, error) { func (d *FTP) Link(ctx context.Context, file model.Obj, args model.LinkArgs) (*model.Link, error) {
if err := d.login(); err != nil { if err := d.login(); err != nil {
return nil, err return nil, err

View File

@ -4,14 +4,12 @@ import (
"context" "context"
"fmt" "fmt"
"net/http" "net/http"
stdpath "path"
"strconv" "strconv"
"github.com/alist-org/alist/v3/drivers/base" "github.com/alist-org/alist/v3/drivers/base"
"github.com/alist-org/alist/v3/internal/driver" "github.com/alist-org/alist/v3/internal/driver"
"github.com/alist-org/alist/v3/internal/errs" "github.com/alist-org/alist/v3/internal/errs"
"github.com/alist-org/alist/v3/internal/model" "github.com/alist-org/alist/v3/internal/model"
"github.com/alist-org/alist/v3/internal/op"
"github.com/alist-org/alist/v3/pkg/utils" "github.com/alist-org/alist/v3/pkg/utils"
"github.com/go-resty/resty/v2" "github.com/go-resty/resty/v2"
) )
@ -51,11 +49,6 @@ func (d *GoogleDrive) List(ctx context.Context, dir model.Obj, args model.ListAr
}) })
} }
//func (d *GoogleDrive) Get(ctx context.Context, path string) (model.Obj, error) {
// // this is optional
// return nil, errs.NotImplement
//}
func (d *GoogleDrive) Link(ctx context.Context, file model.Obj, args model.LinkArgs) (*model.Link, error) { func (d *GoogleDrive) Link(ctx context.Context, file model.Obj, args model.LinkArgs) (*model.Link, error) {
url := fmt.Sprintf("https://www.googleapis.com/drive/v3/files/%s?includeItemsFromAllDrives=true&supportsAllDrives=true", file.GetID()) url := fmt.Sprintf("https://www.googleapis.com/drive/v3/files/%s?includeItemsFromAllDrives=true&supportsAllDrives=true", file.GetID())
_, err := d.request(url, http.MethodGet, nil, nil) _, err := d.request(url, http.MethodGet, nil, nil)
@ -117,8 +110,7 @@ func (d *GoogleDrive) Remove(ctx context.Context, obj model.Obj) error {
} }
func (d *GoogleDrive) Put(ctx context.Context, dstDir model.Obj, stream model.FileStreamer, up driver.UpdateProgress) error { func (d *GoogleDrive) Put(ctx context.Context, dstDir model.Obj, stream model.FileStreamer, up driver.UpdateProgress) error {
obj, _ := op.Get(ctx, d, stdpath.Join(dstDir.GetPath(), stream.GetName())) obj := stream.GetOld()
var ( var (
e Error e Error
url string url string

View File

@ -47,11 +47,6 @@ func (d *GooglePhoto) List(ctx context.Context, dir model.Obj, args model.ListAr
}) })
} }
//func (d *GooglePhoto) Get(ctx context.Context, path string) (model.Obj, error) {
// // this is optional
// return nil, errs.NotImplement
//}
func (d *GooglePhoto) Link(ctx context.Context, file model.Obj, args model.LinkArgs) (*model.Link, error) { func (d *GooglePhoto) Link(ctx context.Context, file model.Obj, args model.LinkArgs) (*model.Link, error) {
f, err := d.getMedia(file.GetID()) f, err := d.getMedia(file.GetID())
if err != nil { if err != nil {

View File

@ -16,7 +16,6 @@ import (
"github.com/alist-org/alist/v3/internal/conf" "github.com/alist-org/alist/v3/internal/conf"
"github.com/alist-org/alist/v3/internal/driver" "github.com/alist-org/alist/v3/internal/driver"
"github.com/alist-org/alist/v3/internal/errs"
"github.com/alist-org/alist/v3/internal/model" "github.com/alist-org/alist/v3/internal/model"
"github.com/alist-org/alist/v3/internal/sign" "github.com/alist-org/alist/v3/internal/sign"
"github.com/alist-org/alist/v3/pkg/utils" "github.com/alist-org/alist/v3/pkg/utils"
@ -34,19 +33,18 @@ func (d *Local) Config() driver.Config {
return config return config
} }
func (d *Local) Init(ctx context.Context) (err error) { func (d *Local) Init(ctx context.Context) error {
if !utils.Exists(d.GetRootPath()) { if !utils.Exists(d.GetRootPath()) {
err = fmt.Errorf("root folder %s not exists", d.GetRootPath()) return fmt.Errorf("root folder %s not exists", d.GetRootPath())
} else { }
if !filepath.IsAbs(d.GetRootPath()) { if !filepath.IsAbs(d.GetRootPath()) {
abs, err := filepath.Abs(d.GetRootPath()) abs, err := filepath.Abs(d.GetRootPath())
if err != nil { if err != nil {
return err return err
} }
d.SetRootPath(abs) d.Addition.RootFolderPath = abs
} }
} return nil
return err
} }
func (d *Local) Drop(ctx context.Context) error { func (d *Local) Drop(ctx context.Context) error {
@ -75,12 +73,13 @@ func (d *Local) List(ctx context.Context, dir model.Obj, args model.ListArgs) ([
thumb += "?type=thumb&sign=" + sign.Sign(stdpath.Join(args.ReqPath, f.Name())) thumb += "?type=thumb&sign=" + sign.Sign(stdpath.Join(args.ReqPath, f.Name()))
} }
isFolder := f.IsDir() || isSymlinkDir(f, fullPath) isFolder := f.IsDir() || isSymlinkDir(f, fullPath)
size := f.Size() var size int64
if isFolder { if !isFolder {
size = 0 size = f.Size()
} }
file := model.ObjThumb{ file := model.ObjThumb{
Object: model.Object{ Object: model.Object{
Path: filepath.Join(dir.GetPath(), f.Name()),
Name: f.Name(), Name: f.Name(),
Modified: f.ModTime(), Modified: f.ModTime(),
Size: size, Size: size,
@ -95,29 +94,6 @@ func (d *Local) List(ctx context.Context, dir model.Obj, args model.ListArgs) ([
return files, nil return files, nil
} }
func (d *Local) Get(ctx context.Context, path string) (model.Obj, error) {
f, err := os.Stat(path)
if err != nil {
if strings.Contains(err.Error(), "cannot find the file") {
return nil, errs.ObjectNotFound
}
return nil, err
}
isFolder := f.IsDir() || isSymlinkDir(f, path)
size := f.Size()
if isFolder {
size = 0
}
file := model.Object{
Path: path,
Name: f.Name(),
Modified: f.ModTime(),
Size: size,
IsFolder: isFolder,
}
return &file, nil
}
func (d *Local) Link(ctx context.Context, file model.Obj, args model.LinkArgs) (*model.Link, error) { func (d *Local) Link(ctx context.Context, file model.Obj, args model.LinkArgs) (*model.Link, error) {
fullPath := file.GetPath() fullPath := file.GetPath()
var link model.Link var link model.Link

View File

@ -8,14 +8,12 @@ import (
"io" "io"
"net/http" "net/http"
"os" "os"
"path"
"strconv" "strconv"
"time" "time"
"github.com/alist-org/alist/v3/drivers/base" "github.com/alist-org/alist/v3/drivers/base"
"github.com/alist-org/alist/v3/internal/driver" "github.com/alist-org/alist/v3/internal/driver"
"github.com/alist-org/alist/v3/internal/model" "github.com/alist-org/alist/v3/internal/model"
"github.com/alist-org/alist/v3/internal/op"
"github.com/alist-org/alist/v3/pkg/utils" "github.com/alist-org/alist/v3/pkg/utils"
"github.com/aws/aws-sdk-go/aws" "github.com/aws/aws-sdk-go/aws"
"github.com/aws/aws-sdk-go/aws/credentials" "github.com/aws/aws-sdk-go/aws/credentials"
@ -59,7 +57,7 @@ func (d *MediaTrack) List(ctx context.Context, dir model.Obj, args model.ListArg
if f.File != nil && f.File.Cover != "" { if f.File != nil && f.File.Cover != "" {
thumb = "https://nano.mtres.cn/" + f.File.Cover thumb = "https://nano.mtres.cn/" + f.File.Cover
} }
return &model.ObjThumb{ return &Object{
Object: model.Object{ Object: model.Object{
ID: f.ID, ID: f.ID,
Name: f.Title, Name: f.Title,
@ -68,15 +66,11 @@ func (d *MediaTrack) List(ctx context.Context, dir model.Obj, args model.ListArg
Size: size, Size: size,
}, },
Thumbnail: model.Thumbnail{Thumbnail: thumb}, Thumbnail: model.Thumbnail{Thumbnail: thumb},
ParentID: dir.GetID(),
}, nil }, nil
}) })
} }
//func (d *MediaTrack) Get(ctx context.Context, path string) (model.Obj, error) {
// // this is optional
// return nil, errs.NotImplement
//}
func (d *MediaTrack) Link(ctx context.Context, file model.Obj, args model.LinkArgs) (*model.Link, error) { func (d *MediaTrack) Link(ctx context.Context, file model.Obj, args model.LinkArgs) (*model.Link, error) {
url := fmt.Sprintf("https://kayn.api.mediatrack.cn/v1/download_token/asset?asset_id=%s&source_type=project&password=&source_id=%s", url := fmt.Sprintf("https://kayn.api.mediatrack.cn/v1/download_token/asset?asset_id=%s&source_type=project&password=&source_id=%s",
file.GetID(), d.ProjectID) file.GetID(), d.ProjectID)
@ -151,16 +145,18 @@ func (d *MediaTrack) Copy(ctx context.Context, srcObj, dstDir model.Obj) error {
} }
func (d *MediaTrack) Remove(ctx context.Context, obj model.Obj) error { func (d *MediaTrack) Remove(ctx context.Context, obj model.Obj) error {
dir, err := op.Get(ctx, d, path.Dir(obj.GetPath())) var parentID string
if err != nil { if o, ok := obj.(*Object); ok {
return err parentID = o.ParentID
} else {
return fmt.Errorf("obj is not local Object")
} }
data := base.Json{ data := base.Json{
"origin_id": dir.GetID(), "origin_id": parentID,
"ids": []string{obj.GetID()}, "ids": []string{obj.GetID()},
} }
url := "https://jayce.api.mediatrack.cn/v4/assets/batch/delete" url := "https://jayce.api.mediatrack.cn/v4/assets/batch/delete"
_, err = d.request(url, http.MethodDelete, func(req *resty.Request) { _, err := d.request(url, http.MethodDelete, func(req *resty.Request) {
req.SetBody(data) req.SetBody(data)
}, nil) }, nil)
return err return err

View File

@ -1,6 +1,10 @@
package mediatrack package mediatrack
import "time" import (
"time"
"github.com/alist-org/alist/v3/internal/model"
)
type BaseResp struct { type BaseResp struct {
Status string `json:"status"` Status string `json:"status"`
@ -60,3 +64,9 @@ type UploadResp struct {
TraceID string `json:"trace_id"` TraceID string `json:"trace_id"`
RequestID string `json:"requestId"` RequestID string `json:"requestId"`
} }
type Object struct {
model.Object
model.Thumbnail
ParentID string
}

View File

@ -9,7 +9,6 @@ import (
"github.com/alist-org/alist/v3/internal/driver" "github.com/alist-org/alist/v3/internal/driver"
"github.com/alist-org/alist/v3/internal/errs" "github.com/alist-org/alist/v3/internal/errs"
"github.com/alist-org/alist/v3/internal/model" "github.com/alist-org/alist/v3/internal/model"
"github.com/alist-org/alist/v3/pkg/chanio"
log "github.com/sirupsen/logrus" log "github.com/sirupsen/logrus"
"github.com/t3rm1n4l/go-mega" "github.com/t3rm1n4l/go-mega"
) )
@ -56,14 +55,11 @@ func (d *Mega) List(ctx context.Context, dir model.Obj, args model.ListArgs) ([]
return nil, fmt.Errorf("unable to convert dir to mega node") return nil, fmt.Errorf("unable to convert dir to mega node")
} }
func (d *Mega) Get(ctx context.Context, path string) (model.Obj, error) { func (d *Mega) GetRoot(ctx context.Context) (model.Obj, error) {
if path == "/" {
n := d.c.FS.GetRoot() n := d.c.FS.GetRoot()
log.Debugf("mega root: %+v", *n) log.Debugf("mega root: %+v", *n)
return &MegaNode{n}, nil return &MegaNode{n}, nil
} }
return nil, errs.NotSupport
}
func (d *Mega) Link(ctx context.Context, file model.Obj, args model.LinkArgs) (*model.Link, error) { func (d *Mega) Link(ctx context.Context, file model.Obj, args model.LinkArgs) (*model.Link, error) {
if node, ok := file.(*MegaNode); ok { if node, ok := file.(*MegaNode); ok {
@ -79,17 +75,21 @@ func (d *Mega) Link(ctx context.Context, file model.Obj, args model.LinkArgs) (*
//u := down.GetResourceUrl() //u := down.GetResourceUrl()
//u = strings.Replace(u, "http", "https", 1) //u = strings.Replace(u, "http", "https", 1)
//return &model.Link{URL: u}, nil //return &model.Link{URL: u}, nil
c := chanio.New() r, w := io.Pipe()
go func() { go func() {
defer func() { defer func() {
_ = recover() _ = recover()
}() }()
log.Debugf("chunk size: %d", down.Chunks()) log.Debugf("chunk size: %d", down.Chunks())
var (
chunk []byte
err error
)
for id := 0; id < down.Chunks(); id++ { for id := 0; id < down.Chunks(); id++ {
chunk, err := down.DownloadChunk(id) chunk, err = down.DownloadChunk(id)
if err != nil { if err != nil {
log.Errorf("mega down: %+v", err) log.Errorf("mega down: %+v", err)
return break
} }
log.Debugf("id: %d,len: %d", id, len(chunk)) log.Debugf("id: %d,len: %d", id, len(chunk))
//_, _, err = down.ChunkLocation(id) //_, _, err = down.ChunkLocation(id)
@ -98,14 +98,16 @@ func (d *Mega) Link(ctx context.Context, file model.Obj, args model.LinkArgs) (*
// return // return
//} //}
//_, err = c.Write(chunk) //_, err = c.Write(chunk)
_, err = c.Write(chunk) if _, err = w.Write(chunk); err != nil {
break
} }
err := c.Close() }
err = w.CloseWithError(err)
if err != nil { if err != nil {
log.Errorf("mega down: %+v", err) log.Errorf("mega down: %+v", err)
} }
}() }()
return &model.Link{Data: c}, nil return &model.Link{Data: r}, nil
} }
return nil, fmt.Errorf("unable to convert dir to mega node") return nil, fmt.Errorf("unable to convert dir to mega node")
} }

View File

@ -2,14 +2,13 @@ package onedrive
import ( import (
"context" "context"
"fmt"
"net/http" "net/http"
stdpath "path"
"github.com/alist-org/alist/v3/drivers/base" "github.com/alist-org/alist/v3/drivers/base"
"github.com/alist-org/alist/v3/internal/driver" "github.com/alist-org/alist/v3/internal/driver"
"github.com/alist-org/alist/v3/internal/errs" "github.com/alist-org/alist/v3/internal/errs"
"github.com/alist-org/alist/v3/internal/model" "github.com/alist-org/alist/v3/internal/model"
"github.com/alist-org/alist/v3/internal/op"
"github.com/alist-org/alist/v3/pkg/utils" "github.com/alist-org/alist/v3/pkg/utils"
"github.com/go-resty/resty/v2" "github.com/go-resty/resty/v2"
) )
@ -45,7 +44,7 @@ func (d *Onedrive) List(ctx context.Context, dir model.Obj, args model.ListArgs)
return nil, err return nil, err
} }
return utils.SliceConvert(files, func(src File) (model.Obj, error) { return utils.SliceConvert(files, func(src File) (model.Obj, error) {
return fileToObj(src), nil return fileToObj(src, dir.GetID()), nil
}) })
} }
@ -90,18 +89,21 @@ func (d *Onedrive) Move(ctx context.Context, srcObj, dstDir model.Obj) error {
} }
func (d *Onedrive) Rename(ctx context.Context, srcObj model.Obj, newName string) error { func (d *Onedrive) Rename(ctx context.Context, srcObj model.Obj, newName string) error {
dstDir, err := op.Get(ctx, d, stdpath.Dir(srcObj.GetPath())) //dstDir, err := op.GetUnwrap(ctx, d, stdpath.Dir(srcObj.GetPath()))
if err != nil { var parentID string
return err if o, ok := srcObj.(*Object); ok {
parentID = o.ParentID
} else {
return fmt.Errorf("srcObj is not Object")
} }
data := base.Json{ data := base.Json{
"parentReference": base.Json{ "parentReference": base.Json{
"id": dstDir.GetID(), "id": parentID,
}, },
"name": newName, "name": newName,
} }
url := d.GetMetaUrl(false, srcObj.GetPath()) url := d.GetMetaUrl(false, srcObj.GetPath())
_, err = d.Request(url, http.MethodPatch, func(req *resty.Request) { _, err := d.Request(url, http.MethodPatch, func(req *resty.Request) {
req.SetBody(data) req.SetBody(data)
}, nil) }, nil)
return err return err

View File

@ -42,12 +42,18 @@ type File struct {
} `json:"parentReference"` } `json:"parentReference"`
} }
func fileToObj(f File) *model.ObjThumbURL { type Object struct {
model.ObjThumbURL
ParentID string
}
func fileToObj(f File, parentID string) *Object {
thumb := "" thumb := ""
if len(f.Thumbnails) > 0 { if len(f.Thumbnails) > 0 {
thumb = f.Thumbnails[0].Medium.Url thumb = f.Thumbnails[0].Medium.Url
} }
return &model.ObjThumbURL{ return &Object{
ObjThumbURL: model.ObjThumbURL{
Object: model.Object{ Object: model.Object{
ID: f.Id, ID: f.Id,
Name: f.Name, Name: f.Name,
@ -57,6 +63,8 @@ func fileToObj(f File) *model.ObjThumbURL {
}, },
Thumbnail: model.Thumbnail{Thumbnail: thumb}, Thumbnail: model.Thumbnail{Thumbnail: thumb},
Url: model.Url{Url: f.Url}, Url: model.Url{Url: f.Url},
},
ParentID: parentID,
} }
} }

View File

@ -51,11 +51,6 @@ func (d *Quark) List(ctx context.Context, dir model.Obj, args model.ListArgs) ([
}) })
} }
//func (d *Quark) Get(ctx context.Context, path string) (model.Obj, error) {
// // TODO this is optional
// return nil, errs.NotImplement
//}
func (d *Quark) Link(ctx context.Context, file model.Obj, args model.LinkArgs) (*model.Link, error) { func (d *Quark) Link(ctx context.Context, file model.Obj, args model.LinkArgs) (*model.Link, error) {
data := base.Json{ data := base.Json{
"fids": []string{file.GetID()}, "fids": []string{file.GetID()},

View File

@ -57,11 +57,6 @@ func (d *S3) List(ctx context.Context, dir model.Obj, args model.ListArgs) ([]mo
return d.listV1(dir.GetPath()) return d.listV1(dir.GetPath())
} }
//func (d *S3) Get(ctx context.Context, path string) (model.Obj, error) {
// // this is optional
// return nil, errs.NotImplement
//}
func (d *S3) Link(ctx context.Context, file model.Obj, args model.LinkArgs) (*model.Link, error) { func (d *S3) Link(ctx context.Context, file model.Obj, args model.LinkArgs) (*model.Link, error) {
path := getKey(file.GetPath(), false) path := getKey(file.GetPath(), false)
disposition := fmt.Sprintf(`attachment;filename="%s"`, url.QueryEscape(stdpath.Base(path))) disposition := fmt.Sprintf(`attachment;filename="%s"`, url.QueryEscape(stdpath.Base(path)))

View File

@ -47,11 +47,6 @@ func (d *SFTP) List(ctx context.Context, dir model.Obj, args model.ListArgs) ([]
}) })
} }
//func (d *SFTP) Get(ctx context.Context, path string) (model.Obj, error) {
// // this is optional
// return nil, errs.NotImplement
//}
func (d *SFTP) Link(ctx context.Context, file model.Obj, args model.LinkArgs) (*model.Link, error) { func (d *SFTP) Link(ctx context.Context, file model.Obj, args model.LinkArgs) (*model.Link, error) {
remoteFile, err := d.client.Open(file.GetPath()) remoteFile, err := d.client.Open(file.GetPath())
if err != nil { if err != nil {

View File

@ -69,11 +69,6 @@ func (d *SMB) List(ctx context.Context, dir model.Obj, args model.ListArgs) ([]m
return files, nil return files, nil
} }
//func (d *SMB) Get(ctx context.Context, path string) (model.Obj, error) {
// // this is optional
// return nil, errs.NotImplement
//}
func (d *SMB) Link(ctx context.Context, file model.Obj, args model.LinkArgs) (*model.Link, error) { func (d *SMB) Link(ctx context.Context, file model.Obj, args model.LinkArgs) (*model.Link, error) {
if err := d.checkConn(); err != nil { if err := d.checkConn(); err != nil {
return nil, err return nil, err

View File

@ -37,11 +37,6 @@ func (d *Teambition) List(ctx context.Context, dir model.Obj, args model.ListArg
return d.getFiles(dir.GetID()) return d.getFiles(dir.GetID())
} }
//func (d *Teambition) Get(ctx context.Context, path string) (model.Obj, error) {
// // this is optional
// return nil, errs.NotImplement
//}
func (d *Teambition) Link(ctx context.Context, file model.Obj, args model.LinkArgs) (*model.Link, error) { func (d *Teambition) Link(ctx context.Context, file model.Obj, args model.LinkArgs) (*model.Link, error) {
if u, ok := file.(model.URL); ok { if u, ok := file.(model.URL); ok {
url := u.URL() url := u.URL()

View File

@ -36,11 +36,6 @@ func (d *Template) List(ctx context.Context, dir model.Obj, args model.ListArgs)
return nil, errs.NotImplement return nil, errs.NotImplement
} }
//func (d *Template) Get(ctx context.Context, path string) (model.Obj, error) {
// // this is optional
// return nil, errs.NotImplement
//}
func (d *Template) Link(ctx context.Context, file model.Obj, args model.LinkArgs) (*model.Link, error) { func (d *Template) Link(ctx context.Context, file model.Obj, args model.LinkArgs) (*model.Link, error) {
// TODO return link of file // TODO return link of file
return nil, errs.NotImplement return nil, errs.NotImplement

View File

@ -4,8 +4,6 @@ import (
"fmt" "fmt"
"strconv" "strconv"
"time" "time"
"github.com/alist-org/alist/v3/pkg/utils"
) )
type ErrResp struct { type ErrResp struct {
@ -149,7 +147,7 @@ type Files struct {
} }
func (c *Files) GetSize() int64 { size, _ := strconv.ParseInt(c.Size, 10, 64); return size } func (c *Files) GetSize() int64 { size, _ := strconv.ParseInt(c.Size, 10, 64); return size }
func (c *Files) GetName() string { return utils.MappingName(c.Name) } func (c *Files) GetName() string { return c.Name }
func (c *Files) ModTime() time.Time { return c.ModifiedTime } func (c *Files) ModTime() time.Time { return c.ModifiedTime }
func (c *Files) IsDir() bool { return c.Kind == FOLDER } func (c *Files) IsDir() bool { return c.Kind == FOLDER }
func (c *Files) GetID() string { return c.ID } func (c *Files) GetID() string { return c.ID }

View File

@ -71,11 +71,6 @@ func (d *USS) List(ctx context.Context, dir model.Obj, args model.ListArgs) ([]m
return res, err return res, err
} }
//func (d *USS) Get(ctx context.Context, path string) (model.Obj, error) {
// // this is optional
// return nil, errs.NotImplement
//}
func (d *USS) Link(ctx context.Context, file model.Obj, args model.LinkArgs) (*model.Link, error) { func (d *USS) Link(ctx context.Context, file model.Obj, args model.LinkArgs) (*model.Link, error) {
key := getKey(file.GetPath(), false) key := getKey(file.GetPath(), false)
host := d.CustomHost host := d.CustomHost

View File

@ -62,11 +62,6 @@ func (d *WebDav) List(ctx context.Context, dir model.Obj, args model.ListArgs) (
}) })
} }
//func (d *WebDav) Get(ctx context.Context, path string) (model.Obj, error) {
// // this is optional
// return nil, errs.NotImplement
//}
func (d *WebDav) Link(ctx context.Context, file model.Obj, args model.LinkArgs) (*model.Link, error) { func (d *WebDav) Link(ctx context.Context, file model.Obj, args model.LinkArgs) (*model.Link, error) {
url, header, err := d.client.Link(file.GetPath()) url, header, err := d.client.Link(file.GetPath())
if err != nil { if err != nil {

View File

@ -45,11 +45,6 @@ func (d *YandexDisk) List(ctx context.Context, dir model.Obj, args model.ListArg
}) })
} }
//func (d *YandexDisk) Get(ctx context.Context, path string) (model.Obj, error) {
// // this is optional
// return nil, errs.NotImplement
//}
func (d *YandexDisk) Link(ctx context.Context, file model.Obj, args model.LinkArgs) (*model.Link, error) { func (d *YandexDisk) Link(ctx context.Context, file model.Obj, args model.LinkArgs) (*model.Link, error) {
var resp DownResp var resp DownResp
_, err := d.request("/download", http.MethodGet, func(req *resty.Request) { _, err := d.request("/download", http.MethodGet, func(req *resty.Request) {

View File

@ -19,7 +19,7 @@ var metaCache = cache.NewMemCache(cache.WithShards[*model.Meta](2))
var metaG singleflight.Group[*model.Meta] var metaG singleflight.Group[*model.Meta]
func GetNearestMeta(path string) (*model.Meta, error) { func GetNearestMeta(path string) (*model.Meta, error) {
path = utils.StandardizePath(path) path = utils.FixAndCleanPath(path)
meta, err := GetMetaByPath(path) meta, err := GetMetaByPath(path)
if err == nil { if err == nil {
return meta, nil return meta, nil

View File

@ -30,6 +30,7 @@ func BatchCreateSearchNodes(nodes *[]model.SearchNode) error {
} }
func DeleteSearchNodesByParent(prefix string) error { func DeleteSearchNodesByParent(prefix string) error {
prefix = utils.FixAndCleanPath(prefix)
err := db.Where(whereInParent(prefix)).Delete(&model.SearchNode{}).Error err := db.Where(whereInParent(prefix)).Delete(&model.SearchNode{}).Error
if err != nil { if err != nil {
return err return err
@ -37,7 +38,7 @@ func DeleteSearchNodesByParent(prefix string) error {
dir, name := path.Split(prefix) dir, name := path.Split(prefix)
return db.Where(fmt.Sprintf("%s = ? AND %s = ?", return db.Where(fmt.Sprintf("%s = ? AND %s = ?",
columnName("parent"), columnName("name")), columnName("parent"), columnName("name")),
utils.StandardizePath(dir), name).Delete(&model.SearchNode{}).Error dir, name).Delete(&model.SearchNode{}).Error
} }
func ClearSearchNodes() error { func ClearSearchNodes() error {

View File

@ -39,7 +39,7 @@ type Reader interface {
} }
type Getter interface { type Getter interface {
Get(ctx context.Context, path string) (model.Obj, error) GetRoot(ctx context.Context) (model.Obj, error)
} }
type Writer interface { type Writer interface {

View File

@ -80,7 +80,7 @@ func Remove(ctx context.Context, path string) error {
return err 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) err := putDirectly(ctx, dstDirPath, file)
if err != nil { if err != nil {
log.Errorf("failed put %s: %+v", dstDirPath, err) log.Errorf("failed put %s: %+v", dstDirPath, err)
@ -88,7 +88,7 @@ func PutDirectly(ctx context.Context, dstDirPath string, file model.FileStreamer
return err return err
} }
func PutAsTask(dstDirPath string, file model.FileStreamer) error { func PutAsTask(dstDirPath string, file *model.FileStream) error {
err := putAsTask(dstDirPath, file) err := putAsTask(dstDirPath, file)
if err != nil { if err != nil {
log.Errorf("failed put %s: %+v", dstDirPath, err) log.Errorf("failed put %s: %+v", dstDirPath, err)

View File

@ -12,7 +12,7 @@ import (
) )
func get(ctx context.Context, path string) (model.Obj, error) { func get(ctx context.Context, path string) (model.Obj, error) {
path = utils.StandardizePath(path) path = utils.FixAndCleanPath(path)
// maybe a virtual file // maybe a virtual file
if path != "/" { if path != "/" {
virtualFiles := op.GetStorageVirtualFilesByPath(stdpath.Dir(path)) virtualFiles := op.GetStorageVirtualFilesByPath(stdpath.Dir(path))

View File

@ -18,7 +18,7 @@ var UploadTaskManager = task.NewTaskManager(3, func(tid *uint64) {
}) })
// putAsTask add as a put task and return immediately // 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) storage, dstDirActualPath, err := op.GetStorageAndActualPath(dstDirPath)
if err != nil { if err != nil {
return errors.WithMessage(err, "failed get storage") 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 // 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) storage, dstDirActualPath, err := op.GetStorageAndActualPath(dstDirPath)
if err != nil { if err != nil {
return errors.WithMessage(err, "failed get storage") return errors.WithMessage(err, "failed get storage")

View File

@ -35,7 +35,7 @@ func containsByName(files []model.Obj, file model.Obj) bool {
var httpClient = &http.Client{} 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 var rc io.ReadCloser
mimetype := utils.GetMimeType(file.GetName()) mimetype := utils.GetMimeType(file.GetName())
if link.Data != nil { if link.Data != nil {

View File

@ -8,11 +8,18 @@ import (
"github.com/maruel/natural" "github.com/maruel/natural"
) )
type UnwrapObj interface {
Unwrap() Obj
}
type Obj interface { type Obj interface {
GetSize() int64 GetSize() int64
GetName() string GetName() string
ModTime() time.Time ModTime() time.Time
IsDir() bool IsDir() bool
// The internal information of the driver.
// If you want to use it, please understand what it means
GetID() string GetID() string
GetPath() string GetPath() string
} }
@ -24,6 +31,7 @@ type FileStreamer interface {
SetReadCloser(io.ReadCloser) SetReadCloser(io.ReadCloser)
NeedStore() bool NeedStore() bool
GetReadCloser() io.ReadCloser GetReadCloser() io.ReadCloser
GetOld() Obj
} }
type URL interface { type URL interface {
@ -86,3 +94,9 @@ func ExtractFolder(objs []Obj, extractFolder string) {
return false return false
}) })
} }
func WrapObjsName(objs []Obj) {
for i := 0; i < len(objs); i++ {
objs[i] = &ObjWrapName{Obj: objs[i]}
}
}

View File

@ -6,6 +6,22 @@ import (
"github.com/alist-org/alist/v3/pkg/utils" "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 { type Object struct {
ID string ID string
Path string Path string
@ -16,7 +32,7 @@ type Object struct {
} }
func (o *Object) GetName() string { func (o *Object) GetName() string {
return utils.MappingName(o.Name) return o.Name
} }
func (o *Object) GetSize() int64 { func (o *Object) GetSize() int64 {

View File

@ -9,6 +9,7 @@ type FileStream struct {
io.ReadCloser io.ReadCloser
Mimetype string Mimetype string
WebPutAsTask bool WebPutAsTask bool
Old Obj
} }
func (f *FileStream) GetMimetype() string { func (f *FileStream) GetMimetype() string {
@ -26,3 +27,7 @@ func (f *FileStream) GetReadCloser() io.ReadCloser {
func (f *FileStream) SetReadCloser(rc io.ReadCloser) { func (f *FileStream) SetReadCloser(rc io.ReadCloser) {
f.ReadCloser = rc f.ReadCloser = rc
} }
func (f *FileStream) GetOld() Obj {
return f.Old
}

View File

@ -1,3 +1,6 @@
package op package op
var WORK = "work" const (
WORK = "work"
RootName = "root"
)

View File

@ -4,7 +4,6 @@ import (
"context" "context"
"os" "os"
stdpath "path" stdpath "path"
"strings"
"time" "time"
"github.com/Xhofe/go-cache" "github.com/Xhofe/go-cache"
@ -23,12 +22,11 @@ var listCache = cache.NewMemCache(cache.WithShards[[]model.Obj](64))
var listG singleflight.Group[[]model.Obj] var listG singleflight.Group[[]model.Obj]
func ClearCache(storage driver.Driver, path string) { func ClearCache(storage driver.Driver, path string) {
key := stdpath.Join(storage.GetStorage().MountPath, path) listCache.Del(Key(storage, path))
listCache.Del(key)
} }
func Key(storage driver.Driver, path string) string { 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 // 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 { if storage.Config().CheckStatus && storage.GetStorage().Status != WORK {
return nil, errors.Errorf("storage not init: %s", storage.GetStorage().Status) 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) log.Debugf("op.List %s", path)
key := Key(storage, path) key := Key(storage, path)
if len(refresh) == 0 || !refresh[0] { 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 return files, nil
} }
} }
dir, err := Get(ctx, storage, path) dir, err := GetUnwrap(ctx, storage, path)
if err != nil { if err != nil {
return nil, errors.WithMessage(err, "failed get dir") 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 { if err != nil {
return nil, errors.Wrapf(err, "failed to list objs") 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 // call hooks
go func(reqPath string, files []model.Obj) { go func(reqPath string, files []model.Obj) {
for _, hook := range objsUpdateHooks { for _, hook := range objsUpdateHooks {
@ -78,48 +84,49 @@ func List(ctx context.Context, storage driver.Driver, path string, args model.Li
return objs, err 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 // Get object from list of files
func Get(ctx context.Context, storage driver.Driver, path string) (model.Obj, error) { 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) 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 // is root folder
if r, ok := storage.GetAddition().(driver.IRootId); ok && utils.PathEqual(path, "/") { if utils.PathEqual(path, "/") {
return &model.Object{ var rootObj model.Obj
switch r := storage.GetAddition().(type) {
case driver.IRootId:
rootObj = &model.Object{
ID: r.GetRootId(), ID: r.GetRootId(),
Name: "root", Name: RootName,
Size: 0, Size: 0,
Modified: storage.GetStorage().Modified, Modified: storage.GetStorage().Modified,
IsFolder: true, IsFolder: true,
}, nil
} }
if r, ok := storage.GetAddition().(driver.IRootPath); ok && isRoot(path, r.GetRootPath()) { case driver.IRootPath:
return &model.Object{ rootObj = &model.Object{
Path: r.GetRootPath(), Path: r.GetRootPath(),
Name: "root", Name: RootName,
Size: 0, Size: 0,
Modified: storage.GetStorage().Modified, Modified: storage.GetStorage().Modified,
IsFolder: true, 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 }, nil
} }
// not root folder // not root folder
dir, name := stdpath.Split(path) dir, name := stdpath.Split(path)
files, err := List(ctx, storage, dir, model.ListArgs{}) 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 { for _, f := range files {
// TODO maybe copy obj here // TODO maybe copy obj here
if f.GetName() == name { 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 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) 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 linkCache = cache.NewMemCache(cache.WithShards[*model.Link](16))
var linkG singleflight.Group[*model.Link] 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 { if storage.Config().CheckStatus && storage.GetStorage().Status != WORK {
return nil, nil, errors.Errorf("storage not init: %s", storage.GetStorage().Status) 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 { if err != nil {
return nil, nil, errors.WithMessage(err, "failed to get file") 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 // Other api
func Other(ctx context.Context, storage driver.Driver, args model.FsOtherArgs) (interface{}, error) { 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 { if err != nil {
return nil, errors.WithMessagef(err, "failed to get obj") 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 { if storage.Config().CheckStatus && storage.GetStorage().Status != WORK {
return errors.Errorf("storage not init: %s", storage.GetStorage().Status) return errors.Errorf("storage not init: %s", storage.GetStorage().Status)
} }
path = utils.StandardizePath(path) path = utils.FixAndCleanPath(path)
key := Key(storage, path) key := Key(storage, path)
_, err, _ := mkdirG.Do(key, func() (interface{}, error) { _, err, _ := mkdirG.Do(key, func() (interface{}, error) {
// check if dir exists // check if dir exists
f, err := Get(ctx, storage, path) f, err := GetUnwrap(ctx, storage, path)
if err != nil { if err != nil {
if errs.IsObjectNotFound(err) { if errs.IsObjectNotFound(err) {
parentPath, dirName := stdpath.Split(path) parentPath, dirName := stdpath.Split(path)
@ -211,7 +222,7 @@ func MakeDir(ctx context.Context, storage driver.Driver, path string) error {
if err != nil { if err != nil {
return nil, errors.WithMessagef(err, "failed to make parent dir [%s]", parentPath) 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 // this should not happen
if err != nil { if err != nil {
return nil, errors.WithMessagef(err, "failed to get parent dir [%s]", parentPath) 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 { if storage.Config().CheckStatus && storage.GetStorage().Status != WORK {
return errors.Errorf("storage not init: %s", storage.GetStorage().Status) 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 { if err != nil {
return errors.WithMessage(err, "failed to get src object") return errors.WithMessage(err, "failed to get src object")
} }
dstDir, err := Get(ctx, storage, dstDirPath) dstDir, err := GetUnwrap(ctx, storage, dstDirPath)
if err != nil { if err != nil {
return errors.WithMessage(err, "failed to get dst dir") 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 { if storage.Config().CheckStatus && storage.GetStorage().Status != WORK {
return errors.Errorf("storage not init: %s", storage.GetStorage().Status) 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 { if err != nil {
return errors.WithMessage(err, "failed to get src object") 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 { if storage.Config().CheckStatus && storage.GetStorage().Status != WORK {
return errors.Errorf("storage not init: %s", storage.GetStorage().Status) 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 { if err != nil {
return errors.WithMessage(err, "failed to get src object") 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)) 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 { if storage.Config().CheckStatus && storage.GetStorage().Status != WORK {
return errors.Errorf("storage not init: %s", storage.GetStorage().Status) 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 err != nil {
// if object not found, it's ok // if object not found, it's ok
if errs.IsObjectNotFound(err) { if errs.IsObjectNotFound(err) {
@ -313,7 +324,7 @@ func Remove(ctx context.Context, storage driver.Driver, path string) error {
return errors.WithStack(err) 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 { if storage.Config().CheckStatus && storage.GetStorage().Status != WORK {
return errors.Errorf("storage not init: %s", storage.GetStorage().Status) 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 // if file exist and size = 0, delete it
dstPath := stdpath.Join(dstDirPath, file.GetName()) dstPath := stdpath.Join(dstDirPath, file.GetName())
fi, err := Get(ctx, storage, dstPath) fi, err := GetUnwrap(ctx, storage, dstPath)
if err == nil { if err == nil {
if fi.GetSize() == 0 { if fi.GetSize() == 0 {
err = Remove(ctx, storage, dstPath) err = Remove(ctx, storage, dstPath)
if err != nil { if err != nil {
return errors.WithMessagef(err, "failed remove file that exist and have size 0") return errors.WithMessagef(err, "failed remove file that exist and have size 0")
} }
} else {
file.Old = fi
} }
} }
err = MakeDir(ctx, storage, dstDirPath) err = MakeDir(ctx, storage, dstDirPath)
if err != nil { if err != nil {
return errors.WithMessagef(err, "failed to make dir [%s]", dstDirPath) 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 // this should not happen
if err != nil { if err != nil {
return errors.WithMessagef(err, "failed to get dir [%s]", dstDirPath) return errors.WithMessagef(err, "failed to get dir [%s]", dstDirPath)

View File

@ -1,7 +1,6 @@
package op package op
import ( import (
stdpath "path"
"strings" "strings"
"github.com/alist-org/alist/v3/internal/driver" "github.com/alist-org/alist/v3/internal/driver"
@ -10,30 +9,17 @@ import (
log "github.com/sirupsen/logrus" 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 // GetStorageAndActualPath Get the corresponding storage and actual path
// for path: remove the mount path prefix and join the actual root folder if exists // for path: remove the mount path prefix and join the actual root folder if exists
func GetStorageAndActualPath(rawPath string) (driver.Driver, string, error) { func GetStorageAndActualPath(rawPath string) (storage driver.Driver, actualPath string, err error) {
rawPath = utils.StandardizePath(rawPath) rawPath = utils.FixAndCleanPath(rawPath)
// why can remove this check? because reqPath has joined the base_path of user, no relative path storage = GetBalancedStorage(rawPath)
//if strings.Contains(rawPath, "..") {
// return nil, "", errors.WithStack(errs.RelativePath)
//}
storage := GetBalancedStorage(rawPath)
if storage == nil { 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) log.Debugln("use storage: ", storage.GetStorage().MountPath)
virtualPath := utils.GetActualVirtualPath(storage.GetStorage().MountPath) virtualPath := utils.GetActualVirtualPath(storage.GetStorage().MountPath)
actualPath := strings.TrimPrefix(rawPath, virtualPath) actualPath = utils.FixAndCleanPath(strings.TrimPrefix(rawPath, virtualPath))
actualPath = ActualPath(storage.GetAddition(), actualPath) return
return storage, actualPath, nil
} }

View File

@ -11,6 +11,7 @@ import (
"github.com/alist-org/alist/v3/internal/model" "github.com/alist-org/alist/v3/internal/model"
"github.com/alist-org/alist/v3/pkg/generic_sync" "github.com/alist-org/alist/v3/pkg/generic_sync"
"github.com/alist-org/alist/v3/pkg/utils" "github.com/alist-org/alist/v3/pkg/utils"
mapset "github.com/deckarep/golang-set/v2"
"github.com/pkg/errors" "github.com/pkg/errors"
log "github.com/sirupsen/logrus" log "github.com/sirupsen/logrus"
) )
@ -25,10 +26,11 @@ func GetAllStorages() []driver.Driver {
} }
func HasStorage(mountPath string) bool { func HasStorage(mountPath string) bool {
return storagesMap.Has(mountPath) return storagesMap.Has(utils.FixAndCleanPath(mountPath))
} }
func GetStorageByVirtualPath(virtualPath string) (driver.Driver, error) { func GetStorageByVirtualPath(virtualPath string) (driver.Driver, error) {
virtualPath = utils.FixAndCleanPath(virtualPath)
storageDriver, ok := storagesMap.Load(virtualPath) storageDriver, ok := storagesMap.Load(virtualPath)
if !ok { if !ok {
return nil, errors.Errorf("no mount path for an storage is: %s", virtualPath) 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 // then instantiate corresponding driver and save it in memory
func CreateStorage(ctx context.Context, storage model.Storage) (uint, error) { func CreateStorage(ctx context.Context, storage model.Storage) (uint, error) {
storage.Modified = time.Now() storage.Modified = time.Now()
storage.MountPath = utils.StandardizePath(storage.MountPath) storage.MountPath = utils.FixAndCleanPath(storage.MountPath)
var err error var err error
// check driver first // check driver first
driverName := storage.Driver 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 // LoadStorage load exist storage in db to memory
func LoadStorage(ctx context.Context, storage model.Storage) error { func LoadStorage(ctx context.Context, storage model.Storage) error {
storage.MountPath = utils.StandardizePath(storage.MountPath) storage.MountPath = utils.FixAndCleanPath(storage.MountPath)
// check driver first // check driver first
driverName := storage.Driver driverName := storage.Driver
driverNew, err := GetDriverNew(driverName) driverNew, err := GetDriverNew(driverName)
@ -158,7 +160,7 @@ func UpdateStorage(ctx context.Context, storage model.Storage) error {
return errors.Errorf("driver cannot be changed") return errors.Errorf("driver cannot be changed")
} }
storage.Modified = time.Now() storage.Modified = time.Now()
storage.MountPath = utils.StandardizePath(storage.MountPath) storage.MountPath = utils.FixAndCleanPath(storage.MountPath)
err = db.UpdateStorage(&storage) err = db.UpdateStorage(&storage)
if err != nil { if err != nil {
return errors.WithMessage(err, "failed update storage in database") 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 { func getStoragesByPath(path string) []driver.Driver {
storages := make([]driver.Driver, 0) storages := make([]driver.Driver, 0)
curSlashCount := 0 curSlashCount := 0
storagesMap.Range(func(key string, value driver.Driver) bool { storagesMap.Range(func(mountPath string, value driver.Driver) bool {
virtualPath := utils.GetActualVirtualPath(value.GetStorage().MountPath) virtualPath := utils.GetActualVirtualPath(mountPath)
if virtualPath == "/" { // is this path
virtualPath = "" if strings.HasPrefix(path, virtualPath) {
} slashCount := strings.Count(utils.PathAddSeparatorSuffix(virtualPath), "/")
// not this
if path != virtualPath && !strings.HasPrefix(path, virtualPath+"/") {
return true
}
slashCount := strings.Count(virtualPath, "/")
// not the longest match // not the longest match
if slashCount < curSlashCount {
return true
}
if slashCount > curSlashCount { if slashCount > curSlashCount {
storages = storages[:0] storages = storages[:0]
curSlashCount = slashCount curSlashCount = slashCount
} }
if slashCount == curSlashCount {
storages = append(storages, value) storages = append(storages, value)
}
}
return true return true
}) })
// make sure the order is the same for same input // 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 return storages[i].GetStorage().Order < storages[j].GetStorage().Order
}) })
prefix = utils.StandardizePath(prefix)
if prefix != "/" { prefix = utils.FixAndCleanPath(prefix)
prefix += "/" set := mapset.NewSet[string]()
}
set := make(map[string]interface{})
for _, v := range storages { for _, v := range storages {
// TODO should save a balanced storage virtualPath := utils.GetActualVirtualPath(v.GetStorage().MountPath)
// balance storage // Exclude prefix itself and non prefix
if utils.IsBalance(v.GetStorage().MountPath) { if len(prefix) >= len(virtualPath) || !strings.HasPrefix(virtualPath, prefix) {
continue
}
virtualPath := v.GetStorage().MountPath
if len(virtualPath) <= len(prefix) {
continue
}
// not prefixed with `prefix`
if !strings.HasPrefix(virtualPath, prefix) {
continue
}
name := strings.Split(strings.TrimPrefix(virtualPath, prefix), "/")[0]
if _, ok := set[name]; ok {
continue continue
} }
name := strings.SplitN(strings.TrimPrefix(virtualPath[len(prefix):], "/"), "/", 1)[0]
if set.Add(name) {
files = append(files, &model.Object{ files = append(files, &model.Object{
Name: name, Name: name,
Size: 0, Size: 0,
Modified: v.GetStorage().Modified, Modified: v.GetStorage().Modified,
IsFolder: true, IsFolder: true,
}) })
set[name] = nil }
} }
return files return files
} }
@ -315,7 +301,7 @@ var balanceMap generic_sync.MapOf[string, int]
// GetBalancedStorage get storage by path // GetBalancedStorage get storage by path
func GetBalancedStorage(path string) driver.Driver { func GetBalancedStorage(path string) driver.Driver {
path = utils.StandardizePath(path) path = utils.FixAndCleanPath(path)
storages := getStoragesByPath(path) storages := getStoragesByPath(path)
storageNum := len(storages) storageNum := len(storages)
switch storageNum { switch storageNum {
@ -325,15 +311,9 @@ func GetBalancedStorage(path string) driver.Driver {
return storages[0] return storages[0]
default: default:
virtualPath := utils.GetActualVirtualPath(storages[0].GetStorage().MountPath) virtualPath := utils.GetActualVirtualPath(storages[0].GetStorage().MountPath)
cur, ok := balanceMap.Load(virtualPath) i, _ := balanceMap.LoadOrStore(virtualPath, 0)
i := 0
if ok {
i = cur
i = (i + 1) % storageNum i = (i + 1) % storageNum
balanceMap.Store(virtualPath, i) balanceMap.Store(virtualPath, i)
} else {
balanceMap.Store(virtualPath, i)
}
return storages[i] return storages[i]
} }
} }

View File

@ -3,7 +3,6 @@ package utils
import ( import (
"fmt" "fmt"
"io" "io"
"io/ioutil"
"mime" "mime"
"os" "os"
"path" "path"
@ -43,7 +42,7 @@ func CopyFile(src, dst string) error {
// CopyDir Dir copies a whole directory recursively // CopyDir Dir copies a whole directory recursively
func CopyDir(src string, dst string) error { func CopyDir(src string, dst string) error {
var err error var err error
var fds []os.FileInfo var fds []os.DirEntry
var srcinfo os.FileInfo var srcinfo os.FileInfo
if srcinfo, err = os.Stat(src); err != nil { if srcinfo, err = os.Stat(src); err != nil {
@ -52,7 +51,7 @@ func CopyDir(src string, dst string) error {
if err = os.MkdirAll(dst, srcinfo.Mode()); err != nil { if err = os.MkdirAll(dst, srcinfo.Mode()); err != nil {
return err return err
} }
if fds, err = ioutil.ReadDir(src); err != nil { if fds, err = os.ReadDir(src); err != nil {
return err return err
} }
for _, fd := range fds { for _, fd := range fds {

View File

@ -3,33 +3,37 @@ package utils
import ( import (
"net/url" "net/url"
stdpath "path" stdpath "path"
"path/filepath"
"runtime"
"strings" "strings"
"github.com/alist-org/alist/v3/internal/errs"
) )
// StandardizePath convert path like '/' '/root' '/a/b' // FixAndCleanPath
func StandardizePath(path string) string { // The upper layer of the root directory is still the root directory.
path = strings.TrimSuffix(path, "/") // So ".." And "." will be cleared
// abs path // for example
if filepath.IsAbs(path) && runtime.GOOS == "windows" { // 1. ".." or "." => "/"
return path // 2. "../..." or "./..." => "/..."
} // 3. "../.x." or "./.x." => "/.x."
// relative path with prefix '..' // 4. "x//\\y" = > "/z/x"
if strings.HasPrefix(path, ".") { func FixAndCleanPath(path string) string {
return path path = strings.ReplaceAll(path, "\\", "/")
}
if !strings.HasPrefix(path, "/") { if !strings.HasPrefix(path, "/") {
path = "/" + path path = "/" + path
} }
return stdpath.Clean(path)
}
// PathAddSeparatorSuffix Add path '/' suffix
// for example /root => /root/
func PathAddSeparatorSuffix(path string) string {
if !strings.HasSuffix(path, "/") {
path = path + "/"
}
return path return path
} }
// PathEqual judge path is equal // PathEqual judge path is equal
func PathEqual(path1, path2 string) bool { func PathEqual(path1, path2 string) bool {
return StandardizePath(path1) == StandardizePath(path2) return FixAndCleanPath(path1) == FixAndCleanPath(path2)
} }
func Ext(path string) string { func Ext(path string) string {
@ -64,8 +68,5 @@ func EncodePath(path string, all ...bool) string {
} }
func JoinBasePath(basePath, reqPath string) (string, error) { func JoinBasePath(basePath, reqPath string) (string, error) {
if strings.HasSuffix(reqPath, "..") || strings.Contains(reqPath, "../") { return stdpath.Join(FixAndCleanPath(basePath), FixAndCleanPath(reqPath)), nil
return "", errs.RelativePath
}
return stdpath.Join(basePath, reqPath), nil
} }

View File

@ -5,3 +5,18 @@ import "testing"
func TestEncodePath(t *testing.T) { func TestEncodePath(t *testing.T) {
t.Log(EncodePath("http://localhost:5244/d/123#.png")) t.Log(EncodePath("http://localhost:5244/d/123#.png"))
} }
func TestFixAndCleanPath(t *testing.T) {
datas := map[string]string{
"": "/",
".././": "/",
"../../.../": "/...",
"x//\\y/": "/x/y",
".././.x/.y/.//..x../..y..": "/.x/.y/..x../..y..",
}
for key, value := range datas {
if FixAndCleanPath(key) != value {
t.Logf("raw %s fix fail", key)
}
}
}

View File

@ -44,7 +44,7 @@ func CreateMeta(c *gin.Context) {
common.ErrorStrResp(c, fmt.Sprintf("%s is illegal: %s", r, err.Error()), 400) common.ErrorStrResp(c, fmt.Sprintf("%s is illegal: %s", r, err.Error()), 400)
return return
} }
req.Path = utils.StandardizePath(req.Path) req.Path = utils.FixAndCleanPath(req.Path)
if err := db.CreateMeta(&req); err != nil { if err := db.CreateMeta(&req); err != nil {
common.ErrorResp(c, err, 500, true) common.ErrorResp(c, err, 500, true)
} else { } else {
@ -63,7 +63,7 @@ func UpdateMeta(c *gin.Context) {
common.ErrorStrResp(c, fmt.Sprintf("%s is illegal: %s", r, err.Error()), 400) common.ErrorStrResp(c, fmt.Sprintf("%s is illegal: %s", r, err.Error()), 400)
return return
} }
req.Path = utils.StandardizePath(req.Path) req.Path = utils.FixAndCleanPath(req.Path)
if err := db.UpdateMeta(&req); err != nil { if err := db.UpdateMeta(&req); err != nil {
common.ErrorResp(c, err, 500, true) common.ErrorResp(c, err, 500, true)
} else { } else {

View File

@ -42,7 +42,7 @@ func Down(c *gin.Context) {
// TODO: implement // TODO: implement
// path maybe contains # ? etc. // path maybe contains # ? etc.
func parsePath(path string) string { func parsePath(path string) string {
return utils.StandardizePath(path) return utils.FixAndCleanPath(path)
} }
func needSign(meta *model.Meta, path string) bool { func needSign(meta *model.Meta, path string) bool {

View File

@ -31,7 +31,7 @@ func getSiteConfig() SiteConfig {
siteConfig.BasePath = setting.GetStr(conf.BasePath) siteConfig.BasePath = setting.GetStr(conf.BasePath)
} }
if siteConfig.BasePath != "" { if siteConfig.BasePath != "" {
siteConfig.BasePath = utils.StandardizePath(siteConfig.BasePath) siteConfig.BasePath = utils.FixAndCleanPath(siteConfig.BasePath)
} }
if siteConfig.Cdn == "" { if siteConfig.Cdn == "" {
siteConfig.Cdn = siteConfig.BasePath siteConfig.Cdn = siteConfig.BasePath