use actix::prelude::*; use kxio::network::{self, json}; use tracing::{info, warn}; use std::{fmt::Display, ops::Deref}; use crate::server::{ actors::{ repo::{RepoActor, WebhookRegistered}, webhook::WebhookMessage, }, config::Webhook, }; #[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 } } impl Display for WebhookId { fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result { write!(f, "{}", self.0) } } pub async fn unregister( webhook_id: WebhookId, repo_details: crate::server::config::RepoDetails, net: network::Network, ) { info!(?webhook_id, "unregister 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/{webhook_id}?token={token}" )); 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"), } } pub async fn register( repo_details: crate::server::config::RepoDetails, webhook: Webhook, addr: actix::prelude::Addr, net: network::Network, ) { 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::(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)) } } impl Handler 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 } }