Skip to main content

cloudreve_api/api/v3/
models.rs

1//! Common data models for the Cloudreve API v3
2
3use serde::{Deserialize, Serialize};
4use serde_json::Value;
5
6/// API response wrapper for v3 API
7#[derive(Debug, Serialize, Deserialize)]
8pub struct ApiResponse<T> {
9    pub code: i32,
10    pub msg: String,
11    pub data: Option<T>,
12}
13
14/// User information for v3 API
15#[derive(Debug, Serialize, Deserialize, Clone)]
16pub struct User {
17    pub id: String,
18    pub user_name: String,
19    pub nickname: String,
20    #[serde(default)]
21    pub status: i32,
22    #[serde(default)]
23    pub avatar: String,
24    pub created_at: String,
25    #[serde(default)]
26    pub preferred_theme: String,
27    #[serde(default)]
28    pub anonymous: bool,
29    pub group: UserGroup,
30    #[serde(default)]
31    pub tags: Vec<String>,
32}
33
34/// User group information for v3 API
35#[derive(Debug, Serialize, Deserialize, Clone)]
36pub struct UserGroup {
37    #[serde(default)]
38    pub id: i32,
39    #[serde(default)]
40    pub name: String,
41    #[serde(default)]
42    pub allow_share: bool,
43    #[serde(default)]
44    pub allow_remote_download: bool,
45    #[serde(default)]
46    pub allow_archive_download: bool,
47    #[serde(default)]
48    pub share_download: bool,
49    #[serde(default)]
50    pub compress: bool,
51    #[serde(default)]
52    pub webdav: bool,
53    #[serde(default)]
54    pub source_batch: i32,
55    #[serde(default)]
56    pub advance_delete: bool,
57    #[serde(default)]
58    pub allow_web_dav_proxy: bool,
59}
60
61/// File or folder object for v3 API
62#[derive(Debug, Serialize, Deserialize, Clone)]
63pub struct Object {
64    pub id: String,
65    pub name: String,
66    pub path: String,
67    pub thumb: bool,
68    pub size: i64,
69    #[serde(rename = "type")]
70    pub object_type: String,
71    pub date: String,
72    pub create_date: String,
73    pub source_enabled: bool,
74}
75
76/// Storage policy information for v3 API
77#[derive(Debug, Serialize, Deserialize, Clone, Default)]
78pub struct Policy {
79    #[serde(default)]
80    pub id: String,
81    #[serde(default)]
82    pub name: String,
83    #[serde(rename = "type")]
84    #[serde(default)]
85    pub policy_type: String,
86    #[serde(default)]
87    pub max_size: i64,
88    #[serde(default)]
89    pub file_type: Option<Vec<String>>,
90}
91
92/// File or folder property for v3 API
93#[derive(Debug, Serialize, Deserialize)]
94pub struct Property {
95    pub created_at: String,
96    pub updated_at: String,
97    pub policy: String,
98    pub size: i64,
99    pub child_folder_num: i32,
100    pub child_file_num: i32,
101    pub path: String,
102    pub query_date: String,
103}
104
105/// Directory listing response for v3 API
106#[derive(Debug, Serialize, Deserialize)]
107pub struct DirectoryList {
108    #[serde(default)]
109    pub parent: String,
110    #[serde(default)]
111    pub objects: Vec<Object>,
112    #[serde(default)]
113    pub policy: Policy,
114}
115
116/// Upload session information for v3 API
117#[derive(Debug, Serialize, Deserialize)]
118pub struct UploadSession {
119    #[serde(rename = "sessionID")]
120    pub session_id: String,
121    #[serde(rename = "chunkSize")]
122    pub chunk_size: i64,
123    pub expires: i64,
124}
125
126/// Upload file request for v3 API
127#[derive(Debug, Serialize)]
128pub struct UploadFileRequest<'a> {
129    pub path: &'a str,
130    pub size: i64,
131    pub name: &'a str,
132    pub policy_id: &'a str,
133    pub last_modified: i64,
134    pub mime_type: &'a str,
135}
136
137/// Download file response for v3 API
138#[derive(Debug, Serialize, Deserialize)]
139pub struct DownloadUrl {
140    pub url: String,
141}
142
143/// File source information for v3 API
144#[derive(Debug, Deserialize)]
145pub struct FileSource {
146    pub url: String,
147    pub name: String,
148    pub parent: i64,
149}
150
151/// Storage information for v3 API
152#[derive(Debug, Serialize, Deserialize)]
153pub struct StorageInfo {
154    pub used: i64,
155    pub free: i64,
156    pub total: i64,
157}
158
159/// Share link information for v3 API
160#[derive(Debug, Deserialize, Default)]
161pub struct Share {
162    #[serde(default)]
163    pub key: String,
164    #[serde(default)]
165    pub password: String,
166    #[serde(default)]
167    pub is_dir: bool,
168    #[serde(default)]
169    pub downloads: i64,
170    #[serde(default)]
171    pub remain_downloads: i64,
172    #[serde(default)]
173    pub expire: i64,
174    #[serde(default)]
175    pub preview: bool,
176    #[serde(default)]
177    pub owner_id: String,
178    #[serde(default)]
179    pub owner_nick: String,
180    #[serde(default)]
181    pub created_at: String,
182}
183
184/// Share request for v3 API
185#[derive(Debug, Serialize)]
186pub struct ShareRequest {
187    pub id: String,
188    pub is_dir: bool,
189    pub password: String,
190    pub downloads: i32,
191    pub expire: i32,
192    pub preview: bool,
193}
194
195/// Site configuration for v3 API
196#[derive(Debug, Clone, Serialize, Deserialize, Default)]
197pub struct SiteConfig {
198    #[serde(default)]
199    pub title: String,
200    #[serde(default)]
201    pub login_captcha: bool,
202    #[serde(default)]
203    pub reg_captcha: bool,
204    #[serde(default)]
205    pub forget_captcha: bool,
206    #[serde(default)]
207    pub email_active: bool,
208    #[serde(default)]
209    pub themes: String,
210    #[serde(default)]
211    pub default_theme: String,
212    #[serde(default)]
213    pub home_view_method: String,
214    #[serde(default)]
215    pub share_view_method: String,
216    #[serde(default)]
217    pub authn: bool,
218    #[serde(default)]
219    pub user: Option<User>,
220    #[serde(default)]
221    pub captcha_recaptcha_key: String,
222    #[serde(default)]
223    pub captcha_type: String,
224    #[serde(default)]
225    pub tcaptcha_captcha_app_id: String,
226    #[serde(default)]
227    pub register_enabled: bool,
228    #[serde(default)]
229    pub app_promotion: bool,
230    #[serde(default)]
231    pub wopi_exts: Option<Value>,
232    #[serde(default)]
233    pub direct_link_batch_size: i64,
234}
235
236/// Login request for v3 API
237#[derive(Debug, Serialize)]
238pub struct LoginRequest<'a> {
239    #[serde(rename = "userName")]
240    pub user_name: &'a str,
241    #[serde(rename = "Password")]
242    pub password: &'a str,
243    #[serde(rename = "captchaCode")]
244    pub captcha_code: Option<&'a str>,
245}
246
247/// CAPTCHA response for v3 API
248#[derive(Debug, Deserialize)]
249pub struct CaptchaResponse {
250    pub image: String,
251    pub ticket: String,
252}
253
254/// OTP login request for v3 API
255#[derive(Debug, Serialize)]
256pub struct OtpLoginRequest {
257    pub code: String,
258}
259
260/// Create directory request for v3 API
261#[derive(Debug, Serialize)]
262pub struct CreateDirectoryRequest<'a> {
263    pub path: &'a str,
264}
265
266/// Create file request for v3 API
267#[derive(Debug, Serialize)]
268pub struct CreateFileRequest<'a> {
269    pub path: &'a str,
270}
271
272/// File source request for v3 API
273#[derive(Debug, Serialize)]
274pub struct FileSourceRequest {
275    pub items: Vec<String>,
276}
277
278/// Rename object request for v3 API
279#[derive(Debug, Serialize)]
280pub struct RenameObjectRequest<'a> {
281    pub action: &'a str,
282    pub src: SourceItems<'a>,
283    pub new_name: &'a str,
284}
285
286/// Source items for object operations
287#[derive(Debug, Serialize)]
288pub struct SourceItems<'a> {
289    pub dirs: Vec<&'a str>,
290    pub items: Vec<&'a str>,
291}
292
293/// Move object request for v3 API
294#[derive(Debug, Serialize)]
295pub struct MoveObjectRequest<'a> {
296    pub action: &'a str,
297    pub src_dir: &'a str,
298    pub src: SourceItems<'a>,
299    pub dst: &'a str,
300}
301
302/// Copy object request for v3 API
303#[derive(Debug, Serialize)]
304pub struct CopyObjectRequest<'a> {
305    pub src_dir: &'a str,
306    pub src: SourceItems<'a>,
307    pub dst: &'a str,
308}
309
310/// Delete object request for v3 API
311#[derive(Debug, Serialize)]
312pub struct DeleteObjectRequest<'a> {
313    pub items: Vec<&'a str>,
314    pub dirs: Vec<&'a str>,
315    pub force: bool,
316    pub unlink: bool,
317}
318
319/// Object property request for v3 API
320pub struct ObjectPropertyRequest<'a> {
321    pub id: &'a str,
322    pub is_folder: Option<bool>,
323    pub trace_root: Option<bool>,
324}
325
326/// Aria2 download progress info as reported by aria2c JSON-RPC, surfaced verbatim by
327/// Cloudreve v3 inside the `info` field of `/aria2/downloading` items. All fields are
328/// strings because aria2 itself emits them that way; consumers typically parse on demand.
329#[derive(Debug, Serialize, Deserialize, Clone, Default)]
330pub struct Aria2DownloadInfo {
331    #[serde(default)]
332    pub gid: String,
333    #[serde(default)]
334    pub status: String,
335    #[serde(default, rename = "totalLength")]
336    pub total_length: String,
337    #[serde(default, rename = "completedLength")]
338    pub completed_length: String,
339    #[serde(default, rename = "uploadLength")]
340    pub upload_length: String,
341    #[serde(default)]
342    pub bitfield: String,
343    #[serde(default, rename = "downloadSpeed")]
344    pub download_speed: String,
345    #[serde(default, rename = "uploadSpeed")]
346    pub upload_speed: String,
347    #[serde(default, rename = "infoHash")]
348    pub info_hash: String,
349    #[serde(default, rename = "numSeeders")]
350    pub num_seeders: String,
351    #[serde(default)]
352    pub seeder: String,
353    #[serde(default, rename = "pieceLength")]
354    pub piece_length: String,
355    #[serde(default, rename = "numPieces")]
356    pub num_pieces: String,
357    #[serde(default)]
358    pub connections: String,
359    #[serde(default, rename = "errorCode")]
360    pub error_code: String,
361    #[serde(default, rename = "errorMessage")]
362    pub error_message: String,
363    #[serde(default, rename = "belongsTo")]
364    pub belongs_to: String,
365    #[serde(default)]
366    pub dir: String,
367    #[serde(default)]
368    pub files: Vec<Aria2File>,
369    #[serde(default)]
370    pub bittorrent: Option<serde_json::Value>,
371}
372
373/// One source URI inside an aria2 file entry. Cloudreve v3 echoes aria2's own shape.
374#[derive(Debug, Serialize, Deserialize, Clone, Default)]
375pub struct Aria2FileUri {
376    #[serde(default)]
377    pub uri: String,
378    #[serde(default)]
379    pub status: String,
380}
381
382/// One file inside an aria2 task. For multi-file torrents Cloudreve v3 may expose more
383/// than one entry; for plain HTTP downloads there is exactly one.
384#[derive(Debug, Serialize, Deserialize, Clone, Default)]
385pub struct Aria2File {
386    #[serde(default)]
387    pub index: String,
388    #[serde(default)]
389    pub path: String,
390    #[serde(default)]
391    pub length: String,
392    #[serde(default, rename = "completedLength")]
393    pub completed_length: String,
394    /// `selected` arrives as a JSON string ("true"/"false") from aria2c — Cloudreve passes
395    /// it through unchanged — but in `/aria2/finished` Cloudreve sometimes coerces it to a
396    /// real bool. Accept either form.
397    #[serde(default, deserialize_with = "deserialize_loose_bool")]
398    pub selected: bool,
399    #[serde(default)]
400    pub uris: Vec<Aria2FileUri>,
401}
402
403fn deserialize_loose_bool<'de, D>(deserializer: D) -> Result<bool, D::Error>
404where
405    D: serde::Deserializer<'de>,
406{
407    use serde::de::{Error, Unexpected};
408    let value = Option::<serde_json::Value>::deserialize(deserializer)?;
409    match value {
410        None | Some(serde_json::Value::Null) => Ok(false),
411        Some(serde_json::Value::Bool(b)) => Ok(b),
412        Some(serde_json::Value::String(s)) => match s.trim().to_ascii_lowercase().as_str() {
413            "true" | "1" | "yes" => Ok(true),
414            "false" | "0" | "no" | "" => Ok(false),
415            other => Err(D::Error::invalid_value(
416                Unexpected::Str(other),
417                &"a boolean or boolean-like string",
418            )),
419        },
420        Some(serde_json::Value::Number(n)) => Ok(n.as_i64().map(|x| x != 0).unwrap_or(false)),
421        Some(other) => Err(D::Error::invalid_type(
422            Unexpected::Other(&other.to_string()),
423            &"a boolean",
424        )),
425    }
426}
427
428/// In-flight aria2 task as returned by `GET /aria2/downloading`.
429///
430/// Cloudreve v3 returns rich progress here — `info` mirrors aria2's JSON-RPC payload,
431/// while the top-level fields are pre-computed summaries the web UI uses for the row.
432#[derive(Debug, Serialize, Deserialize, Clone, Default)]
433pub struct Aria2DownloadingTask {
434    #[serde(default)]
435    pub update: String,
436    #[serde(default)]
437    pub interval: i64,
438    #[serde(default)]
439    pub name: String,
440    /// Aria2 status as Cloudreve's internal int (see Cloudreve `aria2` constants:
441    /// 0 ready, 1 downloading, 2 paused, 3 error, 4 complete, 5 canceled).
442    #[serde(default)]
443    pub status: i32,
444    #[serde(default)]
445    pub dst: String,
446    #[serde(default)]
447    pub total: i64,
448    #[serde(default)]
449    pub downloaded: i64,
450    #[serde(default)]
451    pub speed: i64,
452    #[serde(default)]
453    pub info: Aria2DownloadInfo,
454    #[serde(default)]
455    pub node: String,
456}
457
458/// Finished aria2 task as returned by `GET /aria2/finished`.
459///
460/// Cloudreve v3 emits a flattened representation here — there is no nested aria2 `info`,
461/// instead the task lifecycle (`task_status`, `task_error`) and per-file completion are
462/// surfaced directly.
463#[derive(Debug, Serialize, Deserialize, Clone, Default)]
464pub struct Aria2FinishedTask {
465    #[serde(default)]
466    pub name: String,
467    #[serde(default)]
468    pub gid: String,
469    #[serde(default)]
470    pub status: i32,
471    #[serde(default)]
472    pub dst: String,
473    /// Legacy aria2-side error string. Prefer `task_error` for new code.
474    #[serde(default)]
475    pub error: String,
476    #[serde(default)]
477    pub total: i64,
478    #[serde(default)]
479    pub files: Vec<Aria2File>,
480    /// Cloudreve task lifecycle status (matches the Aria2 status int set).
481    #[serde(default)]
482    pub task_status: i32,
483    #[serde(default)]
484    pub task_error: String,
485    /// ISO-8601 creation timestamp.
486    #[serde(default)]
487    pub create: String,
488    /// ISO-8601 last-update timestamp.
489    #[serde(default)]
490    pub update: String,
491    #[serde(default)]
492    pub node: String,
493}
494
495/// Aria2 create download request for v3 API
496#[derive(Debug, Serialize)]
497pub struct Aria2CreateRequest<'a> {
498    pub dst: &'a str,
499    pub url: Vec<&'a str>,
500}
501
502/// One entry of the user's task queue (`/user/setting/tasks`). Distinct from aria2 tasks:
503/// covers compression, decompression, recycle, transfer, etc.
504#[derive(Debug, Serialize, Deserialize, Clone, Default)]
505pub struct UserTask {
506    #[serde(default)]
507    pub id: String,
508    #[serde(default)]
509    pub name: String,
510    /// Cloudreve's `task.Status` int (0 queued, 1 processing, 2 error, 3 canceled, 4 complete).
511    #[serde(default)]
512    pub status: i32,
513    /// Cloudreve's `task.Type` int (see Cloudreve `models/task`).
514    #[serde(default, rename = "type")]
515    pub task_type: i32,
516    #[serde(default)]
517    pub create_date: String,
518    #[serde(default)]
519    pub progress: i64,
520    /// JSON-encoded error payload (Cloudreve's own format: `{"msg":"...","error":"..."}`).
521    #[serde(default)]
522    pub error: String,
523}
524
525/// Paginated response for `/user/setting/tasks`.
526#[derive(Debug, Serialize, Deserialize, Clone, Default)]
527pub struct UserTaskList {
528    #[serde(default)]
529    pub tasks: Vec<UserTask>,
530    #[serde(default)]
531    pub total: i64,
532}
533
534/// WebDAV account information for v3 API
535#[derive(Debug, Deserialize)]
536pub struct WebdavAccount {
537    #[serde(rename = "ID")]
538    pub id: i32,
539    #[serde(rename = "Name")]
540    pub name: String,
541    #[serde(rename = "Root")]
542    pub uri: String,
543    #[serde(rename = "Password")]
544    pub password: String,
545    #[serde(rename = "CreatedAt")]
546    pub created_at: String,
547}