cloudreve_api/api/v4/
session.rs1use crate::Error;
4use crate::api::v4::ApiV4Client;
5use crate::api::v4::models::*;
6use serde::Serialize;
7
8impl ApiV4Client {
9 pub async fn prepare_login(&self, email: &str) -> Result<LoginPreparation, Error> {
10 let endpoint = format!("/session/prepare?email={}", email);
11 let response: crate::ApiResponse<LoginPreparation> = self.get(&endpoint).await?;
12 match response.data {
13 Some(preparation) => Ok(preparation),
14 None => Err(crate::Error::Api {
15 code: response.code,
16 message: response.msg,
17 }),
18 }
19 }
20
21 pub async fn prepare_openid_signin(
22 &self,
23 request: &OpenIdPrepareRequest<'_>,
24 ) -> Result<String, Error> {
25 let response: crate::ApiResponse<String> = self.put("/session/openid", request).await?;
26 match response.data {
27 Some(url) => Ok(url),
28 None => Err(crate::Error::Api {
29 code: response.code,
30 message: response.msg,
31 }),
32 }
33 }
34
35 pub async fn finish_openid_signin(
36 &self,
37 request: &OpenIdFinishRequest<'_>,
38 ) -> Result<LoginResponse, Error> {
39 let response: crate::ApiResponse<LoginResponse> =
40 self.post("/session/openid", request).await?;
41 match response.data {
42 Some(login_response) => Ok(login_response),
43 None => Err(crate::Error::Api {
44 code: response.code,
45 message: response.msg,
46 }),
47 }
48 }
49
50 pub async fn unlink_openid(&self, provider_id: i32) -> Result<(), Error> {
51 let endpoint = format!("/session/openid/{}", provider_id);
52 let response: crate::ApiResponse<()> = self.delete(&endpoint).await?;
53 if response.code == 0 {
54 Ok(())
55 } else {
56 Err(crate::Error::Api {
57 code: response.code,
58 message: response.msg,
59 })
60 }
61 }
62
63 pub async fn prepare_passkey_signin(&self) -> Result<PasskeySignInPreparation, Error> {
64 let response: crate::ApiResponse<PasskeySignInPreparation> =
65 self.put("/session/authn", &()).await?;
66 match response.data {
67 Some(preparation) => Ok(preparation),
68 None => Err(crate::Error::Api {
69 code: response.code,
70 message: response.msg,
71 }),
72 }
73 }
74
75 pub async fn finish_passkey_signin(
76 &self,
77 request: &PasskeySignInRequest<'_>,
78 ) -> Result<LoginResponse, Error> {
79 let response: crate::ApiResponse<LoginResponse> =
80 self.post("/session/authn", request).await?;
81 match response.data {
82 Some(login_response) => Ok(login_response),
83 None => Err(crate::Error::Api {
84 code: response.code,
85 message: response.msg,
86 }),
87 }
88 }
89
90 pub async fn login(&self, request: &LoginRequest<'_>) -> Result<LoginData, Error> {
91 let response_text = self.post_raw("/session/token", request).await?;
94
95 log::debug!("Raw login response: {}", response_text);
96
97 if let Ok(api_response) = serde_json::from_str::<ApiResponse<String>>(&response_text) {
100 if api_response.code == 203 {
101 let session_id = api_response.data.unwrap_or_default();
103 log::debug!("2FA required, session ID: {}", session_id);
104 return Err(crate::Error::TwoFactorRequired(session_id));
105 }
106
107 if api_response.code != 0 {
109 return Err(crate::Error::Api {
110 code: api_response.code,
111 message: api_response.msg,
112 });
113 }
114 }
115
116 match serde_json::from_str::<ApiResponse<LoginData>>(&response_text) {
118 Ok(api_response) => {
119 if api_response.code != 0 {
121 return Err(crate::Error::Api {
122 code: api_response.code,
123 message: api_response.msg,
124 });
125 }
126
127 match api_response.data {
128 Some(data) => Ok(data),
129 None => Err(crate::Error::InvalidResponse(
130 "Missing login data in response".to_string(),
131 )),
132 }
133 }
134 Err(e) => {
135 log::error!("Failed to parse login response: {}", e);
136 log::error!("Response text: {}", response_text);
137 Err(crate::Error::InvalidResponse(format!(
138 "Invalid login response format: {}",
139 e
140 )))
141 }
142 }
143 }
144
145 async fn post_raw(&self, endpoint: &str, body: &impl Serialize) -> Result<String, Error> {
147 let url = self.get_url(endpoint);
148 let mut http_request = self.http_client.post(&url).json(body);
149
150 if let Some(token) = &self.token {
151 http_request = http_request.header("Authorization", format!("Bearer {}", token));
152 }
153
154 let response = http_request.send().await?;
155 let status = response.status();
156
157 let raw_text = response.text().await?;
158
159 if !status.is_success() {
160 return Err(crate::Error::Api {
161 code: status.as_u16() as i32,
162 message: raw_text.trim().to_string(),
163 });
164 }
165
166 Ok(raw_text)
167 }
168
169 pub async fn finish_2fa_login(
170 &self,
171 request: &TwoFactorLoginRequest<'_>,
172 ) -> Result<LoginData, Error> {
173 let response: ApiResponse<LoginData> = self.post("/session/token/2fa", request).await?;
174 match response.data {
175 Some(data) => Ok(data),
176 None => Err(crate::Error::Api {
177 code: response.code,
178 message: response.msg,
179 }),
180 }
181 }
182
183 pub async fn refresh_token(&self, request: &RefreshTokenRequest<'_>) -> Result<Token, Error> {
184 let response: crate::ApiResponse<Token> =
185 self.post("/session/token/refresh", request).await?;
186 match response.data {
187 Some(token) => Ok(token),
188 None => Err(crate::Error::Api {
189 code: response.code,
190 message: response.msg,
191 }),
192 }
193 }
194
195 pub async fn logout(&self) -> Result<(), Error> {
196 let _: crate::ApiResponse<()> = self.delete("/session/token").await?;
197 Ok(())
198 }
199}