Skip to content
Esta página fue generada y traducida con asistencia de IA. Si encuentra alguna imprecisión, no dude en ayudar a mejorarla. Editar en GitHub

Configuración SMTP

PRX-Email envía email a través de SMTP usando el crate lettre con TLS rustls. El pipeline de buzón de salida usa un flujo de trabajo atómico de reclamación-envío-finalización para prevenir envíos duplicados, con reintento de retroceso exponencial y claves de idempotencia deterministas de Message-ID.

Configuración SMTP Básica

rust
use prx_email::plugin::{SmtpConfig, AuthConfig};

let smtp = SmtpConfig {
    host: "smtp.example.com".to_string(),
    port: 465,
    user: "[email protected]".to_string(),
    auth: AuthConfig {
        password: Some("your-app-password".to_string()),
        oauth_token: None,
    },
};

Campos de Configuración

CampoTipoRequeridoDescripción
hostStringNombre de host del servidor SMTP (no debe estar vacío)
portu16Puerto del servidor SMTP (465 para TLS implícito, 587 para STARTTLS)
userStringNombre de usuario SMTP (generalmente la dirección de email)
auth.passwordOption<String>Uno deContraseña para SMTP AUTH PLAIN/LOGIN
auth.oauth_tokenOption<String>Uno deToken de acceso OAuth para XOAUTH2

Ajustes Comunes de Proveedores

ProveedorHostPuertoMétodo de Auth
Gmailsmtp.gmail.com465Contraseña de app o XOAUTH2
Outlook / Office 365smtp.office365.com587XOAUTH2
Yahoosmtp.mail.yahoo.com465Contraseña de app
Fastmailsmtp.fastmail.com465Contraseña de app

Enviar Email

Envío Básico

rust
use prx_email::plugin::SendEmailRequest;

let response = plugin.send(SendEmailRequest {
    account_id: 1,
    to: "[email protected]".to_string(),
    subject: "Hello".to_string(),
    body_text: "Message body here.".to_string(),
    now_ts: now,
    attachment: None,
    failure_mode: None,
});

Responder a un Mensaje

rust
use prx_email::plugin::ReplyEmailRequest;

let response = plugin.reply(ReplyEmailRequest {
    account_id: 1,
    in_reply_to_message_id: "<[email protected]>".to_string(),
    body_text: "Thanks for your message!".to_string(),
    now_ts: now,
    attachment: None,
    failure_mode: None,
});

Las respuestas automáticamente:

  • Establecen el encabezado In-Reply-To
  • Construyen la cadena References del mensaje padre
  • Derivan el destinatario del remitente del mensaje padre
  • Prefijan el asunto con Re:

Pipeline de Buzón de Salida

El pipeline de buzón de salida asegura la entrega confiable de emails a través de una máquina de estados atómica:

mermaid
stateDiagram-v2
    [*] --> pending: send() / reply()
    pending --> sending: claim (atomic)
    sending --> sent: finalize (success)
    sending --> failed: finalize (error)
    failed --> sending: retry (claim)
    failed --> sending: retry_outbox()
    pending --> sending: retry_outbox()

Reglas de la Máquina de Estados

TransiciónCondiciónGuarda
pending -> sendingclaim_outbox_for_send()status IN ('pending','failed') AND next_attempt_at <= now
sending -> sentProveedor aceptóupdate_outbox_status_if_current(status='sending')
sending -> failedProveedor rechazó o error de redupdate_outbox_status_if_current(status='sending')
failed -> sendingretry_outbox()status IN ('pending','failed') AND next_attempt_at <= now

Idempotencia

Cada mensaje del buzón de salida obtiene un Message-ID determinista:

<outbox-{id}-{retries}@prx-email.local>

Esto asegura que los reintentos sean distinguibles del envío original, y los proveedores que de-duplican por Message-ID aceptarán cada reintento.

Retroceso de Reintento

Los envíos fallidos usan retroceso exponencial:

next_attempt_at = now + base_backoff * 2^retries

Con un retroceso base de 5 segundos:

ReintentoRetroceso
110s
220s
340s
480s
5160s
6320s
7640s
105,120s (~85 min)

Reintento Manual

rust
use prx_email::plugin::RetryOutboxRequest;

let response = plugin.retry_outbox(RetryOutboxRequest {
    outbox_id: 42,
    now_ts: now,
    failure_mode: None,
});

El reintento se rechaza si:

  • El estado del buzón de salida es sent o sending (no reintenatable)
  • El next_attempt_at aún no ha sido alcanzado (retry_not_due)

Adjuntos

Enviar con un Adjunto

rust
use prx_email::plugin::{SendEmailRequest, AttachmentInput};

let response = plugin.send(SendEmailRequest {
    account_id: 1,
    to: "[email protected]".to_string(),
    subject: "Report attached".to_string(),
    body_text: "Please find the report attached.".to_string(),
    now_ts: now,
    attachment: Some(AttachmentInput {
        filename: "report.pdf".to_string(),
        content_type: "application/pdf".to_string(),
        base64: Some(base64_encoded_content),
        path: None,
    }),
    failure_mode: None,
});

Política de Adjuntos

El AttachmentPolicy aplica restricciones de tamaño y tipo MIME:

rust
use prx_email::plugin::AttachmentPolicy;

let policy = AttachmentPolicy {
    max_size_bytes: 25 * 1024 * 1024,  // 25 MiB
    allowed_content_types: [
        "application/pdf",
        "image/jpeg",
        "image/png",
        "text/plain",
        "application/zip",
    ].into_iter().map(String::from).collect(),
};
ReglaComportamiento
El tamaño supera max_size_bytesRechazado con attachment exceeds size limit
El tipo MIME no está en allowed_content_typesRechazado con attachment content type is not allowed
Adjunto basado en ruta sin attachment_storeRechazado con attachment store not configured
La ruta escapa de la raíz de almacenamiento (../ traversal)Rechazado con attachment path escapes storage root

Adjuntos Basados en Ruta

Para adjuntos almacenados en disco, configura el almacén de adjuntos:

rust
use prx_email::plugin::AttachmentStoreConfig;

let store = AttachmentStoreConfig {
    enabled: true,
    dir: "/var/lib/prx-email/attachments".to_string(),
};

La resolución de rutas incluye protecciones contra traversal de directorios -- cualquier ruta que se resuelva fuera de la raíz de almacenamiento configurada se rechaza, incluyendo escapes basados en symlinks.

Formato de Respuesta de API

Todas las operaciones de envío devuelven un ApiResponse<SendResult>:

rust
pub struct SendResult {
    pub outbox_id: i64,
    pub status: String,          // "sent" or "failed"
    pub retries: i64,
    pub provider_message_id: Option<String>,
    pub next_attempt_at: i64,
}

Siguientes Pasos

Released under the Apache-2.0 License.