154 lines
3.4 KiB
JavaScript
154 lines
3.4 KiB
JavaScript
const fs = require("fs");
|
|
const path = require("path");
|
|
const { spawn } = require("child_process");
|
|
|
|
const ROOT = path.join(__dirname, "..");
|
|
const DATA_DIR = path.join(ROOT, "data");
|
|
const LOG_DIR = path.join(ROOT, "logs");
|
|
const PID_FILE = path.join(DATA_DIR, "server.pid");
|
|
const OUT_LOG = path.join(LOG_DIR, "server.out.log");
|
|
const ERR_LOG = path.join(LOG_DIR, "server.err.log");
|
|
|
|
fs.mkdirSync(DATA_DIR, { recursive: true });
|
|
fs.mkdirSync(LOG_DIR, { recursive: true });
|
|
|
|
const command = process.argv[2] || "status";
|
|
|
|
if (command === "start") {
|
|
start();
|
|
} else if (command === "stop") {
|
|
stop();
|
|
} else if (command === "restart") {
|
|
restart();
|
|
} else if (command === "status") {
|
|
status();
|
|
} else {
|
|
console.error(`Unknown command: ${command}`);
|
|
process.exit(1);
|
|
}
|
|
|
|
function start() {
|
|
const pid = readPid();
|
|
if (pid && isRunning(pid)) {
|
|
console.log(`Live Event server already running with PID ${pid}`);
|
|
console.log(`Logs: ${OUT_LOG}`);
|
|
return;
|
|
}
|
|
|
|
cleanupStalePid();
|
|
|
|
const stdoutFd = fs.openSync(OUT_LOG, "a");
|
|
const stderrFd = fs.openSync(ERR_LOG, "a");
|
|
const child = spawn(process.execPath, ["server.js"], {
|
|
cwd: ROOT,
|
|
detached: true,
|
|
windowsHide: true,
|
|
stdio: ["ignore", stdoutFd, stderrFd],
|
|
env: {
|
|
...process.env,
|
|
RC_TIMING_PID_FILE: PID_FILE,
|
|
},
|
|
});
|
|
|
|
child.unref();
|
|
fs.writeFileSync(PID_FILE, `${child.pid}\n`);
|
|
|
|
console.log(`Live Event server started in background`);
|
|
console.log(`PID: ${child.pid}`);
|
|
console.log(`Logs: ${OUT_LOG}`);
|
|
}
|
|
|
|
async function stop() {
|
|
const pid = readPid();
|
|
if (!pid) {
|
|
console.log("Live Event server is not running");
|
|
return;
|
|
}
|
|
|
|
if (!isRunning(pid)) {
|
|
cleanupStalePid();
|
|
console.log("Removed stale pid file");
|
|
return;
|
|
}
|
|
|
|
try {
|
|
process.kill(pid, "SIGTERM");
|
|
} catch (error) {
|
|
console.error(`Could not stop PID ${pid}: ${error instanceof Error ? error.message : String(error)}`);
|
|
process.exit(1);
|
|
}
|
|
|
|
const stopped = await waitForExit(pid, 5000);
|
|
if (!stopped && process.platform !== "win32") {
|
|
try {
|
|
process.kill(pid, "SIGKILL");
|
|
} catch {
|
|
// ignore
|
|
}
|
|
await waitForExit(pid, 1000);
|
|
}
|
|
|
|
cleanupStalePid();
|
|
console.log(`Live Event server stopped`);
|
|
}
|
|
|
|
async function restart() {
|
|
await stop();
|
|
start();
|
|
}
|
|
|
|
function status() {
|
|
const pid = readPid();
|
|
if (pid && isRunning(pid)) {
|
|
console.log(`Live Event server is running`);
|
|
console.log(`PID: ${pid}`);
|
|
console.log(`Logs: ${OUT_LOG}`);
|
|
return;
|
|
}
|
|
|
|
cleanupStalePid();
|
|
console.log("Live Event server is not running");
|
|
}
|
|
|
|
function readPid() {
|
|
if (!fs.existsSync(PID_FILE)) {
|
|
return null;
|
|
}
|
|
const raw = fs.readFileSync(PID_FILE, "utf8").trim();
|
|
const pid = Number(raw);
|
|
return Number.isFinite(pid) && pid > 0 ? pid : null;
|
|
}
|
|
|
|
function isRunning(pid) {
|
|
try {
|
|
process.kill(pid, 0);
|
|
return true;
|
|
} catch {
|
|
return false;
|
|
}
|
|
}
|
|
|
|
function cleanupStalePid() {
|
|
const pid = readPid();
|
|
if (!pid || !isRunning(pid)) {
|
|
fs.rmSync(PID_FILE, { force: true });
|
|
}
|
|
}
|
|
|
|
function waitForExit(pid, timeoutMs) {
|
|
return new Promise((resolve) => {
|
|
const started = Date.now();
|
|
const timer = setInterval(() => {
|
|
if (!isRunning(pid)) {
|
|
clearInterval(timer);
|
|
resolve(true);
|
|
return;
|
|
}
|
|
if (Date.now() - started >= timeoutMs) {
|
|
clearInterval(timer);
|
|
resolve(false);
|
|
}
|
|
}, 150);
|
|
});
|
|
}
|