WIP
This commit is contained in:
parent
ab2af2596e
commit
72e409f952
4 changed files with 73 additions and 48 deletions
|
@ -243,3 +243,11 @@ pub struct OutboundWebhook {
|
||||||
url: String,
|
url: String,
|
||||||
secret: String,
|
secret: String,
|
||||||
}
|
}
|
||||||
|
impl OutboundWebhook {
|
||||||
|
pub fn url(&self) -> &str {
|
||||||
|
self.url.as_ref()
|
||||||
|
}
|
||||||
|
pub fn secret(&self) -> Secret<String> {
|
||||||
|
Secret::new(self.secret.clone())
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
|
@ -1,39 +1,81 @@
|
||||||
//
|
//
|
||||||
use actix::prelude::*;
|
use actix::prelude::*;
|
||||||
use git_next_config::server::NotificationType;
|
use git_next_config::server::{Notification, NotificationType};
|
||||||
use git_next_repo_actor::messages::NotifyUser;
|
use git_next_repo_actor::messages::NotifyUser;
|
||||||
|
use secrecy::ExposeSecret;
|
||||||
|
use standardwebhooks::Webhook;
|
||||||
|
use tracing::Instrument;
|
||||||
|
|
||||||
use crate::ServerActor;
|
use crate::ServerActor;
|
||||||
|
|
||||||
impl Handler<NotifyUser> for ServerActor {
|
impl Handler<NotifyUser> for ServerActor {
|
||||||
type Result = ();
|
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 {
|
let Some(server_config) = &self.server_config else {
|
||||||
return;
|
return;
|
||||||
};
|
};
|
||||||
let notification = &server_config.notification();
|
let notification_config = server_config.notification().clone();
|
||||||
match notification.r#type() {
|
let net = self.net.clone();
|
||||||
NotificationType::None => { /* do nothing */ }
|
async move {
|
||||||
NotificationType::Webhook => {
|
match notification_config.r#type() {
|
||||||
let message_id = format!("msg_{}", ulid::Ulid::new());
|
NotificationType::None => { /* do nothing */ }
|
||||||
let timestamp = time::OffsetDateTime::now_utc(); //.unix_timestamp().to_string();
|
NotificationType::Webhook => send_webhook(msg, notification_config, net).await,
|
||||||
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
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
.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");
|
||||||
|
},
|
||||||
|
|_| (),
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
|
@ -1,7 +1,5 @@
|
||||||
use actix::prelude::*;
|
use actix::prelude::*;
|
||||||
use git_next_config::server::NotificationType;
|
|
||||||
use git_next_webhook_actor::{AddWebhookRecipient, ShutdownWebhook, WebhookActor, WebhookRouter};
|
use git_next_webhook_actor::{AddWebhookRecipient, ShutdownWebhook, WebhookActor, WebhookRouter};
|
||||||
use standardwebhooks::Webhook;
|
|
||||||
|
|
||||||
use crate::{
|
use crate::{
|
||||||
messages::{ReceiveValidServerConfig, ValidServerConfig},
|
messages::{ReceiveValidServerConfig, ValidServerConfig},
|
||||||
|
@ -21,26 +19,6 @@ impl Handler<ReceiveValidServerConfig> for ServerActor {
|
||||||
if let Some(webhook_actor_addr) = self.webhook_actor_addr.take() {
|
if let Some(webhook_actor_addr) = self.webhook_actor_addr.take() {
|
||||||
webhook_actor_addr.do_send(ShutdownWebhook);
|
webhook_actor_addr.do_send(ShutdownWebhook);
|
||||||
}
|
}
|
||||||
match server_config.notification().r#type() {
|
|
||||||
NotificationType::None => { /* do nothing */ }
|
|
||||||
NotificationType::Webhook => {
|
|
||||||
// Create webhook signer
|
|
||||||
use secrecy::ExposeSecret;
|
|
||||||
let webhook = server_config
|
|
||||||
.notification()
|
|
||||||
.webhook_secret()
|
|
||||||
.map(|secret| Webhook::new(secret.expose_secret()))
|
|
||||||
.transpose()
|
|
||||||
.map_err(|e| {
|
|
||||||
tracing::error!(
|
|
||||||
"Invalid notification webhook secret (will not send notifications): {e}"
|
|
||||||
)
|
|
||||||
})
|
|
||||||
.ok()
|
|
||||||
.flatten();
|
|
||||||
self.webhook = webhook;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
self.generation.inc();
|
self.generation.inc();
|
||||||
// Webhook Server
|
// Webhook Server
|
||||||
tracing::info!("Starting Webhook Server...");
|
tracing::info!("Starting Webhook Server...");
|
||||||
|
|
|
@ -14,7 +14,6 @@ use git_next_repo_actor::messages::NotifyUser;
|
||||||
use git_next_repo_actor::{messages::CloneRepo, RepoActor};
|
use git_next_repo_actor::{messages::CloneRepo, RepoActor};
|
||||||
use git_next_webhook_actor as webhook;
|
use git_next_webhook_actor as webhook;
|
||||||
use kxio::{fs::FileSystem, network::Network};
|
use kxio::{fs::FileSystem, network::Network};
|
||||||
use standardwebhooks::Webhook;
|
|
||||||
use std::{
|
use std::{
|
||||||
collections::BTreeMap,
|
collections::BTreeMap,
|
||||||
path::PathBuf,
|
path::PathBuf,
|
||||||
|
@ -54,7 +53,6 @@ pub struct ServerActor {
|
||||||
repository_factory: Box<dyn RepositoryFactory>,
|
repository_factory: Box<dyn RepositoryFactory>,
|
||||||
sleep_duration: std::time::Duration,
|
sleep_duration: std::time::Duration,
|
||||||
repo_actors: BTreeMap<(ForgeAlias, RepoAlias), Addr<RepoActor>>,
|
repo_actors: BTreeMap<(ForgeAlias, RepoAlias), Addr<RepoActor>>,
|
||||||
webhook: Option<Webhook>,
|
|
||||||
|
|
||||||
// testing
|
// testing
|
||||||
message_log: Option<Arc<RwLock<Vec<String>>>>,
|
message_log: Option<Arc<RwLock<Vec<String>>>>,
|
||||||
|
@ -80,7 +78,6 @@ impl ServerActor {
|
||||||
repository_factory: repo,
|
repository_factory: repo,
|
||||||
sleep_duration,
|
sleep_duration,
|
||||||
repo_actors: BTreeMap::new(),
|
repo_actors: BTreeMap::new(),
|
||||||
webhook: None,
|
|
||||||
message_log: None,
|
message_log: None,
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
Loading…
Reference in a new issue