cloudreve-api 0.8.4

A Rust library for interacting with Cloudreve API
Documentation
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
291
292
293
294
295
296
297
298
299
300
301
302
303
304
305
306
307
308
309
310
311
312
313
314
315
316
317
318
319
320
321
322
323
324
325
326
327
328
329
330
331
332
333
334
335
336
337
338
339
340
341
342
343
344
345
346
347
348
349
350
351
352
353
354
355
356
357
358
359
360
361
362
363
364
365
366
367
368
369
370
371
372
373
374
375
376
377
378
379
380
381
382
383
384
385
386
387
388
389
390
391
392
393
394
395
396
397
398
399
400
401
402
403
404
405
406
407
408
409
410
411
412
413
414
415
416
417
418
419
420
421
422
423
424
425
426
427
428
429
430
431
432
433
434
435
436
437
438
439
440
441
442
443
444
445
446
447
448
449
450
451
452
453
454
455
456
457
458
459
460
461
462
463
464
465
466
467
468
469
470
471
472
473
474
475
476
477
478
479
480
481
482
483
484
485
486
487
488
489
490
491
492
493
494
495
496
497
498
499
500
501
502
503
504
505
506
507
508
509
510
511
512
513
514
515
516
517
518
519
520
521
522
523
524
525
526
527
528
529
530
531
532
533
534
535
536
537
538
539
540
541
542
543
544
545
546
547
548
549
//! Common data models for the Cloudreve API v3

use serde::{Deserialize, Serialize};
use serde_json::Value;

/// API response wrapper for v3 API
#[derive(Debug, Serialize, Deserialize)]
pub struct ApiResponse<T> {
    pub code: i32,
    pub msg: String,
    pub data: Option<T>,
}

/// User information for v3 API
#[derive(Debug, Serialize, Deserialize, Clone)]
pub struct User {
    pub id: String,
    pub user_name: String,
    pub nickname: String,
    #[serde(default)]
    pub status: i32,
    #[serde(default)]
    pub avatar: String,
    pub created_at: String,
    #[serde(default)]
    pub preferred_theme: String,
    #[serde(default)]
    pub anonymous: bool,
    pub group: UserGroup,
    #[serde(default)]
    pub tags: Vec<String>,
}

/// User group information for v3 API
#[derive(Debug, Serialize, Deserialize, Clone)]
pub struct UserGroup {
    #[serde(default)]
    pub id: i32,
    #[serde(default)]
    pub name: String,
    #[serde(default)]
    pub allow_share: bool,
    #[serde(default)]
    pub allow_remote_download: bool,
    #[serde(default)]
    pub allow_archive_download: bool,
    #[serde(default)]
    pub share_download: bool,
    #[serde(default)]
    pub compress: bool,
    #[serde(default)]
    pub webdav: bool,
    #[serde(default)]
    pub source_batch: i32,
    #[serde(default)]
    pub advance_delete: bool,
    #[serde(default)]
    pub allow_web_dav_proxy: bool,
}

/// File or folder object for v3 API
#[derive(Debug, Serialize, Deserialize, Clone)]
pub struct Object {
    pub id: String,
    pub name: String,
    pub path: String,
    pub thumb: bool,
    pub size: i64,
    #[serde(rename = "type")]
    pub object_type: String,
    pub date: String,
    pub create_date: String,
    pub source_enabled: bool,
}

/// Storage policy information for v3 API
#[derive(Debug, Serialize, Deserialize, Clone, Default)]
pub struct Policy {
    #[serde(default)]
    pub id: String,
    #[serde(default)]
    pub name: String,
    #[serde(rename = "type")]
    #[serde(default)]
    pub policy_type: String,
    #[serde(default)]
    pub max_size: i64,
    #[serde(default)]
    pub file_type: Option<Vec<String>>,
}

/// File or folder property for v3 API
#[derive(Debug, Serialize, Deserialize)]
pub struct Property {
    pub created_at: String,
    pub updated_at: String,
    pub policy: String,
    pub size: i64,
    pub child_folder_num: i32,
    pub child_file_num: i32,
    pub path: String,
    pub query_date: String,
}

/// Directory listing response for v3 API
#[derive(Debug, Serialize, Deserialize)]
pub struct DirectoryList {
    #[serde(default)]
    pub parent: String,
    #[serde(default)]
    pub objects: Vec<Object>,
    #[serde(default)]
    pub policy: Policy,
}

/// Upload session information for v3 API
#[derive(Debug, Serialize, Deserialize)]
pub struct UploadSession {
    #[serde(rename = "sessionID")]
    pub session_id: String,
    #[serde(rename = "chunkSize")]
    pub chunk_size: i64,
    pub expires: i64,
}

