From df513b7dc0e7203db4cc8f980dd9fe89a00f9acf Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=E5=BE=AE=E5=87=89?= Date: Sun, 23 Jan 2022 14:03:04 +0800 Subject: [PATCH] :sparkles: teambition upload (<= 20 MB) --- bootstrap/account.go | 3 +- drivers/teambition/driver.go | 29 +++++++- drivers/teambition/teambition.go | 110 +++++++++++++++++++++++++------ drivers/teambition/types.go | 63 ++++++++++++++++++ drivers/teambition/util.go | 18 +++++ 5 files changed, 202 insertions(+), 21 deletions(-) create mode 100644 drivers/teambition/types.go create mode 100644 drivers/teambition/util.go diff --git a/bootstrap/account.go b/bootstrap/account.go index dc781b4e..b5d8ea97 100644 --- a/bootstrap/account.go +++ b/bootstrap/account.go @@ -19,6 +19,7 @@ func InitAccounts() { if !ok { log.Errorf("no [%s] driver", account.Type) } else { + log.Infof("start init account: [%s], type: [%s]", account.Name, account.Type) err := driver.Save(&accounts[i], nil) if err != nil { log.Errorf("init account [%s] error:[%s]", account.Name, err.Error()) @@ -27,4 +28,4 @@ func InitAccounts() { } } } -} \ No newline at end of file +} diff --git a/drivers/teambition/driver.go b/drivers/teambition/driver.go index 028b3d80..0949aad1 100644 --- a/drivers/teambition/driver.go +++ b/drivers/teambition/driver.go @@ -252,7 +252,34 @@ func (driver Teambition) Delete(path string, account *model.Account) error { } func (driver Teambition) Upload(file *model.FileStream, account *model.Account) error { - return base.ErrNotImplement + if file == nil { + return base.ErrEmptyFile + } + parentFile, err := driver.File(file.ParentPath, account) + if !parentFile.IsDir() { + return base.ErrNotFolder + } + if err != nil { + return err + } + res, err := driver.Request("/projects", base.Get, nil, nil, nil, nil, nil, account) + if err != nil { + return err + } + token := GetBetweenStr(string(res), "strikerAuth":"", "","phoneForLogin") + var newFile *FileUpload + if file.Size <= 20971520 { + // post upload + newFile, err = driver.upload(file, token, account) + } else { + // chunk upload + err = base.ErrNotImplement + //newFile, err = driver.chunkUpload(file, token, account) + } + if err != nil { + return err + } + return driver.finishUpload(newFile, parentFile.Id, account) } var _ base.Driver = (*Teambition)(nil) diff --git a/drivers/teambition/teambition.go b/drivers/teambition/teambition.go index e91639c4..ccf68cda 100644 --- a/drivers/teambition/teambition.go +++ b/drivers/teambition/teambition.go @@ -2,11 +2,14 @@ package teambition import ( "errors" + "fmt" "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" + log "github.com/sirupsen/logrus" + "io" "path" "strconv" "time" @@ -66,25 +69,6 @@ func (driver Teambition) Request(pathname string, method int, headers, query, fo 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 @@ -151,6 +135,94 @@ func (driver Teambition) GetFiles(parentId string, account *model.Account) ([]mo return files, nil } +func (driver Teambition) upload(file *model.FileStream, token string, account *model.Account) (*FileUpload, error) { + prefix := "tcs" + if account.InternalType == "International" { + prefix = "us-tcs" + } + var newFile FileUpload + _, err := base.RestyClient.R().SetResult(&newFile).SetHeader("Authorization", token). + SetMultipartFormData(map[string]string{ + "name": file.GetFileName(), + "type": file.GetMIMEType(), + "size": strconv.FormatUint(file.GetSize(), 10), + //"lastModifiedDate": "", + }).SetMultipartField("file", file.GetFileName(), file.GetMIMEType(), file). + Post(fmt.Sprintf("https://%s.teambition.net/upload", prefix)) + if err != nil { + return nil, err + } + return &newFile, nil +} + +func (driver Teambition) chunkUpload(file *model.FileStream, token string, account *model.Account) (*FileUpload, error) { + prefix := "tcs" + if account.InternalType == "International" { + prefix = "us-tcs" + } + var newChunk ChunkUpload + _, err := base.RestyClient.R().SetResult(&newChunk).SetHeader("Authorization", token). + SetBody(base.Json{ + "fileName": file.GetFileName(), + "fileSize": file.GetSize(), + "lastUpdated": time.Now(), + }).Post(fmt.Sprintf("https://%s.teambition.net/upload/chunk", prefix)) + if err != nil { + return nil, err + } + for i := 0; i < newChunk.Chunks; i++ { + chunkSize := newChunk.ChunkSize + if i == newChunk.Chunks-1 { + chunkSize = int(file.GetSize()) - i*chunkSize + } + log.Debugf("%d : %d", i, chunkSize) + chunkData := make([]byte, chunkSize) + _, err = io.ReadFull(file, chunkData) + if err != nil { + return nil, err + } + u := fmt.Sprintf("https://%s.teambition.net/upload/chunk/%s?chunk=%d&chunks=%d", + prefix, newChunk.FileKey, i+1, newChunk.Chunks) + log.Debugf("url: %s", u) + res, err := base.RestyClient.R().SetHeaders(map[string]string{ + "Authorization": token, + "Content-Type": "application/octet-stream", + "Referer": "https://www.teambition.com/", + }).SetBody(chunkData).Post(u) + if err != nil { + return nil, err + } + log.Debug(res.Status(), res.String()) + //req, err := http.NewRequest("POST", + // u, + // bytes.NewBuffer(chunkData)) + //if err != nil { + // return nil, err + //} + //req.Header.Set("Authorization", token) + //req.Header.Set("Content-Type", "application/octet-stream") + //req.Header.Set("Referer", "https://www.teambition.com/") + //resp, err := base.HttpClient.Do(req) + //res, _ := ioutil.ReadAll(resp.Body) + //log.Debugf("chunk upload status: %s, res: %s", resp.Status, string(res)) + if err != nil { + return nil, err + } + } + return &newChunk.FileUpload, nil +} + +func (driver Teambition) finishUpload(file *FileUpload, parentId string, account *model.Account) error { + file.InvolveMembers = []interface{}{} + file.Visible = "members" + file.ParentId = parentId + _, err := driver.Request("/api/works", base.Post, nil, nil, nil, base.Json{ + "works": []FileUpload{*file}, + "_parentId": parentId, + }, nil, account) + return err +} + func init() { base.RegisterDriver(&Teambition{}) } diff --git a/drivers/teambition/types.go b/drivers/teambition/types.go new file mode 100644 index 00000000..d62c07dc --- /dev/null +++ b/drivers/teambition/types.go @@ -0,0 +1,63 @@ +package teambition + +import "time" + +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"` +} + +type FileUpload struct { + FileKey string `json:"fileKey"` + FileName string `json:"fileName"` + FileType string `json:"fileType"` + FileSize int `json:"fileSize"` + FileCategory string `json:"fileCategory"` + ImageWidth int `json:"imageWidth"` + ImageHeight int `json:"imageHeight"` + InvolveMembers []interface{} `json:"involveMembers"` + Source string `json:"source"` + Visible string `json:"visible"` + ParentId string `json:"_parentId"` +} + +type ChunkUpload struct { + FileUpload + Storage string `json:"storage"` + MimeType string `json:"mimeType"` + Chunks int `json:"chunks"` + ChunkSize int `json:"chunkSize"` + Created time.Time `json:"created"` + FileMD5 string `json:"fileMD5"` + LastUpdated time.Time `json:"lastUpdated"` + UploadedChunks []interface{} `json:"uploadedChunks"` + Token struct { + AppID string `json:"AppID"` + OrganizationID string `json:"OrganizationID"` + UserID string `json:"UserID"` + Exp time.Time `json:"Exp"` + Storage string `json:"Storage"` + Resource string `json:"Resource"` + Speed int `json:"Speed"` + } `json:"token"` + DownloadUrl string `json:"downloadUrl"` + ThumbnailUrl string `json:"thumbnailUrl"` + PreviewUrl string `json:"previewUrl"` + ImmPreviewUrl string `json:"immPreviewUrl"` + PreviewExt string `json:"previewExt"` + LastUploadTime interface{} `json:"lastUploadTime"` +} diff --git a/drivers/teambition/util.go b/drivers/teambition/util.go new file mode 100644 index 00000000..d14f6882 --- /dev/null +++ b/drivers/teambition/util.go @@ -0,0 +1,18 @@ +package teambition + +import "strings" + +func GetBetweenStr(str, start, end string) string { + n := strings.Index(str, start) + if n == -1 { + return "" + } + n = n + len(start) + str = string([]byte(str)[n:]) + m := strings.Index(str, end) + if m == -1 { + return "" + } + str = string([]byte(str)[:m]) + return str +}