38 lines
1.4 KiB
JavaScript
38 lines
1.4 KiB
JavaScript
/**
|
|
* Pi/host fleet heartbeat: self-report local pm2 process health to Postgres so a
|
|
* central detector on Erik can catch crash-loops (restart_time velocity + near-0
|
|
* uptime) and stale/dead hosts — which a naive "pm2 status == online" check misses.
|
|
* Runs on each Pi via cron (every 5 min). DB via the local tunnel (127.0.0.1:5433).
|
|
*/
|
|
import { execFileSync } from "node:child_process";
|
|
import os from "node:os";
|
|
import { config } from "dotenv";
|
|
import pg from "pg";
|
|
|
|
config({ path: new URL("../.env", import.meta.url) }); // ~/tip/.env
|
|
|
|
const host = os.hostname();
|
|
let procs = [];
|
|
try { procs = JSON.parse(execFileSync("pm2", ["jlist"], { encoding: "utf8", maxBuffer: 8 * 1024 * 1024 })); }
|
|
catch { process.exit(0); }
|
|
|
|
const pool = new pg.Pool({
|
|
host: "127.0.0.1", port: 5433,
|
|
user: process.env.POSTGRES_USER || "tip",
|
|
database: process.env.POSTGRES_DB || "transceiver_db",
|
|
password: process.env.POSTGRES_PASSWORD,
|
|
max: 1, connectionTimeoutMillis: 8000, statement_timeout: 8000,
|
|
});
|
|
|
|
try {
|
|
for (const p of procs) {
|
|
const e = p.pm2_env || {};
|
|
const uptimeS = Math.round((Date.now() - (e.pm_uptime || Date.now())) / 1000);
|
|
await pool.query(
|
|
"insert into fleet_health(host,pm_name,status,restart_time,uptime_s) values($1,$2,$3,$4,$5)",
|
|
[host, p.name, e.status || null, e.restart_time ?? 0, uptimeS],
|
|
);
|
|
}
|
|
} catch { /* heartbeat is best-effort; never crash the host */ }
|
|
finally { await pool.end().catch(() => {}); }
|