mirror of
https://github.com/dreamhunter2333/cloudflare_temp_email.git
synced 2026-05-31 13:10:18 +08:00
* fix: auto initialize default send balance * fix: tighten send access auto init flow * refactor: centralize send balance state * fix: separate legacy repair from admin control in send balance Add an `address_sender.source` column to distinguish legacy / auto / user / admin rows. `ensureDefaultSendBalance` now only repairs rows with `source IS NULL`, so admin-disabled and user-requested rows are never overwritten. Admin POST writes tag `source = 'admin'`; new auto-init inserts tag `'auto'`; `requestSendMailAccess` inserts tag `'user'`. Bumps DB_VERSION to v0.0.8 with the usual `PRAGMA table_info` guarded ALTER, plus a standalone SQL patch under db/. Adds E2E regressions: legacy repair path, admin-disabled rows stay disabled across settings and send, send after admin deletion auto-initializes a fresh row. * fix: drop runtime legacy repair; backfill source='legacy' on migrate Pre-v0.0.8 schema cannot distinguish legacy request-send-access remnants from admin-disabled rows — both share `balance = 0, enabled = 0`. Letting ensureDefaultSendBalance repair that shape on upgrade could silently re-enable an admin-disabled row. Remove the runtime repair path entirely: - `ensureDefaultSendBalance` now uses `ON CONFLICT(address) DO NOTHING`; existing rows are never touched. - The v0.0.8 migration (and the matching SQL patch) backfills every pre-existing row with `source = 'legacy'`, making pre-migration state explicitly off-limits to runtime auto-init. - E2E: flip the legacy test to the negative direction — a `source='legacy'` zero-balance row stays untouched by settings reads and send attempts. Harden `resetSenderToLegacy` to return 404 when `meta.changes < 1`. - Update changelog and docs: legacy/admin-disabled rows must be restored manually via the admin UI. * refactor: collapse send balance auto-init to missing-row insert Per review feedback: the runtime guarantee we actually need is "create an address_sender row when one is missing, leave existing rows alone". Once `ensureDefaultSendBalance` switched to `ON CONFLICT DO NOTHING`, the `source` column, the v0.0.8 migration, and the `resetSenderToLegacy` test endpoint became dead weight — the DO NOTHING path already protects admin-disabled and admin-edited rows without any provenance metadata. - Drop `address_sender.source` and the v0.0.8 migration; revert DB_VERSION to v0.0.7. No schema change ships with this PR. - Strip the `source` field from `ensureDefaultSendBalance`, `requestSendMailAccess`, and the admin-update path. - Remove the `/admin/test/reset_sender_to_legacy` test endpoint and its E2E helper; the negative legacy-repair test it served is no longer needed because the runtime no longer touches existing rows. - E2E coverage stays focused on the three guardrails: missing-row auto-init, admin-disabled rows stay disabled, admin deletion triggers a fresh re-insert. - Tighten changelog and docs to "auto-initialize missing rows". * docs: align common-issues with missing-row-only auto-init The FAQ entries for "DEFAULT_SEND_BALANCE set but still No balance" still described the old behaviour of repairing legacy `balance = 0 && enabled = 0` rows. Rewrite both zh and en rows to match the current runtime: only addresses with no existing `address_sender` row get auto-initialised; legacy, admin-disabled, and admin-edited rows must be restored manually through the admin console.
173 lines
6.3 KiB
Markdown
173 lines
6.3 KiB
Markdown
|
||
# 配置发送邮件
|
||
|
||
::: tip 推荐方案
|
||
推荐使用 Cloudflare `send_email` binding 作为默认发信通道。绑定 `SEND_MAIL` 并完成 Email Routing onboarding 后,即可直接向任意外部地址发信。
|
||
|
||
Workers Paid 每月含 3,000 封,超出部分 $0.35 / 1000 封。
|
||
:::
|
||
|
||
## 发信通道优先级
|
||
|
||
每次 `/api/send_mail` 请求按如下顺序匹配通道,**命中即发送**:
|
||
|
||
| 顺序 | 条件 | 通道 | 扣 balance |
|
||
|------|------|------|-----------|
|
||
| 1 | `SEND_MAIL` 已绑定 **且** 收件人在 `verifiedAddressList` | Cloudflare binding(兼容模式) | 否 |
|
||
| 2 | `RESEND_TOKEN` 或 `RESEND_TOKEN_<DOMAIN>` 已配置 | Resend API | 是 |
|
||
| 3 | `SMTP_CONFIG` 含当前域名配置 | worker-mailer SMTP | 是 |
|
||
| 4 | `SEND_MAIL` 已绑定(以上均未命中) | **Cloudflare binding(推荐主通道)** | 是 |
|
||
| — | 以上均未命中 | 抛错 | — |
|
||
|
||
> [!NOTE]
|
||
> binding 发信失败会直接报错。
|
||
|
||
## 使用 Cloudflare `send_email` binding(推荐)
|
||
|
||
仅 CLI 部署时使用,在 `wrangler.toml` 中添加:
|
||
|
||
```toml
|
||
# 通过 Cloudflare send_email binding 发送邮件
|
||
send_email = [
|
||
{ name = "SEND_MAIL" },
|
||
]
|
||
```
|
||
|
||
> [!warning] 重要
|
||
> 绑定名必须为 `SEND_MAIL`,与 Cloudflare 官方文档示例中的 `SEND_EMAIL` 不同。
|
||
|
||
完成下列步骤后即可直接向任意外部地址发信:
|
||
|
||
1. 在 Cloudflare Dashboard 给对应域名开启 Email Routing 并完成 onboarding
|
||
2. `wrangler.toml` 添加上述 `send_email` 绑定
|
||
3. 部署 Worker
|
||
|
||
无需配置任何额外的 env var。
|
||
|
||
## 使用 Resend 发送邮件
|
||
|
||
注册 `https://resend.com/domains` 根据提示添加 DNS 记录,
|
||
|
||
`API KEYS` 页面创建 `api key`
|
||
|
||
然后执行下面的命令,将 `RESEND_TOKEN` 添加到 secrets 中
|
||
|
||
> [!NOTE]
|
||
> 如果你觉得麻烦,也可以直接明文放在 `wrangler.toml` 中 `[vars]` 下面,但是不推荐这样做
|
||
|
||
如果你是通过 UI 部署的,可以在 Cloudflare 的 UI 界面中添加到 `Variables and Secrets` 下面
|
||
|
||
```bash
|
||
# 切换到 worker 目录
|
||
cd worker
|
||
wrangler secret put RESEND_TOKEN
|
||
```
|
||
|
||
如果你有多个域名,对应不同的 `api key`,可以在 `wrangler.toml` 中添加多个 secret, 名称为 `RESEND_TOKEN_` + `<. 换成 _ 的 大写域名>`,例如
|
||
|
||
```bash
|
||
wrangler secret put RESEND_TOKEN_XXX_COM
|
||
wrangler secret put RESEND_TOKEN_DREAMHUNTER2333_XYZ
|
||
```
|
||
|
||
## 使用 SMTP 发送邮件
|
||
|
||
`SMTP_CONFIG` 的格式如下,**key 必须是你自己的发信域名**,value 为 SMTP 配置。
|
||
|
||
SMTP 配置格式详情可以参考 [zou-yu/worker-mailer](https://github.com/zou-yu/worker-mailer/blob/main/README_zh-CN.md)
|
||
|
||
> [!warning] 重要
|
||
> JSON 中的 key(如下面示例中的 `your-domain.com`)必须替换为**你自己的域名**,即 `DOMAINS` 变量中配置的域名。
|
||
> 这是最常见的配置错误之一,请勿直接复制示例中的域名。
|
||
|
||
```json
|
||
{
|
||
"your-domain.com": {
|
||
"host": "smtp.example.com",
|
||
"port": 465,
|
||
"secure": true,
|
||
"authType": [
|
||
"plain",
|
||
"login"
|
||
],
|
||
"credentials": {
|
||
"username": "your-smtp-username",
|
||
"password": "your-smtp-password"
|
||
}
|
||
}
|
||
}
|
||
```
|
||
|
||
**字段说明:**
|
||
|
||
| 字段 | 说明 |
|
||
|------|------|
|
||
| key(如 `your-domain.com`) | 你的发信域名,必须与 `DOMAINS` 中配置的域名一致 |
|
||
| `host` | SMTP 服务器地址,如 `smtp.mailgun.org`、`smtp.gmail.com` 或你自建的 SMTP 服务器地址 |
|
||
| `port` | SMTP 端口,通常 `465`(SSL)或 `587`(STARTTLS) |
|
||
| `secure` | 是否使用 SSL/TLS,端口 465 时设为 `true`,端口 587 时设为 `false` |
|
||
| `authType` | 认证方式,一般使用 `["plain", "login"]` |
|
||
| `credentials.username` | SMTP 服务器的登录用户名 |
|
||
| `credentials.password` | SMTP 服务器的登录密码 |
|
||
|
||
如果你有**多个域名**使用不同的 SMTP 服务,在同一个 JSON 中添加多个 key 即可:
|
||
|
||
```json
|
||
{
|
||
"domain-a.com": {
|
||
"host": "smtp.mailgun.org",
|
||
"port": 465,
|
||
"secure": true,
|
||
"authType": ["plain", "login"],
|
||
"credentials": { "username": "user@domain-a.com", "password": "xxx" }
|
||
},
|
||
"domain-b.com": {
|
||
"host": "smtp.gmail.com",
|
||
"port": 465,
|
||
"secure": true,
|
||
"authType": ["plain", "login"],
|
||
"credentials": { "username": "user@gmail.com", "password": "app-password" }
|
||
}
|
||
}
|
||
```
|
||
|
||
然后执行下面的命令,将 `SMTP_CONFIG` 添加到 secrets 中
|
||
|
||
> [!NOTE]
|
||
> 如果你觉得麻烦,也可以直接明文放在 `wrangler.toml` 中 `[vars]` 下面,但是不推荐这样做
|
||
|
||
如果你是通过 UI 部署的,可以在 Cloudflare 的 UI 界面中添加到 `Variables and Secrets` 下面
|
||
|
||
```bash
|
||
# 切换到 worker 目录
|
||
cd worker
|
||
wrangler secret put SMTP_CONFIG
|
||
```
|
||
|
||
## 发信余额机制
|
||
|
||
用户发送邮件需要有发信余额。余额机制如下:
|
||
|
||
1. **自动初始化默认额度**:当 `DEFAULT_SEND_BALANCE > 0` 时,用户打开前端发信页或第一次调用发信接口时,系统会自动为该地址初始化默认额度
|
||
2. **手动申请**:如果 `DEFAULT_SEND_BALANCE = 0`,用户仍可以在前端界面点击「申请发信权限」按钮,创建待管理员处理的发信权限记录
|
||
3. **无限制发送**:以下方式可以跳过余额检查:
|
||
- 在 admin 后台将地址加入「无限制发送地址列表」
|
||
- 配置 `NO_LIMIT_SEND_ROLE` 环境变量,指定可以无限发送的用户角色
|
||
|
||
> [!NOTE]
|
||
> `DEFAULT_SEND_BALANCE` 仅在地址尚无 `address_sender` 记录时自动插入初始额度(`ON CONFLICT DO NOTHING`),已有记录(包括管理员禁用或手动设置的行)一律保持原样,runtime 不会修改;历史异常或被禁用的地址需由管理员在后台手动启用并设置余额。
|
||
>
|
||
> 第 1 层 `verifiedAddressList` 命中时不扣余额,但同样计入发信额度;第 2/3/4 层统一扣 balance。
|
||
>
|
||
> 发信额度对**全部**发信渠道生效,admin 发信接口也会一起计入。
|
||
>
|
||
> 每日和每月额度按 **UTC** 时间窗口计算。
|
||
>
|
||
> 当前额度实现属于 **soft guard**,适合日常额度控制;在数据库异常或高并发场景下,它不适合作为绝对严格的成本硬闸。
|
||
|
||
## 给 Cloudflare 上已认证的转发邮箱发送邮件
|
||
|
||
适合未完成 Email Routing onboarding 的域名,或 Workers 免费版。
|
||
|
||
只有收件人在 admin 后台的 `已验证地址列表` 中时,才会通过 `SEND_MAIL` binding 发信。
|