this repo has no description
plcbundle-watch.pages.dev
1<script lang="ts">
2 import { onMount } from 'svelte';
3 import instancesData from './instances.json';
4
5 type Instance = {
6 url: string,
7 cors?: boolean,
8 status?: object,
9 modern?: boolean,
10 }
11
12 let lastKnownBundle = $state({
13 number: 0,
14 hash: null,
15 })
16
17 let instances = $state(instancesData)
18 let instancesSorted = $derived(instances.sort((a, b) => a.status?.responseTime > b.status?.responseTime ? 1 : -1))
19
20 async function getStatus(instance: Instance) {
21 let statusResp: object | undefined;
22 let url: string = instance.url;
23 const start = performance.now();
24 try {
25 statusResp = await (await fetch(`${url}/status`)).json()
26 } catch (e) {}
27 if (!statusResp) {
28 url = `https://keyoxide.org/api/3/get/http?url=${encodeURIComponent(url)}&format=text&time=${Date.now()}`
29 const indexResp = await (await fetch(url)).text()
30 const [ _, from, to ] = indexResp?.match(/Range:\s+(\d{6}) - (\d{6})/)
31 statusResp = {
32 bundles: {
33 last_bundle: Number(to),
34 root_hash: indexResp?.match(/Root: ([a-f0-9]{64})/)[1],
35 head_hash: indexResp?.match(/Head: ([a-f0-9]{64})/)[1],
36 },
37 server: {
38 uptime: 1,
39 }
40 }
41 }
42 if (statusResp) {
43 statusResp.responseTime = performance.now() - start;
44 }
45 return statusResp
46 }
47
48 async function doCheck() {
49 for (const i of instances) {
50 i.status = undefined
51 }
52
53 await Promise.all(instances.map(async (instance) => {
54 const status = await getStatus(instance)
55
56 if (status?.bundles?.last_bundle > lastKnownBundle.number) {
57 lastKnownBundle.number = status?.bundles?.last_bundle
58 lastKnownBundle.hash = status?.bundles?.head_hash
59 }
60 instance.status = status
61 }))
62 }
63
64 onMount(() => {
65 doCheck()
66 })
67</script>
68
69<main class="w-full mt-10">
70 <div class="max-w-4xl mx-auto px-3">
71
72 <header>
73 <h1 class="text-3xl">plcbundle instances</h1>
74 </header>
75
76 <div class="flex items-center gap-2 mt-10 flex-wrap">
77 <div class="grow flex items-center text-lg">
78 <div><span class="opacity-50">Last known bundle:</span> <span class="font-semibold">{lastKnownBundle.number}</span> [<span class="font-mono text-base">{lastKnownBundle?.hash?.slice(0, 7)}</span>]</div>
79 </div>
80 <div class="">
81 <button type="button" class="btn btn-sm preset-tonal-primary" onclick={() => doCheck()}>Refresh</button>
82 </div>
83 </div>
84
85 <table class="table mt-4">
86 <thead>
87 <tr>
88 <th>endpoint</th>
89 <th>status</th>
90 <th>last bundle</th>
91 <th>mempool</th>
92 <th>head</th>
93 <th>root</th>
94 <th>version</th>
95 <th>latency</th>
96 </tr>
97 </thead>
98 <tbody>
99 {#each instances as instance}
100 <tr>
101 <td><a href={instance.url} target="_blank" class="font-semibold">{instance.url.replace("https://", "")}</a></td>
102 <td>{#if instance.status?.bundles?.last_bundle === lastKnownBundle.number}✅{:else if instance.status}🔄{:else}⌛{/if}</td>
103 <td>{#if instance.status?.bundles?.last_bundle}{instance.status?.bundles?.last_bundle}{/if}</td>
104 <td>{#if instance.status?.mempool && instance.status?.bundles?.last_bundle === lastKnownBundle.number}{instance.status?.mempool.count}{:else if instance.status}<span class="opacity-25">syncing</span>{/if}</td>
105 <td><span class="font-mono text-xs">{#if instance.status?.bundles?.head_hash}{instance.status?.bundles?.head_hash.slice(0, 7)}{/if}</span></td>
106 <td><span class="font-mono text-xs">{#if instance.status?.bundles?.root_hash}{instance.status?.bundles?.root_hash.slice(0, 7)}{/if}</span></td>
107 <td>{#if instance.status?.server?.version}{instance.status?.server?.version}{/if}</td>
108 <td class="opacity-50">{#if instance.status?.responseTime}{Math.round(instance.status?.responseTime)}ms{/if}</td>
109 </tr>
110 {/each}
111 </tbody>
112 </table>
113
114 <div class="mt-12 opacity-50">
115 Source: <a href="https://tangled.org/@tree.fail/plcbundle-watch">https://tangled.org/@tree.fail/plcbundle-watch</a>
116 </div>
117 </div>
118</main>
119