2026-06-15 15:04:43 -07:00
|
|
|
|
#!/usr/bin/env node
|
|
|
|
|
|
/**
|
|
|
|
|
|
* Map plaza.tkmind.cn -> 127.0.0.1 for local Plaza dev.
|
2026-06-15 22:09:38 -07:00
|
|
|
|
* 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.
|
2026-06-15 15:04:43 -07:00
|
|
|
|
*
|
|
|
|
|
|
* Usage:
|
|
|
|
|
|
* node scripts/setup-plaza-local-dns.mjs
|
|
|
|
|
|
* node scripts/setup-plaza-local-dns.mjs --remove
|
|
|
|
|
|
*/
|
|
|
|
|
|
import fs from 'node:fs';
|
2026-06-15 22:09:38 -07:00
|
|
|
|
import path from 'node:path';
|
2026-06-15 15:04:43 -07:00
|
|
|
|
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';
|
2026-06-15 22:09:38 -07:00
|
|
|
|
const DNS_PORT = Number(process.env.PLAZA_LOCAL_DNS_PORT ?? 5533);
|
2026-06-15 15:04:43 -07:00
|
|
|
|
const MARKER = '# tkmind-plaza-local';
|
2026-06-15 22:09:38 -07:00
|
|
|
|
const RESOLVER_MARKER = '# tkmind-plaza-local';
|
2026-06-15 15:04:43 -07:00
|
|
|
|
const LINE = `${IP} ${HOST} ${MARKER}`;
|
|
|
|
|
|
const remove = process.argv.includes('--remove');
|
|
|
|
|
|
|
|
|
|
|
|
const hostsPath = '/etc/hosts';
|
2026-06-15 22:09:38 -07:00
|
|
|
|
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
|
|
|
|
|
|
}
|
|
|
|
|
|
}
|
|
|
|
|
|
|
2026-06-15 15:04:43 -07:00
|
|
|
|
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) {
|
2026-06-15 22:09:38 -07:00
|
|
|
|
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 {
|
2026-06-15 15:04:43 -07:00
|
|
|
|
console.log(`hosts 中未找到 ${HOST},无需移除`);
|
|
|
|
|
|
}
|
2026-06-15 22:09:38 -07:00
|
|
|
|
removeResolver();
|
|
|
|
|
|
flushDnsCache();
|
|
|
|
|
|
console.log('已移除 per-domain resolver');
|
2026-06-15 15:04:43 -07:00
|
|
|
|
process.exit(0);
|
|
|
|
|
|
}
|
|
|
|
|
|
|
2026-06-15 22:09:38 -07:00
|
|
|
|
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)`);
|
2026-06-15 15:04:43 -07:00
|
|
|
|
process.exit(0);
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
if (process.getuid?.() !== 0) {
|
2026-06-15 22:09:38 -07:00
|
|
|
|
console.log('需要 root 权限写入 /etc/hosts 和 /etc/resolver,请执行:');
|
2026-06-15 15:04:43 -07:00
|
|
|
|
console.log(` sudo node ${process.argv[1]}`);
|
|
|
|
|
|
process.exit(1);
|
|
|
|
|
|
}
|
2026-06-15 22:09:38 -07:00
|
|
|
|
|
|
|
|
|
|
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 中指向本地`);
|
|
|
|
|
|
}
|
|
|
|
|
|
|
2026-06-15 15:04:43 -07:00
|
|
|
|
try {
|
2026-06-15 22:09:38 -07:00
|
|
|
|
writeResolver();
|
|
|
|
|
|
console.log(`已添加 resolver:${resolverPath} -> 127.0.0.1:${DNS_PORT}`);
|
|
|
|
|
|
} catch (err) {
|
|
|
|
|
|
console.error('写入 /etc/resolver 失败:', err instanceof Error ? err.message : err);
|
2026-06-15 15:04:43 -07:00
|
|
|
|
process.exit(1);
|
|
|
|
|
|
}
|
2026-06-15 22:09:38 -07:00
|
|
|
|
|
|
|
|
|
|
flushDnsCache();
|
|
|
|
|
|
console.log('已刷新 DNS 缓存');
|
|
|
|
|
|
console.log('');
|
|
|
|
|
|
console.log('下一步:');
|
|
|
|
|
|
console.log(' pnpm dev:plaza # 含本地 DNS + Plaza 服务');
|
|
|
|
|
|
console.log(' sudo pnpm dev:plaza-proxy # HTTPS :443 代理');
|