| use serde::{Deserialize, Serialize}; |
|
|
| |
| const CLIENT_ID: &str = "1071006060591-tmhssin2h21lcre235vtolojh4g403ep.apps.googleusercontent.com"; |
| const CLIENT_SECRET: &str = "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"; |
| const TOKEN_URL: &str = "https://oauth2.googleapis.com/token"; |
| const USERINFO_URL: &str = "https://www.googleapis.com/oauth2/v2/userinfo"; |
|
|
| #[derive(Debug, Serialize, Deserialize)] |
| pub struct TokenResponse { |
| pub access_token: String, |
| pub expires_in: i64, |
| #[serde(default)] |
| pub token_type: String, |
| #[serde(default)] |
| pub refresh_token: Option<String>, |
| } |
|
|
| #[derive(Debug, Serialize, Deserialize)] |
| pub struct UserInfo { |
| pub email: String, |
| pub name: Option<String>, |
| pub given_name: Option<String>, |
| pub family_name: Option<String>, |
| pub picture: Option<String>, |
| } |
|
|
| impl UserInfo { |
| |
| pub fn get_display_name(&self) -> Option<String> { |
| |
| if let Some(name) = &self.name { |
| if !name.trim().is_empty() { |
| return Some(name.clone()); |
| } |
| } |
|
|
| |
| match (&self.given_name, &self.family_name) { |
| (Some(given), Some(family)) => Some(format!("{} {}", given, family)), |
| (Some(given), None) => Some(given.clone()), |
| (None, Some(family)) => Some(family.clone()), |
| (None, None) => None, |
| } |
| } |
| } |
|
|
| |
| pub async fn refresh_access_token(refresh_token: &str) -> Result<TokenResponse, String> { |
| let client = crate::utils::http::create_client(15); |
|
|
| let params = [ |
| ("client_id", CLIENT_ID), |
| ("client_secret", CLIENT_SECRET), |
| ("refresh_token", refresh_token), |
| ("grant_type", "refresh_token"), |
| ]; |
|
|
| crate::modules::logger::log_info("Refreshing token..."); |
|
|
| let response = client |
| .post(TOKEN_URL) |
| .form(¶ms) |
| .send() |
| .await |
| .map_err(|e| format!("Refresh request failed: {}", e))?; |
|
|
| if response.status().is_success() { |
| let token_data = response |
| .json::<TokenResponse>() |
| .await |
| .map_err(|e| format!("Failed to parse refresh data: {}", e))?; |
|
|
| crate::modules::logger::log_info(&format!("Token refreshed! Valid for: {} seconds", token_data.expires_in)); |
| Ok(token_data) |
| } else { |
| let error_text = response.text().await.unwrap_or_default(); |
| Err(format!("Refresh failed: {}", error_text)) |
| } |
| } |
|
|
| |
| pub async fn get_user_info(access_token: &str) -> Result<UserInfo, String> { |
| let client = crate::utils::http::create_client(15); |
|
|
| let response = client |
| .get(USERINFO_URL) |
| .bearer_auth(access_token) |
| .send() |
| .await |
| .map_err(|e| format!("User info request failed: {}", e))?; |
|
|
| if response.status().is_success() { |
| response.json::<UserInfo>() |
| .await |
| .map_err(|e| format!("Failed to parse user info: {}", e)) |
| } else { |
| let error_text = response.text().await.unwrap_or_default(); |
| Err(format!("Failed to get user info: {}", error_text)) |
| } |
| } |
|
|
| |
| |
| pub async fn ensure_fresh_token( |
| current_token: &crate::models::TokenData, |
| ) -> Result<crate::models::TokenData, String> { |
| let now = chrono::Local::now().timestamp(); |
|
|
| |
| if current_token.expiry_timestamp > now + 300 { |
| return Ok(current_token.clone()); |
| } |
|
|
| |
| crate::modules::logger::log_info("Token about to expire, refreshing..."); |
| let response = refresh_access_token(¤t_token.refresh_token).await?; |
|
|
| |
| Ok(crate::models::TokenData::new( |
| response.access_token, |
| current_token.refresh_token.clone(), |
| response.expires_in, |
| current_token.email.clone(), |
| current_token.project_id.clone(), |
| None, |
| )) |
| } |
|
|