refactor: keep existing rate limit behavior

This commit is contained in:
dreamhunter2333
2026-08-24 00:17:25 +08:00
parent 1cd43578ad
commit 7a09deb4a4
7 changed files with 9 additions and 144 deletions
+1 -1
View File
@@ -17,7 +17,7 @@
- fix: |Admin| 修复切换一级标签页时二级标签页偶发无选中项、内容不显示及指示条偏移的问题
- fix: |发信页面| 统一邮箱与名称字段顺序,并修复空正文输入框的光标与占位文字错位
- fix: |用户发信| 用户地址发信接口支持角色无限额度,并让同一 IP 的所有绑定地址共用发信频率限制
- fix: |用户发信| 用户地址发信接口支持角色无限额度
### Improvements
+1 -1
View File
@@ -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
-21
View File
@@ -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:
-34
View File
@@ -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 }
-7
View File
@@ -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 "$@"
+4 -75
View File
@@ -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}`);
}
}
});
});
+3 -5
View File
@@ -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)
}