check-online.mjs 4.0 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172
  1. #!/usr/bin/env node
  2. // 线上环境一键核查(Node >=18, 零依赖): 登录 → 核心接口链 → 12 项 PASS/FAIL
  3. // 用法:
  4. // node check-online.mjs # 核查线上 https://recording.sh-lami.com
  5. // SMARTBADGE_API_BASE=http://localhost:3002 node check-online.mjs # 核查本地
  6. // 密码: SMARTBADGE_PASSWORD 环境变量或第一个参数
  7. import { baseUrl, login, httpJson, loadToken, fail } from './lib.mjs';
  8. const PASSWORD = process.env.SMARTBADGE_PASSWORD || process.argv[2];
  9. const base = baseUrl();
  10. if (!PASSWORD) fail('用法: node check-online.mjs [密码](或先 SMARTBADGE_PASSWORD=<密码>)');
  11. let pass = 0;
  12. let failCount = 0;
  13. const check = (name, cond, extra = '') => {
  14. const ok = !!cond;
  15. console.log(`[${ok ? 'PASS' : 'FAIL'}] ${name}${extra ? ` — ${extra}` : ''}`);
  16. ok ? pass++ : failCount++;
  17. };
  18. console.log(`== 线上健康核查 (${base}) ==`);
  19. // 1. 登录
  20. let token;
  21. try {
  22. token = await login({ base, password: PASSWORD });
  23. } catch (err) {
  24. console.log(`\n== 结果: 0 PASS / FAIL(登录失败) ==`);
  25. process.exit(1);
  26. }
  27. const auth = { base, token };
  28. // 2. health / 错误面
  29. const h = await httpJson('GET', '/api/analysis/health', undefined, auth);
  30. const warnings = h.json?.data?.warnings ?? [];
  31. const isBenign = (w) => /无待转录文件|属正常/.test(String(w));
  32. const realWarnings = warnings.filter((w) => !isBenign(w));
  33. check('health ok(仅无害告警)', h.ok && (h.json?.data?.ok === true || realWarnings.length === 0), `warnings=${warnings.length}${realWarnings.length ? `(异常: ${realWarnings.join('; ')})` : ''}`);
  34. check('无害告警外无警告', realWarnings.length === 0, realWarnings.join('; ') || `无害=${warnings.length}条`);
  35. check('自动重试中=0', Number(h.json?.data?.retryingCount ?? -1) === 0, `retryingCount=${h.json?.data?.retryingCount}`);
  36. check('OSS 缺失=0', Number(h.json?.data?.ossMissingCount ?? -1) === 0, `ossMissingCount=${h.json?.data?.ossMissingCount}`);
  37. const cat = h.json?.data?.categoryCounts ?? {};
  38. const catTotal = Object.values(cat).reduce((a, b) => a + b, 0);
  39. check('错误分类计数=0', catTotal === 0, JSON.stringify(cat));
  40. // 3. 归档面
  41. const missing = await httpJson('GET', '/api/archive/missing', undefined, auth);
  42. const missingList = missing.json?.data?.items ?? missing.json?.data;
  43. check('归档失败待重试=空', missing.ok && Array.isArray(missingList) && missingList.length === 0, `missing=${Array.isArray(missingList) ? missingList.length : 'err'}`);
  44. // 4. 列表面(ADR-005 本地主路径 + 实时分流)
  45. const rec = await httpJson('POST', '/api/smartbadge/recordings/list', { page: 1, pageSize: 5 }, auth);
  46. check('录音本地列表', rec.ok && (rec.json?.data?.total || 0) > 0, `total=${rec.json?.data?.total}`);
  47. check('录音列表无失败项', rec.ok && Array.isArray(rec.json?.data?.failed) && rec.json.data.failed.length === 0);
  48. const today = new Date().toISOString().slice(0, 10);
  49. const rt = await httpJson('POST', '/api/smartbadge/recordings/list', { endDate: today, page: 1, pageSize: 5 }, auth);
  50. check('实时窗分流', rt.ok && Array.isArray(rt.json?.data?.items), `total=${rt.json?.data?.total}`);
  51. const hb = await httpJson('POST', '/api/smartbadge/logs/heartbeat', { page: 1, pageSize: 5 }, auth);
  52. check('心跳接口', hb.ok && typeof hb.json?.data?.total === 'number', `total=${hb.json?.data?.total}(上游无数据属正常)`);
  53. const devs = await httpJson('GET', '/api/devices?page=1&pageSize=15', undefined, auth);
  54. check('设备列表', devs.ok && Array.isArray(devs.json?.data?.items || devs.json?.data), `count=${(devs.json?.data?.items || devs.json?.data || []).length}`);
  55. // 5. 任务/报告
  56. const jobs = await httpJson('GET', '/api/analysis/jobs?pageSize=5', undefined, auth);
  57. const list = jobs.json?.data?.items ?? jobs.json?.data ?? [];
  58. const running = list.filter((j) => ['pending', 'downloading', 'filtering', 'transcribing', 'reporting'].includes(j.status));
  59. check('无运行中任务(互斥空闲)', running.length === 0, `running=${running.length}`);
  60. console.log(`\n== 结果: ${pass} PASS / ${failCount} FAIL ==`);
  61. process.exit(failCount > 0 ? 1 : 0);