Book a Demo!
CoCalc Logo Icon
StoreFeaturesDocsShareSupportNewsAboutPoliciesSign UpSign In
alist-org
GitHub Repository: alist-org/alist
Path: blob/main/drivers/aliyundrive_open/util.go
1987 views
1
package aliyundrive_open
2
3
import (
4
"context"
5
"encoding/base64"
6
"errors"
7
"fmt"
8
"net/http"
9
"strings"
10
"time"
11
12
"github.com/alist-org/alist/v3/drivers/base"
13
"github.com/alist-org/alist/v3/internal/model"
14
"github.com/alist-org/alist/v3/internal/op"
15
"github.com/alist-org/alist/v3/pkg/utils"
16
"github.com/go-resty/resty/v2"
17
log "github.com/sirupsen/logrus"
18
)
19
20
// do others that not defined in Driver interface
21
22
func (d *AliyundriveOpen) _refreshToken() (string, string, error) {
23
url := API_URL + "/oauth/access_token"
24
if d.OauthTokenURL != "" && d.ClientID == "" {
25
url = d.OauthTokenURL
26
}
27
//var resp base.TokenResp
28
var e ErrResp
29
res, err := base.RestyClient.R().
30
//ForceContentType("application/json").
31
SetBody(base.Json{
32
"client_id": d.ClientID,
33
"client_secret": d.ClientSecret,
34
"grant_type": "refresh_token",
35
"refresh_token": d.RefreshToken,
36
}).
37
//SetResult(&resp).
38
SetError(&e).
39
Post(url)
40
if err != nil {
41
return "", "", err
42
}
43
log.Debugf("[ali_open] refresh token response: %s", res.String())
44
if e.Code != "" {
45
return "", "", fmt.Errorf("failed to refresh token: %s", e.Message)
46
}
47
refresh, access := utils.Json.Get(res.Body(), "refresh_token").ToString(), utils.Json.Get(res.Body(), "access_token").ToString()
48
if refresh == "" {
49
return "", "", fmt.Errorf("failed to refresh token: refresh token is empty, resp: %s", res.String())
50
}
51
curSub, err := getSub(d.RefreshToken)
52
if err != nil {
53
return "", "", err
54
}
55
newSub, err := getSub(refresh)
56
if err != nil {
57
return "", "", err
58
}
59
if curSub != newSub {
60
return "", "", errors.New("failed to refresh token: sub not match")
61
}
62
return refresh, access, nil
63
}
64
65
func getSub(token string) (string, error) {
66
segments := strings.Split(token, ".")
67
if len(segments) != 3 {
68
return "", errors.New("not a jwt token because of invalid segments")
69
}
70
bs, err := base64.RawStdEncoding.DecodeString(segments[1])
71
if err != nil {
72
return "", errors.New("failed to decode jwt token")
73
}
74
return utils.Json.Get(bs, "sub").ToString(), nil
75
}
76
77
func (d *AliyundriveOpen) refreshToken() error {
78
if d.ref != nil {
79
return d.ref.refreshToken()
80
}
81
refresh, access, err := d._refreshToken()
82
for i := 0; i < 3; i++ {
83
if err == nil {
84
break
85
} else {
86
log.Errorf("[ali_open] failed to refresh token: %s", err)
87
}
88
refresh, access, err = d._refreshToken()
89
}
90
if err != nil {
91
return err
92
}
93
log.Infof("[ali_open] token exchange: %s -> %s", d.RefreshToken, refresh)
94
d.RefreshToken, d.AccessToken = refresh, access
95
op.MustSaveDriverStorage(d)
96
return nil
97
}
98
99
func (d *AliyundriveOpen) request(uri, method string, callback base.ReqCallback, retry ...bool) ([]byte, error) {
100
b, err, _ := d.requestReturnErrResp(uri, method, callback, retry...)
101
return b, err
102
}
103
104
func (d *AliyundriveOpen) requestReturnErrResp(uri, method string, callback base.ReqCallback, retry ...bool) ([]byte, error, *ErrResp) {
105
req := base.RestyClient.R()
106
// TODO check whether access_token is expired
107
req.SetHeader("Authorization", "Bearer "+d.getAccessToken())
108
if method == http.MethodPost {
109
req.SetHeader("Content-Type", "application/json")
110
}
111
if callback != nil {
112
callback(req)
113
}
114
var e ErrResp
115
req.SetError(&e)
116
res, err := req.Execute(method, API_URL+uri)
117
if err != nil {
118
if res != nil {
119
log.Errorf("[aliyundrive_open] request error: %s", res.String())
120
}
121
return nil, err, nil
122
}
123
isRetry := len(retry) > 0 && retry[0]
124
if e.Code != "" {
125
if !isRetry && (utils.SliceContains([]string{"AccessTokenInvalid", "AccessTokenExpired", "I400JD"}, e.Code) || d.getAccessToken() == "") {
126
err = d.refreshToken()
127
if err != nil {
128
return nil, err, nil
129
}
130
return d.requestReturnErrResp(uri, method, callback, true)
131
}
132
return nil, fmt.Errorf("%s:%s", e.Code, e.Message), &e
133
}
134
return res.Body(), nil, nil
135
}
136
137
func (d *AliyundriveOpen) list(ctx context.Context, data base.Json) (*Files, error) {
138
var resp Files
139
_, err := d.request("/adrive/v1.0/openFile/list", http.MethodPost, func(req *resty.Request) {
140
req.SetBody(data).SetResult(&resp)
141
})
142
if err != nil {
143
return nil, err
144
}
145
return &resp, nil
146
}
147
148
func (d *AliyundriveOpen) getFiles(ctx context.Context, fileId string) ([]File, error) {
149
marker := "first"
150
res := make([]File, 0)
151
for marker != "" {
152
if marker == "first" {
153
marker = ""
154
}
155
data := base.Json{
156
"drive_id": d.DriveId,
157
"limit": 200,
158
"marker": marker,
159
"order_by": d.OrderBy,
160
"order_direction": d.OrderDirection,
161
"parent_file_id": fileId,
162
//"category": "",
163
//"type": "",
164
//"video_thumbnail_time": 120000,
165
//"video_thumbnail_width": 480,
166
//"image_thumbnail_width": 480,
167
}
168
resp, err := d.limitList(ctx, data)
169
if err != nil {
170
return nil, err
171
}
172
marker = resp.NextMarker
173
res = append(res, resp.Items...)
174
}
175
return res, nil
176
}
177
178
func getNowTime() (time.Time, string) {
179
nowTime := time.Now()
180
nowTimeStr := nowTime.Format("2006-01-02T15:04:05.000Z")
181
return nowTime, nowTimeStr
182
}
183
184
func (d *AliyundriveOpen) getAccessToken() string {
185
if d.ref != nil {
186
return d.ref.getAccessToken()
187
}
188
return d.AccessToken
189
}
190
191
// Remove duplicate files with the same name in the given directory path,
192
// preserving the file with the given skipID if provided
193
func (d *AliyundriveOpen) removeDuplicateFiles(ctx context.Context, parentPath string, fileName string, skipID string) error {
194
// Handle empty path (root directory) case
195
if parentPath == "" {
196
parentPath = "/"
197
}
198
199
// List all files in the parent directory
200
files, err := op.List(ctx, d, parentPath, model.ListArgs{})
201
if err != nil {
202
return err
203
}
204
205
// Find all files with the same name
206
var duplicates []model.Obj
207
for _, file := range files {
208
if file.GetName() == fileName && file.GetID() != skipID {
209
duplicates = append(duplicates, file)
210
}
211
}
212
213
// Remove all duplicates files, except the file with the given ID
214
for _, file := range duplicates {
215
err := d.Remove(ctx, file)
216
if err != nil {
217
return err
218
}
219
}
220
221
return nil
222
}
223
224