mailrs_outbound_queue/queue/
mod.rs1#[cfg(feature = "pg")]
2use crate::BackendPool;
3#[cfg(feature = "pg")]
4use kevy_embedded::Store;
5
6mod admin;
7mod lifecycle;
8pub mod suppression;
9
10pub use admin::*;
11pub use lifecycle::*;
12
13#[derive(Debug, Clone, PartialEq, Eq)]
15pub enum QueueStatus {
16 Pending,
18 InFlight,
20 Delivered,
22 Failed,
24 Bounced,
26}
27
28impl QueueStatus {
29 pub fn as_str(&self) -> &'static str {
31 match self {
32 Self::Pending => "pending",
33 Self::InFlight => "inflight",
34 Self::Delivered => "delivered",
35 Self::Failed => "failed",
36 Self::Bounced => "bounced",
37 }
38 }
39
40 pub fn parse(s: &str) -> Option<Self> {
42 match s {
43 "pending" => Some(Self::Pending),
44 "inflight" => Some(Self::InFlight),
45 "delivered" => Some(Self::Delivered),
46 "failed" => Some(Self::Failed),
47 "bounced" => Some(Self::Bounced),
48 _ => None,
49 }
50 }
51}
52
53#[derive(Debug, Clone)]
55pub struct QueuedMessage {
56 pub id: i64,
58 pub sender: String,
60 pub recipient: String,
63 pub domain: String,
65 pub message_data: Vec<u8>,
67 pub status: QueueStatus,
69 pub attempts: u32,
71 pub max_attempts: u32,
73 pub next_retry: i64,
75 pub last_error: Option<String>,
77 pub message_id: Option<String>,
79 pub created_at: i64,
81 pub updated_at: i64,
83 pub is_forwarded: bool,
86}
87
88#[cfg(feature = "pg")]
90pub async fn enqueue(
91 pool: &BackendPool,
92 sender: &str,
93 recipient: &str,
94 domain: &str,
95 message_data: &[u8],
96 message_id: Option<&str>,
97 now: i64,
98) -> Result<i64, sqlx::Error> {
99 enqueue_ex(
100 pool,
101 sender,
102 recipient,
103 domain,
104 message_data,
105 message_id,
106 now,
107 false,
108 )
109 .await
110}
111
112#[allow(clippy::too_many_arguments)]
114#[cfg(feature = "pg")]
115pub async fn enqueue_ex(
116 pool: &BackendPool,
117 sender: &str,
118 recipient: &str,
119 domain: &str,
120 message_data: &[u8],
121 message_id: Option<&str>,
122 now: i64,
123 is_forwarded: bool,
124) -> Result<i64, sqlx::Error> {
125 let row: (i64,) = sqlx::query_as(
126 "INSERT INTO outbound_queue (sender, recipient, domain, message_data, status, next_retry, message_id, created_at, updated_at, is_forwarded)
127 VALUES ($1, $2, $3, $4, 'pending', $5, $6, $5, $5, $7)
128 RETURNING id",
129 )
130 .bind(sender)
131 .bind(recipient)
132 .bind(domain)
133 .bind(message_data)
134 .bind(now)
135 .bind(message_id)
136 .bind(is_forwarded)
137 .fetch_one(pool)
138 .await?;
139 Ok(row.0)
140}
141
142#[cfg(feature = "pg")]
145pub fn notify(kevy: &Store) {
146 let _ = kevy.publish(b"queue:notify", b"1");
147}
148
149#[allow(clippy::too_many_arguments)]
151#[cfg(feature = "pg")]
152pub async fn enqueue_scheduled(
153 pool: &BackendPool,
154 sender: &str,
155 recipient: &str,
156 domain: &str,
157 message_data: &[u8],
158 message_id: Option<&str>,
159 created_at: i64,
160 scheduled_at: i64,
161) -> Result<i64, sqlx::Error> {
162 let row: (i64,) = sqlx::query_as(
163 "INSERT INTO outbound_queue (sender, recipient, domain, message_data, status, next_retry, message_id, created_at, updated_at, is_forwarded)
164 VALUES ($1, $2, $3, $4, 'pending', $5, $6, $7, $7, false)
165 RETURNING id",
166 )
167 .bind(sender)
168 .bind(recipient)
169 .bind(domain)
170 .bind(message_data)
171 .bind(scheduled_at)
172 .bind(message_id)
173 .bind(created_at)
174 .fetch_one(pool)
175 .await?;
176 Ok(row.0)
177}