#!/usr/bin/env node /** * Map plaza.tkmind.cn -> 127.0.0.1 for local Plaza dev. * Requires sudo to edit /etc/hosts and /etc/resolver. * * macOS + Tailscale/Clash fake-ip: /etc/hosts alone is not enough — browsers may * still resolve 198.18.x.x. Per-domain resolver forces local DNS server. * * Usage: * node scripts/setup-plaza-local-dns.mjs * node scripts/setup-plaza-local-dns.mjs --remove */ import fs from 'node:fs'; import path from 'node:path'; import { execSync } from 'node:child_process'; const HOST = process.env.PLAZA_LOCAL_HOST ?? 'plaza.tkmind.cn'; const IP = process.env.PLAZA_LOCAL_IP ?? '127.0.0.1'; const DNS_PORT = Number(process.env.PLAZA_LOCAL_DNS_PORT ?? 5533); const MARKER = '# tkmind-plaza-local'; const RESOLVER_MARKER = '# tkmind-plaza-local'; const LINE = `${IP} ${HOST} ${MARKER}`; const remove = process.argv.includes('--remove'); const hostsPath = '/etc/hosts'; const resolverDir = '/etc/resolver'; const resolverPath = path.join(resolverDir, HOST); const resolverBody = `nameserver 127.0.0.1\nport ${DNS_PORT}\n${RESOLVER_MARKER}\n`; function flushDnsCache() { try { execSync('dscacheutil -flushcache', { stdio: 'ignore' }); execSync('killall -HUP mDNSResponder', { stdio: 'ignore' }); } catch { // best effort } } function writeResolver() { fs.mkdirSync(resolverDir, { recursive: true }); fs.writeFileSync(resolverPath, resolverBody, 'utf8'); } function removeResolver() { try { if (fs.existsSync(resolverPath)) fs.unlinkSync(resolverPath); } catch { // ignore } } const current = fs.readFileSync(hostsPath, 'utf8'); const lines = current.split('\n'); const filtered = lines.filter( (line) => !line.includes(MARKER) && !line.match(new RegExp(`\\s${HOST.replace('.', '\\.')}(\\s|$)`)), ); if (remove) { const next = filtered.join('\n').replace(/\n?$/, '\n'); if (filtered.length !== lines.length) { execSync(`tee ${hostsPath}`, { input: next, stdio: ['pipe', 'inherit', 'inherit'] }); console.log(`已移除 hosts 中的 ${HOST}`); } else { console.log(`hosts 中未找到 ${HOST},无需移除`); } removeResolver(); flushDnsCache(); console.log('已移除 per-domain resolver'); process.exit(0); } const hostsReady = lines.some( (line) => line.includes(MARKER) || new RegExp(`\\s${HOST.replace('.', '\\.')}(\\s|$)`).test(line), ); const resolverReady = fs.existsSync(resolverPath) && fs.readFileSync(resolverPath, 'utf8').trim() === resolverBody.trim(); if (hostsReady && resolverReady) { console.log(`${HOST} 本地解析已配置(hosts + resolver)`); process.exit(0); } if (process.getuid?.() !== 0) { console.log('需要 root 权限写入 /etc/hosts 和 /etc/resolver,请执行:'); console.log(` sudo node ${process.argv[1]}`); process.exit(1); } if (!hostsReady) { const next = `${filtered.join('\n').replace(/\n?$/, '\n')}${LINE}\n`; try { execSync(`tee ${hostsPath}`, { input: next, stdio: ['pipe', 'inherit', 'inherit'] }); console.log(`已添加 hosts:${LINE}`); } catch { console.error('写入 /etc/hosts 失败,请手动执行:'); console.error(` sudo sh -c 'echo "${LINE}" >> /etc/hosts'`); process.exit(1); } } else { console.log(`${HOST} 已在 /etc/hosts 中指向本地`); } try { writeResolver(); console.log(`已添加 resolver:${resolverPath} -> 127.0.0.1:${DNS_PORT}`); } catch (err) { console.error('写入 /etc/resolver 失败:', err instanceof Error ? err.message : err); process.exit(1); } flushDnsCache(); console.log('已刷新 DNS 缓存'); console.log(''); console.log('下一步:'); console.log(' pnpm dev:plaza # 含本地 DNS + Plaza 服务'); console.log(' sudo pnpm dev:plaza-proxy # HTTPS :443 代理');