Add IAX Ping for all servers for healthcheck

This commit is contained in:
Christopher Cookman 2026-06-24 04:39:17 -06:00
parent f850288796
commit 409a12ea89
3 changed files with 73 additions and 2 deletions

View file

@ -23,6 +23,7 @@ const crypto = require("crypto")
const dns = require("dns"); const dns = require("dns");
const app = express(); const app = express();
const port = process.env.SERVER_PORT || 3000; const port = process.env.SERVER_PORT || 3000;
const iaxping = require("iaxping");
const invalidBlocks = [ const invalidBlocks = [
// Emergency number prefixes (112, 911, 999, 110, 117, 119, 113, 191, 111) // Emergency number prefixes (112, 911, 999, 110, 117, 119, 113, 191, 111)
@ -1050,8 +1051,71 @@ app.get("/api/healthcheck", (req, res) => {
}); });
}); });
// Periodic healthcheck on routes (iax ping every server every 5 minutes)
let persistentHealthCheckData;
function healthCheck() {
return new Promise((resolve, reject) => {
let healthCheckData = {};
pool.getConnection().then(conn => {
conn.query('SELECT * FROM routes').then((rows) => {
let count = 0;
for (const row of rows) {
const host = row.server;
const port = row.port;
const block = row.block_start;
iaxping({ host, port }).then((pingResult) => {
// Good ping
healthCheckData[block] = { online: true, ping: pingResult.rttMs, timestamp: Date.now() };
}).catch((pingErr) => {
switch (pingErr.code) {
case "ERR_POKE_TIMEOUT":
healthCheckData[block] = { online: false, ping: null, timestamp: Date.now(), error: "Timeout" };
break;
case "ENOTFOUND":
healthCheckData[block] = { online: false, ping: null, timestamp: Date.now(), error: "Host not found (DNS)" };
break;
default:
healthCheckData[block] = { online: false, ping: null, timestamp: Date.now(), error: pingErr.message };
break;
}
}).finally(() => {
count++;
if (count === rows.length) {
conn.release();
persistentHealthCheckData = healthCheckData;
resolve(healthCheckData);
}
});
}
}).catch(err => {
console.error('Error getting routes for health check:', err);
conn.release();
reject(err);
});
}).catch(err => {
console.error('Error getting connection for health check:', err);
reject(err);
});
});
};
setInterval(healthCheck, 300000); // Run every 5 minutes
app.get("/api/servers", (req, res) => {
if (!persistentHealthCheckData) {
healthCheck().then((data) => {
res.json(data);
}).catch(err => {
console.error('Error running health check:', err);
res.status(500).json({ error: 'Internal server error' });
});
} else {
res.json(persistentHealthCheckData);
}
});
// logCall function (caller, callee) // logCall function (caller, callee)
const logCall = (caller, callee, srcIp="none_given", success, reason="none_given") => { const logCall = (caller, callee, srcIp = "none_given", success, reason = "none_given") => {
pool.getConnection().then(conn => { pool.getConnection().then(conn => {
conn.query('INSERT INTO callLogs (caller, callee, timestamp, srcIp, success, reason) VALUES (?, ?, ?, ?, ?, ?)', conn.query('INSERT INTO callLogs (caller, callee, timestamp, srcIp, success, reason) VALUES (?, ?, ?, ?, ?, ?)',
[caller, callee, Math.floor(Date.now()), srcIp, success, reason]).catch(err => { [caller, callee, Math.floor(Date.now()), srcIp, success, reason]).catch(err => {
@ -1104,7 +1168,7 @@ const genCall = (req, res, apiKey, ani, number) => {
logCall(ani, number, srcIp, false, "no_route"); logCall(ani, number, srcIp, false, "no_route");
return; return;
} }
conn.query('SELECT * FROM blocklist WHERE (blockType = 1 AND blockValue = ?) OR (blockType = 2 AND ? BETWEEN blockValue AND blockValue + ?);', [ani, ani, row.block_length]).then((blockRows) => { conn.query('SELECT * FROM blocklist WHERE (blockType = 1 AND blockValue = ?) OR (blockType = 2 AND ? BETWEEN blockValue AND blockValue + ?);', [ani, ani, row.block_length]).then((blockRows) => {
if (blockRows.length > 0) { if (blockRows.length > 0) {
// ANI is blocked from calling this route // ANI is blocked from calling this route

6
package-lock.json generated
View file

@ -15,6 +15,7 @@
"escape-html": "^1.0.3", "escape-html": "^1.0.3",
"express": "^4.21.2", "express": "^4.21.2",
"express-session": "^1.18.1", "express-session": "^1.18.1",
"iaxping": "github:ChrisChrome/IAXPing.js",
"mariadb": "^3.4.0", "mariadb": "^3.4.0",
"session-file-store": "^1.5.0" "session-file-store": "^1.5.0"
} }
@ -868,6 +869,11 @@
"integrity": "sha512-6FlzubTLZG3J2a/NVCAleEhjzq5oxgHyaCU9yYXvcLsvoVaHJq/s5xXI6/XXP6tz7R9xAOtHnSO/tXtF3WRTlA==", "integrity": "sha512-6FlzubTLZG3J2a/NVCAleEhjzq5oxgHyaCU9yYXvcLsvoVaHJq/s5xXI6/XXP6tz7R9xAOtHnSO/tXtF3WRTlA==",
"license": "MIT" "license": "MIT"
}, },
"node_modules/iaxping": {
"version": "1.0.0",
"resolved": "git+ssh://git@github.com/ChrisChrome/IAXPing.js.git#105e669e5916cecc30273c9e4aa1f7bc47440f52",
"license": "GPL-3.0-only"
},
"node_modules/iconv-lite": { "node_modules/iconv-lite": {
"version": "0.4.24", "version": "0.4.24",
"resolved": "https://registry.npmjs.org/iconv-lite/-/iconv-lite-0.4.24.tgz", "resolved": "https://registry.npmjs.org/iconv-lite/-/iconv-lite-0.4.24.tgz",

View file

@ -16,6 +16,7 @@
"escape-html": "^1.0.3", "escape-html": "^1.0.3",
"express": "^4.21.2", "express": "^4.21.2",
"express-session": "^1.18.1", "express-session": "^1.18.1",
"iaxping": "github:ChrisChrome/IAXPing.js",
"mariadb": "^3.4.0", "mariadb": "^3.4.0",
"session-file-store": "^1.5.0" "session-file-store": "^1.5.0"
} }