summaryrefslogtreecommitdiffhomepage
path: root/src/main.rs
diff options
context:
space:
mode:
Diffstat (limited to 'src/main.rs')
-rw-r--r--src/main.rs43
1 files changed, 42 insertions, 1 deletions
diff --git a/src/main.rs b/src/main.rs
index 1f336ae..7f00c62 100644
--- a/src/main.rs
+++ b/src/main.rs
@@ -7,6 +7,7 @@ use hyper::body::Incoming;
use hyper::server::conn::http1;
use hyper::service::service_fn;
use hyper::{Method, Request, Response, StatusCode, Uri};
+use hyper::header::HeaderMap;
use hyper_util::client::legacy::Client;
use hyper_util::rt::TokioExecutor;
use tokio::net::TcpListener;
@@ -40,6 +41,27 @@ fn map_path(path: &str) -> Option<String> {
None
}
+/// Check if the Content-Type header indicates form-urlencoded data.
+fn content_type_is_form_urlencoded(headers: &HeaderMap) -> bool {
+ headers
+ .get(hyper::header::CONTENT_TYPE)
+ .and_then(|v| v.to_str().ok())
+ .map(|ct| ct.starts_with("application/x-www-form-urlencoded"))
+ .unwrap_or(false)
+}
+
+/// Extract JSON from a form-urlencoded body.
+/// GitHub sends webhooks as `payload=<url-encoded-json>` when the webhook
+/// content type is set to `application/x-www-form-urlencoded`.
+fn extract_json_from_form(body: &Bytes) -> Option<Bytes> {
+ for (key, value) in form_urlencoded::parse(body) {
+ if key == "payload" {
+ return Some(Bytes::from(value.into_owned()));
+ }
+ }
+ None
+}
+
async fn handle(
base_url: String,
req: Request<Incoming>,
@@ -75,8 +97,11 @@ async fn handle(
.method(Method::POST)
.uri(&upstream_uri);
+ // Extract headers before consuming the request body
+ let headers = req.headers().clone();
+
// Copy headers (skip Host, it should match the upstream)
- for (name, value) in req.headers() {
+ for (name, value) in &headers {
if name != hyper::header::HOST {
builder = builder.header(name, value);
}
@@ -84,6 +109,22 @@ async fn handle(
// Collect the incoming body
let body_bytes = req.into_body().collect().await?.to_bytes();
+
+ // If the body is application/x-www-form-urlencoded (GitHub default), extract the
+ // "payload" field and convert to application/json so Dokploy can parse it.
+ let body_bytes = if content_type_is_form_urlencoded(&headers) {
+ match extract_json_from_form(&body_bytes) {
+ Some(json_bytes) => {
+ eprintln!("POST {} -> converting form-urlencoded payload to JSON", path);
+ builder = builder.header(hyper::header::CONTENT_TYPE, "application/json");
+ json_bytes
+ }
+ None => body_bytes,
+ }
+ } else {
+ body_bytes
+ };
+
let upstream_req = builder.body(Full::new(body_bytes))?;
// Send to Dokploy