use crate::i18n::UseI18n; use serde_json; use shared::auth::AuthError; /// Helper struct for handling authentication errors with internationalization #[derive(Clone)] pub struct AuthErrorHandler { i18n: UseI18n, } impl AuthErrorHandler { pub fn new(i18n: UseI18n) -> Self { Self { i18n } } /// Convert a server response error to a localized error message pub async fn handle_response_error(&self, response: &reqwasm::http::Response) -> String { if let Ok(error_text) = response.text().await { self.map_error_to_localized_message(&error_text) } else { self.i18n.t("unknown-error") } } /// Map error text to localized message pub fn map_error_to_localized_message(&self, error_text: &str) -> String { let translation_key = self.map_error_to_translation_key(error_text); self.i18n.t(&translation_key) } /// Map server errors to translation keys pub fn map_error_to_translation_key(&self, error_text: &str) -> String { // Try to parse as JSON first (standard API error response) if let Ok(json_value) = serde_json::from_str::(error_text) { if let Some(message) = json_value.get("message").and_then(|m| m.as_str()) { return self.map_error_message_to_key(message); } if let Some(errors) = json_value.get("errors").and_then(|e| e.as_array()) { if let Some(first_error) = errors.first().and_then(|e| e.as_str()) { return self.map_error_message_to_key(first_error); } } } // Fallback to direct message mapping self.map_error_message_to_key(error_text) } /// Map error messages to translation keys fn map_error_message_to_key(&self, message: &str) -> String { let message_lower = message.to_lowercase(); match message_lower.as_str() { msg if msg.contains("invalid credentials") => "invalid-credentials".to_string(), msg if msg.contains("user not found") => "user-not-found".to_string(), msg if msg.contains("email already exists") => "email-already-exists".to_string(), msg if msg.contains("username already exists") => "username-already-exists".to_string(), msg if msg.contains("invalid token") => "invalid-token".to_string(), msg if msg.contains("token expired") => "token-expired".to_string(), msg if msg.contains("insufficient permissions") => { "insufficient-permissions".to_string() } msg if msg.contains("account not verified") => "account-not-verified".to_string(), msg if msg.contains("account suspended") => "account-suspended".to_string(), msg if msg.contains("rate limit exceeded") => "rate-limit-exceeded".to_string(), msg if msg.contains("oauth") => "oauth-error".to_string(), msg if msg.contains("database") => "database-error".to_string(), msg if msg.contains("validation") => "validation-error".to_string(), msg if msg.contains("login failed") => "login-failed".to_string(), msg if msg.contains("registration failed") => "registration-failed".to_string(), msg if msg.contains("session expired") => "session-expired".to_string(), msg if msg.contains("profile") && msg.contains("failed") => { "profile-update-failed".to_string() } msg if msg.contains("password") && msg.contains("failed") => { "password-change-failed".to_string() } msg if msg.contains("network") => "network-error".to_string(), msg if msg.contains("server") => "server-error".to_string(), msg if msg.contains("internal") => "internal-error".to_string(), _ => "unknown-error".to_string(), } } /// Handle AuthError enum directly pub fn handle_auth_error(&self, error: &AuthError) -> String { let translation_key = match error { AuthError::InvalidCredentials => "invalid-credentials", AuthError::UserNotFound => "user-not-found", AuthError::EmailAlreadyExists => "email-already-exists", AuthError::UsernameAlreadyExists => "username-already-exists", AuthError::InvalidToken => "invalid-token", AuthError::TokenExpired => "token-expired", AuthError::InsufficientPermissions => "insufficient-permissions", AuthError::AccountNotVerified => "account-not-verified", AuthError::AccountSuspended => "account-suspended", AuthError::RateLimitExceeded => "rate-limit-exceeded", AuthError::OAuthError(_) => "oauth-error", AuthError::DatabaseError => "database-error", AuthError::InternalError => "internal-error", AuthError::ValidationError(_) => "validation-error", }; self.i18n.t(translation_key) } /// Handle network errors pub fn handle_network_error(&self) -> String { self.i18n.t("network-error") } /// Handle generic request failures pub fn handle_request_failure(&self, operation: &str) -> String { match operation { "login" => self.i18n.t("login-failed"), "register" => self.i18n.t("registration-failed"), "profile-update" => self.i18n.t("profile-update-failed"), "password-change" => self.i18n.t("password-change-failed"), _ => self.i18n.t("request-failed"), } } /// Check if an error indicates session expiration pub fn is_session_expired(&self, error_text: &str) -> bool { let error_lower = error_text.to_lowercase(); error_lower.contains("session expired") || error_lower.contains("token expired") || error_lower.contains("invalid token") || error_lower.contains("unauthorized") } /// Get appropriate error message for session expiration pub fn get_session_expired_message(&self) -> String { self.i18n.t("session-expired") } } /// Helper function to create an AuthErrorHandler pub fn create_auth_error_handler(i18n: UseI18n) -> AuthErrorHandler { AuthErrorHandler::new(i18n) } /// Trait for handling authentication errors consistently pub trait AuthErrorHandling { fn handle_auth_error(&self, error: &str) -> String; fn handle_network_error(&self) -> String; fn handle_session_expired(&self) -> String; } impl AuthErrorHandling for UseI18n { fn handle_auth_error(&self, error: &str) -> String { let handler = create_auth_error_handler(self.clone()); handler.map_error_to_localized_message(error) } fn handle_network_error(&self) -> String { self.t("network-error") } fn handle_session_expired(&self) -> String { self.t("session-expired") } }