cloudreve_api/api/v4/
file.rs

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