mirror of
https://github.com/decolua/9router.git
synced 2026-05-08 12:01:28 +00:00
fix bug
This commit is contained in:
@@ -1,6 +1,6 @@
|
||||
{
|
||||
"name": "9router-app",
|
||||
"version": "0.2.95",
|
||||
"version": "0.2.98",
|
||||
"description": "9Router web dashboard",
|
||||
"private": true,
|
||||
"scripts": {
|
||||
|
||||
@@ -4,6 +4,8 @@ const { exec } = require("child_process");
|
||||
const { execWithPassword } = require("../dns/dnsConfig.js");
|
||||
|
||||
const IS_WIN = process.platform === "win32";
|
||||
const IS_MAC = process.platform === "darwin";
|
||||
const LINUX_CERT_DIR = "/usr/local/share/ca-certificates";
|
||||
|
||||
// Get SHA1 fingerprint from cert file using Node.js crypto
|
||||
function getCertFingerprint(certPath) {
|
||||
@@ -16,10 +18,9 @@ function getCertFingerprint(certPath) {
|
||||
* Check if certificate is already installed in system store
|
||||
*/
|
||||
async function checkCertInstalled(certPath) {
|
||||
if (IS_WIN) {
|
||||
return checkCertInstalledWindows(certPath);
|
||||
}
|
||||
return checkCertInstalledMac(certPath);
|
||||
if (IS_WIN) return checkCertInstalledWindows(certPath);
|
||||
if (IS_MAC) return checkCertInstalledMac(certPath);
|
||||
return checkCertInstalledLinux();
|
||||
}
|
||||
|
||||
function checkCertInstalledMac(certPath) {
|
||||
@@ -60,8 +61,10 @@ async function installCert(sudoPassword, certPath) {
|
||||
|
||||
if (IS_WIN) {
|
||||
await installCertWindows(certPath);
|
||||
} else {
|
||||
} else if (IS_MAC) {
|
||||
await installCertMac(sudoPassword, certPath);
|
||||
} else {
|
||||
await installCertLinux(sudoPassword, certPath);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -103,8 +106,10 @@ async function uninstallCert(sudoPassword, certPath) {
|
||||
|
||||
if (IS_WIN) {
|
||||
await uninstallCertWindows();
|
||||
} else {
|
||||
} else if (IS_MAC) {
|
||||
await uninstallCertMac(sudoPassword, certPath);
|
||||
} else {
|
||||
await uninstallCertLinux(sudoPassword);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -133,4 +138,32 @@ async function uninstallCertWindows() {
|
||||
});
|
||||
}
|
||||
|
||||
function checkCertInstalledLinux() {
|
||||
const certFile = `${LINUX_CERT_DIR}/9router-mitm.crt`;
|
||||
return Promise.resolve(fs.existsSync(certFile));
|
||||
}
|
||||
|
||||
async function installCertLinux(sudoPassword, certPath) {
|
||||
const destFile = `${LINUX_CERT_DIR}/9router-mitm.crt`;
|
||||
// Try update-ca-certificates (Debian/Ubuntu), fallback to update-ca-trust (Fedora/RHEL)
|
||||
const cmd = `cp "${certPath}" "${destFile}" && (update-ca-certificates 2>/dev/null || update-ca-trust 2>/dev/null || true)`;
|
||||
try {
|
||||
await execWithPassword(cmd, sudoPassword);
|
||||
console.log("✅ Installed certificate to Linux trust store");
|
||||
} catch (error) {
|
||||
throw new Error("Certificate install failed");
|
||||
}
|
||||
}
|
||||
|
||||
async function uninstallCertLinux(sudoPassword) {
|
||||
const destFile = `${LINUX_CERT_DIR}/9router-mitm.crt`;
|
||||
const cmd = `rm -f "${destFile}" && (update-ca-certificates 2>/dev/null || update-ca-trust 2>/dev/null || true)`;
|
||||
try {
|
||||
await execWithPassword(cmd, sudoPassword);
|
||||
console.log("✅ Uninstalled certificate from Linux trust store");
|
||||
} catch (error) {
|
||||
throw new Error("Failed to uninstall certificate");
|
||||
}
|
||||
}
|
||||
|
||||
module.exports = { installCert, uninstallCert, checkCertInstalled };
|
||||
|
||||
@@ -1,9 +1,11 @@
|
||||
const { exec, spawn } = require("child_process");
|
||||
const fs = require("fs");
|
||||
const path = require("path");
|
||||
const os = require("os");
|
||||
|
||||
const TARGET_HOST = "daily-cloudcode-pa.googleapis.com";
|
||||
const IS_WIN = process.platform === "win32";
|
||||
const IS_MAC = process.platform === "darwin";
|
||||
const HOSTS_FILE = IS_WIN
|
||||
? path.join(process.env.SystemRoot || "C:\\Windows", "System32", "drivers", "etc", "hosts")
|
||||
: "/etc/hosts";
|
||||
@@ -81,8 +83,11 @@ async function addDNSEntry(sudoPassword) {
|
||||
// Flush DNS cache
|
||||
if (IS_WIN) {
|
||||
await execElevatedWindows("ipconfig /flushdns");
|
||||
} else {
|
||||
} else if (IS_MAC) {
|
||||
await execWithPassword("dscacheutil -flushcache && killall -HUP mDNSResponder", sudoPassword);
|
||||
} else {
|
||||
// Linux: try systemd-resolved, fall back silently
|
||||
await execWithPassword("resolvectl flush-caches 2>/dev/null || true", sudoPassword);
|
||||
}
|
||||
console.log(`✅ Added DNS entry: ${entry}`);
|
||||
} catch (error) {
|
||||
@@ -102,23 +107,37 @@ async function removeDNSEntry(sudoPassword) {
|
||||
|
||||
try {
|
||||
if (IS_WIN) {
|
||||
// Windows: read, filter, write back via elevated PowerShell
|
||||
const psScript = `(Get-Content '${HOSTS_FILE}') | Where-Object { $_ -notmatch '${TARGET_HOST}' } | Set-Content '${HOSTS_FILE}'`;
|
||||
const psCommand = `Start-Process powershell -ArgumentList '-Command','${psScript.replace(/'/g, "''")}' -Verb RunAs -Wait`;
|
||||
// Read in Node, filter, write to temp file, then elevated-copy over hosts
|
||||
const content = fs.readFileSync(HOSTS_FILE, "utf8");
|
||||
const filtered = content.split(/\r?\n/).filter(l => !l.includes(TARGET_HOST)).join("\r\n");
|
||||
if (!filtered.trim() && content.trim()) {
|
||||
throw new Error("Filtered hosts content is empty, aborting to prevent data loss");
|
||||
}
|
||||
const tmpFile = path.join(os.tmpdir(), "hosts_filtered.tmp");
|
||||
fs.writeFileSync(tmpFile, filtered, "utf8");
|
||||
// Use elevated cmd to copy temp file over hosts (safe: original untouched until copy succeeds)
|
||||
const psCommand = `Start-Process cmd -ArgumentList '/c','copy /Y "${tmpFile}" "${HOSTS_FILE}"' -Verb RunAs -Wait`;
|
||||
await new Promise((resolve, reject) => {
|
||||
exec(`powershell -Command "${psCommand}"`, (error) => {
|
||||
try { fs.unlinkSync(tmpFile); } catch { /* ignore */ }
|
||||
if (error) reject(new Error(`Failed to remove DNS entry: ${error.message}`));
|
||||
else resolve();
|
||||
});
|
||||
});
|
||||
} else {
|
||||
await execWithPassword(`sed -i '' '/${TARGET_HOST}/d' ${HOSTS_FILE}`, sudoPassword);
|
||||
// sed -i '' is macOS syntax; Linux uses sed -i without the empty string arg
|
||||
const sedCmd = IS_MAC
|
||||
? `sed -i '' '/${TARGET_HOST}/d' ${HOSTS_FILE}`
|
||||
: `sed -i '/${TARGET_HOST}/d' ${HOSTS_FILE}`;
|
||||
await execWithPassword(sedCmd, sudoPassword);
|
||||
}
|
||||
// Flush DNS cache
|
||||
if (IS_WIN) {
|
||||
await execElevatedWindows("ipconfig /flushdns");
|
||||
} else {
|
||||
} else if (IS_MAC) {
|
||||
await execWithPassword("dscacheutil -flushcache && killall -HUP mDNSResponder", sudoPassword);
|
||||
} else {
|
||||
await execWithPassword("resolvectl flush-caches 2>/dev/null || true", sudoPassword);
|
||||
}
|
||||
console.log(`✅ Removed DNS entry for ${TARGET_HOST}`);
|
||||
} catch (error) {
|
||||
|
||||
@@ -389,13 +389,14 @@ async function startMitm(apiKey, sudoPassword) {
|
||||
console.log("Starting MITM server...");
|
||||
|
||||
if (IS_WIN) {
|
||||
// Launch elevated node via PowerShell RunAs (triggers UAC prompt)
|
||||
const nodePath = process.execPath.replace(/'/g, "''");
|
||||
const serverPath = SERVER_PATH.replace(/'/g, "''");
|
||||
// Use cmd /c to set env vars inline before launching node (env vars survive RunAs)
|
||||
const nodePath = process.execPath.replace(/"/g, '\\"');
|
||||
const serverPath = SERVER_PATH.replace(/"/g, '\\"');
|
||||
const cmdLine = `set ROUTER_API_KEY=${apiKey}&& set NODE_ENV=production&& "${nodePath}" "${serverPath}"`;
|
||||
serverProcess = spawn("powershell", [
|
||||
"-NoProfile", "-Command",
|
||||
`$env:ROUTER_API_KEY='${apiKey}'; $env:NODE_ENV='production'; Start-Process '${nodePath}' -ArgumentList '''${serverPath}''' -Verb RunAs -WindowStyle Hidden`
|
||||
], { stdio: "ignore", env: process.env });
|
||||
`Start-Process cmd -ArgumentList '/c','${cmdLine.replace(/'/g, "''")}' -Verb RunAs -WindowStyle Hidden`
|
||||
], { stdio: "ignore" });
|
||||
} else {
|
||||
// sudo -S: read password from stdin, -E: preserve env vars
|
||||
// Pass ROUTER_API_KEY inline via env=... wrapper to avoid sudo stripping env
|
||||
@@ -413,23 +414,25 @@ async function startMitm(apiKey, sudoPassword) {
|
||||
fs.writeFileSync(PID_FILE, String(serverPid));
|
||||
|
||||
let startError = null;
|
||||
serverProcess.stdout.on("data", (data) => {
|
||||
console.log(`[MITM Server] ${data.toString().trim()}`);
|
||||
});
|
||||
serverProcess.stderr.on("data", (data) => {
|
||||
const msg = data.toString().trim();
|
||||
// Capture meaningful errors (ignore sudo password prompt noise)
|
||||
if (msg && !msg.includes("Password:") && !msg.includes("password for")) {
|
||||
console.error(`[MITM Server Error] ${msg}`);
|
||||
startError = msg;
|
||||
}
|
||||
});
|
||||
serverProcess.on("exit", (code) => {
|
||||
console.log(`MITM server exited with code ${code}`);
|
||||
serverProcess = null;
|
||||
serverPid = null;
|
||||
try { fs.unlinkSync(PID_FILE); } catch { /* ignore */ }
|
||||
});
|
||||
if (!IS_WIN) {
|
||||
serverProcess.stdout.on("data", (data) => {
|
||||
console.log(`[MITM Server] ${data.toString().trim()}`);
|
||||
});
|
||||
serverProcess.stderr.on("data", (data) => {
|
||||
const msg = data.toString().trim();
|
||||
// Capture meaningful errors (ignore sudo password prompt noise)
|
||||
if (msg && !msg.includes("Password:") && !msg.includes("password for")) {
|
||||
console.error(`[MITM Server Error] ${msg}`);
|
||||
startError = msg;
|
||||
}
|
||||
});
|
||||
serverProcess.on("exit", (code) => {
|
||||
console.log(`MITM server exited with code ${code}`);
|
||||
serverProcess = null;
|
||||
serverPid = null;
|
||||
try { fs.unlinkSync(PID_FILE); } catch { /* ignore */ }
|
||||
});
|
||||
}
|
||||
|
||||
// Wait for server to be ready by polling health endpoint
|
||||
const health = await pollMitmHealth(IS_WIN ? 12000 : 8000);
|
||||
|
||||
Reference in New Issue
Block a user