mirror of
https://github.com/dreamhunter2333/cloudflare_temp_email.git
synced 2026-07-20 20:11:28 +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.7 KiB
Markdown
173 lines
6.7 KiB
Markdown
|
|
# Configure Email Sending
|
|
|
|
::: tip Recommended
|
|
Use Cloudflare `send_email` binding as the default send channel. Bind `SEND_MAIL` and finish Email Routing onboarding, then the Worker can send to any external address directly.
|
|
|
|
Workers Paid includes 3,000 messages/month, then $0.35 per 1,000 messages.
|
|
:::
|
|
|
|
## Send Channel Priority
|
|
|
|
Each `/api/send_mail` request matches channels in order; **the first hit sends**:
|
|
|
|
| Order | Condition | Channel | Deducts balance |
|
|
|-------|-----------|---------|----------------|
|
|
| 1 | `SEND_MAIL` bound **AND** recipient in `verifiedAddressList` | Cloudflare binding (compat mode) | No |
|
|
| 2 | `RESEND_TOKEN` or `RESEND_TOKEN_<DOMAIN>` set | Resend API | Yes |
|
|
| 3 | `SMTP_CONFIG` has entry for current domain | worker-mailer SMTP | Yes |
|
|
| 4 | `SEND_MAIL` bound (none of the above) | **Cloudflare binding (recommended primary)** | Yes |
|
|
| — | None of the above | Throws | — |
|
|
|
|
> [!NOTE]
|
|
> Binding send failures return an error directly.
|
|
|
|
## Using the Cloudflare `send_email` Binding (Recommended)
|
|
|
|
Only available when deploying via CLI. Add to `wrangler.toml`:
|
|
|
|
```toml
|
|
# Send emails via the Cloudflare send_email binding
|
|
send_email = [
|
|
{ name = "SEND_MAIL" },
|
|
]
|
|
```
|
|
|
|
> [!warning] Important
|
|
> The binding name must be `SEND_MAIL` — different from Cloudflare's official `SEND_EMAIL` example.
|
|
|
|
After the following steps, you can send to any external address directly:
|
|
|
|
1. Enable Email Routing on the domain in the Cloudflare Dashboard and complete onboarding
|
|
2. Add the `send_email` binding shown above to `wrangler.toml`
|
|
3. Deploy the Worker
|
|
|
|
No additional env var is required.
|
|
|
|
## Send Emails Using Resend
|
|
|
|
Register at `https://resend.com/domains` and add DNS records according to the instructions.
|
|
|
|
Create an `api key` on the `API KEYS` page.
|
|
|
|
Then execute the following command to add `RESEND_TOKEN` to secrets:
|
|
|
|
> [!NOTE]
|
|
> If you find this troublesome, you can also put it directly in plain text under `[vars]` in `wrangler.toml`, but this is not recommended
|
|
|
|
If you deployed through the UI, you can add it under `Variables and Secrets` in the Cloudflare UI interface.
|
|
|
|
```bash
|
|
# Switch to worker directory
|
|
cd worker
|
|
wrangler secret put RESEND_TOKEN
|
|
```
|
|
|
|
If you have multiple domains with different `api keys`, you can add multiple secrets in `wrangler.toml`, named `RESEND_TOKEN_` + `<UPPERCASE DOMAIN WITH . REPLACED BY _>`, for example:
|
|
|
|
```bash
|
|
wrangler secret put RESEND_TOKEN_XXX_COM
|
|
wrangler secret put RESEND_TOKEN_DREAMHUNTER2333_XYZ
|
|
```
|
|
|
|
## Send Emails Using SMTP
|
|
|
|
The format of `SMTP_CONFIG` is as follows. **The key must be your own sending domain**, and the value is the SMTP configuration.
|
|
|
|
For SMTP configuration format details, refer to [zou-yu/worker-mailer](https://github.com/zou-yu/worker-mailer/blob/main/README_zh-CN.md)
|
|
|
|
> [!warning] Important
|
|
> The JSON key (e.g. `your-domain.com` in the example below) must be replaced with **your own domain** — the domain configured in your `DOMAINS` variable.
|
|
> This is one of the most common configuration mistakes. Do not copy the example domain directly.
|
|
|
|
```json
|
|
{
|
|
"your-domain.com": {
|
|
"host": "smtp.example.com",
|
|
"port": 465,
|
|
"secure": true,
|
|
"authType": [
|
|
"plain",
|
|
"login"
|
|
],
|
|
"credentials": {
|
|
"username": "your-smtp-username",
|
|
"password": "your-smtp-password"
|
|
}
|
|
}
|
|
}
|
|
```
|
|
|
|
**Field Reference:**
|
|
|
|
| Field | Description |
|
|
|-------|-------------|
|
|
| key (e.g. `your-domain.com`) | Your sending domain, must match a domain configured in `DOMAINS` |
|
|
| `host` | SMTP server address, e.g. `smtp.mailgun.org`, `smtp.gmail.com`, or your self-hosted SMTP server |
|
|
| `port` | SMTP port, typically `465` (SSL) or `587` (STARTTLS) |
|
|
| `secure` | Whether to use SSL/TLS. Set to `true` for port 465, `false` for port 587 |
|
|
| `authType` | Authentication method, typically `["plain", "login"]` |
|
|
| `credentials.username` | SMTP server login username |
|
|
| `credentials.password` | SMTP server login password |
|
|
|
|
If you have **multiple domains** using different SMTP services, add multiple keys in the same JSON:
|
|
|
|
```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" }
|
|
}
|
|
}
|
|
```
|
|
|
|
Then execute the following command to add `SMTP_CONFIG` to secrets:
|
|
|
|
> [!NOTE]
|
|
> If you find this troublesome, you can also put it directly in plain text under `[vars]` in `wrangler.toml`, but this is not recommended
|
|
|
|
If you deployed through the UI, you can add it under `Variables and Secrets` in the Cloudflare UI interface.
|
|
|
|
```bash
|
|
# Switch to worker directory
|
|
cd worker
|
|
wrangler secret put SMTP_CONFIG
|
|
```
|
|
|
|
## Send Balance Mechanism
|
|
|
|
Users need a send balance to send emails. The balance mechanism works as follows:
|
|
|
|
1. **Auto-initialize Default Quota**: When `DEFAULT_SEND_BALANCE > 0`, the system automatically initializes the default quota when the user opens the send page or calls the send-mail API for the first time
|
|
2. **Manual Request**: If `DEFAULT_SEND_BALANCE = 0`, users can still click "Request Send Permission" in the frontend to create a pending send-access record for admins to review
|
|
3. **Unlimited Sending**: The following methods can bypass balance checks:
|
|
- Add the address to the "No Limit Send Address List" in the admin console
|
|
- Configure the `NO_LIMIT_SEND_ROLE` environment variable to specify roles that can send without limits
|
|
|
|
> [!NOTE]
|
|
> `DEFAULT_SEND_BALANCE` only inserts an initial quota for addresses that do not yet have an `address_sender` row (`ON CONFLICT DO NOTHING`); existing rows — including admin-disabled or admin-edited ones — are never modified by the runtime path. Restoring a previously disabled or pre-existing address must go through the admin console (enable + set balance).
|
|
>
|
|
> Layer 1 (`verifiedAddressList` hit) does not deduct balance, but it still counts toward send limits; layers 2/3/4 all deduct balance.
|
|
>
|
|
> Send limits apply to **all** send channels, including admin send endpoints.
|
|
>
|
|
> Daily and monthly windows are calculated in **UTC**.
|
|
>
|
|
> The current limit implementation is a **soft guard**. It is suitable for routine quota control, but it should not be treated as a strict hard-stop cost gate under database errors or high concurrency.
|
|
|
|
## Send Emails to Authenticated Forwarding Addresses on Cloudflare
|
|
|
|
Typical use case: non-onboarded domains or Workers free-tier users.
|
|
|
|
In this compatibility mode, mail is sent via `SEND_MAIL` binding only when the recipient is in the admin `Verified Address List`.
|