diff --git a/scripts/check_uptime.js b/scripts/check_uptime.js new file mode 100644 index 0000000..cbafc46 --- /dev/null +++ b/scripts/check_uptime.js @@ -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();