v2.4.1 回读校验收紧到 status:SPF 冲突(misconfigured)不再谎报已配置
实测 adx9.com 全链路时发现的新缺口:/enable 成功后 enabled=true,但 zone 的
Email Routing status 可能仍是 misconfigured(SPF 指向旧邮件服务)——
照 v2.4.0 的校验(enabled===true)照样会把域名标成「已配置」,而邮件根本不会投递,
与 MX 假成功是同一类问题。
- cfapi: 回读校验增加 status 判定:
- ready / unlocked(过渡态,实测邮件照常投递)视为成功
- misconfigured* 判失败,并把状态对象里的 errors 翻译成中文
(spf.foreign = 现有 SPF 指向其它邮件服务;spf.missing = 给出应替换的 SPF 内容)
- cf-mock: 读回的状态随 emailRoutingStatusAfterEnable 可配置
- oauth.test 新增 2 例:misconfigured 判失败(不标 active)、unlocked 视为成功
线上排查记录(2026-09-30,已手工修复):
adx9.com 删除自建 MX(10 mx.adx9.com)→ POST /email/routing/enable 成功但
status=misconfigured/locked(errors: spf.foreign/spf.missing)→ PATCH
/email/routing/dns(unlock)→ 替换 SPF TXT 为 v=spf1 include:_spf.mx.cloudflare.net
~all → Graph API 发测试邮件 → Console 收到(msg_7eb6afeba6179293),全链路打通。
This commit is contained in:
@@ -395,6 +395,17 @@ export interface CFEmailRoutingSettings {
|
||||
enabled?: boolean;
|
||||
status?: string;
|
||||
skip_wizard?: boolean;
|
||||
/** Cloudflare 侧的配置问题清单(如 spf.foreign / spf.missing;非 API 错误信封) **/
|
||||
errors?: { code?: string; existing?: CFRoutingDnsRecord; missing?: CFRoutingDnsRecord }[];
|
||||
}
|
||||
|
||||
/** # 成员变量: Email Routing 指出的 DNS 记录(existing = 有冲突的,missing = 应补的) **/
|
||||
export interface CFRoutingDnsRecord {
|
||||
id?: string;
|
||||
name?: string;
|
||||
content?: string;
|
||||
type?: string;
|
||||
ttl?: number;
|
||||
}
|
||||
|
||||
/** # 成员变量: 域名的一条 MX 记录 **/
|
||||
@@ -426,8 +437,8 @@ export interface CFApi {
|
||||
/** Worker credential secret(HMAC 签名密钥) **/
|
||||
workerSecret: string;
|
||||
}): Promise<{ id: string }>;
|
||||
/** 开启 Zone 的 Email Routing(幂等;开启后会回读校验真的生效) */
|
||||
enableEmailRouting(opts: { accessToken: string; zoneId: string }): Promise<{ enabled: boolean }>;
|
||||
/** 开启 Zone 的 Email Routing(加并锁定 MX/SPF 记录;开启后会回读校验真的生效) */
|
||||
enableEmailRouting(opts: { accessToken: string; zoneId: string }): Promise<{ enabled: boolean; status?: string }>;
|
||||
/** 读取 Zone 的 Email Routing 设置 */
|
||||
getEmailRoutingSettings(opts: { accessToken: string; zoneId: string }): Promise<CFEmailRoutingSettings>;
|
||||
/** 配置 Zone catch-all 规则指向 Email Worker(幂等) */
|
||||
@@ -628,8 +639,7 @@ export class RealCFApi implements CFApi {
|
||||
*
|
||||
* 开启后**回读校验** enabled:接口说成功不算数,状态对象变成 ready 才算开成。
|
||||
*/
|
||||
async enableEmailRouting(opts: { accessToken: string; zoneId: string }) {
|
||||
try {
|
||||
async enableEmailRouting(opts: { accessToken: string; zoneId: string }) { try {
|
||||
await this.api(opts.accessToken, `/zones/${opts.zoneId}/email/routing/enable`, {
|
||||
method: "POST",
|
||||
body: JSON.stringify({ skip_wizard: true }),
|
||||
@@ -653,7 +663,33 @@ export class RealCFApi implements CFApi {
|
||||
`(enabled=false);请到 Cloudflare Dashboard 检查该域名的邮件路由状态`,
|
||||
);
|
||||
}
|
||||
return { enabled: true };
|
||||
// enabled 只是开关;真正能收信还要 status 不是 misconfigured(如 SPF 指向别的邮件服务)。
|
||||
// unlocked 是刚解锁待复核的过渡态,实测邮件照常投递,不视为失败。
|
||||
const status = settings.status ?? "";
|
||||
if (status.startsWith("misconfigured")) {
|
||||
throw new CFApiError(this.describeStatusErrors(settings.errors), undefined, `/zones/${opts.zoneId}/email/routing`);
|
||||
}
|
||||
return { enabled: true, status: status || "ready" };
|
||||
}
|
||||
|
||||
/** 把 Email Routing 状态对象里的 errors(spf.foreign / spf.missing 等)翻译成可执行的提示 */
|
||||
private describeStatusErrors(errors?: CFEmailRoutingSettings["errors"]): string {
|
||||
const parts: string[] = [];
|
||||
for (const e of errors ?? []) {
|
||||
if (e.code === "spf.foreign" && e.existing) {
|
||||
parts.push(`现有 SPF 记录(${e.existing.content ?? ""})指向其它邮件服务`);
|
||||
} else if (e.code === "spf.missing" && e.missing) {
|
||||
parts.push(
|
||||
`缺少 Cloudflare 的 SPF 记录,请在 DNS 里把 @ 的 TXT 记录内容替换为:${e.missing.content ?? "v=spf1 include:_spf.mx.cloudflare.net ~all"}`,
|
||||
);
|
||||
} else if (e.code) {
|
||||
parts.push(`存在配置问题(${e.code})`);
|
||||
}
|
||||
}
|
||||
return (
|
||||
"Email Routing 已开启但状态是 misconfigured,邮件不会正常投递。" +
|
||||
(parts.length ? `原因:${parts.join(";")}。` : "请到 Cloudflare Dashboard → Email → Email Routing 查看具体原因。")
|
||||
);
|
||||
}
|
||||
|
||||
/** 把 2008 翻译成可执行的提示:点名冲突的 MX 记录,并说明删掉的后果 */
|
||||
|
||||
Reference in New Issue
Block a user