use std::sync::Arc;
use std::time::Instant;
use night_fury_core::BrowserSession;
use serde_json::Value;
use tokio::sync::Mutex;
use crate::error::SiteError;
use crate::site::{SessionStatus, Site};
pub struct SessionManager {
site: Arc<dyn Site>,
browser: Arc<BrowserSession>,
state: Mutex<SessionState>,
}
struct SessionState {
cookies: Vec<Value>,
last_refresh: Option<Instant>,
}
impl SessionManager {
pub fn new(site: Arc<dyn Site>, browser: BrowserSession) -> Self {
Self {
site,
browser: Arc::new(browser),
state: Mutex::new(SessionState {
cookies: Vec::new(),
last_refresh: None,
}),
}
}
pub fn site(&self) -> &Arc<dyn Site> {
&self.site
}
pub fn browser(&self) -> &Arc<BrowserSession> {
&self.browser
}
pub async fn refresh(&self) -> Result<Vec<Value>, SiteError> {
let cookies = self.site.refresh(&self.browser).await?;
let mut state = self.state.lock().await;
state.cookies = cookies.clone();
state.last_refresh = Some(Instant::now());
Ok(cookies)
}
pub async fn refresh_with_seed(&self, seed: &[Value]) -> Result<Vec<Value>, SiteError> {
if !seed.is_empty() {
self.browser.set_cookies(seed.to_vec()).await.map_err(|e| {
SiteError::RefreshFailed {
site: self.site.id(),
reason: format!("set_cookies: {e}"),
}
})?;
}
self.refresh().await
}
pub async fn refresh_if_stale(&self) -> Result<Option<Vec<Value>>, SiteError> {
let should_refresh = {
let state = self.state.lock().await;
match state.last_refresh {
None => true,
Some(t) => t.elapsed() >= self.site.refresh_interval_min(),
}
};
if should_refresh {
self.refresh().await.map(Some)
} else {
Ok(None)
}
}
pub async fn validate(&self) -> Result<SessionStatus, SiteError> {
self.site.validate(&self.browser).await
}
pub async fn cookies(&self) -> Vec<Value> {
self.state.lock().await.cookies.clone()
}
pub async fn reload_cookies(&self) -> Result<Vec<Value>, SiteError> {
let pattern = self
.site
.cookie_domain_patterns()
.first()
.copied()
.unwrap_or("*");
let cookies = self
.browser
.get_cookies_for_domain(pattern)
.await
.map_err(|e| SiteError::RefreshFailed {
site: self.site.id(),
reason: format!("reload_cookies: {e}"),
})?;
let mut state = self.state.lock().await;
state.cookies = cookies.clone();
Ok(cookies)
}
}