/** * run-api-check.js —— 不依赖 Playwright,直接用 Node.js httpx 验证 API 端点 * 用法: node run-api-check.js */ const http = (() => { const base = process.env.BASE_URL || 'http://127.0.0.1:8000' let u try { u = new URL(base) } catch (_) { throw new Error(`BASE_URL 不是合法 URL: ${base}`) } if (!['127.0.0.1', 'localhost', '::1'].includes(u.hostname)) { throw new Error(`BASE_URL 仅允许本机回环地址(当前: ${u.hostname}),防 SSRF`) } return base.replace(/\/+$/, '') })() async function check(method, path, body, label) { try { const opts = { method, headers: { 'Content-Type': 'application/json' } } if (body) opts.body = JSON.stringify(body) const res = await fetch(`${http}${path}`, opts) const text = await res.text() let data try { data = JSON.parse(text) } catch (_) { data = text } const ok = res.status >= 200 && res.status < 300 const icon = ok ? '✅' : '❌' console.log(`${icon} ${method} ${path} → ${res.status} (${label})`) if (!ok) { console.log(` 内容: ${text.slice(0, 200)}`) } return { ok, status: res.status, data } } catch (err) { console.log(`❌ ${method} ${path} → 网络错误: ${err.message}`) return { ok: false, status: 0, data: null } } } async function main() { console.log(`\n=== 端云协同 LLM 系统 API 验证 ===`) console.log(`Base URL: ${http}\n`) const results = [] // 1. 健康检查 results.push(await check('GET', '/health', null, '系统健康')) // 2. 异步 /chat const chat = await check('POST', '/chat', { query: '用Python写快速排序' }, '异步提交') const rid = chat.data?.request_id if (rid) { console.log(` request_id: ${rid}`) // 3. 轮询状态 let retries = 0 while (retries < 30) { await new Promise((r) => setTimeout(r, 300)) const s = await check('GET', `/runs/${rid}/status`, null, `状态轮询[${retries}]`) if (s.data?.status === 'done' || s.data?.status === 'failed') break retries++ } } // 4. GET /runs/{id}/status (不存在ID) results.push(await check('GET', '/runs/notexist/status', null, '404 不存在任务')) // 5. /metrics results.push(await check('GET', '/metrics', null, '系统指标')) // 6. /review/queue results.push(await check('GET', '/review/queue', null, '检验队列')) // 7. /config results.push(await check('GET', '/config', null, '当前配置')) // 8. /chat/legacy (v1) results.push(await check('POST', '/chat/legacy', { query: 'Python快速排序' }, 'v1 legacy')) // 9. /static/index.html const staticRes = await fetch(`${http}/static/index.html`) const staticOk = staticRes.status === 200 console.log(`${staticOk ? '✅' : '❌'} GET /static/index.html → ${staticRes.status} (Vue SPA)`) results.push({ ok: staticOk }) // 10. /static/assets/ 检查 const staticRes2 = await fetch(`${http}/static/assets/`) const staticOk2 = staticRes2.status >= 200 console.log(`${staticOk2 ? '✅' : '❌'} GET /static/assets/ → ${staticRes2.status} (静态资源目录)`) results.push({ ok: staticOk2 }) // 总结 const passed = results.filter((r) => r.ok).length const total = results.length console.log(`\n=== 结果:${passed}/${total} 通过 ===`) process.exit(passed === total ? 0 : 1) } main().catch(console.error)