fix(deploy): 失败通知不再把所有失败都说成「Gateway 不可达」
# 误导的来源
`reportFailure` 只在 `result.sent` 上分叉,而 sent 为假同时覆盖「fetch 抛」「HTTP 4xx/5xx」,
于是两种情况都打印同一句话。
# 实测代价
切换插件时 dsh 进了崩溃循环(我自己的部署缺陷所致),通知脚本连打六条
「Gateway 不可达」并写进 spool —— 而网关**一直在正常服务**(NRestarts=0),
日志里真实响应是 **HTTP 403**:
POST http://127.0.0.1:8180/api/v1/mail/send ... - 403 245B
403 的原因是同一会话连续中继邮件撞上防「两个 Agent 互相唤醒」的跳数上限
(`maxRelayHops=5`):告警邮件的会话别名由主题派生,崩溃循环下六封落进同一条会话。
一句话把人送去查网络,而问题在策略层 —— **故障通知本身给出误导性诊断,
是「静默失败」的另一种形态**。
# 修法
新增 `describeSendError`:`Gateway HTTP <status>: <body>` 归类为「网关可达,但返回
HTTP xxx(附响应体)」;只有 fetch 本身失败 / AbortError 才说「不可达」/「超时」。
双向验证(真实走代码路径,都不实际发信):
网关指向关闭端口 → 「网关不可达:fetch failed」
真网关 + 坏密钥 → 「网关可达,但返回 HTTP 401:{"error":"密钥无效"}」
This commit is contained in:
@ -213,6 +213,26 @@ export async function flushSpool(env = process.env) {
|
||||
return { total: names.length, sent, failed };
|
||||
}
|
||||
|
||||
/**
|
||||
* 把发送失败翻译成一句能定位原因的话。
|
||||
*
|
||||
* `postPayload` 对非 2xx 抛的是 `Gateway HTTP <status>: <body>` —— 里面已经带着
|
||||
* 状态码与响应体,直接打出来就够定位了。只有 fetch 本身抛(DNS/连接/超时)才真的是
|
||||
* 「不可达」,此时按名字区分开。
|
||||
*/
|
||||
function describeSendError(error) {
|
||||
const message = String(error?.message || error || '');
|
||||
const httpMatch = message.match(/^Gateway HTTP (\d{3}): ([\s\S]*)$/);
|
||||
if (httpMatch) {
|
||||
return `网关可达,但返回 HTTP ${httpMatch[1]}:${httpMatch[2].slice(0, 300)}`;
|
||||
}
|
||||
if (error?.name === 'AbortError') return '请求超时(网关未在限定时间内响应)';
|
||||
if (/fetch failed|ECONNREFUSED|ENOTFOUND|EHOSTUNREACH/i.test(message)) {
|
||||
return `网关不可达:${message}`;
|
||||
}
|
||||
return message || '未知错误';
|
||||
}
|
||||
|
||||
async function main(argv = process.argv.slice(2), env = process.env) {
|
||||
const serviceName = argValue(argv, '--service');
|
||||
if (argv.includes('--flush')) {
|
||||
@ -234,9 +254,25 @@ async function main(argv = process.argv.slice(2), env = process.env) {
|
||||
env,
|
||||
serviceName,
|
||||
});
|
||||
console.error(result.sent
|
||||
? `[agentmail-failure-notify] ${serviceName} 异常已上报`
|
||||
: `[agentmail-failure-notify] Gateway 不可达,报告已暂存 ${result.path}`);
|
||||
if (result.sent) {
|
||||
console.error(`[agentmail-failure-notify] ${serviceName} 异常已上报`);
|
||||
return;
|
||||
}
|
||||
/*
|
||||
* 区分「连不上」与「连上了但被拒」。
|
||||
*
|
||||
* 原先两种情况都打印「Gateway 不可达」。实测代价:切换插件时 dsh 进了崩溃循环,
|
||||
* 通知脚本连打六条「Gateway 不可达」并写进 spool —— 而网关**一直在正常服务**
|
||||
* (NRestarts=0),真实响应是 **HTTP 403**(同一会话连续中继邮件撞上防互相唤醒
|
||||
* 的跳数上限,`maxRelayHops=5`)。
|
||||
*
|
||||
* 那句话会把人送去查网络,而问题在策略层。故障通知本身给出误导性诊断,
|
||||
* 是「静默失败」的另一种形态。
|
||||
*/
|
||||
console.error(
|
||||
`[agentmail-failure-notify] ${serviceName} 异常上报失败,报告已暂存 ${result.path}`
|
||||
);
|
||||
console.error(`[agentmail-failure-notify] 原因:${describeSendError(result.error)}`);
|
||||
}
|
||||
|
||||
const isCLI = process.argv[1] && resolve(process.argv[1]) === fileURLToPath(import.meta.url);
|
||||
|
||||
Reference in New Issue
Block a user