2024-04-13 16:16:09 +01:00
|
|
|
use actix::prelude::*;
|
2024-04-14 06:48:26 +01:00
|
|
|
use kxio::network::{self, json};
|
2024-04-13 20:59:57 +01:00
|
|
|
use tracing::{info, warn};
|
2024-04-13 16:16:09 +01:00
|
|
|
|
2024-04-13 20:59:57 +01:00
|
|
|
use std::{fmt::Display, ops::Deref};
|
2024-04-09 19:30:05 +01:00
|
|
|
|
2024-04-14 06:48:26 +01:00
|
|
|
use crate::server::{
|
|
|
|
actors::{
|
|
|
|
repo::{RepoActor, WebhookRegistered},
|
|
|
|
webhook::WebhookMessage,
|
|
|
|
},
|
|
|
|
config::Webhook,
|
2024-04-13 16:16:09 +01:00
|
|
|
};
|
2024-04-11 18:30:36 +01:00
|
|
|
|
2024-04-09 19:30:05 +01:00
|
|
|
#[derive(Clone, Debug, PartialEq, Eq)]
|
|
|
|
pub struct WebhookId(String);
|
|
|
|
impl WebhookId {
|
|
|
|
#[allow(dead_code)]
|
|
|
|
pub const fn new(id: String) -> Self {
|
|
|
|
Self(id)
|
|
|
|
}
|
|
|
|
}
|
|
|
|
impl Deref for WebhookId {
|
|
|
|
type Target = String;
|
|
|
|
fn deref(&self) -> &Self::Target {
|
|
|
|
&self.0
|
|
|
|
}
|
|
|
|
}
|
2024-04-13 20:59:57 +01:00
|
|
|
impl Display for WebhookId {
|
|
|
|
fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
|
|
|
|
write!(f, "{}", self.0)
|
|
|
|
}
|
|
|
|
}
|
2024-04-09 19:30:05 +01:00
|
|
|
|
|
|
|
pub async fn unregister(
|
2024-04-13 20:59:57 +01:00
|
|
|
webhook_id: WebhookId,
|
|
|
|
repo_details: crate::server::config::RepoDetails,
|
|
|
|
net: network::Network,
|
2024-04-09 19:30:05 +01:00
|
|
|
) {
|
2024-04-13 20:59:57 +01:00
|
|
|
info!(?webhook_id, "unregister webhook");
|
|
|
|
let hostname = &repo_details.forge.hostname;
|
|
|
|
let path = repo_details.repo;
|
2024-04-14 06:48:26 +01:00
|
|
|
use secrecy::ExposeSecret;
|
|
|
|
let token = repo_details.forge.token.expose_secret();
|
2024-04-13 20:59:57 +01:00
|
|
|
let url = network::NetUrl::new(format!(
|
2024-04-14 06:48:26 +01:00
|
|
|
"https://{hostname}/api/v1/repos/{path}/hooks/{webhook_id}?token={token}"
|
2024-04-13 20:59:57 +01:00
|
|
|
));
|
|
|
|
let request = network::NetRequest::new(
|
|
|
|
network::RequestMethod::Delete,
|
|
|
|
url,
|
|
|
|
network::NetRequestHeaders::new(),
|
|
|
|
network::RequestBody::None,
|
|
|
|
network::ResponseType::Json,
|
|
|
|
None,
|
|
|
|
network::NetRequestLogging::None,
|
|
|
|
);
|
|
|
|
let result = net.delete(request).await;
|
|
|
|
match result {
|
|
|
|
Ok(_) => info!(?webhook_id, "unregistered webhook"),
|
|
|
|
Err(_) => warn!(?webhook_id, "Failed to unregister webhook"),
|
|
|
|
}
|
2024-04-09 19:30:05 +01:00
|
|
|
}
|
|
|
|
|
|
|
|
pub async fn register(
|
2024-04-14 06:48:26 +01:00
|
|
|
repo_details: crate::server::config::RepoDetails,
|
|
|
|
webhook: Webhook,
|
2024-04-11 18:30:36 +01:00
|
|
|
addr: actix::prelude::Addr<super::RepoActor>,
|
2024-04-14 06:48:26 +01:00
|
|
|
net: network::Network,
|
2024-04-09 19:30:05 +01:00
|
|
|
) {
|
2024-04-14 06:48:26 +01:00
|
|
|
let Some(repo_config) = repo_details.config else {
|
|
|
|
return;
|
|
|
|
};
|
|
|
|
info!("Registering webhook");
|
|
|
|
let hostname = &repo_details.forge.hostname;
|
|
|
|
let path = repo_details.repo;
|
|
|
|
use secrecy::ExposeSecret;
|
|
|
|
let token = repo_details.forge.token.expose_secret();
|
|
|
|
let url = network::NetUrl::new(format!(
|
|
|
|
"https://{hostname}/api/v1/repos/{path}/hooks?token={token}"
|
|
|
|
));
|
|
|
|
let webhook_url = webhook.url();
|
|
|
|
let repo_alias = repo_details.name;
|
|
|
|
let headers = network::NetRequestHeaders::new().with("Content-Type", "application/json");
|
|
|
|
let authorisation = ulid::Ulid::new();
|
|
|
|
let body = json!({
|
|
|
|
"active": true,
|
|
|
|
"authorization_header": format!("Basic {}", authorisation),
|
|
|
|
"branch_filter": format!("{{{},{},{}}}", repo_config.branches().main(), repo_config.branches().next(), repo_config.branches().dev()),
|
|
|
|
"config": {
|
|
|
|
"content_type": "json",
|
|
|
|
"url": format!("{}/{}", webhook_url.as_ref(), repo_alias),
|
|
|
|
},
|
|
|
|
"events": [ "push" ],
|
|
|
|
"type": "forgejo"
|
|
|
|
});
|
|
|
|
let request = network::NetRequest::new(
|
|
|
|
network::RequestMethod::Post,
|
|
|
|
url,
|
|
|
|
headers,
|
|
|
|
network::RequestBody::Json(body),
|
|
|
|
network::ResponseType::Json,
|
|
|
|
None,
|
|
|
|
network::NetRequestLogging::None,
|
|
|
|
);
|
|
|
|
let result = net.post_json::<Hook>(request).await;
|
|
|
|
match result {
|
|
|
|
Ok(response) => {
|
|
|
|
if let Some(hook) = response.response_body() {
|
|
|
|
info!("Webhook registered");
|
|
|
|
addr.do_send(WebhookRegistered(hook.id(), authorisation));
|
|
|
|
}
|
|
|
|
}
|
|
|
|
Err(_) => warn!("Failed to register webhook"),
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
#[derive(Debug, serde::Deserialize)]
|
|
|
|
struct Hook {
|
|
|
|
id: i64,
|
|
|
|
}
|
|
|
|
impl Hook {
|
|
|
|
fn id(&self) -> WebhookId {
|
|
|
|
WebhookId(format!("{}", self.id))
|
|
|
|
}
|
2024-04-09 19:30:05 +01:00
|
|
|
}
|
2024-04-13 16:16:09 +01:00
|
|
|
|
|
|
|
impl Handler<WebhookMessage> for RepoActor {
|
|
|
|
type Result = ();
|
|
|
|
|
|
|
|
fn handle(&mut self, msg: WebhookMessage, _ctx: &mut Self::Context) -> Self::Result {
|
|
|
|
let id = msg.id();
|
|
|
|
let body = msg.body();
|
|
|
|
info!(?id, ?body, "RepoActor received message");
|
|
|
|
// TODO: (#43) do something with the message
|
|
|
|
}
|
|
|
|
}
|