public uptime monitoring + (soon) observability with events saved to PDS
1<script lang="ts"> 2 import { onMount } from 'svelte'; 3 import { fetchUptimeChecks } from './lib/atproto.ts'; 4 import UptimeDisplay from './lib/uptime-display.svelte'; 5 import type { UptimeCheckRecord } from './lib/types.ts'; 6 import { config } from './lib/config.ts'; 7 8 let checks = $state<UptimeCheckRecord[]>([]); 9 let loading = $state(true); 10 let error = $state(''); 11 let lastUpdate = $state<Date | null>(null); 12 13 async function loadChecks() { 14 loading = true; 15 error = ''; 16 17 try { 18 checks = await fetchUptimeChecks(config.pds, config.did); 19 lastUpdate = new Date(); 20 } catch (err) { 21 error = (err as Error).message || 'failed to fetch uptime checks'; 22 checks = []; 23 } finally { 24 loading = false; 25 } 26 } 27 28 onMount(() => { 29 // load checks immediately 30 loadChecks(); 31 32 // refresh every 10 seconds 33 const interval = setInterval(loadChecks, 10 * 1000); 34 return () => clearInterval(interval); 35 }); 36</script> 37 38<main class="max-w-6xl mx-auto p-8"> 39 <header class="text-center mb-8"> 40 <h1 class="text-5xl font-bold text-accent mb-2">{config.title}</h1> 41 <p class="text-muted-foreground">{config.subtitle}</p> 42 </header> 43 44 <div class="bg-card rounded-lg shadow-sm p-4 mb-8 flex justify-between items-center"> 45 <div class="flex items-center gap-4"> 46 {#if lastUpdate} 47 <span class="text-sm text-muted-foreground"> 48 last updated: {lastUpdate.toLocaleTimeString()} 49 </span> 50 {/if} 51 </div> 52 <button 53 class="px-4 py-2 bg-primary text-primary-foreground rounded-md text-sm font-medium hover:opacity-90 disabled:opacity-50 transition-opacity" 54 onclick={loadChecks} 55 disabled={loading} 56 > 57 {loading ? 'refreshing...' : 'refresh'} 58 </button> 59 </div> 60 61 {#if error} 62 <div class="bg-destructive/10 text-destructive rounded-lg p-4 mb-4"> 63 {error} 64 </div> 65 {/if} 66 67 {#if loading && checks.length === 0} 68 <div class="text-center py-12 bg-card rounded-lg shadow-sm text-muted-foreground"> 69 loading uptime data... 70 </div> 71 {:else if checks.length > 0} 72 <UptimeDisplay {checks} /> 73 {:else if !loading} 74 <div class="text-center py-12 bg-card rounded-lg shadow-sm text-muted-foreground"> 75 no uptime data available 76 </div> 77 {/if} 78 79 <footer class="mt-12 pt-8 border-t border-border text-center text-sm text-muted-foreground"> 80 <p> 81 built by <a href="https://bsky.app/profile/nekomimi.pet" target="_blank" rel="noopener noreferrer" class="text-accent hover:underline">@nekomimi.pet</a> 82 · <a href="https://tangled.org/@nekomimi.pet/cute-monitor" target="_blank" rel="noopener noreferrer" class="text-accent hover:underline">source</a> 83 </p> 84 </footer> 85</main> 86