ops: GOA vanilla landings stats wdGet + collect greening
This commit is contained in:
parent
0d881cd726
commit
337d253691
24 changed files with 1074 additions and 107 deletions
|
|
@ -1118,12 +1118,18 @@
|
||||||
<script src="/intro/qrcode.min.js"></script>
|
<script src="/intro/qrcode.min.js"></script>
|
||||||
</head>
|
</head>
|
||||||
<body data-shop-theme="bank">
|
<body data-shop-theme="bank">
|
||||||
<p class="stack-bar" role="note" title="Taler software on this bank service (links → git.taler.net tags)">
|
<!-- stack-bar -->
|
||||||
|
<p class="stack-bar" role="note" title="Taler software on this bank service (goa-regio-ng vanilla)">
|
||||||
<strong>stack</strong><span class="sep">·</span>
|
<strong>stack</strong><span class="sep">·</span>
|
||||||
<a href="https://git.taler.net/taler/libeufin/commit/ab00480dd9b3be50704fb449b750c764fd4c1de0.html" target="_blank" rel="noopener noreferrer" title="libeufin tag v1.6.6">libeufin-bank 1.6.6</a>
|
<a href="https://git.taler.net/taler-deployment.git/tree/?h=dev/hernani/goa-regio-ng-vanilla" target="_blank" rel="noopener noreferrer" title="taler-deployment branch dev/hernani/goa-regio-ng-vanilla">vanilla</a>
|
||||||
<span class="sep">·</span>
|
<span class="sep">·</span>
|
||||||
<a href="https://git.taler.net/taler/libeufin/commit/ab00480dd9b3be50704fb449b750c764fd4c1de0.html" target="_blank" rel="noopener noreferrer" title="libeufin tag v1.6.6">libeufin-common 1.6.6</a>
|
libeufin-bank 1.6.10
|
||||||
|
<span class="sep">·</span>
|
||||||
|
libeufin-common 1.6.10
|
||||||
|
<span class="sep">·</span>
|
||||||
|
libeufin-bank-webui 1.6.38
|
||||||
</p>
|
</p>
|
||||||
|
<!-- /stack-bar -->
|
||||||
<main>
|
<main>
|
||||||
<header class="hero">
|
<header class="hero">
|
||||||
<span class="badge">Intergalactic · GOA · no IBAN</span>
|
<span class="badge">Intergalactic · GOA · no IBAN</span>
|
||||||
|
|
@ -1558,10 +1564,10 @@ tw run-until-done && tw balance</pre>
|
||||||
<a href="https://bank.hacktivism.ch/privacy" style="color:#5eead4">/privacy</a>.
|
<a href="https://bank.hacktivism.ch/privacy" style="color:#5eead4">/privacy</a>.
|
||||||
</p>
|
</p>
|
||||||
<!-- landing-rev -->
|
<!-- landing-rev -->
|
||||||
<p class="landing-rev" data-landing-site="bank" data-landing-version="44" data-landing-commit="689ee59">
|
<p class="landing-rev" data-landing-site="bank" data-landing-version="45" data-landing-commit="0d881cd">
|
||||||
Bank landing <strong>v44</strong>
|
Bank landing <strong>v45</strong>
|
||||||
· content <time datetime="2026-07-17T18:31:49+02:00">2026-07-17 18:31 CEST</time>
|
· content <time datetime="2026-09-16T23:19:33+02:00">2026-09-16 23:19 CEST</time>
|
||||||
· <a href="https://git.hacktivism.ch/hernani/koopa-admin-log/commit/689ee595e08f3880c1e4068aac8e972574e20d21" title="koopa-admin-log 689ee595e08f3880c1e4068aac8e972574e20d21">689ee59</a>
|
· <a href="https://git.hacktivism.ch/hernani/koopa-admin-log/commit/0d881cd726da8d298c98114678253e3cbb7aa5e6" title="koopa-admin-log 0d881cd726da8d298c98114678253e3cbb7aa5e6">0d881cd</a>
|
||||||
</p>
|
</p>
|
||||||
<!-- /landing-rev -->
|
<!-- /landing-rev -->
|
||||||
</footer>
|
</footer>
|
||||||
|
|
|
||||||
|
|
@ -49,6 +49,16 @@ server {
|
||||||
add_header Cache-Control "no-store" always;
|
add_header Cache-Control "no-store" always;
|
||||||
add_header Access-Control-Allow-Origin * always;
|
add_header Access-Control-Allow-Origin * always;
|
||||||
}
|
}
|
||||||
|
# FP pattern: GET Integration via demo-api (inject suggested/required_exchange)
|
||||||
|
# Caddy @wdGet → :9013 → here → :19096; POST/other Integration stays on :9012
|
||||||
|
location /taler-integration/withdrawal-operation/ {
|
||||||
|
proxy_pass http://127.0.0.1:19096;
|
||||||
|
proxy_http_version 1.1;
|
||||||
|
proxy_set_header Host $host;
|
||||||
|
proxy_read_timeout 70s;
|
||||||
|
add_header Cache-Control "no-store" always;
|
||||||
|
add_header Access-Control-Allow-Origin * always;
|
||||||
|
}
|
||||||
location /intro/ {
|
location /intro/ {
|
||||||
alias /var/www/bank-landing/;
|
alias /var/www/bank-landing/;
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -204,6 +204,19 @@ bank.hacktivism.ch {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
# FP pattern (francpaysan-admin-log files/caddy/Caddyfile @wdGet):
|
||||||
|
# GET withdrawal-operation → landing :9013 → demo-api :19096
|
||||||
|
# (injects suggested_exchange / required_exchange for Android/iOS)
|
||||||
|
@wdGet {
|
||||||
|
method GET
|
||||||
|
path /taler-integration/withdrawal-operation/*
|
||||||
|
}
|
||||||
|
handle @wdGet {
|
||||||
|
reverse_proxy 127.0.0.1:9013 {
|
||||||
|
import proxy_public
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
reverse_proxy 127.0.0.1:9012 {
|
reverse_proxy 127.0.0.1:9012 {
|
||||||
import proxy_public
|
import proxy_public
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -289,18 +289,22 @@
|
||||||
</style>
|
</style>
|
||||||
</head>
|
</head>
|
||||||
<body>
|
<body>
|
||||||
<p class="stack-bar" role="note" title="Taler software on this exchange service (links → git.taler.net tags)">
|
<!-- stack-bar -->
|
||||||
|
<p class="stack-bar" role="note" title="Taler software on this exchange service (goa-regio-ng vanilla)">
|
||||||
<strong>stack</strong><span class="sep">·</span>
|
<strong>stack</strong><span class="sep">·</span>
|
||||||
<a href="https://git.taler.net/taler/exchange/commit/6dbfa41e3fc6657eff28d11166d24301cdfa93ff.html" target="_blank" rel="noopener noreferrer" title="exchange tag v1.6.6">taler-exchange 1.6.6</a>
|
<a href="https://git.taler.net/taler-deployment.git/tree/?h=dev/hernani/goa-regio-ng-vanilla" target="_blank" rel="noopener noreferrer" title="taler-deployment branch dev/hernani/goa-regio-ng-vanilla">vanilla</a>
|
||||||
<span class="sep">·</span>
|
<span class="sep">·</span>
|
||||||
<a href="https://git.taler.net/taler/exchange/commit/6dbfa41e3fc6657eff28d11166d24301cdfa93ff.html" target="_blank" rel="noopener noreferrer" title="exchange tag v1.6.6">libtalerexchange 1.6.6</a>
|
taler-exchange 1.6.15
|
||||||
<span class="sep">·</span>
|
<span class="sep">·</span>
|
||||||
<a href="https://git.taler.net/taler/exchange/commit/6dbfa41e3fc6657eff28d11166d24301cdfa93ff.html" target="_blank" rel="noopener noreferrer" title="exchange tag v1.6.6">taler-exchange-database 1.6.6</a>
|
libtalerexchange 1.6.15
|
||||||
<span class="sep">·</span>
|
<span class="sep">·</span>
|
||||||
<a href="https://git.taler.net/taler/exchange/commit/6dbfa41e3fc6657eff28d11166d24301cdfa93ff.html" target="_blank" rel="noopener noreferrer" title="exchange tag v1.6.6">taler-exchange-offline 1.6.6</a>
|
taler-exchange-database 1.6.15
|
||||||
<span class="sep">·</span>
|
<span class="sep">·</span>
|
||||||
<a href="https://git.taler.net/taler/exchange/commit/6dbfa41e3fc6657eff28d11166d24301cdfa93ff.html" target="_blank" rel="noopener noreferrer" title="exchange tag v1.6.6">taler-terms-generator 1.6.6</a>
|
taler-exchange-offline 1.6.15
|
||||||
|
<span class="sep">·</span>
|
||||||
|
taler-terms-generator 1.6.15
|
||||||
</p>
|
</p>
|
||||||
|
<!-- /stack-bar -->
|
||||||
<main>
|
<main>
|
||||||
<header class="hero">
|
<header class="hero">
|
||||||
<span class="badge">GOA · Exchange</span>
|
<span class="badge">GOA · Exchange</span>
|
||||||
|
|
@ -500,10 +504,10 @@ sudo apt-get install -y taler-wallet-cli</pre>
|
||||||
<a href="https://exchange.hacktivism.ch/privacy" style="color:#5eead4">/privacy</a>.
|
<a href="https://exchange.hacktivism.ch/privacy" style="color:#5eead4">/privacy</a>.
|
||||||
</p>
|
</p>
|
||||||
<!-- landing-rev -->
|
<!-- landing-rev -->
|
||||||
<p class="landing-rev" data-landing-site="exchange" data-landing-version="43" data-landing-commit="d0594cb">
|
<p class="landing-rev" data-landing-site="exchange" data-landing-version="44" data-landing-commit="0d881cd">
|
||||||
Exchange landing <strong>v43</strong>
|
Exchange landing <strong>v44</strong>
|
||||||
· content <time datetime="2026-07-17T18:33:13+02:00">2026-07-17 18:33 CEST</time>
|
· content <time datetime="2026-09-16T23:19:33+02:00">2026-09-16 23:19 CEST</time>
|
||||||
· <a href="https://git.hacktivism.ch/hernani/koopa-admin-log/commit/d0594cb44ffb1bec2e009f6812167cbbf216eda9" title="koopa-admin-log d0594cb44ffb1bec2e009f6812167cbbf216eda9">d0594cb</a>
|
· <a href="https://git.hacktivism.ch/hernani/koopa-admin-log/commit/0d881cd726da8d298c98114678253e3cbb7aa5e6" title="koopa-admin-log 0d881cd726da8d298c98114678253e3cbb7aa5e6">0d881cd</a>
|
||||||
</p>
|
</p>
|
||||||
<!-- /landing-rev -->
|
<!-- /landing-rev -->
|
||||||
</footer>
|
</footer>
|
||||||
|
|
|
||||||
|
|
@ -530,18 +530,22 @@
|
||||||
</style>
|
</style>
|
||||||
</head>
|
</head>
|
||||||
<body>
|
<body>
|
||||||
<p class="stack-bar" role="note" title="Taler software on this merchant service (links → git.taler.net tags)">
|
<!-- stack-bar -->
|
||||||
|
<p class="stack-bar" role="note" title="Taler software on this merchant service (goa-regio-ng vanilla)">
|
||||||
<strong>stack</strong><span class="sep">·</span>
|
<strong>stack</strong><span class="sep">·</span>
|
||||||
<a href="https://git.taler.net/taler/merchant/commit/b5af1c99052f4c5053070cd5c6cc39b68eca2a20.html" target="_blank" rel="noopener noreferrer" title="merchant tag v1.6.9">taler-merchant 1.6.9</a>
|
<a href="https://git.taler.net/taler-deployment.git/tree/?h=dev/hernani/goa-regio-ng-vanilla" target="_blank" rel="noopener noreferrer" title="taler-deployment branch dev/hernani/goa-regio-ng-vanilla">vanilla</a>
|
||||||
<span class="sep">·</span>
|
<span class="sep">·</span>
|
||||||
<a href="https://git.taler.net/taler/taler-typescript-core/commit/d7ab425239226b798a95dbf6742fe4b4a2e30e47.html" target="_blank" rel="noopener noreferrer" title="taler-typescript-core tag v1.6.8-dev.6">taler-merchant-webui 1.6.8~dev6</a>
|
taler-merchant 1.6.23
|
||||||
<span class="sep">·</span>
|
<span class="sep">·</span>
|
||||||
<a href="https://git.taler.net/taler/merchant/commit/b5af1c99052f4c5053070cd5c6cc39b68eca2a20.html" target="_blank" rel="noopener noreferrer" title="merchant tag v1.6.9">libtalermerchant 1.6.9</a>
|
taler-merchant-webui 1.6.38
|
||||||
<span class="sep">·</span>
|
<span class="sep">·</span>
|
||||||
<a href="https://git.taler.net/taler/exchange/commit/fed7d0e782ea4d36b2d2e337d93bfd2723c6ed64.html" target="_blank" rel="noopener noreferrer" title="exchange tag v1.6.7-dev.2">libtalerexchange 1.6.7~dev2</a>
|
libtalermerchant 1.6.23
|
||||||
<span class="sep">·</span>
|
<span class="sep">·</span>
|
||||||
<a href="https://git.taler.net/taler/exchange/commit/fed7d0e782ea4d36b2d2e337d93bfd2723c6ed64.html" target="_blank" rel="noopener noreferrer" title="exchange tag v1.6.7-dev.2">taler-terms-generator 1.6.7~dev2</a>
|
libtalerexchange 1.6.15
|
||||||
|
<span class="sep">·</span>
|
||||||
|
taler-terms-generator 1.6.15
|
||||||
</p>
|
</p>
|
||||||
|
<!-- /stack-bar -->
|
||||||
<main>
|
<main>
|
||||||
<header class="hero">
|
<header class="hero">
|
||||||
<span class="badge">Merchant · dual currency</span>
|
<span class="badge">Merchant · dual currency</span>
|
||||||
|
|
@ -833,10 +837,10 @@ sudo apt-get install -y taler-wallet-cli</pre>
|
||||||
<a href="https://taler.hacktivism.ch/privacy" style="color:#5eead4">/privacy</a>.
|
<a href="https://taler.hacktivism.ch/privacy" style="color:#5eead4">/privacy</a>.
|
||||||
</p>
|
</p>
|
||||||
<!-- landing-rev -->
|
<!-- landing-rev -->
|
||||||
<p class="landing-rev" data-landing-site="merchant" data-landing-version="42" data-landing-commit="37ea4bb">
|
<p class="landing-rev" data-landing-site="merchant" data-landing-version="43" data-landing-commit="0d881cd">
|
||||||
Merchant landing <strong>v42</strong>
|
Merchant landing <strong>v43</strong>
|
||||||
· content <time datetime="2026-07-17T18:27:48+02:00">2026-07-17 18:27 CEST</time>
|
· content <time datetime="2026-09-16T23:19:33+02:00">2026-09-16 23:19 CEST</time>
|
||||||
· <a href="https://git.hacktivism.ch/hernani/koopa-admin-log/commit/37ea4bb8396691e3ea69ae61374e6623bf6c5ea7" title="koopa-admin-log 37ea4bb8396691e3ea69ae61374e6623bf6c5ea7">37ea4bb</a>
|
· <a href="https://git.hacktivism.ch/hernani/koopa-admin-log/commit/0d881cd726da8d298c98114678253e3cbb7aa5e6" title="koopa-admin-log 0d881cd726da8d298c98114678253e3cbb7aa5e6">0d881cd</a>
|
||||||
</p>
|
</p>
|
||||||
<!-- /landing-rev -->
|
<!-- /landing-rev -->
|
||||||
</footer>
|
</footer>
|
||||||
|
|
|
||||||
79
configs/shared/landing-stack-versions.json
Normal file
79
configs/shared/landing-stack-versions.json
Normal file
|
|
@ -0,0 +1,79 @@
|
||||||
|
{
|
||||||
|
"updated_iso": "2026-09-16T23:19:33+02:00",
|
||||||
|
"updated_human": "2026-09-16 23:19 CEST",
|
||||||
|
"ctr": "goa-regio-ng",
|
||||||
|
"ssh": "hernani@192.168.100.95",
|
||||||
|
"packages": {
|
||||||
|
"libeufin-bank": {
|
||||||
|
"dpkg": "1.6.10-0+trixie",
|
||||||
|
"display": "1.6.10"
|
||||||
|
},
|
||||||
|
"libeufin-bank-webui": {
|
||||||
|
"dpkg": "1.6.38-0+trixie",
|
||||||
|
"display": "1.6.38"
|
||||||
|
},
|
||||||
|
"libeufin-common": {
|
||||||
|
"dpkg": "1.6.10-0+trixie",
|
||||||
|
"display": "1.6.10"
|
||||||
|
},
|
||||||
|
"libtalerexchange": {
|
||||||
|
"dpkg": "1.6.15-0+trixie",
|
||||||
|
"display": "1.6.15"
|
||||||
|
},
|
||||||
|
"libtalermerchant": {
|
||||||
|
"dpkg": "1.6.23-0+trixie",
|
||||||
|
"display": "1.6.23"
|
||||||
|
},
|
||||||
|
"taler-exchange": {
|
||||||
|
"dpkg": "1.6.15-0+trixie",
|
||||||
|
"display": "1.6.15"
|
||||||
|
},
|
||||||
|
"taler-exchange-database": {
|
||||||
|
"dpkg": "1.6.15-0+trixie",
|
||||||
|
"display": "1.6.15"
|
||||||
|
},
|
||||||
|
"taler-exchange-offline": {
|
||||||
|
"dpkg": "1.6.15-0+trixie",
|
||||||
|
"display": "1.6.15"
|
||||||
|
},
|
||||||
|
"taler-merchant": {
|
||||||
|
"dpkg": "1.6.23-0+trixie",
|
||||||
|
"display": "1.6.23"
|
||||||
|
},
|
||||||
|
"taler-merchant-webui": {
|
||||||
|
"dpkg": "1.6.38-0+trixie",
|
||||||
|
"display": "1.6.38"
|
||||||
|
},
|
||||||
|
"taler-terms-generator": {
|
||||||
|
"dpkg": "1.6.15-0+trixie",
|
||||||
|
"display": "1.6.15"
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"sites": {
|
||||||
|
"bank": {
|
||||||
|
"packages": {
|
||||||
|
"libeufin-bank": "1.6.10",
|
||||||
|
"libeufin-common": "1.6.10",
|
||||||
|
"libeufin-bank-webui": "1.6.38"
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"exchange": {
|
||||||
|
"packages": {
|
||||||
|
"taler-exchange": "1.6.15",
|
||||||
|
"libtalerexchange": "1.6.15",
|
||||||
|
"taler-exchange-database": "1.6.15",
|
||||||
|
"taler-exchange-offline": "1.6.15",
|
||||||
|
"taler-terms-generator": "1.6.15"
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"merchant": {
|
||||||
|
"packages": {
|
||||||
|
"taler-merchant": "1.6.23",
|
||||||
|
"taler-merchant-webui": "1.6.38",
|
||||||
|
"libtalermerchant": "1.6.23",
|
||||||
|
"libtalerexchange": "1.6.15",
|
||||||
|
"taler-terms-generator": "1.6.15"
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
35
configs/shared/landing-stack.json
Normal file
35
configs/shared/landing-stack.json
Normal file
|
|
@ -0,0 +1,35 @@
|
||||||
|
{
|
||||||
|
"note": "Stack-bar package lists per landing. Versions filled by scripts/taler-landing/stamp-landing-stack.sh from live CTR dpkg. Only hard link = vanilla branch.",
|
||||||
|
"vanilla_href": "https://git.taler.net/taler-deployment.git/tree/?h=dev/hernani/goa-regio-ng-vanilla",
|
||||||
|
"vanilla_title": "taler-deployment branch dev/hernani/goa-regio-ng-vanilla",
|
||||||
|
"sites": {
|
||||||
|
"bank": {
|
||||||
|
"title": "Taler software on this bank service (goa-regio-ng vanilla)",
|
||||||
|
"packages": [
|
||||||
|
"libeufin-bank",
|
||||||
|
"libeufin-common",
|
||||||
|
"libeufin-bank-webui"
|
||||||
|
]
|
||||||
|
},
|
||||||
|
"exchange": {
|
||||||
|
"title": "Taler software on this exchange service (goa-regio-ng vanilla)",
|
||||||
|
"packages": [
|
||||||
|
"taler-exchange",
|
||||||
|
"libtalerexchange",
|
||||||
|
"taler-exchange-database",
|
||||||
|
"taler-exchange-offline",
|
||||||
|
"taler-terms-generator"
|
||||||
|
]
|
||||||
|
},
|
||||||
|
"merchant": {
|
||||||
|
"title": "Taler software on this merchant service (goa-regio-ng vanilla)",
|
||||||
|
"packages": [
|
||||||
|
"taler-merchant",
|
||||||
|
"taler-merchant-webui",
|
||||||
|
"libtalermerchant",
|
||||||
|
"libtalerexchange",
|
||||||
|
"taler-terms-generator"
|
||||||
|
]
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
@ -2,34 +2,34 @@
|
||||||
"note": "Per-site landing content versions (start 42). Bump: scripts/taler-landing/stamp-landing-version.sh --bump bank|exchange|merchant",
|
"note": "Per-site landing content versions (start 42). Bump: scripts/taler-landing/stamp-landing-version.sh --bump bank|exchange|merchant",
|
||||||
"sites": {
|
"sites": {
|
||||||
"bank": {
|
"bank": {
|
||||||
"version": 44,
|
"version": 45,
|
||||||
"label": "Bank landing",
|
"label": "Bank landing",
|
||||||
"path": "configs/bank-landing",
|
"path": "configs/bank-landing",
|
||||||
"updated_iso": "2026-07-17T18:31:49+02:00",
|
"updated_iso": "2026-09-16T23:19:33+02:00",
|
||||||
"updated_human": "2026-07-17 18:31 CEST",
|
"updated_human": "2026-09-16 23:19 CEST",
|
||||||
"commit": "689ee595e08f3880c1e4068aac8e972574e20d21",
|
"commit": "0d881cd726da8d298c98114678253e3cbb7aa5e6",
|
||||||
"commit_short": "689ee59",
|
"commit_short": "0d881cd",
|
||||||
"repo_commit_url": "https://git.hacktivism.ch/hernani/koopa-admin-log/commit/689ee595e08f3880c1e4068aac8e972574e20d21"
|
"repo_commit_url": "https://git.hacktivism.ch/hernani/koopa-admin-log/commit/0d881cd726da8d298c98114678253e3cbb7aa5e6"
|
||||||
},
|
},
|
||||||
"exchange": {
|
"exchange": {
|
||||||
"version": 43,
|
"version": 44,
|
||||||
"label": "Exchange landing",
|
"label": "Exchange landing",
|
||||||
"path": "configs/exchange-landing",
|
"path": "configs/exchange-landing",
|
||||||
"updated_iso": "2026-07-17T18:33:13+02:00",
|
"updated_iso": "2026-09-16T23:19:33+02:00",
|
||||||
"updated_human": "2026-07-17 18:33 CEST",
|
"updated_human": "2026-09-16 23:19 CEST",
|
||||||
"commit": "d0594cb44ffb1bec2e009f6812167cbbf216eda9",
|
"commit": "0d881cd726da8d298c98114678253e3cbb7aa5e6",
|
||||||
"commit_short": "d0594cb",
|
"commit_short": "0d881cd",
|
||||||
"repo_commit_url": "https://git.hacktivism.ch/hernani/koopa-admin-log/commit/d0594cb44ffb1bec2e009f6812167cbbf216eda9"
|
"repo_commit_url": "https://git.hacktivism.ch/hernani/koopa-admin-log/commit/0d881cd726da8d298c98114678253e3cbb7aa5e6"
|
||||||
},
|
},
|
||||||
"merchant": {
|
"merchant": {
|
||||||
"version": 42,
|
"version": 43,
|
||||||
"label": "Merchant landing",
|
"label": "Merchant landing",
|
||||||
"path": "configs/merchant-landing",
|
"path": "configs/merchant-landing",
|
||||||
"updated_iso": "2026-07-17T18:27:48+02:00",
|
"updated_iso": "2026-09-16T23:19:33+02:00",
|
||||||
"updated_human": "2026-07-17 18:27 CEST",
|
"updated_human": "2026-09-16 23:19 CEST",
|
||||||
"commit": "37ea4bb8396691e3ea69ae61374e6623bf6c5ea7",
|
"commit": "0d881cd726da8d298c98114678253e3cbb7aa5e6",
|
||||||
"commit_short": "37ea4bb",
|
"commit_short": "0d881cd",
|
||||||
"repo_commit_url": "https://git.hacktivism.ch/hernani/koopa-admin-log/commit/37ea4bb8396691e3ea69ae61374e6623bf6c5ea7"
|
"repo_commit_url": "https://git.hacktivism.ch/hernani/koopa-admin-log/commit/0d881cd726da8d298c98114678253e3cbb7aa5e6"
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -204,6 +204,19 @@ bank.hacktivism.ch {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
# FP pattern (francpaysan-admin-log files/caddy/Caddyfile @wdGet):
|
||||||
|
# GET withdrawal-operation → landing :9013 → demo-api :19096
|
||||||
|
# (injects suggested_exchange / required_exchange for Android/iOS)
|
||||||
|
@wdGet {
|
||||||
|
method GET
|
||||||
|
path /taler-integration/withdrawal-operation/*
|
||||||
|
}
|
||||||
|
handle @wdGet {
|
||||||
|
reverse_proxy 127.0.0.1:9013 {
|
||||||
|
import proxy_public
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
reverse_proxy 127.0.0.1:9012 {
|
reverse_proxy 127.0.0.1:9012 {
|
||||||
import proxy_public
|
import proxy_public
|
||||||
}
|
}
|
||||||
|
|
|
||||||
354
scripts/caddy/Caddyfile.koopa-live-wdGet
Normal file
354
scripts/caddy/Caddyfile.koopa-live-wdGet
Normal file
|
|
@ -0,0 +1,354 @@
|
||||||
|
# Internal only (not in the browser URL):
|
||||||
|
# 9010 merchant API | 9011 exchange API | 9012 bank API
|
||||||
|
# 9013 bank landing | 9014 exchange landing | 9015 merchant landing
|
||||||
|
# 9020 castopod | 9021 bonfire | 9022 prime | 9023 bt | 9024 forgejo | 9025 paivana | 9026 lemmy | 9027 decidim | 9200 forgejo-ssh
|
||||||
|
# 9090 tops ng1 | 9091 tops ng2 | 9092 tops ng3
|
||||||
|
{
|
||||||
|
email info+koopa@hacktivism.ch
|
||||||
|
http_port 9000
|
||||||
|
https_port 9001
|
||||||
|
auto_https disable_redirects
|
||||||
|
# Caddy listens on 9001 behind VeciGate/https-proxy :443.
|
||||||
|
# Default HTTP/3 would send Alt-Svc: h3=":9001" — break public HTTPS on :443.
|
||||||
|
servers {
|
||||||
|
protocols h1 h2
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
(proxy_public) {
|
||||||
|
header_up Host {host}
|
||||||
|
header_up X-Forwarded-Port 443
|
||||||
|
header_down Location "^https?://[^/]+:90[0-9]{2}(.*)$" "https://{host}$1"
|
||||||
|
}
|
||||||
|
|
||||||
|
taler.hacktivism.ch {
|
||||||
|
header Alt-Svc "clear"
|
||||||
|
|
||||||
|
# Public landing first
|
||||||
|
redir / /intro/ 302
|
||||||
|
|
||||||
|
handle /intro* {
|
||||||
|
reverse_proxy 127.0.0.1:9015 {
|
||||||
|
import proxy_public
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
# surface + aptdeploy HTML (taler.hacktivism.ch only)
|
||||||
|
handle /taler-monitoring-surface_err {
|
||||||
|
redir /taler-monitoring-surface_err/ 302
|
||||||
|
}
|
||||||
|
handle /taler-monitoring-surface_err* {
|
||||||
|
root * /var/www/monitoring-sites/taler.hacktivism.ch
|
||||||
|
file_server
|
||||||
|
}
|
||||||
|
handle /taler-monitoring-surface {
|
||||||
|
redir /taler-monitoring-surface/ 302
|
||||||
|
}
|
||||||
|
handle /taler-monitoring-surface* {
|
||||||
|
root * /var/www/monitoring-sites/taler.hacktivism.ch
|
||||||
|
file_server
|
||||||
|
}
|
||||||
|
handle /taler-monitoring-aptdeploy_err {
|
||||||
|
redir /taler-monitoring-aptdeploy_err/ 302
|
||||||
|
}
|
||||||
|
handle /taler-monitoring-aptdeploy_err* {
|
||||||
|
root * /var/www/monitoring-sites/taler.hacktivism.ch
|
||||||
|
file_server
|
||||||
|
}
|
||||||
|
handle /taler-monitoring-aptdeploy {
|
||||||
|
redir /taler-monitoring-aptdeploy/ 302
|
||||||
|
}
|
||||||
|
handle /taler-monitoring-aptdeploy* {
|
||||||
|
root * /var/www/monitoring-sites/taler.hacktivism.ch
|
||||||
|
file_server
|
||||||
|
}
|
||||||
|
|
||||||
|
handle /taler-monitoring-mattermost_err {
|
||||||
|
redir /taler-monitoring-mattermost_err/ 302
|
||||||
|
}
|
||||||
|
handle /taler-monitoring-mattermost_err* {
|
||||||
|
root * /var/www/monitoring-sites/taler.hacktivism.ch
|
||||||
|
file_server
|
||||||
|
}
|
||||||
|
handle /taler-monitoring-mattermost {
|
||||||
|
redir /taler-monitoring-mattermost/ 302
|
||||||
|
}
|
||||||
|
handle /taler-monitoring-mattermost* {
|
||||||
|
root * /var/www/monitoring-sites/taler.hacktivism.ch
|
||||||
|
file_server
|
||||||
|
}
|
||||||
|
|
||||||
|
handle /taler-monitoring-mail_err {
|
||||||
|
redir /taler-monitoring-mail_err/ 302
|
||||||
|
}
|
||||||
|
handle /taler-monitoring-mail_err* {
|
||||||
|
root * /var/www/monitoring-sites/taler.hacktivism.ch
|
||||||
|
file_server
|
||||||
|
}
|
||||||
|
handle /taler-monitoring-mail {
|
||||||
|
redir /taler-monitoring-mail/ 302
|
||||||
|
}
|
||||||
|
handle /taler-monitoring-mail* {
|
||||||
|
root * /var/www/monitoring-sites/taler.hacktivism.ch
|
||||||
|
file_server
|
||||||
|
}
|
||||||
|
|
||||||
|
# taler-monitoring console HTML (host static — not in app containers)
|
||||||
|
handle /monitoring_err {
|
||||||
|
redir /monitoring_err/ 302
|
||||||
|
}
|
||||||
|
handle /monitoring_err* {
|
||||||
|
root * /var/www/monitoring-sites/taler.hacktivism.ch
|
||||||
|
file_server
|
||||||
|
}
|
||||||
|
handle /monitoring {
|
||||||
|
redir /monitoring/ 302
|
||||||
|
}
|
||||||
|
handle /monitoring* {
|
||||||
|
root * /var/www/monitoring-sites/taler.hacktivism.ch
|
||||||
|
file_server
|
||||||
|
}
|
||||||
|
|
||||||
|
# SPA: /webui → /webui/
|
||||||
|
redir /webui /webui/ 302
|
||||||
|
|
||||||
|
# Merchant API + WebUI (nginx :9010 → unix socket)
|
||||||
|
reverse_proxy https://127.0.0.1:9010 {
|
||||||
|
transport http {
|
||||||
|
tls_insecure_skip_verify
|
||||||
|
}
|
||||||
|
import proxy_public
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
exchange.hacktivism.ch {
|
||||||
|
header Alt-Svc "clear"
|
||||||
|
|
||||||
|
# Public landing first
|
||||||
|
redir / /intro/ 302
|
||||||
|
|
||||||
|
handle /intro* {
|
||||||
|
reverse_proxy 127.0.0.1:9014 {
|
||||||
|
import proxy_public
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
# taler-monitoring console HTML (host static — not in app containers)
|
||||||
|
handle /monitoring_err {
|
||||||
|
redir /monitoring_err/ 302
|
||||||
|
}
|
||||||
|
handle /monitoring_err* {
|
||||||
|
root * /var/www/monitoring-sites/exchange.hacktivism.ch
|
||||||
|
file_server
|
||||||
|
}
|
||||||
|
handle /monitoring {
|
||||||
|
redir /monitoring/ 302
|
||||||
|
}
|
||||||
|
handle /monitoring* {
|
||||||
|
root * /var/www/monitoring-sites/exchange.hacktivism.ch
|
||||||
|
file_server
|
||||||
|
}
|
||||||
|
|
||||||
|
reverse_proxy 127.0.0.1:9011 {
|
||||||
|
import proxy_public
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
bank.hacktivism.ch {
|
||||||
|
header Alt-Svc "clear"
|
||||||
|
|
||||||
|
# Public landing first
|
||||||
|
redir / /intro/ 302
|
||||||
|
|
||||||
|
handle /intro* {
|
||||||
|
reverse_proxy 127.0.0.1:9013 {
|
||||||
|
import proxy_public
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
# taler-monitoring console HTML (host static — not in app containers)
|
||||||
|
handle /monitoring_err {
|
||||||
|
redir /monitoring_err/ 302
|
||||||
|
}
|
||||||
|
handle /monitoring_err* {
|
||||||
|
root * /var/www/monitoring-sites/bank.hacktivism.ch
|
||||||
|
file_server
|
||||||
|
}
|
||||||
|
handle /monitoring {
|
||||||
|
redir /monitoring/ 302
|
||||||
|
}
|
||||||
|
handle /monitoring* {
|
||||||
|
root * /var/www/monitoring-sites/bank.hacktivism.ch
|
||||||
|
file_server
|
||||||
|
}
|
||||||
|
|
||||||
|
# Static terms/privacy on landing nginx :9013
|
||||||
|
handle /terms* {
|
||||||
|
reverse_proxy 127.0.0.1:9013 {
|
||||||
|
import proxy_public
|
||||||
|
}
|
||||||
|
}
|
||||||
|
handle /privacy* {
|
||||||
|
reverse_proxy 127.0.0.1:9013 {
|
||||||
|
import proxy_public
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
# FP pattern (francpaysan-admin-log files/caddy/Caddyfile @wdGet):
|
||||||
|
# GET withdrawal-operation → landing :9013 → demo-api :19096
|
||||||
|
# (injects suggested_exchange / required_exchange for Android/iOS)
|
||||||
|
@wdGet {
|
||||||
|
method GET
|
||||||
|
path /taler-integration/withdrawal-operation/*
|
||||||
|
}
|
||||||
|
handle @wdGet {
|
||||||
|
reverse_proxy 127.0.0.1:9013 {
|
||||||
|
import proxy_public
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
reverse_proxy 127.0.0.1:9012 {
|
||||||
|
import proxy_public
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
castopod.hacktivism.ch {
|
||||||
|
header Alt-Svc "clear"
|
||||||
|
reverse_proxy 127.0.0.1:9020 {
|
||||||
|
header_up Host {host}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
bonfire.hacktivism.ch {
|
||||||
|
header Alt-Svc "clear"
|
||||||
|
reverse_proxy 127.0.0.1:9021 {
|
||||||
|
header_up Host {host}
|
||||||
|
flush_interval -1
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
prime.hacktivism.ch {
|
||||||
|
header Alt-Svc "clear"
|
||||||
|
reverse_proxy 127.0.0.1:9022 {
|
||||||
|
header_up Host {host}
|
||||||
|
flush_interval -1
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
bt.hacktivism.ch {
|
||||||
|
header Alt-Svc "clear"
|
||||||
|
reverse_proxy 127.0.0.1:9023 {
|
||||||
|
header_up Host {host}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
# 9024 forgejo HTTP (SSH :9200 host-direct, not via Caddy)
|
||||||
|
git.hacktivism.ch {
|
||||||
|
header Alt-Svc "clear"
|
||||||
|
reverse_proxy 127.0.0.1:9024 {
|
||||||
|
header_up Host {host}
|
||||||
|
flush_interval -1
|
||||||
|
transport http {
|
||||||
|
read_timeout 3600s
|
||||||
|
write_timeout 3600s
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
# Taler Operations design previews (static nginx)
|
||||||
|
tops.ng1.hacktivism.ch {
|
||||||
|
header Alt-Svc "clear"
|
||||||
|
reverse_proxy 127.0.0.1:9090 {
|
||||||
|
header_up Host {host}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
tops.ng2.hacktivism.ch {
|
||||||
|
header Alt-Svc "clear"
|
||||||
|
reverse_proxy 127.0.0.1:9091 {
|
||||||
|
header_up Host {host}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
tops.ng3.hacktivism.ch {
|
||||||
|
header Alt-Svc "clear"
|
||||||
|
reverse_proxy 127.0.0.1:9092 {
|
||||||
|
header_up Host {host}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
# 9025 paivana-httpd (GOA paywall)
|
||||||
|
paivana.hacktivism.ch {
|
||||||
|
header Alt-Svc "clear"
|
||||||
|
reverse_proxy 127.0.0.1:9025 {
|
||||||
|
header_up Host {host}
|
||||||
|
header_up X-Forwarded-Port 443
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
# 9026 lemmy (+ ui/pictrs via internal nginx)
|
||||||
|
lemmy.hacktivism.ch {
|
||||||
|
header Alt-Svc "clear"
|
||||||
|
reverse_proxy 127.0.0.1:9026 {
|
||||||
|
header_up Host {host}
|
||||||
|
flush_interval -1
|
||||||
|
transport http {
|
||||||
|
read_timeout 3600s
|
||||||
|
write_timeout 3600s
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
# 9027 decidim (rails)
|
||||||
|
decidim.hacktivism.ch {
|
||||||
|
header Alt-Svc "clear"
|
||||||
|
reverse_proxy 127.0.0.1:9027 {
|
||||||
|
header_up Host {host}
|
||||||
|
header_up X-Forwarded-Port 443
|
||||||
|
flush_interval -1
|
||||||
|
transport http {
|
||||||
|
read_timeout 3600s
|
||||||
|
write_timeout 3600s
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
http://taler.hacktivism.ch, http://exchange.hacktivism.ch, http://bank.hacktivism.ch, http://castopod.hacktivism.ch, http://bonfire.hacktivism.ch, http://prime.hacktivism.ch, http://bt.hacktivism.ch, http://git.hacktivism.ch, http://paivana.hacktivism.ch, http://tops.ng1.hacktivism.ch, http://tops.ng2.hacktivism.ch, http://tops.ng3.hacktivism.ch, http://lemmy.hacktivism.ch, http://decidim.hacktivism.ch {
|
||||||
|
handle /.well-known/acme-challenge/* {
|
||||||
|
root * /var/www/acme
|
||||||
|
file_server
|
||||||
|
}
|
||||||
|
handle {
|
||||||
|
redir https://{host}{uri} permanent
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
# --- LLM Lightweight Monitoring (applied 2026-09-05T10:08:03+02:00) ---
|
||||||
|
monitoring.hacktivism.ch {
|
||||||
|
header Alt-Svc "clear"
|
||||||
|
root * /var/www/monitoring-sites/monitoring.hacktivism.ch
|
||||||
|
file_server
|
||||||
|
try_files {path} /index.html
|
||||||
|
}
|
||||||
|
|
||||||
|
# ngi0-landing dossiers test sites (static) — numbering like tops.ng*
|
||||||
|
dossiers.ngi.hacktivism.ch {
|
||||||
|
header Alt-Svc "clear"
|
||||||
|
root * /var/www/monitoring-sites/dossiers.ngi.hacktivism.ch
|
||||||
|
file_server
|
||||||
|
try_files {path} /index.html
|
||||||
|
}
|
||||||
|
|
||||||
|
dossiers.2.ngi.hacktivism.ch {
|
||||||
|
header Alt-Svc "clear"
|
||||||
|
root * /var/www/monitoring-sites/dossiers.2.ngi.hacktivism.ch
|
||||||
|
file_server
|
||||||
|
try_files {path} /index.html
|
||||||
|
}
|
||||||
|
|
||||||
|
dossiers.3.ngi.hacktivism.ch {
|
||||||
|
header Alt-Svc "clear"
|
||||||
|
root * /var/www/monitoring-sites/dossiers.3.ngi.hacktivism.ch
|
||||||
|
file_server
|
||||||
|
try_files {path} /index.html
|
||||||
|
}
|
||||||
|
|
@ -10,6 +10,24 @@ sudo caddy validate --config /etc/caddy/Caddyfile
|
||||||
sudo systemctl reload caddy
|
sudo systemctl reload caddy
|
||||||
```
|
```
|
||||||
|
|
||||||
|
Preferred live path: edit `~/koopa-caddy/Caddyfile`, then `sudo ~/bin/caddy-apply`
|
||||||
|
(copies → `/etc/caddy/Caddyfile`, validate, `systemctl reload caddy`).
|
||||||
|
|
||||||
|
## GOA bank @wdGet (FP Android withdraw) — full replace
|
||||||
|
|
||||||
|
Live snapshot + `@wdGet` (GET Integration → `:9013` → demo-api):
|
||||||
|
|
||||||
|
- `Caddyfile.koopa-live-wdGet` — whole-file replacement (based on live koopa, not admin-log mirror)
|
||||||
|
- `apply-goa-wdGet-full-replace.sh` — replaces `~/koopa-caddy/Caddyfile` **and** `/etc/caddy/Caddyfile`, reload, smoke
|
||||||
|
|
||||||
|
```bash
|
||||||
|
# on koopa (root) — scp both files first if no admin-log checkout:
|
||||||
|
sudo bash /tmp/goa-fp-port/caddy/apply-goa-wdGet-full-replace.sh \
|
||||||
|
/tmp/goa-fp-port/caddy/Caddyfile.koopa-live-wdGet
|
||||||
|
```
|
||||||
|
|
||||||
|
FP SoT: `$HOME/git/admin-logs/taler/francpaysan-admin-log` `files/caddy/Caddyfile` `@wdGet`.
|
||||||
|
|
||||||
## dossiers.ngi* — basic_auth (htaccess-in-front)
|
## dossiers.ngi* — basic_auth (htaccess-in-front)
|
||||||
|
|
||||||
Caddy **ignores** Apache `.htaccess`. Use:
|
Caddy **ignores** Apache `.htaccess`. Use:
|
||||||
|
|
|
||||||
80
scripts/caddy/apply-goa-wdGet-full-replace.sh
Executable file
80
scripts/caddy/apply-goa-wdGet-full-replace.sh
Executable file
|
|
@ -0,0 +1,80 @@
|
||||||
|
#!/usr/bin/env bash
|
||||||
|
# Full-replace koopa Caddyfile with live snapshot + FP @wdGet (GOA Android withdraw).
|
||||||
|
#
|
||||||
|
# Replaces BOTH:
|
||||||
|
# /home/hernani/koopa-caddy/Caddyfile (runtime SoT for ~/bin/caddy-apply)
|
||||||
|
# /etc/caddy/Caddyfile (active config)
|
||||||
|
# then validates + systemctl reload caddy.
|
||||||
|
#
|
||||||
|
# On koopa (root):
|
||||||
|
# sudo bash /path/to/apply-goa-wdGet-full-replace.sh
|
||||||
|
# # or with explicit file:
|
||||||
|
# sudo bash …/apply-goa-wdGet-full-replace.sh /path/to/Caddyfile.koopa-live-wdGet
|
||||||
|
#
|
||||||
|
# FP pattern: francpaysan-admin-log files/caddy/Caddyfile @wdGet
|
||||||
|
# Ship file: scripts/caddy/Caddyfile.koopa-live-wdGet (live koopa + @wdGet only)
|
||||||
|
set -euo pipefail
|
||||||
|
|
||||||
|
HERE="$(cd "$(dirname "${BASH_SOURCE[0]}")" && pwd)"
|
||||||
|
SRC="${1:-$HERE/Caddyfile.koopa-live-wdGet}"
|
||||||
|
USER_CADDY="/home/hernani/koopa-caddy/Caddyfile"
|
||||||
|
ETC_CADDY="/etc/caddy/Caddyfile"
|
||||||
|
BACKUP_DIR="/etc/caddy"
|
||||||
|
|
||||||
|
if [[ "$(id -u)" -ne 0 ]]; then
|
||||||
|
echo "ERROR: run as root: sudo bash $0${1:+ $1}" >&2
|
||||||
|
exit 1
|
||||||
|
fi
|
||||||
|
|
||||||
|
[[ -f "$SRC" ]] || { echo "ERROR: missing source: $SRC" >&2; exit 1; }
|
||||||
|
[[ -d "$(dirname "$USER_CADDY")" ]] || { echo "ERROR: missing dir: $(dirname "$USER_CADDY")" >&2; exit 1; }
|
||||||
|
[[ -f "$ETC_CADDY" ]] || { echo "ERROR: missing $ETC_CADDY" >&2; exit 1; }
|
||||||
|
command -v caddy >/dev/null || { echo "ERROR: caddy not in PATH" >&2; exit 1; }
|
||||||
|
|
||||||
|
if ! grep -q '@wdGet' "$SRC"; then
|
||||||
|
echo "ERROR: source lacks @wdGet — refusing replace: $SRC" >&2
|
||||||
|
exit 1
|
||||||
|
fi
|
||||||
|
if ! grep -q 'path /taler-integration/withdrawal-operation/\*' "$SRC"; then
|
||||||
|
echo "ERROR: source lacks Integration @wdGet path — refusing: $SRC" >&2
|
||||||
|
exit 1
|
||||||
|
fi
|
||||||
|
|
||||||
|
ts=$(date +%Y%m%d-%H%M%S)
|
||||||
|
bak_etc="${BACKUP_DIR}/Caddyfile.bak-goa-wdGet-${ts}"
|
||||||
|
bak_user="${USER_CADDY}.bak-goa-wdGet-${ts}"
|
||||||
|
|
||||||
|
cp -a "$ETC_CADDY" "$bak_etc"
|
||||||
|
echo "backup etc: $bak_etc"
|
||||||
|
if [[ -f "$USER_CADDY" ]]; then
|
||||||
|
cp -a "$USER_CADDY" "$bak_user"
|
||||||
|
echo "backup user: $bak_user"
|
||||||
|
fi
|
||||||
|
|
||||||
|
install -o hernani -g hernani -m 644 "$SRC" "$USER_CADDY"
|
||||||
|
echo "wrote: $USER_CADDY"
|
||||||
|
|
||||||
|
install -o root -g caddy -m 644 "$SRC" "$ETC_CADDY" 2>/dev/null \
|
||||||
|
|| install -o root -g root -m 644 "$SRC" "$ETC_CADDY"
|
||||||
|
echo "wrote: $ETC_CADDY"
|
||||||
|
|
||||||
|
echo "validate..."
|
||||||
|
caddy validate --config "$ETC_CADDY"
|
||||||
|
|
||||||
|
echo "reload..."
|
||||||
|
systemctl reload caddy
|
||||||
|
systemctl is-active caddy
|
||||||
|
|
||||||
|
echo "spot-check @wdGet:"
|
||||||
|
grep -n '@wdGet\|withdrawal-operation\|reverse_proxy 127.0.0.1:9013\|reverse_proxy 127.0.0.1:9012' "$ETC_CADDY" | head -20
|
||||||
|
|
||||||
|
echo "smoke public Integration (expect suggested_exchange + required_exchange):"
|
||||||
|
smoke="$(curl -sS --max-time 10 \
|
||||||
|
'https://bank.hacktivism.ch/taler-integration/withdrawal-operation/00000000-0000-0000-0000-000000000001' || true)"
|
||||||
|
echo "$smoke"
|
||||||
|
if echo "$smoke" | grep -q 'suggested_exchange' && echo "$smoke" | grep -q 'required_exchange'; then
|
||||||
|
echo "OK: public Integration injects exchange fields"
|
||||||
|
else
|
||||||
|
echo "WARN: public smoke missing exchange fields — check nginx Integration + demo-api :19096" >&2
|
||||||
|
exit 1
|
||||||
|
fi
|
||||||
18
scripts/caddy/wdGet-bank-snippet.caddy
Normal file
18
scripts/caddy/wdGet-bank-snippet.caddy
Normal file
|
|
@ -0,0 +1,18 @@
|
||||||
|
# Paste into live ~/koopa-caddy/Caddyfile inside bank.hacktivism.ch { ... }
|
||||||
|
# BEFORE the catch-all: reverse_proxy 127.0.0.1:9012
|
||||||
|
# Then on koopa: sudo ~/bin/caddy-apply
|
||||||
|
#
|
||||||
|
# FP pattern: francpaysan-admin-log files/caddy/Caddyfile @wdGet
|
||||||
|
# GET Integration → landing :9013 → demo-api :19096
|
||||||
|
# (injects suggested_exchange / required_exchange for Android/iOS)
|
||||||
|
# SoT mirrors: configs/caddy/Caddyfile + host/caddy/Caddyfile
|
||||||
|
|
||||||
|
@wdGet {
|
||||||
|
method GET
|
||||||
|
path /taler-integration/withdrawal-operation/*
|
||||||
|
}
|
||||||
|
handle @wdGet {
|
||||||
|
reverse_proxy 127.0.0.1:9013 {
|
||||||
|
import proxy_public
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
@ -38,24 +38,48 @@ Details + JSON schema: `configs/bank-landing/README.md`.
|
||||||
|
|
||||||
## Demo withdraw + auto-account API
|
## Demo withdraw + auto-account API
|
||||||
|
|
||||||
`demo-withdraw-api.py` listens on **127.0.0.1:19096** (proxied by nginx on the landing):
|
**FP reference SoT:** `$HOME/git/admin-logs/taler/francpaysan-admin-log`
|
||||||
|
(`files/testpaysan/demo-withdraw-api.py`, `files/caddy/Caddyfile` `@wdGet`).
|
||||||
|
GOA ports that pattern: mint `amount`+`exchange_url`, and GET Integration
|
||||||
|
injects `suggested_exchange` / `required_exchange` (Android/iOS).
|
||||||
|
|
||||||
|
`demo-withdraw-api.py` listens on **127.0.0.1:19096** (in bank CTR; nginx on :9013):
|
||||||
|
|
||||||
| Path | Behaviour |
|
| Path | Behaviour |
|
||||||
|------|-----------|
|
|------|-----------|
|
||||||
| `GET /demo-withdraw.json` | Mint one-shot withdraw from shared **`explorer`** pool; write `withdraw.uri` + watch ids |
|
| `GET /demo-withdraw.json` | Mint one-shot withdraw from shared **`explorer`** pool; write `withdraw.uri` + watch ids |
|
||||||
| `GET /auto-account.json` | Public `POST /accounts` with generated **`goa-account-<random>`** user + password containing **pleasechangeme**; **balance GOA:0**; return credentials once |
|
| `GET /auto-account.json` | Public `POST /accounts` with generated **`goa-account-<random>`** user + password containing **pleasechangeme**; **balance GOA:0**; return credentials once |
|
||||||
|
| `GET /taler-integration/withdrawal-operation/*` | Proxy libeufin + inject `suggested_exchange`/`required_exchange` |
|
||||||
|
|
||||||
Install / restart:
|
**Public path (Android):** Caddy `@wdGet` (GET only) → landing **:9013** → nginx → **:19096**.
|
||||||
|
POST / other Integration stays Caddy → **:9012** (raw libeufin).
|
||||||
|
FP stage proxies `@wdGet` straight to host `:19096`; GOA keeps 19096 CTR-only
|
||||||
|
and routes via published 9013 instead.
|
||||||
|
|
||||||
|
Install / restart (host, podman → `taler-hacktivism-bank`):
|
||||||
|
|
||||||
```bash
|
```bash
|
||||||
./install-demo-withdraw-api.sh
|
./install-demo-withdraw-api.sh
|
||||||
# Public checks:
|
# Public checks:
|
||||||
curl -sS https://bank.hacktivism.ch/intro/demo-withdraw.json | head
|
curl -sS https://bank.hacktivism.ch/intro/demo-withdraw.json | head
|
||||||
curl -sS https://bank.hacktivism.ch/intro/auto-account.json | head # creates a real account
|
curl -sS https://bank.hacktivism.ch/intro/auto-account.json | head # creates a real account
|
||||||
|
# After Caddy @wdGet + nginx Integration are live:
|
||||||
|
WID=… # from demo-withdraw.json
|
||||||
|
curl -sS "https://bank.hacktivism.ch/taler-integration/withdrawal-operation/$WID" \
|
||||||
|
| python3 -c 'import sys,json; d=json.load(sys.stdin); print(d.get("suggested_exchange"), d.get("required_exchange"))'
|
||||||
```
|
```
|
||||||
|
|
||||||
Requires **python3** in the bank container. Env: `BANK_URL`, `BANK_USER`/`BANK_PASS`
|
Requires **python3** in the bank container. Env: `BANK_URL`, `BANK_USER`/`BANK_PASS`
|
||||||
(or `/root/bank-explorer-password.txt`), `AMOUNT` (default `GOA:10` for shared withdraws).
|
(or `/root/bank-explorer-password.txt`), `AMOUNT` (default `GOA:10`),
|
||||||
|
`EXCHANGE_URL` (default `https://exchange.hacktivism.ch/`).
|
||||||
|
|
||||||
|
**Caddy (root on koopa — you apply):** insert `@wdGet` before the bank catch-all
|
||||||
|
`reverse_proxy 127.0.0.1:9012` in `~/koopa-caddy/Caddyfile` (SoT:
|
||||||
|
`configs/caddy/Caddyfile` / `host/caddy/Caddyfile`; paste-ready snippet:
|
||||||
|
`scripts/caddy/wdGet-bank-snippet.caddy`, also copied live as
|
||||||
|
`~/koopa-caddy/wdGet-bank-snippet.caddy`), then `sudo ~/bin/caddy-apply`.
|
||||||
|
Caddy alone is not enough — demo-api + nginx Integration must be installed
|
||||||
|
via `./install-demo-withdraw-api.sh` first.
|
||||||
|
|
||||||
### Auto-confirm (explorer only)
|
### Auto-confirm (explorer only)
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -34,6 +34,10 @@ BANK = os.environ.get("BANK_URL", "http://127.0.0.1:9012").rstrip("/")
|
||||||
BANK_PUBLIC = os.environ.get("BANK_PUBLIC", "https://bank.hacktivism.ch").rstrip("/")
|
BANK_PUBLIC = os.environ.get("BANK_PUBLIC", "https://bank.hacktivism.ch").rstrip("/")
|
||||||
USER = os.environ.get("BANK_USER", "explorer")
|
USER = os.environ.get("BANK_USER", "explorer")
|
||||||
AMOUNT = os.environ.get("AMOUNT", "GOA:10")
|
AMOUNT = os.environ.get("AMOUNT", "GOA:10")
|
||||||
|
EXCHANGE = (
|
||||||
|
os.environ.get("EXCHANGE_URL", "https://exchange.hacktivism.ch/").rstrip("/")
|
||||||
|
+ "/"
|
||||||
|
)
|
||||||
LANDING = Path(os.environ.get("LANDING_DIR", "/var/www/bank-landing"))
|
LANDING = Path(os.environ.get("LANDING_DIR", "/var/www/bank-landing"))
|
||||||
LISTEN = ("127.0.0.1", int(os.environ.get("DEMO_WITHDRAW_PORT", "19096")))
|
LISTEN = ("127.0.0.1", int(os.environ.get("DEMO_WITHDRAW_PORT", "19096")))
|
||||||
|
|
||||||
|
|
@ -112,7 +116,7 @@ def http_json(method: str, url: str, body=None, headers=None, auth=None):
|
||||||
return e.code, {"raw": raw[:500]}
|
return e.code, {"raw": raw[:500]}
|
||||||
|
|
||||||
|
|
||||||
def mint_withdraw() -> dict:
|
def mint_withdraw(amount: str | None = None) -> dict:
|
||||||
pw = load_pass()
|
pw = load_pass()
|
||||||
code, tok = http_json(
|
code, tok = http_json(
|
||||||
"POST",
|
"POST",
|
||||||
|
|
@ -123,10 +127,14 @@ def mint_withdraw() -> dict:
|
||||||
if code != 200 or not tok.get("access_token"):
|
if code != 200 or not tok.get("access_token"):
|
||||||
raise RuntimeError(f"token failed HTTP {code}: {tok}")
|
raise RuntimeError(f"token failed HTTP {code}: {tok}")
|
||||||
access = tok["access_token"]
|
access = tok["access_token"]
|
||||||
|
amt = amount or AMOUNT
|
||||||
|
if ":" not in str(amt):
|
||||||
|
amt = f"GOA:{amt}"
|
||||||
|
# FP pattern: amount + exchange_url (not suggested_amount alone)
|
||||||
code, wd = http_json(
|
code, wd = http_json(
|
||||||
"POST",
|
"POST",
|
||||||
f"{BANK}/accounts/{USER}/withdrawals",
|
f"{BANK}/accounts/{USER}/withdrawals",
|
||||||
{"suggested_amount": AMOUNT},
|
{"amount": amt, "exchange_url": EXCHANGE},
|
||||||
headers={"Authorization": f"Bearer {access}"},
|
headers={"Authorization": f"Bearer {access}"},
|
||||||
)
|
)
|
||||||
if code not in (200, 201):
|
if code not in (200, 201):
|
||||||
|
|
@ -142,7 +150,7 @@ def mint_withdraw() -> dict:
|
||||||
uri = normalize_taler_withdraw_uri(str(uri).strip())
|
uri = normalize_taler_withdraw_uri(str(uri).strip())
|
||||||
LANDING.mkdir(parents=True, exist_ok=True)
|
LANDING.mkdir(parents=True, exist_ok=True)
|
||||||
(LANDING / "withdraw.uri").write_text(uri + "\n")
|
(LANDING / "withdraw.uri").write_text(uri + "\n")
|
||||||
(LANDING / "withdraw.amount").write_text(AMOUNT + "\n")
|
(LANDING / "withdraw.amount").write_text(amt + "\n")
|
||||||
(LANDING / "withdraw.created").write_text(
|
(LANDING / "withdraw.created").write_text(
|
||||||
time.strftime("%Y-%m-%dT%H:%MZ", time.gmtime()) + "\n"
|
time.strftime("%Y-%m-%dT%H:%MZ", time.gmtime()) + "\n"
|
||||||
)
|
)
|
||||||
|
|
@ -151,7 +159,8 @@ def mint_withdraw() -> dict:
|
||||||
"ok": True,
|
"ok": True,
|
||||||
"taler_withdraw_uri": uri,
|
"taler_withdraw_uri": uri,
|
||||||
"withdrawal_id": wid,
|
"withdrawal_id": wid,
|
||||||
"amount": AMOUNT,
|
"amount": amt,
|
||||||
|
"exchange_url": EXCHANGE,
|
||||||
"pool_account": USER,
|
"pool_account": USER,
|
||||||
"taler_integration_base": f"{BANK_PUBLIC}/taler-integration/",
|
"taler_integration_base": f"{BANK_PUBLIC}/taler-integration/",
|
||||||
"hint": "Open in GNU Taler Wallet (iOS/Android/desktop). No bank registration.",
|
"hint": "Open in GNU Taler Wallet (iOS/Android/desktop). No bank registration.",
|
||||||
|
|
@ -301,15 +310,54 @@ class Handler(BaseHTTPRequestHandler):
|
||||||
self._cors()
|
self._cors()
|
||||||
self.end_headers()
|
self.end_headers()
|
||||||
|
|
||||||
def do_GET(self):
|
def _proxy_wd(self, parsed):
|
||||||
path = self.path.split("?", 1)[0]
|
"""GET withdrawal-operation: inject suggested/required exchange (Android/iOS)."""
|
||||||
|
q = ("?" + parsed.query) if parsed.query else ""
|
||||||
|
url = f"{BANK}{parsed.path}{q}"
|
||||||
|
ctx = ssl.create_default_context()
|
||||||
|
req = urllib.request.Request(url, method="GET")
|
||||||
try:
|
try:
|
||||||
|
with urllib.request.urlopen(req, context=ctx, timeout=70) as r:
|
||||||
|
raw = r.read()
|
||||||
|
status = r.status
|
||||||
|
except urllib.error.HTTPError as e:
|
||||||
|
raw = e.read()
|
||||||
|
status = e.code
|
||||||
|
except Exception as e:
|
||||||
|
raw = json.dumps({"ok": False, "error": str(e)}).encode()
|
||||||
|
status = 502
|
||||||
|
try:
|
||||||
|
body = json.loads(raw.decode() or "{}")
|
||||||
|
if isinstance(body, dict):
|
||||||
|
body.setdefault("suggested_exchange", EXCHANGE)
|
||||||
|
body.setdefault("required_exchange", EXCHANGE)
|
||||||
|
raw = json.dumps(body).encode()
|
||||||
|
except Exception:
|
||||||
|
pass
|
||||||
|
self.send_response(status)
|
||||||
|
self._cors()
|
||||||
|
self.send_header("Content-Type", "application/json")
|
||||||
|
self.send_header("Content-Length", str(len(raw)))
|
||||||
|
self.end_headers()
|
||||||
|
self.wfile.write(raw)
|
||||||
|
|
||||||
|
def do_GET(self):
|
||||||
|
from urllib.parse import parse_qs, urlparse
|
||||||
|
|
||||||
|
parsed = urlparse(self.path)
|
||||||
|
path = parsed.path
|
||||||
|
qs = parse_qs(parsed.query)
|
||||||
|
try:
|
||||||
|
if path.startswith("/taler-integration/withdrawal-operation/"):
|
||||||
|
self._proxy_wd(parsed)
|
||||||
|
return
|
||||||
if path in (
|
if path in (
|
||||||
"/",
|
"/",
|
||||||
"/demo-withdraw.json",
|
"/demo-withdraw.json",
|
||||||
"/intro/demo-withdraw.json",
|
"/intro/demo-withdraw.json",
|
||||||
):
|
):
|
||||||
body = mint_withdraw()
|
amt = (qs.get("amount") or qs.get("n") or [None])[0]
|
||||||
|
body = mint_withdraw(amt)
|
||||||
elif path in (
|
elif path in (
|
||||||
"/auto-account.json",
|
"/auto-account.json",
|
||||||
"/intro/auto-account.json",
|
"/intro/auto-account.json",
|
||||||
|
|
|
||||||
|
|
@ -14,61 +14,86 @@ podman exec -u root "$CTR" chmod 755 \
|
||||||
/usr/local/bin/auto-confirm-withdrawals.sh \
|
/usr/local/bin/auto-confirm-withdrawals.sh \
|
||||||
/usr/local/bin/refresh-demo-withdraw.sh
|
/usr/local/bin/refresh-demo-withdraw.sh
|
||||||
|
|
||||||
# nginx: proxy demo-withdraw.json
|
# nginx: ensure demo-withdraw / auto-account / Integration → :19096
|
||||||
NGX=/etc/nginx/sites-available/bank-landing
|
# FP pattern: Caddy @wdGet GET → :9013 → this location → demo-api
|
||||||
|
# (single in-CTR python; no nested podman)
|
||||||
podman exec -u root "$CTR" bash -lc '
|
podman exec -u root "$CTR" bash -lc '
|
||||||
set -e
|
set -e
|
||||||
f=/etc/nginx/sites-available/bank-landing
|
python3 - <<PY
|
||||||
if ! grep -q demo-withdraw.json "$f"; then
|
|
||||||
# insert before location /intro/
|
|
||||||
python3 - <<PY
|
|
||||||
from pathlib import Path
|
from pathlib import Path
|
||||||
p=Path("/etc/nginx/sites-available/bank-landing")
|
p = Path("/etc/nginx/sites-available/bank-landing")
|
||||||
t=p.read_text()
|
t = p.read_text()
|
||||||
block=""" location = /intro/demo-withdraw.json {
|
changed = False
|
||||||
|
anchor = " location /intro/ {"
|
||||||
|
blocks = []
|
||||||
|
if "demo-withdraw.json" not in t:
|
||||||
|
blocks.append(""" location = /intro/demo-withdraw.json {
|
||||||
proxy_pass http://127.0.0.1:19096/demo-withdraw.json;
|
proxy_pass http://127.0.0.1:19096/demo-withdraw.json;
|
||||||
proxy_http_version 1.1;
|
proxy_http_version 1.1;
|
||||||
proxy_set_header Host \$host;
|
proxy_set_header Host \$host;
|
||||||
add_header Cache-Control "no-store" always;
|
add_header Cache-Control "no-store" always;
|
||||||
add_header Access-Control-Allow-Origin * always;
|
add_header Access-Control-Allow-Origin * always;
|
||||||
}
|
}
|
||||||
"""
|
""")
|
||||||
if "demo-withdraw.json" not in t:
|
print("nginx demo-withdraw location added")
|
||||||
t=t.replace(" location /intro/ {", block+" location /intro/ {", 1)
|
|
||||||
p.write_text(t)
|
|
||||||
print("nginx location added")
|
|
||||||
else:
|
else:
|
||||||
print("nginx already has demo-withdraw")
|
print("nginx already has demo-withdraw")
|
||||||
if "auto-account.json" not in t:
|
if "auto-account.json" not in t:
|
||||||
t=p.read_text()
|
blocks.append(""" location = /intro/auto-account.json {
|
||||||
block2=""" location = /intro/auto-account.json {
|
|
||||||
proxy_pass http://127.0.0.1:19096/auto-account.json;
|
proxy_pass http://127.0.0.1:19096/auto-account.json;
|
||||||
proxy_http_version 1.1;
|
proxy_http_version 1.1;
|
||||||
proxy_set_header Host \$host;
|
proxy_set_header Host \$host;
|
||||||
add_header Cache-Control "no-store" always;
|
add_header Cache-Control "no-store" always;
|
||||||
add_header Access-Control-Allow-Origin * always;
|
add_header Access-Control-Allow-Origin * always;
|
||||||
}
|
}
|
||||||
"""
|
""")
|
||||||
t=t.replace(" location /intro/ {", block2+" location /intro/ {", 1)
|
|
||||||
p.write_text(t)
|
|
||||||
print("nginx auto-account location added")
|
print("nginx auto-account location added")
|
||||||
else:
|
else:
|
||||||
print("nginx already has auto-account")
|
print("nginx already has auto-account")
|
||||||
|
if "taler-integration/withdrawal-operation" not in t:
|
||||||
|
blocks.append(""" # FP pattern: GET Integration via demo-api (suggested/required_exchange)
|
||||||
|
# Caddy @wdGet → :9013 → here → :19096; POST/other stays on :9012
|
||||||
|
location /taler-integration/withdrawal-operation/ {
|
||||||
|
proxy_pass http://127.0.0.1:19096;
|
||||||
|
proxy_http_version 1.1;
|
||||||
|
proxy_set_header Host \$host;
|
||||||
|
proxy_read_timeout 70s;
|
||||||
|
add_header Cache-Control "no-store" always;
|
||||||
|
add_header Access-Control-Allow-Origin * always;
|
||||||
|
}
|
||||||
|
""")
|
||||||
|
print("nginx Integration withdrawal-operation location added")
|
||||||
|
else:
|
||||||
|
print("nginx already has Integration withdrawal-operation")
|
||||||
|
if blocks:
|
||||||
|
if anchor not in t:
|
||||||
|
raise SystemExit("nginx anchor location /intro/ not found")
|
||||||
|
t = t.replace(anchor, "".join(blocks) + anchor, 1)
|
||||||
|
p.write_text(t)
|
||||||
|
changed = True
|
||||||
|
if not changed:
|
||||||
|
print("nginx locations already complete")
|
||||||
PY
|
PY
|
||||||
nginx -t && nginx -s reload || true
|
nginx -t && nginx -s reload || true
|
||||||
else
|
|
||||||
echo "nginx already configured"
|
|
||||||
fi
|
|
||||||
'
|
'
|
||||||
|
|
||||||
# start/restart API + single auto-confirm loop (flock inside script)
|
# start/restart API + single auto-confirm loop (flock inside script)
|
||||||
podman exec -u root "$CTR" bash -lc '
|
podman exec -u root "$CTR" bash -lc '
|
||||||
# stop demo-withdraw by pid (avoid pkill -f self-match)
|
# stop demo-withdraw by pid (avoid pkill -f self-match); wait for :19096
|
||||||
ps -eo pid=,args= | awk "/demo-withdraw-api\\.py/ && !/awk/ {print \$1}" | while read p; do kill \$p 2>/dev/null || true; done
|
ps -eo pid=,args= | awk "/demo-withdraw-api\\.py/ && !/awk/ {print \$1}" | while read p; do kill \$p 2>/dev/null || true; done
|
||||||
sleep 0.3
|
sleep 1
|
||||||
|
ps -eo pid=,args= | awk "/demo-withdraw-api\\.py/ && !/awk/ {print \$1}" | while read p; do kill -9 \$p 2>/dev/null || true; done
|
||||||
|
sleep 0.5
|
||||||
nohup python3 /usr/local/bin/demo-withdraw-api.py \
|
nohup python3 /usr/local/bin/demo-withdraw-api.py \
|
||||||
>>/var/log/demo-withdraw-api.log 2>&1 </dev/null &
|
>>/var/log/demo-withdraw-api.log 2>&1 </dev/null &
|
||||||
echo "api pid $!"
|
echo "api pid $!"
|
||||||
|
# fail loud if bind lost to a leftover listener
|
||||||
|
sleep 0.5
|
||||||
|
ps -eo pid=,args= | awk "/demo-withdraw-api\\.py/ && !/awk/" || {
|
||||||
|
echo "ERROR: demo-withdraw-api failed to stay up (port busy?)" >&2
|
||||||
|
tail -20 /var/log/demo-withdraw-api.log >&2 || true
|
||||||
|
exit 1
|
||||||
|
}
|
||||||
# stop auto-confirm by pid
|
# stop auto-confirm by pid
|
||||||
ps -eo pid=,args= | awk "/auto-confirm-withdrawals\\.sh --loop/ && !/awk/ {print \$1}" | while read p; do kill \$p 2>/dev/null || true; done
|
ps -eo pid=,args= | awk "/auto-confirm-withdrawals\\.sh --loop/ && !/awk/ {print \$1}" | while read p; do kill \$p 2>/dev/null || true; done
|
||||||
sleep 0.5
|
sleep 0.5
|
||||||
|
|
|
||||||
|
|
@ -203,8 +203,11 @@ if [ -n "$ADMIN_TOKEN" ]; then
|
||||||
u=$(printf '%s' "$line" | sed -n 's/.*"username"[[:space:]]*:[[:space:]]*"\([^"]*\)".*/\1/p')
|
u=$(printf '%s' "$line" | sed -n 's/.*"username"[[:space:]]*:[[:space:]]*"\([^"]*\)".*/\1/p')
|
||||||
[ -n "$u" ] && echo "$u"
|
[ -n "$u" ] && echo "$u"
|
||||||
done >"$WORKDIR/usernames.txt" || true
|
done >"$WORKDIR/usernames.txt" || true
|
||||||
ACCOUNTS_N=$(grep -cve '^\s*$' "$WORKDIR/usernames.txt" 2>/dev/null || echo 0)
|
# grep -c exits 1 on zero matches; do not `|| echo 0` (would print 0\n0 and break JSON)
|
||||||
ACCOUNTS_USERS=$(grep -Eve '^(admin|exchange)$' "$WORKDIR/usernames.txt" 2>/dev/null | grep -cve '^\s*$' || echo 0)
|
ACCOUNTS_N=$(grep -cve '^\s*$' "$WORKDIR/usernames.txt" 2>/dev/null || true)
|
||||||
|
ACCOUNTS_N=${ACCOUNTS_N:-0}
|
||||||
|
ACCOUNTS_USERS=$(grep -Eve '^(admin|exchange)$' "$WORKDIR/usernames.txt" 2>/dev/null | grep -cve '^\s*$' || true)
|
||||||
|
ACCOUNTS_USERS=${ACCOUNTS_USERS:-0}
|
||||||
else
|
else
|
||||||
echo "$BANK_USER" >"$WORKDIR/usernames.txt"
|
echo "$BANK_USER" >"$WORKDIR/usernames.txt"
|
||||||
ACCOUNTS_N=1
|
ACCOUNTS_N=1
|
||||||
|
|
@ -323,9 +326,11 @@ sort -t$'\t' -k2,2nr "$WORKDIR/all-in.tsv" -o "$WORKDIR/all-in-sorted.tsv" 2>/de
|
||||||
|| cp "$WORKDIR/all-in.tsv" "$WORKDIR/all-in-sorted.tsv"
|
|| cp "$WORKDIR/all-in.tsv" "$WORKDIR/all-in-sorted.tsv"
|
||||||
|
|
||||||
# Unique reserves = individual wallet withdraws (each wallet reserve_pub)
|
# Unique reserves = individual wallet withdraws (each wallet reserve_pub)
|
||||||
WALLETS_N=$(awk -F'\t' '$5!=""{print $5}' "$WORKDIR/all-wd-sorted.tsv" | sort -u | grep -cve '^\s*$' || echo 0)
|
WALLETS_N=$(awk -F'\t' '$5!=""{print $5}' "$WORKDIR/all-wd-sorted.tsv" | sort -u | grep -cve '^\s*$' || true)
|
||||||
|
WALLETS_N=${WALLETS_N:-0}
|
||||||
# Accounts that funded at least one withdraw
|
# Accounts that funded at least one withdraw
|
||||||
ACCOUNTS_WITH_WD=$(awk -F'\t' '$4!=""{print $4}' "$WORKDIR/all-wd-sorted.tsv" | sort -u | grep -cve '^\s*$' || echo 0)
|
ACCOUNTS_WITH_WD=$(awk -F'\t' '$4!=""{print $4}' "$WORKDIR/all-wd-sorted.tsv" | sort -u | grep -cve '^\s*$' || true)
|
||||||
|
ACCOUNTS_WITH_WD=${ACCOUNTS_WITH_WD:-0}
|
||||||
|
|
||||||
# Aggregates
|
# Aggregates
|
||||||
NOW=$(now_unix)
|
NOW=$(now_unix)
|
||||||
|
|
|
||||||
|
|
@ -2,7 +2,8 @@
|
||||||
# Run INSIDE taler-hacktivism-exchange-ansible.
|
# Run INSIDE taler-hacktivism-exchange-ansible.
|
||||||
# Writes /var/www/exchange-landing/stats.json
|
# Writes /var/www/exchange-landing/stats.json
|
||||||
#
|
#
|
||||||
# Data lives in Postgres DB taler-exchange, schema exchange.*
|
# Data lives in Postgres (DB name varies: vanilla regio-ng uses "exchange";
|
||||||
|
# older hacktivism images use "taler-exchange"). Schema is exchange.*.
|
||||||
# (reserves, reserves_in, known_coins, withdraw, denominations, …)
|
# (reserves, reserves_in, known_coins, withdraw, denominations, …)
|
||||||
#
|
#
|
||||||
# IMPORTANT: never use psql -F$'\t' -v ON_ERROR_STOP=1
|
# IMPORTANT: never use psql -F$'\t' -v ON_ERROR_STOP=1
|
||||||
|
|
@ -16,7 +17,38 @@ LANDING_DIR="${LANDING_DIR:-/var/www/exchange-landing}"
|
||||||
OUT="$LANDING_DIR/stats.json"
|
OUT="$LANDING_DIR/stats.json"
|
||||||
RUN="$LANDING_DIR/stats-run.json"
|
RUN="$LANDING_DIR/stats-run.json"
|
||||||
TMP="${OUT}.tmp.$$"
|
TMP="${OUT}.tmp.$$"
|
||||||
DB="${EXCHANGE_DB:-taler-exchange}"
|
# Resolve DB: EXCHANGE_DB env → CONFIG=postgres:///NAME in secret conf → fallbacks
|
||||||
|
_resolve_exchange_db() {
|
||||||
|
if [ -n "${EXCHANGE_DB:-}" ]; then
|
||||||
|
printf '%s' "$EXCHANGE_DB"
|
||||||
|
return
|
||||||
|
fi
|
||||||
|
local conf c name
|
||||||
|
for conf in \
|
||||||
|
/etc/taler-exchange/secrets/exchange-db.secret.conf \
|
||||||
|
/etc/taler/secrets/exchange-db.secret.conf; do
|
||||||
|
if [ -r "$conf" ]; then
|
||||||
|
c=$(grep -E '^[[:space:]]*CONFIG[[:space:]]*=' "$conf" | head -1 | sed 's/^[^=]*=[[:space:]]*//')
|
||||||
|
# postgres:///dbname or postgres://user@/dbname
|
||||||
|
name=$(printf '%s' "$c" | sed -n 's#.*postgres://[^/]*/\([^?]*\).*#\1#p')
|
||||||
|
if [ -n "$name" ]; then
|
||||||
|
printf '%s' "$name"
|
||||||
|
return
|
||||||
|
fi
|
||||||
|
fi
|
||||||
|
done
|
||||||
|
for name in exchange taler-exchange; do
|
||||||
|
if [ -d "/var/lib/postgresql" ] || command -v psql >/dev/null 2>&1; then
|
||||||
|
if runuser -u postgres -- psql -d "$name" -At -c 'SELECT 1' >/dev/null 2>&1 \
|
||||||
|
|| su -s /bin/bash postgres -c "psql -d $name -At -c 'SELECT 1'" >/dev/null 2>&1; then
|
||||||
|
printf '%s' "$name"
|
||||||
|
return
|
||||||
|
fi
|
||||||
|
fi
|
||||||
|
done
|
||||||
|
printf '%s' "taler-exchange"
|
||||||
|
}
|
||||||
|
DB="$(_resolve_exchange_db)"
|
||||||
BASE_URL="${EXCHANGE_BASE_URL:-https://exchange.hacktivism.ch}"
|
BASE_URL="${EXCHANGE_BASE_URL:-https://exchange.hacktivism.ch}"
|
||||||
ERRLOG="${LANDING_STATS_ERRLOG:-/var/log/landing-stats-exchange.err}"
|
ERRLOG="${LANDING_STATS_ERRLOG:-/var/log/landing-stats-exchange.err}"
|
||||||
mkdir -p "$LANDING_DIR"
|
mkdir -p "$LANDING_DIR"
|
||||||
|
|
@ -253,7 +285,7 @@ cat >"$TMP" <<EOF
|
||||||
{
|
{
|
||||||
"ok": true,
|
"ok": true,
|
||||||
"source": "exchange-db",
|
"source": "exchange-db",
|
||||||
"schema": "exchange.* @ taler-exchange",
|
"schema": "exchange.* @ ${DB}",
|
||||||
"focus": "coins",
|
"focus": "coins",
|
||||||
"timezone": $(json_str "$TZ"),
|
"timezone": $(json_str "$TZ"),
|
||||||
"generated_at": $(json_str "$GEN"),
|
"generated_at": $(json_str "$GEN"),
|
||||||
|
|
|
||||||
|
|
@ -56,9 +56,9 @@ COLLECT_MERCHANT="${COLLECT_MERCHANT:-1}"
|
||||||
COLLECT_RESOURCES="${COLLECT_RESOURCES:-1}"
|
COLLECT_RESOURCES="${COLLECT_RESOURCES:-1}"
|
||||||
|
|
||||||
# Containers (empty = skip podman publish for that role)
|
# Containers (empty = skip podman publish for that role)
|
||||||
BANK_CTR="${BANK_CTR:-taler-hacktivism-bank}"
|
BANK_CTR="${BANK_CTR:-goa-regio-ng}"
|
||||||
EX_CTR="${EX_CTR:-taler-hacktivism-exchange-ansible}"
|
EX_CTR="${EX_CTR:-goa-regio-ng}"
|
||||||
MER_CTR="${MER_CTR:-taler-hacktivism}"
|
MER_CTR="${MER_CTR:-goa-regio-ng}"
|
||||||
|
|
||||||
BANK_URL="${BANK_URL:-http://127.0.0.1:9012}"
|
BANK_URL="${BANK_URL:-http://127.0.0.1:9012}"
|
||||||
BANK_PUBLIC_URL="${BANK_PUBLIC_URL:-https://bank.hacktivism.ch}"
|
BANK_PUBLIC_URL="${BANK_PUBLIC_URL:-https://bank.hacktivism.ch}"
|
||||||
|
|
@ -322,10 +322,14 @@ run_incontainer_stats() {
|
||||||
fi
|
fi
|
||||||
log "$label: run $script inside $ctr"
|
log "$label: run $script inside $ctr"
|
||||||
set +e
|
set +e
|
||||||
|
# Pass optional DB overrides (vanilla regio-ng: EXCHANGE_DB=exchange)
|
||||||
podman exec \
|
podman exec \
|
||||||
-e LANDING_DIR="$landing" \
|
-e LANDING_DIR="$landing" \
|
||||||
-e TZ=Europe/Zurich \
|
-e TZ=Europe/Zurich \
|
||||||
-e PATH="/usr/local/sbin:/usr/local/bin:/usr/sbin:/usr/bin:/sbin:/bin" \
|
-e PATH="/usr/local/sbin:/usr/local/bin:/usr/sbin:/usr/bin:/sbin:/bin" \
|
||||||
|
${EXCHANGE_DB:+-e EXCHANGE_DB="$EXCHANGE_DB"} \
|
||||||
|
${MERCHANT_DB:+-e MERCHANT_DB="$MERCHANT_DB"} \
|
||||||
|
${EXCHANGE_BASE_URL:+-e EXCHANGE_BASE_URL="$EXCHANGE_BASE_URL"} \
|
||||||
"$ctr" bash "$script" >>"$LOG_DIR/${label}.log" 2>&1
|
"$ctr" bash "$script" >>"$LOG_DIR/${label}.log" 2>&1
|
||||||
local ec=$?
|
local ec=$?
|
||||||
set -e
|
set -e
|
||||||
|
|
|
||||||
|
|
@ -130,6 +130,11 @@ def get_token(bank: str, user: str, password: str) -> str:
|
||||||
|
|
||||||
def list_all_accounts(bank: str, token: str, delta: int) -> List[str]:
|
def list_all_accounts(bank: str, token: str, delta: int) -> List[str]:
|
||||||
"""List accounts; page with start if needed."""
|
"""List accounts; page with start if needed."""
|
||||||
|
# libeufin-bank: |delta| must be in 1..1024 (same floor as Param 'limit')
|
||||||
|
if delta == 0:
|
||||||
|
delta = -1024
|
||||||
|
elif abs(delta) > 1024:
|
||||||
|
delta = -1024 if delta < 0 else 1024
|
||||||
names: List[str] = []
|
names: List[str] = []
|
||||||
seen: Set[str] = set()
|
seen: Set[str] = set()
|
||||||
start: Optional[int] = None
|
start: Optional[int] = None
|
||||||
|
|
@ -213,9 +218,10 @@ def iter_transactions(
|
||||||
pages = 0
|
pages = 0
|
||||||
safety = max_pages if max_pages > 0 else 10_000
|
safety = max_pages if max_pages > 0 else 10_000
|
||||||
|
|
||||||
|
page = min(abs(page), 1024) or 1024
|
||||||
while pages < safety:
|
while pages < safety:
|
||||||
pages += 1
|
pages += 1
|
||||||
q = f"delta=-{abs(page)}"
|
q = f"delta=-{page}"
|
||||||
if start is not None:
|
if start is not None:
|
||||||
q += f"&start={start}"
|
q += f"&start={start}"
|
||||||
code, data, raw = http_json(
|
code, data, raw = http_json(
|
||||||
|
|
@ -364,7 +370,7 @@ def main() -> int:
|
||||||
ap.add_argument(
|
ap.add_argument(
|
||||||
"--accounts-delta",
|
"--accounts-delta",
|
||||||
type=int,
|
type=int,
|
||||||
default=int(env("ACCOUNTS_DELTA", "-10000") or "-10000"),
|
default=int(env("ACCOUNTS_DELTA", "-1024") or "-1024"),
|
||||||
)
|
)
|
||||||
ap.add_argument("--recent", type=int, default=int(env("RECENT_WD_N", "10") or "10"))
|
ap.add_argument("--recent", type=int, default=int(env("RECENT_WD_N", "10") or "10"))
|
||||||
ap.add_argument("--public-base", default=env("BANK_PUBLIC_URL", "https://bank.hacktivism.ch"))
|
ap.add_argument("--public-base", default=env("BANK_PUBLIC_URL", "https://bank.hacktivism.ch"))
|
||||||
|
|
|
||||||
|
|
@ -3,9 +3,11 @@
|
||||||
# listens on 9014 (exchange) / 9015 (merchant).
|
# listens on 9014 (exchange) / 9015 (merchant).
|
||||||
#
|
#
|
||||||
# Port map (wire these in Caddy / podman -p):
|
# Port map (wire these in Caddy / podman -p):
|
||||||
# 9013 bank landing (already on taler-hacktivism-bank)
|
# 9013 bank landing
|
||||||
# 9014 exchange landing (taler-hacktivism-exchange-ansible)
|
# 9014 exchange landing
|
||||||
# 9015 merchant landing (taler-hacktivism)
|
# 9015 merchant landing
|
||||||
|
# Unified vanilla CTR: set LANDING_CTR=goa-regio-ng (default) so all three
|
||||||
|
# landings land in the same container publishing 9010-9015.
|
||||||
#
|
#
|
||||||
# Run on koopa (host) with podman access.
|
# Run on koopa (host) with podman access.
|
||||||
set -euo pipefail
|
set -euo pipefail
|
||||||
|
|
@ -18,9 +20,10 @@ SRC_BANK="${SRC_BANK:-$ROOT/configs/bank-landing}"
|
||||||
SRC_QR="${SRC_QR:-$ROOT/configs/bank-landing/qrcode.min.js}"
|
SRC_QR="${SRC_QR:-$ROOT/configs/bank-landing/qrcode.min.js}"
|
||||||
SRC_GOA_AMT="${SRC_GOA_AMT:-$ROOT/configs/shared/goa-amount.js}"
|
SRC_GOA_AMT="${SRC_GOA_AMT:-$ROOT/configs/shared/goa-amount.js}"
|
||||||
|
|
||||||
C_EX=taler-hacktivism-exchange-ansible
|
LANDING_CTR="${LANDING_CTR:-goa-regio-ng}"
|
||||||
C_MER=taler-hacktivism
|
C_EX="${C_EX:-$LANDING_CTR}"
|
||||||
C_BANK="${C_BANK:-taler-hacktivism-bank}"
|
C_MER="${C_MER:-$LANDING_CTR}"
|
||||||
|
C_BANK="${C_BANK:-$LANDING_CTR}"
|
||||||
|
|
||||||
# Prefer per-landing copy, then shared (keeps bank/exchange/merchant in sync)
|
# Prefer per-landing copy, then shared (keeps bank/exchange/merchant in sync)
|
||||||
resolve_goa_amount() {
|
resolve_goa_amount() {
|
||||||
|
|
@ -104,8 +107,8 @@ podman exec "$C_MER" curl -sS -m 3 -o /dev/null -w "merchant_landing=%{http_code
|
||||||
|
|
||||||
echo
|
echo
|
||||||
echo "PORTS TO WIRE (host → Caddy / firewall / podman -p):"
|
echo "PORTS TO WIRE (host → Caddy / firewall / podman -p):"
|
||||||
echo " 9013 bank landing (taler-hacktivism-bank) already published"
|
echo " 9013 bank landing ($C_BANK)"
|
||||||
echo " 9014 exchange landing (taler-hacktivism-exchange-ansible) NEEDS -p 9014:9014"
|
echo " 9014 exchange landing ($C_EX)"
|
||||||
echo " 9015 merchant landing (taler-hacktivism) NEEDS -p 9015:9015"
|
echo " 9015 merchant landing ($C_MER)"
|
||||||
echo
|
echo
|
||||||
echo "If host curl to :9014/:9015 fails, republish pasta ports (commit+replace) — see README."
|
echo "If host curl to :9014/:9015 fails, republish pasta ports (commit+replace) — see README."
|
||||||
|
|
|
||||||
|
|
@ -1,8 +1,8 @@
|
||||||
# Stack profile: GOA / hacktivism
|
# Stack profile: GOA / goa-regio-ng vanilla (9010-9015)
|
||||||
# systemd EnvironmentFile: KEY=value only (no export)
|
# systemd EnvironmentFile: KEY=value only (no export)
|
||||||
BANK_CTR=taler-hacktivism-bank
|
BANK_CTR=goa-regio-ng
|
||||||
EX_CTR=taler-hacktivism-exchange-ansible
|
EX_CTR=goa-regio-ng
|
||||||
MER_CTR=taler-hacktivism
|
MER_CTR=goa-regio-ng
|
||||||
BANK_URL=http://127.0.0.1:9012
|
BANK_URL=http://127.0.0.1:9012
|
||||||
BANK_PUBLIC_URL=https://bank.hacktivism.ch
|
BANK_PUBLIC_URL=https://bank.hacktivism.ch
|
||||||
EXCHANGE_CONFIG_URL=https://exchange.hacktivism.ch/config
|
EXCHANGE_CONFIG_URL=https://exchange.hacktivism.ch/config
|
||||||
|
|
@ -15,4 +15,4 @@ COLLECT_EXCHANGE=1
|
||||||
COLLECT_MERCHANT=1
|
COLLECT_MERCHANT=1
|
||||||
COLLECT_RESOURCES=1
|
COLLECT_RESOURCES=1
|
||||||
PUBLISH_PODMAN=1
|
PUBLISH_PODMAN=1
|
||||||
STATS_SOURCE_LABEL="host collect_bank_stats.py goa hacktivism"
|
STATS_SOURCE_LABEL="host collect_bank_stats.py goa regio-ng vanilla"
|
||||||
|
|
|
||||||
179
scripts/taler-landing/stamp-landing-stack.sh
Normal file
179
scripts/taler-landing/stamp-landing-stack.sh
Normal file
|
|
@ -0,0 +1,179 @@
|
||||||
|
#!/usr/bin/env bash
|
||||||
|
# Fill landing stack-bar package versions from live CTR dpkg (not hand-edited HTML).
|
||||||
|
#
|
||||||
|
# Reads: configs/shared/landing-stack.json (package lists + vanilla href)
|
||||||
|
# Writes: configs/{bank,exchange,merchant}-landing/index.html (<!-- stack-bar --> block)
|
||||||
|
# configs/shared/landing-stack-versions.json (last dpkg snapshot)
|
||||||
|
#
|
||||||
|
# Usage (from koopa-admin-log root, laptop or koopa):
|
||||||
|
# ./scripts/taler-landing/stamp-landing-stack.sh
|
||||||
|
# ./scripts/taler-landing/stamp-landing-stack.sh --ctr goa-regio-ng --ssh hernani@192.168.100.95
|
||||||
|
# ./scripts/taler-landing/stamp-landing-stack.sh bank exchange
|
||||||
|
#
|
||||||
|
# After this, bump footer dates with:
|
||||||
|
# ./scripts/taler-landing/stamp-landing-version.sh --bump --all
|
||||||
|
# Then deploy (unified vanilla CTR):
|
||||||
|
# LANDING_CTR=goa-regio-ng ./scripts/taler-landing/deploy-landings.sh
|
||||||
|
set -euo pipefail
|
||||||
|
|
||||||
|
ROOT="$(cd "$(dirname "$0")/../.." && pwd)"
|
||||||
|
STACK_FILE="$ROOT/configs/shared/landing-stack.json"
|
||||||
|
VERS_OUT="$ROOT/configs/shared/landing-stack-versions.json"
|
||||||
|
|
||||||
|
CTR="${LANDING_CTR:-goa-regio-ng}"
|
||||||
|
SSH_HOST="${LANDING_SSH:-hernani@192.168.100.95}"
|
||||||
|
USE_SSH=1
|
||||||
|
sites=()
|
||||||
|
|
||||||
|
while [ $# -gt 0 ]; do
|
||||||
|
case "$1" in
|
||||||
|
--ctr) CTR="${2:-}"; shift 2 ;;
|
||||||
|
--ssh) SSH_HOST="${2:-}"; USE_SSH=1; shift 2 ;;
|
||||||
|
--local) USE_SSH=0; shift ;;
|
||||||
|
bank|exchange|merchant) sites+=("$1"); shift ;;
|
||||||
|
-h|--help)
|
||||||
|
sed -n '2,20p' "$0"
|
||||||
|
exit 0
|
||||||
|
;;
|
||||||
|
*) echo "unknown arg: $1" >&2; exit 2 ;;
|
||||||
|
esac
|
||||||
|
done
|
||||||
|
|
||||||
|
if [ "${#sites[@]}" -eq 0 ]; then
|
||||||
|
sites=(bank exchange merchant)
|
||||||
|
fi
|
||||||
|
|
||||||
|
[ -f "$STACK_FILE" ] || { echo "missing $STACK_FILE" >&2; exit 1; }
|
||||||
|
|
||||||
|
# Collect unique package names from JSON for the selected sites
|
||||||
|
mapfile -t PKGS < <(python3 - "$STACK_FILE" "${sites[@]}" <<'PY'
|
||||||
|
import json, sys
|
||||||
|
data = json.load(open(sys.argv[1], encoding="utf-8"))
|
||||||
|
sites = sys.argv[2:]
|
||||||
|
seen = []
|
||||||
|
for s in sites:
|
||||||
|
for p in data["sites"][s]["packages"]:
|
||||||
|
if p not in seen:
|
||||||
|
seen.append(p)
|
||||||
|
print("\n".join(seen))
|
||||||
|
PY
|
||||||
|
)
|
||||||
|
|
||||||
|
if [ "${#PKGS[@]}" -eq 0 ]; then
|
||||||
|
echo "no packages configured" >&2
|
||||||
|
exit 1
|
||||||
|
fi
|
||||||
|
|
||||||
|
pkg_args=$(printf '%q ' "${PKGS[@]}")
|
||||||
|
query_cmd="podman exec $(printf '%q' "$CTR") dpkg-query -W -f='\${Package}=\${Version}\n' ${pkg_args}"
|
||||||
|
|
||||||
|
if [ "$USE_SSH" = 1 ]; then
|
||||||
|
echo "dpkg ← ${SSH_HOST} ctr=${CTR}"
|
||||||
|
# shellcheck disable=SC2086
|
||||||
|
raw=$(ssh -o BatchMode=yes -o ConnectTimeout=12 "$SSH_HOST" "podman exec $(printf '%q' "$CTR") dpkg-query -W -f='\${Package}=\${Version}\n' ${pkg_args}")
|
||||||
|
else
|
||||||
|
echo "dpkg ← local ctr=${CTR}"
|
||||||
|
# shellcheck disable=SC2086
|
||||||
|
raw=$(podman exec "$CTR" dpkg-query -W -f='${Package}=${Version}\n' "${PKGS[@]}")
|
||||||
|
fi
|
||||||
|
|
||||||
|
python3 - "$STACK_FILE" "$VERS_OUT" "$ROOT" "$CTR" "$SSH_HOST" "$raw" "${sites[@]}" <<'PY'
|
||||||
|
import json, re, sys
|
||||||
|
from pathlib import Path
|
||||||
|
from datetime import datetime
|
||||||
|
from zoneinfo import ZoneInfo
|
||||||
|
|
||||||
|
stack_path, vers_out, root, ctr, ssh_host, raw = sys.argv[1:7]
|
||||||
|
sites = sys.argv[7:]
|
||||||
|
stack = json.load(open(stack_path, encoding="utf-8"))
|
||||||
|
tz = ZoneInfo("Europe/Zurich")
|
||||||
|
now = datetime.now(tz)
|
||||||
|
iso = now.isoformat(timespec="seconds")
|
||||||
|
human = now.strftime("%Y-%m-%d %H:%M %Z")
|
||||||
|
|
||||||
|
versions = {}
|
||||||
|
for line in raw.splitlines():
|
||||||
|
line = line.strip()
|
||||||
|
if not line or "=" not in line:
|
||||||
|
continue
|
||||||
|
pkg, ver = line.split("=", 1)
|
||||||
|
# display upstream-ish: drop Debian revision (1.6.10-0+trixie → 1.6.10)
|
||||||
|
short = re.sub(r"-[0-9].*$", "", ver)
|
||||||
|
versions[pkg] = {"dpkg": ver, "display": short}
|
||||||
|
|
||||||
|
missing = []
|
||||||
|
for site in sites:
|
||||||
|
for pkg in stack["sites"][site]["packages"]:
|
||||||
|
if pkg not in versions:
|
||||||
|
missing.append(pkg)
|
||||||
|
if missing:
|
||||||
|
raise SystemExit(f"dpkg missing packages: {', '.join(sorted(set(missing)))}")
|
||||||
|
|
||||||
|
vanilla_href = stack["vanilla_href"]
|
||||||
|
vanilla_title = stack.get("vanilla_title", "vanilla")
|
||||||
|
sep = '<span class="sep">·</span>'
|
||||||
|
|
||||||
|
def render_block(site: str) -> str:
|
||||||
|
meta = stack["sites"][site]
|
||||||
|
parts = [
|
||||||
|
f' <!-- stack-bar -->',
|
||||||
|
f' <p class="stack-bar" role="note" title="{meta["title"]}">',
|
||||||
|
f' <strong>stack</strong>{sep}',
|
||||||
|
f' <a href="{vanilla_href}" target="_blank" rel="noopener noreferrer" title="{vanilla_title}">vanilla</a>',
|
||||||
|
]
|
||||||
|
for pkg in meta["packages"]:
|
||||||
|
disp = versions[pkg]["display"]
|
||||||
|
parts.append(f" {sep}")
|
||||||
|
parts.append(f" {pkg} {disp}")
|
||||||
|
parts.append(" </p>")
|
||||||
|
parts.append(" <!-- /stack-bar -->")
|
||||||
|
return "\n".join(parts) + "\n"
|
||||||
|
|
||||||
|
site_html = {
|
||||||
|
"bank": Path(root) / "configs/bank-landing/index.html",
|
||||||
|
"exchange": Path(root) / "configs/exchange-landing/index.html",
|
||||||
|
"merchant": Path(root) / "configs/merchant-landing/index.html",
|
||||||
|
}
|
||||||
|
|
||||||
|
marker_re = re.compile(r"[ \t]*<!-- stack-bar -->.*?<!-- /stack-bar -->\n?", re.S)
|
||||||
|
bare_re = re.compile(
|
||||||
|
r"[ \t]*<p class=\"stack-bar\"[^>]*>.*?</p>\n?",
|
||||||
|
re.S,
|
||||||
|
)
|
||||||
|
|
||||||
|
for site in sites:
|
||||||
|
path = site_html[site]
|
||||||
|
text = path.read_text(encoding="utf-8")
|
||||||
|
block = render_block(site)
|
||||||
|
if marker_re.search(text):
|
||||||
|
text = marker_re.sub(block, text, count=1)
|
||||||
|
elif bare_re.search(text):
|
||||||
|
text = bare_re.sub(block, text, count=1)
|
||||||
|
else:
|
||||||
|
raise SystemExit(f"no stack-bar in {path}")
|
||||||
|
path.write_text(text, encoding="utf-8")
|
||||||
|
print(f"stack → {path.relative_to(root)} ({site})")
|
||||||
|
|
||||||
|
out = {
|
||||||
|
"updated_iso": iso,
|
||||||
|
"updated_human": human,
|
||||||
|
"ctr": ctr,
|
||||||
|
"ssh": ssh_host if ssh_host else None,
|
||||||
|
"packages": versions,
|
||||||
|
"sites": {
|
||||||
|
site: {
|
||||||
|
"packages": {
|
||||||
|
pkg: versions[pkg]["display"]
|
||||||
|
for pkg in stack["sites"][site]["packages"]
|
||||||
|
}
|
||||||
|
}
|
||||||
|
for site in ("bank", "exchange", "merchant")
|
||||||
|
if site in stack["sites"]
|
||||||
|
},
|
||||||
|
}
|
||||||
|
Path(vers_out).write_text(json.dumps(out, indent=2, ensure_ascii=False) + "\n", encoding="utf-8")
|
||||||
|
print(f"versions → {Path(vers_out).relative_to(root)}")
|
||||||
|
print(f"OK stack stamped: {' '.join(sites)} @ {human}")
|
||||||
|
PY
|
||||||
|
|
||||||
|
echo "OK stamp-landing-stack: ${sites[*]} ← ${CTR}"
|
||||||
|
|
@ -3,6 +3,7 @@
|
||||||
# Host: koopa (hernani, podman). No Freigabe-bypass — caller must have Freigabe.
|
# Host: koopa (hernani, podman). No Freigabe-bypass — caller must have Freigabe.
|
||||||
#
|
#
|
||||||
# ./goa-live-backup-upgrade.sh dry-run
|
# ./goa-live-backup-upgrade.sh dry-run
|
||||||
|
# ./goa-live-backup-upgrade.sh backup # stamp only (no apt / no commit)
|
||||||
# ./goa-live-backup-upgrade.sh apply # backup + apt + commit + smoke
|
# ./goa-live-backup-upgrade.sh apply # backup + apt + commit + smoke
|
||||||
# ./goa-live-backup-upgrade.sh status
|
# ./goa-live-backup-upgrade.sh status
|
||||||
#
|
#
|
||||||
|
|
@ -312,6 +313,12 @@ cmd_status() {
|
||||||
|
|
||||||
case "$MODE" in
|
case "$MODE" in
|
||||||
dry-run|dry) cmd_dry_run ;;
|
dry-run|dry) cmd_dry_run ;;
|
||||||
|
backup)
|
||||||
|
set_status BACKUP_RUNNING
|
||||||
|
cmd_backup
|
||||||
|
set_status BACKUP_OK "STAMP=$STAMP" "CURRENT=$ROOT/current"
|
||||||
|
log "BACKUP_OK"
|
||||||
|
;;
|
||||||
apply) cmd_apply ;;
|
apply) cmd_apply ;;
|
||||||
status) cmd_status ;;
|
status) cmd_status ;;
|
||||||
-h|--help|help)
|
-h|--help|help)
|
||||||
|
|
@ -319,7 +326,7 @@ case "$MODE" in
|
||||||
exit 0
|
exit 0
|
||||||
;;
|
;;
|
||||||
*)
|
*)
|
||||||
echo "usage: $0 dry-run|apply|status" >&2
|
echo "usage: $0 dry-run|backup|apply|status" >&2
|
||||||
exit 2
|
exit 2
|
||||||
;;
|
;;
|
||||||
esac
|
esac
|
||||||
|
|
|
||||||
Loading…
Add table
Add a link
Reference in a new issue