diff --git a/bootstrap/setting.go b/bootstrap/setting.go index 11e067db..b734e9c6 100644 --- a/bootstrap/setting.go +++ b/bootstrap/setting.go @@ -204,12 +204,13 @@ func InitSettings() { Group: model.BACK, }, { - Key: "load type", - Value: "all", - Type: "select", - Values: "all,load more,auto load more,pagination", - Access: model.PUBLIC, - Group: model.FRONT, + Key: "load type", + Value: "all", + Type: "select", + Values: "all,load more,auto load more,pagination", + Description: "Not recommended to choose to auto load more, it has bugs now", + Access: model.PUBLIC, + Group: model.FRONT, }, { Key: "default page size", diff --git a/drivers/all.go b/drivers/all.go index 153534b6..7b58e6fe 100644 --- a/drivers/all.go +++ b/drivers/all.go @@ -14,6 +14,7 @@ import ( _ "github.com/Xhofe/alist/drivers/pikpak" _ "github.com/Xhofe/alist/drivers/s3" _ "github.com/Xhofe/alist/drivers/shandian" + _ "github.com/Xhofe/alist/drivers/teambition" _ "github.com/Xhofe/alist/drivers/webdav" log "github.com/sirupsen/logrus" "strings" diff --git a/drivers/teambition/driver.go b/drivers/teambition/driver.go new file mode 100644 index 00000000..cb454df1 --- /dev/null +++ b/drivers/teambition/driver.go @@ -0,0 +1,252 @@ +package teambition + +import ( + "github.com/Xhofe/alist/conf" + "github.com/Xhofe/alist/drivers/base" + "github.com/Xhofe/alist/model" + "github.com/Xhofe/alist/utils" + "github.com/gin-gonic/gin" + log "github.com/sirupsen/logrus" + "path/filepath" +) + +type Teambition struct{} + +func (driver Teambition) Config() base.DriverConfig { + return base.DriverConfig{ + Name: "Teambition", + } +} + +func (driver Teambition) Items() []base.Item { + return []base.Item{ + { + Name: "internal_type", + Label: "Teambition type", + Type: base.TypeSelect, + Required: true, + Values: "China,International", + }, + { + Name: "access_token", + Label: "Cookie", + Type: base.TypeString, + Description: "Unknown expiration time", + }, + { + Name: "zone", + Label: "Project id", + Type: base.TypeString, + }, + { + Name: "root_folder", + Label: "root folder file_id", + Type: base.TypeString, + }, + { + Name: "order_by", + Label: "order_by", + Type: base.TypeSelect, + Values: "fileName,fileSize,updated,created", + Required: true, + }, + { + Name: "order_direction", + Label: "order_direction", + Type: base.TypeSelect, + Values: "Asc,Desc", + Required: true, + }, + } +} + +func (driver Teambition) Save(account *model.Account, old *model.Account) error { + _, err := driver.Request("/api/v2/roles", base.Get, nil, nil, nil, nil, nil, account) + return err +} + +func (driver Teambition) 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 Teambition) 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 { + file, err := driver.File(path, account) + if err != nil { + return nil, err + } + files, err = driver.GetFiles(file.Id, account) + if err != nil { + return nil, err + } + if len(files) > 0 { + _ = base.SetCache(path, files, account) + } + } + return files, nil +} + +func (driver Teambition) Link(args base.Args, account *model.Account) (*base.Link, error) { + path := args.Path + file, err := driver.File(path, account) + if err != nil { + return nil, err + } + url := file.Url + res, err := base.NoRedirectClient.R().Get(url) + if res.StatusCode() == 302 { + url = res.Header().Get("location") + } + return &base.Link{Url: url}, nil +} + +func (driver Teambition) Path(path string, account *model.Account) (*model.File, []model.File, error) { + path = utils.ParsePath(path) + log.Debugf("teambition 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 Teambition) Proxy(c *gin.Context, account *model.Account) { + +} + +func (driver Teambition) Preview(path string, account *model.Account) (interface{}, error) { + return nil, base.ErrNotSupport +} + +func (driver Teambition) MakeDir(path string, account *model.Account) error { + parentFile, err := driver.File(utils.Dir(path), account) + if err != nil { + return err + } + data := base.Json{ + "objectType": "collection", + "_projectId": account.Zone, + "_creatorId": "", + "created": "", + "updated": "", + "title": utils.Base(path), + "color": "blue", + "description": "", + "workCount": 0, + "collectionType": "", + "recentWorks": []interface{}{}, + "_parentId": parentFile.Id, + "subCount": nil, + } + _, err = driver.Request("/api/collections", base.Post, nil, nil, nil, &data, nil, account) + return err +} + +func (driver Teambition) Move(src string, dst string, account *model.Account) error { + srcFile, err := driver.File(src, account) + if err != nil { + return err + } + dstParentFile, err := driver.File(utils.Dir(dst), account) + if err != nil { + return err + } + pre := "/api/works/" + if srcFile.IsDir() { + pre = "/api/collections/" + } + _, err = driver.Request(pre+srcFile.Id+"/move", base.Put, nil, nil, nil, &base.Json{ + "_parentId": dstParentFile.Id, + }, nil, account) + return err +} + +func (driver Teambition) Rename(src string, dst string, account *model.Account) error { + srcFile, err := driver.File(src, account) + if err != nil { + return err + } + pre := "/api/works/" + data := base.Json{ + "fileName": utils.Base(dst), + } + if srcFile.IsDir() { + pre = "/api/collections/" + data = base.Json{ + "title": utils.Base(dst), + } + } + _, err = driver.Request(pre+srcFile.Id, base.Put, nil, nil, nil, &data, nil, account) + return err +} + +func (driver Teambition) Copy(src string, dst string, account *model.Account) error { + srcFile, err := driver.File(src, account) + if err != nil { + return err + } + dstParentFile, err := driver.File(utils.Dir(dst), account) + if err != nil { + return err + } + pre := "/api/works/" + if srcFile.IsDir() { + pre = "/api/collections/" + } + _, err = driver.Request(pre+srcFile.Id+"/fork", base.Put, nil, nil, nil, &base.Json{ + "_parentId": dstParentFile.Id, + }, nil, account) + return err +} + +func (driver Teambition) Delete(path string, account *model.Account) error { + srcFile, err := driver.File(path, account) + if err != nil { + return err + } + pre := "/api/works/" + if srcFile.IsDir() { + pre = "/api/collections/" + } + _, err = driver.Request(pre+srcFile.Id+"/archive", base.Post, nil, nil, nil, nil, nil, account) + return err +} + +func (driver Teambition) Upload(file *model.FileStream, account *model.Account) error { + return base.ErrNotImplement +} + +var _ base.Driver = (*Teambition)(nil) diff --git a/drivers/teambition/teambition.go b/drivers/teambition/teambition.go new file mode 100644 index 00000000..a66fb597 --- /dev/null +++ b/drivers/teambition/teambition.go @@ -0,0 +1,153 @@ +package teambition + +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/go-resty/resty/v2" + "path" + "strconv" + "time" +) + +type ErrResp struct { + Name string `json:"name"` + Message string `json:"message"` +} + +func (driver Teambition) Request(pathname string, method int, headers, query, form map[string]string, data interface{}, resp interface{}, account *model.Account) ([]byte, error) { + url := "https://www.teambition.com" + pathname + if account.InternalType == "International" { + url = "https://us.teambition.com" + pathname + } + req := base.RestyClient.R() + req.SetHeader("Cookie", account.AccessToken) + if headers != nil { + req.SetHeaders(headers) + } + if query != nil { + req.SetQueryParams(query) + } + if form != nil { + req.SetFormData(form) + } + if data != nil { + req.SetBody(data) + } + if resp != nil { + req.SetResult(resp) + } + var e ErrResp + var err error + var res *resty.Response + req.SetError(&e) + switch method { + case base.Get: + res, err = req.Get(url) + case base.Post: + res, err = req.Post(url) + case base.Delete: + res, err = req.Delete(url) + case base.Patch: + res, err = req.Patch(url) + case base.Put: + res, err = req.Put(url) + default: + return nil, base.ErrNotSupport + } + if err != nil { + return nil, err + } + if e.Name != "" { + return nil, errors.New(e.Message) + } + return res.Body(), nil +} + +type Collection struct { + ID string `json:"_id"` + Title string `json:"title"` + Updated time.Time `json:"updated"` +} + +type Work struct { + ID string `json:"_id"` + FileName string `json:"fileName"` + FileSize int64 `json:"fileSize"` + FileKey string `json:"fileKey"` + FileCategory string `json:"fileCategory"` + DownloadURL string `json:"downloadUrl"` + ThumbnailURL string `json:"thumbnailUrl"` + Thumbnail string `json:"thumbnail"` + Updated time.Time `json:"updated"` + PreviewURL string `json:"previewUrl"` +} + +func (driver Teambition) GetFiles(parentId string, account *model.Account) ([]model.File, error) { + files := make([]model.File, 0) + page := 1 + for { + var collections []Collection + _, err := driver.Request("/api/collections", base.Get, nil, map[string]string{ + "_parentId": parentId, + "_projectId": account.Zone, + "order": account.OrderBy + account.OrderDirection, + "count": "50", + "page": strconv.Itoa(page), + }, nil, nil, &collections, account) + if err != nil { + return nil, err + } + if len(collections) == 0 { + break + } + page++ + for _, collection := range collections { + files = append(files, model.File{ + Id: collection.ID, + Name: collection.Title, + Size: 0, + Type: conf.FOLDER, + Driver: driver.Config().Name, + UpdatedAt: &collection.Updated, + }) + } + } + page = 1 + for { + var works []Work + _, err := driver.Request("/api/works", base.Get, nil, map[string]string{ + "_parentId": parentId, + "_projectId": account.Zone, + "order": account.OrderBy + account.OrderDirection, + "count": "50", + "page": strconv.Itoa(page), + }, nil, nil, &works, account) + if err != nil { + return nil, err + } + if len(works) == 0 { + break + } + page++ + for _, work := range works { + files = append(files, model.File{ + Id: work.ID, + Name: work.FileName, + Size: work.FileSize, + Type: utils.GetFileType(path.Ext(work.FileName)), + Driver: driver.Config().Name, + UpdatedAt: &work.Updated, + Thumbnail: work.Thumbnail, + Url: work.DownloadURL, + }) + } + } + return files, nil +} + +func init() { + base.RegisterDriver(&Teambition{}) +}