Book a Demo!
CoCalc Logo Icon
StoreFeaturesDocsShareSupportNewsAboutPoliciesSign UpSign In
alist-org
GitHub Repository: alist-org/alist
Path: blob/main/drivers/123/driver.go
1986 views
1
package _123
2
3
import (
4
"context"
5
"encoding/base64"
6
"fmt"
7
"net/http"
8
"net/url"
9
"strconv"
10
"strings"
11
"sync"
12
"time"
13
14
"golang.org/x/time/rate"
15
16
"github.com/alist-org/alist/v3/drivers/base"
17
"github.com/alist-org/alist/v3/internal/driver"
18
"github.com/alist-org/alist/v3/internal/errs"
19
"github.com/alist-org/alist/v3/internal/model"
20
"github.com/alist-org/alist/v3/internal/stream"
21
"github.com/alist-org/alist/v3/pkg/utils"
22
"github.com/aws/aws-sdk-go/aws"
23
"github.com/aws/aws-sdk-go/aws/credentials"
24
"github.com/aws/aws-sdk-go/aws/session"
25
"github.com/aws/aws-sdk-go/service/s3/s3manager"
26
"github.com/go-resty/resty/v2"
27
log "github.com/sirupsen/logrus"
28
)
29
30
type Pan123 struct {
31
model.Storage
32
Addition
33
apiRateLimit sync.Map
34
safeBoxUnlocked sync.Map
35
}
36
37
func (d *Pan123) Config() driver.Config {
38
return config
39
}
40
41
func (d *Pan123) GetAddition() driver.Additional {
42
return &d.Addition
43
}
44
45
func (d *Pan123) Init(ctx context.Context) error {
46
_, err := d.Request(UserInfo, http.MethodGet, nil, nil)
47
return err
48
}
49
50
func (d *Pan123) Drop(ctx context.Context) error {
51
_, _ = d.Request(Logout, http.MethodPost, func(req *resty.Request) {
52
req.SetBody(base.Json{})
53
}, nil)
54
return nil
55
}
56
57
func (d *Pan123) List(ctx context.Context, dir model.Obj, args model.ListArgs) ([]model.Obj, error) {
58
if f, ok := dir.(File); ok && f.IsLock {
59
if err := d.unlockSafeBox(f.FileId); err != nil {
60
return nil, err
61
}
62
}
63
files, err := d.getFiles(ctx, dir.GetID(), dir.GetName())
64
if err != nil {
65
msg := strings.ToLower(err.Error())
66
if strings.Contains(msg, "safe box") || strings.Contains(err.Error(), "保险箱") {
67
if id, e := strconv.ParseInt(dir.GetID(), 10, 64); e == nil {
68
if e = d.unlockSafeBox(id); e == nil {
69
files, err = d.getFiles(ctx, dir.GetID(), dir.GetName())
70
} else {
71
return nil, e
72
}
73
}
74
}
75
if err != nil {
76
return nil, err
77
}
78
}
79
return utils.SliceConvert(files, func(src File) (model.Obj, error) {
80
return src, nil
81
})
82
}
83
84
func (d *Pan123) Link(ctx context.Context, file model.Obj, args model.LinkArgs) (*model.Link, error) {
85
if f, ok := file.(File); ok {
86
//var resp DownResp
87
var headers map[string]string
88
if !utils.IsLocalIPAddr(args.IP) {
89
headers = map[string]string{
90
//"X-Real-IP": "1.1.1.1",
91
"X-Forwarded-For": args.IP,
92
}
93
}
94
data := base.Json{
95
"driveId": 0,
96
"etag": f.Etag,
97
"fileId": f.FileId,
98
"fileName": f.FileName,
99
"s3keyFlag": f.S3KeyFlag,
100
"size": f.Size,
101
"type": f.Type,
102
}
103
resp, err := d.Request(DownloadInfo, http.MethodPost, func(req *resty.Request) {
104
105
req.SetBody(data).SetHeaders(headers)
106
}, nil)
107
if err != nil {
108
return nil, err
109
}
110
downloadUrl := utils.Json.Get(resp, "data", "DownloadUrl").ToString()
111
u, err := url.Parse(downloadUrl)
112
if err != nil {
113
return nil, err
114
}
115
nu := u.Query().Get("params")
116
if nu != "" {
117
du, _ := base64.StdEncoding.DecodeString(nu)
118
u, err = url.Parse(string(du))
119
if err != nil {
120
return nil, err
121
}
122
}
123
u_ := u.String()
124
log.Debug("download url: ", u_)
125
res, err := base.NoRedirectClient.R().SetHeader("Referer", "https://www.123pan.com/").Get(u_)
126
if err != nil {
127
return nil, err
128
}
129
log.Debug(res.String())
130
link := model.Link{
131
URL: u_,
132
}
133
log.Debugln("res code: ", res.StatusCode())
134
if res.StatusCode() == 302 {
135
link.URL = res.Header().Get("location")
136
} else if res.StatusCode() < 300 {
137
link.URL = utils.Json.Get(res.Body(), "data", "redirect_url").ToString()
138
}
139
link.Header = http.Header{
140
"Referer": []string{"https://www.123pan.com/"},
141
}
142
return &link, nil
143
} else {
144
return nil, fmt.Errorf("can't convert obj")
145
}
146
}
147
148
func (d *Pan123) MakeDir(ctx context.Context, parentDir model.Obj, dirName string) error {
149
data := base.Json{
150
"driveId": 0,
151
"etag": "",
152
"fileName": dirName,
153
"parentFileId": parentDir.GetID(),
154
"size": 0,
155
"type": 1,
156
}
157
_, err := d.Request(Mkdir, http.MethodPost, func(req *resty.Request) {
158
req.SetBody(data)
159
}, nil)
160
return err
161
}
162
163
func (d *Pan123) Move(ctx context.Context, srcObj, dstDir model.Obj) error {
164
data := base.Json{
165
"fileIdList": []base.Json{{"FileId": srcObj.GetID()}},
166
"parentFileId": dstDir.GetID(),
167
}
168
_, err := d.Request(Move, http.MethodPost, func(req *resty.Request) {
169
req.SetBody(data)
170
}, nil)
171
return err
172
}
173
174
func (d *Pan123) Rename(ctx context.Context, srcObj model.Obj, newName string) error {
175
data := base.Json{
176
"driveId": 0,
177
"fileId": srcObj.GetID(),
178
"fileName": newName,
179
}
180
_, err := d.Request(Rename, http.MethodPost, func(req *resty.Request) {
181
req.SetBody(data)
182
}, nil)
183
return err
184
}
185
186
func (d *Pan123) Copy(ctx context.Context, srcObj, dstDir model.Obj) error {
187
return errs.NotSupport
188
}
189
190
func (d *Pan123) Remove(ctx context.Context, obj model.Obj) error {
191
if f, ok := obj.(File); ok {
192
data := base.Json{
193
"driveId": 0,
194
"operation": true,
195
"fileTrashInfoList": []File{f},
196
}
197
_, err := d.Request(Trash, http.MethodPost, func(req *resty.Request) {
198
req.SetBody(data)
199
}, nil)
200
return err
201
} else {
202
return fmt.Errorf("can't convert obj")
203
}
204
}
205
206
func (d *Pan123) Put(ctx context.Context, dstDir model.Obj, file model.FileStreamer, up driver.UpdateProgress) error {
207
etag := file.GetHash().GetHash(utils.MD5)
208
var err error
209
if len(etag) < utils.MD5.Width {
210
_, etag, err = stream.CacheFullInTempFileAndHash(file, utils.MD5)
211
if err != nil {
212
return err
213
}
214
}
215
data := base.Json{
216
"driveId": 0,
217
"duplicate": 2, // 2->覆盖 1->重命名 0->默认
218
"etag": etag,
219
"fileName": file.GetName(),
220
"parentFileId": dstDir.GetID(),
221
"size": file.GetSize(),
222
"type": 0,
223
}
224
var resp UploadResp
225
res, err := d.Request(UploadRequest, http.MethodPost, func(req *resty.Request) {
226
req.SetBody(data).SetContext(ctx)
227
}, &resp)
228
if err != nil {
229
return err
230
}
231
log.Debugln("upload request res: ", string(res))
232
if resp.Data.Reuse || resp.Data.Key == "" {
233
return nil
234
}
235
if resp.Data.AccessKeyId == "" || resp.Data.SecretAccessKey == "" || resp.Data.SessionToken == "" {
236
err = d.newUpload(ctx, &resp, file, up)
237
return err
238
} else {
239
cfg := &aws.Config{
240
Credentials: credentials.NewStaticCredentials(resp.Data.AccessKeyId, resp.Data.SecretAccessKey, resp.Data.SessionToken),
241
Region: aws.String("123pan"),
242
Endpoint: aws.String(resp.Data.EndPoint),
243
S3ForcePathStyle: aws.Bool(true),
244
}
245
s, err := session.NewSession(cfg)
246
if err != nil {
247
return err
248
}
249
uploader := s3manager.NewUploader(s)
250
if file.GetSize() > s3manager.MaxUploadParts*s3manager.DefaultUploadPartSize {
251
uploader.PartSize = file.GetSize() / (s3manager.MaxUploadParts - 1)
252
}
253
input := &s3manager.UploadInput{
254
Bucket: &resp.Data.Bucket,
255
Key: &resp.Data.Key,
256
Body: driver.NewLimitedUploadStream(ctx, &driver.ReaderUpdatingProgress{
257
Reader: file,
258
UpdateProgress: up,
259
}),
260
}
261
_, err = uploader.UploadWithContext(ctx, input)
262
if err != nil {
263
return err
264
}
265
}
266
_, err = d.Request(UploadComplete, http.MethodPost, func(req *resty.Request) {
267
req.SetBody(base.Json{
268
"fileId": resp.Data.FileId,
269
}).SetContext(ctx)
270
}, nil)
271
return err
272
}
273
274
func (d *Pan123) APIRateLimit(ctx context.Context, api string) error {
275
value, _ := d.apiRateLimit.LoadOrStore(api,
276
rate.NewLimiter(rate.Every(700*time.Millisecond), 1))
277
limiter := value.(*rate.Limiter)
278
279
return limiter.Wait(ctx)
280
}
281
282
var _ driver.Driver = (*Pan123)(nil)
283
284