mirror of
https://github.com/diegosouzapw/OmniRoute.git
synced 2026-07-31 12:22:14 +03:00
Adiciona ServerSupervisor (bin/cli/runtime/processSupervisor.mjs) que reinicia o
servidor com backoff exponencial (1s, 2s, 4s... cap 10s) em caso de crash.
Após maxRestarts falhas em 30s exibe crash log e encerra. Detecta MITM como
causa do crash via heurística e desabilita automaticamente.
PID management agora é granular por subprocesso (~/.omniroute/{service}/.pid)
suportando server, mitm e tunnel/cloudflared|tailscale. `stop` e
`killAllSubprocesses` encerram todos os serviços registrados.
Novas opções em `serve`: --log (passa stdout/stderr inline), --no-recovery
(comportamento legado sem supervisor), --max-restarts <n> (padrão 2).
77 lines
1.8 KiB
JavaScript
77 lines
1.8 KiB
JavaScript
import { existsSync, mkdirSync, readFileSync, unlinkSync, writeFileSync } from "node:fs";
|
|
import { join } from "node:path";
|
|
import { resolveDataDir } from "../data-dir.mjs";
|
|
|
|
const SERVICES = ["server", "mitm", "tunnel/cloudflared", "tunnel/tailscale"];
|
|
|
|
function getServicePidPath(service) {
|
|
return join(resolveDataDir(), service, ".pid");
|
|
}
|
|
|
|
export function writePidFile(service, pid) {
|
|
try {
|
|
const dir = join(resolveDataDir(), service);
|
|
mkdirSync(dir, { recursive: true });
|
|
writeFileSync(getServicePidPath(service), String(pid), "utf8");
|
|
return true;
|
|
} catch {
|
|
return false;
|
|
}
|
|
}
|
|
|
|
export function readPidFile(service) {
|
|
try {
|
|
const file = getServicePidPath(service);
|
|
if (!existsSync(file)) return null;
|
|
const pid = parseInt(readFileSync(file, "utf8").trim(), 10);
|
|
return Number.isFinite(pid) ? pid : null;
|
|
} catch {
|
|
return null;
|
|
}
|
|
}
|
|
|
|
export function cleanupPidFile(service) {
|
|
try {
|
|
unlinkSync(getServicePidPath(service));
|
|
} catch {}
|
|
}
|
|
|
|
export function killAllSubprocesses() {
|
|
for (const service of SERVICES) {
|
|
const pid = readPidFile(service);
|
|
if (!pid) continue;
|
|
try {
|
|
process.kill(pid, "SIGTERM");
|
|
} catch {}
|
|
cleanupPidFile(service);
|
|
}
|
|
}
|
|
|
|
export function isPidRunning(pid) {
|
|
if (!pid) return false;
|
|
try {
|
|
process.kill(pid, 0);
|
|
return true;
|
|
} catch {
|
|
return false;
|
|
}
|
|
}
|
|
|
|
export function sleep(ms) {
|
|
return new Promise((resolve) => setTimeout(resolve, ms));
|
|
}
|
|
|
|
export async function waitForServer(port, timeout = 15000) {
|
|
const start = Date.now();
|
|
while (Date.now() - start < timeout) {
|
|
try {
|
|
const res = await fetch(`http://localhost:${port}/api/health`, {
|
|
signal: AbortSignal.timeout(2000),
|
|
});
|
|
if (res.ok) return true;
|
|
} catch {}
|
|
await sleep(500);
|
|
}
|
|
return false;
|
|
}
|