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}