/// Upload file request for v3 API
#[derive(Debug, Serialize)]
pub struct UploadFileRequest<'a> {
    pub path: &'a str,
    pub size: i64,
    pub name: &'a str,
    pub policy_id: &'a str,
    /// 原文件修改时间,**Unix 毫秒**(服务端走 time.UnixMilli)。0 表示不设置,
    /// 由服务端用上传时刻。
    pub last_modified: i64,
    pub mime_type: &'a str,
}

/// Download file response for v3 API
#[derive(Debug, Serialize, Deserialize)]
pub struct DownloadUrl {
    pub url: String,
}

/// File source information for v3 API
#[derive(Debug, Deserialize)]
pub struct FileSource {
    pub url: String,
    pub name: String,
    pub parent: i64,
}

/// Storage information for v3 API
#[derive(Debug, Serialize, Deserialize)]
pub struct StorageInfo {
    pub used: i64,
    pub free: i64,
    pub total: i64,
}

/// Share link information for v3 API
#[derive(Debug, Deserialize, Default)]
pub struct Share {
    #[serde(default)]
    pub key: String,
    #[serde(default)]
    pub password: String,
    #[serde(default)]
    pub is_dir: bool,
    #[serde(default)]
    pub downloads: i64,
    #[serde(default)]
    pub remain_downloads: i64,
    #[serde(default)]
    pub expire: i64,
    #[serde(default)]
    pub preview: bool,
    #[serde(default)]
    pub owner_id: String,
    #[serde(default)]
    pub owner_nick: String,
    #[serde(default)]
    pub created_at: String,
}

/// Share request for v3 API
#[derive(Debug, Serialize)]
pub struct ShareRequest {
    pub id: String,
    pub is_dir: bool,
    pub password: String,
    pub downloads: i32,
    pub expire: i32,
    pub preview: bool,
}

/// Site configuration for v3 API
#[derive(Debug, Clone, Serialize, Deserialize, Default)]
pub struct SiteConfig {
    #[serde(default)]
    pub title: String,
    #[serde(default)]
    pub login_captcha: bool,
    #[serde(default)]
    pub reg_captcha: bool,
    #[serde(default)]
    pub forget_captcha: bool,
    #[serde(default)]
    pub email_active: bool,
    #[serde(default)]
    pub themes: String,
    #[serde(default)]
    pub default_theme: String,
    #[serde(default)]
    pub home_view_method: String,
    #[serde(default)]
    pub share_view_method: String,
    #[serde(default)]
    pub authn: bool,
    #[serde(default)]
    pub user: Option<User>,
    #[serde(default)]
    pub captcha_recaptcha_key: String,
    #[serde(default)]
    pub captcha_type: String,
    #[serde(default)]
    pub tcaptcha_captcha_app_id: String,
    #[serde(default)]
    pub register_enabled: bool,
    #[serde(default)]
    pub app_promotion: bool,
    #[serde(default)]
    pub wopi_exts: Option<Value>,
    #[serde(default)]
    pub direct_link_batch_size: i64,
}

/// Login request for v3 API
#[derive(Debug, Serialize)]
pub struct LoginRequest<'a> {
    #[serde(rename = "userName")]
    pub user_name: &'a str,
    #[serde(rename = "Password")]
    pub password: &'a str,
    #[serde(rename = "captchaCode")]
    pub captcha_code: Option<&'a str>,
}

/// CAPTCHA response for v3 API
#[derive(Debug, Deserialize)]
pub struct CaptchaResponse {
    pub image: String,
    pub ticket: String,
}

/// OTP login request for v3 API
#[derive(Debug, Serialize)]
pub struct OtpLoginRequest {
    pub code: String,
}

/// Create directory request for v3 API
#[derive(Debug, Serialize)]
pub struct CreateDirectoryRequest<'a> {
    pub path: &'a str,
}

/// Create file request for v3 API
#[derive(Debug, Serialize)]
pub struct CreateFileRequest<'a> {
    pub path: &'a str,
}

/// File source request for v3 API
#[derive(Debug, Serialize)]
pub struct FileSourceRequest {
    pub items: Vec<String>,
}

/// Rename object request for v3 API
#[derive(Debug, Serialize)]
pub struct RenameObjectRequest<'a> {
    pub action: &'a str,
    pub src: SourceItems<'a>,
    pub new_name: &'a str,
}

