use bitflags::bitflags;
use diesel::{ExpressionMethods, QueryDsl};
use diesel_async::RunQueryDsl;
use tracing::*;
use uuid::Uuid;
bitflags! {
#[derive(Debug, Clone, Copy)]
pub struct Action: i64 {
const NEW_DISCUSSION = 0x1;
const ADD_PATCH = 0x2;
const APPLY_PATCH = 0x4;
}
}
#[derive(Debug)]
pub struct Hook {
pub url: String,
pub secret: String,
}
impl Hook {
async fn run(&self, body: String) -> Result<(), crate::Error> {
debug!("hook: {:?}", self);
let client = reqwest::Client::new();
let mut req = client.post(&self.url);
if let Ok(url) = self.url.parse::<url::Url>() {
if let (Some(host), Some(port)) = (url.host(), url.port()) {
req = req.header("Host", format!("{}:{}", host, port).as_str());
}
}
let signature = {
use openssl::hash::MessageDigest;
use openssl::pkey::PKey;
use openssl::sign::Signer;
let pkey = PKey::hmac(self.secret.as_bytes()).unwrap();
let mut signer = Signer::new(MessageDigest::sha256(), &pkey).unwrap();
signer.update(body.as_bytes()).unwrap();
signer.sign_to_vec().unwrap()
};
let s = format!("sha256={}", data_encoding::HEXLOWER.encode(&signature));
req.header("X-Nest-Event-Signature", s.as_str())
.body(body)
.send()
.await?;
Ok(())
}
}
pub async fn run_hooks_by_repo_id(
db: &mut diesel_async::AsyncPgConnection,
id: Uuid,
action: Action,
contents: &pijul_hooks::HookContent,
) -> Result<(), crate::Error> {
let body = serde_json::to_string(contents).unwrap();
use crate::db::hooks::dsl as hooks;
for (url, secret, a) in hooks::hooks
.filter(hooks::repository.eq(id))
.select((hooks::url, hooks::secret, hooks::action))
.get_results::<(String, String, Option<i64>)>(db)
.await?
{
if let Some(a) = a {
if (action.bits() & a) != 0 {
(Hook { url, secret }).run(body.clone()).await?
}
} else {
(Hook { url, secret }).run(body.clone()).await?
}
}
Ok(())
}