use std::net::SocketAddr;
use axum::Json;
use axum::extract::{ConnectInfo, Request, State};
use axum::http::{HeaderMap, StatusCode};
use axum::middleware::Next;
use axum::response::{IntoResponse, Response};
use serde_json::Value;
use tracing::{error, warn};
use trust_tasks_rs::{RejectReason, TrustTask};
use did_hosting_common::server::auth::session::now_epoch;
use did_hosting_common::server::rate_limit::resolve_client_ip;
use crate::server::AppState;
use crate::trust_tasks::{Via, dispatch_inbound_document};
pub async fn log_missing_connect_info(req: Request, next: Next) -> Response {
if req.extensions().get::<ConnectInfo<SocketAddr>>().is_none() {
error!("trust-tasks request has no ConnectInfo<SocketAddr>; it will be refused with a 500");
}
next.run(req).await
}
pub async fn receive(
State(state): State<AppState>,
ConnectInfo(addr): ConnectInfo<SocketAddr>,
headers: HeaderMap,
body: axum::body::Bytes,
) -> Response {
let xff = headers.get("x-forwarded-for").and_then(|v| v.to_str().ok());
let client_ip = resolve_client_ip(addr.ip(), xff, &state.config.server.trusted_proxies);
if let Err(e) = state
.trust_tasks_rate_limiter
.try_consume(client_ip, now_epoch())
{
warn!(ip = %client_ip, error = %e, "trust-tasks request rate limited");
return e.into_response();
}
let doc: TrustTask<Value> = match serde_json::from_slice(&body) {
Ok(doc) => doc,
Err(e) => {
return (
StatusCode::BAD_REQUEST,
Json(serde_json::json!({
"error": "malformed_request",
"message": format!("the body is not a Trust Task document: {e}"),
})),
)
.into_response();
}
};
let refusal = doc.reject_with(
format!("urn:uuid:{}", uuid::Uuid::new_v4()),
RejectReason::PermissionDenied {
reason: "the document was not accepted".into(),
},
);
match dispatch_inbound_document(&state, Via::Https, None, doc).await {
Some(reply) => (StatusCode::OK, Json(reply)).into_response(),
None => (StatusCode::FORBIDDEN, Json(refusal)).into_response(),
}
}