Spaces:
Paused
Paused
| package dropbox | |
| import ( | |
| "context" | |
| "fmt" | |
| "io" | |
| "net/http" | |
| "strings" | |
| "github.com/alist-org/alist/v3/drivers/base" | |
| "github.com/alist-org/alist/v3/internal/op" | |
| "github.com/alist-org/alist/v3/pkg/utils" | |
| "github.com/go-resty/resty/v2" | |
| log "github.com/sirupsen/logrus" | |
| ) | |
| func (d *Dropbox) refreshToken() error { | |
| url := d.base + "/oauth2/token" | |
| if utils.SliceContains([]string{"", DefaultClientID}, d.ClientID) { | |
| url = d.OauthTokenURL | |
| } | |
| var tokenResp TokenResp | |
| resp, err := base.RestyClient.R(). | |
| //ForceContentType("application/x-www-form-urlencoded"). | |
| //SetBasicAuth(d.ClientID, d.ClientSecret). | |
| SetFormData(map[string]string{ | |
| "grant_type": "refresh_token", | |
| "refresh_token": d.RefreshToken, | |
| "client_id": d.ClientID, | |
| "client_secret": d.ClientSecret, | |
| }). | |
| Post(url) | |
| if err != nil { | |
| return err | |
| } | |
| log.Debugf("[dropbox] refresh token response: %s", resp.String()) | |
| if resp.StatusCode() != 200 { | |
| return fmt.Errorf("failed to refresh token: %s", resp.String()) | |
| } | |
| _ = utils.Json.UnmarshalFromString(resp.String(), &tokenResp) | |
| d.AccessToken = tokenResp.AccessToken | |
| op.MustSaveDriverStorage(d) | |
| return nil | |
| } | |
| func (d *Dropbox) request(uri, method string, callback base.ReqCallback, retry ...bool) ([]byte, error) { | |
| req := base.RestyClient.R() | |
| req.SetHeader("Authorization", "Bearer "+d.AccessToken) | |
| if d.RootNamespaceId != "" { | |
| apiPathRootJson, err := utils.Json.MarshalToString(map[string]interface{}{ | |
| ".tag": "root", | |
| "root": d.RootNamespaceId, | |
| }) | |
| if err != nil { | |
| return nil, err | |
| } | |
| req.SetHeader("Dropbox-API-Path-Root", apiPathRootJson) | |
| } | |
| if callback != nil { | |
| callback(req) | |
| } | |
| if method == http.MethodPost && req.Body != nil { | |
| req.SetHeader("Content-Type", "application/json") | |
| } | |
| var e ErrorResp | |
| req.SetError(&e) | |
| res, err := req.Execute(method, d.base+uri) | |
| if err != nil { | |
| return nil, err | |
| } | |
| log.Debugf("[dropbox] request (%s) response: %s", uri, res.String()) | |
| isRetry := len(retry) > 0 && retry[0] | |
| if res.StatusCode() != 200 { | |
| body := res.String() | |
| if !isRetry && (utils.SliceMeet([]string{"expired_access_token", "invalid_access_token", "authorization"}, body, | |
| func(item string, v string) bool { | |
| return strings.Contains(v, item) | |
| }) || d.AccessToken == "") { | |
| err = d.refreshToken() | |
| if err != nil { | |
| return nil, err | |
| } | |
| return d.request(uri, method, callback, true) | |
| } | |
| return nil, fmt.Errorf("%s:%s", e.Error, e.ErrorSummary) | |
| } | |
| return res.Body(), nil | |
| } | |
| func (d *Dropbox) list(ctx context.Context, data base.Json, isContinue bool) (*ListResp, error) { | |
| var resp ListResp | |
| uri := "/2/files/list_folder" | |
| if isContinue { | |
| uri += "/continue" | |
| } | |
| _, err := d.request(uri, http.MethodPost, func(req *resty.Request) { | |
| req.SetContext(ctx).SetBody(data).SetResult(&resp) | |
| }) | |
| if err != nil { | |
| return nil, err | |
| } | |
| return &resp, nil | |
| } | |
| func (d *Dropbox) getFiles(ctx context.Context, path string) ([]File, error) { | |
| hasMore := true | |
| var marker string | |
| res := make([]File, 0) | |
| data := base.Json{ | |
| "include_deleted": false, | |
| "include_has_explicit_shared_members": false, | |
| "include_mounted_folders": false, | |
| "include_non_downloadable_files": false, | |
| "limit": 2000, | |
| "path": path, | |
| "recursive": false, | |
| } | |
| resp, err := d.list(ctx, data, false) | |
| if err != nil { | |
| return nil, err | |
| } | |
| marker = resp.Cursor | |
| hasMore = resp.HasMore | |
| res = append(res, resp.Entries...) | |
| for hasMore { | |
| data := base.Json{ | |
| "cursor": marker, | |
| } | |
| resp, err := d.list(ctx, data, true) | |
| if err != nil { | |
| return nil, err | |
| } | |
| marker = resp.Cursor | |
| hasMore = resp.HasMore | |
| res = append(res, resp.Entries...) | |
| } | |
| return res, nil | |
| } | |
| func (d *Dropbox) finishUploadSession(ctx context.Context, toPath string, offset int64, sessionId string) error { | |
| url := d.contentBase + "/2/files/upload_session/finish" | |
| req, err := http.NewRequest(http.MethodPost, url, nil) | |
| if err != nil { | |
| return err | |
| } | |
| req = req.WithContext(ctx) | |
| req.Header.Set("Content-Type", "application/octet-stream") | |
| req.Header.Set("Authorization", "Bearer "+d.AccessToken) | |
| uploadFinishArgs := UploadFinishArgs{ | |
| Commit: struct { | |
| Autorename bool `json:"autorename"` | |
| Mode string `json:"mode"` | |
| Mute bool `json:"mute"` | |
| Path string `json:"path"` | |
| StrictConflict bool `json:"strict_conflict"` | |
| }{ | |
| Autorename: true, | |
| Mode: "add", | |
| Mute: false, | |
| Path: toPath, | |
| StrictConflict: false, | |
| }, | |
| Cursor: UploadCursor{ | |
| Offset: offset, | |
| SessionID: sessionId, | |
| }, | |
| } | |
| argsJson, err := utils.Json.MarshalToString(uploadFinishArgs) | |
| if err != nil { | |
| return err | |
| } | |
| req.Header.Set("Dropbox-API-Arg", argsJson) | |
| res, err := base.HttpClient.Do(req) | |
| if err != nil { | |
| log.Errorf("failed to update file when finish session, err: %+v", err) | |
| return err | |
| } | |
| _ = res.Body.Close() | |
| return nil | |
| } | |
| func (d *Dropbox) startUploadSession(ctx context.Context) (string, error) { | |
| url := d.contentBase + "/2/files/upload_session/start" | |
| req, err := http.NewRequest(http.MethodPost, url, nil) | |
| if err != nil { | |
| return "", err | |
| } | |
| req = req.WithContext(ctx) | |
| req.Header.Set("Content-Type", "application/octet-stream") | |
| req.Header.Set("Authorization", "Bearer "+d.AccessToken) | |
| req.Header.Set("Dropbox-API-Arg", "{\"close\":false}") | |
| res, err := base.HttpClient.Do(req) | |
| if err != nil { | |
| log.Errorf("failed to update file when start session, err: %+v", err) | |
| return "", err | |
| } | |
| body, err := io.ReadAll(res.Body) | |
| sessionId := utils.Json.Get(body, "session_id").ToString() | |
| _ = res.Body.Close() | |
| return sessionId, nil | |
| } | |