Skip to content
Cette page a été générée et traduite avec l'aide de l'IA. Si vous remarquez des inexactitudes, n'hésitez pas à contribuer. Modifier sur GitHub

Configuration SMTP ​

PRX-Email envoie des emails via SMTP en utilisant le crate lettre avec TLS rustls. Le pipeline de boîte d'envoi utilise un flux de travail atomique claim-send-finalize pour empêcher les envois dupliqués, avec une nouvelle tentative à backoff exponentiel et des clés d'idempotence déterministes de Message-ID.

Configuration SMTP de base ​

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,
    },
};

Champs de configuration ​

ChampTypeRequisDescription
hostStringOuiNom d'hôte du serveur SMTP (ne doit pas être vide)
portu16OuiPort du serveur SMTP (465 pour TLS implicite, 587 pour STARTTLS)
userStringOuiNom d'utilisateur SMTP (généralement l'adresse email)
auth.passwordOption<String>L'un des deuxMot de passe pour SMTP AUTH PLAIN/LOGIN
auth.oauth_tokenOption<String>L'un des deuxJeton d'accès OAuth pour XOAUTH2

Paramètres courants des fournisseurs ​

FournisseurHôtePortMéthode d'auth
Gmailsmtp.gmail.com465Mot de passe d'application ou XOAUTH2
Outlook / Office 365smtp.office365.com587XOAUTH2
Yahoosmtp.mail.yahoo.com465Mot de passe d'application
Fastmailsmtp.fastmail.com465Mot de passe d'application

Envoyer des emails ​

Envoi de base ​

rust
use prx_email::plugin::SendEmailRequest;

let response = plugin.send(SendEmailRequest {
    account_id: 1,
    to: "[email protected]".to_string(),
    subject: "Bonjour".to_string(),
    body_text: "Corps du message ici.".to_string(),
    now_ts: now,
    attachment: None,
    failure_mode: None,
});

Répondre à un message ​

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: "Merci pour votre message !".to_string(),
    now_ts: now,
    attachment: None,
    failure_mode: None,
});

Les réponses définissent automatiquement :

  • L'en-tête In-Reply-To
  • La chaîne References depuis le message parent
  • Le destinataire déduit de l'expéditeur du message parent
  • Le sujet préfixé par Re:

Pipeline de boîte d'envoi ​

Le pipeline de boîte d'envoi garantit une livraison fiable des emails via une machine à états atomique :

mermaid
stateDiagram-v2
    [*] --> pending: send() / reply()
    pending --> sending: claim (atomique)
    sending --> sent: finalize (succès)
    sending --> failed: finalize (erreur)
    failed --> sending: retry (claim)
    failed --> sending: retry_outbox()
    pending --> sending: retry_outbox()

Règles de la machine à états ​

TransitionConditionGarde
pending -> sendingclaim_outbox_for_send()status IN ('pending','failed') AND next_attempt_at <= now
sending -> sentFournisseur a acceptéupdate_outbox_status_if_current(status='sending')
sending -> failedFournisseur a rejeté ou erreur réseauupdate_outbox_status_if_current(status='sending')
failed -> sendingretry_outbox()status IN ('pending','failed') AND next_attempt_at <= now

Idempotence ​

Chaque message de boîte d'envoi obtient un Message-ID déterministe :

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

Cela garantit que les nouvelles tentatives se distinguent de l'envoi original, et les fournisseurs qui dédupliquent par Message-ID accepteront chaque nouvelle tentative.

Backoff de nouvelle tentative ​

Les envois échoués utilisent un backoff exponentiel :

next_attempt_at = now + base_backoff * 2^retries

Avec un backoff de base de 5 secondes :

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

Nouvelle tentative manuelle ​

rust
use prx_email::plugin::RetryOutboxRequest;

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

La nouvelle tentative est rejetée si :

  • Le statut de la boîte d'envoi est sent ou sending (non relançable)
  • next_attempt_at n'a pas encore été atteint (retry_not_due)

Pièces jointes ​

Envoyer avec une pièce jointe ​

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

let response = plugin.send(SendEmailRequest {
    account_id: 1,
    to: "[email protected]".to_string(),
    subject: "Rapport en pièce jointe".to_string(),
    body_text: "Veuillez trouver le rapport en pièce jointe.".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,
});

Politique des pièces jointes ​

AttachmentPolicy applique des restrictions de taille et de type MIME :

rust
use prx_email::plugin::AttachmentPolicy;

let policy = AttachmentPolicy {
    max_size_bytes: 25 * 1024 * 1024,  // 25 MiO
    allowed_content_types: [
        "application/pdf",
        "image/jpeg",
        "image/png",
        "text/plain",
        "application/zip",
    ].into_iter().map(String::from).collect(),
};
RègleComportement
Taille dépasse max_size_bytesRejeté avec attachment exceeds size limit
Type MIME absent de allowed_content_typesRejeté avec attachment content type is not allowed
Pièce jointe par chemin sans attachment_storeRejeté avec attachment store not configured
Chemin échappant la racine de stockage (../ traversal)Rejeté avec attachment path escapes storage root

Pièces jointes basées sur un chemin ​

Pour les pièces jointes stockées sur disque, configurez le stockage de pièces jointes :

rust
use prx_email::plugin::AttachmentStoreConfig;

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

La résolution des chemins inclut des gardes contre la traversée de répertoire -- tout chemin se résolvant hors de la racine de stockage configurée est rejeté, y compris les échappements basés sur les liens symboliques.

Format de réponse de l'API ​

Toutes les opérations d'envoi retournent un ApiResponse<SendResult> :

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

Étapes suivantes ​

Released under the Apache-2.0 License.