diff --git a/drivers/uss/driver.go b/drivers/uss/driver.go new file mode 100644 index 00000000..e40d4672 --- /dev/null +++ b/drivers/uss/driver.go @@ -0,0 +1,237 @@ +package uss + +import ( + "fmt" + "github.com/Xhofe/alist/conf" + "github.com/Xhofe/alist/drivers/base" + "github.com/Xhofe/alist/model" + "github.com/Xhofe/alist/utils" + log "github.com/sirupsen/logrus" + "github.com/upyun/go-sdk/v3/upyun" + "net/url" + "path/filepath" + "strings" + "time" +) + +type USS struct { +} + +func (driver USS) Config() base.DriverConfig { + return base.DriverConfig{ + Name: "USS", + LocalSort: true, + } +} + +func (driver USS) Items() []base.Item { + return []base.Item{ + { + Name: "bucket", + Label: "Bucket", + Type: base.TypeString, + Required: true, + }, + { + Name: "endpoint", + Label: "Endpoint", + Type: base.TypeString, + Required: true, + }, + { + Name: "access_key", + Label: "Operator Name", + Type: base.TypeString, + Required: true, + }, + { + Name: "access_secret", + Label: "Operator Password", + Type: base.TypeString, + Required: true, + }, + { + Name: "root_folder", + Label: "root folder path", + Type: base.TypeString, + Required: false, + }, + { + Name: "custom_host", + Label: "Custom Host", + Type: base.TypeString, + }, + { + Name: "limit", + Label: "Sign url expire time(hours)", + Type: base.TypeNumber, + Default: "4", + Description: "default 4 hours", + }, + //{ + // Name: "zone", + // Label: "placeholder filename", + // Type: base.TypeString, + // Description: "default empty string", + //}, + } +} + +func (driver USS) Save(account *model.Account, old *model.Account) error { + if account == nil { + return nil + } + if account.Limit == 0 { + account.Limit = 4 + } + client, err := driver.NewUpYun(account) + if err != nil { + account.Status = err.Error() + } else { + clientsMap[account.Name] = client + account.Status = "work" + } + _ = model.SaveAccount(account) + return err +} + +func (driver USS) File(path string, account *model.Account) (*model.File, error) { + path = utils.ParsePath(path) + if path == "/" { + return &model.File{ + Id: account.RootFolder, + Name: account.Name, + Size: 0, + Type: conf.FOLDER, + Driver: driver.Config().Name, + UpdatedAt: account.UpdatedAt, + }, nil + } + dir, name := filepath.Split(path) + files, err := driver.Files(dir, account) + if err != nil { + return nil, err + } + for _, file := range files { + if file.Name == name { + return &file, nil + } + } + return nil, base.ErrPathNotFound +} + +func (driver USS) Files(path string, account *model.Account) ([]model.File, error) { + path = utils.ParsePath(path) + var files []model.File + cache, err := base.GetCache(path, account) + if err == nil { + files, _ = cache.([]model.File) + } else { + files, err = driver.List(path, account) + if err == nil && len(files) > 0 { + _ = base.SetCache(path, files, account) + } + } + return files, err +} + +func (driver USS) Link(args base.Args, account *model.Account) (*base.Link, error) { + key := driver.GetKey(args.Path, account, false) + host := account.CustomHost + if host == "" { + host = account.Endpoint + } + if strings.Contains(host, "://") { + host = "https://" + host + } + u := fmt.Sprintf("%s/%s", host, key) + downExp := time.Hour * time.Duration(account.Limit) + expireAt := time.Now().Add(downExp).Unix() + upd := url.QueryEscape(utils.Base(args.Path)) + signStr := strings.Join([]string{account.AccessSecret, fmt.Sprint(expireAt), fmt.Sprintf("/%s", key)}, "&") + upt := utils.GetMD5Encode(signStr)[12:20] + fmt.Sprint(expireAt) + link := fmt.Sprintf("%s?_upd=%s&_upt=%s", u, upd, upt) + return &base.Link{Url: link}, nil +} + +func (driver USS) Path(path string, account *model.Account) (*model.File, []model.File, error) { + path = utils.ParsePath(path) + log.Debugf("s3 path: %s", path) + file, err := driver.File(path, account) + if err != nil { + return nil, nil, err + } + if !file.IsDir() { + return file, nil, nil + } + files, err := driver.Files(path, account) + if err != nil { + return nil, nil, err + } + return nil, files, nil +} + +func (driver USS) Preview(path string, account *model.Account) (interface{}, error) { + return nil, base.ErrNotSupport +} + +func (driver USS) MakeDir(path string, account *model.Account) error { + client, err := driver.GetClient(account) + if err != nil { + return err + } + return client.Mkdir(driver.GetKey(path, account, true)) +} + +func (driver USS) Move(src string, dst string, account *model.Account) error { + client, err := driver.GetClient(account) + if err != nil { + return err + } + return client.Move(&upyun.MoveObjectConfig{ + SrcPath: driver.GetKey(src, account, false), + DestPath: driver.GetKey(dst, account, false), + }) +} + +func (driver USS) Rename(src string, dst string, account *model.Account) error { + return driver.Move(src, dst, account) +} + +func (driver USS) Copy(src string, dst string, account *model.Account) error { + client, err := driver.GetClient(account) + if err != nil { + return err + } + return client.Copy(&upyun.CopyObjectConfig{ + SrcPath: driver.GetKey(src, account, false), + DestPath: driver.GetKey(dst, account, false), + }) +} + +func (driver USS) Delete(path string, account *model.Account) error { + client, err := driver.GetClient(account) + if err != nil { + return err + } + return client.Delete(&upyun.DeleteObjectConfig{ + Path: driver.GetKey(path, account, false), + Async: false, + }) +} + +func (driver USS) Upload(file *model.FileStream, account *model.Account) error { + if file == nil { + return base.ErrEmptyFile + } + client, err := driver.GetClient(account) + if err != nil { + return err + } + return client.Put(&upyun.PutObjectConfig{ + Path: driver.GetKey(utils.Join(file.ParentPath, file.GetFileName()), account, false), + Reader: file, + }) +} + +var _ base.Driver = (*USS)(nil) diff --git a/drivers/uss/uss.go b/drivers/uss/uss.go new file mode 100644 index 00000000..a9bbeaf4 --- /dev/null +++ b/drivers/uss/uss.go @@ -0,0 +1,82 @@ +package uss + +import ( + "errors" + "github.com/Xhofe/alist/conf" + "github.com/Xhofe/alist/drivers/base" + "github.com/Xhofe/alist/model" + "github.com/Xhofe/alist/utils" + "github.com/upyun/go-sdk/v3/upyun" + "path" + "strings" +) + +var clientsMap map[string]*upyun.UpYun + +func (driver USS) NewUpYun(account *model.Account) (*upyun.UpYun, error) { + return upyun.NewUpYun(&upyun.UpYunConfig{ + Bucket: account.Bucket, + Operator: account.AccessKey, + Password: account.AccessToken, + }), nil +} + +func (driver USS) GetClient(account *model.Account) (*upyun.UpYun, error) { + client, ok := clientsMap[account.Name] + if ok { + return client, nil + } + return nil, errors.New("can't get client") +} + +func (driver USS) List(prefix string, account *model.Account) ([]model.File, error) { + prefix = driver.GetKey(prefix, account, true) + client, err := driver.GetClient(account) + if err != nil { + return nil, err + } + objsChan := make(chan *upyun.FileInfo, 10) + defer close(objsChan) + go func() { + err = client.List(&upyun.GetObjectsConfig{ + Path: prefix, + ObjectsChan: objsChan, + MaxListObjects: 0, + MaxListLevel: 1, + }) + }() + if err != nil { + return nil, err + } + res := make([]model.File, 0) + for obj := range objsChan { + t := obj.Time + f := model.File{ + Name: obj.Name, + Size: obj.Size, + UpdatedAt: &t, + Driver: driver.Config().Name, + } + if obj.IsDir { + f.Type = conf.FOLDER + } else { + f.Type = utils.GetFileType(path.Ext(obj.Name)) + } + res = append(res, f) + } + return res, err +} + +func (driver USS) GetKey(path string, account *model.Account, dir bool) string { + path = utils.Join(account.RootFolder, path) + path = strings.TrimPrefix(path, "/") + if dir { + path += "/" + } + return path +} + +func init() { + clientsMap = make(map[string]*upyun.UpYun) + base.RegisterDriver(&USS{}) +} diff --git a/go.mod b/go.mod index 6687761f..0e5419b0 100644 --- a/go.mod +++ b/go.mod @@ -16,6 +16,7 @@ require ( github.com/robfig/cron/v3 v3.0.0 github.com/sirupsen/logrus v1.8.1 github.com/studio-b12/gowebdav v0.0.0-20211109083228-3f8721cd4b6f + github.com/upyun/go-sdk/v3 v3.0.2 golang.org/x/text v0.3.7 gorm.io/driver/mysql v1.2.3 gorm.io/driver/postgres v1.2.3 diff --git a/go.sum b/go.sum index eff4639b..9189710f 100644 --- a/go.sum +++ b/go.sum @@ -510,6 +510,8 @@ github.com/ugorji/go v1.2.6/go.mod h1:anCg0y61KIhDlPZmnH+so+RQbysYVyDko0IMgJv0Nn github.com/ugorji/go/codec v1.1.7/go.mod h1:Ax+UKWsSmolVDwsd+7N3ZtXu+yMGCf907BLYF3GoBXY= github.com/ugorji/go/codec v1.2.6 h1:7kbGefxLoDBuYXOms4yD7223OpNMMPNPZxXk5TvFcyQ= github.com/ugorji/go/codec v1.2.6/go.mod h1:V6TCNZ4PHqoHGFZuSG1W8nrCzzdgA2DozYxWFFpvxTw= +github.com/upyun/go-sdk/v3 v3.0.2 h1:Ke+iOipK5CT0xzMwsgJsi7faJV7ID4lAs+wrH1RH0dA= +github.com/upyun/go-sdk/v3 v3.0.2/go.mod h1:P/SnuuwhrIgAVRd/ZpzDWqCsBAf/oHg7UggbAxyZa0E= github.com/urfave/cli v1.20.0/go.mod h1:70zkFmudgCuE/ngEzBv17Jvp/497gISqfk5gWijbERA= github.com/urfave/cli v1.22.1/go.mod h1:Gos4lmkARVdJ6EkW0WaNv/tZAAMe9V7XWyB60NtXRu0= github.com/vmihailenco/msgpack v4.0.4+incompatible/go.mod h1:fy3FlTQTDXWkZ7Bh6AcGMlsjHatGryHQYUTf1ShIgkk=