cloudreve_api/api/v4/
file.rs

1//! File-related API endpoints for Cloudreve v4 API
2
3use crate::Error;
4use crate::api::v4::ApiV4Client;
5use crate::api::v4::models::*;
6use crate::api::v4::uri::*;
7
8/// File management methods
9impl ApiV4Client {
10    pub async fn upload_file(&self, request: &UploadRequest<'_>) -> Result<File, Error> {
11        let response: ApiResponse<File> = self.post("/file", request).await?;
12        match response.data {
13            Some(data) => Ok(data),
14            None => Err(Error::InvalidResponse(format!(
15                "API returned no data for upload_file request: {:?}",
16                response
17            ))),
18        }
19    }
20
21    /// Lists files in a directory with full response including metadata
22    ///
23    /// Returns a complete `ListResponse` containing:
24    /// - `files`: Vector of files/folders in the directory
25    /// - `parent`: Information about the parent directory
26    /// - `pagination`: Pagination information (page, total_items, etc.)
27    /// - `storage_policy`: Preferred storage policy for uploads to this directory
28    /// - `props`: Navigator capabilities and settings
29    ///
30    /// # Arguments
31    /// * `request` - ListFilesRequest with path and optional pagination params
32    pub async fn list_files(&self, request: &ListFilesRequest<'_>) -> Result<ListResponse, Error> {
33        let mut url = "/file".to_string();
34        if let Some(path) = request.path.strip_prefix('/') {
35            url.push_str(&format!("?uri={}", path_to_uri(path)));
36        } else {
37            url.push_str(&format!("?uri={}", path_to_uri(request.path)));
38        }
39        if let Some(page) = request.page {
40            url.push_str(&format!("&page={}", page));
41        }
42        if let Some(page_size) = request.page_size {
43            url.push_str(&format!("&page_size={}", page_size));
44        }
45        if let Some(order_by) = request.order_by {
46            url.push_str(&format!("&order_by={}", order_by));
47        }
48        if let Some(order_direction) = request.order_direction {
49            url.push_str(&format!("&order_direction={}", order_direction));
50        }
51        if let Some(next_page_token) = request.next_page_token {
52            url.push_str(&format!("&next_page_token={}", next_page_token));
53        }
54
55        let response: ApiResponse<ListResponse> = self.get(&url).await?;
56        match response.data {
57            Some(data) => Ok(data),
58            None => Err(Error::InvalidResponse(format!(
59                "API returned no data for list_files request: {:?}",
60                response
61            ))),
62        }
63    }
64
65    pub async fn get_file_info(&self, file_path: &str) -> Result<File, Error> {
66        let response: ApiResponse<File> = self.get(&format!("/file/{}", file_path)).await?;
67        match response.data {
68            Some(data) => Ok(data),
69            None => Err(Error::InvalidResponse(format!(
70                "API returned no data for get_file_info request: {:?}",
71                response
72            ))),
73        }
74    }
75
76    pub async fn get_file_stat(&self, file_path: &str) -> Result<FileStat, Error> {
77        let response: ApiResponse<FileStat> =
78            self.get(&format!("/file/stat/{}", file_path)).await?;
79        match response.data {
80            Some(data) => Ok(data),
81            None => Err(Error::InvalidResponse(format!(
82                "API returned no data for get_file_stat request: {:?}",
83                response
84            ))),
85        }
86    }
87
88    pub async fn move_file(&self, request: &MoveFileRequest<'_>) -> Result<(), Error> {
89        let _: ApiResponse<()> = self.post("/file/move", request).await?;
90        Ok(())
91    }
92
93    pub async fn copy_file(&self, request: &CopyFileRequest<'_>) -> Result<(), Error> {
94        let _: ApiResponse<()> = self.post("/file/copy", request).await?;
95        Ok(())
96    }
97
98    pub async fn rename_file(
99        &self,
100        file_path: &str,
101        request: &RenameFileRequest<'_>,
102    ) -> Result<(), Error> {
103        let _: ApiResponse<()> = self
104            .put(&format!("/file/rename/{}", file_path), request)
105            .await?;
106        Ok(())
107    }
108
109    pub async fn delete_file(&self, file_path: &str) -> Result<(), Error> {
110        let _: ApiResponse<()> = self.delete(&format!("/file/{}", file_path)).await?;
111        Ok(())
112    }
113
114    pub async fn create_directory(&self, path: &str) -> Result<(), Error> {
115        // Convert path to URI format
116        let uri = path_to_uri(path);
117
118        // Use the correct /file/create endpoint
119        let request = serde_json::json!({
120            "uri": uri,
121            "type": "folder"
122        });
123
124        let response: ApiResponse<serde_json::Value> = self.post("/file/create", &request).await?;
125        match response.code {
126            0 => Ok(()),
127            code => Err(Error::Api {
128                code,
129                message: response.msg,
130            }),
131        }
132    }
133
134    pub async fn set_file_permission(
135        &self,
136        request: &SetFilePermissionRequest<'_>,
137    ) -> Result<(), Error> {
138        let _: ApiResponse<()> = self.post("/file/permission", request).await?;
139        Ok(())
140    }
141
142    pub async fn delete_file_permission(&self, path: &str) -> Result<(), Error> {
143        let uri = path_to_uri(path);
144        let _: ApiResponse<()> = self
145            .delete(&format!("/file/permission?uri={}", uri))
146            .await?;
147        Ok(())
148    }
149
150    pub async fn create_upload_session(
151        &self,
152        request: &CreateUploadSessionRequest<'_>,
153    ) -> Result<UploadSessionResponse, Error> {
154        let response: ApiResponse<UploadSessionResponse> =
155            self.put("/file/upload", request).await?;
156        match response.data {
157            Some(data) => Ok(data),
158            None => Err(Error::InvalidResponse(format!(
159                "API returned no data for create_upload_session request: {:?}",
160                response
161            ))),
162        }
163    }
164
165    pub async fn upload_file_chunk(
166        &self,
167        session_id: &str,
168        index: u32,
169        chunk_data: &[u8],
170    ) -> Result<(), Error> {
171        let url = format!("/file/upload/{}/{}", session_id, index);
172        let full_url = self.get_url(&url);
173
174        let mut request = self.http_client.post(&full_url).body(chunk_data.to_vec());
175
176        if let Some(token) = &self.token {
177            request = request.bearer_auth(token);
178        }
179
180        let response = request.send().await?;
181        let status = response.status();
182
183        if !status.is_success() {
184            let error_text = response
185                .text()
186                .await
187                .unwrap_or_else(|_| "Unknown error".to_string());
188            return Err(Error::Api {
189                code: status.as_u16() as i32,
190                message: error_text,
191            });
192        }
193
194        Ok(())
195    }
196
197    pub async fn delete_upload_session(&self, path: &str, session_id: &str) -> Result<(), Error> {
198        let uri = path_to_uri(path);
199        let request = DeleteUploadSessionRequest {
200            id: session_id,
201            uri: &uri,
202        };
203        let url = self.get_url("/file/upload");
204
205        let body = serde_json::to_string(&request)?;
206
207        let mut http_req = self.http_client.delete(&url);
208        if let Some(token) = &self.token {
209            http_req = http_req.bearer_auth(token);
210        }
211
212        let response = http_req
213            .header("Content-Type", "application/json")
214            .body(body)
215            .send()
216            .await?;
217
218        let status = response.status();
219        if !status.is_success() {
220            let error_text = response
221                .text()
222                .await
223                .unwrap_or_else(|_| "Unknown error".to_string());
224            return Err(Error::Api {
225                code: status.as_u16() as i32,
226                message: error_text,
227            });
228        }
229
230        Ok(())
231    }
232
233    pub async fn get_thumbnail_url(
234        &self,
235        path: &str,
236        width: Option<u32>,
237        height: Option<u32>,
238    ) -> Result<String, Error> {
239        let uri = path_to_uri(path);
240        let mut url = format!("/file/thumb?uri={}", uri);
241        if let Some(w) = width {
242            url.push_str(&format!("&width={}", w));
243        }
244        if let Some(h) = height {
245            url.push_str(&format!("&height={}", h));
246        }
247
248        let response: ApiResponse<String> = self.get(&url).await?;
249        match response.data {
250            Some(data) => Ok(data),
251            None => Err(Error::InvalidResponse(format!(
252                "API returned no data for get_thumbnail_url request: {:?}",
253                response
254            ))),
255        }
256    }
257
258    pub async fn get_file_content(&self, path: &str) -> Result<String, Error> {
259        let uri = path_to_uri(path);
260        let response: ApiResponse<String> = self.get(&format!("/file/content?uri={}", uri)).await?;
261        match response.data {
262            Some(data) => Ok(data),
263            None => Err(Error::InvalidResponse(format!(
264                "API returned no data for get_file_content request: {:?}",
265                response
266            ))),
267        }
268    }
269
270    pub async fn update_file_content(
271        &self,
272        request: &UpdateFileContentRequest<'_>,
273    ) -> Result<(), Error> {
274        let _: ApiResponse<()> = self.put("/file/content", request).await?;
275        Ok(())
276    }
277
278    pub async fn create_viewer_session(
279        &self,
280        request: &CreateViewerSessionRequest<'_>,
281    ) -> Result<ViewerSessionResponse, Error> {
282        let response: ApiResponse<ViewerSessionResponse> =
283            self.put("/file/viewerSession", request).await?;
284        match response.data {
285            Some(data) => Ok(data),
286            None => Err(Error::InvalidResponse(format!(
287                "API returned no data for create_viewer_session request: {:?}",
288                response
289            ))),
290        }
291    }
292
293    pub async fn create_file(&self, request: &CreateFileRequest<'_>) -> Result<File, Error> {
294        let response: ApiResponse<File> = self.post("/file/create", request).await?;
295        match response.data {
296            Some(data) => Ok(data),
297            None => Err(Error::InvalidResponse(format!(
298                "API returned no data for create_file request: {:?}",
299                response
300            ))),
301        }
302    }
303
304    pub async fn rename_multiple(&self, request: &RenameMultipleRequest<'_>) -> Result<(), Error> {
305        let _: ApiResponse<()> = self.post("/file/rename", request).await?;
306        Ok(())
307    }
308
309    pub async fn move_copy_files(&self, request: &MoveCopyFileRequest<'_>) -> Result<(), Error> {
310        let _: ApiResponse<()> = self.post("/file/move", request).await?;
311        Ok(())
312    }
313
314    pub async fn create_download_url(
315        &self,
316        request: &CreateDownloadUrlRequest<'_>,
317    ) -> Result<DownloadUrlResponse, Error> {
318        let uris = paths_to_uris(&request.uris);
319        let uris_refs: Vec<&str> = uris.iter().map(|s| s.as_str()).collect();
320
321        let converted_request = CreateDownloadUrlRequest {
322            uris: uris_refs,
323            download: request.download,
324            redirect: request.redirect,
325            entity: request.entity,
326            use_primary_site_url: request.use_primary_site_url,
327            skip_error: request.skip_error,
328            archive: request.archive,
329            no_cache: request.no_cache,
330        };
331
332        let response: ApiResponse<DownloadUrlResponse> =
333            self.post("/file/url", &converted_request).await?;
334        match response.data {
335            Some(data) => Ok(data),
336            None => Err(Error::InvalidResponse(format!(
337                "API returned no data for create_download_url request: {:?}",
338                response
339            ))),
340        }
341    }
342
343    pub async fn restore_from_trash(&self, request: &RestoreFileRequest<'_>) -> Result<(), Error> {
344        let uris = paths_to_uris(&request.uris);
345        let uris_refs: Vec<&str> = uris.iter().map(|s| s.as_str()).collect();
346
347        let converted_request = RestoreFileRequest { uris: uris_refs };
348
349        let _: ApiResponse<()> = self.post("/file/restore", &converted_request).await?;
350        Ok(())
351    }
352
353    pub async fn force_unlock(&self, path: &str) -> Result<(), Error> {
354        let uri = path_to_uri(path);
355        let _: ApiResponse<()> = self.delete(&format!("/file/lock?uri={}", uri)).await?;
356        Ok(())
357    }
358
359    pub async fn patch_metadata(
360        &self,
361        path: &str,
362        request: &UpdateMetadataRequest,
363    ) -> Result<(), Error> {
364        let uri = path_to_uri(path);
365        let full_url = format!("/file/metadata?uri={}", uri);
366        let _: ApiResponse<()> = self.patch(&full_url, request).await?;
367        Ok(())
368    }
369
370    pub async fn mount_storage_policy(
371        &self,
372        path: &str,
373        request: &MountStoragePolicyRequest,
374    ) -> Result<(), Error> {
375        let uri = path_to_uri(path);
376        let full_url = format!("/file/policy?uri={}", uri);
377        let _: ApiResponse<()> = self.patch(&full_url, request).await?;
378        Ok(())
379    }
380
381    pub async fn update_view_settings(
382        &self,
383        path: &str,
384        request: &UpdateViewRequest,
385    ) -> Result<(), Error> {
386        let uri = path_to_uri(path);
387        let full_url = format!("/file/view?uri={}", uri);
388        let _: ApiResponse<()> = self.patch(&full_url, request).await?;
389        Ok(())
390    }
391
392    pub async fn get_file_activities(
393        &self,
394        path: &str,
395        page: Option<u32>,
396        page_size: Option<u32>,
397    ) -> Result<FileActivitiesResponse, Error> {
398        let uri = path_to_uri(path);
399        let mut url = format!("/file/activities?uri={}", uri);
400        if let Some(p) = page {
401            url.push_str(&format!("&page={}", p));
402        }
403        if let Some(ps) = page_size {
404            url.push_str(&format!("&page_size={}", ps));
405        }
406
407        let response: ApiResponse<FileActivitiesResponse> = self.get(&url).await?;
408        match response.data {
409            Some(data) => Ok(data),
410            None => Err(Error::InvalidResponse(format!(
411                "API returned no data for get_file_activities request: {:?}",
412                response
413            ))),
414        }
415    }
416
417    pub async fn get_file_info_extended(
418        &self,
419        request: &GetFileInfoRequest<'_>,
420    ) -> Result<File, Error> {
421        let uri = path_to_uri(request.uri);
422        let mut url = format!("/file/info?uri={}", uri);
423        if let Some(include_extended) = request.include_extended_info {
424            url.push_str(&format!("&extended={}", include_extended));
425        }
426
427        let response: ApiResponse<File> = self.get(&url).await?;
428        match response.data {
429            Some(data) => Ok(data),
430            None => Err(Error::InvalidResponse(format!(
431                "API returned no data for get_file_info_extended request: {:?}",
432                response
433            ))),
434        }
435    }
436
437    pub async fn get_archive_list(
438        &self,
439        request: &GetArchiveListRequest<'_>,
440    ) -> Result<ArchiveListResponse, Error> {
441        let uri = path_to_uri(request.uri);
442        let url = format!("/file/archive?uri={}", uri);
443
444        let response: ApiResponse<ArchiveListResponse> = self.get(&url).await?;
445        match response.data {
446            Some(data) => Ok(data),
447            None => Err(Error::InvalidResponse(format!(
448                "API returned no data for get_archive_list request: {:?}",
449                response
450            ))),
451        }
452    }
453}