From f551dc76d05733f316f021759703cafa7bd730a2 Mon Sep 17 00:00:00 2001 From: Noah Hsu Date: Tue, 30 Aug 2022 21:52:06 +0800 Subject: [PATCH] feat: add onedrive driver --- drivers/all.go | 1 + drivers/base/client.go | 26 ++++ drivers/base/types.go | 8 + drivers/local/driver.go | 2 - drivers/local/meta.go | 2 +- drivers/onedrive/driver.go | 157 ++++++++++++++++++++ drivers/onedrive/meta.go | 31 ++++ drivers/onedrive/types.go | 48 ++++++ drivers/onedrive/util.go | 217 ++++++++++++++++++++++++++++ drivers/virtual/driver.go | 4 - drivers/virtual/meta.go | 2 +- go.mod | 5 +- go.sum | 7 + internal/aria2/aria2_test.go | 2 +- internal/bootstrap/data/dev.go | 2 +- internal/driver/driver.go | 7 +- internal/errs/driver.go | 7 + internal/model/storage.go | 8 +- internal/operations/driver.go | 7 +- internal/operations/storage.go | 8 +- internal/operations/storage_test.go | 14 +- 21 files changed, 535 insertions(+), 30 deletions(-) create mode 100644 drivers/base/client.go create mode 100644 drivers/base/types.go create mode 100644 drivers/onedrive/driver.go create mode 100644 drivers/onedrive/meta.go create mode 100644 drivers/onedrive/types.go create mode 100644 drivers/onedrive/util.go create mode 100644 internal/errs/driver.go diff --git a/drivers/all.go b/drivers/all.go index f93a3ad4f53..ffb5a534f92 100644 --- a/drivers/all.go +++ b/drivers/all.go @@ -2,6 +2,7 @@ package drivers import ( _ "github.com/alist-org/alist/v3/drivers/local" + _ "github.com/alist-org/alist/v3/drivers/onedrive" _ "github.com/alist-org/alist/v3/drivers/virtual" ) diff --git a/drivers/base/client.go b/drivers/base/client.go new file mode 100644 index 00000000000..0c1c2e9b45e --- /dev/null +++ b/drivers/base/client.go @@ -0,0 +1,26 @@ +package base + +import ( + "net/http" + "time" + + "github.com/go-resty/resty/v2" +) + +var NoRedirectClient *resty.Client +var RestyClient = resty.New() +var HttpClient = &http.Client{} +var UserAgent = "Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/87.0.4280.88 Safari/537.36" +var DefaultTimeout = time.Second * 10 + +func init() { + NoRedirectClient = resty.New().SetRedirectPolicy( + resty.RedirectPolicyFunc(func(req *http.Request, via []*http.Request) error { + return http.ErrUseLastResponse + }), + ) + NoRedirectClient.SetHeader("user-agent", UserAgent) + RestyClient.SetHeader("user-agent", UserAgent) + RestyClient.SetRetryCount(3) + RestyClient.SetTimeout(DefaultTimeout) +} diff --git a/drivers/base/types.go b/drivers/base/types.go new file mode 100644 index 00000000000..d3ad3a60f87 --- /dev/null +++ b/drivers/base/types.go @@ -0,0 +1,8 @@ +package base + +type Json map[string]interface{} + +type TokenResp struct { + AccessToken string `json:"access_token"` + RefreshToken string `json:"refresh_token"` +} diff --git a/drivers/local/driver.go b/drivers/local/driver.go index 9aabbb3bd31..32a1941cb7b 100644 --- a/drivers/local/driver.go +++ b/drivers/local/driver.go @@ -40,7 +40,6 @@ func (d *Local) Init(ctx context.Context, storage model.Storage) error { } if !utils.Exists(d.RootFolder) { err = errors.Errorf("root folder %s not exists", d.RootFolder) - d.SetStatus(err.Error()) } else { if !filepath.IsAbs(d.RootFolder) { d.RootFolder, err = filepath.Abs(d.RootFolder) @@ -48,7 +47,6 @@ func (d *Local) Init(ctx context.Context, storage model.Storage) error { return errors.Wrap(err, "error while get abs path") } } - d.SetStatus("OK") } operations.MustSaveDriverStorage(d) return err diff --git a/drivers/local/meta.go b/drivers/local/meta.go index 4ab3bd58951..0935ec83763 100644 --- a/drivers/local/meta.go +++ b/drivers/local/meta.go @@ -11,7 +11,7 @@ type Addition struct { } var config = driver.Config{ - Name: "Local", + Name: "local", OnlyLocal: true, LocalSort: true, NoCache: true, diff --git a/drivers/onedrive/driver.go b/drivers/onedrive/driver.go new file mode 100644 index 00000000000..113f9f83795 --- /dev/null +++ b/drivers/onedrive/driver.go @@ -0,0 +1,157 @@ +package onedrive + +import ( + "context" + "net/http" + stdpath "path" + + "github.com/alist-org/alist/v3/drivers/base" + "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/pkg/utils" + "github.com/go-resty/resty/v2" + "github.com/pkg/errors" +) + +type Onedrive struct { + model.Storage + Addition + AccessToken string +} + +func (d *Onedrive) Config() driver.Config { + return config +} + +func (d *Onedrive) GetAddition() driver.Additional { + return d.Addition +} + +func (d *Onedrive) Init(ctx context.Context, storage model.Storage) error { + d.Storage = storage + err := utils.Json.UnmarshalFromString(d.Storage.Addition, &d.Addition) + if err != nil { + return errors.Wrap(err, "error while unmarshal addition") + } + return d.refreshToken() +} + +func (d *Onedrive) Drop(ctx context.Context) error { + return nil +} + +func (d *Onedrive) List(ctx context.Context, dir model.Obj, args model.ListArgs) ([]model.Obj, error) { + files, err := d.GetFiles(dir.GetID()) + if err != nil { + return nil, err + } + objs := make([]model.Obj, len(files)) + for i := 0; i < len(files); i++ { + objs[i] = fileToObj(files[i]) + } + return objs, nil +} + +func (d *Onedrive) Link(ctx context.Context, file model.Obj, args model.LinkArgs) (*model.Link, error) { + f, err := d.GetFile(file.GetID()) + if err != nil { + return nil, err + } + if f.File == nil { + return nil, errs.NotFile + } + return &model.Link{ + URL: f.Url, + }, nil +} + +func (d *Onedrive) MakeDir(ctx context.Context, parentDir model.Obj, dirName string) error { + url := d.GetMetaUrl(false, parentDir.GetID()) + "/children" + data := base.Json{ + "name": dirName, + "folder": base.Json{}, + "@microsoft.graph.conflictBehavior": "rename", + } + _, err := d.Request(url, http.MethodPost, func(req *resty.Request) { + req.SetBody(data) + }, nil) + return err +} + +func (d *Onedrive) Move(ctx context.Context, srcObj, dstDir model.Obj) error { + dst, err := d.GetFile(dstDir.GetID()) + if err != nil { + return err + } + data := base.Json{ + "parentReference": base.Json{ + "id": dst.Id, + }, + "name": srcObj.GetName(), + } + url := d.GetMetaUrl(false, srcObj.GetID()) + _, err = d.Request(url, http.MethodPatch, func(req *resty.Request) { + req.SetBody(data) + }, nil) + return err +} + +func (d *Onedrive) Rename(ctx context.Context, srcObj model.Obj, newName string) error { + dstDir, err := d.GetFile(stdpath.Dir(srcObj.GetID())) + if err != nil { + return err + } + data := base.Json{ + "parentReference": base.Json{ + "id": dstDir.Id, + }, + "name": newName, + } + url := d.GetMetaUrl(false, srcObj.GetID()) + _, err = d.Request(url, http.MethodPatch, func(req *resty.Request) { + req.SetBody(data) + }, nil) + return err +} + +func (d *Onedrive) Copy(ctx context.Context, srcObj, dstDir model.Obj) error { + dst, err := d.GetFile(dstDir.GetID()) + if err != nil { + return err + } + data := base.Json{ + "parentReference": base.Json{ + "driveId": dst.ParentReference.DriveId, + "id": dst.Id, + }, + "name": srcObj.GetName(), + } + url := d.GetMetaUrl(false, srcObj.GetID()) + "/copy" + _, err = d.Request(url, http.MethodPost, func(req *resty.Request) { + req.SetBody(data) + }, nil) + return err +} + +func (d *Onedrive) Remove(ctx context.Context, obj model.Obj) error { + url := d.GetMetaUrl(false, obj.GetID()) + _, err := d.Request(url, http.MethodDelete, nil, nil) + return err +} + +func (d *Onedrive) Put(ctx context.Context, dstDir model.Obj, stream model.FileStreamer, up driver.UpdateProgress) error { + var err error + if stream.GetSize() <= 4*1024*1024 { + err = d.upSmall(dstDir, stream) + } else { + err = d.upBig(ctx, dstDir, stream, up) + } + return err +} + +func (d *Onedrive) Other(ctx context.Context, args model.OtherArgs) (interface{}, error) { + return nil, errs.NotSupport +} + +var _ driver.Driver = (*Onedrive)(nil) diff --git a/drivers/onedrive/meta.go b/drivers/onedrive/meta.go new file mode 100644 index 00000000000..7e4abe4aa43 --- /dev/null +++ b/drivers/onedrive/meta.go @@ -0,0 +1,31 @@ +package onedrive + +import ( + "github.com/alist-org/alist/v3/internal/driver" + "github.com/alist-org/alist/v3/internal/operations" +) + +type Addition struct { + driver.RootFolderPath + Region string `json:"region" type:"select" required:"true" options:"global,cn,us,de"` + IsSharepoint bool `json:"is_sharepoint"` + ClientId string `json:"client_id" required:"true"` + ClientSecret string `json:"client_secret" required:"true"` + RedirectUri string `json:"redirect_uri" required:"true" default:"https://tool.nn.ci/onedrive/callback"` + RefreshToken string `json:"refresh_token" required:"true"` + SiteId string `json:"site_id"` +} + +var config = driver.Config{ + Name: "onedrive", + LocalSort: true, + DefaultRoot: "/", +} + +func New() driver.Driver { + return &Onedrive{} +} + +func init() { + operations.RegisterDriver(config, New) +} diff --git a/drivers/onedrive/types.go b/drivers/onedrive/types.go new file mode 100644 index 00000000000..111564cc43b --- /dev/null +++ b/drivers/onedrive/types.go @@ -0,0 +1,48 @@ +package onedrive + +import ( + "time" + + "github.com/alist-org/alist/v3/internal/model" +) + +type File struct { + Id string `json:"id"` + Name string `json:"name"` + Size int64 `json:"size"` + LastModifiedDateTime time.Time `json:"lastModifiedDateTime"` + Url string `json:"@microsoft.graph.downloadUrl"` + File *struct { + MimeType string `json:"mimeType"` + } `json:"file"` + Thumbnails []struct { + Medium struct { + Url string `json:"url"` + } `json:"medium"` + } `json:"thumbnails"` + ParentReference struct { + DriveId string `json:"driveId"` + } `json:"parentReference"` +} + +func fileToObj(f File) *model.ObjectThumbnail { + thumb := "" + if len(f.Thumbnails) > 0 { + thumb = f.Thumbnails[0].Medium.Url + } + return &model.ObjectThumbnail{ + Object: model.Object{ + //ID: f.Id, + Name: f.Name, + Size: f.Size, + Modified: f.LastModifiedDateTime, + IsFolder: f.File == nil, + }, + Thumbnail: model.Thumbnail{Thumbnail: thumb}, + } +} + +type Files struct { + Value []File `json:"value"` + NextLink string `json:"@odata.nextLink"` +} diff --git a/drivers/onedrive/util.go b/drivers/onedrive/util.go new file mode 100644 index 00000000000..5411eedab64 --- /dev/null +++ b/drivers/onedrive/util.go @@ -0,0 +1,217 @@ +package onedrive + +import ( + "bytes" + "context" + "fmt" + "io" + "net/http" + stdpath "path" + "strconv" + + "github.com/alist-org/alist/v3/drivers/base" + "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/operations" + "github.com/alist-org/alist/v3/pkg/utils" + "github.com/go-resty/resty/v2" + jsoniter "github.com/json-iterator/go" + "github.com/pkg/errors" + log "github.com/sirupsen/logrus" +) + +type Host struct { + Oauth string + Api string +} + +var onedriveHostMap = map[string]Host{ + "global": { + Oauth: "https://login.microsoftonline.com", + Api: "https://graph.microsoft.com", + }, + "cn": { + Oauth: "https://login.chinacloudapi.cn", + Api: "https://microsoftgraph.chinacloudapi.cn", + }, + "us": { + Oauth: "https://login.microsoftonline.us", + Api: "https://graph.microsoft.us", + }, + "de": { + Oauth: "https://login.microsoftonline.de", + Api: "https://graph.microsoft.de", + }, +} + +func (d *Onedrive) GetMetaUrl(auth bool, path string) string { + host, _ := onedriveHostMap[d.Region] + if auth { + return host.Oauth + } + if d.IsSharepoint { + if path == "/" || path == "\\" { + return fmt.Sprintf("%s/v1.0/sites/%s/drive/root", host.Api, d.SiteId) + } else { + return fmt.Sprintf("%s/v1.0/sites/%s/drive/root:%s:", host.Api, d.SiteId, path) + } + } else { + if path == "/" || path == "\\" { + return fmt.Sprintf("%s/v1.0/me/drive/root", host.Api) + } else { + return fmt.Sprintf("%s/v1.0/me/drive/root:%s:", host.Api, path) + } + } +} + +func (d *Onedrive) refreshToken() error { + var err error + for i := 0; i < 3; i++ { + err = d._refreshToken() + if err == nil { + break + } + } + return err +} + +type TokenErr struct { + Error string `json:"error"` + ErrorDescription string `json:"error_description"` +} + +func (d *Onedrive) _refreshToken() error { + url := d.GetMetaUrl(true, "") + "/common/oauth2/v2.0/token" + var resp base.TokenResp + var e TokenErr + _, err := base.RestyClient.R().SetResult(&resp).SetError(&e).SetFormData(map[string]string{ + "grant_type": "refresh_token", + "client_id": d.ClientId, + "client_secret": d.ClientSecret, + "redirect_uri": d.RedirectUri, + "refresh_token": d.RefreshToken, + }).Post(url) + if err != nil { + return err + } + if e.Error != "" { + return fmt.Errorf("%s", e.ErrorDescription) + } + if resp.RefreshToken == "" { + return errs.EmptyToken + } + d.RefreshToken, d.AccessToken = resp.RefreshToken, resp.AccessToken + operations.MustSaveDriverStorage(d) + return nil +} + +type RespErr struct { + Error struct { + Code string `json:"code"` + Message string `json:"message"` + } `json:"error"` +} + +func (d *Onedrive) Request(url string, method string, callback func(*resty.Request), resp interface{}) ([]byte, error) { + req := base.RestyClient.R() + req.SetHeader("Authorization", "Bearer "+d.AccessToken) + if callback != nil { + callback(req) + } + if resp != nil { + req.SetResult(resp) + } + var e RespErr + req.SetError(&e) + res, err := req.Execute(method, url) + if err != nil { + return nil, errors.WithStack(err) + } + if e.Error.Code != "" { + if e.Error.Code == "InvalidAuthenticationToken" { + err = d.refreshToken() + if err != nil { + return nil, err + } + return d.Request(url, method, callback, resp) + } + return nil, errors.New(e.Error.Message) + } + return res.Body(), nil +} + +func (d *Onedrive) GetFiles(path string) ([]File, error) { + var res []File + nextLink := d.GetMetaUrl(false, path) + "/children?$expand=thumbnails" + for nextLink != "" { + var files Files + _, err := d.Request(nextLink, http.MethodGet, nil, &files) + if err != nil { + return nil, err + } + res = append(res, files.Value...) + nextLink = files.NextLink + } + return res, nil +} + +func (d *Onedrive) GetFile(path string) (*File, error) { + var file File + u := d.GetMetaUrl(false, path) + _, err := d.Request(u, http.MethodGet, nil, &file) + return &file, err +} + +func (d *Onedrive) upSmall(dstDir model.Obj, stream model.FileStreamer) error { + url := d.GetMetaUrl(false, stdpath.Join(dstDir.GetID(), stream.GetName())) + "/content" + data, err := io.ReadAll(stream) + if err != nil { + return err + } + _, err = d.Request(url, http.MethodPut, func(req *resty.Request) { + req.SetBody(data) + }, nil) + return err +} + +func (d *Onedrive) upBig(ctx context.Context, dstDir model.Obj, stream model.FileStreamer, up driver.UpdateProgress) error { + url := d.GetMetaUrl(false, stdpath.Join(dstDir.GetID(), stream.GetName())) + "/createUploadSession" + res, err := d.Request(url, http.MethodPost, nil, nil) + if err != nil { + return err + } + uploadUrl := jsoniter.Get(res, "uploadUrl").ToString() + var finish int64 = 0 + const DEFAULT = 4 * 1024 * 1024 + for finish < stream.GetSize() { + if utils.IsCanceled(ctx) { + return ctx.Err() + } + log.Debugf("upload: %d", finish) + var byteSize int64 = DEFAULT + left := stream.GetSize() - finish + if left < DEFAULT { + byteSize = left + } + byteData := make([]byte, byteSize) + n, err := io.ReadFull(stream, byteData) + log.Debug(err, n) + if err != nil { + return err + } + req, err := http.NewRequest("PUT", uploadUrl, bytes.NewBuffer(byteData)) + req.Header.Set("Content-Length", strconv.Itoa(int(byteSize))) + req.Header.Set("Content-Range", fmt.Sprintf("bytes %d-%d/%d", finish, finish+byteSize-1, stream.GetSize())) + finish += byteSize + res, err := base.HttpClient.Do(req) + if res.StatusCode != 201 && res.StatusCode != 202 { + data, _ := io.ReadAll(res.Body) + res.Body.Close() + return errors.New(string(data)) + } + res.Body.Close() + up(int(finish / stream.GetSize())) + } + return nil +} diff --git a/drivers/virtual/driver.go b/drivers/virtual/driver.go index a59cde65420..58e220efd6d 100644 --- a/drivers/virtual/driver.go +++ b/drivers/virtual/driver.go @@ -35,10 +35,6 @@ func (d *Virtual) Drop(ctx context.Context) error { return nil } -func (d *Virtual) GetStorage() model.Storage { - return d.Storage -} - func (d *Virtual) GetAddition() driver.Additional { return d.Addition } diff --git a/drivers/virtual/meta.go b/drivers/virtual/meta.go index 83b00eb5794..18ea998a90c 100644 --- a/drivers/virtual/meta.go +++ b/drivers/virtual/meta.go @@ -14,7 +14,7 @@ type Addition struct { } var config = driver.Config{ - Name: "Virtual", + Name: "virtual", OnlyLocal: true, LocalSort: true, NeedMs: true, diff --git a/go.mod b/go.mod index f018183fd7d..ee1431c0570 100644 --- a/go.mod +++ b/go.mod @@ -30,6 +30,7 @@ require ( github.com/go-playground/locales v0.14.0 // indirect github.com/go-playground/universal-translator v0.18.0 // indirect github.com/go-playground/validator/v10 v10.11.0 // indirect + github.com/go-resty/resty/v2 v2.7.0 // indirect github.com/go-sql-driver/mysql v1.6.0 // indirect github.com/goccy/go-json v0.9.7 // indirect github.com/inconshreveable/mousetrap v1.0.0 // indirect @@ -53,8 +54,8 @@ require ( github.com/ugorji/go/codec v1.2.7 // indirect golang.org/x/crypto v0.0.0-20220622213112-05595931fe9d // indirect golang.org/x/image v0.0.0-20220722155232-062f8c9fd539 // indirect - golang.org/x/net v0.0.0-20220531201128-c960675eff93 // indirect - golang.org/x/sys v0.0.0-20220520151302-bc2c85ada10a // indirect + golang.org/x/net v0.0.0-20220826154423-83b083e8dc8b // indirect + golang.org/x/sys v0.0.0-20220728004956-3c1f35247d10 // indirect golang.org/x/text v0.3.7 // indirect google.golang.org/protobuf v1.28.0 // indirect gopkg.in/natefinch/lumberjack.v2 v2.0.0 // indirect diff --git a/go.sum b/go.sum index 4798a92aaa3..a7cb39b3a19 100644 --- a/go.sum +++ b/go.sum @@ -39,6 +39,8 @@ github.com/go-playground/universal-translator v0.18.0 h1:82dyy6p4OuJq4/CByFNOn/j github.com/go-playground/universal-translator v0.18.0/go.mod h1:UvRDBj+xPUEGrFYl+lu/H90nyDXpg0fqeB/AQUGNTVA= github.com/go-playground/validator/v10 v10.11.0 h1:0W+xRM511GY47Yy3bZUbJVitCNg2BOGlCyvTqsp/xIw= github.com/go-playground/validator/v10 v10.11.0/go.mod h1:i+3WkQ1FvaUjjxh1kSvIA4dMGDBiPU55YFDl0WbKdWU= +github.com/go-resty/resty/v2 v2.7.0 h1:me+K9p3uhSmXtrBZ4k9jcEAfJmuC8IivWHwaLZwPrFY= +github.com/go-resty/resty/v2 v2.7.0/go.mod h1:9PWDzw47qPphMRFfhsyk0NnSgvluHcljSMVIq3w7q0I= github.com/go-sql-driver/mysql v1.6.0 h1:BCTh4TKNUYmOmMUcQ3IipzF5prigylS7XXjEkfCHuOE= github.com/go-sql-driver/mysql v1.6.0/go.mod h1:DCzpHaOWr8IXmIStZouvnhqoel9Qv2LBy8hT2VhHyBg= github.com/go-stack/stack v1.8.0/go.mod h1:v0f6uXyyMGvRgIKkXu+yp6POWl0qKG85gN/melR3HDY= @@ -238,9 +240,12 @@ golang.org/x/net v0.0.0-20190404232315-eb5bcb51f2a3/go.mod h1:t9HGtf8HONx5eT2rtn golang.org/x/net v0.0.0-20190620200207-3b0461eec859/go.mod h1:z5CRVTTTmAJ677TzLLGU+0bjPO0LkuOLi4/5GtJWs/s= golang.org/x/net v0.0.0-20190813141303-74dc4d7220e7/go.mod h1:z5CRVTTTmAJ677TzLLGU+0bjPO0LkuOLi4/5GtJWs/s= golang.org/x/net v0.0.0-20210226172049-e18ecbb05110/go.mod h1:m0MpNAwzfU5UDzcl9v0D8zg8gWTRqZa9RBIspLL5mdg= +golang.org/x/net v0.0.0-20211029224645-99673261e6eb/go.mod h1:9nx3DQGgdP8bBQD5qxJ1jj9UTztislL4KSBs9R2vV5Y= golang.org/x/net v0.0.0-20211112202133-69e39bad7dc2/go.mod h1:9nx3DQGgdP8bBQD5qxJ1jj9UTztislL4KSBs9R2vV5Y= golang.org/x/net v0.0.0-20220531201128-c960675eff93 h1:MYimHLfoXEpOhqd/zgoA/uoXzHB86AEky4LAx5ij9xA= golang.org/x/net v0.0.0-20220531201128-c960675eff93/go.mod h1:XRhObCWvk6IyKnWLug+ECip1KBveYUHfp+8e9klMJ9c= +golang.org/x/net v0.0.0-20220826154423-83b083e8dc8b h1:ZmngSVLe/wycRns9MKikG9OWIEjGcGAkacif7oYQaUY= +golang.org/x/net v0.0.0-20220826154423-83b083e8dc8b/go.mod h1:YDH+HFinaLZZlnHAfSS6ZXJJ9M9t4Dl22yv3iI2vPwk= golang.org/x/sync v0.0.0-20190423024810-112230192c58/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM= golang.org/x/sys v0.0.0-20180905080454-ebe1bf3edb33/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY= golang.org/x/sys v0.0.0-20190215142949-d0b11bdaac8a/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY= @@ -259,6 +264,8 @@ golang.org/x/sys v0.0.0-20210630005230-0f9fa26af87c/go.mod h1:oPkhp1MJrh7nUepCBc golang.org/x/sys v0.0.0-20210806184541-e5e7981a1069/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= golang.org/x/sys v0.0.0-20220520151302-bc2c85ada10a h1:dGzPydgVsqGcTRVwiLJ1jVbufYwmzD3LfVPLKsKg+0k= golang.org/x/sys v0.0.0-20220520151302-bc2c85ada10a/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= +golang.org/x/sys v0.0.0-20220728004956-3c1f35247d10 h1:WIoqL4EROvwiPdUtaip4VcDdpZ4kha7wBWZrbVKCIZg= +golang.org/x/sys v0.0.0-20220728004956-3c1f35247d10/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= golang.org/x/term v0.0.0-20201117132131-f5c789dd3221/go.mod h1:Nr5EML6q2oocZ2LXRh80K7BxOlk5/8JxuGnuhpl+muw= golang.org/x/term v0.0.0-20201126162022-7de9c90e9dd1/go.mod h1:bj7SfCRtBDWHUb9snDiAeCFNEtKQo2Wmx5Cou7ajbmo= golang.org/x/text v0.3.0/go.mod h1:NqM8EUOU14njkJ3fqMW+pc6Ldnwhi/IjpwHt7yyuwOQ= diff --git a/internal/aria2/aria2_test.go b/internal/aria2/aria2_test.go index 51499dc7651..e52896cf678 100644 --- a/internal/aria2/aria2_test.go +++ b/internal/aria2/aria2_test.go @@ -43,7 +43,7 @@ func TestDown(t *testing.T) { ID: 0, MountPath: "/", Index: 0, - Driver: "Local", + Driver: "local", Status: "", Addition: `{"root_folder":"../../data"}`, Remark: "", diff --git a/internal/bootstrap/data/dev.go b/internal/bootstrap/data/dev.go index cdb9afd53fd..1608d6b678d 100644 --- a/internal/bootstrap/data/dev.go +++ b/internal/bootstrap/data/dev.go @@ -15,7 +15,7 @@ func initDevData() { err := operations.CreateStorage(context.Background(), model.Storage{ MountPath: "/", Index: 0, - Driver: "Local", + Driver: "local", Status: "", Addition: `{"root_folder":"."}`, }) diff --git a/internal/driver/driver.go b/internal/driver/driver.go index b91c91f69cb..58547412757 100644 --- a/internal/driver/driver.go +++ b/internal/driver/driver.go @@ -15,13 +15,14 @@ type Driver interface { type Meta interface { Config() Config + // GetStorage just get raw storage, no need to implement, because model.Storage have implemented + GetStorage() *model.Storage + // GetAddition Additional can't be modified externally, so needn't return pointer + GetAddition() Additional // Init If already initialized, drop first // need to unmarshal string to addition first Init(ctx context.Context, storage model.Storage) error Drop(ctx context.Context) error - // GetStorage just get raw storage - GetStorage() model.Storage - GetAddition() Additional } type Other interface { diff --git a/internal/errs/driver.go b/internal/errs/driver.go new file mode 100644 index 00000000000..4b6b5cac48e --- /dev/null +++ b/internal/errs/driver.go @@ -0,0 +1,7 @@ +package errs + +import "errors" + +var ( + EmptyToken = errors.New("empty token") +) diff --git a/internal/model/storage.go b/internal/model/storage.go index f70d62f85bc..4a4068e8fad 100644 --- a/internal/model/storage.go +++ b/internal/model/storage.go @@ -29,12 +29,12 @@ type Proxy struct { DownProxyUrl string `json:"down_proxy_url"` } -func (a *Storage) GetStorage() Storage { - return *a +func (s *Storage) GetStorage() *Storage { + return s } -func (a *Storage) SetStatus(status string) { - a.Status = status +func (s *Storage) SetStatus(status string) { + s.Status = status } func (p Proxy) Webdav302() bool { diff --git a/internal/operations/driver.go b/internal/operations/driver.go index b85e8861338..bd059b45382 100644 --- a/internal/operations/driver.go +++ b/internal/operations/driver.go @@ -128,12 +128,13 @@ func getAdditionalItems(t reflect.Type, defaultRoot string) []driver.Item { continue } tag := field.Tag - ignore, ok := tag.Lookup("ignore") - if ok && ignore == "true" { + ignore, ok1 := tag.Lookup("ignore") + name, ok2 := tag.Lookup("json") + if (ok1 && ignore == "true") || !ok2 { continue } item := driver.Item{ - Name: tag.Get("json"), + Name: name, Type: strings.ToLower(field.Type.Name()), Default: tag.Get("default"), Options: tag.Get("options"), diff --git a/internal/operations/storage.go b/internal/operations/storage.go index a83fa6e0e7c..922c9c5f3dd 100644 --- a/internal/operations/storage.go +++ b/internal/operations/storage.go @@ -2,6 +2,7 @@ package operations import ( "context" + "fmt" "sort" "strings" "time" @@ -49,7 +50,12 @@ func CreateStorage(ctx context.Context, storage model.Storage) error { // already has an id err = storageDriver.Init(ctx, storage) if err != nil { + storageDriver.GetStorage().SetStatus(fmt.Sprintf("%+v", err.Error())) + MustSaveDriverStorage(storageDriver) return errors.WithMessage(err, "failed init storage but storage is already created") + } else { + storageDriver.GetStorage().SetStatus("work") + MustSaveDriverStorage(storageDriver) } log.Debugf("storage %+v is created", storageDriver) storagesMap.Store(storage.MountPath, storageDriver) @@ -204,7 +210,7 @@ func saveDriverStorage(driver driver.Driver) error { return errors.Wrap(err, "error while marshal addition") } storage.Addition = string(bytes) - err = db.UpdateStorage(&storage) + err = db.UpdateStorage(storage) if err != nil { return errors.WithMessage(err, "failed update storage in database") } diff --git a/internal/operations/storage_test.go b/internal/operations/storage_test.go index 7521bc6750f..2d4dbbfe28b 100644 --- a/internal/operations/storage_test.go +++ b/internal/operations/storage_test.go @@ -25,8 +25,8 @@ func TestCreateStorage(t *testing.T) { storage model.Storage isErr bool }{ - {storage: model.Storage{Driver: "Local", MountPath: "/local", Addition: `{"root_folder":"."}`}, isErr: false}, - {storage: model.Storage{Driver: "Local", MountPath: "/local", Addition: `{"root_folder":"."}`}, isErr: true}, + {storage: model.Storage{Driver: "local", MountPath: "/local", Addition: `{"root_folder":"."}`}, isErr: false}, + {storage: model.Storage{Driver: "local", MountPath: "/local", Addition: `{"root_folder":"."}`}, isErr: true}, {storage: model.Storage{Driver: "None", MountPath: "/none", Addition: `{"root_folder":"."}`}, isErr: true}, } for _, storage := range storages { @@ -70,11 +70,11 @@ func TestGetBalancedStorage(t *testing.T) { func setupStorages(t *testing.T) { var storages = []model.Storage{ - {Driver: "Local", MountPath: "/a/b", Index: 0, Addition: `{"root_folder":"."}`}, - {Driver: "Local", MountPath: "/a/c", Index: 1, Addition: `{"root_folder":"."}`}, - {Driver: "Local", MountPath: "/a/d", Index: 2, Addition: `{"root_folder":"."}`}, - {Driver: "Local", MountPath: "/a/d/e", Index: 3, Addition: `{"root_folder":"."}`}, - {Driver: "Local", MountPath: "/a/d/e.balance", Index: 4, Addition: `{"root_folder":"."}`}, + {Driver: "local", MountPath: "/a/b", Index: 0, Addition: `{"root_folder":"."}`}, + {Driver: "local", MountPath: "/a/c", Index: 1, Addition: `{"root_folder":"."}`}, + {Driver: "local", MountPath: "/a/d", Index: 2, Addition: `{"root_folder":"."}`}, + {Driver: "local", MountPath: "/a/d/e", Index: 3, Addition: `{"root_folder":"."}`}, + {Driver: "local", MountPath: "/a/d/e.balance", Index: 4, Addition: `{"root_folder":"."}`}, } for _, storage := range storages { err := operations.CreateStorage(context.Background(), storage)