Files
zclaw_openfang/desktop/src-tauri/src/browser/session.rs
2026-03-17 23:26:16 +08:00

191 lines
4.6 KiB
Rust

// 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<String>,
}
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<String>,
/// Page title
pub title: Option<String>,
/// Session status
pub status: SessionStatus,
/// Creation timestamp
pub created_at: DateTime<Utc>,
/// Last activity timestamp
pub last_activity: DateTime<Utc>,
/// Session configuration
pub config: SessionConfig,
/// Custom metadata
pub metadata: HashMap<String, String>,
}
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<String>, title: Option<String>) {
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<RwLock<HashMap<String, BrowserSession>>>,
}
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<BrowserSession> {
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<BrowserSession> {
let mut sessions = self.sessions.write().await;
sessions.remove(id)
}
pub async fn list_sessions(&self) -> Vec<BrowserSession> {
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),
}
}
}