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}