2024-05-10 22:01:47 +01:00
|
|
|
use std::net::SocketAddr;
|
|
|
|
|
2024-04-13 16:16:09 +01:00
|
|
|
use actix::prelude::*;
|
|
|
|
|
2024-05-20 20:41:01 +01:00
|
|
|
use git_next_config::RepoAlias;
|
2024-05-18 20:26:26 +01:00
|
|
|
use tracing::{info, warn};
|
2024-05-20 20:41:01 +01:00
|
|
|
use warp::reject::Rejection;
|
2024-04-13 16:16:09 +01:00
|
|
|
|
2024-05-20 20:41:01 +01:00
|
|
|
use crate::actors::{repo::webhook::WebhookAuth, webhook::message::WebhookMessage};
|
|
|
|
|
|
|
|
use super::message;
|
2024-04-13 16:16:09 +01:00
|
|
|
|
2024-05-10 22:01:47 +01:00
|
|
|
pub async fn start(
|
|
|
|
socket_addr: SocketAddr,
|
2024-05-20 20:41:01 +01:00
|
|
|
address: actix::prelude::Recipient<message::WebhookMessage>,
|
2024-05-10 22:01:47 +01:00
|
|
|
) {
|
2024-04-13 16:16:09 +01:00
|
|
|
// start webhook server
|
|
|
|
use warp::Filter;
|
|
|
|
// Define the Warp route to handle incoming HTTP requests
|
|
|
|
let route = warp::post()
|
|
|
|
.map(move || address.clone())
|
|
|
|
.and(warp::path::param())
|
|
|
|
// .and(warp::query::raw())
|
2024-04-14 19:12:51 +01:00
|
|
|
.and(warp::header::headers_cloned())
|
2024-04-13 16:16:09 +01:00
|
|
|
.and(warp::body::bytes())
|
|
|
|
.and_then(
|
|
|
|
|recipient: Recipient<WebhookMessage>,
|
2024-05-20 20:41:01 +01:00
|
|
|
path: String,
|
2024-04-13 16:16:09 +01:00
|
|
|
// query: String,
|
2024-04-14 19:12:51 +01:00
|
|
|
headers: warp::http::HeaderMap,
|
2024-04-13 16:16:09 +01:00
|
|
|
body: bytes::Bytes| async move {
|
2024-05-18 20:26:26 +01:00
|
|
|
info!("POST received");
|
2024-05-20 20:41:01 +01:00
|
|
|
let repo_alias = RepoAlias::new(path);
|
2024-04-13 16:16:09 +01:00
|
|
|
let bytes = body.to_vec();
|
2024-05-20 20:41:01 +01:00
|
|
|
let body = message::Body::new(String::from_utf8_lossy(&bytes).to_string());
|
|
|
|
headers.get("Authorization").map_or_else(
|
|
|
|
|| {
|
2024-05-18 20:26:26 +01:00
|
|
|
warn!("No Authorization header");
|
|
|
|
Err(warp::reject())
|
2024-05-20 20:41:01 +01:00
|
|
|
},
|
|
|
|
|authorisation_header| {
|
|
|
|
info!(?repo_alias, ?authorisation_header, "Received webhook",);
|
|
|
|
match parse_auth(authorisation_header) {
|
|
|
|
Ok(authorisation) => {
|
|
|
|
let message = WebhookMessage::new(repo_alias, authorisation, body);
|
|
|
|
recipient
|
|
|
|
.try_send(message)
|
|
|
|
.map(|_| {
|
|
|
|
info!("Message sent ok");
|
|
|
|
warp::reply::with_status("OK", warp::http::StatusCode::OK)
|
|
|
|
})
|
|
|
|
.map_err(|e| {
|
|
|
|
warn!("Unknown error: {:?}", e);
|
|
|
|
warp::reject()
|
|
|
|
})
|
|
|
|
}
|
|
|
|
Err(e) => {
|
|
|
|
warn!(?e, "Failed to decode authorization header");
|
|
|
|
Err(warp::reject())
|
|
|
|
}
|
|
|
|
}
|
|
|
|
},
|
|
|
|
)
|
2024-04-13 16:16:09 +01:00
|
|
|
},
|
|
|
|
);
|
|
|
|
|
|
|
|
// Start the server
|
2024-05-10 22:01:47 +01:00
|
|
|
info!("Starting webhook server: {}", socket_addr);
|
|
|
|
warp::serve(route).run(socket_addr).await;
|
2024-04-13 16:16:09 +01:00
|
|
|
}
|
2024-05-20 20:41:01 +01:00
|
|
|
|
|
|
|
fn parse_auth(authorization_header: &warp::http::HeaderValue) -> Result<WebhookAuth, Rejection> {
|
|
|
|
WebhookAuth::from_str(
|
|
|
|
authorization_header
|
|
|
|
.to_str()
|
|
|
|
.map_err(|e| {
|
|
|
|
warn!("Invalid non-ascii value in authorization: {:?}", e);
|
|
|
|
warp::reject()
|
|
|
|
}) // valid characters
|
|
|
|
.map(|v| {
|
|
|
|
info!("raw auth header: {}", v);
|
|
|
|
v
|
|
|
|
})?
|
|
|
|
.strip_prefix("Basic ")
|
|
|
|
.ok_or_else(|| {
|
|
|
|
warn!("Authorization must be 'Basic'");
|
|
|
|
warp::reject()
|
|
|
|
})?, // must start with "Basic "
|
|
|
|
)
|
|
|
|
.map_err(|e| {
|
|
|
|
warn!(?e, "decode error");
|
|
|
|
warp::reject()
|
|
|
|
})
|
|
|
|
}
|