1use axum::{
2 extract::{Path, State},
3 http::HeaderMap,
4 response::IntoResponse,
5};
6use bytes::Bytes;
7use sms_core::{Headers, InboundRegistry};
8use sms_web_generic::{HeaderConverter, ResponseConverter, WebhookProcessor};
9
10#[derive(Clone)]
11pub struct AppState {
12 pub registry: InboundRegistry,
13}
14
15pub struct AxumHeaderConverter;
17
18impl HeaderConverter for AxumHeaderConverter {
19 type HeaderType = HeaderMap;
20
21 fn to_generic_headers(headers: &Self::HeaderType) -> Headers {
22 headers
23 .iter()
24 .map(|(k, v)| {
25 (
26 k.as_str().to_string(),
27 v.to_str().unwrap_or_default().to_string(),
28 )
29 })
30 .collect()
31 }
32}
33
34pub struct AxumResponseConverter;
36
37impl ResponseConverter for AxumResponseConverter {
38 type ResponseType = axum::response::Response;
39
40 fn from_webhook_response(response: sms_core::WebhookResponse) -> Self::ResponseType {
41 let status = axum::http::StatusCode::from_u16(response.status.as_u16())
42 .unwrap_or(axum::http::StatusCode::INTERNAL_SERVER_ERROR);
43
44 match response.content_type.as_str() {
46 "application/json" => (status, response.body).into_response(),
47 _ => (status, response.body).into_response(),
48 }
49 }
50}
51
52pub async fn unified_webhook(
54 State(state): State<AppState>,
55 Path(provider): Path<String>,
56 headers: HeaderMap,
57 body: Bytes,
58) -> impl IntoResponse {
59 let processor = WebhookProcessor::new(state.registry);
60 let generic_headers = AxumHeaderConverter::to_generic_headers(&headers);
61 let response = processor.process_webhook(&provider, generic_headers, &body);
62 AxumResponseConverter::from_webhook_response(response)
63}