DEVELOPMENT ENVIRONMENT

~liljamo/canwa

efb58ac0c57a9d4dc8e6f8ea6776696bd93e9979 — Jonni Liljamo 10 days ago 9621cd5
feat: route helpers
M src/routes/message.rs => src/routes/message.rs +7 -29
@@ 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<MessageForm>,
) -> 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, "")
}

M src/routes/mod.rs => src/routes/mod.rs +18 -0
@@ 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;

M src/service/email.rs => src/service/email.rs +4 -1
@@ 59,7 59,10 @@ impl EmailService {

#[async_trait]
impl Service for EmailService {
    async fn send(&self, form: &MessageForm) -> Result<(), Box<dyn std::error::Error>> {
    async fn send(
        &self,
        form: &MessageForm,
    ) -> Result<(), Box<dyn std::error::Error + Send + Sync>> {
        let message = Message::builder()
            .from(Mailbox::new(
                self.config.from_name.clone(),

M src/service/gotify.rs => src/service/gotify.rs +4 -1
@@ 45,7 45,10 @@ impl GotifyService {

#[async_trait]
impl Service for GotifyService {
    async fn send(&self, form: &MessageForm) -> Result<(), Box<dyn std::error::Error>> {
    async fn send(
        &self,
        form: &MessageForm,
    ) -> Result<(), Box<dyn std::error::Error + Send + Sync>> {
        let _ = self
            .client
            .post(format!("{}/message", self.config.instance))

M src/service/matrix.rs => src/service/matrix.rs +4 -1
@@ 61,7 61,10 @@ impl MatrixService {

#[async_trait]
impl Service for MatrixService {
    async fn send(&self, form: &MessageForm) -> Result<(), Box<dyn std::error::Error>> {
    async fn send(
        &self,
        form: &MessageForm,
    ) -> Result<(), Box<dyn std::error::Error + Send + Sync>> {
        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);

M src/service/mod.rs => src/service/mod.rs +4 -1
@@ 16,7 16,10 @@ pub mod matrix;

#[async_trait]
pub trait Service {
    async fn send(&self, form: &MessageForm) -> Result<(), Box<dyn std::error::Error>>;
    async fn send(
        &self,
        form: &MessageForm,
    ) -> Result<(), Box<dyn std::error::Error + Send + Sync>>;
}

macro_rules! make_service_match {

M src/state.rs => src/state.rs +25 -0
@@ 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<Box<dyn std::error::Error + Send + Sync>>> {
        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(()) }
    }
}