mirror of
https://github.com/Nezumi-2711/9router.git
synced 2026-09-22 13:38:31 +00:00
MITM Warning
This commit is contained in:
+7
-2
@@ -698,6 +698,9 @@ function startServer(latestVersion) {
|
||||
// old NSStatusItem released before a new tray process can register;
|
||||
// otherwise the bgProcess tray silently fails ("works sometimes").
|
||||
try { await require("./src/cli/tray/tray").killTray(); } catch (e) { }
|
||||
// Extra delay so macOS NSStatusBar fully removes the old icon before
|
||||
// bgProcess spawns a new one. Without this, two icons appear briefly.
|
||||
await new Promise((r) => setTimeout(r, 400));
|
||||
|
||||
// Enable auto startup on OS boot
|
||||
try {
|
||||
@@ -731,8 +734,10 @@ function startServer(latestVersion) {
|
||||
console.log(` • Open Dashboard`);
|
||||
console.log(` • Quit\n`);
|
||||
|
||||
// Exit current process - background process takes over
|
||||
cleanup();
|
||||
// Exit current process - background process takes over.
|
||||
// Don't call cleanup() here: tray already killed above, and cleanup()
|
||||
// would kill the server which bgProcess relies on staying alive.
|
||||
isShuttingDown = true;
|
||||
process.exit(0);
|
||||
} else if (choice === "exit") {
|
||||
isShuttingDown = true;
|
||||
|
||||
+37
-12
@@ -60,19 +60,45 @@ function isBetterSqliteBinaryValid() {
|
||||
} catch { return false; }
|
||||
}
|
||||
|
||||
function npmInstall(pkgs, opts = {}) {
|
||||
const cwd = ensureRuntimeDir();
|
||||
const args = ["install", ...pkgs, "--no-audit", "--no-fund", "--prefer-online"];
|
||||
if (opts.optional) args.push("--no-save");
|
||||
// Extract a short, user-friendly reason from npm stderr.
|
||||
function summarizeNpmError(stderr = "") {
|
||||
const text = String(stderr);
|
||||
if (/ENOTFOUND|ETIMEDOUT|EAI_AGAIN|network|getaddrinfo/i.test(text)) return "No internet connection or registry unreachable";
|
||||
if (/EACCES|EPERM|permission denied/i.test(text)) return "Permission denied (check folder permissions)";
|
||||
if (/ENOSPC|no space/i.test(text)) return "Not enough disk space";
|
||||
if (/node-gyp|gyp ERR|python|MSBuild|Visual Studio|Xcode/i.test(text)) return "Missing build tools (Xcode CLT / Python / VS Build Tools)";
|
||||
if (/ETARGET|version.*not found/i.test(text)) return "Package version not found on registry";
|
||||
const m = text.match(/npm ERR! (.+)/);
|
||||
if (m) return m[1].slice(0, 200);
|
||||
const lastLine = text.trim().split(/\r?\n/).filter(Boolean).pop();
|
||||
return lastLine ? lastLine.slice(0, 200) : "Unknown error";
|
||||
}
|
||||
|
||||
function runNpmInstall({ cwd, pkgs, extraArgs = [], timeout = 180000 }) {
|
||||
const args = ["install", ...pkgs, "--no-audit", "--no-fund", "--prefer-online", ...extraArgs];
|
||||
const npmCmd = process.platform === "win32" ? "npm.cmd" : "npm";
|
||||
console.log(`[9router][runtime] ${npmCmd} ${args.join(" ")} (cwd: ${cwd})`);
|
||||
const res = spawnSync(npmCmd, args, {
|
||||
cwd,
|
||||
stdio: opts.silent ? "ignore" : "inherit",
|
||||
timeout: opts.timeout || 180000,
|
||||
stdio: ["ignore", "pipe", "pipe"],
|
||||
timeout,
|
||||
shell: process.platform === "win32",
|
||||
encoding: "utf8",
|
||||
});
|
||||
return res.status === 0;
|
||||
return { ok: res.status === 0, code: res.status, stderr: res.stderr || "", stdout: res.stdout || "" };
|
||||
}
|
||||
|
||||
function npmInstall(pkgs, opts = {}) {
|
||||
const cwd = ensureRuntimeDir();
|
||||
const extra = opts.optional ? ["--no-save"] : [];
|
||||
if (!opts.silent) console.log("⏳ Installing SQLite engine (first run)...");
|
||||
const res = runNpmInstall({ cwd, pkgs, extraArgs: extra, timeout: opts.timeout || 180000 });
|
||||
if (!res.ok && !opts.silent) {
|
||||
const reason = summarizeNpmError(res.stderr);
|
||||
console.warn("⚠️ SQLite engine install failed — using fallback");
|
||||
console.warn(` Reason: ${reason}`);
|
||||
console.warn(` Retry: cd "${cwd}" && npm install ${pkgs.join(" ")}`);
|
||||
}
|
||||
return res.ok;
|
||||
}
|
||||
|
||||
// Public: ensure better-sqlite3 native module is installed in user-writable
|
||||
@@ -83,14 +109,11 @@ function ensureSqliteRuntime({ silent = false } = {}) {
|
||||
|
||||
const needBetterSqlite = !hasModule("better-sqlite3") || !isBetterSqliteBinaryValid();
|
||||
if (!needBetterSqlite) {
|
||||
if (!silent) console.log("[9router][runtime] better-sqlite3 OK");
|
||||
if (!silent) console.log("✅ SQLite engine ready");
|
||||
return { betterSqlite: true };
|
||||
}
|
||||
|
||||
const ok = npmInstall([`better-sqlite3@${BETTER_SQLITE3_VERSION}`], { optional: true, silent });
|
||||
if (!ok && !silent) {
|
||||
console.warn("[9router][runtime] better-sqlite3 install failed (will use node:sqlite or sql.js fallback)");
|
||||
}
|
||||
return {
|
||||
betterSqlite: ok && hasModule("better-sqlite3") && isBetterSqliteBinaryValid(),
|
||||
};
|
||||
@@ -111,4 +134,6 @@ module.exports = {
|
||||
buildEnvWithRuntime,
|
||||
getRuntimeDir,
|
||||
getRuntimeNodeModules,
|
||||
runNpmInstall,
|
||||
summarizeNpmError,
|
||||
};
|
||||
|
||||
+11
-15
@@ -9,7 +9,7 @@
|
||||
const { spawnSync } = require("child_process");
|
||||
const fs = require("fs");
|
||||
const path = require("path");
|
||||
const { getRuntimeDir, getRuntimeNodeModules } = require("./sqliteRuntime");
|
||||
const { getRuntimeDir, getRuntimeNodeModules, runNpmInstall, summarizeNpmError } = require("./sqliteRuntime");
|
||||
|
||||
const SYSTRAY_PKG = "systray2";
|
||||
const SYSTRAY_VERSION = "2.1.4";
|
||||
@@ -73,16 +73,15 @@ function ensureRuntimeDir() {
|
||||
|
||||
function npmInstall(pkgs, { silent = false } = {}) {
|
||||
const cwd = ensureRuntimeDir();
|
||||
const args = ["install", ...pkgs, "--no-audit", "--no-fund", "--no-save", "--prefer-online"];
|
||||
const npmCmd = process.platform === "win32" ? "npm.cmd" : "npm";
|
||||
if (!silent) console.log(`[9router][runtime] ${npmCmd} ${args.join(" ")} (cwd: ${cwd})`);
|
||||
const res = spawnSync(npmCmd, args, {
|
||||
cwd,
|
||||
stdio: silent ? "ignore" : "inherit",
|
||||
timeout: 120000,
|
||||
shell: process.platform === "win32"
|
||||
});
|
||||
return res.status === 0;
|
||||
if (!silent) console.log("⏳ Installing system tray (first run)...");
|
||||
const res = runNpmInstall({ cwd, pkgs, extraArgs: ["--no-save"], timeout: 120000 });
|
||||
if (!res.ok && !silent) {
|
||||
const reason = summarizeNpmError(res.stderr);
|
||||
console.warn("⚠️ System tray install failed — tray disabled");
|
||||
console.warn(` Reason: ${reason}`);
|
||||
console.warn(` Retry: cd "${cwd}" && npm install ${pkgs.join(" ")}`);
|
||||
}
|
||||
return res.ok;
|
||||
}
|
||||
|
||||
// Public: ensure systray2 is installed on macOS/Linux only.
|
||||
@@ -97,14 +96,11 @@ function ensureTrayRuntime({ silent = false } = {}) {
|
||||
}
|
||||
if (hasSystray()) {
|
||||
chmodSystrayBin({ silent });
|
||||
if (!silent) console.log("[9router][runtime] systray2 OK");
|
||||
if (!silent) console.log("✅ System tray ready");
|
||||
return { systray: true };
|
||||
}
|
||||
const ok = npmInstall([`${SYSTRAY_PKG}@${SYSTRAY_VERSION}`], { silent });
|
||||
if (ok) chmodSystrayBin({ silent });
|
||||
if (!ok && !silent) {
|
||||
console.warn("[9router][runtime] systray2 install failed (tray will be disabled)");
|
||||
}
|
||||
return { systray: ok && hasSystray() };
|
||||
}
|
||||
|
||||
|
||||
@@ -260,16 +260,20 @@ function killTray() {
|
||||
return Promise.resolve();
|
||||
}
|
||||
|
||||
// Unix: get the Go tray child process handle, SIGKILL it, await "exit"
|
||||
// Unix: get the Go tray child process handle.
|
||||
let proc = null;
|
||||
try {
|
||||
proc = instance._process || (typeof instance.process === "function" ? instance.process() : null);
|
||||
} catch (e) {}
|
||||
|
||||
// Always close IPC (best-effort, may throw if pipe already broken)
|
||||
// Graceful shutdown: send {type:"exit"} via IPC so the Go binary can call
|
||||
// systray.Quit() and release NSStatusItem. SIGKILL leaves a ghost icon on
|
||||
// the macOS menubar until logout, causing duplicate icons after re-spawn.
|
||||
const gracefulQuit = () => { try { instance.kill(true); } catch (e) {} };
|
||||
const closeIpc = () => { try { instance.kill(false); } catch (e) {} };
|
||||
|
||||
if (!proc || !proc.pid) {
|
||||
gracefulQuit();
|
||||
closeIpc();
|
||||
return Promise.resolve();
|
||||
}
|
||||
@@ -279,7 +283,11 @@ function killTray() {
|
||||
const finish = () => { if (done) return; done = true; closeIpc(); resolve(); };
|
||||
|
||||
proc.once("exit", finish);
|
||||
try { proc.kill("SIGKILL"); } catch (e) {}
|
||||
gracefulQuit();
|
||||
|
||||
// Escalate: SIGTERM after 800ms, SIGKILL after 1600ms if still alive.
|
||||
setTimeout(() => { try { process.kill(proc.pid, 0); proc.kill("SIGTERM"); } catch (e) {} }, 800);
|
||||
setTimeout(() => { try { process.kill(proc.pid, 0); proc.kill("SIGKILL"); } catch (e) {} }, 1600);
|
||||
|
||||
// Fallback poll in case "exit" never fires (detached child, pipe closed)
|
||||
const deadline = Date.now() + 3000;
|
||||
|
||||
Reference in New Issue
Block a user