/// Source items for object operations
#[derive(Debug, Serialize)]
pub struct SourceItems<'a> {
    pub dirs: Vec<&'a str>,
    pub items: Vec<&'a str>,
}

/// Move object request for v3 API
#[derive(Debug, Serialize)]
pub struct MoveObjectRequest<'a> {
    pub action: &'a str,
    pub src_dir: &'a str,
    pub src: SourceItems<'a>,
    pub dst: &'a str,
}

/// Copy object request for v3 API
#[derive(Debug, Serialize)]
pub struct CopyObjectRequest<'a> {
    pub src_dir: &'a str,
    pub src: SourceItems<'a>,
    pub dst: &'a str,
}

/// Delete object request for v3 API
#[derive(Debug, Serialize)]
pub struct DeleteObjectRequest<'a> {
    pub items: Vec<&'a str>,
    pub dirs: Vec<&'a str>,
    pub force: bool,
    pub unlink: bool,
}

/// Object property request for v3 API
pub struct ObjectPropertyRequest<'a> {
    pub id: &'a str,
    pub is_folder: Option<bool>,
    pub trace_root: Option<bool>,
}

/// Aria2 download progress info as reported by aria2c JSON-RPC, surfaced verbatim by
/// Cloudreve v3 inside the `info` field of `/aria2/downloading` items. All fields are
/// strings because aria2 itself emits them that way; consumers typically parse on demand.
#[derive(Debug, Serialize, Deserialize, Clone, Default)]
pub struct Aria2DownloadInfo {
    #[serde(default)]
    pub gid: String,
    #[serde(default)]
    pub status: String,
    #[serde(default, rename = "totalLength")]
    pub total_length: String,
    #[serde(default, rename = "completedLength")]
    pub completed_length: String,
    #[serde(default, rename = "uploadLength")]
    pub upload_length: String,
    #[serde(default)]
    pub bitfield: String,
    #[serde(default, rename = "downloadSpeed")]
    pub download_speed: String,
    #[serde(default, rename = "uploadSpeed")]
    pub upload_speed: String,
    #[serde(default, rename = "infoHash")]
    pub info_hash: String,
    #[serde(default, rename = "numSeeders")]
    pub num_seeders: String,
    #[serde(default)]
    pub seeder: String,
    #[serde(default, rename = "pieceLength")]
    pub piece_length: String,
    #[serde(default, rename = "numPieces")]
    pub num_pieces: String,
    #[serde(default)]
    pub connections: String,
    #[serde(default, rename = "errorCode")]
    pub error_code: String,
    #[serde(default, rename = "errorMessage")]
    pub error_message: String,
    #[serde(default, rename = "belongsTo")]
    pub belongs_to: String,
    #[serde(default)]
    pub dir: String,
    #[serde(default)]
    pub files: Vec<Aria2File>,
    #[serde(default)]
    pub bittorrent: Option<serde_json::Value>,
}

/// One source URI inside an aria2 file entry. Cloudreve v3 echoes aria2's own shape.
#[derive(Debug, Serialize, Deserialize, Clone, Default)]
pub struct Aria2FileUri {
    #[serde(default)]
    pub uri: String,
    #[serde(default)]
    pub status: String,
}

/// One file inside an aria2 task. For multi-file torrents Cloudreve v3 may expose more
/// than one entry; for plain HTTP downloads there is exactly one.
#[derive(Debug, Serialize, Deserialize, Clone, Default)]
pub struct Aria2File {
    #[serde(default)]
    pub index: String,
    #[serde(default)]
    pub path: String,
    #[serde(default)]
    pub length: String,
    #[serde(default, rename = "completedLength")]
    pub completed_length: String,
    /// `selected` arrives as a JSON string ("true"/"false") from aria2c — Cloudreve passes
    /// it through unchanged — but in `/aria2/finished` Cloudreve sometimes coerces it to a
    /// real bool. Accept either form.
    #[serde(default, deserialize_with = "deserialize_loose_bool")]
    pub selected: bool,
    #[serde(default)]
    pub uris: Vec<Aria2FileUri>,
}

fn deserialize_loose_bool<'de, D>(deserializer: D) -> Result<bool, D::Error>
where
    D: serde::Deserializer<'de>,
{
    use serde::de::{Error, Unexpected};
    let value = Option::<serde_json::Value>::deserialize(deserializer)?;
    match value {
        None | Some(serde_json::Value::Null) => Ok(false),
        Some(serde_json::Value::Bool(b)) => Ok(b),
        Some(serde_json::Value::String(s)) => match s.trim().to_ascii_lowercase().as_str() {
            "true" | "1" | "yes" => Ok(true),
            "false" | "0" | "no" | "" => Ok(false),
            other => Err(D::Error::invalid_value(
                Unexpected::Str(other),
                &"a boolean or boolean-like string",
            )),
        },
        Some(serde_json::Value::Number(n)) => Ok(n.as_i64().map(|x| x != 0).unwrap_or(false)),
        Some(other) => Err(D::Error::invalid_type(
            Unexpected::Other(&other.to_string()),
            &"a boolean",
        )),
    }
}

