Files

104 lines
2.8 KiB
JavaScript

import net from "node:net";
import { spawn, spawnSync } from "node:child_process";
import { envForDetachedSpawn } from "./core.js";
const encoded = process.env.DSH_PURGE_RESTART;
if (!encoded) process.exit(1);
let info;
try {
info = JSON.parse(encoded);
} catch {
process.exit(1);
}
const port = Number(info.port) || 3080;
const host = typeof info.host === "string" && info.host ? info.host : "127.0.0.1";
function portFree() {
return new Promise((resolve) => {
const socket = net.createConnection({ host, port }, () => {
socket.destroy();
resolve(false);
});
socket.on("error", () => resolve(true));
});
}
function killOldServer() {
const pid = Number(info.pid);
if (!pid || pid === process.pid) return;
if (process.platform === "win32") {
spawnSync("taskkill", ["/PID", String(pid), "/F"], { windowsHide: true, stdio: "ignore" });
return;
}
try { process.kill(pid, "SIGTERM"); } catch { /* already gone */ }
}
function openBrowser(url) {
if (!url) return;
if (process.platform === "win32") {
spawn("cmd.exe", ["/d", "/s", "/c", "start", "", url], {
detached: true,
stdio: "ignore",
windowsHide: true,
}).unref();
return;
}
const opener = process.platform === "darwin" ? "open" : "xdg-open";
spawn(opener, [url], { detached: true, stdio: "ignore" }).unref();
}
async function waitFree() {
for (let i = 0; i < 40; i++) {
if (await portFree()) return true;
if (i === 8) killOldServer();
await new Promise((r) => setTimeout(r, 120));
}
killOldServer();
for (let i = 0; i < 40; i++) {
if (await portFree()) return true;
await new Promise((r) => setTimeout(r, 120));
}
return false;
}
const ok = await waitFree();
if (!ok) process.exit(1);
// 接着 stdout 里的新地址打开浏览器。旧页面的 token 在新进程里无效,只刷新会打不开。
let opened = false;
const child = spawn(info.execPath, info.argv, {
detached: true,
stdio: ["ignore", "pipe", "pipe"],
cwd: info.cwd || process.cwd(),
env: envForDetachedSpawn(),
windowsHide: true,
shell: false,
});
child.on("error", () => process.exit(1));
const wantBrowser = info.openBrowser !== false;
const takeUrl = (chunk) => {
const text = String(chunk || "");
if (opened) return;
const match = text.match(/dsh web:\s+(https?:\/\/\S+)/);
if (!match) return;
opened = true;
if (!wantBrowser) return;
const url = match[1].replace(/[),.;]+$/, "");
openBrowser(url);
};
child.stdout?.on("data", takeUrl);
child.stderr?.on("data", takeUrl);
child.unref();
if (wantBrowser) {
const started = Date.now();
while (!opened && Date.now() - started < 20000) {
await new Promise((r) => setTimeout(r, 200));
}
} else {
// 等子进程真正起来再退出,避免端口未监听时父链误判失败又拉一把。
await new Promise((r) => setTimeout(r, 1500));
}
process.exit(0);