@aglyn/ 共享用户电子邮件
从Aglyn monorepo出版到npm. 随后的版本 语义版本。 。 。 .
安装
npm install @aglyn/shared-util-email
一个送出的地方 应用程序 电子邮件,通过邮件提供者 部署选择 : 重新发送 默认,或 接线员自己的发件人身后一个webhook.
在这图书馆之前,一个卖家的30行 fetch 已复制
跨越 10 个文件, 每个 读取 env vars 本身, 每个处理失败 a
有点不一样 合并它们就意味着提供者,发送者
和失败的语义 在一个地方变化.
哪些是范围,哪些不是范围
| 邮件 | 寄来的 | 这里? |
|---|---|---|
| 邀请、收据、使用摘要、运动、工作人员通知 | 邮件提供者 | ✅ |
| 核实、密码重置 | 火地轴 | ❌ |
寄入邮件到 @aglyn.com 邮箱 | 谷歌工作空间 | ❌ |
配置
发送者和携带邮件的提供者的设置,在 调用时间 :
USAGE_EMAIL_FROM="Aglyn <[email protected]>"
RESEND_API_KEY=re_xxxxxxxx # the default provider
# or, for your own sender:
AGLYN_MAIL_WEBHOOK_URL=https://relay.internal/send
AGLYN_MAIL_WEBHOOK_TOKEN=…
需要发送者和提供者的设置。 和两个
失踪, sendEmail() 警告和返回 { sent: false, reason: 'unconfigured' } 它不扔出, 因此本地和预览环境运行
没有邮件提供商就没事.
邮件提供者
sendEmail() 决定信息是否、作为谁和由谁发出 -- --
镇压 派遣率高的总督 钓鱼屏 派遣
身份,未订阅页脚。 A 类 MailProvider (单位:千美元)mail-provider.ts仅限
并回答平台的其他问题
关于平台本身词汇中的邮件: 是否其认证有效,
一个送货的网友说, 收到的信息包含什么
账户已发送 .
mail-providers.ts 选择部署的提供者 :
AGLYN_MAIL_PROVIDER一个名字..resend, (中文(简体) ),webhook,或 id 插件或 登记在registerMailProvider()。 。 。 .- 取消设置, 第一个内置设置胜出 :
resend何时RESEND_API_KEY已经设定,webhook仅当AGLYN_MAIL_WEBHOOK_URL这是, 否则resend,没有配置. - 既未建入也未注册的名称是 拒绝: 每一次发送 被错误命名而跳过, 没有任何东西会回到另一个 卖家.
内置内容汇编到这个库中,而不是由 插件, 因为这是交易栏: 它必须从每个服务器发送 已装入或未装入插件的进程,以及未装入的注册 事情的发生 看起来像邮件关闭.
其余的平台通过询问 normalizeDeliveryEvents(), (中文(简体) ),
readInboundMailEvent() 财务报告和审定财务报表 mailProviderReads(),从未通过提供者
以名为相. mail-provider-resend.ts 是唯一一个知道Resend的模块
有线格式,以及 mail-provider-webhook.ts 记录了webhook的.
设置和 DNS 记录在 docs/EMAIL_SETUP.md。 。 。 .
使用量
import { sendEmail } from '@aglyn/shared-util-email'
const result = await sendEmail({
to: '[email protected]',
subject: 'You have been invited',
text: 'Sign in to accept.',
context: 'invite', // shows up in logs on failure
})
if (!result.sent) {
// 'unconfigured' | 'no-recipient' | 'rejected' | 'network'
console.warn('no mail went out:', result.reason)
}
sendEmail() 从不扔。出局邮件在任何地方都是最好的
这个代码库——退票不能因为收件被反弹而失败——所以,每个
结果作为结果对象回来。 检查 sent:这是让
控制台诚实地告诉用户消息是否真的发出.
可选字段 : html, (中文(简体) ), headers (例如:. List-Unsubscribe, (中文(简体) ), tags (网络
归属问题), replyTo,以及 from (覆盖所配置的发送者——很少)
对,因为 USAGE_EMAIL_FROM 是一个经过验证的身份.
运输边界
postResendEmail() (单位:千美元) mail-provider-resend.ts)是唯一一个函数
Resend发送端点的 POSTs,
和这个 掷出 没有有效载荷 to 而不是把它放在线上.
此有效载荷不能成为消息; 重新发送答案 422 missing_required_field,它花费API电话,然后坐在供应商
仪表板看起来就像邮件 未能交付,什么都没有
给负责的代码起名. sendEmail() 筛选很久之前的收件人
这一点,所以普通的发送者从来没有遇到过警卫——它的存在是因为
RESEND_SEND_ENDPOINT 导出并直接取回它的模块
绕过每次检查 sendEmail() 拥有.
列表未订阅设置
list-unsubscribe.ts 就是那个地方 List-Unsubscribe 编号 :
List-Unsubscribe-Post (RFC 8058)对对是模型,每个发送者制作
它是一个背景——一个外联序列(默认)和营销活动
(默认的) :
import {
decideListUnsubscribe,
listUnsubscribeHeaders,
readListUnsubscribeSetting,
} from '@aglyn/shared-util-email/list-unsubscribe'
const requested = readListUnsubscribeSetting(stored.listUnsubscribe, true)
const decision = decideListUnsubscribe({
requested,
recentVolume, // the organization's campaign messages in the last day
sendVolume, // everyone this email will address
})
const headers = decision.on ? listUnsubscribeHeaders({ url: oneClickUrl }) : {}
decideListUnsubscribe 是散装的守望者。当他们出发的时候
这需要组织 EMAIL_LIST_UNSUBSCRIBE_BULK_THRESHOLD 信件
(默认5000, Gmail's and Yahoo's line) 背负的对反正
判决说这是强迫的,为什么。 头是普通头,所以任何
后置提供者 sendEmail 送他们.
正在检查配置
import { isEmailConfigured, describeEmailConfig, checkEmailCredentials }
from '@aglyn/shared-util-email'
isEmailConfigured()——提供者有其设置,发送者有设置. 用来回答501而不是假装送来的.describeEmailConfig()——哪个提供者,其设置中哪个是 缺少, 加上发送者及其域名, 不包括任何证书 .checkEmailCredentials()——询问提供者是否具有 已接受 没有发送任何信息。没有探测器答案的提供者unknown。Resend的探测器GETs 域集和 读取错误 NAME 而不是状态。 A 类2xx,或一个restricted_api_key编号 :invalid_permission拒绝( 发送范围) 密钥获得,并且只有在Resend认证了密钥后才能到达, 是指证书的作品.missing_api_key, (中文(简体) ),validation_error财务报告和审定财务报表suspended_api_key意思是被拒绝了 还有什么是unknown永远invalid-key。它从不触及发送端点:瞄准它的探测器 由Resend 登录为 a422打开POST /emails并在仪表板上宣读, 作为失败的邮件。 它无法确认域验证—— 只有真正的发送 这样做.
这些只有工作人员才能拿回来 /api/admin/email-health 运行在控制台.
运行单元测试
运行 nx test shared-util-email 通过
欢乐。 。 。 .
注意: 倾向于裸体 jest 结束 nx test 当测试取决于env vars—— nx
输入根 .env,这可以让真正的失败 测试绿色.
许可证
阿帕奇2.0 (英语). 资料来源: https://github.com/aglyn/aglyn/tree/main/libs/shared/util/email 互联网档案馆的存檔,存档日期2013-12-02