use std::{ net::SocketAddr, collections::HashMap, sync::{Arc, Mutex}, }; use axum::{ extract::State, routing::post, http::StatusCode, response::{IntoResponse, Response}, Json, Router, }; use serde::Deserialize; mod jabber; #[derive(Deserialize, Clone, Debug)] #[allow(non_snake_case)] struct Alert { generatorURL: String, startsAt: String, annotations: AlertAnnotations, labels: HashMap, } #[derive(Deserialize, Clone, Debug)] struct AlertAnnotations { summary: String, } #[derive(Clone)] struct AppState { jabber: jabber::Handle, alerts: Arc>>, } async fn alerts( State(state): State, Json(payload): Json>, ) -> Response { let mut message = String::new(); let mut alerts = state.alerts.lock().unwrap(); for alert in payload { let is_old = if let Some(old_alert) = alerts.get(&alert.generatorURL) { old_alert.startsAt == alert.startsAt } else { false }; if ! is_old { if !message.is_empty() { message += "\n"; } let title = match (alert.labels.get("alertname"), alert.labels.get("instance")) { (Some(name), Some(instance)) => format!("{}@{}", name, instance), _ => alert.annotations.summary.clone(), }; message += &format!("{}: {}", title, alert.generatorURL); alerts.insert(alert.generatorURL.clone(), alert); } } drop(alerts); if !message.is_empty() { let jabber = state.jabber.clone(); tokio::spawn(async move { jabber.send_message(message).await; }); } StatusCode::OK.into_response() } #[derive(Deserialize)] struct Config { listen_port: u16, jid: String, password: String, muc: String, } #[tokio::main] async fn main() { // initialize tracing tracing_subscriber::fmt::init(); let _config: Config = serde_json::from_str( &std::fs::read_to_string( std::env::args().nth(1) .expect("Call with config.json") ).expect("read config") ).expect("parse config"); let jabber = jabber::run(/*secret*/).await; let state = AppState { jabber, alerts: Arc::new(Mutex::new(HashMap::new())), }; // build our application with a route let app = Router::new() .route("/api/v2/alerts", post(alerts)) .with_state(state); let addr = SocketAddr::from(([127, 0, 0, 1], 9022)); tracing::debug!("listening on {}", addr); let server = axum::Server::bind(&addr) .serve(app.into_make_service()); systemd::daemon::notify(false, [(systemd::daemon::STATE_READY, "1")].iter()) .unwrap(); server.await .unwrap(); }