use std::collections::HashMap; use std::path::{Path, PathBuf}; use std::sync::LazyLock; use salvo::{http::cookie::Cookie, Response}; use tokio::sync::Mutex; use crate::errors::{AppError, Result}; use crate::models::{BuildLogs, Executable, ExecutableJson, Session}; pub static STORE: LazyLock> = LazyLock::new(|| Mutex::new(State::new())); #[derive(Default)] pub struct State { pub sessions: HashMap, pub executables: HashMap, pub build_logs: Option, pub build_log_url: Option, } impl State { pub fn new() -> Self { Self { sessions: HashMap::new(), executables: HashMap::new(), build_logs: None, build_log_url: None, } } pub fn add_executable(&mut self, exe_type: &str, exe_path: &str) -> Result<()> { let path = Path::new(exe_path); let data = std::fs::read(path).map_err(|_| AppError::ExecutableNotFound { path: PathBuf::from(exe_path), })?; let pattern = "a".repeat(1024); let name = path .file_stem() .and_then(|s| s.to_str()) .unwrap_or_default() .to_string(); let key_start = Executable::search_pattern(&data, pattern.as_bytes(), 0) .ok_or_else(|| AppError::KeyPatternNotFound { name: name.clone() })?; let key_end = key_start + pattern.len(); let extension = path .extension() .and_then(|s| s.to_str()) .unwrap_or_default() .to_string(); let exe = Executable { data, filename: path .file_name() .and_then(|s| s.to_str()) .unwrap_or_default() .to_string(), name, extension, key_start, key_end, }; self.executables.insert(exe_type.to_string(), exe); Ok(()) } pub async fn new_session(&mut self, res: &mut Response) -> u32 { let id: u32 = rand::random(); let now = chrono::Utc::now(); self.sessions.insert( id, Session { id, downloads: Vec::new(), last_seen: now, last_request: now, first_seen: now, tx: None, }, ); tracing::info!("New session created: {}", id); res.add_cookie( Cookie::build(("Session", id.to_string())) .http_only(true) .partitioned(true) .secure(cfg!(debug_assertions) == false) .path("/") // Use SameSite=None only in development .same_site(if cfg!(debug_assertions) { salvo::http::cookie::SameSite::None } else { salvo::http::cookie::SameSite::Strict }) .permanent() .build(), ); id } pub fn executable_json(&self) -> Vec { let mut executables = Vec::new(); for (key, exe) in &self.executables { executables.push(ExecutableJson { id: key.to_string(), size: exe.data.len(), filename: exe.filename.clone(), }); } executables } }