| 123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183 |
- #!/usr/bin/env node
- /**
- * VOC + TikHub 接口连通性测试
- *
- * 测试维度:
- * - 可达性(TCP/TLS/HTTP 响应)
- * - 延迟(ms)
- * - 认证链路(无/有 token 对比)
- * - 响应格式(JSON 解析)
- *
- * 用法:
- * node scripts/tools/test-connectivity.js
- * node scripts/tools/test-connectivity.js --token=r:xxx # 覆盖 voc-credentials.json
- * node scripts/tools/test-connectivity.js --tikhub=tk:xxx # 额外测试 TikHub 直连
- */
- const fs = require('fs');
- const os = require('os');
- const path = require('path');
- // ─── 解析参数 ───
- const args = process.argv.slice(2);
- const argToken = (args.find(a => a.startsWith('--token=')) || '').slice(8);
- const tikhubToken = (args.find(a => a.startsWith('--tikhub=')) || '').slice(9);
- // ─── 从 voc-credentials.json 读 token 兜底 ───
- let sessionToken = argToken;
- if (!sessionToken) {
- const cfg = path.join(os.homedir(), '.openclaw', 'voc-credentials.json');
- if (fs.existsSync(cfg)) {
- try {
- const data = JSON.parse(fs.readFileSync(cfg, 'utf-8'));
- sessionToken = data.vocToken || '';
- } catch (_) {}
- }
- }
- const APP_ID = 'ncloudmaster';
- const APIG_ID = 'Vo3ROWEvDy';
- const VOC_BASE = 'https://server.fmode.cn';
- const TIKHUB_BASE = 'https://api.tikhub.io';
- // ─── 单次测试执行器 ───
- async function probe(label, url, opts = {}) {
- const t0 = Date.now();
- try {
- const ctrl = new AbortController();
- const timer = setTimeout(() => ctrl.abort(), opts.timeoutMs || 8000);
- const resp = await fetch(url, { ...opts, signal: ctrl.signal });
- clearTimeout(timer);
- const ms = Date.now() - t0;
- const txt = await resp.text();
- let json = null;
- try { json = JSON.parse(txt); } catch (_) {}
- return {
- label, url, ok: resp.ok, status: resp.status, ms,
- body: json || txt.slice(0, 200),
- error: null
- };
- } catch (e) {
- return {
- label, url, ok: false, status: 0, ms: Date.now() - t0,
- body: null, error: e.message
- };
- }
- }
- function printResult(r) {
- const statusColor = r.ok ? '\x1b[32m' : (r.status > 0 ? '\x1b[33m' : '\x1b[31m');
- const statusTxt = r.ok ? `✅ ${r.status}` : (r.status > 0 ? `⚠️ ${r.status}` : `❌ 无响应`);
- console.log(`${statusColor}${statusTxt}\x1b[0m ${r.ms}ms ${r.label}`);
- console.log(` ${r.url}`);
- if (r.error) console.log(` 错误: ${r.error}`);
- else if (typeof r.body === 'object' && r.body !== null) {
- const s = JSON.stringify(r.body);
- console.log(` 响应: ${s.slice(0, 180)}${s.length > 180 ? '...' : ''}`);
- } else if (r.body) {
- console.log(` 响应: ${String(r.body).slice(0, 180)}`);
- }
- console.log('');
- }
- // ─── 主流程 ───
- (async () => {
- console.log('═══════════════════════════════════════════════════════════════');
- console.log(' VOC + TikHub 接口连通性测试');
- console.log('═══════════════════════════════════════════════════════════════');
- console.log(` VOC: ${VOC_BASE}`);
- console.log(` TikHub: ${TIKHUB_BASE}`);
- console.log(` APIG_ID: ${APIG_ID}`);
- console.log(` Session token: ${sessionToken ? sessionToken.slice(0, 6) + '...' + sessionToken.slice(-4) : '(无)'}`);
- console.log(` TikHub token: ${tikhubToken ? tikhubToken.slice(0, 6) + '...' + tikhubToken.slice(-4) : '(未提供,跳过直连测试)'}`);
- console.log('');
- const results = [];
- // ─── Section 1: VOC 基础可达性 ───
- console.log('── [1/3] VOC 基础可达性 ──');
- results.push(await probe(
- 'Parse health',
- `${VOC_BASE}/parse/health`,
- { headers: { 'X-Parse-Application-Id': APP_ID } }
- ));
- results.push(await probe(
- 'Parse REST: 读取 APIG 记录',
- `${VOC_BASE}/parse/classes/APIG/${APIG_ID}`,
- { headers: { 'X-Parse-Application-Id': APP_ID } }
- ));
- results.push(await probe(
- 'APIG getApig (无 token)',
- `${VOC_BASE}/api/apig/getApig`,
- {
- method: 'POST',
- headers: { 'Content-Type': 'application/json' },
- body: JSON.stringify({ authid: '' })
- }
- ));
- results.slice(-3).forEach(printResult);
- // ─── Section 2: VOC 鉴权链路(带 session token) ───
- console.log('── [2/3] VOC 鉴权链路 ──');
- if (sessionToken) {
- results.push(await probe(
- 'Parse /users/me (用当前 token)',
- `${VOC_BASE}/parse/users/me?include=company`,
- { headers: { 'X-Parse-Application-Id': APP_ID, 'X-Parse-Session-Token': sessionToken } }
- ));
- printResult(results[results.length - 1]);
- } else {
- console.log(' (跳过,无 session token)\n');
- }
- // ─── Section 3: TikHub 代理链路(通过 VOC) ───
- console.log('── [3/3] TikHub 代理链路(通过 VOC) ──');
- // Instagram 代理(无 token)
- results.push(await probe(
- 'VOC→TikHub: Instagram search (无 token)',
- `${VOC_BASE}/api/voc-social/instagram/v1/fetch_search?query=nike&select=users`,
- { method: 'GET' }
- ));
- printResult(results[results.length - 1]);
- // TikTok 代理(无 token)
- results.push(await probe(
- 'VOC→TikHub: TikTok video search (无 token)',
- `${VOC_BASE}/api/voc-social/tiktok/app/v3/fetch_video_search_result?keyword=nike&count=1&cursor=0`,
- { method: 'GET' }
- ));
- printResult(results[results.length - 1]);
- // TikHub 直连测试(如果提供了 TikHub token)
- if (tikhubToken) {
- console.log('── [额外] TikHub 直连测试 ──');
- results.push(await probe(
- 'TikHub 直连: Instagram search',
- `${TIKHUB_BASE}/api/v1/instagram/web_app/fetch_search_users_v1?keyword=nike&count=1`,
- { headers: { Authorization: `Bearer ${tikhubToken}` } }
- ));
- printResult(results[results.length - 1]);
- }
- // ─── 汇总报告 ───
- console.log('═══════════════════════════════════════════════════════════════');
- console.log(' 汇总');
- console.log('═══════════════════════════════════════════════════════════════');
- const pass = results.filter(r => r.ok).length;
- const softFail = results.filter(r => !r.ok && r.status > 0).length;
- const hardFail = results.filter(r => r.status === 0).length;
- console.log(` ✅ 可达 + 2xx: ${pass}`);
- console.log(` ⚠️ 可达但非 2xx: ${softFail} (通常是鉴权/参数问题,不是连通性问题)`);
- console.log(` ❌ 完全不可达: ${hardFail}`);
- console.log('');
- const avgMs = Math.round(results.filter(r => r.ms > 0).reduce((s, r) => s + r.ms, 0) / results.length);
- console.log(` 平均延迟: ${avgMs}ms`);
- console.log('');
- if (hardFail > 0) {
- console.log(' ❌ 存在完全不可达的端点 — 检查网络/DNS/防火墙');
- process.exitCode = 1;
- } else {
- console.log(' ✅ 所有端点 TCP/TLS/HTTP 层面都可达');
- }
- })();
|