v2.9.2: 开放 API 域名可见性修复——GET /api/v1/domains 与 MCP list_domains 只返回可用域名(active+已绑 zone),指定 domain_id 建邮箱同样校验可用,杜绝未接入域名被报成可用

This commit is contained in:
bigcode
2026-10-03 23:54:28 +08:00
parent 52d5827932
commit 1c7dba025b
6 changed files with 89 additions and 16 deletions
+60 -2
View File
@@ -80,6 +80,8 @@ describe("Console API v1(Bearer wmh_ Key)", () => {
const t = await makeTestEnv();
const cookie = await loginCookie(t);
const key = await createKey(t, cookie);
// v2.9.2 起指定域名建邮箱要求域名可用(已绑 zone),先给种子域名绑上
await t.env.DB.prepare("UPDATE domains SET zone_id = 'zone-v1' WHERE id = ?").bind(t.domainId).run();
const create = await t.app.fetch(
new Request("http://localhost/api/v1/mailboxes", {
method: "POST",
@@ -169,6 +171,8 @@ describe("MCP(/api/v1/mcp,JSON-RPC over HTTP)", () => {
const t = await makeTestEnv();
const cookie = await loginCookie(t);
const key = await createKey(t, cookie);
// v2.9.2 起指定域名建邮箱要求域名可用(已绑 zone),先给种子域名绑上
await t.env.DB.prepare("UPDATE domains SET zone_id = 'zone-mcp' WHERE id = ?").bind(t.domainId).run();
const email = await ingestEmail(t.env, {
raw: new TextEncoder().encode(TEST_MAIL),
mailFrom: "a@b.c",
@@ -238,7 +242,7 @@ describe("MCP(/api/v1/mcp,JSON-RPC over HTTP)", () => {
const t = await makeTestEnv();
const cookie = await loginCookie(t);
const key = await createKey(t, cookie);
// 种子域名无 zone_id(未启用 Email Workers);再补一个已启用的域名
// 种子域名无 zone_id(未启用 Email Workers,v2.9.2 起不进可用列表);再补一个已启用的域名
const now = new Date().toISOString();
await t.env.DB.prepare(
"INSERT INTO domains (id, cloudflare_account_id, account_id, name, status, zone_id, created_at, updated_at) VALUES (?, 'cfacct-1', ?, 'ready.test', 'active', 'zone-ready', ?, ?)",
@@ -248,7 +252,8 @@ describe("MCP(/api/v1/mcp,JSON-RPC over HTTP)", () => {
const domainsData = JSON.parse(((await domains.json()) as { result: { content: { text: string }[] } }).result.content[0].text) as {
domains: { id: string; name: string }[];
};
expect(domainsData.domains.map((d) => d.name).sort()).toEqual(["example.test", "ready.test"]);
// 未启用 Email Workers 的域名不可见
expect(domainsData.domains.map((d) => d.name)).toEqual(["ready.test"]);
// 省略 domain_id:必须落在已启用 Email Workers 的域名上,且 webhook 参数生效
const created = await mcpCall(t, key, {
@@ -320,4 +325,57 @@ describe("MCP(/api/v1/mcp,JSON-RPC over HTTP)", () => {
const mcpRow = await t.env.DB.prepare("SELECT id FROM mailboxes WHERE address = ?").bind("mcpbad@example.test").first();
expect(mcpRow).toBeNull();
});
test("域名可见性与建邮箱可用校验:未启用 Email Workers 的域名不可见、不可指定(v2.9.2)", async () => {
const t = await makeTestEnv();
const cookie = await loginCookie(t);
const key = await createKey(t, cookie);
// 种子域名 active 但未绑 zone → GET /api/v1/domains 不返回它
const rest = await t.app.fetch(
new Request("http://localhost/api/v1/domains", { headers: { Authorization: `Bearer ${key}` } }),
t.env,
);
expect(rest.status).toBe(200);
expect(((await rest.json()) as { items: { name: string }[] }).items.map((d) => d.name)).not.toContain("example.test");
// 显式指定未启用域名建邮箱:REST 400 / MCP -32002
const restBad = await t.app.fetch(
new Request("http://localhost/api/v1/mailboxes", {
method: "POST",
headers: { Authorization: `Bearer ${key}`, "Content-Type": "application/json" },
body: JSON.stringify({ domain_id: t.domainId, address: "pendingbox" }),
}),
t.env,
);
expect(restBad.status).toBe(400);
expect(((await restBad.json()) as { error: string }).error).toContain("not available");
const mcpBad = await mcpCall(t, key, {
jsonrpc: "2.0",
id: 30,
method: "tools/call",
params: { name: "create_mailbox", arguments: { domain_id: t.domainId, address: "pendingmcp" } },
});
expect(((await mcpBad.json()) as { error: { message: string } }).error.message).toContain("not available");
const left = await t.env.DB.prepare("SELECT id FROM mailboxes WHERE address = 'pendingbox@example.test'").first();
expect(left).toBeNull();
// 绑上 zone 后变为可用:列表可见、创建成功
await t.env.DB.prepare("UPDATE domains SET zone_id = 'zone-ok' WHERE id = ?").bind(t.domainId).run();
const rest2 = await t.app.fetch(
new Request("http://localhost/api/v1/domains", { headers: { Authorization: `Bearer ${key}` } }),
t.env,
);
expect(((await rest2.json()) as { items: { name: string }[] }).items.map((d) => d.name)).toContain("example.test");
const ok = await t.app.fetch(
new Request("http://localhost/api/v1/mailboxes", {
method: "POST",
headers: { Authorization: `Bearer ${key}`, "Content-Type": "application/json" },
body: JSON.stringify({ domain_id: t.domainId, address: "pendingbox" }),
}),
t.env,
);
expect(ok.status).toBe(201);
});
});