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)) => (row.id, row.email, row.handle),
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
86 let hostname = std::env::var("PDS_HOSTNAME").unwrap_or_else(|_| "localhost".to_string());
87 let subject = input
88 .subject
89 .clone()
90 .unwrap_or_else(|| format!("Message from {}", hostname));
91
92 let notification = crate::notifications::NewNotification::email(
93 user_id,
94 crate::notifications::NotificationType::AdminEmail,
95 email,
96 subject,
97 content.to_string(),
98 );
99
100 let result = crate::notifications::enqueue_notification(&state.db, notification).await;
101
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}