koopa-admin-log/configs/exchange-landing/index.html
Hernâni Marques d81e452ffb
landings: QR label “Scan in wallet”
Shorter than “Scan or open in wallet” — enough next to the open CTA.
2026-07-10 20:47:25 +02:00

443 lines
25 KiB
HTML
Raw Blame History

This file contains ambiguous Unicode characters

This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.

<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="utf-8" />
<meta name="viewport" content="width=device-width, initial-scale=1" />
<title>GOA Exchange</title>
<base href="/intro/" />
<style>
:root {
--bg: #1a1410; --card: rgba(42, 32, 24, 0.82); --text: #fff6e8; --muted: #c9b8a0;
--accent2: #3ecfbf; --border: rgba(232, 168, 56, 0.28);
--glow-gold: rgba(232, 168, 56, 0.35); --glow-teal: rgba(62, 207, 191, 0.22);
--val: #f0d090;
}
* { box-sizing: border-box; }
body {
margin: 0; min-height: 100vh;
font-family: system-ui, -apple-system, "Segoe UI", Roboto, sans-serif;
color: var(--text); line-height: 1.55; background-color: var(--bg);
background-image:
radial-gradient(ellipse 90% 55% at 50% 108%, rgba(26, 107, 110, 0.55) 0%, transparent 55%),
radial-gradient(circle 420px at 12% 18%, var(--glow-gold) 0%, transparent 62%),
linear-gradient(165deg, #2c1e14 0%, #1a1410 38%, #12181a 72%, #0e1c1e 100%);
background-attachment: fixed;
}
main { max-width: 42rem; margin: 0 auto; padding: 2.25rem 1.25rem 3.5rem; }
.hero { text-align: center; margin: 0 0 1.25rem; }
h1 {
font-size: 1.85rem; font-weight: 750; margin: 0 0 0.45rem;
background: linear-gradient(120deg, #fff6e8 0%, #f0c86a 45%, #3ecfbf 120%);
-webkit-background-clip: text; background-clip: text; color: transparent;
}
.sub { color: var(--muted); max-width: 32rem; margin: 0 auto; }
.badge {
display: inline-block; font-size: 0.75rem; font-weight: 700; letter-spacing: 0.04em;
padding: 0.2rem 0.7rem; border-radius: 999px; margin-bottom: 0.75rem;
border: 1px solid rgba(232, 168, 56, 0.4); color: #f0d090;
background: linear-gradient(135deg, rgba(232, 168, 56, 0.2), rgba(62, 207, 191, 0.15));
}
.stats {
background: rgba(18, 28, 28, 0.55); border: 1px solid rgba(62, 207, 191, 0.28);
border-radius: 16px; padding: 1.1rem 1.15rem; margin: 0 0 1.1rem;
}
.stats h2 {
margin: 0 0 0.35rem; font-size: 0.78rem; font-weight: 700; letter-spacing: 0.12em;
text-transform: uppercase; color: var(--muted); text-align: center;
}
.note { text-align: center; font-size: 0.82rem; color: var(--muted); margin: 0 0 0.9rem; }
.stats-grid { display: grid; grid-template-columns: 1fr 1fr; gap: 0.65rem; }
@media (min-width: 420px) { .stats-grid { grid-template-columns: 1fr 1fr 1fr; } }
.stat { background: rgba(0,0,0,0.22); border-radius: 10px; padding: 0.55rem 0.65rem; }
.stat .k { display: block; font-size: 0.68rem; color: var(--muted); text-transform: uppercase; text-align: center; margin-bottom: 0.15rem; letter-spacing: 0.04em; }
.stat .v { display: block; font-size: 1.05rem; font-weight: 750; color: var(--val); text-align: center; font-variant-numeric: tabular-nums; word-break: break-word; }
.stat .v.teal { color: #5eead4; }
.stat .v.sm { font-size: 0.92rem; }
.stats-foot { margin: 0.75rem 0 0; font-size: 0.72rem; color: var(--muted); text-align: center; }
.stats-foot.err { color: #f0a090; }
.stats-run-note {
margin: 0.35rem 0 0; font-size: 0.7rem; color: #f0a090; text-align: center;
}
.sub-block {
margin-top: 0.85rem; padding: 0.75rem 0.85rem;
border-radius: 12px; border: 1px solid rgba(232, 168, 56, 0.22);
background: rgba(0,0,0,0.18);
}
.sub-block h3 {
margin: 0 0 0.5rem; font-size: 0.78rem; font-weight: 700; letter-spacing: 0.1em;
text-transform: uppercase; color: var(--muted); text-align: center;
}
.ladder {
display: flex; flex-wrap: wrap; gap: 0.35rem; justify-content: center;
}
.pill {
font-size: 0.72rem; font-weight: 700; padding: 0.18rem 0.45rem; border-radius: 999px;
border: 1px solid rgba(62, 207, 191, 0.35); color: #99f6e4;
background: rgba(62, 207, 191, 0.1); font-variant-numeric: tabular-nums;
}
.by-denom { font-size: 0.82rem; color: var(--muted); text-align: center; }
.by-denom table { width: 100%; border-collapse: collapse; margin-top: 0.35rem; }
.by-denom th, .by-denom td {
padding: 0.28rem 0.35rem; border-bottom: 1px solid rgba(255,255,255,0.06);
text-align: center; font-variant-numeric: tabular-nums;
}
.by-denom th { color: var(--muted); font-size: 0.68rem; text-transform: uppercase; font-weight: 700; }
.by-denom td { color: var(--val); font-weight: 650; }
.step-card {
background: var(--card); border: 1px solid var(--border); border-radius: 18px;
padding: 1.3rem 1.25rem; margin-bottom: 1.2rem;
}
.step-card h2 { margin: 0 0 0.4rem; text-align: center; font-size: 1.2rem; }
.step-tagline { text-align: center; color: var(--muted); margin: 0 0 1rem; }
.step-body { display: flex; flex-direction: column; align-items: center; gap: 1rem; }
.qr-box { background: #fff; padding: 0.85rem; border-radius: 14px; text-align: center; }
.qr-label { margin-top: 0.5rem; color: #3a342c; font-weight: 700; font-size: 0.85rem; }
.cta {
display: inline-block; text-decoration: none; font-weight: 750; padding: 0.7rem 1.15rem;
border-radius: 12px; background: linear-gradient(135deg, #f0c86a, #c47a18); color: #1a1208;
}
.cta.teal { background: linear-gradient(135deg, #5eead4, #0d9488); color: #042f2e; }
.links { display: flex; flex-direction: column; gap: 0.5rem; width: 100%; }
.cta-sec {
display: block; text-align: center; text-decoration: none; color: var(--accent2);
border: 1px solid rgba(62, 207, 191, 0.4); padding: 0.55rem; border-radius: 10px; font-weight: 600;
}
.meta { font-size: 0.78rem; color: var(--muted); word-break: break-all; text-align: center; }
.app-links {
display: flex; flex-wrap: wrap; gap: 0.5rem; justify-content: center; width: 100%;
}
.app-btn {
display: inline-flex; align-items: center; justify-content: center; gap: 0.45rem;
min-width: 9rem; text-decoration: none; font-weight: 700; font-size: 0.88rem;
padding: 0.55rem 0.85rem; border-radius: 10px;
border: 1px solid rgba(232, 168, 56, 0.4); background: rgba(0,0,0,0.28); color: #f0d090;
}
.app-btn.ios { border-color: rgba(62, 207, 191, 0.4); color: #b8f5ec; }
.app-btn.hub {
flex: 1 1 100%; border-color: rgba(201, 184, 160, 0.35); color: var(--muted);
font-weight: 600; font-size: 0.82rem; gap: 0.5rem;
}
.app-ico { width: 1.15rem; height: 1.15rem; flex-shrink: 0; display: block; }
.app-ico:not(.browser-ico) path,
.app-ico:not(.browser-ico) circle,
.app-ico:not(.browser-ico) rect { fill: currentColor; }
.app-ico.stroke-ico path, .app-ico.stroke-ico circle, .app-ico.stroke-ico rect {
fill: none; stroke: currentColor; stroke-width: 1.6; stroke-linecap: round; stroke-linejoin: round;
}
.app-btn:not(.ios):not(.hub) .app-ico { color: #f0c86a; }
.app-btn.ios .app-ico { color: #5eead4; }
.hub-icons { display: inline-flex; align-items: center; gap: 0.4rem; }
.hub-icons .app-ico { width: 1.25rem; height: 1.25rem; }
.hub-icons .browser-ico { border-radius: 3px; flex-shrink: 0; }
.step-tagline .app-ico {
width: 1rem; height: 1rem; display: inline-block; vertical-align: -0.15em; margin: 0 0.12em;
}
.step-tagline .app-ico.android { color: #f0c86a; }
.step-tagline .app-ico.ios { color: #5eead4; }
.step-body ol { margin: 0; padding-left: 1.2rem; width: 100%; align-self: stretch; color: var(--muted); }
.step-body li { margin: 0.35rem 0; }
footer { margin-top: 1.75rem; text-align: center; color: var(--muted); font-size: 0.85rem; }
footer a { color: #e8c878; }
code { font-family: ui-monospace, Menlo, monospace; font-size: 0.86em; background: rgba(0,0,0,0.35); padding: 0.1em 0.35em; border-radius: 4px; color: #f0d090; }
</style>
<script src="/intro/qrcode.min.js"></script>
</head>
<body>
<main>
<header class="hero">
<span class="badge">GOA · Exchange</span>
<h1>GOA Exchange</h1>
<p class="sub">Explorational exchange — coins, denoms, one-step withdraw.</p>
</header>
<section class="stats" id="stats-coins" aria-labelledby="coins-title">
<h2 id="coins-title">Coins</h2>
<p class="note">From exchange DB · coin lifecycle</p>
<div class="stats-grid">
<div class="stat"><span class="k">Known coins</span><span class="v teal" id="st-coins"></span></div>
<div class="stat"><span class="k">Live (remaining)</span><span class="v" id="st-coins-live"></span></div>
<div class="stat"><span class="k">Spent / zero</span><span class="v" id="st-coins-spent"></span></div>
<div class="stat"><span class="k">Remaining amt</span><span class="v teal sm" id="st-remaining"></span></div>
<div class="stat"><span class="k">Withdraw ops</span><span class="v" id="st-withdraw"></span></div>
<div class="stat"><span class="k">Withdraw amt</span><span class="v teal sm" id="st-withdraw-amt"></span></div>
<div class="stat"><span class="k">Refresh</span><span class="v" id="st-refresh"></span></div>
<div class="stat"><span class="k">Coin deposits</span><span class="v" id="st-coin-dep"></span></div>
<div class="stat"><span class="k">Batch deposits</span><span class="v" id="st-batch-dep"></span></div>
<div class="stat"><span class="k">Recoup</span><span class="v" id="st-recoup"></span></div>
<div class="stat"><span class="k">Refunds</span><span class="v" id="st-refunds"></span></div>
<div class="stat"><span class="k">History events</span><span class="v" id="st-hist"></span></div>
</div>
<div class="sub-block" id="by-denom-wrap" hidden>
<h3>Known coins by value</h3>
<div class="by-denom" id="st-by-denom"></div>
</div>
</section>
<section class="stats" id="stats-denoms" aria-labelledby="denoms-title">
<h2 id="denoms-title">Denominations</h2>
<p class="note">Signed keys in DB · public ladder on <code>/keys</code></p>
<div class="stats-grid">
<div class="stat"><span class="k">Denom keys</span><span class="v teal" id="st-denoms"></span></div>
<div class="stat"><span class="k">Unique values</span><span class="v" id="st-denom-vals"></span></div>
<div class="stat"><span class="k">Withdrawable now</span><span class="v teal" id="st-denoms-wd"></span></div>
</div>
<div class="sub-block">
<h3>Value ladder</h3>
<div class="ladder" id="st-ladder"></div>
</div>
</section>
<section class="stats" id="stats-wire" aria-labelledby="wire-title">
<h2 id="wire-title">Reserves · wire</h2>
<p class="note">Backing money movement</p>
<div class="stats-grid">
<div class="stat"><span class="k">Reserves</span><span class="v teal" id="st-reserves"></span></div>
<div class="stat"><span class="k">Wire in</span><span class="v" id="st-wire-in-n"></span></div>
<div class="stat"><span class="k">Wire in amt</span><span class="v teal sm" id="st-wire-in-amt"></span></div>
<div class="stat"><span class="k">Wire out</span><span class="v" id="st-wire-out"></span></div>
</div>
<p class="stats-foot" id="st-foot">Loading…</p>
<p class="stats-run-note" id="st-run-note" hidden></p>
</section>
<section class="stats" id="stats-perf" aria-labelledby="perf-title">
<h2 id="perf-title">Performance</h2>
<p class="note">Live HTTP probes + container memory (RSS)</p>
<div class="stats-grid">
<div class="stat"><span class="k">/keys</span><span class="v teal" id="st-keys-ms"></span></div>
<div class="stat"><span class="k">/config</span><span class="v" id="st-config-ms"></span></div>
<div class="stat"><span class="k">Loadavg</span><span class="v sm" id="st-load"></span></div>
<div class="stat"><span class="k">Container</span><span class="v teal" id="st-mem-ctr"></span></div>
<div class="stat"><span class="k">PostgreSQL</span><span class="v" id="st-mem-pg"></span></div>
<div class="stat"><span class="k">Taler procs</span><span class="v" id="st-mem-taler"></span></div>
<div class="stat"><span class="k">Nginx</span><span class="v sm" id="st-mem-nginx"></span></div>
</div>
<div class="sub-block" style="margin-top:0.85rem">
<h3>Top processes (RSS)</h3>
<div id="st-mem-top" class="by-denom"></div>
</div>
<p class="stats-foot" id="st-perf-foot">Memory from /proc + cgroup inside podman container.</p>
</section>
<section class="step-card">
<h2>Get the wallet</h2>
<div class="step-body">
<div class="app-links">
<a class="app-btn" href="https://play.google.com/store/apps/details?id=net.taler.wallet" target="_blank" rel="noopener noreferrer" aria-label="Android Play Store">
<svg class="app-ico" viewBox="0 0 24 24" aria-hidden="true"><path d="M8.2 2.4l-.9-1.6c-.1-.2 0-.4.2-.5.2-.1.4 0 .5.2l.9 1.6c.8-.3 1.7-.5 2.6-.5s1.8.2 2.6.5l.9-1.6c.1-.2.3-.3.5-.2.2.1.3.3.2.5l-.9 1.6c1.8.8 3.1 2.5 3.3 4.6H4.9c.2-2.1 1.5-3.8 3.3-4.6zM7 8.5c.6 0 1 .4 1 1s-.4 1-1 1-1-.4-1-1 .4-1 1-1zm10 0c.6 0 1 .4 1 1s-.4 1-1 1-1-.4-1-1 .4-1 1-1zM5 10.5h14c.6 0 1 .4 1 1v6.2c0 1.3-1 2.3-2.3 2.3h-.5v2.2c0 .5-.4.9-.9.9s-.9-.4-.9-.9v-2.2h-4.8v2.2c0 .5-.4.9-.9.9s-.9-.4-.9-.9v-2.2h-.5C5.9 20 5 19 5 17.7v-6.2c0-.6.4-1 1-1z"/></svg>
Play Store
</a>
<a class="app-btn" href="https://f-droid.org/en/packages/net.taler.wallet.fdroid/" target="_blank" rel="noopener noreferrer" aria-label="Android F-Droid">
<svg class="app-ico" viewBox="0 0 24 24" aria-hidden="true"><path d="M8.2 2.4l-.9-1.6c-.1-.2 0-.4.2-.5.2-.1.4 0 .5.2l.9 1.6c.8-.3 1.7-.5 2.6-.5s1.8.2 2.6.5l.9-1.6c.1-.2.3-.3.5-.2.2.1.3.3.2.5l-.9 1.6c1.8.8 3.1 2.5 3.3 4.6H4.9c.2-2.1 1.5-3.8 3.3-4.6zM7 8.5c.6 0 1 .4 1 1s-.4 1-1 1-1-.4-1-1 .4-1 1-1zm10 0c.6 0 1 .4 1 1s-.4 1-1 1-1-.4-1-1 .4-1 1-1zM5 10.5h14c.6 0 1 .4 1 1v6.2c0 1.3-1 2.3-2.3 2.3h-.5v2.2c0 .5-.4.9-.9.9s-.9-.4-.9-.9v-2.2h-4.8v2.2c0 .5-.4.9-.9.9s-.9-.4-.9-.9v-2.2h-.5C5.9 20 5 19 5 17.7v-6.2c0-.6.4-1 1-1z"/></svg>
F-Droid
</a>
<a class="app-btn ios" href="https://apps.apple.com/app/taler-wallet/id6463440117" target="_blank" rel="noopener noreferrer" aria-label="iOS App Store">
<svg class="app-ico" viewBox="0 0 24 24" aria-hidden="true"><path d="M16.7 12.6c0-2.1 1.7-3.1 1.8-3.2-1-1.4-2.5-1.6-3-1.7-1.3-.1-2.5.8-3.1.8s-1.6-.7-2.7-.7c-1.4 0-2.7.8-3.4 2.1-1.5 2.5-.4 6.3 1 8.3.7 1 1.5 2.1 2.6 2 1-.1 1.5-.7 2.8-.7s1.7.7 2.8.7 1.9-1 2.6-2c.8-1.1 1.1-2.2 1.1-2.3-.1 0-2.2-.8-2.2-3.3zm-2-6.2c.6-.7 1-1.7.9-2.7-1 .1-2.1.6-2.8 1.4-.6.7-1.1 1.7-.9 2.6 1 .1 2-.5 2.8-1.3z"/></svg>
App Store
</a>
<a class="app-btn hub" href="https://wallet.taler.net/" target="_blank" rel="noopener noreferrer" aria-label="Web browsers (experimental): Chrome, Firefox, Edge, Brave">
<span class="hub-icons" aria-hidden="true" title="Chrome · Firefox · Edge · Brave">
<svg class="app-ico browser-ico" viewBox="0 0 24 24" role="img" aria-label="Chrome">
<circle cx="12" cy="12" r="10" fill="#DEE1E6"/>
<path fill="#EA4335" d="M12 2a10 10 0 0 1 8.66 5H12a4 4 0 0 0-3.46 2L5.1 4.1A10 10 0 0 1 12 2z"/>
<path fill="#FBBC04" d="M20.66 7A10 10 0 0 1 12 22l3.46-6A4 4 0 0 0 12 8h8.66z"/>
<path fill="#34A853" d="M5.1 4.1 8.54 9A4 4 0 0 0 12 16l-3.46 6A10 10 0 0 1 5.1 4.1z"/>
<circle cx="12" cy="12" r="4" fill="#fff"/>
<circle cx="12" cy="12" r="3.1" fill="#4285F4"/>
</svg>
<svg class="app-ico browser-ico" viewBox="0 0 24 24" role="img" aria-label="Firefox">
<circle cx="12" cy="12.2" r="8.2" fill="#9059FF"/>
<path fill="#FF7139" d="M20.2 9.2c-.2-1.4-1.1-2.6-2.2-3.4.5 1.1.6 2.3.3 3.4-.9-1.6-2.5-2.6-4.4-2.6-1.3 0-2.5.4-3.4 1.1C9.3 5.4 10.9 3.8 13 3.2c-2.5.1-4.7 1.4-6.1 3.4C5.3 8.2 4.6 10.2 4.8 12.3c.3 3.9 3.5 7 7.4 7.1 3.6.1 6.7-2.2 7.7-5.5.4-1.4.4-2.9.3-4.7z"/>
<path fill="#FFD567" d="M12.1 9.4c1.6 0 2.9.9 3.5 2.2-.7-.4-1.5-.6-2.4-.6-2.2 0-4 1.6-4.2 3.7-.5-.8-.7-1.8-.6-2.8.4-1.5 1.8-2.5 3.7-2.5z"/>
<circle cx="12.2" cy="13.4" r="2.35" fill="#20123A"/>
</svg>
<svg class="app-ico browser-ico" viewBox="0 0 24 24" role="img" aria-label="Edge">
<defs>
<linearGradient id="edge-g-ex" x1="4" y1="4" x2="20" y2="20" gradientUnits="userSpaceOnUse">
<stop stop-color="#0C59A4"/>
<stop offset="0.55" stop-color="#1B9DE2"/>
<stop offset="1" stop-color="#36C5F0"/>
</linearGradient>
</defs>
<circle cx="12" cy="12" r="10" fill="url(#edge-g-ex)"/>
<path fill="#fff" d="M6.2 14.6c.6 3.1 3.3 5.4 6.6 5.4 3.7 0 6.7-2.7 7.1-6.2-1.4 1.7-3.6 2.7-6 2.7-1.5 0-2.9-.4-4-1.2.6.1 1.2.2 1.8.2 3.4 0 5.9-1.9 5.9-4.6 0-.7-.2-1.3-.5-1.9-1.4-2.5-4.3-3.6-7.2-2.7C7.1 7.2 5.3 9.6 5.3 12.3c0 .8.2 1.6.5 2.3h.4z"/>
<path fill="#9CE4FA" opacity="0.9" d="M12.8 8.1c1.9 0 3.5 1.1 3.5 2.8 0 1.9-1.8 3.2-4.4 3.2-1.4 0-2.6-.4-3.5-1 1.5 2.1 4.1 3.2 6.7 2.8 2.2-.3 3.9-1.9 4.4-4-1.1 1.1-2.7 1.7-4.4 1.7-1.6 0-2.9-.7-2.9-1.9 0-1.1 1-1.9 2.5-2.1.4-.1.7-.3.9-.5-.8-.6-1.7-.9-2.8-.9z"/>
</svg>
<svg class="app-ico browser-ico" viewBox="0 0 24 24" role="img" aria-label="Brave">
<path fill="#FB542B" d="M12 2.2 4.8 5.1v5.6c0 5.1 3.4 9.8 7.2 11.1 3.8-1.3 7.2-6 7.2-11.1V5.1L12 2.2z"/>
<path fill="#fff" d="M12 5.1 8.1 6.7l.7 1.5L7.2 10l1.5.9L7.6 13.2l2.2.4L12 18.2l2.2-4.6 2.2-.4-1.1-2.3 1.5-.9-1.6-1.8.7-1.5L12 5.1z"/>
<path fill="#FB542B" d="M12 7.2 10.3 8l.5 1.1h2.4l.5-1.1L12 7.2zm-1.6 3.1L9.2 11.6l.9.5.4-1.8h-.1zm3.2 0h-.1l.4 1.8.9-.5-1.2-1.3zM10.4 13l-.5 1.1L12 16.1l2.1-2-.5-1.1h-3.2z"/>
</svg>
</span>
Web browsers (experimental)
</a>
</div>
</div>
</section>
<section class="step-card">
<h2>Withdraw GOA</h2>
<p class="step-tagline">One-step: add exchange + withdraw</p>
<div class="step-body">
<div class="qr-box">
<div id="qr-exchange"></div>
<div class="qr-label">Scan in wallet</div>
</div>
<a class="cta teal" href="taler://withdraw-exchange/exchange.hacktivism.ch/">Open in wallet →</a>
<p class="meta">taler://withdraw-exchange/exchange.hacktivism.ch/</p>
<div class="links">
<a class="cta-sec" href="https://bank.hacktivism.ch/intro/">Bank →</a>
<a class="cta-sec" href="https://exchange.hacktivism.ch/keys">/keys</a>
<a class="cta-sec" href="https://exchange.hacktivism.ch/terms">/terms</a>
<a class="cta-sec" href="https://exchange.hacktivism.ch/privacy">/privacy</a>
<a class="cta-sec" href="https://exchange.hacktivism.ch/config">/config</a>
</div>
</div>
</section>
<footer>
<a href="https://bank.hacktivism.ch/intro/">Bank</a> ·
<a href="https://exchange.hacktivism.ch/intro/">Exchange</a> ·
<a href="https://taler.hacktivism.ch/intro/">Merchant</a> ·
<a href="https://exchange.hacktivism.ch/terms">Terms</a> ·
<a href="https://exchange.hacktivism.ch/privacy">Privacy</a> ·
<a href="https://git.hacktivism.ch/hernani/koopa-admin-log/src/branch/main/configs/exchange-landing">Source</a>
<p class="privacy-note" style="margin:0.65rem 0 0;font-size:0.78rem;color:var(--muted);line-height:1.4">
Privacy: processing under Swiss FADP (revDSG). What is retained is listed on
<a href="https://exchange.hacktivism.ch/privacy" style="color:#5eead4">/privacy</a>.
</p>
</footer>
</main>
<script>
(function () {
function set(id, v) {
var el = document.getElementById(id);
if (el) el.textContent = v == null || v === "" ? "—" : String(v);
}
function msLabel(ms, http) {
if (ms == null || ms === "") return "—";
var s = ms + " ms";
if (http && http !== "200") s += " (" + http + ")";
return s;
}
fetch("/intro/stats.json", { cache: "no-store" })
.then(function (r) { if (!r.ok) throw new Error("x"); return r.json(); })
.then(function (d) {
if (!d || !d.ok) throw new Error("bad");
set("st-coins", d.known_coins);
set("st-coins-live", d.coins_live);
set("st-coins-spent", d.coins_spent);
set("st-remaining", d.coins_remaining_amount);
set("st-withdraw", d.withdraw_ops);
set("st-withdraw-amt", d.withdraw_amount);
set("st-refresh", d.refresh_ops);
set("st-coin-dep", d.coin_deposits);
set("st-batch-dep", d.batch_deposits);
set("st-recoup", d.recoup);
set("st-refunds", d.refunds);
set("st-hist", d.coin_history_events);
set("st-denoms", d.denominations);
set("st-denom-vals", d.denom_values);
set("st-denoms-wd", d.denoms_withdrawable);
set("st-reserves", d.reserves);
set("st-wire-in-n", d.wire_in_count);
set("st-wire-in-amt", d.wire_in_amount);
set("st-wire-out", d.wire_out);
var ladder = document.getElementById("st-ladder");
if (ladder) {
ladder.innerHTML = "";
(d.denom_ladder || []).forEach(function (x) {
var s = document.createElement("span");
s.className = "pill";
s.textContent = x.value + (x.keys > 1 ? " ×" + x.keys : "");
ladder.appendChild(s);
});
if (!(d.denom_ladder || []).length) ladder.textContent = "—";
}
var by = d.by_denom || [];
var wrap = document.getElementById("by-denom-wrap");
var box = document.getElementById("st-by-denom");
if (wrap && box && by.length) {
wrap.hidden = false;
var html = "<table><thead><tr><th>Value</th><th>Coins</th><th>Live</th></tr></thead><tbody>";
by.forEach(function (r) {
html += "<tr><td>" + r.value + "</td><td>" + r.coins + "</td><td>" + r.live + "</td></tr>";
});
html += "</tbody></table>";
box.innerHTML = html;
}
var foot = document.getElementById("st-foot");
if (foot) {
foot.textContent = (d.wirewatch_running ? "wirewatch on · " : "wirewatch off · ") +
(d.generated_at_human || d.generated_at || "");
}
var p = d.performance || {};
set("st-keys-ms", msLabel(p.keys_ms, p.keys_http));
set("st-config-ms", msLabel(p.config_ms, p.config_http));
set("st-load", p.loadavg || "—");
var mem = p.memory || {};
set("st-mem-ctr", mem.container_rss_human || "—");
set("st-mem-pg", mem.postgres_rss_human
? mem.postgres_rss_human + (mem.postgres_n ? " · " + mem.postgres_n + "p" : "")
: "—");
set("st-mem-taler", mem.taler_rss_human
? mem.taler_rss_human + (mem.taler_n ? " · " + mem.taler_n + "p" : "")
: "—");
set("st-mem-nginx", mem.nginx_rss_human || "—");
var topBox = document.getElementById("st-mem-top");
if (topBox) {
var tops = mem.top || [];
if (!tops.length) {
topBox.textContent = "—";
} else {
var th = "<table><thead><tr><th>RSS</th><th>Comm</th><th>Command</th></tr></thead><tbody>";
tops.forEach(function (t) {
th += "<tr><td>" + (t.rss_human || "?") + "</td><td>" +
(t.comm || "") + "</td><td style=\"font-size:0.72rem;word-break:break-all\">" +
(t.cmd || "") + "</td></tr>";
});
th += "</tbody></table>";
topBox.innerHTML = th;
}
}
var pf = document.getElementById("st-perf-foot");
if (pf) {
pf.textContent = "Container RSS + process groups · " +
(d.generated_at_human || d.generated_at || "");
}
return fetch("/intro/stats-run.json", { cache: "no-store" })
.then(function (r) { return r.ok ? r.json() : null; })
.catch(function () { return null; });
})
.then(function (run) {
var note = document.getElementById("st-run-note");
if (!note) return;
if (run && run.ok === false) {
note.hidden = false;
note.textContent = "Last refresh failed" +
(run.at_human ? " · " + run.at_human : "") +
" · showing last good data";
} else {
note.hidden = true;
note.textContent = "";
}
})
.catch(function () {
var foot = document.getElementById("st-foot");
if (foot) foot.innerHTML = 'Stats offline · <a href="https://bank.hacktivism.ch/intro/" style="color:#e8c878">bank.hacktivism.ch</a>';
});
var el = document.getElementById("qr-exchange");
if (el && typeof QRCode !== "undefined") {
new QRCode(el, {
text: "taler://withdraw-exchange/exchange.hacktivism.ch/",
width: 220, height: 220, correctLevel: QRCode.CorrectLevel.M
});
}
})();
</script>
</body>
</html>