exchange landing: intro page + nginx :9014
This commit is contained in:
parent
a3334dbcf8
commit
dd4a984912
3 changed files with 398 additions and 0 deletions
21
configs/exchange-landing/README.md
Normal file
21
configs/exchange-landing/README.md
Normal file
|
|
@ -0,0 +1,21 @@
|
||||||
|
# Exchange landing (`exchange.hacktivism.ch` intro)
|
||||||
|
|
||||||
|
Static intro (add-exchange QR + links) for the **GOA exchange**.
|
||||||
|
|
||||||
|
| Item | Value |
|
||||||
|
|------|--------|
|
||||||
|
| Container | `taler-hacktivism-exchange-ansible` |
|
||||||
|
| In-container path | `/var/www/exchange-landing/` |
|
||||||
|
| nginx | listen **9014** (`nginx-landing.conf`) |
|
||||||
|
| **Host port to publish / wire** | **`9014`** → `127.0.0.1:9014` |
|
||||||
|
| Public path (once Caddy wired) | e.g. `https://exchange.hacktivism.ch/intro/` *or* dedicated intro host |
|
||||||
|
|
||||||
|
## Deploy
|
||||||
|
|
||||||
|
```bash
|
||||||
|
# from laptop → koopa
|
||||||
|
scp -r configs/exchange-landing/* koopa:/tmp/exchange-landing/
|
||||||
|
# on koopa: install nginx if missing, copy files, enable site, publish port
|
||||||
|
```
|
||||||
|
|
||||||
|
See `scripts/taler-landing/deploy-landings.sh`.
|
||||||
353
configs/exchange-landing/index.html
Normal file
353
configs/exchange-landing/index.html
Normal file
|
|
@ -0,0 +1,353 @@
|
||||||
|
<!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; }
|
||||||
|
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 intergalactic exchange — <strong>coins</strong>, denominations, withdraw / refresh / deposit.</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>Add the exchange</h2>
|
||||||
|
<p class="step-tagline">Trust <code>exchange.hacktivism.ch</code></p>
|
||||||
|
<div class="step-body">
|
||||||
|
<div class="qr-box">
|
||||||
|
<div id="qr-exchange"></div>
|
||||||
|
<div class="qr-label">Add exchange</div>
|
||||||
|
</div>
|
||||||
|
<a class="cta" href="taler://add-exchange/exchange.hacktivism.ch/">Open in wallet →</a>
|
||||||
|
<p class="meta">taler://add-exchange/exchange.hacktivism.ch/</p>
|
||||||
|
<div class="links">
|
||||||
|
<a class="cta teal" href="https://bank.hacktivism.ch/intro/">Withdraw GOA at 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>
|
||||||
|
<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://add-exchange/exchange.hacktivism.ch/",
|
||||||
|
width: 220, height: 220, correctLevel: QRCode.CorrectLevel.M
|
||||||
|
});
|
||||||
|
}
|
||||||
|
})();
|
||||||
|
</script>
|
||||||
|
</body>
|
||||||
|
</html>
|
||||||
24
configs/exchange-landing/nginx-landing.conf
Normal file
24
configs/exchange-landing/nginx-landing.conf
Normal file
|
|
@ -0,0 +1,24 @@
|
||||||
|
# Exchange landing — :9014 (behind Caddy — no port in Location)
|
||||||
|
server {
|
||||||
|
listen 9014;
|
||||||
|
listen [::]:9014;
|
||||||
|
server_name exchange.hacktivism.ch _;
|
||||||
|
root /var/www/exchange-landing;
|
||||||
|
index index.html;
|
||||||
|
|
||||||
|
absolute_redirect off;
|
||||||
|
port_in_redirect off;
|
||||||
|
|
||||||
|
location = / {
|
||||||
|
return 302 /intro/;
|
||||||
|
}
|
||||||
|
location = /intro {
|
||||||
|
return 302 /intro/;
|
||||||
|
}
|
||||||
|
location /intro/ {
|
||||||
|
alias /var/www/exchange-landing/;
|
||||||
|
}
|
||||||
|
location / {
|
||||||
|
try_files $uri $uri/ =404;
|
||||||
|
}
|
||||||
|
}
|
||||||
Loading…
Add table
Add a link
Reference in a new issue