Skip to main content

cloudreve_api/api/v3/
site.rs

1//! Site-related API endpoints for Cloudreve API v3
2
3use crate::Error;
4use crate::VERSION;
5use crate::api::VersionInfo;
6use crate::api::v3::ApiV3Client;
7use crate::api::v3::models::*;
8use log::debug;
9
10impl ApiV3Client {
11    /// Get site configuration
12    pub async fn get_site_config(&self) -> Result<SiteConfig, Error> {
13        let response: ApiResponse<SiteConfig> = self.get("/site/config").await?;
14        match response.data {
15            Some(config) => Ok(config),
16            None => Err(Error::Api {
17                code: response.code,
18                message: response.msg,
19            }),
20        }
21    }
22
23    /// Get CAPTCHA for login
24    pub async fn get_captcha(&mut self) -> Result<CaptchaResponse, Error> {
25        let url = self.get_url("/site/captcha");
26        let mut request = self.http_client.get(&url);
27
28        // Send existing session cookie if available
29        if let Some(cookie) = &self.session_cookie {
30            request = request.header("Cookie", format!("cloudreve-session={}", cookie));
31            debug!(
32                "Sending cookie with CAPTCHA request: {}...",
33                &cookie[..cookie.len().min(20)]
34            );
35        }
36
37        debug!("GET URL: {}", url);
38
39        let response = request.send().await?;
40
41        // Extract session cookie from Set-Cookie headers BEFORE consuming the response
42        let cookie_headers = response.headers().get_all("Set-Cookie");
43        for cookie_header in cookie_headers {
44            if let Ok(cookie_str) = cookie_header.to_str()
45                && cookie_str.contains("cloudreve-session=")
46            {
47                for part in cookie_str.split(';') {
48                    let part = part.trim();
49                    if part.starts_with("cloudreve-session=") {
50                        let session_value = part.trim_start_matches("cloudreve-session=");
51                        self.session_cookie = Some(session_value.to_string());
52                        debug!(
53                            "Updated session cookie from CAPTCHA request: {}...",
54                            &session_value[..session_value.len().min(20)]
55                        );
56                        break;
57                    }
58                }
59            }
60        }
61
62        let raw_text = response.text().await?;
63
64        // Print first 200 chars for debugging
65        let preview = if raw_text.len() > 200 {
66            format!("{}...", &raw_text[..200])
67        } else {
68            raw_text.clone()
69        };
70        debug!("CAPTCHA response preview: {}", preview);
71
72        // V3 API returns: { "code": 0, "data": "data:image/png;base64,...", "msg": "" }
73        let api_response: ApiResponse<String> = serde_json::from_str(&raw_text).map_err(|e| {
74            debug!("Failed to parse CAPTCHA response: {}", e);
75            Error::Json(e)
76        })?;
77
78        match api_response.data {
79            Some(image_data) => Ok(CaptchaResponse {
80                image: image_data,
81                ticket: String::new(),
82            }),
83            None => Err(Error::Api {
84                code: api_response.code,
85                message: api_response.msg,
86            }),
87        }
88    }
89
90    /// Check if CAPTCHA is required for login
91    pub async fn is_captcha_required(&self) -> Result<bool, Error> {
92        let config = self.get_site_config().await?;
93        Ok(config.login_captcha)
94    }
95
96    /// Get user storage information
97    pub async fn get_user_storage(&self) -> Result<StorageInfo, Error> {
98        let response: ApiResponse<StorageInfo> = self.get("/user/storage").await?;
99        match response.data {
100            Some(storage) => Ok(storage),
101            None => Err(Error::Api {
102                code: response.code,
103                message: response.msg,
104            }),
105        }
106    }
107
108    /// Ping the server and get server version
109    pub async fn ping(&self) -> Result<String, Error> {
110        let response: ApiResponse<String> = self.get("/site/ping").await?;
111        match response.data {
112            Some(version) => Ok(version),
113            None => Err(Error::Api {
114                code: response.code,
115                message: response.msg,
116            }),
117        }
118    }
119
120    /// Get API version information
121    pub async fn get_version(&self) -> Result<VersionInfo, Error> {
122        let server_version = self.ping().await.unwrap_or_else(|_| "unknown".to_string());
123        Ok(VersionInfo {
124            api_version: "v3".to_string(),
125            library_version: VERSION.to_string(),
126            server_version,
127        })
128    }
129
130    /// Get user settings
131    pub async fn get_user_settings(&self) -> Result<StorageInfo, Error> {
132        let response: ApiResponse<StorageInfo> = self.get("/user/setting").await?;
133        match response.data {
134            Some(settings) => Ok(settings),
135            None => Err(Error::Api {
136                code: response.code,
137                message: response.msg,
138            }),
139        }
140    }
141
142    /// Get task queue
143    pub async fn get_task_queue(&self) -> Result<Vec<Aria2Task>, Error> {
144        let response: ApiResponse<Vec<Aria2Task>> = self.get("/user/setting/tasks").await?;
145        match response.data {
146            Some(tasks) => Ok(tasks),
147            None => Err(Error::Api {
148                code: response.code,
149                message: response.msg,
150            }),
151        }
152    }
153}