fix: 固化NapCat验证码日志超时规则

This commit is contained in:
sunlei 2026-06-13 13:40:22 +08:00
parent c7b503e61c
commit 2543e584dc
4 changed files with 78 additions and 2 deletions

View File

@ -23,7 +23,7 @@
- 生成验证进程清理计划:按项目路径和端口给出 PowerShell 检查命令,不直接杀进程。 - 生成验证进程清理计划:按项目路径和端口给出 PowerShell 检查命令,不直接杀进程。
- 清理历史产物:统一治理 `.kt-workspace` 下的测试/验证产物,按目录最近修改时间只保留最近 3 轮模板目录永久保留CLI 默认 dry-run真实清理必须显式传 `--execute` - 清理历史产物:统一治理 `.kt-workspace` 下的测试/验证产物,按目录最近修改时间只保留最近 3 轮模板目录永久保留CLI 默认 dry-run真实清理必须显式传 `--execute`
- 检查 env 策略和变更风险:区分后端真实 env 与前端客户端 `.env*`提醒锁文件、核心表格组件、API 时间序列化 KtDateTime 列/DTO 装饰器入口、部署链路和 Vue TSX 插槽写法等高风险改动。 - 检查 env 策略和变更风险:区分后端真实 env 与前端客户端 `.env*`提醒锁文件、核心表格组件、API 时间序列化 KtDateTime 列/DTO 装饰器入口、部署链路和 Vue TSX 插槽写法等高风险改动。
- 全局 CodeReview 只读扫描:汇总全部 KT 子仓库的 Git 状态、敏感文件跟踪、冲突标记、运行时调试输出、疑似凭据字面量、NapCat `latest` 镜像漂移风险、QQBot 重型服务单测导入风险、QQBot OneBot/QQ 登录态边界回归、NapCat 密码登录验证码 wait 阶段、日志窗口锚点、验证码状态早于日志 URL 的短轮询和清理前 pending 回归、普通处理中状态误吃旧验证码日志回归、根目录生成产物、`TASKS.md` 最近记录字段结构和当前变更风险;默认只对变更文件做内容扫描,并放过短中文显示标签和明显测试占位 token避免历史误报污染上下文任何文件改动后都要跑一遍。 - 全局 CodeReview 只读扫描:汇总全部 KT 子仓库的 Git 状态、敏感文件跟踪、冲突标记、运行时调试输出、疑似凭据字面量、NapCat `latest` 镜像漂移风险、QQBot 重型服务单测导入风险、QQBot OneBot/QQ 登录态边界回归、NapCat 密码登录验证码 wait 阶段、日志窗口锚点、验证码状态早于日志 URL 的短轮询、API Pod SSH 日志读取超时和清理前 pending 回归、普通处理中状态误吃旧验证码日志回归、根目录生成产物、`TASKS.md` 最近记录字段结构和当前变更风险;默认只对变更文件做内容扫描,并放过短中文显示标签和明显测试占位 token避免历史误报污染上下文任何文件改动后都要跑一遍。
- 生成或写入 `TASKS.md` 最近记录:默认 `dryRun=true`,确认后再落盘。 - 生成或写入 `TASKS.md` 最近记录:默认 `dryRun=true`,确认后再落盘。
- 生成提交前检查清单:校验 KT commit message 约定。 - 生成提交前检查清单:校验 KT commit message 约定。
@ -106,7 +106,7 @@ pnpm run admin-login -- --url http://127.0.0.1:5999/#/auth/login
| 脚本 | 用途 | | 脚本 | 用途 |
| ------------------------------ | ------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- | | ------------------------------ | ------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- |
| `pnpm run admin-login` | 使用可见 Edge 打开 Admin 登录页,填写账号密码,拖动滑块,保存登录态和截图。默认账号来自初始化数据 `admin/123456`,生产或个人账号用 `KT_ADMIN_USERNAME` / `KT_ADMIN_PASSWORD` 或 CLI 参数覆盖。 | | `pnpm run admin-login` | 使用可见 Edge 打开 Admin 登录页,填写账号密码,拖动滑块,保存登录态和截图。默认账号来自初始化数据 `admin/123456`,生产或个人账号用 `KT_ADMIN_USERNAME` / `KT_ADMIN_PASSWORD` 或 CLI 参数覆盖。 |
| `pnpm run global-review` | 对 KT 全部子仓库做只读全局 CodeReview 扫描,默认仅对变更文件做内容深扫,输出 JSON 复审报告,并校验 `TASKS.md` 最近记录只保留范围、关键词、验证字段、NapCat `latest` 漂移、QQBot 重型服务单测导入风险、OneBot/QQ 登录态边界、NapCat 密码登录验证码 wait 阶段、日志窗口锚点、验证码状态早于日志 URL 的短轮询及清理前 pending 回归,以及普通处理中状态误吃旧验证码日志回归;明显测试占位 token 不报凭据风险,确认误报时优先升级 `src/tools/review.ts`,不要把误报沉积到上下文。 | | `pnpm run global-review` | 对 KT 全部子仓库做只读全局 CodeReview 扫描,默认仅对变更文件做内容深扫,输出 JSON 复审报告,并校验 `TASKS.md` 最近记录只保留范围、关键词、验证字段、NapCat `latest` 漂移、QQBot 重型服务单测导入风险、OneBot/QQ 登录态边界、NapCat 密码登录验证码 wait 阶段、日志窗口锚点、验证码状态早于日志 URL 的短轮询、API Pod SSH 日志读取超时及清理前 pending 回归,以及普通处理中状态误吃旧验证码日志回归;明显测试占位 token 不报凭据风险,确认误报时优先升级 `src/tools/review.ts`,不要把误报沉积到上下文。 |
| `pnpm run obsidian-context` | 输出 Obsidian 索引上下文;可传 `--module Admin`、`--query QQBot`、`--max-documents 10`。 | | `pnpm run obsidian-context` | 输出 Obsidian 索引上下文;可传 `--module Admin`、`--query QQBot`、`--max-documents 10`。 |
| `pnpm run obsidian-validate` | 校验 KT Obsidian vault 结构和链接;默认 warning 不让脚本失败,需要严格模式时传 `--fail-on-warnings`。 | | `pnpm run obsidian-validate` | 校验 KT Obsidian vault 结构和链接;默认 warning 不让脚本失败,需要严格模式时传 `--fail-on-warnings`。 |
| `pnpm run obsidian-sync` | 审计 Obsidian 工作流入口是否连通,并联动执行 validate。 | | `pnpm run obsidian-sync` | 审计 Obsidian 工作流入口是否连通,并联动执行 validate。 |

