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