SPB Git

spb/coinexplorer Public MIT

Self-hosted, zero-API-key explorer for stablecoins and major crypto.

Python 60.3% HTML 23.6% JavaScript 8.1% CSS 6.8% SQL 1%
3.6 KB · 76 lines html
Raw Blame History
1<!doctype html>2<!-- Author: Simon-Pierre Boucher -->3<!-- Mail: contact@spboucher.ai -->4<html lang="en">5<head>6<meta charset="utf-8"><meta name="viewport" content="width=device-width, initial-scale=1">7<title>status — coinexplorer</title>8<link rel="stylesheet" href="/assets/style.css">9</head>10<body>11<main>12  <div class="pagehead"><h1>Indexer status</h1>13    <span class="sub">per-chain progress, coverage and free-RPC endpoint health ·14      <a href="/metrics">Prometheus</a></span></div>1516  <section class="card tile"><h2>Transfers indexed</h2><div class="big" id="tTotal">—</div></section>17  <section class="card tile"><h2>Chains at head</h2><div class="big" id="tHead">—</div>18    <div class="note">lag ≤ 100 blocks</div></section>19  <section class="card tile"><h2>Healthy endpoints</h2><div class="big" id="tEp">—</div>20    <div class="note">score ≥ 0.8</div></section>21  <section class="card tile"><h2>Benched endpoints</h2><div class="big" id="tBench">—</div>22    <div class="note">currently cooling down</div></section>2324  <section class="card"><h2>Chains</h2>25    <div class="scroll" style="max-height:55vh"><table>26      <thead><tr><th>chain</th><th class="num">indexed</th><th class="num">cursor</th>27        <th class="num">head</th><th class="num">lag</th><th class="num">floor</th></tr></thead>28      <tbody id="chains"></tbody></table></div></section>2930  <section class="card"><h2>RPC endpoints</h2>31    <div class="scroll" style="max-height:55vh"><table>32      <thead><tr><th>chain</th><th>endpoint</th><th class="num">score</th><th class="num">ok</th>33        <th class="num">fail</th><th class="num">latency</th><th class="num">cooldown</th></tr></thead>34      <tbody id="rpc"></tbody></table></div></section>35</main>3637<script type="module">38import { $, j, fmtN, mountNav, chainLink } from "/assets/app.js";39mountNav("Status");4041async function load() {42  const [status, health] = await Promise.all([j("/v1/status"), j("/v1/rpc/health")]);43  $("tTotal").textContent = fmtN(Object.values(status.indexed_transfers).reduce((a, b) => a + b, 0));44  const cs = Object.entries(status.cursors);45  $("tHead").textContent = `${cs.filter(([, c]) => c.lag != null && c.lag <= 100).length}/${cs.length}`;46  const eps = Object.values(health).flat();47  $("tEp").textContent = `${eps.filter((e) => e.score >= 0.8).length}/${eps.length}`;48  $("tBench").textContent = eps.filter((e) => e.cooldown_s > 0).length;4950  $("chains").innerHTML = cs.sort((a, b) =>51    (status.indexed_transfers[b[0]] || 0) - (status.indexed_transfers[a[0]] || 0))52    .map(([c, cur]) => {53      const lag = cur.lag;54      const cls = lag == null ? "" : lag > 1000 ? "burn" : lag > 100 ? "" : "mint";55      return `<tr><td>${chainLink(c)}</td>56        <td class="num">${fmtN(status.indexed_transfers[c] || 0)}</td>57        <td class="num">${fmtN(cur.last_block)}</td>58        <td class="num">${cur.head_block ? fmtN(cur.head_block) : "—"}</td>59        <td class="num ${cls}">${lag != null ? fmtN(lag) : "—"}</td>60        <td class="num">${cur.backfill_block != null ? fmtN(cur.backfill_block) : "—"}</td></tr>`;61    }).join("");6263  $("rpc").innerHTML = Object.entries(health).flatMap(([c, list]) => list.map((e) =>64    `<tr><td>${chainLink(c)}</td>65     <td class="mono" style="max-width:none" title="${e.url}">${e.url}</td>66     <td class="num ${e.score >= 0.8 ? "mint" : e.score < 0.5 ? "burn" : ""}">${e.score}</td>67     <td class="num">${fmtN(e.ok)}</td><td class="num">${fmtN(e.fail)}</td>68     <td class="num">${e.latency_ms} ms</td>69     <td class="num">${e.cooldown_s > 0 ? e.cooldown_s + "s" : "—"}</td></tr>`)).join("");70}71load();72setInterval(load, 15_000);73</script>74</body>75</html>76