// Browser session management use serde::{Deserialize, Serialize}; use std::collections::HashMap; use std::sync::Arc; use tokio::sync::RwLock; use chrono::{DateTime, Utc}; /// Browser session configuration #[derive(Debug, Clone, Serialize, Deserialize)] pub struct SessionConfig { /// WebDriver URL (e.g., "http://localhost:4444") pub webdriver_url: String, /// Browser type (chrome, firefox, etc.) pub browser_type: BrowserType, /// Headless mode pub headless: bool, /// Window size (width, height) pub window_size: Option<(u32, u32)>, /// Page load timeout in seconds pub page_load_timeout: u64, /// Script timeout in seconds pub script_timeout: u64, /// Implicit wait timeout in milliseconds pub implicit_wait_timeout: u64, /// Custom browser arguments pub browser_args: Vec, } impl Default for SessionConfig { fn default() -> Self { Self { webdriver_url: "http://localhost:4444".to_string(), browser_type: BrowserType::Chrome, headless: true, window_size: Some((1920, 1080)), page_load_timeout: 30, script_timeout: 30, implicit_wait_timeout: 1000, browser_args: vec![], } } } #[derive(Debug, Clone, Serialize, Deserialize, PartialEq)] #[serde(rename_all = "lowercase")] pub enum BrowserType { Chrome, Firefox, Edge, Safari, } /// Active browser session #[derive(Debug, Clone, Serialize, Deserialize)] pub struct BrowserSession { /// Unique session identifier pub id: String, /// Session name for display pub name: String, /// Current URL pub current_url: Option, /// Page title pub title: Option, /// Session status pub status: SessionStatus, /// Creation timestamp pub created_at: DateTime, /// Last activity timestamp pub last_activity: DateTime, /// Session configuration pub config: SessionConfig, /// Custom metadata pub metadata: HashMap, } impl BrowserSession { pub fn new(id: String, config: SessionConfig) -> Self { let now = Utc::now(); Self { id, name: format!("Browser Session"), current_url: None, title: None, status: SessionStatus::Connected, created_at: now, last_activity: now, config, metadata: HashMap::new(), } } pub fn touch(&mut self) { self.last_activity = Utc::now(); } pub fn update_location(&mut self, url: Option, title: Option) { self.current_url = url; self.title = title; self.touch(); } } #[derive(Debug, Clone, Serialize, Deserialize, PartialEq)] #[serde(rename_all = "lowercase")] pub enum SessionStatus { Connecting, Connected, Active, Idle, Disconnected, Error, } /// Session manager for multiple browser instances pub struct SessionManager { sessions: Arc>>, } impl SessionManager { pub fn new() -> Self { Self { sessions: Arc::new(RwLock::new(HashMap::new())), } } pub async fn add_session(&self, session: BrowserSession) { let mut sessions = self.sessions.write().await; sessions.insert(session.id.clone(), session); } pub async fn get_session(&self, id: &str) -> Option { let sessions = self.sessions.read().await; sessions.get(id).cloned() } pub async fn update_session(&self, id: &str, updater: impl FnOnce(&mut BrowserSession)) { let mut sessions = self.sessions.write().await; if let Some(session) = sessions.get_mut(id) { updater(session); } } pub async fn remove_session(&self, id: &str) -> Option { let mut sessions = self.sessions.write().await; sessions.remove(id) } pub async fn list_sessions(&self) -> Vec { let sessions = self.sessions.read().await; sessions.values().cloned().collect() } /// Get the number of active sessions /// Reserved for future status dashboard #[allow(dead_code)] pub async fn session_count(&self) -> usize { let sessions = self.sessions.read().await; sessions.len() } } impl Default for SessionManager { fn default() -> Self { Self::new() } } impl Clone for SessionManager { fn clone(&self) -> Self { Self { sessions: Arc::clone(&self.sessions), } } }