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