api.mjs 3.0 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172
  1. #!/usr/bin/env node
  2. // 智能工牌录音管道 API 工具(零依赖, Node >=18)
  3. // 用法:
  4. // node api.mjs login [<密码>] # 登录并缓存 token 到 scripts/.token(密码可取自 .env/环境变量)
  5. // node api.mjs get /api/archive/missing
  6. // node api.mjs post /api/archive/run '{"dates":["2026-09-03"]}'
  7. // node api.mjs env # 查看连接诊断(BASE / 是否已登录)
  8. // node api.mjs get /api/reports --raw # 输出完整响应(含 code/message)
  9. // node api.mjs get /api/reports --base http://localhost:3002 # 指定接口地址
  10. // 环境变量: SMARTBADGE_API_BASE(默认线上 https://recording.sh-lami.com)、
  11. // SMARTBADGE_USERNAME、SMARTBADGE_PASSWORD(或参数传入;均无内置默认值,见 .env.example)
  12. import { baseUrl, fail, login, httpJson, loadToken, TOKEN_FILE, findEnvFile, loadEnv } from './lib.mjs';
  13. // .env 密码回退(与 check-online 同链): 已有环境变量优先,不覆盖
  14. const env = findEnvFile();
  15. if (env.hit) loadEnv(env.hit);
  16. const args = process.argv.slice(2);
  17. const positional = [];
  18. let baseOverride;
  19. let rawFlag = false;
  20. for (let i = 0; i < args.length; i++) {
  21. const a = args[i];
  22. if (a === '--raw') rawFlag = true;
  23. else if (a === '--base') baseOverride = args[++i];
  24. else positional.push(a);
  25. }
  26. const [cmd, apiPath, bodyStr] = positional;
  27. async function main() {
  28. if (cmd === 'login') {
  29. const token = await login({
  30. base: baseUrl(baseOverride),
  31. username: process.env.SMARTBADGE_USERNAME,
  32. password: process.env.SMARTBADGE_PASSWORD || apiPath,
  33. });
  34. console.log(`登录成功, token 已缓存(${token.slice(0, 8)}...)`);
  35. return;
  36. }
  37. if (cmd === 'env') {
  38. const token = loadToken();
  39. console.log(`BASE = ${baseUrl(baseOverride)}`);
  40. console.log(`USERNAME= ${process.env.SMARTBADGE_USERNAME || '<未配置: 设置环境变量或 .env>'}`);
  41. console.log(`TOKEN = ${token ? `已登录 (${token.slice(0, 8)}... @ ${TOKEN_FILE})` : '未登录(先: node api.mjs login <密码>)'}`);
  42. return;
  43. }
  44. if (!cmd || !apiPath) fail('用法: node api.mjs login|env|get|post|put|delete <路径> [JSON体] [--raw] [--base URL]');
  45. const body = bodyStr !== undefined && bodyStr.trim() ? JSON.parse(bodyStr) : undefined;
  46. const r = await httpJson(cmd, apiPath, body, { base: baseOverride, raw: rawFlag });
  47. if (r.status >= 400 && r.json?.code !== 0 && !rawFlag) {
  48. fail(`请求失败(${r.status}): ${JSON.stringify(r.json)}`);
  49. }
  50. if (rawFlag) {
  51. console.log(JSON.stringify(r.json, null, 2));
  52. } else {
  53. const data = r.json?.data;
  54. console.log(data === undefined || data === null
  55. ? `(${r.status}) ${r.json?.message || 'no data'}`
  56. : JSON.stringify(data, null, 2));
  57. }
  58. if (!r.ok && process.env.SMARTBADGE_DEBUG) fail(`HTTP ${r.status}`, 1);
  59. }
  60. main().catch((err) => {
  61. if (err instanceof SyntaxError && /JSON/i.test(err.message)) {
  62. fail(`JSON 体解析失败: ${err.message}\n 示例: node api.mjs post /api/archive/run '{"dates":["2026-09-03"]}'`);
  63. }
  64. fail(err.message);
  65. });