sms_web_axum/
lib.rs

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
15/// Axum-specific header converter
16pub 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
34/// Axum-specific response converter
35pub 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        // For JSON responses, just return the body as-is since it's already JSON formatted
45        match response.content_type.as_str() {
46            "application/json" => (status, response.body).into_response(),
47            _ => (status, response.body).into_response(),
48        }
49    }
50}
51
52/// Unified handler: POST /webhooks/:provider
53pub 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}