this repo has no description
1use crate::state::AppState; 2use axum::{ 3 Json, 4 extract::State, 5 http::StatusCode, 6 response::{IntoResponse, Response}, 7}; 8use serde::{Deserialize, Serialize}; 9use serde_json::json; 10use tracing::{error, warn}; 11#[derive(Deserialize)] 12#[serde(rename_all = "camelCase")] 13pub struct SendEmailInput { 14 pub recipient_did: String, 15 pub sender_did: String, 16 pub content: String, 17 pub subject: Option<String>, 18 pub comment: Option<String>, 19} 20#[derive(Serialize)] 21pub struct SendEmailOutput { 22 pub sent: bool, 23} 24pub async fn send_email( 25 State(state): State<AppState>, 26 headers: axum::http::HeaderMap, 27 Json(input): Json<SendEmailInput>, 28) -> Response { 29 let auth_header = headers.get("Authorization"); 30 if auth_header.is_none() { 31 return ( 32 StatusCode::UNAUTHORIZED, 33 Json(json!({"error": "AuthenticationRequired"})), 34 ) 35 .into_response(); 36 } 37 let recipient_did = input.recipient_did.trim(); 38 let content = input.content.trim(); 39 if recipient_did.is_empty() { 40 return ( 41 StatusCode::BAD_REQUEST, 42 Json(json!({"error": "InvalidRequest", "message": "recipientDid is required"})), 43 ) 44 .into_response(); 45 } 46 if content.is_empty() { 47 return ( 48 StatusCode::BAD_REQUEST, 49 Json(json!({"error": "InvalidRequest", "message": "content is required"})), 50 ) 51 .into_response(); 52 } 53 let user = sqlx::query!( 54 "SELECT id, email, handle FROM users WHERE did = $1", 55 recipient_did 56 ) 57 .fetch_optional(&state.db) 58 .await; 59 let (user_id, email, handle) = match user { 60 Ok(Some(row)) => { 61 let email = match row.email { 62 Some(e) => e, 63 None => { 64 return ( 65 StatusCode::BAD_REQUEST, 66 Json(json!({"error": "NoEmail", "message": "Recipient has no email address"})), 67 ) 68 .into_response(); 69 } 70 }; 71 (row.id, email, row.handle) 72 } 73 Ok(None) => { 74 return ( 75 StatusCode::NOT_FOUND, 76 Json(json!({"error": "AccountNotFound", "message": "Recipient account not found"})), 77 ) 78 .into_response(); 79 } 80 Err(e) => { 81 error!("DB error in send_email: {:?}", e); 82 return ( 83 StatusCode::INTERNAL_SERVER_ERROR, 84 Json(json!({"error": "InternalError"})), 85 ) 86 .into_response(); 87 } 88 }; 89 let hostname = std::env::var("PDS_HOSTNAME").unwrap_or_else(|_| "localhost".to_string()); 90 let subject = input 91 .subject 92 .clone() 93 .unwrap_or_else(|| format!("Message from {}", hostname)); 94 let notification = crate::notifications::NewNotification::email( 95 user_id, 96 crate::notifications::NotificationType::AdminEmail, 97 email, 98 subject, 99 content.to_string(), 100 ); 101 let result = crate::notifications::enqueue_notification(&state.db, notification).await; 102 match result { 103 Ok(_) => { 104 tracing::info!( 105 "Admin email queued for {} ({})", 106 handle, 107 recipient_did 108 ); 109 (StatusCode::OK, Json(SendEmailOutput { sent: true })).into_response() 110 } 111 Err(e) => { 112 warn!("Failed to enqueue admin email: {:?}", e); 113 (StatusCode::OK, Json(SendEmailOutput { sent: false })).into_response() 114 } 115 } 116}