128 lines
4.2 KiB
JavaScript
128 lines
4.2 KiB
JavaScript
// ---------------------------------------------------------------------------
|
|
// Health checks for all dashboard services.
|
|
// Reads the service URLs from script.js (single source of truth), pings each
|
|
// one in parallel and writes health.json. Scheduled every minute by cron.
|
|
//
|
|
// Status classification:
|
|
// online - got any HTTP response below 500 (401/404 still means "up")
|
|
// error - server responded with 5xx
|
|
// offline - timeout / connection refused / DNS failure
|
|
// TLS certificate validity is ignored (reachability check only).
|
|
// ---------------------------------------------------------------------------
|
|
|
|
const http = require("http");
|
|
const https = require("https");
|
|
const fs = require("fs");
|
|
const path = require("path");
|
|
const { URL } = require("url");
|
|
const { proxyFor, connectThroughProxy, proxiedRequestOptions } = require("./proxy");
|
|
|
|
const OUT_FILE = process.env.OUT_FILE || "/usr/share/nginx/html/health.json";
|
|
const TIMEOUT = parseInt(process.env.HEALTH_TIMEOUT || "5000", 10);
|
|
|
|
// Reachability only — don't fail on self-signed certificates.
|
|
const httpAgent = new http.Agent({ rejectUnauthorized: false });
|
|
const httpsAgent = new https.Agent({ rejectUnauthorized: false });
|
|
|
|
function serviceUrls() {
|
|
const candidates = [
|
|
process.env.SERVICES_JS,
|
|
"/usr/share/nginx/html/script.js",
|
|
path.join(__dirname, "..", "script.js"),
|
|
].filter(Boolean);
|
|
for (const p of candidates) {
|
|
try {
|
|
const src = fs.readFileSync(p, "utf8");
|
|
const urls = [...src.matchAll(/url: *"(https?:\/\/[^"]+)"/g)].map((m) => m[1]);
|
|
if (urls.length) return [...new Set(urls)];
|
|
} catch {
|
|
// try next candidate
|
|
}
|
|
}
|
|
return [];
|
|
}
|
|
|
|
function checkUrl(url) {
|
|
return new Promise((resolve) => {
|
|
const started = Date.now();
|
|
let settled = false;
|
|
const done = (result) => {
|
|
if (settled) return;
|
|
settled = true;
|
|
resolve(result);
|
|
};
|
|
|
|
(async () => {
|
|
let req;
|
|
try {
|
|
const u = new URL(url);
|
|
const client = u.protocol === "https:" ? https : http;
|
|
const options = {
|
|
method: "GET",
|
|
timeout: TIMEOUT,
|
|
};
|
|
|
|
// Honor the outbound proxy (GITLAB_PROXY) so the GitLab badge checks
|
|
// the same path the git stats actually take (see api/proxy.js).
|
|
const proxy = proxyFor(url);
|
|
if (proxy) {
|
|
let socket = null;
|
|
if (u.protocol === "https:") {
|
|
socket = await connectThroughProxy(proxy, url, TIMEOUT, { rejectUnauthorized: false });
|
|
}
|
|
Object.assign(options, proxiedRequestOptions(proxy, url, socket));
|
|
} else {
|
|
options.agent = u.protocol === "https:" ? httpsAgent : httpAgent;
|
|
}
|
|
|
|
req = client.request(
|
|
u,
|
|
options,
|
|
(res) => {
|
|
const code = res.statusCode;
|
|
res.destroy(); // response headers are all we need
|
|
done({
|
|
status: code >= 500 ? "error" : "online",
|
|
code,
|
|
latencyMs: Date.now() - started,
|
|
error: null,
|
|
});
|
|
}
|
|
);
|
|
} catch (e) {
|
|
return done({ status: "offline", code: null, latencyMs: null, error: e.message });
|
|
}
|
|
|
|
req.on("timeout", () => {
|
|
req.destroy();
|
|
done({ status: "offline", code: null, latencyMs: null, error: "Timeout" });
|
|
});
|
|
req.on("error", (e) => {
|
|
done({ status: "offline", code: null, latencyMs: null, error: e.message });
|
|
});
|
|
req.end();
|
|
})();
|
|
});
|
|
}
|
|
|
|
async function main() {
|
|
const urls = serviceUrls();
|
|
if (urls.length === 0) {
|
|
console.error("No service URLs found — is script.js available?");
|
|
process.exit(1);
|
|
}
|
|
|
|
const results = await Promise.all(urls.map(async (u) => [u, await checkUrl(u)]));
|
|
const services = Object.fromEntries(results);
|
|
|
|
const payload = { updatedAt: new Date().toISOString(), services };
|
|
fs.writeFileSync(OUT_FILE, JSON.stringify(payload, null, 2));
|
|
|
|
const count = (s) => results.filter(([, r]) => r.status === s).length;
|
|
console.log(`Health: ${urls.length} services — ${count("online")} online, ${count("error")} error, ${count("offline")} offline`);
|
|
}
|
|
|
|
main().catch((err) => {
|
|
console.error(err);
|
|
process.exit(1);
|
|
}); |