Files
ginnoirandClaude Sonnet 5 10997e4b16 feat(scripts): add uptime-check retry + Obsidian variant, add LLM benchmark docs
check_uptime.js gets a fetchWithRetry wrapper (3 attempts, 2s backoff)
for transient failures against the status page/heartbeat API.
check_uptime_to_obsidian.js is a variant that logs results into the
Obsidian vault instead of stdout. Also adds two benchmark writeups
(gpt-oss-20b on Ollama, 73-node Ollama fleet).

Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com>
2026-07-01 02:17:53 -05:00

133 lines
4.5 KiB
JavaScript
Raw Permalink Blame History

This file contains ambiguous Unicode characters
This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.
const fs = require('fs');
const path = require('path');
async function fetchWithRetry(url, options = {}, retries = 3, backoff = 2000) {
for (let i = 0; i < retries; i++) {
try {
const response = await fetch(url, options);
if (response.ok) return response;
if (response.status >= 500) {
throw new Error(`HTTP ${response.status}: ${response.statusText}`);
}
return response;
} catch (err) {
if (i === retries - 1) throw err;
console.warn(`Fetch to ${url} failed (attempt ${i + 1}/${retries}): ${err.message}. Retrying in ${backoff}ms...`);
await new Promise(resolve => setTimeout(resolve, backoff));
}
}
}
async function checkStatus() {
try {
// 1. Fetch status page HTML
const htmlResponse = await fetchWithRetry('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 fetchWithRetry('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();