voidcrawl_mcp/
sessions.rs1use std::{collections::HashMap, path::PathBuf, sync::Arc};
8
9use tempfile::TempDir;
10use tokio::sync::{Mutex, RwLock};
11use void_crawl_core::{
12 AntibotVerdict, BrowserSession, ChallengeSnapshot, DownloadCapture, ManagedProfileLease, Page,
13 ResolutionOutcome,
14};
15
16pub type SessionId = String;
17
18#[derive(Debug)]
21pub struct PendingDownload {
22 pub capture: DownloadCapture,
23 pub quarantine: TempDir,
24 pub output_dir: PathBuf,
25 pub max_bytes: u64,
26}
27
28#[derive(Debug, Clone)]
31pub struct LastNavigation {
32 pub url: String,
33 pub status_code: Option<u16>,
34 pub antibot: Option<AntibotVerdict>,
35}
36
37#[derive(Debug, Clone)]
39pub struct PendingChallenge {
40 pub snapshot: ChallengeSnapshot,
41 pub outcome: Option<ResolutionOutcome>,
42}
43
44#[derive(Debug)]
46pub struct DedicatedSession {
47 pub session: Arc<BrowserSession>,
48 pub page: Mutex<Page>,
49 pub profile_lease: Option<ManagedProfileLease>,
50 pub last_navigation: Mutex<Option<LastNavigation>>,
51 pub challenge: Mutex<Option<PendingChallenge>>,
52 pub pending_download: Mutex<Option<PendingDownload>>,
54}
55
56#[derive(Debug, Default)]
58pub struct SessionRegistry {
59 inner: RwLock<HashMap<SessionId, Arc<DedicatedSession>>>,
60}
61
62impl SessionRegistry {
63 pub async fn insert(&self, id: SessionId, session: Arc<DedicatedSession>) {
64 self.inner.write().await.insert(id, session);
65 }
66
67 pub async fn get(&self, id: &str) -> Option<Arc<DedicatedSession>> {
68 self.inner.read().await.get(id).cloned()
69 }
70
71 pub async fn remove(&self, id: &str) -> Option<Arc<DedicatedSession>> {
72 self.inner.write().await.remove(id)
73 }
74
75 pub async fn len(&self) -> usize {
76 self.inner.read().await.len()
77 }
78
79 pub async fn is_empty(&self) -> bool {
80 self.inner.read().await.is_empty()
81 }
82
83 pub async fn drain(&self) -> Vec<Arc<DedicatedSession>> {
85 self.inner.write().await.drain().map(|(_, v)| v).collect()
86 }
87}