my own status page
0
fork

Configure Feed

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

feat: add health and last updated

+50 -2
+9
src/db.ts
··· 86 86 return result; 87 87 } 88 88 89 + export async function getLastCheckTime( 90 + db: D1Database, 91 + ): Promise<number | null> { 92 + const row = await db 93 + .prepare("SELECT MAX(timestamp) as ts FROM pings") 94 + .first<{ ts: number | null }>(); 95 + return row?.ts ?? null; 96 + } 97 + 89 98 export async function pruneOldPings( 90 99 db: D1Database, 91 100 days: number,
+4
src/index.ts
··· 17 17 return handleIndex(env); 18 18 } 19 19 20 + if (path === "/health") { 21 + return Response.json({ ok: true, timestamp: new Date().toISOString() }); 22 + } 23 + 20 24 if (path.startsWith("/api/status")) { 21 25 const res = await handleStatusRoute(env, path); 22 26 if (res) return res;
+37 -2
src/routes/index.ts
··· 1 1 import type { Env } from "../types"; 2 2 import { getManifest } from "../manifest"; 3 - import { getLatestPing, getUptime7d } from "../db"; 3 + import { getLatestPing, getUptime7d, getLastCheckTime } from "../db"; 4 4 import { getDeviceStatus } from "../tailscale"; 5 5 import { COMMIT_SHA } from "../version"; 6 6 ··· 28 28 return { name, type: machine.type, online, services }; 29 29 }), 30 30 ); 31 + 32 + const lastCheck = await getLastCheckTime(env.DB); 33 + const lastCheckISO = lastCheck 34 + ? new Date(lastCheck * 1000).toISOString() 35 + : null; 31 36 32 37 const servers = machines.filter((m) => m.type === "server"); 33 38 const clients = machines.filter((m) => m.type === "client"); ··· 110 115 ${clients.map((m) => `<span class="client"><span class="dot ${m.online ? "online" : "offline"}"></span>${esc(m.name)}</span>`).join("\n")} 111 116 </div> 112 117 </div>` : ""} 113 - <footer><span>checked every 5 min</span><a href="https://github.com/taciturnaxolotl/status/commit/${COMMIT_SHA}">${COMMIT_SHA}</a></footer> 118 + <footer><span>${lastCheckISO ? `updated <relative-time datetime="${lastCheckISO}" prefix="">loading</relative-time>` : "no checks yet"}</span><a href="https://github.com/taciturnaxolotl/status/commit/${COMMIT_SHA}">${COMMIT_SHA}</a></footer> 119 + <script> 120 + class RelativeTimeElement extends HTMLElement { 121 + static get observedAttributes() { return ['datetime']; } 122 + connectedCallback() { this.update(); } 123 + disconnectedCallback() { this.timer && clearTimeout(this.timer); } 124 + attributeChangedCallback() { this.update(); } 125 + get datetime() { return this.getAttribute('datetime') || ''; } 126 + update() { 127 + const d = this.datetime; 128 + if (!d) return; 129 + const date = new Date(d); 130 + if (isNaN(date.getTime())) return; 131 + const diff = Date.now() - date.getTime(); 132 + const abs = Math.abs(diff); 133 + const s = Math.floor(abs / 1000); 134 + const m = Math.floor(s / 60); 135 + const h = Math.floor(m / 60); 136 + const days = Math.floor(h / 24); 137 + const rtf = new Intl.RelativeTimeFormat(navigator.language, { numeric: 'auto' }); 138 + const sign = diff > 0 ? -1 : 1; 139 + if (s < 60) this.textContent = rtf.format(sign * s, 'second'); 140 + else if (m < 60) this.textContent = rtf.format(sign * m, 'minute'); 141 + else if (h < 24) this.textContent = rtf.format(sign * h, 'hour'); 142 + else this.textContent = rtf.format(sign * days, 'day'); 143 + const delay = s < 60 ? 1000 : m < 60 ? 60000 : 3600000; 144 + this.timer = setTimeout(() => this.update(), delay); 145 + } 146 + } 147 + customElements.define('relative-time', RelativeTimeElement); 148 + </script> 114 149 </body> 115 150 </html>`; 116 151