Book a Demo!
CoCalc Logo Icon
StoreFeaturesDocsShareSupportNewsAboutPoliciesSign UpSign In
alist-org
GitHub Repository: alist-org/alist
Path: blob/main/drivers/cloudreve_v4/driver.go
1986 views
1
package cloudreve_v4
2
3
import (
4
"context"
5
"errors"
6
"net/http"
7
"strconv"
8
"strings"
9
"time"
10
11
"github.com/alist-org/alist/v3/drivers/base"
12
"github.com/alist-org/alist/v3/internal/driver"
13
"github.com/alist-org/alist/v3/internal/errs"
14
"github.com/alist-org/alist/v3/internal/model"
15
"github.com/alist-org/alist/v3/internal/op"
16
"github.com/alist-org/alist/v3/pkg/utils"
17
"github.com/go-resty/resty/v2"
18
)
19
20
type CloudreveV4 struct {
21
model.Storage
22
Addition
23
ref *CloudreveV4
24
}
25
26
func (d *CloudreveV4) Config() driver.Config {
27
if d.ref != nil {
28
return d.ref.Config()
29
}
30
if d.EnableVersionUpload {
31
config.NoOverwriteUpload = false
32
}
33
return config
34
}
35
36
func (d *CloudreveV4) GetAddition() driver.Additional {
37
return &d.Addition
38
}
39
40
func (d *CloudreveV4) Init(ctx context.Context) error {
41
// removing trailing slash
42
d.Address = strings.TrimSuffix(d.Address, "/")
43
op.MustSaveDriverStorage(d)
44
if d.ref != nil {
45
return nil
46
}
47
if d.AccessToken == "" && d.RefreshToken != "" {
48
return d.refreshToken()
49
}
50
if d.Username != "" {
51
return d.login()
52
}
53
return nil
54
}
55
56
func (d *CloudreveV4) InitReference(storage driver.Driver) error {
57
refStorage, ok := storage.(*CloudreveV4)
58
if ok {
59
d.ref = refStorage
60
return nil
61
}
62
return errs.NotSupport
63
}
64
65
func (d *CloudreveV4) Drop(ctx context.Context) error {
66
d.ref = nil
67
return nil
68
}
69
70
func (d *CloudreveV4) List(ctx context.Context, dir model.Obj, args model.ListArgs) ([]model.Obj, error) {
71
const pageSize int = 100
72
var f []File
73
var r FileResp
74
params := map[string]string{
75
"page_size": strconv.Itoa(pageSize),
76
"uri": dir.GetPath(),
77
"order_by": d.OrderBy,
78
"order_direction": d.OrderDirection,
79
"page": "0",
80
}
81
82
for {
83
err := d.request(http.MethodGet, "/file", func(req *resty.Request) {
84
req.SetQueryParams(params)
85
}, &r)
86
if err != nil {
87
return nil, err
88
}
89
f = append(f, r.Files...)
90
if r.Pagination.NextToken == "" || len(r.Files) < pageSize {
91
break
92
}
93
params["next_page_token"] = r.Pagination.NextToken
94
}
95
96
return utils.SliceConvert(f, func(src File) (model.Obj, error) {
97
if d.EnableFolderSize && src.Type == 1 {
98
var ds FolderSummaryResp
99
err := d.request(http.MethodGet, "/file/info", func(req *resty.Request) {
100
req.SetQueryParam("uri", src.Path)
101
req.SetQueryParam("folder_summary", "true")
102
}, &ds)
103
if err == nil && ds.FolderSummary.Size > 0 {
104
src.Size = ds.FolderSummary.Size
105
}
106
}
107
var thumb model.Thumbnail
108
if d.EnableThumb && src.Type == 0 {
109
var t FileThumbResp
110
err := d.request(http.MethodGet, "/file/thumb", func(req *resty.Request) {
111
req.SetQueryParam("uri", src.Path)
112
}, &t)
113
if err == nil && t.URL != "" {
114
thumb = model.Thumbnail{
115
Thumbnail: t.URL,
116
}
117
}
118
}
119
return &model.ObjThumb{
120
Object: model.Object{
121
ID: src.ID,
122
Path: src.Path,
123
Name: src.Name,
124
Size: src.Size,
125
Modified: src.UpdatedAt,
126
Ctime: src.CreatedAt,
127
IsFolder: src.Type == 1,
128
},
129
Thumbnail: thumb,
130
}, nil
131
})
132
}
133
134
func (d *CloudreveV4) Link(ctx context.Context, file model.Obj, args model.LinkArgs) (*model.Link, error) {
135
var url FileUrlResp
136
err := d.request(http.MethodPost, "/file/url", func(req *resty.Request) {
137
req.SetBody(base.Json{
138
"uris": []string{file.GetPath()},
139
"download": true,
140
})
141
}, &url)
142
if err != nil {
143
return nil, err
144
}
145
if len(url.Urls) == 0 {
146
return nil, errors.New("server returns no url")
147
}
148
exp := time.Until(url.Expires)
149
return &model.Link{
150
URL: url.Urls[0].URL,
151
Expiration: &exp,
152
}, nil
153
}
154
155
func (d *CloudreveV4) MakeDir(ctx context.Context, parentDir model.Obj, dirName string) error {
156
return d.request(http.MethodPost, "/file/create", func(req *resty.Request) {
157
req.SetBody(base.Json{
158
"type": "folder",
159
"uri": parentDir.GetPath() + "/" + dirName,
160
"error_on_conflict": true,
161
})
162
}, nil)
163
}
164
165
func (d *CloudreveV4) Move(ctx context.Context, srcObj, dstDir model.Obj) error {
166
return d.request(http.MethodPost, "/file/move", func(req *resty.Request) {
167
req.SetBody(base.Json{
168
"uris": []string{srcObj.GetPath()},
169
"dst": dstDir.GetPath(),
170
"copy": false,
171
})
172
}, nil)
173
}
174
175
func (d *CloudreveV4) Rename(ctx context.Context, srcObj model.Obj, newName string) error {
176
return d.request(http.MethodPost, "/file/create", func(req *resty.Request) {
177
req.SetBody(base.Json{
178
"new_name": newName,
179
"uri": srcObj.GetPath(),
180
})
181
}, nil)
182
183
}
184
185
func (d *CloudreveV4) Copy(ctx context.Context, srcObj, dstDir model.Obj) error {
186
return d.request(http.MethodPost, "/file/move", func(req *resty.Request) {
187
req.SetBody(base.Json{
188
"uris": []string{srcObj.GetPath()},
189
"dst": dstDir.GetPath(),
190
"copy": true,
191
})
192
}, nil)
193
}
194
195
func (d *CloudreveV4) Remove(ctx context.Context, obj model.Obj) error {
196
return d.request(http.MethodDelete, "/file", func(req *resty.Request) {
197
req.SetBody(base.Json{
198
"uris": []string{obj.GetPath()},
199
"unlink": false,
200
"skip_soft_delete": true,
201
})
202
}, nil)
203
}
204
205
func (d *CloudreveV4) Put(ctx context.Context, dstDir model.Obj, file model.FileStreamer, up driver.UpdateProgress) error {
206
if file.GetSize() == 0 {
207
// 空文件使用新建文件方法,避免上传卡锁
208
return d.request(http.MethodPost, "/file/create", func(req *resty.Request) {
209
req.SetBody(base.Json{
210
"type": "file",
211
"uri": dstDir.GetPath() + "/" + file.GetName(),
212
"error_on_conflict": true,
213
})
214
}, nil)
215
}
216
var p StoragePolicy
217
var r FileResp
218
var u FileUploadResp
219
var err error
220
params := map[string]string{
221
"page_size": "10",
222
"uri": dstDir.GetPath(),
223
"order_by": "created_at",
224
"order_direction": "asc",
225
"page": "0",
226
}
227
err = d.request(http.MethodGet, "/file", func(req *resty.Request) {
228
req.SetQueryParams(params)
229
}, &r)
230
if err != nil {
231
return err
232
}
233
p = r.StoragePolicy
234
body := base.Json{
235
"uri": dstDir.GetPath() + "/" + file.GetName(),
236
"size": file.GetSize(),
237
"policy_id": p.ID,
238
"last_modified": file.ModTime().UnixMilli(),
239
"mime_type": "",
240
}
241
if d.EnableVersionUpload {
242
body["entity_type"] = "version"
243
}
244
err = d.request(http.MethodPut, "/file/upload", func(req *resty.Request) {
245
req.SetBody(body)
246
}, &u)
247
if err != nil {
248
return err
249
}
250
if u.StoragePolicy.Relay {
251
err = d.upLocal(ctx, file, u, up)
252
} else {
253
switch u.StoragePolicy.Type {
254
case "local":
255
err = d.upLocal(ctx, file, u, up)
256
case "remote":
257
err = d.upRemote(ctx, file, u, up)
258
case "onedrive":
259
err = d.upOneDrive(ctx, file, u, up)
260
case "s3":
261
err = d.upS3(ctx, file, u, up)
262
default:
263
return errs.NotImplement
264
}
265
}
266
if err != nil {
267
// 删除失败的会话
268
_ = d.request(http.MethodDelete, "/file/upload", func(req *resty.Request) {
269
req.SetBody(base.Json{
270
"id": u.SessionID,
271
"uri": u.URI,
272
})
273
}, nil)
274
return err
275
}
276
return nil
277
}
278
279
func (d *CloudreveV4) GetArchiveMeta(ctx context.Context, obj model.Obj, args model.ArchiveArgs) (model.ArchiveMeta, error) {
280
// TODO get archive file meta-info, return errs.NotImplement to use an internal archive tool, optional
281
return nil, errs.NotImplement
282
}
283
284
func (d *CloudreveV4) ListArchive(ctx context.Context, obj model.Obj, args model.ArchiveInnerArgs) ([]model.Obj, error) {
285
// TODO list args.InnerPath in the archive obj, return errs.NotImplement to use an internal archive tool, optional
286
return nil, errs.NotImplement
287
}
288
289
func (d *CloudreveV4) Extract(ctx context.Context, obj model.Obj, args model.ArchiveInnerArgs) (*model.Link, error) {
290
// TODO return link of file args.InnerPath in the archive obj, return errs.NotImplement to use an internal archive tool, optional
291
return nil, errs.NotImplement
292
}
293
294
func (d *CloudreveV4) ArchiveDecompress(ctx context.Context, srcObj, dstDir model.Obj, args model.ArchiveDecompressArgs) ([]model.Obj, error) {
295
// TODO extract args.InnerPath path in the archive srcObj to the dstDir location, optional
296
// a folder with the same name as the archive file needs to be created to store the extracted results if args.PutIntoNewDir
297
// return errs.NotImplement to use an internal archive tool
298
return nil, errs.NotImplement
299
}
300
301
//func (d *CloudreveV4) Other(ctx context.Context, args model.OtherArgs) (interface{}, error) {
302
// return nil, errs.NotSupport
303
//}
304
305
var _ driver.Driver = (*CloudreveV4)(nil)
306
307