From efb58ac0c57a9d4dc8e6f8ea6776696bd93e9979 Mon Sep 17 00:00:00 2001 From: Jonni Liljamo Date: Mon, 24 Nov 2025 23:41:20 +0200 Subject: [PATCH] feat: route helpers --- src/routes/message.rs | 36 +++++++----------------------------- src/routes/mod.rs | 18 ++++++++++++++++++ src/service/email.rs | 5 ++++- src/service/gotify.rs | 5 ++++- src/service/matrix.rs | 5 ++++- src/service/mod.rs | 5 ++++- src/state.rs | 25 +++++++++++++++++++++++++ 7 files changed, 66 insertions(+), 33 deletions(-) diff --git a/src/routes/message.rs b/src/routes/message.rs index fd3c96d..165b825 100644 --- a/src/routes/message.rs +++ b/src/routes/message.rs @@ -10,9 +10,8 @@ use std::sync::Arc; use axum::{Json, http::HeaderMap, response::IntoResponse}; use reqwest::StatusCode; use serde::Deserialize; -use tracing::{error, info}; -use crate::state::State; +use crate::{routes::extract_token, state::State}; #[derive(Deserialize)] pub struct MessageForm { @@ -27,38 +26,17 @@ pub async fn message( headers: HeaderMap, Json(message): Json, ) -> impl IntoResponse { - let token = match headers.get("Authorization") { - Some(token) => match token.to_str() { - Ok(token) => token, - Err(_) => { - return (StatusCode::UNAUTHORIZED, "unauthorized"); - } - }, - None => { - return (StatusCode::UNAUTHORIZED, "unauthorized"); - } - }; + let token = extract_token!(headers); - let notifier = match state.notifiers.iter().find(|(_k, v)| v.token == token) { + let notifier = match state.find_notifier(token) { Some(n) => n, None => return (StatusCode::UNAUTHORIZED, "unauthorized"), }; - info!(msg = "message", notifier = notifier.0); - - for (_k, v) in state - .services - .iter() - .filter(|(k, _v)| notifier.1.services.contains(k)) - { - match v.send(&message).await { - Ok(_) => {} - Err(err) => { - error!(msg = "message sending failed", ?err); - return (StatusCode::INTERNAL_SERVER_ERROR, "failed to send message"); - } - } - } + if let Err(err) = state.send_message(notifier, &message).await { + tracing::error!(?err, "message sending failed"); + return (StatusCode::INTERNAL_SERVER_ERROR, "failed to send message"); + }; (StatusCode::OK, "") } diff --git a/src/routes/mod.rs b/src/routes/mod.rs index 42f212d..c22fbb6 100644 --- a/src/routes/mod.rs +++ b/src/routes/mod.rs @@ -6,3 +6,21 @@ */ pub mod message; + +macro_rules! extract_token { + ($headers:expr) => { + match $headers.get("Authorization") { + Some(token) => match token.to_str() { + Ok(token) => token, + Err(_) => { + return (StatusCode::UNAUTHORIZED, "unauthorized"); + } + }, + None => { + return (StatusCode::UNAUTHORIZED, "unauthorized"); + } + } + }; +} + +use extract_token; diff --git a/src/service/email.rs b/src/service/email.rs index d754a9a..813b00f 100644 --- a/src/service/email.rs +++ b/src/service/email.rs @@ -59,7 +59,10 @@ impl EmailService { #[async_trait] impl Service for EmailService { - async fn send(&self, form: &MessageForm) -> Result<(), Box> { + async fn send( + &self, + form: &MessageForm, + ) -> Result<(), Box> { let message = Message::builder() .from(Mailbox::new( self.config.from_name.clone(), diff --git a/src/service/gotify.rs b/src/service/gotify.rs index b409856..79157da 100644 --- a/src/service/gotify.rs +++ b/src/service/gotify.rs @@ -45,7 +45,10 @@ impl GotifyService { #[async_trait] impl Service for GotifyService { - async fn send(&self, form: &MessageForm) -> Result<(), Box> { + async fn send( + &self, + form: &MessageForm, + ) -> Result<(), Box> { let _ = self .client .post(format!("{}/message", self.config.instance)) diff --git a/src/service/matrix.rs b/src/service/matrix.rs index 5166923..2bd5836 100644 --- a/src/service/matrix.rs +++ b/src/service/matrix.rs @@ -61,7 +61,10 @@ impl MatrixService { #[async_trait] impl Service for MatrixService { - async fn send(&self, form: &MessageForm) -> Result<(), Box> { + async fn send( + &self, + form: &MessageForm, + ) -> Result<(), Box> { let body = if form.format_commonmark { let bold_title = format!("**{}**", form.title.trim()); let title_parser = pulldown_cmark::Parser::new_ext(&bold_title, COMMONMARK_OPTIONS); diff --git a/src/service/mod.rs b/src/service/mod.rs index 7c9629a..6d60498 100644 --- a/src/service/mod.rs +++ b/src/service/mod.rs @@ -16,7 +16,10 @@ pub mod matrix; #[async_trait] pub trait Service { - async fn send(&self, form: &MessageForm) -> Result<(), Box>; + async fn send( + &self, + form: &MessageForm, + ) -> Result<(), Box>; } macro_rules! make_service_match { diff --git a/src/state.rs b/src/state.rs index 706e57e..73d3e33 100644 --- a/src/state.rs +++ b/src/state.rs @@ -9,6 +9,7 @@ use std::{collections::HashMap, sync::Arc}; use crate::{ config::{Config, NotifierConfig}, + routes::message::MessageForm, service::{self, Service}, }; @@ -40,4 +41,28 @@ impl State { notifiers: config.notifiers.clone(), }) } + + pub fn find_notifier(&self, token: &str) -> Option<(&String, &NotifierConfig)> { + self.notifiers.iter().find(|(_k, v)| v.token == token) + } + + pub async fn send_message( + &self, + notifier: (&String, &NotifierConfig), + form: &MessageForm, + ) -> Result<(), Vec>> { + let mut errs = vec![]; + + for (_k, v) in self + .services + .iter() + .filter(|(k, _v)| notifier.1.services.contains(k)) + { + if let Err(err) = v.send(form).await { + errs.push(err); + }; + } + + if !errs.is_empty() { Err(errs) } else { Ok(()) } + } } -- 2.44.1