電子郵件
預設使用 Resend,也能改用 Cloudflare Email。範本已內建身分驗證郵件,新的業務郵件統一透過 emailService 與既有範本寄送。
Saavo 範本已提供統一的郵件寄送功能,並內建一組身分驗證相關的郵件範本。註冊驗證、忘記密碼、修改電子郵件地址與雙重驗證變更等流程,都透過同一套郵件服務寄送。
開發自己的產品時,通常不必在頁面或業務程式碼中直接呼叫 Resend、Cloudflare 等郵件服務 SDK。選擇適合的郵件服務、設定寄件者資訊後,透過 emailService 統一寄送即可。本機開發環境不會實際寄信,而是將郵件內容輸出到主控台,方便偵錯。
既有功能
範本初始化後,即可使用下列郵件功能:
| 功能 | 預設值 | 說明 |
|---|---|---|
| 郵件寄送方式 | Resend | config/deploy.ts → emailProvider.type |
| 寄件者 | config/base.ts → fromEmailAddress | 顯示名稱與地址 |
| 回覆地址 | supportEmail | 使用者回信時使用 |
| 本機開發寄送 | 主控台預覽 | 不會實際傳送 |
| 寄送速率限制 | 每個電子郵件地址每天 5 封 | 正式環境超過上限時會失敗 |
已註冊的範本與目前觸發方式:
| 範本 | 目前用途 |
|---|---|
register | 註冊後的驗證郵件,由註冊 Reaction 觸發 |
verifyEmail | 電子郵件驗證信 |
accountVerification | 帳號敏感操作的驗證郵件 |
passwordReset | 密碼重設郵件 |
emailChanged | 修改電子郵件地址後的安全通知 |
twoFactorSecurityChanged | 雙重驗證設定變更後的安全通知 |
welcome | 註冊成功的歡迎郵件,目前未使用 |
業務功能透過 src/core/services/email 的 emailService 寄送。不要在頁面元件中直接使用第三方郵件服務 SDK。
先體驗郵件寄送
使用預設設定註冊帳號,開發伺服器主控台會出現類似下列記錄:
[DEV EMAIL PREVIEW] {
recipients: [ 'you@example.com' ],
subject: '...',
text: '...Your verification code is ...'
}使用記錄中的驗證碼,即可完成電子郵件驗證,與身分驗證文件中的流程相同。

只有正式環境或預覽環境才會實際寄信。開發模式即使觸發寄送速率限制,也只會寫入記錄,仍會完成預覽流程。
注意事項
啟用強制電子郵件驗證前,必須先確認正式環境能實際寄出郵件,否則新使用者會停在驗證步驟,無法繼續。
設定電子郵件
選擇寄送方式
預設設定如下:
emailProvider: {
type: 'resend',
}Resend 是範本目前的預設選擇,還需要在正式環境設定 RESEND_API_KEY,並完成網域驗證。
改用 Cloudflare Email:
emailProvider: {
type: 'cloudflare',
}Cloudflare Email 使用 wrangler.jsonc 中名為 EMAIL 的 send_email 繫結。切換前,請先確認目前的 Cloudflare 帳戶已具備向實際使用者寄送郵件的權限。
修改寄件者
fromEmailAddress: {
name: 'Acme',
email: 'noreply@your-domain.com',
},
supportEmail: 'support@your-domain.com',正式環境的 From 必須屬於郵件服務已允許使用的網域,並與這裡的地址一致。寄件者資訊與回覆地址是全域設定,所有郵件都會使用相同設定。
調整寄送頻率限制
spam: {
resourceProtection: {
emailSendService: {
lifetimeDuration: '1d',
maxEmailsPerUser: 5,
idleTimeout: '2d',
},
},
}這是依收件地址計數的保護機制,不是行銷郵件配額,身分驗證郵件也受到限制。不要為了方便測試,就在正式環境將上限調得過高,否則可能影響實際使用者。
準備郵件服務
Cloudflare Email 需要在 Cloudflare 設定寄信網域,並完成 Worker 繫結。Resend 則需要 API Key 與已驗證的網域。本機開發不需要這些設定,也能預覽郵件,但正式對外提供服務前,必須使用非開發環境實際寄出一封郵件。
金鑰與網域說明請參考:
在業務功能中寄送郵件
新的業務郵件應沿用 emailService,不要另外撰寫 SMTP 用戶端。
現成方法如下:
import emailService from '@/core/services/email';
await emailService.sendVerifyEmail(c, {
email: user.email,
name: user.displayName ?? user.userName,
verification: { type: 'code', code },
});此外,也提供 sendRegisterEmail、sendForgotPasswordEmail 與 sendAccountVerificationEmail。通用入口接受 WorkerCtx:
await emailService.send(workerCtx, templateKind, email, data)HTTP 請求中應使用 resolveFetchWorkerCtx(c) 取得 WorkerCtx,不要將 Hono context 直接傳入通用的 send。
新情境可以沿用既有範本,或依序:
- 在
src/libs/email/template新增範本。 - 在
factory.ts的EMAIL_TEMPLATES註冊。 - 再封裝成用途明確的
send*方法。
對於可能重試的業務流程,建議優先透過 Reaction 中的 SendEmailCommand 寄送,以便處理重複執行與非同步投遞。只有目前請求必須立即取得寄送結果時,才直接呼叫 emailService。
目前內建範本預設輸出純文字。郵件服務與 Resend、Cloudflare 兩個寄送配接器,都支援 html 欄位。如果需要 HTML 郵件,可以在範本中產生並回傳 HTML,同時保留純文字內容。
上線檢查
郵件寄送會影響註冊與忘記密碼等重要流程,上線前建議至少確認:
-
fromEmailAddress與supportEmail已改為自己的地址。 - 正式環境的
emailProvider.type與實際金鑰相符。 - 寄信網域已在 Cloudflare 或 Resend 完成驗證。
- 使用正式環境設定實際寄出註冊或驗證郵件,且收件匣可以收到。
- 若啟用強制電子郵件驗證,未收到郵件的使用者就無法繼續使用產品,因此必須先確保能穩定寄信。
- 異常大量呼叫 API 時,寄送速率限制會生效。
不要只用開發主控台的預覽,代替正式環境的郵件寄送測試。
常見問題
接下來
依接下來要開發的功能,可以繼續閱讀:
- 想設定身分驗證郵件 → 使用者身分驗證與帳號
- 想非同步寄送業務郵件 → 背景工作
- 想了解實際產品如何設定品牌與郵件 → WebpageToPDF 實作教學
- 想串接 Resend → 設定 Resend
大多數產品完成本章後,只需要選擇郵件寄送方式,並完成網域驗證。
新增產品郵件時,沿用現有範本的方式,不要在頁面中直接呼叫郵件服務 SDK。