Book a Demo!
CoCalc Logo Icon
StoreFeaturesDocsShareSupportNewsAboutPoliciesSign UpSign In
alist-org
GitHub Repository: alist-org/alist
Path: blob/main/drivers/dropbox/util.go
1987 views
1
package dropbox
2
3
import (
4
"context"
5
"fmt"
6
"io"
7
"net/http"
8
"strings"
9
10
"github.com/alist-org/alist/v3/drivers/base"
11
"github.com/alist-org/alist/v3/internal/op"
12
"github.com/alist-org/alist/v3/pkg/utils"
13
"github.com/go-resty/resty/v2"
14
log "github.com/sirupsen/logrus"
15
)
16
17
func (d *Dropbox) refreshToken() error {
18
url := d.base + "/oauth2/token"
19
if utils.SliceContains([]string{"", DefaultClientID}, d.ClientID) {
20
url = d.OauthTokenURL
21
}
22
var tokenResp TokenResp
23
resp, err := base.RestyClient.R().
24
//ForceContentType("application/x-www-form-urlencoded").
25
//SetBasicAuth(d.ClientID, d.ClientSecret).
26
SetFormData(map[string]string{
27
"grant_type": "refresh_token",
28
"refresh_token": d.RefreshToken,
29
"client_id": d.ClientID,
30
"client_secret": d.ClientSecret,
31
}).
32
Post(url)
33
if err != nil {
34
return err
35
}
36
log.Debugf("[dropbox] refresh token response: %s", resp.String())
37
if resp.StatusCode() != 200 {
38
return fmt.Errorf("failed to refresh token: %s", resp.String())
39
}
40
_ = utils.Json.UnmarshalFromString(resp.String(), &tokenResp)
41
d.AccessToken = tokenResp.AccessToken
42
op.MustSaveDriverStorage(d)
43
return nil
44
}
45
46
func (d *Dropbox) request(uri, method string, callback base.ReqCallback, retry ...bool) ([]byte, error) {
47
req := base.RestyClient.R()
48
req.SetHeader("Authorization", "Bearer "+d.AccessToken)
49
if d.RootNamespaceId != "" {
50
apiPathRootJson, err := utils.Json.MarshalToString(map[string]interface{}{
51
".tag": "root",
52
"root": d.RootNamespaceId,
53
})
54
if err != nil {
55
return nil, err
56
}
57
req.SetHeader("Dropbox-API-Path-Root", apiPathRootJson)
58
}
59
if callback != nil {
60
callback(req)
61
}
62
if method == http.MethodPost && req.Body != nil {
63
req.SetHeader("Content-Type", "application/json")
64
}
65
var e ErrorResp
66
req.SetError(&e)
67
res, err := req.Execute(method, d.base+uri)
68
if err != nil {
69
return nil, err
70
}
71
log.Debugf("[dropbox] request (%s) response: %s", uri, res.String())
72
isRetry := len(retry) > 0 && retry[0]
73
if res.StatusCode() != 200 {
74
body := res.String()
75
if !isRetry && (utils.SliceMeet([]string{"expired_access_token", "invalid_access_token", "authorization"}, body,
76
func(item string, v string) bool {
77
return strings.Contains(v, item)
78
}) || d.AccessToken == "") {
79
err = d.refreshToken()
80
if err != nil {
81
return nil, err
82
}
83
return d.request(uri, method, callback, true)
84
}
85
return nil, fmt.Errorf("%s:%s", e.Error, e.ErrorSummary)
86
}
87
return res.Body(), nil
88
}
89
90
func (d *Dropbox) list(ctx context.Context, data base.Json, isContinue bool) (*ListResp, error) {
91
var resp ListResp
92
uri := "/2/files/list_folder"
93
if isContinue {
94
uri += "/continue"
95
}
96
_, err := d.request(uri, http.MethodPost, func(req *resty.Request) {
97
req.SetContext(ctx).SetBody(data).SetResult(&resp)
98
})
99
if err != nil {
100
return nil, err
101
}
102
return &resp, nil
103
}
104
105
func (d *Dropbox) getFiles(ctx context.Context, path string) ([]File, error) {
106
hasMore := true
107
var marker string
108
res := make([]File, 0)
109
110
data := base.Json{
111
"include_deleted": false,
112
"include_has_explicit_shared_members": false,
113
"include_mounted_folders": false,
114
"include_non_downloadable_files": false,
115
"limit": 2000,
116
"path": path,
117
"recursive": false,
118
}
119
resp, err := d.list(ctx, data, false)
120
if err != nil {
121
return nil, err
122
}
123
marker = resp.Cursor
124
hasMore = resp.HasMore
125
res = append(res, resp.Entries...)
126
127
for hasMore {
128
data := base.Json{
129
"cursor": marker,
130
}
131
resp, err := d.list(ctx, data, true)
132
if err != nil {
133
return nil, err
134
}
135
marker = resp.Cursor
136
hasMore = resp.HasMore
137
res = append(res, resp.Entries...)
138
}
139
return res, nil
140
}
141
142
func (d *Dropbox) finishUploadSession(ctx context.Context, toPath string, offset int64, sessionId string) error {
143
url := d.contentBase + "/2/files/upload_session/finish"
144
req, err := http.NewRequest(http.MethodPost, url, nil)
145
if err != nil {
146
return err
147
}
148
req = req.WithContext(ctx)
149
req.Header.Set("Content-Type", "application/octet-stream")
150
req.Header.Set("Authorization", "Bearer "+d.AccessToken)
151
152
uploadFinishArgs := UploadFinishArgs{
153
Commit: struct {
154
Autorename bool `json:"autorename"`
155
Mode string `json:"mode"`
156
Mute bool `json:"mute"`
157
Path string `json:"path"`
158
StrictConflict bool `json:"strict_conflict"`
159
}{
160
Autorename: true,
161
Mode: "add",
162
Mute: false,
163
Path: toPath,
164
StrictConflict: false,
165
},
166
Cursor: UploadCursor{
167
Offset: offset,
168
SessionID: sessionId,
169
},
170
}
171
172
argsJson, err := utils.Json.MarshalToString(uploadFinishArgs)
173
if err != nil {
174
return err
175
}
176
req.Header.Set("Dropbox-API-Arg", argsJson)
177
178
res, err := base.HttpClient.Do(req)
179
if err != nil {
180
log.Errorf("failed to update file when finish session, err: %+v", err)
181
return err
182
}
183
_ = res.Body.Close()
184
return nil
185
}
186
187
func (d *Dropbox) startUploadSession(ctx context.Context) (string, error) {
188
url := d.contentBase + "/2/files/upload_session/start"
189
req, err := http.NewRequest(http.MethodPost, url, nil)
190
if err != nil {
191
return "", err
192
}
193
req = req.WithContext(ctx)
194
req.Header.Set("Content-Type", "application/octet-stream")
195
req.Header.Set("Authorization", "Bearer "+d.AccessToken)
196
req.Header.Set("Dropbox-API-Arg", "{\"close\":false}")
197
198
res, err := base.HttpClient.Do(req)
199
if err != nil {
200
log.Errorf("failed to update file when start session, err: %+v", err)
201
return "", err
202
}
203
204
body, err := io.ReadAll(res.Body)
205
sessionId := utils.Json.Get(body, "session_id").ToString()
206
207
_ = res.Body.Close()
208
return sessionId, nil
209
}
210
211