types: locale newtype, single comms type, channel identifiers
Lewis: May this revision serve well! <did:plc:3fwecdnvtcscjnrx2p4n7alz>
1234567891011121314151617181920212223242526272829303132333435363738394041424344454647484950515253545556575859606162636465666768697071727374757677787980818283848586878889909192939495969798{ "db_name": "PostgreSQL", "query": "\n SELECT\n created_at,\n channel as \"channel: CommsChannel\",\n comms_type as \"comms_type: CommsType\",\n status as \"status: CommsStatus\",\n subject,\n body\n FROM comms_queue\n WHERE user_id = $1\n ORDER BY created_at DESC\n LIMIT $2\n ", "describe": { "columns": [ { "ordinal": 0, "name": "created_at", "type_info": "Timestamptz" }, { "ordinal": 1, "name": "channel: CommsChannel", "type_info": { "Custom": { "name": "comms_channel", "kind": { "Enum": [ "email", "discord", "telegram", "signal" ] } } } }, { "ordinal": 2, "name": "comms_type: CommsType", "type_info": { "Custom": { "name": "comms_type", "kind": { "Enum": [ "welcome", "email_verification", "password_reset", "email_update", "account_deletion", "admin_email", "plc_operation", "two_factor_code", "channel_verification", "passkey_recovery", "legacy_login_alert", "migration_verification", "channel_verified" ] } } } }, { "ordinal": 3, "name": "status: CommsStatus", "type_info": { "Custom": { "name": "comms_status", "kind": { "Enum": [ "pending", "processing", "sent", "failed" ] } } } }, { "ordinal": 4, "name": "subject", "type_info": "Text" }, { "ordinal": 5, "name": "body", "type_info": "Text" } ], "parameters": { "Left": [ "Uuid", "Int8" ] }, "nullable": [ false, false, false, false, true, false ] }, "hash": "25309f4a08845a49557d694ad9b5b9a137be4dcce28e9293551c8c3fd40fdd86"}123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158{ "db_name": "PostgreSQL", "query": "UPDATE comms_queue\n SET status = 'processing', updated_at = NOW()\n WHERE id IN (\n SELECT id FROM comms_queue\n WHERE attempts < max_attempts\n AND scheduled_for <= $1\n AND (\n status = 'pending'\n OR (status = 'processing'\n AND updated_at < $1 - INTERVAL '10 minutes')\n )\n ORDER BY scheduled_for ASC\n LIMIT $2\n FOR UPDATE SKIP LOCKED\n )\n RETURNING\n id, user_id,\n channel as \"channel: CommsChannel\",\n comms_type as \"comms_type: CommsType\",\n status as \"status: CommsStatus\",\n recipient, subject, body, metadata,\n attempts, max_attempts, last_error,\n created_at, updated_at, scheduled_for, processed_at", "describe": { "columns": [ { "ordinal": 0, "name": "id", "type_info": "Uuid" }, { "ordinal": 1, "name": "user_id", "type_info": "Uuid" }, { "ordinal": 2, "name": "channel: CommsChannel", "type_info": { "Custom": { "name": "comms_channel", "kind": { "Enum": [ "email", "discord", "telegram", "signal" ] } } } }, { "ordinal": 3, "name": "comms_type: CommsType", "type_info": { "Custom": { "name": "comms_type", "kind": { "Enum": [ "welcome", "email_verification", "password_reset", "email_update", "account_deletion", "admin_email", "plc_operation", "two_factor_code", "channel_verification", "passkey_recovery", "legacy_login_alert", "migration_verification", "channel_verified" ] } } } }, { "ordinal": 4, "name": "status: CommsStatus", "type_info": { "Custom": { "name": "comms_status", "kind": { "Enum": [ "pending", "processing", "sent", "failed" ] } } } }, { "ordinal": 5, "name": "recipient", "type_info": "Text" }, { "ordinal": 6, "name": "subject", "type_info": "Text" }, { "ordinal": 7, "name": "body", "type_info": "Text" }, { "ordinal": 8, "name": "metadata", "type_info": "Jsonb" }, { "ordinal": 9, "name": "attempts", "type_info": "Int4" }, { "ordinal": 10, "name": "max_attempts", "type_info": "Int4" }, { "ordinal": 11, "name": "last_error", "type_info": "Text" }, { "ordinal": 12, "name": "created_at", "type_info": "Timestamptz" }, { "ordinal": 13, "name": "updated_at", "type_info": "Timestamptz" }, { "ordinal": 14, "name": "scheduled_for", "type_info": "Timestamptz" }, { "ordinal": 15, "name": "processed_at", "type_info": "Timestamptz" } ], "parameters": { "Left": [ "Timestamptz", "Int8" ] }, "nullable": [ false, false, false, false, false, false, true, false, true, false, false, true, false, false, false, true ] }, "hash": "890aa92acdcb0fe2a3bf04d87e1f16a801d271da7cedc32fc42c2ef5b100faae"}123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158{ "db_name": "PostgreSQL", "query": "UPDATE comms_queue\n SET status = 'processing', updated_at = NOW()\n WHERE id IN (\n SELECT id FROM comms_queue\n WHERE attempts < max_attempts\n AND scheduled_for <= $1\n AND (\n status = 'pending'\n OR (status = 'processing'\n AND updated_at < $1 - INTERVAL '10 minutes')\n )\n ORDER BY scheduled_for ASC\n LIMIT $2\n FOR UPDATE SKIP LOCKED\n )\n RETURNING\n id, user_id,\n channel as \"channel: CommsChannel\",\n comms_type as \"comms_type: CommsMessageType\",\n status as \"status: CommsStatus\",\n recipient, subject, body, metadata,\n attempts, max_attempts, last_error,\n created_at, updated_at, scheduled_for, processed_at", "describe": { "columns": [ { "ordinal": 0, "name": "id", "type_info": "Uuid" }, { "ordinal": 1, "name": "user_id", "type_info": "Uuid" }, { "ordinal": 2, "name": "channel: CommsChannel", "type_info": { "Custom": { "name": "comms_channel", "kind": { "Enum": [ "email", "discord", "telegram", "signal" ] } } } }, { "ordinal": 3, "name": "comms_type: CommsMessageType", "type_info": { "Custom": { "name": "comms_type", "kind": { "Enum": [ "welcome", "email_verification", "password_reset", "email_update", "account_deletion", "admin_email", "plc_operation", "two_factor_code", "channel_verification", "passkey_recovery", "legacy_login_alert", "migration_verification", "channel_verified" ] } } } }, { "ordinal": 4, "name": "status: CommsStatus", "type_info": { "Custom": { "name": "comms_status", "kind": { "Enum": [ "pending", "processing", "sent", "failed" ] } } } }, { "ordinal": 5, "name": "recipient", "type_info": "Text" }, { "ordinal": 6, "name": "subject", "type_info": "Text" }, { "ordinal": 7, "name": "body", "type_info": "Text" }, { "ordinal": 8, "name": "metadata", "type_info": "Jsonb" }, { "ordinal": 9, "name": "attempts", "type_info": "Int4" }, { "ordinal": 10, "name": "max_attempts", "type_info": "Int4" }, { "ordinal": 11, "name": "last_error", "type_info": "Text" }, { "ordinal": 12, "name": "created_at", "type_info": "Timestamptz" }, { "ordinal": 13, "name": "updated_at", "type_info": "Timestamptz" }, { "ordinal": 14, "name": "scheduled_for", "type_info": "Timestamptz" }, { "ordinal": 15, "name": "processed_at", "type_info": "Timestamptz" } ], "parameters": { "Left": [ "Timestamptz", "Int8" ] }, "nullable": [ false, false, false, false, false, false, true, false, true, false, false, true, false, false, false, true ] }, "hash": "8cf0fc438c04715d48fa6644a43b05a7e8f41f74ec540219efa33dadf36734ad"}123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180{ "db_name": "PostgreSQL", "query": "SELECT\n id, user_id,\n channel as \"channel: CommsChannel\",\n comms_type as \"comms_type: CommsType\",\n status as \"status: CommsStatus\",\n recipient, subject, body, metadata,\n attempts, max_attempts, last_error,\n created_at, updated_at, scheduled_for, processed_at\n FROM comms_queue\n WHERE user_id = $1 AND comms_type = $2\n ORDER BY created_at DESC\n LIMIT $3", "describe": { "columns": [ { "ordinal": 0, "name": "id", "type_info": "Uuid" }, { "ordinal": 1, "name": "user_id", "type_info": "Uuid" }, { "ordinal": 2, "name": "channel: CommsChannel", "type_info": { "Custom": { "name": "comms_channel", "kind": { "Enum": [ "email", "discord", "telegram", "signal" ] } } } }, { "ordinal": 3, "name": "comms_type: CommsType", "type_info": { "Custom": { "name": "comms_type", "kind": { "Enum": [ "welcome", "email_verification", "password_reset", "email_update", "account_deletion", "admin_email", "plc_operation", "two_factor_code", "channel_verification", "passkey_recovery", "legacy_login_alert", "migration_verification", "channel_verified" ] } } } }, { "ordinal": 4, "name": "status: CommsStatus", "type_info": { "Custom": { "name": "comms_status", "kind": { "Enum": [ "pending", "processing", "sent", "failed" ] } } } }, { "ordinal": 5, "name": "recipient", "type_info": "Text" }, { "ordinal": 6, "name": "subject", "type_info": "Text" }, { "ordinal": 7, "name": "body", "type_info": "Text" }, { "ordinal": 8, "name": "metadata", "type_info": "Jsonb" }, { "ordinal": 9, "name": "attempts", "type_info": "Int4" }, { "ordinal": 10, "name": "max_attempts", "type_info": "Int4" }, { "ordinal": 11, "name": "last_error", "type_info": "Text" }, { "ordinal": 12, "name": "created_at", "type_info": "Timestamptz" }, { "ordinal": 13, "name": "updated_at", "type_info": "Timestamptz" }, { "ordinal": 14, "name": "scheduled_for", "type_info": "Timestamptz" }, { "ordinal": 15, "name": "processed_at", "type_info": "Timestamptz" } ], "parameters": { "Left": [ "Uuid", { "Custom": { "name": "comms_type", "kind": { "Enum": [ "welcome", "email_verification", "password_reset", "email_update", "account_deletion", "admin_email", "plc_operation", "two_factor_code", "channel_verification", "passkey_recovery", "legacy_login_alert", "migration_verification", "channel_verified" ] } } }, "Int8" ] }, "nullable": [ false, false, false, false, false, false, true, false, true, false, false, true, false, false, false, true ] }, "hash": "9f3f2b36f11e9446915d3ca29ef81e4ada0c6a6d72764116dac4f99a4e09785e"}123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180{ "db_name": "PostgreSQL", "query": "SELECT\n id, user_id,\n channel as \"channel: CommsChannel\",\n comms_type as \"comms_type: CommsMessageType\",\n status as \"status: CommsStatus\",\n recipient, subject, body, metadata,\n attempts, max_attempts, last_error,\n created_at, updated_at, scheduled_for, processed_at\n FROM comms_queue\n WHERE user_id = $1 AND comms_type = $2\n ORDER BY created_at DESC\n LIMIT $3", "describe": { "columns": [ { "ordinal": 0, "name": "id", "type_info": "Uuid" }, { "ordinal": 1, "name": "user_id", "type_info": "Uuid" }, { "ordinal": 2, "name": "channel: CommsChannel", "type_info": { "Custom": { "name": "comms_channel", "kind": { "Enum": [ "email", "discord", "telegram", "signal" ] } } } }, { "ordinal": 3, "name": "comms_type: CommsMessageType", "type_info": { "Custom": { "name": "comms_type", "kind": { "Enum": [ "welcome", "email_verification", "password_reset", "email_update", "account_deletion", "admin_email", "plc_operation", "two_factor_code", "channel_verification", "passkey_recovery", "legacy_login_alert", "migration_verification", "channel_verified" ] } } } }, { "ordinal": 4, "name": "status: CommsStatus", "type_info": { "Custom": { "name": "comms_status", "kind": { "Enum": [ "pending", "processing", "sent", "failed" ] } } } }, { "ordinal": 5, "name": "recipient", "type_info": "Text" }, { "ordinal": 6, "name": "subject", "type_info": "Text" }, { "ordinal": 7, "name": "body", "type_info": "Text" }, { "ordinal": 8, "name": "metadata", "type_info": "Jsonb" }, { "ordinal": 9, "name": "attempts", "type_info": "Int4" }, { "ordinal": 10, "name": "max_attempts", "type_info": "Int4" }, { "ordinal": 11, "name": "last_error", "type_info": "Text" }, { "ordinal": 12, "name": "created_at", "type_info": "Timestamptz" }, { "ordinal": 13, "name": "updated_at", "type_info": "Timestamptz" }, { "ordinal": 14, "name": "scheduled_for", "type_info": "Timestamptz" }, { "ordinal": 15, "name": "processed_at", "type_info": "Timestamptz" } ], "parameters": { "Left": [ "Uuid", { "Custom": { "name": "comms_type", "kind": { "Enum": [ "welcome", "email_verification", "password_reset", "email_update", "account_deletion", "admin_email", "plc_operation", "two_factor_code", "channel_verification", "passkey_recovery", "legacy_login_alert", "migration_verification", "channel_verified" ] } } }, "Int8" ] }, "nullable": [ false, false, false, false, false, false, true, false, true, false, false, true, false, false, false, true ] }, "hash": "be2d31d5d6fcadcb05f914209117b96d61ad9619adb57dde5fba9183a50fcc49"}1234567891011121314151617181920212223242526272829303132333435363738394041424344454647484950515253545556575859606162636465666768697071727374757677787980818283848586878889909192939495969798{ "db_name": "PostgreSQL", "query": "\n SELECT\n created_at,\n channel as \"channel: CommsChannel\",\n comms_type as \"comms_type: CommsMessageType\",\n status as \"status: CommsStatus\",\n subject,\n body\n FROM comms_queue\n WHERE user_id = $1\n ORDER BY created_at DESC\n LIMIT $2\n ", "describe": { "columns": [ { "ordinal": 0, "name": "created_at", "type_info": "Timestamptz" }, { "ordinal": 1, "name": "channel: CommsChannel", "type_info": { "Custom": { "name": "comms_channel", "kind": { "Enum": [ "email", "discord", "telegram", "signal" ] } } } }, { "ordinal": 2, "name": "comms_type: CommsMessageType", "type_info": { "Custom": { "name": "comms_type", "kind": { "Enum": [ "welcome", "email_verification", "password_reset", "email_update", "account_deletion", "admin_email", "plc_operation", "two_factor_code", "channel_verification", "passkey_recovery", "legacy_login_alert", "migration_verification", "channel_verified" ] } } } }, { "ordinal": 3, "name": "status: CommsStatus", "type_info": { "Custom": { "name": "comms_status", "kind": { "Enum": [ "pending", "processing", "sent", "failed" ] } } } }, { "ordinal": 4, "name": "subject", "type_info": "Text" }, { "ordinal": 5, "name": "body", "type_info": "Text" } ], "parameters": { "Left": [ "Uuid", "Int8" ] }, "nullable": [ false, false, false, false, true, false ] }, "hash": "dc5ff65f1b820892c72e7ecbae5692e17f8a1420cd2477dc5f4157efd283f5e7"}123455678960 unmodified lines7071727374747576777822 unmodified lines10110210310410510610710810911011111211311411510510610711910810911011111299 unmodified lines212213214215227216217218219220217 unmodified linesExpand alluse axum::{Json, extract::State};use serde::{Deserialize, Serialize};use serde_json::json;use tracing::info;use tranquil_db_traits::{CommsChannel, CommsStatus, CommsType};use tranquil_db_traits::{CommsChannel, CommsMessageType, CommsStatus};use tranquil_pds::api::error::{ApiError, DbResultExt};use tranquil_pds::auth::{Active, Auth};use tranquil_pds::state::AppState;use tranquil_types::{Did, Handle};60 unmodified lines#[serde(rename_all = "camelCase")]pub struct NotificationHistoryEntry { pub created_at: String, pub channel: CommsChannel, pub comms_type: CommsType, pub comms_type: CommsMessageType, pub status: CommsStatus, pub subject: Option<String>, pub body: String,}22 unmodified lines .get_notification_history(user_id, 50) .await .log_db_err("get notification history")?;
let sensitive_types = [ CommsType::EmailVerification, CommsType::PasswordReset, CommsType::EmailUpdate, CommsType::TwoFactorCode, CommsType::PasskeyRecovery, CommsType::MigrationVerification, CommsType::PlcOperation, CommsType::ChannelVerification, ];
let notifications = rows .iter() .map(|row| { let body = if sensitive_types.contains(&row.comms_type) { let body = if row.comms_type.carries_secret() { "[Code redacted for security]".to_string() } else { row.body.clone() };99 unmodified lines .infra .enqueue_comms( Some(user_id), &recipient, tranquil_db_traits::CommsType::ChannelVerification, tranquil_db_traits::CommsMessageType::ChannelVerification, Some(&subject), &body, Some(json!({"code": formatted_token})), )217 unmodified linesExpand all8 unmodified lines910111213138 unmodified lines};pub use sender::{ CommsSender, DiscordSender, SendError, SignalSender, TelegramSender, is_valid_phone_number,};pub use tranquil_db_traits::{CommsChannel, CommsStatus, CommsType, QueuedComms};pub use tranquil_db_traits::{CommsChannel, CommsMessageType, CommsStatus, QueuedComms};6 unmodified lines789101111121314151617181920192122232425149 unmodified linesExpand all6 unmodified linesuse tokio::io::{AsyncBufReadExt, AsyncWriteExt, BufReader};use tokio::net::{TcpListener, TcpStream};use tranquil_comms::email::transport::SendMode;use tranquil_comms::email::{EmailSender, types::HeloName};use tranquil_comms::{CommsChannel, CommsSender, CommsStatus, CommsType, QueuedComms, SendError};use tranquil_comms::{ CommsChannel, CommsMessageType, CommsSender, CommsStatus, QueuedComms, SendError,};use uuid::Uuid;
fn fixture(recipient: &str, subject: &str, body: &str) -> QueuedComms { QueuedComms { id: Uuid::new_v4(), user_id: None, channel: CommsChannel::Email, comms_type: CommsType::Welcome, comms_type: CommsMessageType::Welcome, status: CommsStatus::Pending, recipient: recipient.to_string(), subject: Some(subject.to_string()), body: body.to_string(),149 unmodified linesExpand all42 unmodified lines4344454647474849505152535455565758596061626364656667505152535 unmodified lines5960616281636465666787 unmodified lines155156157158177159160161162163203 unmodified linesExpand all36736836937038937137237337437537637739637837938038138238340238438538638738810 unmodified lines39940040140242140340440540640712 unmodified lines42 unmodified lines Self::from_disabled_flag(disabled.unwrap_or(false)) }}
pub use tranquil_types::{CommsChannel, Recipient};pub use tranquil_types::{CommsChannel, CommsMessageType, Recipient};
#[derive(Debug, Clone, Copy, PartialEq, Eq, Serialize, Deserialize, sqlx::Type)]#[sqlx(type_name = "comms_type", rename_all = "snake_case")]pub enum CommsType { Welcome, EmailVerification, PasswordReset, EmailUpdate, AccountDeletion, AdminEmail, PlcOperation, TwoFactorCode, PasskeyRecovery, LegacyLoginAlert, MigrationVerification, ChannelVerification, ChannelVerified,}
#[derive(Debug, Clone, Copy, PartialEq, Eq, Serialize, Deserialize, sqlx::Type)]#[sqlx(type_name = "comms_status", rename_all = "snake_case")]pub enum CommsStatus { Pending, Processing,5 unmodified linespub struct QueuedComms { pub id: Uuid, pub user_id: Option<Uuid>, pub channel: CommsChannel, pub comms_type: CommsType, pub comms_type: CommsMessageType, pub status: CommsStatus, pub recipient: String, pub subject: Option<String>, pub body: String,87 unmodified lines async fn enqueue_comms( &self, user_id: Option<Uuid>, recipient: &Recipient, comms_type: CommsType, comms_type: CommsMessageType, subject: Option<&str>, body: &str, metadata: Option<serde_json::Value>, ) -> Result<Uuid, DbError>;203 unmodified linesExpand all
async fn get_latest_comms_for_user( &self, user_id: Uuid, comms_type: CommsType, comms_type: CommsMessageType, limit: i64, ) -> Result<Vec<QueuedComms>, DbError>;
async fn count_comms_by_type( &self, user_id: Uuid, comms_type: CommsType, comms_type: CommsMessageType, ) -> Result<i64, DbError>;
async fn delete_comms_by_type_for_user( &self, user_id: Uuid, comms_type: CommsType, comms_type: CommsMessageType, ) -> Result<u64, DbError>;
async fn expire_deletion_request(&self, token: &str) -> Result<(), DbError>;
10 unmodified lines#[derive(Debug, Clone)]pub struct NotificationHistoryRow { pub created_at: DateTime<Utc>, pub channel: CommsChannel, pub comms_type: CommsType, pub comms_type: CommsMessageType, pub status: CommsStatus, pub subject: Option<String>, pub body: String,}12 unmodified lines