import net from "node:net"; import { spawn } from "node:child_process"; import { fileURLToPath } from "node:url"; import { dirname, resolve } from "node:path"; const webRoot = resolve(dirname(fileURLToPath(import.meta.url)), ".."); const apiRoot = resolve(webRoot, "..", "API"); const apiHost = "127.0.0.1"; const apiPort = 8123; const viteBin = resolve(webRoot, "node_modules", "vite", "bin", "vite.js"); let apiProcess; let viteProcess; let stopping = false; function portOpen() { return new Promise(resolveResult => { const socket = net.createConnection({ host: apiHost, port: apiPort }); const finish = open => { socket.destroy(); resolveResult(open); }; socket.once("connect", () => finish(true)); socket.once("error", () => finish(false)); socket.setTimeout(500, () => finish(false)); }); } async function waitForApi(timeoutMs = 120000) { const deadline = Date.now() + timeoutMs; while (Date.now() < deadline) { if (await portOpen()) return true; if (apiProcess?.exitCode != null) return false; await new Promise(resolveResult => setTimeout(resolveResult, 250)); } return false; } function stop(code = 0) { if (stopping) return; stopping = true; viteProcess?.kill(); apiProcess?.kill(); process.exit(code); } process.once("SIGINT", () => stop(0)); process.once("SIGTERM", () => stop(0)); if (!(await portOpen())) { apiProcess = spawn("go", ["run", "./cmd/api"], { cwd: apiRoot, env: { ...process.env, SERVER_ADDRESS: `:${apiPort}` }, stdio: "inherit", windowsHide: false, }); apiProcess.once("error", error => { console.error(`启动 API 失败:${error.message}`); stop(1); }); } if (!(await waitForApi())) { console.error(`API 未能在规定时间内监听 ${apiHost}:${apiPort}`); stop(1); } viteProcess = spawn(process.execPath, [viteBin, ...process.argv.slice(2)], { cwd: webRoot, stdio: "inherit", windowsHide: false, }); viteProcess.once("error", error => { console.error(`启动 Vite 失败:${error.message}`); stop(1); }); viteProcess.once("exit", code => stop(code ?? 0)); apiProcess?.once("exit", code => { if (!stopping) { console.error("API 进程已退出,停止 Vite。"); stop(code || 1); } });