From fb1a0b0b8ef62d9ec9af65d3feeece5e1019f44d Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Hern=C3=A2ni=20Marques?= Date: Fri, 17 Jul 2026 13:30:23 +0200 Subject: [PATCH] landing: kGOA units, full money stats, rootless resource display Public bank/exchange/merchant landing stats and performance UI only. Secret goa-ui overlay stays on local/merchant-secret-ui (never push). --- configs/bank-landing/goa-amount.js | 152 +++++++++---- configs/bank-landing/index.html | 211 +++++++++++++----- configs/exchange-landing/goa-amount.js | 152 +++++++++---- configs/exchange-landing/index.html | 142 +++++++++--- configs/merchant-landing/goa-amount.js | 152 +++++++++---- configs/merchant-landing/index.html | 103 ++++++--- configs/shared/goa-amount.js | 152 +++++++++---- .../collect_container_resources.sh | 133 ++++++++++- scripts/taler-landing/goa_amounts.py | 126 ++++++++--- scripts/taler-landing/merge_resources.py | 40 +++- scripts/taler-landing/test-landing-stats.sh | 14 +- .../taler-merchant/landing-stats-merchant.sh | 2 +- 12 files changed, 1046 insertions(+), 333 deletions(-) diff --git a/configs/bank-landing/goa-amount.js b/configs/bank-landing/goa-amount.js index 67c3859..cfaaad3 100644 --- a/configs/bank-landing/goa-amount.js +++ b/configs/bank-landing/goa-amount.js @@ -1,29 +1,63 @@ -/* Compact GOA / CUR:amount display using currency alt_unit_names. - * Large values → "1.23 Mega-GOA" (title = full GOA:n) so landing tiles fit. +/* Compact GOA / CUR:amount display using SI-style alt units (kGOA, MGOA, …). + * Large values → "1.23 MGOA" (title = full GOA:n) so landing tiles fit. */ (function (global) { "use strict"; var DEFAULT_ALT = { - "24": "Yotta-GOA", - "21": "Zetta-GOA", - "18": "Exa-GOA", - "15": "Peta-GOA", - "12": "Tera-GOA", - "9": "Giga-GOA", - "6": "Mega-GOA", - "3": "Kilo-GOA", + "24": "YGOA", + "21": "ZGOA", + "18": "EGOA", + "15": "PGOA", + "12": "TGOA", + "9": "GGOA", + "6": "MGOA", + "3": "kGOA", "0": "GOA", - "-1": "Deci-GOA", - "-2": "Centi-GOA", - "-3": "Milli-GOA", - "-6": "Micro-GOA", - "-7": "Deci-Micro-GOA", - "-8": "Atomic-GOA" + "-1": "dGOA", + "-2": "cGOA", + "-3": "mGOA", + "-6": "µGOA", + "-7": "dµGOA", + "-8": "aGOA" + }; + + var LONG_TO_COMPACT = { + "yotta-goa": "YGOA", + "zetta-goa": "ZGOA", + "exa-goa": "EGOA", + "peta-goa": "PGOA", + "tera-goa": "TGOA", + "giga-goa": "GGOA", + "mega-goa": "MGOA", + "kilo-goa": "kGOA", + "goa": "GOA", + "deci-goa": "dGOA", + "centi-goa": "cGOA", + "milli-goa": "mGOA", + "micro-goa": "µGOA", + "deci-micro-goa": "dµGOA", + "atomic-goa": "aGOA" }; var THRESHOLD = 1000; + function compactAlt(altMap) { + var out = {}; + var src = altMap || DEFAULT_ALT; + Object.keys(DEFAULT_ALT).forEach(function (k) { + out[k] = DEFAULT_ALT[k]; + }); + Object.keys(src).forEach(function (k) { + var name = String(src[k] || "").trim(); + var low = name.toLowerCase().replace(/\s+/g, ""); + if (LONG_TO_COMPACT[low]) out[k] = LONG_TO_COMPACT[low]; + else if (DEFAULT_ALT[k] && /goa/i.test(name)) out[k] = DEFAULT_ALT[k]; + else out[k] = name || DEFAULT_ALT[k] || name; + }); + return out; + } + function parseAmount(s) { if (s == null || s === "") return { cur: "GOA", val: 0 }; if (typeof s === "number") return { cur: "GOA", val: s }; @@ -51,7 +85,7 @@ } /** - * @param {string|number} amount + * @param {string|number|object} amount * @param {object} [altMap] * @returns {{ display: string, title: string, raw: string, usedAlt: boolean }} */ @@ -59,24 +93,17 @@ if (amount == null || amount === "") { return { display: "—", title: "", raw: "", usedAlt: false }; } - // Prefer server-provided alt if caller passes object {amount, amount_alt} + // Prefer re-format from canonical amount (keeps kGOA even if server sent Kilo-GOA) if (typeof amount === "object" && amount) { - if (amount.amount_alt) { - var rawO = amount.amount || amount.amount_full || String(amount.amount_alt); - return { - display: String(amount.amount_alt), - title: amount.amount_full || rawO, - raw: String(rawO), - usedAlt: true - }; + if (amount.amount) { + return format(amount.amount, altMap); } - amount = amount.amount || amount.amount_full || ""; + amount = amount.amount_full || amount.amount_alt || ""; } - var alt = altMap || global.__GOA_ALT_UNITS || DEFAULT_ALT; + var alt = compactAlt(altMap || global.__GOA_ALT_UNITS || DEFAULT_ALT); var p = parseAmount(amount); var raw = baseStr(p.cur, p.val); - // Foreign currencies must not pick up Kilo-GOA / Mega-GOA labels var baseName = (alt && alt["0"]) || p.cur; var curU = String(p.cur || "").toUpperCase(); var baseU = String(baseName || "").toUpperCase(); @@ -95,7 +122,9 @@ var n = parseInt(k, 10); if (!isNaN(n)) scales.push({ sc: n, name: String(alt[k]) }); }); - scales.sort(function (a, b) { return b.sc - a.sc; }); + scales.sort(function (a, b) { + return b.sc - a.sc; + }); var abs = Math.abs(p.val); var chosen = null; @@ -136,33 +165,80 @@ setText(document.getElementById(id), amount, altMap); } - /** Prefer amount_alt from stats row, else format amount. */ + /** Prefer formatting from amount field; fall back to amount_alt string. */ function pick(rowOrAmount, altMap) { if (rowOrAmount && typeof rowOrAmount === "object") { + var a = + rowOrAmount.amount || + rowOrAmount.total_amount || + rowOrAmount.amount_paid_sum || + rowOrAmount.amount_sum; + if (a) return format(a, altMap); if (rowOrAmount.amount_alt) { - return format({ - amount: rowOrAmount.amount, - amount_alt: rowOrAmount.amount_alt, - amount_full: rowOrAmount.amount_full - }, altMap); + return { + display: String(rowOrAmount.amount_alt), + title: String(rowOrAmount.amount_full || rowOrAmount.amount_alt), + raw: String(rowOrAmount.amount_full || rowOrAmount.amount_alt), + usedAlt: true + }; } - return format(rowOrAmount.amount || rowOrAmount.total_amount, altMap); + return format("", altMap); } return format(rowOrAmount, altMap); } function rememberAlt(stats) { if (stats && stats.alt_unit_names && typeof stats.alt_unit_names === "object") { - global.__GOA_ALT_UNITS = stats.alt_unit_names; + global.__GOA_ALT_UNITS = compactAlt(stats.alt_unit_names); } } + /** Small legend HTML (positive scales). */ + function legendHtml(stats) { + var alt = compactAlt((stats && stats.alt_unit_names) || global.__GOA_ALT_UNITS || DEFAULT_ALT); + var rows = []; + Object.keys(alt) + .map(function (k) { + return parseInt(k, 10); + }) + .filter(function (n) { + return !isNaN(n) && n > 0; + }) + .sort(function (a, b) { + return a - b; + }) + .forEach(function (sc) { + rows.push( + "10" + + sc + + "" + + alt[String(sc)] + + "" + ); + }); + if (!rows.length) return ""; + return ( + '' + + rows.join("") + + "
×basename
" + ); + } + + function fillLegend(elOrId, stats) { + var el = typeof elOrId === "string" ? document.getElementById(elOrId) : elOrId; + if (!el) return; + el.innerHTML = legendHtml(stats); + } + global.GoaAmount = { format: format, setText: setText, setById: setById, pick: pick, rememberAlt: rememberAlt, + compactAlt: compactAlt, + legendHtml: legendHtml, + fillLegend: fillLegend, DEFAULT_ALT: DEFAULT_ALT, THRESHOLD: THRESHOLD }; diff --git a/configs/bank-landing/index.html b/configs/bank-landing/index.html index bd47143..b9ab241 100644 --- a/configs/bank-landing/index.html +++ b/configs/bank-landing/index.html @@ -995,6 +995,27 @@ color: #f0a090; text-align: center; } + .unit-legend { + margin: 0.65rem auto 0; + border-collapse: collapse; + font-size: 0.68rem; + color: var(--muted); + width: auto; + max-width: 100%; + } + .unit-legend th, .unit-legend td { + padding: 0.12rem 0.45rem; + border-bottom: 1px solid rgba(201, 184, 160, 0.12); + text-align: left; + } + .unit-legend th { font-weight: 700; letter-spacing: 0.04em; text-transform: uppercase; font-size: 0.62rem; } + .unit-legend strong { color: #5eead4; font-weight: 750; } + .unit-legend-note { + margin: 0.35rem 0 0; + font-size: 0.68rem; + color: var(--muted); + text-align: center; + } footer { margin-top: 2rem; color: var(--muted); @@ -1392,39 +1413,67 @@ tw run-until-done && tw balance @@ -1448,11 +1497,19 @@ tw run-until-done && tw balance
- Loadavg + CPU (container) + +
+
+ Loadavg (host)
- Container + PIDs + +
+
+ Container RSS
@@ -1469,10 +1526,10 @@ tw run-until-done && tw balance
-

Top processes (RSS)

+

Top processes (RSS inside container)

-

Memory from /proc + cgroup inside podman container.

+

RSS from /proc inside container (no memory cgroup in rootless podman). CPU from podman stats.

+