/// In-flight aria2 task as returned by `GET /aria2/downloading`.
///
/// Cloudreve v3 returns rich progress here — `info` mirrors aria2's JSON-RPC payload,
/// while the top-level fields are pre-computed summaries the web UI uses for the row.
#[derive(Debug, Serialize, Deserialize, Clone, Default)]
pub struct Aria2DownloadingTask {
    #[serde(default)]
    pub update: String,
    #[serde(default)]
    pub interval: i64,
    #[serde(default)]
    pub name: String,
    /// Aria2 status as Cloudreve's internal int (see Cloudreve `aria2` constants:
    /// 0 ready, 1 downloading, 2 paused, 3 error, 4 complete, 5 canceled).
    #[serde(default)]
    pub status: i32,
    #[serde(default)]
    pub dst: String,
    #[serde(default)]
    pub total: i64,
    #[serde(default)]
    pub downloaded: i64,
    #[serde(default)]
    pub speed: i64,
    #[serde(default)]
    pub info: Aria2DownloadInfo,
    #[serde(default)]
    pub node: String,
}

/// Finished aria2 task as returned by `GET /aria2/finished`.
///
/// Cloudreve v3 emits a flattened representation here — there is no nested aria2 `info`,
/// instead the task lifecycle (`task_status`, `task_error`) and per-file completion are
/// surfaced directly.
#[derive(Debug, Serialize, Deserialize, Clone, Default)]
pub struct Aria2FinishedTask {
    #[serde(default)]
    pub name: String,
    #[serde(default)]
    pub gid: String,
    #[serde(default)]
    pub status: i32,
    #[serde(default)]
    pub dst: String,
    /// Legacy aria2-side error string. Prefer `task_error` for new code.
    #[serde(default)]
    pub error: String,
    #[serde(default)]
    pub total: i64,
    #[serde(default)]
    pub files: Vec<Aria2File>,
    /// Cloudreve task lifecycle status (matches the Aria2 status int set).
    #[serde(default)]
    pub task_status: i32,
    #[serde(default)]
    pub task_error: String,
    /// ISO-8601 creation timestamp.
    #[serde(default)]
    pub create: String,
    /// ISO-8601 last-update timestamp.
    #[serde(default)]
    pub update: String,
    #[serde(default)]
    pub node: String,
}

/// Aria2 create download request for v3 API
#[derive(Debug, Serialize)]
pub struct Aria2CreateRequest<'a> {
    pub dst: &'a str,
    pub url: Vec<&'a str>,
}

/// One entry of the user's task queue (`/user/setting/tasks`). Distinct from aria2 tasks:
/// covers compression, decompression, recycle, transfer, etc.
#[derive(Debug, Serialize, Deserialize, Clone, Default)]
pub struct UserTask {
    #[serde(default)]
    pub id: String,
    #[serde(default)]
    pub name: String,
    /// Cloudreve's `task.Status` int (0 queued, 1 processing, 2 error, 3 canceled, 4 complete).
    #[serde(default)]
    pub status: i32,
    /// Cloudreve's `task.Type` int (see Cloudreve `models/task`).
    #[serde(default, rename = "type")]
    pub task_type: i32,
    #[serde(default)]
    pub create_date: String,
    #[serde(default)]
    pub progress: i64,
    /// JSON-encoded error payload (Cloudreve's own format: `{"msg":"...","error":"..."}`).
    #[serde(default)]
    pub error: String,
}

/// Paginated response for `/user/setting/tasks`.
#[derive(Debug, Serialize, Deserialize, Clone, Default)]
pub struct UserTaskList {
    #[serde(default)]
    pub tasks: Vec<UserTask>,
    #[serde(default)]
    pub total: i64,
}

/// WebDAV account information for v3 API
#[derive(Debug, Deserialize)]
pub struct WebdavAccount {
    #[serde(rename = "ID")]
    pub id: i32,
    #[serde(rename = "Name")]
    pub name: String,
    #[serde(rename = "Root")]
    pub uri: String,
    #[serde(rename = "Password")]
    pub password: String,
    #[serde(rename = "CreatedAt")]
    pub created_at: String,
}