feat: return address_id in /admin/new_address response (#913)

* feat: return address_id in /admin/new_address response

- Add address_id field to newAddress function return type
- Update CHANGELOG.md and CHANGELOG_EN.md

Fixes #912

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>

* test: verify address_id in new_address response

* fix: add address_id validation and improve test coverage

- Add null check for address_id after DB query
- Change address_id to required field in return type
- Add dedicated test for /admin/new_address endpoint
- Update e2e helper return type to non-optional

---------

Co-authored-by: Claude Opus 4.6 <noreply@anthropic.com>
This commit is contained in:
Dream Hunter
2026-03-26 00:18:15 +08:00
committed by GitHub
co-authored by Claude Opus 4.6
parent 03965f3612
commit a45d01f9fd
6 changed files with 31 additions and 4 deletions
+1
View File
@@ -10,6 +10,7 @@
### Features ### Features
- feat: |Admin API| `/admin/new_address` 接口返回值新增 `address_id` 字段,避免创建后需再次查询地址 ID(#912
- feat: |自动回复| 发件人过滤支持正则表达式匹配,使用 `/pattern/` 语法(如 `/@example\.com$/`),同时保持前缀匹配的向后兼容 - feat: |自动回复| 发件人过滤支持正则表达式匹配,使用 `/pattern/` 语法(如 `/@example\.com$/`),同时保持前缀匹配的向后兼容
- feat: |Turnstile| 新增全局登录表单 Turnstile 人机验证,通过 `ENABLE_GLOBAL_TURNSTILE_CHECK` 环境变量控制(#767 - feat: |Turnstile| 新增全局登录表单 Turnstile 人机验证,通过 `ENABLE_GLOBAL_TURNSTILE_CHECK` 环境变量控制(#767
- feat: |Telegram| Telegram 推送支持发送邮件附件(单文件限制 50MB),多附件通过 `sendMediaGroup` 批量发送,通过 `ENABLE_TG_PUSH_ATTACHMENT` 环境变量开启(#894 - feat: |Telegram| Telegram 推送支持发送邮件附件(单文件限制 50MB),多附件通过 `sendMediaGroup` 批量发送,通过 `ENABLE_TG_PUSH_ATTACHMENT` 环境变量开启(#894
+1
View File
@@ -10,6 +10,7 @@
### Features ### Features
- feat: |Admin API| `/admin/new_address` endpoint now returns `address_id` field, avoiding additional query after address creation (#912)
- feat: |Auto Reply| Add regex matching support for sender filter using `/pattern/` syntax (e.g. `/@example\.com$/`), backward compatible with prefix matching - feat: |Auto Reply| Add regex matching support for sender filter using `/pattern/` syntax (e.g. `/@example\.com$/`), backward compatible with prefix matching
- feat: |Turnstile| Add global Turnstile CAPTCHA for all login forms via `ENABLE_GLOBAL_TURNSTILE_CHECK` env var (#767) - feat: |Turnstile| Add global Turnstile CAPTCHA for all login forms via `ENABLE_GLOBAL_TURNSTILE_CHECK` env var (#767)
- feat: |Telegram| Support sending email attachments in Telegram push (50MB per file limit), multiple attachments sent via `sendMediaGroup`, controlled by `ENABLE_TG_PUSH_ATTACHMENT` env var (#894) - feat: |Telegram| Support sending email attachments in Telegram push (50MB per file limit), multiple attachments sent via `sendMediaGroup`, controlled by `ENABLE_TG_PUSH_ATTACHMENT` env var (#894)
+2 -2
View File
@@ -16,7 +16,7 @@ export async function createTestAddress(
ctx: APIRequestContext, ctx: APIRequestContext,
name: string, name: string,
domain: string = TEST_DOMAIN domain: string = TEST_DOMAIN
): Promise<{ jwt: string; address: string }> { ): Promise<{ jwt: string; address: string; address_id: number }> {
const uniqueName = `${name}${Date.now()}`; const uniqueName = `${name}${Date.now()}`;
const res = await ctx.post(`${WORKER_URL}/api/new_address`, { const res = await ctx.post(`${WORKER_URL}/api/new_address`, {
data: { name: uniqueName, domain }, data: { name: uniqueName, domain },
@@ -25,7 +25,7 @@ export async function createTestAddress(
throw new Error(`Failed to create address: ${res.status()} ${await res.text()}`); throw new Error(`Failed to create address: ${res.status()} ${await res.text()}`);
} }
const body = await res.json(); const body = await res.json();
return { jwt: body.jwt, address: body.address }; return { jwt: body.jwt, address: body.address, address_id: body.address_id };
} }
/** /**
+2 -1
View File
@@ -4,9 +4,10 @@ import { WORKER_URL, TEST_DOMAIN, createTestAddress, deleteAddress, requestSendA
test.describe('Address Lifecycle', () => { test.describe('Address Lifecycle', () => {
test('create address, request send access, fetch settings, then delete', async ({ request }) => { test('create address, request send access, fetch settings, then delete', async ({ request }) => {
// Create address // Create address
const { jwt, address } = await createTestAddress(request, 'lifecycle-test'); const { jwt, address, address_id } = await createTestAddress(request, 'lifecycle-test');
expect(address).toContain('@' + TEST_DOMAIN); expect(address).toContain('@' + TEST_DOMAIN);
expect(jwt).toBeTruthy(); expect(jwt).toBeTruthy();
expect(address_id).toBeGreaterThan(0);
// Request send access (creates address_sender row with DEFAULT_SEND_BALANCE) // Request send access (creates address_sender row with DEFAULT_SEND_BALANCE)
await requestSendAccess(request, jwt); await requestSendAccess(request, jwt);
+19
View File
@@ -0,0 +1,19 @@
import { test, expect } from '@playwright/test';
import { WORKER_URL, TEST_DOMAIN } from '../../fixtures/test-helpers';
test.describe('Admin New Address', () => {
test('should return address_id in response', async ({ request }) => {
const uniqueName = `admin-test${Date.now()}`;
const res = await request.post(`${WORKER_URL}/admin/new_address`, {
data: { name: uniqueName, domain: TEST_DOMAIN },
});
expect(res.ok()).toBe(true);
const body = await res.json();
expect(body.address).toContain('@' + TEST_DOMAIN);
expect(body.jwt).toBeTruthy();
expect(body.address_id).toBeGreaterThan(0);
expect(typeof body.address_id).toBe('number');
});
});
+6 -1
View File
@@ -168,7 +168,7 @@ export const newAddress = async (
enableCheckNameRegex?: boolean, enableCheckNameRegex?: boolean,
sourceMeta?: string | undefined | null, sourceMeta?: string | undefined | null,
} }
): Promise<{ address: string, jwt: string, password?: string | null }> => { ): Promise<{ address: string, jwt: string, password?: string | null, address_id: number }> => {
const msgs = i18n.getMessagesbyContext(c); const msgs = i18n.getMessagesbyContext(c);
// trim whitespace and remove special characters // trim whitespace and remove special characters
name = name.trim().replace(getNameRegex(c), '') name = name.trim().replace(getNameRegex(c), '')
@@ -247,6 +247,10 @@ export const newAddress = async (
`SELECT id FROM address where name = ?` `SELECT id FROM address where name = ?`
).bind(name).first<number>("id"); ).bind(name).first<number>("id");
if (!address_id) {
throw new Error(msgs.FailedCreateAddressMsg);
}
// 如果启用地址密码功能,自动生成密码 // 如果启用地址密码功能,自动生成密码
const generatedPassword = await generatePasswordForAddress(c, name); const generatedPassword = await generatePasswordForAddress(c, name);
@@ -259,6 +263,7 @@ export const newAddress = async (
jwt: jwt, jwt: jwt,
address: name, address: name,
password: generatedPassword, password: generatedPassword,
address_id: address_id,
} }
} }