use anyhow::{Context, Result}; use serde::{Deserialize, Serialize}; use std::fs; use std::path::PathBuf; use crate::models::UserInfo; /// Session data stored to disk #[derive(Debug, Clone, Serialize, Deserialize)] pub struct SessionData { pub server_url: String, pub token: String, pub user: UserInfo, pub remember_server: bool, pub remember_username: bool, pub saved_username: Option, #[serde(default)] pub default_printer_id: Option, /// Remember last-used printer (may differ from default_printer_id if user overrides per-print) #[serde(default)] pub last_printer_id: Option, } /// Manages user session and credentials pub struct SessionManager { config_path: PathBuf, current_session: Option, } impl SessionManager { /// Create a new session manager pub fn new() -> Self { let config_dir = dirs::config_dir() .unwrap_or_else(|| PathBuf::from(".")) .join("beepzone"); // Ensure config directory exists let _ = fs::create_dir_all(&config_dir); let config_path = config_dir.join("session.json"); let mut manager = Self { config_path, current_session: None, }; // Try to load existing session let _ = manager.load_session(); manager } /// Save a new session pub fn save_session(&mut self, session: SessionData) -> Result<()> { self.current_session = Some(session.clone()); let json = serde_json::to_string_pretty(&session).context("Failed to serialize session")?; fs::write(&self.config_path, json).context("Failed to write session file")?; log::info!("Session saved to {:?}", self.config_path); Ok(()) } /// Load session from disk pub fn load_session(&mut self) -> Result<()> { if !self.config_path.exists() { return Ok(()); } let json = fs::read_to_string(&self.config_path).context("Failed to read session file")?; let session: SessionData = serde_json::from_str(&json).context("Failed to parse session file")?; self.current_session = Some(session); log::info!("Session loaded from {:?}", self.config_path); Ok(()) } /// Clear the current session pub fn clear_session(&mut self) -> Result<()> { self.current_session = None; if self.config_path.exists() { fs::remove_file(&self.config_path).context("Failed to remove session file")?; log::info!("Session file removed"); } Ok(()) } /// Get the current session pub fn get_session(&self) -> Option<&SessionData> { self.current_session.as_ref() } /// Check if there's a valid session #[allow(dead_code)] pub fn has_session(&self) -> bool { self.current_session.is_some() } /// Get the saved server URL (if remember_server is enabled) pub fn get_saved_server_url(&self) -> Option { self.current_session .as_ref() .filter(|s| s.remember_server) .map(|s| s.server_url.clone()) } /// Get the saved username (if remember_username is enabled) pub fn get_saved_username(&self) -> Option { self.current_session .as_ref() .and_then(|s| s.saved_username.clone()) } /// Update session with new token (for token refresh) #[allow(dead_code)] pub fn update_token(&mut self, new_token: String) -> Result<()> { if let Some(session) = &mut self.current_session { let mut updated_session = session.clone(); updated_session.token = new_token; self.save_session(updated_session)?; } Ok(()) } /// Update default printer ID pub fn update_default_printer(&mut self, printer_id: Option) -> Result<()> { if let Some(session) = &mut self.current_session { let mut updated_session = session.clone(); updated_session.default_printer_id = printer_id; self.save_session(updated_session)?; } Ok(()) } /// Get default printer ID pub fn get_default_printer_id(&self) -> Option { self.current_session .as_ref() .and_then(|s| s.default_printer_id) } /// Get last-used printer ID for printing pub fn get_last_print_preferences(&self) -> Option { if let Some(session) = &self.current_session { session.last_printer_id } else { None } } } impl Default for SessionManager { fn default() -> Self { Self::new() } }