mirror of
https://github.com/Xevion/dynamic-preauth.git
synced 2025-12-15 10:11:40 -06:00
feat(backend): add thiserror-based error handling
Introduce AppError enum to replace panic-based error handling in executable loading and state management. Adds proper error propagation with descriptive error messages for missing executables, key patterns, and environment variables.
This commit is contained in:
1
Cargo.lock
generated
1
Cargo.lock
generated
@@ -359,6 +359,7 @@ dependencies = [
|
|||||||
"salvo",
|
"salvo",
|
||||||
"serde",
|
"serde",
|
||||||
"serde_json",
|
"serde_json",
|
||||||
|
"thiserror 2.0.17",
|
||||||
"tokio",
|
"tokio",
|
||||||
"tokio-stream",
|
"tokio-stream",
|
||||||
"tracing",
|
"tracing",
|
||||||
|
|||||||
4
Justfile
4
Justfile
@@ -44,12 +44,12 @@ frontend-build:
|
|||||||
# Development server with hot reload
|
# Development server with hot reload
|
||||||
dev:
|
dev:
|
||||||
@echo "Starting development server..."
|
@echo "Starting development server..."
|
||||||
cargo watch -x run
|
cargo watch -x run --bin backend
|
||||||
|
|
||||||
# Simple development run (no hot reload)
|
# Simple development run (no hot reload)
|
||||||
run:
|
run:
|
||||||
@echo "Starting server..."
|
@echo "Starting server..."
|
||||||
cargo run
|
cargo run --bin backend
|
||||||
|
|
||||||
# Build release
|
# Build release
|
||||||
build:
|
build:
|
||||||
|
|||||||
@@ -19,6 +19,7 @@ reqwest = { workspace = true, features = ["json", "rustls-tls"] }
|
|||||||
salvo.workspace = true
|
salvo.workspace = true
|
||||||
serde.workspace = true
|
serde.workspace = true
|
||||||
serde_json.workspace = true
|
serde_json.workspace = true
|
||||||
|
thiserror = "2.0.17"
|
||||||
tokio.workspace = true
|
tokio.workspace = true
|
||||||
tokio-stream.workspace = true
|
tokio-stream.workspace = true
|
||||||
tracing.workspace = true
|
tracing.workspace = true
|
||||||
|
|||||||
19
backend/src/errors.rs
Normal file
19
backend/src/errors.rs
Normal file
@@ -0,0 +1,19 @@
|
|||||||
|
use std::path::PathBuf;
|
||||||
|
use thiserror::Error;
|
||||||
|
|
||||||
|
pub type Result<T> = std::result::Result<T, AppError>;
|
||||||
|
|
||||||
|
#[derive(Debug, Error)]
|
||||||
|
pub enum AppError {
|
||||||
|
#[error("executable not found at '{path}'")]
|
||||||
|
ExecutableNotFound { path: PathBuf },
|
||||||
|
|
||||||
|
#[error("key pattern not found in executable '{name}'")]
|
||||||
|
KeyPatternNotFound { name: String },
|
||||||
|
|
||||||
|
#[error("missing required environment variable '{name}'")]
|
||||||
|
MissingEnvVar { name: String },
|
||||||
|
|
||||||
|
#[error("configuration error: {message}")]
|
||||||
|
Config { message: String },
|
||||||
|
}
|
||||||
@@ -1,4 +1,5 @@
|
|||||||
pub mod config;
|
pub mod config;
|
||||||
|
pub mod errors;
|
||||||
pub mod handlers;
|
pub mod handlers;
|
||||||
pub mod models;
|
pub mod models;
|
||||||
pub mod railway;
|
pub mod railway;
|
||||||
|
|||||||
@@ -60,9 +60,16 @@ async fn main() {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
store.add_executable("Windows", "./demo.exe");
|
for (exe_type, exe_path) in [
|
||||||
store.add_executable("Linux", "./demo-linux");
|
("Windows", "./demo.exe"),
|
||||||
// store.add_executable("MacOS", "./demo-macos");
|
("Linux", "./demo-linux"),
|
||||||
|
// ("MacOS", "./demo-macos"),
|
||||||
|
] {
|
||||||
|
if let Err(e) = store.add_executable(exe_type, exe_path) {
|
||||||
|
tracing::error!("{}", e);
|
||||||
|
std::process::exit(1);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
drop(store); // critical: Drop the lock to avoid deadlock, otherwise the server will hang
|
drop(store); // critical: Drop the lock to avoid deadlock, otherwise the server will hang
|
||||||
|
|
||||||
|
|||||||
@@ -1,10 +1,11 @@
|
|||||||
use std::collections::HashMap;
|
use std::collections::HashMap;
|
||||||
use std::path;
|
use std::path::{Path, PathBuf};
|
||||||
use std::sync::LazyLock;
|
use std::sync::LazyLock;
|
||||||
|
|
||||||
use salvo::{http::cookie::Cookie, Response};
|
use salvo::{http::cookie::Cookie, Response};
|
||||||
use tokio::sync::Mutex;
|
use tokio::sync::Mutex;
|
||||||
|
|
||||||
|
use crate::errors::{AppError, Result};
|
||||||
use crate::models::{BuildLogs, Executable, ExecutableJson, Session};
|
use crate::models::{BuildLogs, Executable, ExecutableJson, Session};
|
||||||
|
|
||||||
pub static STORE: LazyLock<Mutex<State>> = LazyLock::new(|| Mutex::new(State::new()));
|
pub static STORE: LazyLock<Mutex<State>> = LazyLock::new(|| Mutex::new(State::new()));
|
||||||
@@ -27,30 +28,49 @@ impl State {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn add_executable(&mut self, exe_type: &str, exe_path: &str) {
|
pub fn add_executable(&mut self, exe_type: &str, exe_path: &str) -> Result<()> {
|
||||||
let data = std::fs::read(exe_path).expect("Unable to read file");
|
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 pattern = "a".repeat(1024);
|
||||||
let key_start = Executable::search_pattern(&data, pattern.as_bytes(), 0).unwrap();
|
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 key_end = key_start + pattern.len();
|
||||||
|
|
||||||
let path = path::Path::new(&exe_path);
|
let extension = path
|
||||||
let name = path.file_stem().unwrap().to_str().unwrap();
|
.extension()
|
||||||
let extension = match path.extension() {
|
.and_then(|s| s.to_str())
|
||||||
Some(s) => s.to_str().unwrap(),
|
.unwrap_or_default()
|
||||||
None => "",
|
.to_string();
|
||||||
};
|
|
||||||
|
|
||||||
let exe = Executable {
|
let exe = Executable {
|
||||||
data,
|
data,
|
||||||
filename: path.file_name().unwrap().to_str().unwrap().to_string(),
|
filename: path
|
||||||
name: name.to_string(),
|
.file_name()
|
||||||
extension: extension.to_string(),
|
.and_then(|s| s.to_str())
|
||||||
|
.unwrap_or_default()
|
||||||
|
.to_string(),
|
||||||
|
name,
|
||||||
|
extension,
|
||||||
key_start,
|
key_start,
|
||||||
key_end,
|
key_end,
|
||||||
};
|
};
|
||||||
|
|
||||||
self.executables.insert(exe_type.to_string(), exe);
|
self.executables.insert(exe_type.to_string(), exe);
|
||||||
|
Ok(())
|
||||||
}
|
}
|
||||||
|
|
||||||
pub async fn new_session(&mut self, res: &mut Response) -> u32 {
|
pub async fn new_session(&mut self, res: &mut Response) -> u32 {
|
||||||
|
|||||||
Reference in New Issue
Block a user