From 7a09deb4a48a10a38c76fdf1d96e23ffc0ec9d8d Mon Sep 17 00:00:00 2001 From: dreamhunter2333 Date: Mon, 24 Aug 2026 00:17:25 +0800 Subject: [PATCH] refactor: keep existing rate limit behavior --- CHANGELOG.md | 2 +- CHANGELOG_EN.md | 2 +- e2e/docker-compose.yml | 21 ------ e2e/fixtures/wrangler.toml.e2e.rate-limit | 34 ---------- e2e/scripts/docker-entrypoint.sh | 7 -- e2e/tests/api/user-send-mail.spec.ts | 79 ++--------------------- worker/src/worker.ts | 8 +-- 7 files changed, 9 insertions(+), 144 deletions(-) delete mode 100644 e2e/fixtures/wrangler.toml.e2e.rate-limit diff --git a/CHANGELOG.md b/CHANGELOG.md index 1616849..155744e 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -17,7 +17,7 @@ - fix: |Admin| 修复切换一级标签页时二级标签页偶发无选中项、内容不显示及指示条偏移的问题 - fix: |发信页面| 统一邮箱与名称字段顺序,并修复空正文输入框的光标与占位文字错位 -- fix: |用户发信| 用户地址发信接口支持角色无限额度,并让同一 IP 的所有绑定地址共用发信频率限制 +- fix: |用户发信| 用户地址发信接口支持角色无限额度 ### Improvements diff --git a/CHANGELOG_EN.md b/CHANGELOG_EN.md index 287b261..0d2eac3 100644 --- a/CHANGELOG_EN.md +++ b/CHANGELOG_EN.md @@ -17,7 +17,7 @@ - fix: |Admin| Fix secondary tabs occasionally losing their active item, hiding content, and leaving the indicator offset after switching primary tabs - fix: |Send Mail| Use a consistent address/name field order and align the empty content editor caret with its placeholder -- fix: |User Send Mail| Apply role-based unlimited sending to user-address APIs and share one send-rate limit across all bound addresses for the same IP +- fix: |User Send Mail| Apply role-based unlimited sending to user-address APIs ### Improvements diff --git a/e2e/docker-compose.yml b/e2e/docker-compose.yml index 98f2cbc..678b124 100644 --- a/e2e/docker-compose.yml +++ b/e2e/docker-compose.yml @@ -90,24 +90,6 @@ services: start_period: 10s retries: 20 - worker-rate-limit: - build: - context: .. - dockerfile: e2e/Dockerfile.worker - args: - WRANGLER_TOML: e2e/fixtures/wrangler.toml.e2e.rate-limit - ports: - - "8792:8792" - command: ["pnpm", "exec", "wrangler", "dev", "--port", "8792", "--ip", "0.0.0.0"] - depends_on: - - mailpit - healthcheck: - test: ["CMD", "curl", "-sf", "http://localhost:8792/health_check"] - interval: 3s - timeout: 5s - start_period: 10s - retries: 20 - frontend: build: context: .. @@ -165,7 +147,6 @@ services: WORKER_URL_ENV_OFF: http://worker-env-off:8790 WORKER_GZIP_URL: http://worker-gzip:8788 WORKER_URL_SEND_MAIL_DOMAIN: http://worker-send-mail-domain:8791 - WORKER_URL_RATE_LIMIT: http://worker-rate-limit:8792 FRONTEND_URL: https://frontend:5173 MAILPIT_API: http://mailpit:8025/api SMTP_PROXY_HOST: smtp-proxy @@ -186,8 +167,6 @@ services: condition: service_healthy worker-send-mail-domain: condition: service_healthy - worker-rate-limit: - condition: service_healthy frontend: condition: service_started smtp-proxy: diff --git a/e2e/fixtures/wrangler.toml.e2e.rate-limit b/e2e/fixtures/wrangler.toml.e2e.rate-limit deleted file mode 100644 index 57948de..0000000 --- a/e2e/fixtures/wrangler.toml.e2e.rate-limit +++ /dev/null @@ -1,34 +0,0 @@ -name = "cloudflare_temp_email_rate_limit" -main = "src/worker.ts" -compatibility_date = "2025-04-01" -compatibility_flags = [ "nodejs_compat" ] -keep_vars = true - -[vars] -PREFIX = "tmp" -DEFAULT_DOMAINS = ["test.example.com"] -DOMAINS = ["test.example.com"] -JWT_SECRET = "e2e-test-secret-key-rate-limit" -BLACK_LIST = "" -ENABLE_USER_CREATE_EMAIL = true -ENABLE_USER_DELETE_EMAIL = true -DEFAULT_SEND_BALANCE = 10 -DISABLE_ADMIN_PASSWORD_CHECK = true -E2E_TEST_MODE = true -SMTP_CONFIG = """ -{"test.example.com":{"host":"mailpit","port":1025,"secure":false}} -""" - -[[kv_namespaces]] -binding = "KV" -id = "e2e-test-kv-rate-limit-00000000-0000-0000-0000-000000000000" - -[[d1_databases]] -binding = "DB" -database_name = "e2e-temp-email-rate-limit" -database_id = "e2e-test-db-rate-limit-00000000-0000-0000-0000-000000000000" - -[[ratelimits]] -name = "RATE_LIMITER" -namespace_id = "1001" -simple = { limit = 2, period = 60 } diff --git a/e2e/scripts/docker-entrypoint.sh b/e2e/scripts/docker-entrypoint.sh index 48932d4..a745319 100755 --- a/e2e/scripts/docker-entrypoint.sh +++ b/e2e/scripts/docker-entrypoint.sh @@ -110,12 +110,5 @@ if [ -n "${WORKER_GZIP_URL:-}" ]; then echo " Gzip worker database initialized" fi -if [ -n "${WORKER_URL_RATE_LIMIT:-}" ]; then - echo "==> Initializing rate-limit worker database" - curl -sf -X POST "$WORKER_URL_RATE_LIMIT/admin/db_initialize" > /dev/null - curl -sf -X POST "$WORKER_URL_RATE_LIMIT/admin/db_migration" > /dev/null - echo " Rate-limit worker database initialized" -fi - echo "==> Running Playwright tests" exec npx playwright test "$@" diff --git a/e2e/tests/api/user-send-mail.spec.ts b/e2e/tests/api/user-send-mail.spec.ts index 3db3d13..ef57fe0 100644 --- a/e2e/tests/api/user-send-mail.spec.ts +++ b/e2e/tests/api/user-send-mail.spec.ts @@ -11,15 +11,15 @@ import { updateAddressSender, } from '../../fixtures/test-helpers'; -async function createUser(request: APIRequestContext, workerUrl = WORKER_URL) { +async function createUser(request: APIRequestContext) { const email = `user-send-${Date.now()}@test.example.com`; const password = hashPassword('test-password-123'); - const registerRes = await request.post(`${workerUrl}/user_api/register`, { + const registerRes = await request.post(`${WORKER_URL}/user_api/register`, { data: { email, password }, }); expect(registerRes.ok()).toBe(true); - const loginRes = await request.post(`${workerUrl}/user_api/login`, { + const loginRes = await request.post(`${WORKER_URL}/user_api/login`, { data: { email, password }, }); expect(loginRes.ok()).toBe(true); @@ -32,9 +32,8 @@ async function bindAddress( request: APIRequestContext, userJwt: string, addressJwt: string, - workerUrl = WORKER_URL, ) { - const response = await request.post(`${workerUrl}/user_api/bind_address`, { + const response = await request.post(`${WORKER_URL}/user_api/bind_address`, { headers: { Authorization: `Bearer ${addressJwt}`, 'x-user-token': userJwt, @@ -369,74 +368,4 @@ test.describe('User send mail API', () => { } }); - test('shares one rate-limit bucket across bound addresses', async ({ request }) => { - const workerUrl = process.env.WORKER_URL_RATE_LIMIT || ''; - test.skip(!workerUrl, 'WORKER_URL_RATE_LIMIT is not configured'); - const addressIds: number[] = []; - let userId: number | undefined; - - try { - const enableUserRes = await request.post(`${workerUrl}/admin/user_settings`, { - data: { - enable: true, - enableMailVerify: false, - maxAddressCount: 0, - }, - }); - expect(enableUserRes.ok()).toBe(true); - const user = await createUser(request, workerUrl); - userId = user.userId; - const createAddress = async (name: string) => { - const response = await request.post(`${workerUrl}/api/new_address`, { - data: { name, domain: 'test.example.com' }, - }); - expect(response.ok()).toBe(true); - return await response.json() as { - jwt: string; - address_id: number; - }; - }; - const first = await createAddress(`rate-first-${Date.now()}`); - const second = await createAddress(`rate-second-${Date.now()}`); - addressIds.push(first.address_id, second.address_id); - await bindAddress(request, user.jwt, first.jwt, workerUrl); - await bindAddress(request, user.jwt, second.jwt, workerUrl); - - const requestAccess = async (address: typeof first) => { - const accessRes = await request.post( - `${workerUrl}/user_api/address/${address.address_id}/request_send_mail_access`, - { headers: { 'x-user-token': user.jwt } }, - ); - expect(accessRes.ok()).toBe(true); - }; - await requestAccess(first); - await requestAccess(second); - - const send = (address: typeof first, sequence: number) => request.post( - `${workerUrl}/user_api/address/${address.address_id}/send_mail`, - { - headers: { 'x-user-token': user.jwt }, - data: { - to_mail: 'recipient@test.example.com', - subject: `Shared rate limit ${sequence} ${Date.now()}`, - content: 'Rate limit test', - is_html: false, - }, - }, - ); - - expect((await send(first, 1)).ok()).toBe(true); - expect((await send(second, 2)).ok()).toBe(true); - const limitedRes = await send(first, 3); - expect(limitedRes.status()).toBe(429); - expect(await limitedRes.text()).toContain('Rate limit exceeded'); - } finally { - await Promise.allSettled(addressIds.map((addressId) => ( - request.delete(`${workerUrl}/admin/delete_address/${addressId}`) - ))); - if (userId !== undefined) { - await request.delete(`${workerUrl}/admin/users/${userId}`); - } - } - }); }); diff --git a/worker/src/worker.ts b/worker/src/worker.ts index 4f87f4e..d68261b 100644 --- a/worker/src/worker.ts +++ b/worker/src/worker.ts @@ -74,11 +74,9 @@ app.use('/*', async (c, next) => { ) { const reqIp = c.req.raw.headers.get("cf-connecting-ip") if (reqIp && c.env.RATE_LIMITER) { - const { success } = await c.env.RATE_LIMITER.limit({ - key: `${c.req.path.startsWith("/user_api/address/") - ? "/user_api/address/send_mail" - : c.req.path}|${reqIp}` - }) + const { success } = await c.env.RATE_LIMITER.limit( + { key: `${c.req.path}|${reqIp}` } + ) if (!success) { return c.text(`IP=${reqIp} Rate limit exceeded for ${c.req.path}`, 429) }