tweaks

Changed files
+48 -11
src
+48 -11
src/App.svelte
··· 6 import instancesData from './instances.json'; 7 import numeral from 'numeral'; 8 9 const AUTO_REFRESH_INTERVAL = 15 // in seconds 10 const BUNDLE_OPS = 10_000 11 ··· 25 26 let isUpdating = $state(false) 27 let canRefresh = $state(true) 28 let lastUpdated = $state(new Date()) 29 let autoRefreshEnabled = $state(true) 30 let instances = $state(instancesData.sort(() => Math.random() - 0.5)) 31 32 - const instanceOrderBy = [['status.head', 'status.latency'], ['desc', 'asc']] 33 34 function formatNumber(n: number) { 35 return numeral(n).format() ··· 60 if (statusResp) { 61 statusResp.latency = performance.now() - start; 62 } 63 return statusResp 64 } 65 66 async function doCheck() { 67 isUpdating = true 68 canRefresh = false ··· 70 i.status = undefined 71 } 72 73 await Promise.all(instances.map(async (instance) => { 74 const status = await getStatus(instance) 75 - 76 instance.status = status 77 - instance.status.head = status?.bundles?.last_bundle > lastKnownBundle.number 78 - if (instance.status.head) { 79 lastKnownBundle.number = status?.bundles?.last_bundle 80 lastKnownBundle.hash = status?.bundles?.head_hash 81 lastKnownBundle.time = status?.bundles?.end_time ··· 87 } 88 } 89 lastUpdated = new Date() 90 })) 91 isUpdating = false 92 setTimeout(() => (canRefresh = false), 1000) 93 } 94 95 - onMount(() => { 96 - doCheck() 97 98 setTimeout(() => { 99 if (autoRefreshEnabled) { ··· 128 <div> 129 <div class="flex items-center gap-5"> 130 <div class="font-semibold text-3xl">{lastKnownBundle.number}</div> 131 - <div class="mt-1 font-mono badge preset-outlined-primary-500 text-xs">{lastKnownBundle?.hash?.slice(0, 7)}</div> 132 </div> 133 <div> 134 <span class="opacity-50">{#if lastKnownBundle?.time} {formatDistanceToNow(lastKnownBundle.time, { addSuffix: true })}{/if}</span> ··· 179 {#each orderBy(instances, ...instanceOrderBy) as instance} 180 <tr> 181 <td><a href={instance.url} target="_blank" class="font-semibold">{instance.url.replace("https://", "")}</a></td> 182 - <td>{#if instance.status?.bundles?.last_bundle === lastKnownBundle.number}✅{:else if instance.status}🔄{:else}⌛{/if}</td> 183 <td>{#if instance.status?.bundles?.last_bundle}{instance.status?.bundles?.last_bundle}{/if}</td> 184 <td>{#if instance.status?.mempool && instance.status?.bundles?.last_bundle === lastKnownBundle.number}{formatNumber(instance.status?.mempool.count)}{:else if instance.status}<span class="opacity-25">syncing</span>{/if}</td> 185 - <td><span class="font-mono text-xs">{#if instance.status?.bundles?.head_hash}{instance.status?.bundles?.head_hash.slice(0, 7)}{/if}</span></td> 186 - <td><span class="font-mono text-xs">{#if instance.status?.bundles?.root_hash}{instance.status?.bundles?.root_hash.slice(0, 7)}{/if}</span></td> 187 <td>{#if instance.status?.server?.version}{instance.status?.server?.version}{/if}</td> 188 <td class="opacity-50">{#if instance.status?.latency}{Math.round(instance.status?.latency)}ms{/if}</td> 189 </tr> ··· 191 </tbody> 192 </table> 193 194 - <div class="mt-12 opacity-50"> 195 <div> 196 Last updated: {formatISO9075(lastUpdated)} 197 </div> ··· 199 Source: <a href="https://tangled.org/@tree.fail/plcbundle-watch">https://tangled.org/@tree.fail/plcbundle-watch</a> 200 </div> 201 </div> 202 </div> 203 </main> 204
··· 6 import instancesData from './instances.json'; 7 import numeral from 'numeral'; 8 9 + const PLC_DIRECTORY = 'plc.directory' 10 + const ROOT = 'cbab6809a136d6a621906ee11199d3b0faf85b422fe0d0d2c346ce8e9dcd7485' 11 const AUTO_REFRESH_INTERVAL = 15 // in seconds 12 const BUNDLE_OPS = 10_000 13 ··· 27 28 let isUpdating = $state(false) 29 let canRefresh = $state(true) 30 + let isConflict = $state(false) 31 let lastUpdated = $state(new Date()) 32 let autoRefreshEnabled = $state(true) 33 let instances = $state(instancesData.sort(() => Math.random() - 0.5)) 34 35 + const instanceOrderBy = [['_head', 'status.latency'], ['desc', 'asc']] 36 37 function formatNumber(n: number) { 38 return numeral(n).format() ··· 63 if (statusResp) { 64 statusResp.latency = performance.now() - start; 65 } 66 + //if (instance.url === 'https://plc.j4ck.xyz') { statusResp.bundles.head_hash = 'f3ad3544452b2c078cba24990486bb9c277a1155'; } 67 return statusResp 68 } 69 70 + function recalculateHead() { 71 + isConflict = false 72 + const headHashes = [] 73 + for (const instance of instances) { 74 + instance._head = instance.status?.bundles?.last_bundle === lastKnownBundle.number 75 + if (instance._head) { 76 + headHashes.push(instance.status?.bundles?.head_hash) 77 + } 78 + } 79 + isConflict = [...new Set(headHashes)].length > 1 80 + } 81 + 82 async function doCheck() { 83 isUpdating = true 84 canRefresh = false ··· 86 i.status = undefined 87 } 88 89 + const statuses = [] 90 + 91 await Promise.all(instances.map(async (instance) => { 92 const status = await getStatus(instance) 93 instance.status = status 94 + if (status?.bundles?.last_bundle > lastKnownBundle.number) { 95 lastKnownBundle.number = status?.bundles?.last_bundle 96 lastKnownBundle.hash = status?.bundles?.head_hash 97 lastKnownBundle.time = status?.bundles?.end_time ··· 103 } 104 } 105 lastUpdated = new Date() 106 + 107 + recalculateHead() 108 })) 109 + 110 isUpdating = false 111 setTimeout(() => (canRefresh = false), 1000) 112 } 113 114 + onMount(async () => { 115 + await doCheck() 116 117 setTimeout(() => { 118 if (autoRefreshEnabled) { ··· 147 <div> 148 <div class="flex items-center gap-5"> 149 <div class="font-semibold text-3xl">{lastKnownBundle.number}</div> 150 + {#if !isConflict} 151 + <div class="mt-1 font-mono badge preset-outlined-primary-500 text-xs">{lastKnownBundle?.hash?.slice(0, 7)}</div> 152 + {:else} 153 + <div class="mt-1 badge preset-filled-error-500">⚠️ conflict!</div> 154 + {/if} 155 </div> 156 <div> 157 <span class="opacity-50">{#if lastKnownBundle?.time} {formatDistanceToNow(lastKnownBundle.time, { addSuffix: true })}{/if}</span> ··· 202 {#each orderBy(instances, ...instanceOrderBy) as instance} 203 <tr> 204 <td><a href={instance.url} target="_blank" class="font-semibold">{instance.url.replace("https://", "")}</a></td> 205 + <td>{#if instance._head}{#if isConflict}⚠️{:else}✅{/if}{:else if instance.status}🔄{:else}⌛{/if}</td> 206 <td>{#if instance.status?.bundles?.last_bundle}{instance.status?.bundles?.last_bundle}{/if}</td> 207 <td>{#if instance.status?.mempool && instance.status?.bundles?.last_bundle === lastKnownBundle.number}{formatNumber(instance.status?.mempool.count)}{:else if instance.status}<span class="opacity-25">syncing</span>{/if}</td> 208 + <td><span class="font-mono text-xs {instance._head ? (isConflict ? 'text-error-600' : 'text-success-600') : 'opacity-50'}">{#if instance.status?.bundles?.head_hash}{instance.status?.bundles?.head_hash.slice(0, 7)}{/if}</span></td> 209 + <td><span class="font-mono text-xs {instance.status ? (instance.status?.bundles?.root_hash === ROOT ? 'text-success-600' : 'text-error-600') : ''}">{#if instance.status?.bundles?.root_hash}{instance.status?.bundles?.root_hash.slice(0, 7)}{/if}</span></td> 210 <td>{#if instance.status?.server?.version}{instance.status?.server?.version}{/if}</td> 211 <td class="opacity-50">{#if instance.status?.latency}{Math.round(instance.status?.latency)}ms{/if}</td> 212 </tr> ··· 214 </tbody> 215 </table> 216 217 + 218 + <div class="mt-12"> 219 + <div> 220 + <span class="opacity-75">PLC Directory:</span> <a href="https://{PLC_DIRECTORY}">{PLC_DIRECTORY}</a> <span class="opacity-50">(origin)</span> 221 + </div> 222 + <div class="mt-2"> 223 + <span class="opacity-75">Root:</span> <span class="font-mono text-xs">{ROOT.slice(0)}</span> 224 + </div> 225 + </div> 226 + 227 + <hr class="hr mt-6" /> 228 + <div class="mt-2 opacity-50"> 229 <div> 230 Last updated: {formatISO9075(lastUpdated)} 231 </div> ··· 233 Source: <a href="https://tangled.org/@tree.fail/plcbundle-watch">https://tangled.org/@tree.fail/plcbundle-watch</a> 234 </div> 235 </div> 236 + 237 + 238 + 239 </div> 240 </main> 241