#!/usr/bin/env node /** * Diagnose local Plaza setup and print fix hints. */ import fs from 'node:fs'; import net from 'node:net'; import dns from 'node:dns'; import { promisify } from 'node:util'; import https from 'node:https'; const host = process.env.PLAZA_LOCAL_HOST ?? 'plaza.tkmind.cn'; const dnsPort = Number(process.env.PLAZA_LOCAL_DNS_PORT ?? 5533); const plazaPort = Number(process.env.PLAZA_PORT ?? 3001); const portalPort = Number(process.env.H5_PORT ?? 8081); const lookup = promisify(dns.lookup); const resolve4 = promisify(dns.resolve4); function tcpOpen(port, bind = '127.0.0.1') { return new Promise((resolve) => { const s = net.connect(port, bind, () => { s.end(); resolve(true); }); s.on('error', () => resolve(false)); s.setTimeout(2000, () => { s.destroy(); resolve(false); }); }); } function httpsPlaza() { return new Promise((resolve) => { const req = https.request( { hostname: host, port: 443, path: '/plaza', rejectUnauthorized: false, timeout: 3000, }, (res) => resolve({ ok: res.statusCode === 200, code: res.statusCode }), ); req.on('error', (err) => resolve({ ok: false, error: err.message })); req.on('timeout', () => { req.destroy(); resolve({ ok: false, error: 'timeout' }); }); req.end(); }); } const issues = []; const ok = []; // hosts try { const hosts = fs.readFileSync('/etc/hosts', 'utf8'); if (new RegExp(`127\\.0\\.0\\.1\\s+${host.replace('.', '\\.')}`).test(hosts)) ok.push('hosts 已指向 127.0.0.1'); else issues.push(`执行:sudo pnpm setup:plaza-dns`); } catch { issues.push('无法读取 /etc/hosts'); } // resolver try { const body = fs.readFileSync(`/etc/resolver/${host}`, 'utf8'); if (body.includes(`port ${dnsPort}`)) ok.push(`resolver -> 127.0.0.1:${dnsPort}`); else issues.push(`resolver 端口不对,执行:sudo pnpm setup:plaza-dns`); } catch { issues.push(`缺少 /etc/resolver/${host},执行:sudo pnpm setup:plaza-dns`); } // services if (await tcpOpen(dnsPort)) ok.push(`本地 DNS :${dnsPort} 运行中`); else issues.push(`本地 DNS 未运行,执行:pnpm dev:plaza-dns(或 pnpm dev:plaza)`); if (await tcpOpen(plazaPort)) ok.push(`Plaza Next.js :${plazaPort} 运行中`); else issues.push(`Plaza 未运行,执行:pnpm dev:plaza`); if (await tcpOpen(portalPort)) ok.push(`Portal :${portalPort} 运行中`); else issues.push(`Portal 未运行(dev:plaza 会自动启动)`); if (await tcpOpen(443)) ok.push('HTTPS 代理 :443 运行中'); else issues.push('HTTPS 代理未运行,执行:sudo pnpm dev:plaza-proxy'); // DNS resolution const looked = await lookup(host, { all: true }); const addrs = looked.map((x) => x.address); if (addrs.every((a) => a === '127.0.0.1' || a.startsWith('::ffff:127.'))) { ok.push(`系统 DNS lookup -> ${addrs.join(', ')}`); } else { issues.push(`系统 DNS 仍含非本地地址:${addrs.join(', ')}(Tailscale fake-ip)`); } try { const r4 = await resolve4(host); if (r4.some((a) => a !== '127.0.0.1')) { issues.push(`resolve4 -> ${r4.join(', ')}(浏览器 Secure DNS 可能走线上 Cloudflare)`); } } catch { // ignore } const httpsResult = await httpsPlaza(); if (httpsResult.ok) ok.push(`终端 HTTPS ${host}/plaza -> ${httpsResult.code}`); else issues.push(`终端 HTTPS 失败:${httpsResult.error ?? httpsResult.code}`); console.log('\n=== Plaza 本地诊断 ===\n'); for (const line of ok) console.log(`✓ ${line}`); for (const line of issues) console.log(`✗ ${line}`); console.log('\n--- 浏览器仍失败?---'); console.log('Chrome/Cursor 内置浏览器默认走「安全 DNS」,会绕过 /etc/hosts 连到线上 Cloudflare。'); console.log(''); console.log('解决:'); console.log(' 1. pnpm open:plaza # 用 Chrome 强制解析到本机'); console.log(' 2. 或关闭浏览器「安全 DNS / Secure DNS」后刷新'); console.log(' 3. 或临时访问 http://127.0.0.1:3001/plaza'); console.log(''); process.exit(issues.length ? 1 : 0);