Files
dynamic-preauth/backend/src/state.rs
Xevion b4022ff9db chore: add bacon config and improve dev workflow
- Add bacon.toml for Rust development watching with keybindings
- Update Justfile to use bacon for dev watching
- Configure frontend to build to ./public for backend serving
- Improve Justfile organization with comments and better task separation
- Add dev-backend and dev-frontend tasks for separate workflows
- Minor formatting fix in backend/src/state.rs
2025-12-11 17:58:10 -06:00

123 lines
3.3 KiB
Rust

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<Mutex<State>> = LazyLock::new(|| Mutex::new(State::new()));
#[derive(Default)]
pub struct State {
pub sessions: HashMap<u32, Session>,
pub executables: HashMap<String, Executable>,
pub build_logs: Option<BuildLogs>,
pub build_log_url: Option<String>,
}
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<ExecutableJson> {
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
}
}