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
+2 -2
View File
@@ -22,9 +22,9 @@ const MCP_TOOLS = [
/** # 成员变量: REST API 端点清单(与 routes/apiv1.ts 一致) **/
const API_ENDPOINTS = [
{ method: "GET", path: "/accounts", desc: "账号列表(不含已撤销)" },
{ method: "GET", path: "/domains", desc: "域名列表" },
{ method: "GET", path: "/domains", desc: "可用域名列表(仅 status=active 且已绑定 zone,即 Email Workers 就绪)" },
{ method: "GET", path: "/mailboxes", desc: "邮箱列表" },
{ method: "POST", path: "/mailboxes", desc: "创建邮箱,body: { address, domain_id?, retention_seconds?, webhook? };省略 domain_id 随机分配;409 已存在/无可用域名" },
{ method: "POST", path: "/mailboxes", desc: "创建邮箱,body: { address, domain_id?, retention_seconds?, webhook? };省略 domain_id 随机分配;指定域名须可用(400 domain not available);409 已存在/无可用域名" },
{ method: "DELETE", path: "/mailboxes/:id", desc: "删除邮箱" },
{ method: "GET", path: "/emails", desc: "未过期邮件列表,query: mailbox_id? / limit / offset" },
{ method: "GET", path: "/emails/:id", desc: "邮件详情 + 附件元数据;410 已过期" },
+10 -5
View File
@@ -29,10 +29,11 @@ export function apiV1Routes(): App {
return c.json({ items: rows.results });
});
/** 域名列表 */
/** 域名列表(只返回可用域名:status='active' 且已绑定 zone,即 Email Workers 就绪;与随机分配建邮箱同一判据) */
app.get("/domains", async (c) => {
const rows = await c.env.DB.prepare(
"SELECT id, name, status, account_id, created_at FROM domains ORDER BY created_at DESC",
`SELECT id, name, status, account_id, created_at FROM domains
WHERE status = 'active' AND zone_id IS NOT NULL ORDER BY created_at DESC`,
).all();
return c.json({ items: rows.results });
});
@@ -50,12 +51,16 @@ export function apiV1Routes(): App {
app.post("/mailboxes", async (c) => {
const body = await readJson<{ domain_id?: string; address?: string; retention_seconds?: number; webhook?: string }>(c.req.raw);
if (!body.address) return c.json({ error: "address required" }, 400);
let domain: { id: string; name: string } | null;
let domain: { id: string; name: string; status?: string; zone_id?: string | null } | null;
if (body.domain_id) {
domain = await c.env.DB.prepare("SELECT id, name FROM domains WHERE id = ?")
domain = await c.env.DB.prepare("SELECT id, name, status, zone_id FROM domains WHERE id = ?")
.bind(body.domain_id)
.first<{ id: string; name: string }>();
.first<{ id: string; name: string; status: string; zone_id: string | null }>();
if (!domain) return c.json({ error: "domain not found" }, 404);
// 指定域名同样必须可用(未装 Email Workers 的 pending 域名收不到件,不可用于建邮箱)
if (domain.status !== "active" || !domain.zone_id) {
return c.json({ error: "domain not available (Email Workers not enabled)" }, 400);
}
} else {
domain = await c.env.DB.prepare(
"SELECT id, name FROM domains WHERE status = 'active' AND zone_id IS NOT NULL ORDER BY RANDOM() LIMIT 1",
+11 -5
View File
@@ -173,8 +173,10 @@ async function callTool(
async function dispatchTool(env: Env, name: string, args: Record<string, unknown>): Promise<unknown> {
switch (name) {
case "list_domains": {
// 只返回可用域名(active + 已绑定 zone,Email Workers 就绪),与随机分配建邮箱同一判据
const rows = await env.DB.prepare(
"SELECT id, name, status, account_id, created_at FROM domains ORDER BY created_at DESC",
`SELECT id, name, status, account_id, created_at FROM domains
WHERE status = 'active' AND zone_id IS NOT NULL ORDER BY created_at DESC`,
).all();
return { domains: rows.results };
}
@@ -230,11 +232,15 @@ async function dispatchTool(env: Env, name: string, args: Record<string, unknown
if (!rawAddress) throw new McpToolError("address required", -32602);
let domain: { id: string; name: string } | null;
if (domainId) {
// 指定域名:校验存在
domain = await env.DB.prepare("SELECT id, name FROM domains WHERE id = ?")
// 指定域名:校验存在且可用(pending/未装 Email Workers 的域名收不到件)
const row = await env.DB.prepare("SELECT id, name, status, zone_id FROM domains WHERE id = ?")
.bind(domainId)
.first<{ id: string; name: string }>();
if (!domain) throw new McpToolError("domain not found", -32002);
.first<{ id: string; name: string; status: string; zone_id: string | null }>();
if (!row) throw new McpToolError("domain not found", -32002);
if (row.status !== "active" || !row.zone_id) {
throw new McpToolError("domain not available (Email Workers not enabled)", -32002);
}
domain = row;
} else {
// 省略域名:随机分配一个已启用 Email Workers 的域名;此时地址必须是本地部分
if (rawAddress.includes("@")) {