use std::sync::Arc;
use axum::{extract::State, http::StatusCode, Json};
use serde_json::{json, Value};
use crate::{
auth::AuthContext,
error::AppError,
mail, smtp, validation,
AppState,
};
pub use crate::validation::MailRequest;
pub async fn send_mail(
State(state): State<Arc<AppState>>,
auth: AuthContext,
Json(payload): Json<MailRequest>,
) -> Result<(StatusCode, Json<Value>), AppError> {
state
.rate_limiter
.check_key(&auth.key_id, auth.key_rate_limit_per_min)
.map_err(|e| {
tracing::warn!(
event = "rate_limited",
tier = "key",
key_id = %auth.key_id,
retry_after = e.retry_after_secs,
);
AppError::RateLimited {
retry_after_secs: Some(e.retry_after_secs),
}
})?;
let validated = validation::validate_mail_request(payload, &state.config, &auth)
.map_err(|e| {
tracing::warn!(
event = "validation_failure",
key_id = %auth.key_id,
client_ip = %auth.client_ip,
error = %e,
);
e
})?;
let message = mail::build_message(&validated, &state.config)?;
let recipient_domain = validated
.to
.split('@')
.nth(1)
.unwrap_or("unknown");
smtp::submit(&state.smtp, message, state.config.smtp.submission_timeout_seconds)
.await
.map_err(|e| {
tracing::error!(
event = "smtp_failure",
key_id = %auth.key_id,
client_ip = %auth.client_ip,
recipient_domain = recipient_domain,
error = %e,
);
e
})?;
tracing::info!(
event = "smtp_submitted",
key_id = %auth.key_id,
recipient_domain = recipient_domain,
);
let request_id = uuid::Uuid::new_v4().to_string();
Ok((
StatusCode::ACCEPTED,
Json(json!({
"request_id": request_id,
"status": "accepted",
})),
))
}