Skip to content
이 페이지는 AI의 도움으로 작성 및 번역되었습니다. 부정확한 내용이 있으면 개선에 참여해 주세요. GitHub에서 편집

SMTP 설정

PRX-Email은 rustls TLS가 있는 lettre 크레이트를 사용하여 SMTP를 통해 이메일을 전송합니다. 아웃박스 파이프라인은 원자적 클레임-전송-파이널라이즈 워크플로우를 사용하여 중복 전송을 방지하며, 지수적 백오프 재시도와 결정론적 Message-ID 멱등성 키를 제공합니다.

기본 SMTP 설정

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

설정 필드

필드타입필수설명
hostStringSMTP 서버 호스트명 (빈 값 불가)
portu16SMTP 서버 포트 (암시적 TLS의 경우 465, STARTTLS의 경우 587)
userStringSMTP 사용자 이름 (보통 이메일 주소)
auth.passwordOption<String>둘 중 하나SMTP AUTH PLAIN/LOGIN을 위한 비밀번호
auth.oauth_tokenOption<String>둘 중 하나XOAUTH2를 위한 OAuth 액세스 토큰

일반 프로바이더 설정

프로바이더호스트포트인증 방법
Gmailsmtp.gmail.com465앱 비밀번호 또는 XOAUTH2
Outlook / Office 365smtp.office365.com587XOAUTH2
Yahoosmtp.mail.yahoo.com465앱 비밀번호
Fastmailsmtp.fastmail.com465앱 비밀번호

이메일 전송

기본 전송

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

메시지 답장

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

답장은 자동으로 다음을 수행합니다:

  • In-Reply-To 헤더 설정
  • 상위 메시지에서 References 체인 구축
  • 상위 메시지의 발신자에서 수신자 파생
  • 제목에 Re: 접두사 추가

아웃박스 파이프라인

아웃박스 파이프라인은 원자적 상태 머신을 통해 신뢰할 수 있는 이메일 전달을 보장합니다:

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()

상태 머신 규칙

전환조건가드
pending -> sendingclaim_outbox_for_send()status IN ('pending','failed') AND next_attempt_at <= now
sending -> sent프로바이더 수락update_outbox_status_if_current(status='sending')
sending -> failed프로바이더 거부 또는 네트워크 오류update_outbox_status_if_current(status='sending')
failed -> sendingretry_outbox()status IN ('pending','failed') AND next_attempt_at <= now

멱등성

각 아웃박스 메시지는 결정론적 Message-ID를 받습니다:

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

이를 통해 재시도를 원래 전송과 구별할 수 있으며, Message-ID로 중복을 제거하는 프로바이더는 각 재시도를 수락합니다.

재시도 백오프

실패한 전송은 지수적 백오프를 사용합니다:

next_attempt_at = now + base_backoff * 2^retries

기본 백오프가 5초인 경우:

재시도백오프
110초
220초
340초
480초
5160초
6320초
7640초
105,120초 (~85분)

수동 재시도

rust
use prx_email::plugin::RetryOutboxRequest;

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

다음의 경우 재시도가 거부됩니다:

  • 아웃박스 상태가 sent 또는 sending인 경우 (재시도 불가)
  • next_attempt_at에 아직 도달하지 않은 경우 (retry_not_due)

첨부 파일

첨부 파일과 함께 전송

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

첨부 파일 정책

AttachmentPolicy는 크기 및 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(),
};
규칙동작
크기가 max_size_bytes 초과attachment exceeds size limit으로 거부
MIME 타입이 allowed_content_types에 없음attachment content type is not allowed으로 거부
attachment_store 없이 경로 기반 첨부 파일attachment store not configured으로 거부
경로가 스토리지 루트를 벗어남 (../ 탐색)attachment path escapes storage root으로 거부

경로 기반 첨부 파일

디스크에 저장된 첨부 파일의 경우 첨부 파일 스토어를 설정합니다:

rust
use prx_email::plugin::AttachmentStoreConfig;

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

경로 해석에는 디렉토리 탐색 가드가 포함됩니다 -- 심볼릭 링크 기반 탈출을 포함하여 설정된 스토리지 루트 외부로 해석되는 모든 경로는 거부됩니다.

API 응답 형식

모든 전송 작업은 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,
}

다음 단계

Released under the Apache-2.0 License.