weft_core/api/
package_webhook.rs1use crate::api::openai_compat::AppState;
2use axum::{
3 body::Bytes,
4 extract::{Path, State},
5 http::{HeaderMap, StatusCode},
6 response::{IntoResponse, Response},
7};
8
9pub async fn package_webhook_no_channel(
11 Path(package_name): Path<String>,
12 State(state): State<AppState>,
13 headers: HeaderMap,
14 body: Bytes,
15) -> Response {
16 do_webhook(&package_name, "", state, headers, body).await
17}
18
19pub async fn package_webhook_with_channel(
21 Path((package_name, channel_type)): Path<(String, String)>,
22 State(state): State<AppState>,
23 headers: HeaderMap,
24 body: Bytes,
25) -> Response {
26 do_webhook(&package_name, &channel_type, state, headers, body).await
27}
28
29async fn do_webhook(
30 package_name: &str,
31 channel_type: &str,
32 state: AppState,
33 headers: HeaderMap,
34 body: Bytes,
35) -> Response {
36 if package_name.is_empty() {
37 return (StatusCode::BAD_REQUEST, "Missing package name").into_response();
38 }
39
40 let headers_map: serde_json::Map<String, serde_json::Value> = headers
42 .iter()
43 .map(|(k, v)| {
44 (
45 k.as_str().to_string(),
46 serde_json::Value::String(v.to_str().unwrap_or("").to_string()),
47 )
48 })
49 .collect();
50
51 let raw_body = String::from_utf8_lossy(&body).to_string();
53 let payload = serde_json::json!({
54 "channel": channel_type,
55 "headers": headers_map,
56 "body": raw_body,
57 });
58
59 let payload_str = payload.to_string();
60
61 let wasm_handle = state.wasm_handle.read().await.clone();
63 let Some(handle) = wasm_handle else {
64 return (
65 StatusCode::INTERNAL_SERVER_ERROR,
66 "No WASM runtime available",
67 )
68 .into_response();
69 };
70
71 match handle.call(package_name, "handle_webhook", &payload_str) {
72 Ok(result_str) => build_response_from_plugin(&result_str),
73 Err(e) => {
74 let msg = e.to_string();
75 if msg.contains("not loaded") || msg.contains("not found") {
76 (
77 StatusCode::NOT_FOUND,
78 format!("Package '{}' not found: {}", package_name, msg),
79 )
80 .into_response()
81 } else {
82 tracing::error!(
83 "[webhook] Package '{}' handle_webhook error: {}",
84 package_name,
85 e
86 );
87 (
88 StatusCode::INTERNAL_SERVER_ERROR,
89 format!("Package error: {}", msg),
90 )
91 .into_response()
92 }
93 }
94 }
95}
96
97fn build_response_from_plugin(result_str: &str) -> Response {
100 let parsed: serde_json::Value = match serde_json::from_str(result_str) {
101 Ok(v) => v,
102 Err(_) => {
104 return (StatusCode::OK, result_str.to_string()).into_response();
105 }
106 };
107
108 let status_code = parsed["status_code"].as_u64().unwrap_or(200) as u16;
109
110 let body = match &parsed["body"] {
111 serde_json::Value::String(s) => s.clone(),
112 serde_json::Value::Null => String::new(),
113 other => other.to_string(),
114 };
115
116 let status = StatusCode::from_u16(status_code).unwrap_or(StatusCode::OK);
117 (status, body).into_response()
118}