diff --git a/daemon/src/main.rs b/daemon/src/main.rs index a1dedbe..8ebb37c 100644 --- a/daemon/src/main.rs +++ b/daemon/src/main.rs @@ -23,6 +23,7 @@ use crate::pcap::get_pcap; use crate::qmdl_store::RecordingStore; use crate::server::{ ServerState, debug_set_display_state, get_config, get_qmdl, get_zip, serve_static, set_config, + test_notification, }; use crate::stats::{get_qmdl_manifest, get_system_stats}; @@ -68,6 +69,7 @@ fn get_router() -> AppRouter { .route("/api/analysis/{name}", post(start_analysis)) .route("/api/config", get(get_config)) .route("/api/config", post(set_config)) + .route("/api/test-notification", post(test_notification)) .route("/api/debug/display-state", post(debug_set_display_state)) .route("/", get(|| async { Redirect::permanent("/index.html") })) .route("/{*path}", get(serve_static)) diff --git a/daemon/src/notifications.rs b/daemon/src/notifications.rs index dff944c..4e5bc85 100644 --- a/daemon/src/notifications.rs +++ b/daemon/src/notifications.rs @@ -6,9 +6,18 @@ use std::{ use log::error; use serde::{Deserialize, Serialize}; +use thiserror::Error; use tokio::sync::mpsc::{self, error::TryRecvError}; use tokio_util::task::TaskTracker; +#[derive(Error, Debug)] +pub enum NotificationError { + #[error("HTTP request failed: {0}")] + RequestFailed(#[from] reqwest::Error), + #[error("Server returned error status: {0}")] + HttpError(reqwest::StatusCode), +} + #[derive(Hash, Eq, PartialEq, Debug, Clone, Serialize, Deserialize)] pub enum NotificationType { Warning, @@ -61,21 +70,17 @@ impl NotificationService { } /// Sends a notification message to the specified URL. -/// Returns true if the notification was sent successfully, false otherwise. -async fn send_notification(http_client: &reqwest::Client, url: &str, message: String) -> bool { - match http_client.post(url).body(message).send().await { - Ok(response) => { - if response.status().is_success() { - true - } else { - error!("Failed to send notification: HTTP {}", response.status()); - false - } - } - Err(e) => { - error!("Failed to send notification to ntfy: {e}"); - false - } +pub async fn send_notification( + http_client: &reqwest::Client, + url: &str, + message: String, +) -> Result<(), NotificationError> { + let response = http_client.post(url).body(message).send().await?; + + if response.status().is_success() { + Ok(()) + } else { + Err(NotificationError::HttpError(response.status())) } } @@ -144,13 +149,18 @@ pub fn run_notification_worker( } } - if send_notification(&http_client, &url, notification.message.clone()).await { - notification.last_sent = Some(Instant::now()); - notification.failed_since_last_success = 0; - notification.needs_sending = false; - } else { - notification.failed_since_last_success += 1; - notification.last_attempt = Some(Instant::now()); + match send_notification(&http_client, &url, notification.message.clone()).await + { + Ok(()) => { + notification.last_sent = Some(Instant::now()); + notification.failed_since_last_success = 0; + notification.needs_sending = false; + } + Err(e) => { + error!("Failed to send notification: {e}"); + notification.failed_since_last_success += 1; + notification.last_attempt = Some(Instant::now()); + } } } diff --git a/daemon/src/server.rs b/daemon/src/server.rs index b9ba5bb..043b479 100644 --- a/daemon/src/server.rs +++ b/daemon/src/server.rs @@ -136,6 +136,40 @@ pub async fn set_config( )) } +pub async fn test_notification( + State(state): State>, +) -> Result<(StatusCode, String), (StatusCode, String)> { + let url = state.config.ntfy_url.as_ref().ok_or(( + StatusCode::BAD_REQUEST, + "No notification URL configured".to_string(), + ))?; + + if url.is_empty() { + return Err(( + StatusCode::BAD_REQUEST, + "Notification URL is empty".to_string(), + )); + } + + let http_client = reqwest::Client::new(); + let message = "Test notification from Rayhunter".to_string(); + + crate::notifications::send_notification(&http_client, url, message) + .await + .map(|()| { + ( + StatusCode::OK, + "Test notification sent successfully".to_string(), + ) + }) + .map_err(|e| { + ( + StatusCode::INTERNAL_SERVER_ERROR, + format!("Failed to send test notification: {e}"), + ) + }) +} + pub async fn get_zip( State(state): State>, Path(entry_name): Path, diff --git a/daemon/web/src/lib/components/ConfigForm.svelte b/daemon/web/src/lib/components/ConfigForm.svelte index edfac92..f0b7a90 100644 --- a/daemon/web/src/lib/components/ConfigForm.svelte +++ b/daemon/web/src/lib/components/ConfigForm.svelte @@ -1,12 +1,15 @@