//! The outbound-email queue. The gated `email.send` tool enqueues rows here //! (after ยง15 policy); the delivery drainer (cm-runtime) sends them over SMTP //! and flips their status. use sqlx::PgPool; use uuid::Uuid; use crate::DbError; /// A queued outbound email awaiting delivery. pub struct QueuedEmail { pub id: Uuid, pub recipient: String, pub subject: String, pub body: String, } /// The oldest `queued` emails, up to `limit` (FIFO by enqueue time). pub async fn list_queued(pool: &PgPool, limit: i64) -> Result, DbError> { let rows = sqlx::query!( "SELECT id, recipient, subject, body FROM outbox WHERE status = 'queued' ORDER BY created_at LIMIT $1", limit, ) .fetch_all(pool) .await?; Ok(rows .into_iter() .map(|r| QueuedEmail { id: r.id, recipient: r.recipient, subject: r.subject, body: r.body, }) .collect()) } /// Mark a queued email delivered. pub async fn mark_sent(pool: &PgPool, id: Uuid) -> Result<(), DbError> { sqlx::query!("UPDATE outbox SET status = 'sent' WHERE id = $1", id) .execute(pool) .await?; Ok(()) } /// Mark a queued email failed (delivery error; left for inspection, not retried). pub async fn mark_failed(pool: &PgPool, id: Uuid) -> Result<(), DbError> { sqlx::query!("UPDATE outbox SET status = 'failed' WHERE id = $1", id) .execute(pool) .await?; Ok(()) }