View File

@ -227,6 +227,51 @@ export async function runSelfTest(): Promise<void> {
) { ) {
throw new Error("QQBot captcha flow valid self-check produced false positive"); throw new Error("QQBot captcha flow valid self-check produced false positive");
} }
const qqbotCaptchaLogTimeoutFindings = findQqbotNapcatCaptchaFlowFindings(
[
"async detectRuntimeCaptchaUrl(runtime, sinceMs) {",
" const result = await this.runProcess(",
" 'ssh',",
" [...this.getSshArgs(), 'sh -s'],",
" script,",
" undefined,",
" this.getRuntimeCheckTimeoutMs(),",
" );",
"}",
],
{
file: "src/qqbot/napcat/qqbot-napcat-container.service.ts",
project: "Node/kt-template-online-api",
},
);
if (
!qqbotCaptchaLogTimeoutFindings.some(
(item) => item.category === "qqbot-napcat-captcha-log-timeout-too-short",
)
) {
throw new Error("QQBot captcha log timeout self-check failed");
}
const qqbotCaptchaLogTimeoutValidFindings =
findQqbotNapcatCaptchaFlowFindings(
[
"async detectRuntimeCaptchaUrl(runtime, sinceMs) {",
" const result = await this.runProcess(",
" 'ssh',",
" [...this.getSshArgs(), 'sh -s'],",
" script,",
" undefined,",
" this.getCaptchaLogReadTimeoutMs(),",
" );",
"}",
],
{
file: "src/qqbot/napcat/qqbot-napcat-container.service.ts",
project: "Node/kt-template-online-api",
},
);
if (qqbotCaptchaLogTimeoutValidFindings.length) {
throw new Error("QQBot captcha log timeout valid self-check false positive");
}
const reviewCliParser = { const reviewCliParser = {
dashedAll: parseGlobalReviewCliArgs([ dashedAll: parseGlobalReviewCliArgs([

View File

@ -336,6 +336,35 @@ export function findQqbotNapcatCaptchaFlowFindings(
}, },
): ReviewFinding[] { ): ReviewFinding[] {
const normalized = context.file.replaceAll("\\", "/"); const normalized = context.file.replaceAll("\\", "/");
if (
context.project === "Node/kt-template-online-api" &&
normalized === "src/qqbot/napcat/qqbot-napcat-container.service.ts"
) {
const detectLine = lines.findIndex((line) =>
/async\s+detectRuntimeCaptchaUrl\s*\(/.test(line),
);
const detectBody =
detectLine >= 0 ? lines.slice(detectLine, detectLine + 45).join("\n") : "";
const findings: ReviewFinding[] = [];
if (
detectBody &&
(detectBody.includes("this.getRuntimeCheckTimeoutMs()") ||
!detectBody.includes("this.getCaptchaLogReadTimeoutMs()"))
) {
findings.push({
category: "qqbot-napcat-captcha-log-timeout-too-short",
file: context.file,
line: detectLine + 1,
level: "P2",
message:
"NapCat 验证码日志读取复用了 5 秒运行态快检超时API Pod 到 NAS 的 SSH 读日志可能被提前杀掉。",
project: context.project,
suggestion:
"detectRuntimeCaptchaUrl 使用独立的验证码日志读取超时,默认不要低于 15 秒,避免 proofWaterUrl 已存在但被 SSH 超时吞掉。",
});
}
return findings;
}
if ( if (
context.project !== "Node/kt-template-online-api" || context.project !== "Node/kt-template-online-api" ||
normalized !== "src/qqbot/account/qqbot-napcat-login.service.ts" normalized !== "src/qqbot/account/qqbot-napcat-login.service.ts"

View File

@ -623,6 +623,7 @@ export function buildComponentWorkflow(
"密码登录触发验证码时保持 scan session pendingAdmin 通过 TencentCaptcha 拿 ticket/randstr/sid再由 API 调用同一 NapCat 容器 /api/QQLogin/CaptchaLogin", "密码登录触发验证码时保持 scan session pendingAdmin 通过 TencentCaptcha 拿 ticket/randstr/sid再由 API 调用同一 NapCat 容器 /api/QQLogin/CaptchaLogin",
"密码登录验证码日志窗口必须从 NAPCAT_QUICK_PASSWORD 环境重建前开始,覆盖重建容器和后续 restart避免 proofWaterUrl 被清理前漏抓", "密码登录验证码日志窗口必须从 NAPCAT_QUICK_PASSWORD 环境重建前开始,覆盖重建容器和后续 restart避免 proofWaterUrl 被清理前漏抓",
"如果验证码状态先于 Docker 日志 URL 到达,必须短窗口轮询当前容器日志拿 proofWaterUrl再进入清理或失败兜底", "如果验证码状态先于 Docker 日志 URL 到达,必须短窗口轮询当前容器日志拿 proofWaterUrl再进入清理或失败兜底",
"API Pod SSH 读取验证码 Docker 日志不能复用 5 秒运行态快检超时,默认读取超时不得低于 15 秒",
"验证码 pending、cancel、expire、success 和失败终态都必须清理临时密码环境变量,清理失败要阻断成功或二维码兜底", "验证码 pending、cancel、expire、success 和失败终态都必须清理临时密码环境变量,清理失败要阻断成功或二维码兜底",
"SSE 返回 step/status/qrcode/error", "SSE 返回 step/status/qrcode/error",
"容器重启只在必要时触发,优先直接通信 NapCat API", "容器重启只在必要时触发,优先直接通信 NapCat API",
@ -633,6 +634,7 @@ export function buildComponentWorkflow(
"docker restart 后 WebUI CheckLoginStatus/GetQQLoginInfo 验证 isLogin/online/uin失败时检查 NapCat 日志是否为无历史登录记录、密码登录失败或需扫码验证", "docker restart 后 WebUI CheckLoginStatus/GetQQLoginInfo 验证 isLogin/online/uin失败时检查 NapCat 日志是否为无历史登录记录、密码登录失败或需扫码验证",
"密码验证码触发时用 watcher 或 scan/status 证明 API 返回 captcha pending 且不暴露完整 proofWaterUrl", "密码验证码触发时用 watcher 或 scan/status 证明 API 返回 captcha pending 且不暴露完整 proofWaterUrl",
"验证码状态先到但 URL 滞后时Jest 需要证明短轮询后仍保持 captcha pending不进入二维码或 error", "验证码状态先到但 URL 滞后时Jest 需要证明短轮询后仍保持 captcha pending不进入二维码或 error",
"NAS 本地日志已有验证码但 API 仍无 captchaUrl 时,必须检查 API Pod SSH 日志读取是否超时",
"scan/events 能看到 quick-login-start/quick-login-fallback/password-login-start/password-login-fallback/重置/生成二维码/等待扫码/成功或失败", "scan/events 能看到 quick-login-start/quick-login-fallback/password-login-start/password-login-fallback/重置/生成二维码/等待扫码/成功或失败",
"目标 Jest 覆盖 quick 成功、password 成功、password 失败进二维码、验证码 CaptchaLogin 回传、密文保存、容器 env 注入和清理失败阻断", "目标 Jest 覆盖 quick 成功、password 成功、password 失败进二维码、验证码 CaptchaLogin 回传、密文保存、容器 env 注入和清理失败阻断",
"手动刷新不超时", "手动刷新不超时",