my own status page
0
fork

Configure Feed

Select the types of activity you want to include in your feed.

feat: update the status routes

+88 -14
+77 -5
README.md
··· 6 6 7 7 ## API 8 8 9 + ### `GET /api/status` 10 + Overall summary with all machines and services. 11 + ```json 12 + { 13 + "ok": true, 14 + "status": "up", 15 + "last_check": 1741900245, 16 + "machines": [{ 17 + "name": "orion", 18 + "hostname": "orion", 19 + "type": "server", 20 + "online": true, 21 + "status": "up", 22 + "services": [{ 23 + "id": "l4", 24 + "status": "up", 25 + "latency_ms": 42, 26 + "uptime_90d": 99.84 27 + }] 28 + }] 29 + } 9 30 ``` 10 - /api/status # overall summary (ok, status, uptime, counts) 11 - /api/status/overall # same as above 12 - /api/status/service/:id # single service status + latency + uptime 13 - /api/status/machine/:name # machine online status + all its services 14 - /api/uptime/:service_id # hourly uptime buckets for a service 31 + 32 + ### `GET /api/status/overall` 33 + Lightweight overall summary. 34 + ```json 35 + { 36 + "ok": true, 37 + "status": "up", 38 + "uptime_90d": 99.84, 39 + "services_total": 20, 40 + "services_monitored": 16, 41 + "machines_total": 4 42 + } 15 43 ``` 44 + 45 + ### `GET /api/status/service/:id` 46 + Single service status. 47 + ```json 48 + { 49 + "id": "l4", 50 + "status": "up", 51 + "latency_ms": 42, 52 + "uptime_90d": 99.84 53 + } 54 + ``` 55 + 56 + ### `GET /api/status/machine/:name` 57 + Machine and all its services. 58 + ```json 59 + { 60 + "name": "orion", 61 + "hostname": "orion", 62 + "type": "server", 63 + "online": true, 64 + "status": "up", 65 + "services": [{ 66 + "id": "l4", 67 + "status": "up", 68 + "latency_ms": 42, 69 + "uptime_90d": 99.84 70 + }] 71 + } 72 + ``` 73 + 74 + ### `GET /api/uptime/:service_id` 75 + Hourly uptime buckets for a service. Optional `?window=<days>` param (default: 90). 76 + ```json 77 + { 78 + "service_id": "l4", 79 + "window_hours": 2160, 80 + "buckets": [{ 81 + "timestamp": 1741896000, 82 + "status": "up" 83 + }] 84 + } 85 + ``` 86 + 87 + **Status values:** `up` · `degraded` · `down` · `partial` · `timeout` · `misconfigured` · `unknown` 16 88 17 89 ## Badges 18 90
+4 -2
src/db.ts
··· 29 29 export async function getUptime7d( 30 30 db: D1Database, 31 31 service_id: string, 32 + days = 90, 32 33 ): Promise<number> { 33 - const since = Math.floor(Date.now() / 1000) - 7 * 24 * 60 * 60; 34 + const since = Math.floor(Date.now() / 1000) - days * 24 * 60 * 60; 34 35 const row = await db 35 36 .prepare( 36 37 "SELECT COUNT(*) as total, SUM(CASE WHEN status = 'up' THEN 1 ELSE 0 END) as up_count FROM pings WHERE service_id = ? AND timestamp >= ?", ··· 66 67 67 68 export async function getAllUptime7d( 68 69 db: D1Database, 70 + days = 90, 69 71 ): Promise<Map<string, number>> { 70 - const since = Math.floor(Date.now() / 1000) - 7 * 24 * 60 * 60; 72 + const since = Math.floor(Date.now() / 1000) - days * 24 * 60 * 60; 71 73 const rows = await db 72 74 .prepare( 73 75 `SELECT service_id, COUNT(*) as total, SUM(CASE WHEN status = 'up' THEN 1 ELSE 0 END) as up_count
+3 -3
src/routes/index.ts
··· 33 33 url: `https://${svc.domain}`, 34 34 status: latestPings.get(svc.name)?.status ?? "unknown", 35 35 latency_ms: latestPings.get(svc.name)?.latency_ms ?? null, 36 - uptime_7d: uptimes.get(svc.name) ?? 100, 36 + uptime_90d: uptimes.get(svc.name) ?? 100, 37 37 has_health: svc.health_url !== null, 38 38 })); 39 39 return { name, type: machine.type, online, services }; ··· 180 180 <span class="svc-name"><a href="${esc(s.url)}">${esc(s.name)}</a></span> 181 181 </div> 182 182 <div class="svc-right"> 183 - ${s.has_health ? `<span class="uptime" data-service-uptime="${esc(s.name)}">${s.uptime_7d}%</span><span class="latency" data-service-latency="${esc(s.name)}">${s.latency_ms !== null ? s.latency_ms + "ms" : "—"}</span>` : `<span class="latency">no health check</span>`} 183 + ${s.has_health ? `<span class="uptime" data-service-uptime="${esc(s.name)}">${s.uptime_90d}%</span><span class="latency" data-service-latency="${esc(s.name)}">${s.latency_ms !== null ? s.latency_ms + "ms" : "—"}</span>` : `<span class="latency">no health check</span>`} 184 184 </div> 185 185 </div>`, 186 186 ) ··· 271 271 const sDot = document.querySelector('[data-service="' + svc.id + '"]'); 272 272 if (sDot) setDot(sDot, svc.status); 273 273 const uEl = document.querySelector('[data-service-uptime="' + svc.id + '"]'); 274 - if (uEl) uEl.textContent = svc.uptime_7d + '%'; 274 + if (uEl) uEl.textContent = svc.uptime_90d + '%'; 275 275 const lEl = document.querySelector('[data-service-latency="' + svc.id + '"]'); 276 276 if (lEl) lEl.textContent = svc.latency_ms !== null ? svc.latency_ms + 'ms' : '—'; 277 277 }
+4 -4
src/routes/status.ts
··· 39 39 { 40 40 ok: grade === "up", 41 41 status: grade, 42 - uptime_7d: avgUptime, 42 + uptime_90d: avgUptime, 43 43 services_total: allServices.length, 44 44 services_monitored: monitored.length, 45 45 machines_total: Object.keys(manifest).length, ··· 64 64 id: svc.name, 65 65 status: (ping?.status ?? "unknown") as string, 66 66 latency_ms: ping?.latency_ms ?? null, 67 - uptime_7d: uptimes.get(svc.name) ?? 100, 67 + uptime_90d: uptimes.get(svc.name) ?? 100, 68 68 }; 69 69 }); 70 70 const svcStatuses = services.map((s) => s.status); ··· 106 106 id, 107 107 status: ping.status, 108 108 latency_ms: ping.latency_ms, 109 - uptime_7d: uptime, 109 + uptime_90d: uptime, 110 110 } ); 111 111 } 112 112 ··· 128 128 id: svc.name, 129 129 status: (ping?.status ?? "unknown") as string, 130 130 latency_ms: ping?.latency_ms ?? null, 131 - uptime_7d: uptime, 131 + uptime_90d: uptime, 132 132 }; 133 133 }), 134 134 );