Skip to main content

cloudreve_api/api/v3/
session.rs

1//! Session and authentication management for Cloudreve API v3
2
3use crate::Error;
4use crate::api::v3::ApiV3Client;
5use crate::api::v3::models::*;
6use log::debug;
7
8impl ApiV3Client {
9    /// Login with email and password
10    pub async fn login(&mut self, request: &LoginRequest<'_>) -> Result<User, Error> {
11        let url = self.get_url("/user/session");
12        let mut http_request = self.http_client.post(&url).json(request);
13
14        if let Some(cookie) = &self.session_cookie {
15            http_request = http_request.header("Cookie", format!("cloudreve-session={}", cookie));
16        }
17
18        let response = http_request.send().await?;
19
20        // Extract session cookie from Set-Cookie headers BEFORE consuming the response
21        let cookie_headers = response.headers().get_all("Set-Cookie");
22        let mut new_session_cookie: Option<String> = None;
23        for cookie_header in cookie_headers {
24            if let Ok(cookie_str) = cookie_header.to_str()
25                && cookie_str.contains("cloudreve-session=")
26            {
27                for part in cookie_str.split(';') {
28                    let part = part.trim();
29                    if part.starts_with("cloudreve-session=") {
30                        let session_value = part.trim_start_matches("cloudreve-session=");
31                        new_session_cookie = Some(session_value.to_string());
32                        debug!(
33                            "Extracted V3 session cookie from response: {}...",
34                            &session_value[..session_value.len().min(20)]
35                        );
36                        break;
37                    }
38                }
39            }
40        }
41
42        let _status = response.status();
43        let api_response: ApiResponse<User> = response.json().await?;
44
45        // Only save session cookie if login was successful
46        match api_response.data {
47            Some(_) => {
48                if let Some(ref cookie) = new_session_cookie {
49                    self.session_cookie = Some(cookie.clone());
50                    debug!(
51                        "V3 session_cookie after successful login: {:?}",
52                        self.session_cookie
53                    );
54                }
55            }
56            None => {
57                debug!("Login failed, not updating session cookie");
58            }
59        }
60
61        match api_response.data {
62            Some(user) => Ok(user),
63            None => {
64                // Code 203 indicates two-factor authentication is required
65                // V3 sets session cookie even when 2FA is required
66                if api_response.code == 203 {
67                    // Save session cookie for 2FA request
68                    if let Some(ref cookie) = new_session_cookie {
69                        self.session_cookie = Some(cookie.clone());
70                        debug!("V3 session_cookie saved for 2FA: {:?}", self.session_cookie);
71                    }
72                    Err(Error::TwoFactorRequired(String::new()))
73                } else {
74                    Err(Error::Api {
75                        code: api_response.code,
76                        message: api_response.msg,
77                    })
78                }
79            }
80        }
81    }
82
83    /// Login with OTP (Two-Factor Authentication)
84    pub async fn login_2fa(&mut self, request: &OtpLoginRequest) -> Result<User, Error> {
85        let url = self.get_url("/user/2fa");
86        let mut http_request = self.http_client.post(&url).json(request);
87
88        if let Some(cookie) = &self.session_cookie {
89            http_request = http_request.header("Cookie", format!("cloudreve-session={}", cookie));
90        }
91
92        let response = http_request.send().await?;
93
94        // Extract session cookie from Set-Cookie headers
95        let cookie_headers = response.headers().get_all("Set-Cookie");
96        for cookie_header in cookie_headers {
97            if let Ok(cookie_str) = cookie_header.to_str()
98                && cookie_str.contains("cloudreve-session=")
99            {
100                for part in cookie_str.split(';') {
101                    let part = part.trim();
102                    if part.starts_with("cloudreve-session=") {
103                        let session_value = part.trim_start_matches("cloudreve-session=");
104                        self.session_cookie = Some(session_value.to_string());
105                        debug!(
106                            "Extracted V3 session cookie (2FA): {}...",
107                            &session_value[..session_value.len().min(20)]
108                        );
109                        break;
110                    }
111                }
112            }
113        }
114
115        let _status = response.status();
116        let api_response: ApiResponse<User> = response.json().await?;
117
118        match api_response.data {
119            Some(user) => Ok(user),
120            None => Err(Error::Api {
121                code: api_response.code,
122                message: api_response.msg,
123            }),
124        }
125    }
126
127    /// Logout from current session
128    pub async fn logout(&self) -> Result<(), Error> {
129        let response: ApiResponse<()> = self.delete("/user/session").await?;
130        if response.code == 0 {
131            Ok(())
132        } else {
133            Err(Error::Api {
134                code: response.code,
135                message: response.msg,
136            })
137        }
138    }
139}