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 let recipient_did = input.recipient_did.trim(); 41 let content = input.content.trim(); 42 if recipient_did.is_empty() { 43 return ( 44 StatusCode::BAD_REQUEST, 45 Json(json!({"error": "InvalidRequest", "message": "recipientDid is required"})), 46 ) 47 .into_response(); 48 } 49 if content.is_empty() { 50 return ( 51 StatusCode::BAD_REQUEST, 52 Json(json!({"error": "InvalidRequest", "message": "content is required"})), 53 ) 54 .into_response(); 55 } 56 let user = sqlx::query!( 57 "SELECT id, email, handle FROM users WHERE did = $1", 58 recipient_did 59 ) 60 .fetch_optional(&state.db) 61 .await; 62 let (user_id, email, handle) = match user { 63 Ok(Some(row)) => { 64 let email = match row.email { 65 Some(e) => e, 66 None => { 67 return ( 68 StatusCode::BAD_REQUEST, 69 Json(json!({"error": "NoEmail", "message": "Recipient has no email address"})), 70 ) 71 .into_response(); 72 } 73 }; 74 (row.id, email, row.handle) 75 } 76 Ok(None) => { 77 return ( 78 StatusCode::NOT_FOUND, 79 Json(json!({"error": "AccountNotFound", "message": "Recipient account not found"})), 80 ) 81 .into_response(); 82 } 83 Err(e) => { 84 error!("DB error in send_email: {:?}", e); 85 return ( 86 StatusCode::INTERNAL_SERVER_ERROR, 87 Json(json!({"error": "InternalError"})), 88 ) 89 .into_response(); 90 } 91 }; 92 let hostname = std::env::var("PDS_HOSTNAME").unwrap_or_else(|_| "localhost".to_string()); 93 let subject = input 94 .subject 95 .clone() 96 .unwrap_or_else(|| format!("Message from {}", hostname)); 97 let notification = crate::notifications::NewNotification::email( 98 user_id, 99 crate::notifications::NotificationType::AdminEmail, 100 email, 101 subject, 102 content.to_string(), 103 ); 104 let result = crate::notifications::enqueue_notification(&state.db, notification).await; 105 match result { 106 Ok(_) => { 107 tracing::info!("Admin email queued for {} ({})", handle, recipient_did); 108 (StatusCode::OK, Json(SendEmailOutput { sent: true })).into_response() 109 } 110 Err(e) => { 111 warn!("Failed to enqueue admin email: {:?}", e); 112 (StatusCode::OK, Json(SendEmailOutput { sent: false })).into_response() 113 } 114 } 115}