diff --git a/bin/src/config.rs b/bin/src/config.rs index 045ea00..5d3ffec 100644 --- a/bin/src/config.rs +++ b/bin/src/config.rs @@ -4,7 +4,7 @@ use rayhunter::analysis::analyzer::AnalyzerConfig; use crate::error::RayhunterError; -#[derive(Debug, Deserialize, Serialize)] +#[derive(Debug, Clone, Deserialize, Serialize)] #[serde(default)] pub struct Config { pub qmdl_store_path: String, diff --git a/bin/src/daemon.rs b/bin/src/daemon.rs index 5435886..4a9817d 100644 --- a/bin/src/daemon.rs +++ b/bin/src/daemon.rs @@ -19,7 +19,7 @@ use crate::diag::run_diag_read_thread; use crate::error::RayhunterError; use crate::pcap::get_pcap; use crate::qmdl_store::RecordingStore; -use crate::server::{get_config, get_qmdl, restart_daemon, serve_static, set_config, ServerState}; +use crate::server::{get_config, get_qmdl, serve_static, set_config, ServerState}; use crate::stats::{get_qmdl_manifest, get_system_stats}; use analysis::{ @@ -57,7 +57,6 @@ fn get_router() -> AppRouter { .route("/api/analysis-report/{name}", get(get_analysis_report)) .route("/api/analysis", get(get_analysis_status)) .route("/api/analysis/{name}", post(start_analysis)) - .route("/api/restart-daemon", post(restart_daemon)) .route("/api/config", get(get_config)) .route("/api/config", post(set_config)) .route("/", get(|| async { Redirect::permanent("/index.html") })) @@ -69,14 +68,14 @@ fn get_router() -> AppRouter { // (i.e. user hit ctrl+c) async fn run_server( task_tracker: &TaskTracker, - config: &config::Config, state: Arc, server_shutdown_rx: oneshot::Receiver<()>, ) -> JoinHandle<()> { info!("spinning up server"); - let app = get_router().with_state(state); - let addr = SocketAddr::from(([0, 0, 0, 0], config.port)); + let addr = SocketAddr::from(([0, 0, 0, 0], state.config.port)); let listener = TcpListener::bind(&addr).await.unwrap(); + let app = get_router().with_state(state); + task_tracker.spawn(async move { info!("The orca is hunting for stingrays..."); axum::serve(listener, app) @@ -189,7 +188,7 @@ async fn main() -> Result<(), RayhunterError> { loop { let config = parse_config(&args.config_path).await?; - if !run_with_config(&args, &config).await? { + if !run_with_config(&args, config).await? { return Ok(()); } } @@ -197,7 +196,7 @@ async fn main() -> Result<(), RayhunterError> { async fn run_with_config( args: &config::Args, - config: &config::Config, + config: config::Config, ) -> Result { // TaskTrackers give us an interface to spawn tokio threads, and then // eventually await all of them ending @@ -273,15 +272,15 @@ async fn run_with_config( ); let state = Arc::new(ServerState { config_path: args.config_path.clone(), + config, qmdl_store_lock: qmdl_store_lock.clone(), diag_device_ctrl_sender: diag_tx, ui_update_sender: ui_update_tx, - debug_mode: config.debug_mode, analysis_status_lock, analysis_sender: analysis_tx, daemon_restart_tx: Arc::new(RwLock::new(Some(daemon_restart_tx))), }); - run_server(&task_tracker, &config, state, server_shutdown_rx).await; + run_server(&task_tracker, state, server_shutdown_rx).await; task_tracker.close(); task_tracker.wait().await; diff --git a/bin/src/diag.rs b/bin/src/diag.rs index bcfb501..bf35e6e 100644 --- a/bin/src/diag.rs +++ b/bin/src/diag.rs @@ -158,7 +158,7 @@ pub fn run_diag_read_thread( pub async fn start_recording( State(state): State>, ) -> Result<(StatusCode, String), (StatusCode, String)> { - if state.debug_mode { + if state.config.debug_mode { return Err((StatusCode::FORBIDDEN, "server is in debug mode".to_string())); } @@ -179,7 +179,7 @@ pub async fn start_recording( pub async fn stop_recording( State(state): State>, ) -> Result<(StatusCode, String), (StatusCode, String)> { - if state.debug_mode { + if state.config.debug_mode { return Err((StatusCode::FORBIDDEN, "server is in debug mode".to_string())); } state @@ -199,7 +199,7 @@ pub async fn delete_recording( State(state): State>, Path(qmdl_name): Path, ) -> Result<(StatusCode, String), (StatusCode, String)> { - if state.debug_mode { + if state.config.debug_mode { return Err((StatusCode::FORBIDDEN, "server is in debug mode".to_string())); } let mut qmdl_store = state.qmdl_store_lock.write().await; @@ -244,7 +244,7 @@ pub async fn delete_recording( pub async fn delete_all_recordings( State(state): State>, ) -> Result<(StatusCode, String), (StatusCode, String)> { - if state.debug_mode { + if state.config.debug_mode { return Err((StatusCode::FORBIDDEN, "server is in debug mode".to_string())); } state diff --git a/bin/src/server.rs b/bin/src/server.rs index 7119999..e90c89c 100644 --- a/bin/src/server.rs +++ b/bin/src/server.rs @@ -14,19 +14,18 @@ use tokio::sync::{oneshot, RwLock}; use tokio_util::io::ReaderStream; use crate::analysis::{AnalysisCtrlMessage, AnalysisStatus}; -use crate::config::parse_config; use crate::config::Config; use crate::qmdl_store::RecordingStore; use crate::{display, DiagDeviceCtrlMessage}; pub struct ServerState { pub config_path: String, + pub config: Config, pub qmdl_store_lock: Arc>, pub diag_device_ctrl_sender: Sender, pub ui_update_sender: Sender, pub analysis_status_lock: Arc>, pub analysis_sender: Sender, - pub debug_mode: bool, pub daemon_restart_tx: Arc>>>, } @@ -86,38 +85,10 @@ pub async fn serve_static( } } -pub async fn restart_daemon( - State(state): State>, -) -> Result<(StatusCode, String), (StatusCode, String)> { - let mut restart_tx = state.daemon_restart_tx.write().await; - - if let Some(sender) = restart_tx.take() { - sender.send(()).map_err(|_| { - ( - StatusCode::INTERNAL_SERVER_ERROR, - "couldn't send restart signal".to_string(), - ) - })?; - Ok((StatusCode::ACCEPTED, "restart signal sent".to_string())) - } else { - Ok(( - StatusCode::ACCEPTED, - "restart already triggered".to_string(), - )) - } -} - pub async fn get_config( State(state): State>, ) -> Result, (StatusCode, String)> { - let config = parse_config(&state.config_path).await.map_err(|err| { - ( - StatusCode::INTERNAL_SERVER_ERROR, - format!("failed to read config file: {}", err), - ) - })?; - - Ok(Json(config)) + Ok(Json(state.config.clone())) } pub async fn set_config( @@ -138,5 +109,23 @@ pub async fn set_config( ) })?; - Ok((StatusCode::ACCEPTED, "wrote config".to_string())) + // Trigger daemon restart after writing config + let mut restart_tx = state.daemon_restart_tx.write().await; + if let Some(sender) = restart_tx.take() { + sender.send(()).map_err(|_| { + ( + StatusCode::INTERNAL_SERVER_ERROR, + "couldn't send restart signal".to_string(), + ) + })?; + Ok(( + StatusCode::ACCEPTED, + "wrote config and triggered restart".to_string(), + )) + } else { + Ok(( + StatusCode::ACCEPTED, + "wrote config but restart already triggered".to_string(), + )) + } } diff --git a/bin/web/src/lib/components/ConfigForm.svelte b/bin/web/src/lib/components/ConfigForm.svelte index a489aa5..4c7819e 100644 --- a/bin/web/src/lib/components/ConfigForm.svelte +++ b/bin/web/src/lib/components/ConfigForm.svelte @@ -5,7 +5,6 @@ let loading = $state(false); let saving = $state(false); - let restarting = $state(false); let message = $state(""); let messageType = $state<"success" | "error" | null>(null); let showConfig = $state(false); @@ -30,7 +29,7 @@ try { saving = true; await set_config(config); - message = "Config saved successfully!"; + message = "Config saved successfully! Rayhunter is restarting now. Reload the page in a few seconds."; messageType = "success"; } catch (error) { message = `Failed to save config: ${error}`; @@ -40,23 +39,6 @@ } } - async function restartRayhunter() { - if (!window.confirm('Are you sure you want to restart Rayhunter? This will temporarily stop all monitoring.')) { - return; - } - - try { - restarting = true; - await fetch('/api/restart-daemon', { method: 'POST' }); - message = "Rayhunter restart initiated!"; - messageType = "success"; - } catch (error) { - message = `Failed to restart Rayhunter: ${error}`; - messageType = "error"; - } finally { - restarting = false; - } - } // Load config when first shown $effect(() => { @@ -192,26 +174,10 @@ - Save Config + Apply and restart {/if} - {#if message}