From 3802ed126f42d9b5fc99bde37e72a34778d1abf8 Mon Sep 17 00:00:00 2001 From: Paul Campbell Date: Tue, 23 Jul 2024 19:10:28 +0100 Subject: [PATCH] FIXUP server-actor handler notify-users --- .../server-actor/src/handlers/notify_user.rs | 88 ++++++++++++++----- 1 file changed, 65 insertions(+), 23 deletions(-) diff --git a/crates/server-actor/src/handlers/notify_user.rs b/crates/server-actor/src/handlers/notify_user.rs index cfb8d6f..118cd42 100644 --- a/crates/server-actor/src/handlers/notify_user.rs +++ b/crates/server-actor/src/handlers/notify_user.rs @@ -1,39 +1,81 @@ // use actix::prelude::*; -use git_next_config::server::NotificationType; +use git_next_config::server::{Notification, NotificationType}; use git_next_repo_actor::messages::NotifyUser; +use secrecy::ExposeSecret; +use standardwebhooks::Webhook; +use tracing::Instrument; use crate::ServerActor; impl Handler for ServerActor { type Result = (); - fn handle(&mut self, msg: NotifyUser, _ctx: &mut Self::Context) -> Self::Result { + fn handle(&mut self, msg: NotifyUser, ctx: &mut Self::Context) -> Self::Result { let Some(server_config) = &self.server_config else { return; }; - let notification = &server_config.notification(); - match notification.r#type() { - NotificationType::None => { /* do nothing */ } - NotificationType::Webhook => { - let message_id = format!("msg_{}", ulid::Ulid::new()); - let timestamp = time::OffsetDateTime::now_utc(); //.unix_timestamp().to_string(); - let payload = msg.as_json(timestamp).to_string(); - let timestamp = timestamp.unix_timestamp(); - let to_sign = format!("{message_id}.{timestamp}.{payload}"); - tracing::info!(?to_sign, ""); - let Some(webhook) = self.webhook.as_ref() else { - tracing::warn!("Invalid notification configuration - can't sent notification"); - return; - }; - #[allow(clippy::expect_used)] - let signature = webhook - .sign(&message_id, timestamp, payload.as_ref()) - .expect("signature"); - tracing::info!(?signature, ""); - // TODO: (#95) should notify user - // send post to notification webhook url + let notification_config = server_config.notification().clone(); + let net = self.net.clone(); + async move { + match notification_config.r#type() { + NotificationType::None => { /* do nothing */ } + NotificationType::Webhook => send_webhook(msg, notification_config, net).await, } } + .in_current_span() + .into_actor(self) + .wait(ctx); } } + +async fn send_webhook( + msg: NotifyUser, + notification_config: Notification, + net: kxio::network::Network, +) { + let Some(webhook_config) = notification_config.webhook() else { + tracing::warn!("Invalid notification configuration (config) - can't sent notification"); + return; + }; + let Ok(webhook) = Webhook::new(webhook_config.secret().expose_secret()) else { + tracing::warn!("Invalid notification configuration (signer) - can't sent notification"); + return; + }; + do_send_webhook(msg, webhook, webhook_config, net).await; +} + +async fn do_send_webhook( + msg: NotifyUser, + webhook: Webhook, + webhook_config: &git_next_config::server::OutboundWebhook, + net: kxio::network::Network, +) { + let message_id = format!("msg_{}", ulid::Ulid::new()); + let timestamp = time::OffsetDateTime::now_utc(); + let payload = msg.as_json(timestamp); + let timestamp = timestamp.unix_timestamp(); + let to_sign = format!("{message_id}.{timestamp}.{payload}"); + tracing::info!(?to_sign, ""); + #[allow(clippy::expect_used)] + let signature = webhook + .sign(&message_id, timestamp, payload.to_string().as_ref()) + .expect("signature"); + tracing::info!(?signature, ""); + let url = webhook_config.url(); + use kxio::network::{NetRequest, NetUrl, RequestBody, ResponseType}; + let net_url = NetUrl::new(url.to_string()); + let request = NetRequest::post(net_url) + .body(RequestBody::Json(payload)) + .header("webhook-id", &message_id) + .header("webhook-timestamp", ×tamp.to_string()) + .header("webhook-signature", &signature) + .response_type(ResponseType::None) + .build(); + net.post_json::<()>(request).await.map_or_else( + |err| { + tracing::warn!(?err, "sending webhook"); + }, + |_| (), + ); +}