feat: add check_uptime.js script for scheduled checks

This commit is contained in:
ginnoir
2026-06-17 12:31:10 -05:00
parent 8dd90854af
commit 4ede8c483a
+115
View File
@@ -0,0 +1,115 @@
const fs = require('fs');
const path = require('path');
async function checkStatus() {
try {
// 1. Fetch status page HTML
const htmlResponse = await fetch('https://uptime.ginnoir.com/status/default');
if (!htmlResponse.ok) {
throw new Error(`Failed to fetch status page: ${htmlResponse.statusText}`);
}
const html = await htmlResponse.text();
// Extract window.preloadData = ...;
const preloadRegex = /window\.preloadData\s*=\s*({.*?});/s;
const match = html.match(preloadRegex);
if (!match) {
throw new Error("Could not find window.preloadData in HTML");
}
let preloadData;
try {
preloadData = new Function(`return ${match[1]};`)();
} catch (e) {
throw new Error(`Failed to parse preloadData: ${e.message}`);
}
const monitors = [];
if (preloadData && preloadData.publicGroupList) {
for (const group of preloadData.publicGroupList) {
if (group.monitorList) {
monitors.push(...group.monitorList);
}
}
}
// 2. Fetch heartbeat JSON
const heartbeatResponse = await fetch('https://uptime.ginnoir.com/api/status-page/heartbeat/default');
if (!heartbeatResponse.ok) {
throw new Error(`Failed to fetch heartbeat: ${heartbeatResponse.statusText}`);
}
const heartbeats = await heartbeatResponse.json();
// 3. Map status and print summary
const monitorMap = {};
for (const monitor of monitors) {
monitorMap[monitor.id] = {
name: monitor.name,
type: monitor.type,
status: 'UNKNOWN',
ping: null,
lastCheck: null,
msg: ''
};
}
const heartbeatList = heartbeats.heartbeatList || {};
for (const id in heartbeatList) {
const list = heartbeatList[id];
if (list && list.length > 0) {
const latest = list[list.length - 1];
if (monitorMap[id]) {
monitorMap[id].status = latest.status === 1 ? 'UP' : 'DOWN';
monitorMap[id].ping = latest.ping;
monitorMap[id].lastCheck = latest.time;
monitorMap[id].msg = latest.msg || '';
}
}
}
// Generate summary
const monitorValues = Object.values(monitorMap);
const total = monitorValues.length;
const up = monitorValues.filter(m => m.status === 'UP').length;
const down = monitorValues.filter(m => m.status === 'DOWN').length;
const unknown = monitorValues.filter(m => m.status === 'UNKNOWN').length;
let summaryMd = `### Uptime Status Summary (Checked at ${new Date().toLocaleString()})\n\n`;
if (down > 0) {
summaryMd += `⚠️ **Status: Degraded (${down}/${total} services DOWN)**\n\n`;
} else if (up === total) {
summaryMd += `✅ **Status: Healthy (All ${total} services UP)**\n\n`;
} else {
summaryMd += `️ **Status: Mixed (UP: ${up}, DOWN: ${down}, UNKNOWN: ${unknown})**\n\n`;
}
if (down > 0) {
summaryMd += `#### 🚨 DOWN Services:\n`;
monitorValues.filter(m => m.status === 'DOWN').forEach(m => {
summaryMd += `- **${m.name}** (${m.type}) - ${m.msg || 'No message'} (Last checked: ${m.lastCheck})\n`;
});
summaryMd += `\n`;
}
summaryMd += `#### 📋 Service Statuses:\n`;
summaryMd += `| Service | Status | Latency (ms) | Last Check |\n`;
summaryMd += `| :--- | :---: | :---: | :--- |\n`;
// Sort services by name
monitorValues.sort((a, b) => a.name.localeCompare(b.name));
for (const m of monitorValues) {
const statusIcon = m.status === 'UP' ? '🟢 UP' : m.status === 'DOWN' ? '🔴 DOWN' : '⚪ UNKNOWN';
const pingText = m.ping !== null ? `${m.ping} ms` : 'N/A';
summaryMd += `| ${m.name} | ${statusIcon} | ${pingText} | ${m.lastCheck || 'N/A'} |\n`;
}
console.log(summaryMd);
} catch (error) {
console.error(`Error checking status: ${error.message}`);
process.exit(1);
}
}
checkStatus();