1 use {
2 chrono::{DateTime, Utc},
3 serde::{Deserialize, Serialize},
4 serde_repr::{Deserialize_repr, Serialize_repr},
5 std::{collections::HashMap, fmt::Display},
6 };
7
8 #[derive(Deserialize_repr, Serialize_repr, Debug, Clone, Copy, PartialEq, Eq, Default)]
9 #[repr(u8)]
10 pub enum MailStatus {
11 #[default]
12 PendingDns = 0,
13 Active = 1,
14 Suspended = 2,
15 }
16
17 impl Display for MailStatus {
18 fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
19 match self {
20 MailStatus::PendingDns => write!(f, "pending_dns"),
21 MailStatus::Active => write!(f, "active"),
22 MailStatus::Suspended => write!(f, "suspended"),
23 }
24 }
25 }
26
27 #[derive(Deserialize_repr, Serialize_repr, Debug, Clone, Copy, PartialEq, Eq, Default)]
28 #[repr(u8)]
29 pub enum MailMessageStatus {
30 #[default]
31 Received = 0,
32 Forwarded = 1,
33 ForwardFailed = 2,
34 }
35
36 impl Display for MailMessageStatus {
37 fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
38 match self {
39 MailMessageStatus::Received => write!(f, "received"),
40 MailMessageStatus::Forwarded => write!(f, "forwarded"),
41 MailMessageStatus::ForwardFailed => write!(f, "forward_failed"),
42 }
43 }
44 }
45
46 #[derive(Deserialize, Serialize, Debug, Clone)]
47 pub struct MailApp {
48 // UUIDs, not sequence numbers. The API moved mail_apps, mail_inboxes and
49 // mail_messages onto UUID primary keys; leaving these as i32 made every
50 // `smb mail *` command and every mail MCP tool fail to deserialize with
51 // "invalid type: string ..., expected i32". tenant_id and project_id are
52 // still integers.
53 pub id: String,
54 pub name: String,
55 pub domain: String,
56 pub aws_region: String,
57 pub project_id: i32,
58 pub tenant_id: i32,
59 pub created_at: DateTime<Utc>,
60 pub updated_at: DateTime<Utc>,
61 pub status: MailStatus,
62 pub ses_inbound_mx_value: String,
63 pub inbound_bucket_name: String,
64 pub inbound_email_key_prefix: String,
65 #[serde(default)]
66 pub lambda_config: HashMap<String, String>,
67 #[serde(default)]
68 pub inboxes: Vec<MailInbox>,
69 }
70
71 #[derive(Deserialize, Serialize, Debug, Clone)]
72 pub struct MailInbox {
73 pub id: String,
74 pub local_part: String,
75 pub full_address: String,
76 pub inbox_email: String,
77 pub sender_email: String,
78 pub forward_to_email: String,
79 pub mail_app_id: String,
80 pub project_id: i32,
81 pub tenant_id: i32,
82 pub last_test_email_sent_at: Option<DateTime<Utc>>,
83 pub created_at: DateTime<Utc>,
84 pub updated_at: DateTime<Utc>,
85 pub status: MailStatus,
86 pub email_key_prefix: String,
87 #[serde(default)]
88 pub lambda_config: HashMap<String, String>,
89 }
90
91 #[derive(Deserialize, Serialize, Debug, Clone)]
92 pub struct MailMessage {
93 pub id: String,
94 pub mail_inbox_id: String,
95 pub provider_message_id: String,
96 pub original_recipient_email: String,
97 pub from_email: String,
98 pub from_name: Option<String>,
99 pub subject: Option<String>,
100 pub text_preview: Option<String>,
101 pub size_bytes: Option<i64>,
102 pub forwarded_at: Option<DateTime<Utc>>,
103 pub forward_error: Option<String>,
104 pub received_at: DateTime<Utc>,
105 pub created_at: DateTime<Utc>,
106 pub updated_at: DateTime<Utc>,
107 pub status: MailMessageStatus,
108 pub text_body: Option<String>,
109 pub html_body: Option<String>,
110 #[serde(default)]
111 pub to_emails: Vec<String>,
112 #[serde(default)]
113 pub cc_emails: Vec<String>,
114 #[serde(default)]
115 pub forward_recipients: Vec<String>,
116 #[serde(default)]
117 pub headers_json: HashMap<String, String>,
118 pub s3_bucket: Option<String>,
119 pub s3_key: Option<String>,
120 }
121
122 #[derive(Serialize, Debug, Clone)]
123 pub struct MailAppCreate {
124 pub name: String,
125 pub project_id: String,
126 pub domain: String,
127 #[serde(skip_serializing_if = "Option::is_none")]
128 pub aws_region: Option<String>,
129 }
130
131 #[derive(Serialize, Debug, Clone, Default)]
132 pub struct MailAppUpdate {
133 #[serde(skip_serializing_if = "Option::is_none")]
134 pub name: Option<String>,
135 #[serde(skip_serializing_if = "Option::is_none")]
136 pub domain: Option<String>,
137 #[serde(skip_serializing_if = "Option::is_none")]
138 pub aws_region: Option<String>,
139 }
140
141 impl MailAppUpdate {
142 pub fn is_empty(&self) -> bool {
143 self.name.is_none() && self.domain.is_none() && self.aws_region.is_none()
144 }
145 }
146
147 #[derive(Serialize, Debug, Clone)]
148 pub struct MailInboxCreate {
149 pub local_part: String,
150 pub forward_to_email: String,
151 #[serde(skip_serializing_if = "Option::is_none")]
152 pub sender_email: Option<String>,
153 }
154
155 #[derive(Serialize, Debug, Clone, Default)]
156 pub struct MailInboxUpdate {
157 #[serde(skip_serializing_if = "Option::is_none")]
158 pub local_part: Option<String>,
159 #[serde(skip_serializing_if = "Option::is_none")]
160 pub forward_to_email: Option<String>,
161 #[serde(skip_serializing_if = "Option::is_none")]
162 pub sender_email: Option<String>,
163 }
164
165 impl MailInboxUpdate {
166 pub fn is_empty(&self) -> bool {
167 self.local_part.is_none() && self.forward_to_email.is_none() && self.sender_email.is_none()
168 }
169 }
170
171 #[derive(Serialize, Debug, Clone, Default)]
172 pub struct MailTestEmailRequest {
173 #[serde(skip_serializing_if = "Option::is_none")]
174 pub recipient_email: Option<String>,
175 #[serde(skip_serializing_if = "Option::is_none")]
176 pub subject: Option<String>,
177 #[serde(skip_serializing_if = "Option::is_none")]
178 pub body: Option<String>,
179 }
180
181 #[derive(Deserialize, Serialize, Debug, Clone)]
182 pub struct MailTestEmailDelivery {
183 pub message_id: String,
184 pub recipient_email: String,
185 pub delivery_method: String,
186 pub sent_at: String,
187 }