i18n: English default, French for FrancPaysan (v1.1)

This commit is contained in:
Hernâni Marques 2026-07-18 14:57:14 +02:00
parent a2afe690b7
commit 2465347fd4
No known key found for this signature in database
GPG key ID: CB5738652768F7E9
13 changed files with 483 additions and 86 deletions

View file

@ -360,3 +360,7 @@ LADDER_LOAD=0 ./taler-monitoring.sh ladder
- SSH `koopa` or `koopa-external` (inside / load / sanity server bits) - SSH `koopa` or `koopa-external` (inside / load / sanity server bits)
- secrets under `koopa-admin-secrets/...` for e2e - secrets under `koopa-admin-secrets/...` for e2e
- `taler-wallet-cli` for e2e - `taler-wallet-cli` for e2e
## Languages (i18n)
Default **English**. FrancPaysan stacks use **French** (`TALER_MON_LANG=fr`, auto for `*lefrancpaysan*`). See `VERSIONS.md` and `i18n.sh`.

View file

@ -1 +1 @@
1.0.0 1.1.0

View file

@ -1,14 +1,36 @@
# VERSIONS — taler-monitoring # VERSIONS — taler-monitoring
Release history for the standalone **taler-monitoring** suite
(https://git.hacktivism.ch/hernani/taler-monitoring).
| Tag | Date (UTC) | Notes | | Tag | Date (UTC) | Notes |
|-----|------------|--------| |-----|------------|--------|
| **v1.0** | 2026-07-18 | Initial standalone release. Split from `koopa-admin-log` at `scripts/taler-monitoring/` (tip after monitoring host-agent/site-gen split commits). | | **v1.1** | 2026-07-18 | **i18n**: default UI/console language **English**; **French** for FrancPaysan (`TALER_MON_LANG=fr`, auto on `*lefrancpaysan*`). Sticky bar “what this monitors” EN/FR; badge/summary chrome + message translation via `i18n.sh`. |
| **v1.0** | 2026-07-18 | Initial standalone release. Split from `koopa-admin-log` (`scripts/taler-monitoring/`). Host-agent HTML, surface, aptdeploy, sticky bar. |
Source: https://git.hacktivism.ch/hernani/taler-monitoring ## Language (v1.1+)
| Context | Language |
|---------|----------|
| Default / GOA / hacktivism | **English** |
| FrancPaysan prod + stage (`*lefrancpaysan*`) | **French** (auto or `TALER_MON_LANG=fr`) |
```bash
# force
TALER_MON_LANG=fr ./taler-monitoring.sh -d lefrancpaysan.ch urls
TALER_MON_LANG=en ./taler-monitoring.sh -d lefrancpaysan.ch urls
```
Host-agent env examples:
- `host-agent/env/francpaysan.env.example``TALER_MON_LANG=fr`
- `host-agent/env/stagepaysan.env.example``TALER_MON_LANG=fr`
- `host-agent/env/hacktivism.env.example``TALER_MON_LANG=en`
## Pin a release
```bash ```bash
git clone https://git.hacktivism.ch/hernani/taler-monitoring.git ~/src/taler-monitoring git clone https://git.hacktivism.ch/hernani/taler-monitoring.git ~/src/taler-monitoring
cd ~/src/taler-monitoring && git checkout v1.0 # or stay on main cd ~/src/taler-monitoring && git checkout v1.1
``` ```
Default host-agent tracks `origin/main` (`SUITE_UPDATE_MODE=reset`). Pin with `SUITE_GIT_REF=v1.0` in `~/.config/taler-monitoring/env`. Default host-agent tracks `origin/main`. Pin with `SUITE_GIT_REF=v1.1` in `~/.config/taler-monitoring/env`.

View file

@ -24,3 +24,4 @@
# HTML_OUT=$HOME/monitoring-sites-staging # HTML_OUT=$HOME/monitoring-sites-staging
# MON_HOSTS="bank.hacktivism.ch exchange.hacktivism.ch taler.hacktivism.ch" # MON_HOSTS="bank.hacktivism.ch exchange.hacktivism.ch taler.hacktivism.ch"
# SOURCE_REPO_WEB=https://git.hacktivism.ch/hernani/taler-monitoring # SOURCE_REPO_WEB=https://git.hacktivism.ch/hernani/taler-monitoring
# TALER_MON_LANG=en # en default; fr for FrancPaysan (auto if domain *lefrancpaysan*)

View file

@ -22,3 +22,6 @@ SUITE_UPDATE_MODE=reset
SOURCE_REPO_WEB=https://git.hacktivism.ch/hernani/taler-monitoring SOURCE_REPO_WEB=https://git.hacktivism.ch/hernani/taler-monitoring
# DEPLOY_WWW_ROOT=/var/www/monitoring-sites # DEPLOY_WWW_ROOT=/var/www/monitoring-sites
# UI language for console + HTML sticky bar (en|fr)
TALER_MON_LANG=fr

View file

@ -22,3 +22,5 @@ SUITE_UPDATE_MODE=reset
SOURCE_REPO_WEB=https://git.hacktivism.ch/hernani/taler-monitoring SOURCE_REPO_WEB=https://git.hacktivism.ch/hernani/taler-monitoring
DEPLOY_WWW_ROOT=/var/www/monitoring-sites DEPLOY_WWW_ROOT=/var/www/monitoring-sites
TALER_MON_LANG=en

View file

@ -25,3 +25,6 @@ SOURCE_REPO_WEB=https://git.hacktivism.ch/hernani/taler-monitoring
# Optional public docroot if this user can write it # Optional public docroot if this user can write it
# DEPLOY_WWW_ROOT=/var/www/monitoring-sites # DEPLOY_WWW_ROOT=/var/www/monitoring-sites
# UI language for console + HTML sticky bar (en|fr)
TALER_MON_LANG=fr

View file

@ -72,6 +72,15 @@ export CONTINUE_ON_ERROR="${CONTINUE_ON_ERROR:-1}"
export AUTH401_CONTINUE="${AUTH401_CONTINUE:-${CONTINUE_ON_ERROR}}" export AUTH401_CONTINUE="${AUTH401_CONTINUE:-${CONTINUE_ON_ERROR}}"
export RUN_TIMEOUT="${RUN_TIMEOUT:-600}" export RUN_TIMEOUT="${RUN_TIMEOUT:-600}"
export TALER_DOMAIN="${TALER_DOMAIN:-hacktivism.ch}" export TALER_DOMAIN="${TALER_DOMAIN:-hacktivism.ch}"
# UI language: en default; fr for FrancPaysan (override with TALER_MON_LANG)
if [ -z "${TALER_MON_LANG:-}" ]; then
case "$TALER_DOMAIN" in
*lefrancpaysan*|*francpaysan*) export TALER_MON_LANG=fr ;;
*) export TALER_MON_LANG=en ;;
esac
fi
export TALER_MON_LANG
PHASES="${PHASES:-urls inside versions}" PHASES="${PHASES:-urls inside versions}"
HTML_BASE="${HTML_OUT:-$HOME/monitoring-sites-staging}" HTML_BASE="${HTML_OUT:-$HOME/monitoring-sites-staging}"
MON_HOSTS="${MON_HOSTS:-}" MON_HOSTS="${MON_HOSTS:-}"
@ -285,6 +294,7 @@ htmlify_host() {
if [ -n "$SITE_GEN" ] && [ -f "$SITE_GEN/console_to_html.py" ]; then if [ -n "$SITE_GEN" ] && [ -f "$SITE_GEN/console_to_html.py" ]; then
python3 "$SITE_GEN/console_to_html.py" \ python3 "$SITE_GEN/console_to_html.py" \
--lang "${TALER_MON_LANG:-en}" \
--log "$LOG" \ --log "$LOG" \
--out "$mon_err" \ --out "$mon_err" \
--hostname "$host" \ --hostname "$host" \
@ -297,6 +307,7 @@ htmlify_host() {
--link-other "$HTML_URL_OK" --link-other "$HTML_URL_OK"
if [ "$ec" -eq 0 ]; then if [ "$ec" -eq 0 ]; then
python3 "$SITE_GEN/console_to_html.py" \ python3 "$SITE_GEN/console_to_html.py" \
--lang "${TALER_MON_LANG:-en}" \
--log "$LOG" \ --log "$LOG" \
--out "$mon" \ --out "$mon" \
--hostname "$host" \ --hostname "$host" \
@ -311,6 +322,7 @@ htmlify_host() {
echo "html $host${HTML_URL_OK} only (clean · ${COMMIT_SHORT:-?})" echo "html $host${HTML_URL_OK} only (clean · ${COMMIT_SHORT:-?})"
else else
python3 "$SITE_GEN/console_to_html.py" \ python3 "$SITE_GEN/console_to_html.py" \
--lang "${TALER_MON_LANG:-en}" \
--log "$LOG" \ --log "$LOG" \
--out "$mon" \ --out "$mon" \
--hostname "$host" \ --hostname "$host" \

101
i18n.sh Normal file
View file

@ -0,0 +1,101 @@
#!/usr/bin/env bash
# i18n.sh — language for taler-monitoring console + HTML chrome
#
# TALER_MON_LANG=en|fr (default en)
# Auto: domains *lefrancpaysan* → fr unless TALER_MON_LANG is set explicitly.
: "${TALER_MON_LANG:=}"
: "${TALER_MON_LANG_SET:=0}"
i18n_init() {
if [ -n "${TALER_MON_LANG:-}" ]; then
TALER_MON_LANG_SET=1
fi
if [ "${TALER_MON_LANG_SET}" != "1" ]; then
case "${TALER_DOMAIN:-}" in
*lefrancpaysan*|*francpaysan*)
TALER_MON_LANG=fr
;;
*)
TALER_MON_LANG=en
;;
esac
fi
case "${TALER_MON_LANG}" in
fr|FR|fra|french) TALER_MON_LANG=fr ;;
*) TALER_MON_LANG=en ;;
esac
export TALER_MON_LANG
}
i18n_tag() {
local t="$1"
if [ "${TALER_MON_LANG:-en}" != "fr" ]; then
printf '%s' "$t"
return 0
fi
case "$t" in
OK) printf 'OK' ;;
ERROR) printf 'ERREUR' ;;
WARN) printf 'AVERT' ;;
INFO) printf 'INFO' ;;
BLOCKER) printf 'BLOCAGE' ;;
BLOCK) printf 'BLOC' ;;
SUMMARY) printf 'RESUME' ;;
PROG|PROGRESS) printf 'PROG' ;;
*) printf '%s' "$t" ;;
esac
}
i18n_text() {
local s="$*"
if [ "${TALER_MON_LANG:-en}" != "fr" ] || [ -z "$s" ]; then
printf '%s' "$s"
return 0
fi
case "$s" in
"numbered checks this run:") printf 'controles numerotes de cette execution :' ;;
"failed — see list above"|"failed - see list above") printf 'echec — voir la liste ci-dessus' ;;
"BLOCKERS · pay/withdraw cannot finish") printf 'BLOCAGES · paiement/retrait impossible' ;;
"BLOCKERS (pay/withdraw cannot finish)") printf 'BLOCAGES (paiement/retrait impossible)' ;;
"ERRORS · failed checks") printf 'ERREURS · controles en echec' ;;
"ERRORS (failed checks)") printf 'ERREURS (controles en echec)' ;;
"pay/withdraw cannot finish") printf 'paiement/retrait impossible a terminer' ;;
"SUMMARY") printf 'RESUME' ;;
"totals:") printf 'totaux :' ;;
"container") printf 'conteneur' ;;
"disk") printf 'disque' ;;
"package") printf 'paquet' ;;
"version") printf 'version' ;;
"reachability") printf 'accessibilite' ;;
"server-header") printf 'en-tete Server' ;;
"inventory") printf 'inventaire' ;;
"flags") printf 'options' ;;
"target domain") printf 'domaine cible' ;;
"currency") printf 'devise' ;;
"phases") printf 'phases' ;;
"access") printf 'acces' ;;
*)
local out="$s"
out=${out//shared library missing/bibliotheque partagee manquante}
out=${out//shared library error/erreur de bibliotheque partagee}
out=${out//not active/non actif}
out=${out//after_start/apres_demarrage}
out=${out//catalogued but not reachable/catalogue mais inaccessible}
out=${out//does not resolve/ne resout pas}
out=${out//protocol confirmed/protocole confirme}
out=${out//catalogued service OK/service catalogue OK}
out=${out//from Server header/depuis en-tete Server}
out=${out//OSV clean/OSV sans vulnerabilite}
out=${out//actionable vuln/vuln. actionnable}
out=${out//header version · not Debian pkg/version en-tete · pas paquet Debian}
out=${out//RUN_TIMEOUT exceeded/RUN_TIMEOUT depasse}
out=${out//public HTTPS/HTTPS public}
out=${out//container status/etat du conteneur}
out=${out//remote-only/uniquement distant}
out=${out//no SSH/sans SSH}
out=${out//failed — see list above/echec — voir la liste ci-dessus}
printf '%s' "$out"
;;
esac
}

61
lib.sh
View file

@ -2,6 +2,18 @@
# Shared helpers for taler-monitoring (laptop or koopa). # Shared helpers for taler-monitoring (laptop or koopa).
# Default stack = GOA / hacktivism (overridden by TALER_DOMAIN / --domain) # Default stack = GOA / hacktivism (overridden by TALER_DOMAIN / --domain)
# i18n (en default; fr for FrancPaysan — see i18n.sh / TALER_MON_LANG)
_I18N_DIR=$(cd "$(dirname "${BASH_SOURCE[0]}")" && pwd)
if [ -f "$_I18N_DIR/i18n.sh" ]; then
# shellcheck source=i18n.sh
source "$_I18N_DIR/i18n.sh"
i18n_init
else
i18n_tag() { printf '%s' "$1"; }
i18n_text() { printf '%s' "$*"; }
i18n_init() { :; }
fi
: "${TALER_DOMAIN:=hacktivism.ch}" : "${TALER_DOMAIN:=hacktivism.ch}"
: "${BANK_PUBLIC:=https://bank.hacktivism.ch}" : "${BANK_PUBLIC:=https://bank.hacktivism.ch}"
: "${EXCHANGE_PUBLIC:=https://exchange.hacktivism.ch}" : "${EXCHANGE_PUBLIC:=https://exchange.hacktivism.ch}"
@ -114,6 +126,11 @@ set_taler_stack() {
BANK_PUBLIC=${BANK_PUBLIC%/} BANK_PUBLIC=${BANK_PUBLIC%/}
EXCHANGE_PUBLIC=${EXCHANGE_PUBLIC%/} EXCHANGE_PUBLIC=${EXCHANGE_PUBLIC%/}
MERCHANT_PUBLIC=${MERCHANT_PUBLIC%/} MERCHANT_PUBLIC=${MERCHANT_PUBLIC%/}
# re-evaluate language after domain profile (FrancPaysan → fr)
if [ "${TALER_MON_LANG_SET:-0}" != "1" ]; then
TALER_MON_LANG=
i18n_init
fi
} }
# Load first matching profile from domains.conf. # Load first matching profile from domains.conf.
@ -265,6 +282,11 @@ apply_taler_domain() {
BANK_PUBLIC=${BANK_PUBLIC%/} BANK_PUBLIC=${BANK_PUBLIC%/}
EXCHANGE_PUBLIC=${EXCHANGE_PUBLIC%/} EXCHANGE_PUBLIC=${EXCHANGE_PUBLIC%/}
MERCHANT_PUBLIC=${MERCHANT_PUBLIC%/} MERCHANT_PUBLIC=${MERCHANT_PUBLIC%/}
# re-evaluate language after domain profile (FrancPaysan → fr)
if [ "${TALER_MON_LANG_SET:-0}" != "1" ]; then
TALER_MON_LANG=
i18n_init
fi
# Koopa SSH only for LOCAL_STACK=1. Stage LFP uses INSIDE_SSH (stagepaysan) separately. # Koopa SSH only for LOCAL_STACK=1. Stage LFP uses INSIDE_SSH (stagepaysan) separately.
if [ "${LOCAL_STACK}" != "1" ]; then if [ "${LOCAL_STACK}" != "1" ]; then
@ -679,6 +701,10 @@ _fmt_badge() {
# $1=badge style $2=tag text $3=label colour $4=label $5=detail # $1=badge style $2=tag text $3=label colour $4=label $5=detail
_msg_line() { _msg_line() {
local badge="$1" tag="$2" lcol="$3" label="$4" detail="${5:-}" local badge="$1" tag="$2" lcol="$3" label="$4" detail="${5:-}"
# i18n: tags + free text (en default; fr for FrancPaysan)
tag=$(i18n_tag "$tag")
label=$(i18n_text "$label")
[ -n "$detail" ] && detail=$(i18n_text "$detail")
if [ -n "$detail" ]; then if [ -n "$detail" ]; then
printf -- '%s %s%s%s%s %s·%s %s%s%s\n' \ printf -- '%s %s%s%s%s %s·%s %s%s%s\n' \
"$(_fmt_badge "$badge" "$tag")" "$(_fmt_tid)" "$lcol" "$label" "$N" "$D" "$N" "$D" "$detail" "$N" "$(_fmt_badge "$badge" "$tag")" "$(_fmt_tid)" "$lcol" "$label" "$N" "$D" "$N" "$D" "$detail" "$N"
@ -749,7 +775,8 @@ blocker() {
} }
section() { section() {
# Boxed section header (3 lines when colour on) # Boxed section header (3 lines when colour on)
local title="$*" local title
title=$(i18n_text "$*")
local w=${#title} local w=${#title}
[ "$w" -lt 24 ] && w=24 [ "$w" -lt 24 ] && w=24
[ "$w" -gt 56 ] && w=56 [ "$w" -gt 56 ] && w=56
@ -783,13 +810,13 @@ summary() {
_progress_bar_line "$PROGRESS_DONE" "$PROGRESS_TOTAL" _progress_bar_line "$PROGRESS_DONE" "$PROGRESS_TOTAL"
fi fi
if [ "$GLOBAL_N" -gt 0 ]; then if [ "$GLOBAL_N" -gt 0 ]; then
printf -- '%s numbered checks this run: #001…#%03d%s\n' "$D" "$GLOBAL_N" "$N" printf -- '%s %s #001…#%03d%s\n' "$D" "$(i18n_text 'numbered checks this run:')" "$GLOBAL_N" "$N"
fi fi
if [ "$blk_n" -gt 0 ]; then if [ "$blk_n" -gt 0 ]; then
if [ "${BOX:-0}" = "1" ]; then if [ "${BOX:-0}" = "1" ]; then
printf -- '%s┌ BLOCKERS · pay/withdraw cannot finish ┐%s\n' "$BG_BLK" "$N" printf -- '%s┌ %s ┐%s\n' "$BG_BLK" "$(i18n_text 'BLOCKERS · pay/withdraw cannot finish')" "$N"
else else
printf -- '%s--- BLOCKERS (pay/withdraw cannot finish) ---%s\n' "$M" "$N" printf -- '%s--- %s ---%s\n' "$M" "$(i18n_text 'BLOCKERS (pay/withdraw cannot finish)')" "$N"
fi fi
local b local b
for b in "${BLOCKERS[@]}"; do for b in "${BLOCKERS[@]}"; do
@ -798,9 +825,9 @@ summary() {
fi fi
if [ "${#ERRORS[@]}" -gt 0 ] && [ "$blk_n" -lt "${#ERRORS[@]}" ]; then if [ "${#ERRORS[@]}" -gt 0 ] && [ "$blk_n" -lt "${#ERRORS[@]}" ]; then
if [ "${BOX:-0}" = "1" ]; then if [ "${BOX:-0}" = "1" ]; then
printf -- '%s┌ ERRORS · failed checks ┐%s\n' "$BG_ERR" "$N" printf -- '%s┌ %s ┐%s\n' "$BG_ERR" "$(i18n_text 'ERRORS · failed checks')" "$N"
else else
printf -- '%s--- ERRORS (failed checks) ---%s\n' "$R" "$N" printf -- '%s--- %s ---%s\n' "$R" "$(i18n_text 'ERRORS (failed checks)')" "$N"
fi fi
local e local e
for e in "${ERRORS[@]}"; do for e in "${ERRORS[@]}"; do
@ -811,9 +838,11 @@ summary() {
# Coloured totals — same severity badges as check lines (always; NO_COLOR blanks ANSI) # Coloured totals — same severity badges as check lines (always; NO_COLOR blanks ANSI)
if [ "${BOX:-0}" = "1" ]; then if [ "${BOX:-0}" = "1" ]; then
printf -- '\n%s┌ SUMMARY ┐%s\n' "$BG_SEC" "$N" printf -- '\n%s┌ %s ┐%s\n' "$BG_SEC" "$(i18n_tag SUMMARY)" "$N"
_sum_badge() { # $1=bg $2=tag $3=fg $4=n _sum_badge() { # $1=bg $2=tag $3=fg $4=n
printf -- ' %s┌ %-5s┐%s %s%4d%s\n' "$1" "$2" "$N" "$3" "$4" "$N" local _tg
_tg=$(i18n_tag "$2")
printf -- ' %s┌ %-5s┐%s %s%4d%s\n' "$1" "$_tg" "$N" "$3" "$4" "$N"
} }
_sum_badge "$BG_OK" "OK" "$G" "$PASS_N" _sum_badge "$BG_OK" "OK" "$G" "$PASS_N"
[ "$FAIL_N" -gt 0 ] && _sum_badge "$BG_ERR" "ERROR" "$R" "$FAIL_N" [ "$FAIL_N" -gt 0 ] && _sum_badge "$BG_ERR" "ERROR" "$R" "$FAIL_N"
@ -821,12 +850,12 @@ summary() {
[ "$INFO_N" -gt 0 ] && _sum_badge "$BG_INFO" "INFO" "$C" "$INFO_N" [ "$INFO_N" -gt 0 ] && _sum_badge "$BG_INFO" "INFO" "$C" "$INFO_N"
[ "$blk_n" -gt 0 ] && _sum_badge "$BG_BLK" "BLOCK" "$M" "$blk_n" [ "$blk_n" -gt 0 ] && _sum_badge "$BG_BLK" "BLOCK" "$M" "$blk_n"
else else
printf -- '\n[ SUMMARY ]\n' printf -- '\n[ %s ]\n' "$(i18n_tag SUMMARY)"
printf -- ' [ OK ] %s%4d%s\n' "$G" "$PASS_N" "$N" printf -- ' [ %-5s ] %s%4d%s\n' "$(i18n_tag OK)" "$G" "$PASS_N" "$N"
[ "$FAIL_N" -gt 0 ] && printf -- ' [ ERROR ] %s%4d%s\n' "$R" "$FAIL_N" "$N" [ "$FAIL_N" -gt 0 ] && printf -- ' [ %-5s ] %s%4d%s\n' "$(i18n_tag ERROR)" "$R" "$FAIL_N" "$N"
[ "$WARN_N" -gt 0 ] && printf -- ' [ WARN ] %s%4d%s\n' "$Y" "$WARN_N" "$N" [ "$WARN_N" -gt 0 ] && printf -- ' [ %-5s ] %s%4d%s\n' "$(i18n_tag WARN)" "$Y" "$WARN_N" "$N"
[ "$INFO_N" -gt 0 ] && printf -- ' [ INFO ] %s%4d%s\n' "$C" "$INFO_N" "$N" [ "$INFO_N" -gt 0 ] && printf -- ' [ %-5s ] %s%4d%s\n' "$(i18n_tag INFO)" "$C" "$INFO_N" "$N"
[ "$blk_n" -gt 0 ] && printf -- ' [ BLOCK ] %s%4d%s\n' "$M" "$blk_n" "$N" [ "$blk_n" -gt 0 ] && printf -- ' [ %-5s ] %s%4d%s\n' "$(i18n_tag BLOCK)" "$M" "$blk_n" "$N"
fi fi
# one-line rollup (bold label, severity-coloured counts) # one-line rollup (bold label, severity-coloured counts)
printf -- ' %stotals:%s %s%d OK%s' "$B" "$N" "$G" "$PASS_N" "$N" printf -- ' %stotals:%s %s%d OK%s' "$B" "$N" "$G" "$PASS_N" "$N"
@ -850,9 +879,9 @@ summary() {
fi fi
else else
if [ "${BOX:-0}" = "1" ]; then if [ "${BOX:-0}" = "1" ]; then
printf -- ' %s┌ ERROR ┐%s %sfailed — see list above%s\n' "$BG_ERR" "$N" "$R" "$N" printf -- ' %s┌ ERROR ┐%s %s"$(i18n_text "failed — see list above")"%s\n' "$BG_ERR" "$N" "$R" "$N"
else else
printf -- ' %s[ ERROR ] failed — see list above%s\n' "$R" "$N" printf -- ' %s[ ERROR ] "$(i18n_text "failed — see list above")"%s\n' "$R" "$N"
fi fi
fi fi
[ "$FAIL_N" -eq 0 ] [ "$FAIL_N" -eq 0 ]

View file

@ -8,6 +8,129 @@ import re
from datetime import datetime, timezone from datetime import datetime, timezone
from pathlib import Path from pathlib import Path
def ui_lang(explicit: str = "") -> str:
"""en (default) or fr. Env: TALER_MON_LANG; auto fr for lefrancpaysan hosts."""
import os
lang = (explicit or os.environ.get("TALER_MON_LANG") or "").strip().lower()
if lang in ("fr", "fra", "french"):
return "fr"
if lang in ("en", "eng", "english"):
return "en"
return "en"
def ui(lang: str, key: str, **kwargs) -> str:
"""UI chrome strings for sticky bar (English default; French for FrancPaysan)."""
en = {
"errors": "ERRORS",
"warnings": "WARNINGS",
"ok": "OK",
"error_one": "{n} error",
"error_many": "{n} errors",
"warn_one": "{n} warning",
"warn_many": "{n} warnings",
"jump_first_error": "Jump to first error",
"jump_first_warn": "Jump to first warning",
"generated": "generated",
"source": "source",
"what_monitors": "What this monitors",
"what_monitors_title": "Expand: what this page checks",
"surface_title": "Remote ecosystem surface inventory",
"surface_summary": "Public catalog hosts · DNS / TCP / HTTPS / TLS · Server version · CVE",
"surface_i1": "Outside-in only: no SSH and no podman exec on remote targets",
"surface_i2": "Catalog (surface-catalog.conf): taler.net, demo/test, gnunet.org, taler-ops.ch, taler-systems.com, deb.taler.net, ftp.gnu.org, …",
"surface_i3": "Per host: DNS resolve, ICMP (info), open ports, protocol probes, TLS cert expiry, Server-header software version, optional CVE check",
"surface_i4": "HTML published only on taler.hacktivism.ch (this page host: {host})",
"surface_i5": "Timer: taler-monitoring-surface.timer (hourly)",
"apt_title": "apt-src merchant deploy tests (podman on koopa)",
"apt_summary": "4 containers · fresh + upgrade tracks · trixie & trixie-testing",
"apt_i1": "koopa-taler-deploy-test-apt-src-trixie (fresh, suite trixie)",
"apt_i2": "koopa-taler-deploy-test-apt-src-trixie-testing (fresh, trixie-testing)",
"apt_i3": "koopa-taler-deploy-test-apt-src-trixie-upgrade (upgrade track)",
"apt_i4": "koopa-taler-deploy-test-apt-src-trixie-testing-upgrade (upgrade track)",
"apt_i5": "Checks: packages, taler-merchant-httpd --version / ldd, systemd unit, optional local /config probe — not public nginx/HTTPS",
"apt_i6": "HTML published only on taler.hacktivism.ch (this page host: {host})",
"apt_i7": "Timer: taler-monitoring-aptdeploy.timer (4h)",
"focus_bank": "Libeufin bank public HTTPS + container inside checks",
"focus_exchange": "Exchange public HTTPS + container inside checks",
"focus_merchant": "Merchant backend / SPA public HTTPS + container inside checks",
"focus_stack": "Taler stack public + inside checks",
"stack_title": "Stack monitoring · {host}",
"stack_summary": "{phases} · public + inside",
"stack_i1": "Focus: {focus}",
"stack_i2": "Phases this run: {phases}",
"stack_i3": "Typical: public URLs (HTTPS, QR, perf), inside (podman/ssh), package versions",
"stack_i4": "HTML host: {host}",
"stack_i5": "Timer: host-agent path + 4h · /monitoring on bank, exchange, merchant fronts",
"stack_i_phases": "Log header phases= {phases}",
"footer": "Console-style render of taler-monitoring output. Sticky bar: green = clean · yellow = warnings · red = errors. Commit pins the exact tree used for this run.",
"redirect_fail": "has failures.",
"redirect_see": "See {link} for the full console log, error index, and sticky status bar.",
"ago_s": "{n}s ago",
"ago_m": "{n}m ago",
"ago_h": "{n}h ago",
"ago_d": "{n}d ago",
"age_title": "page age · updates every second",
}
fr = {
"errors": "ERREURS",
"warnings": "AVERTISSEMENTS",
"ok": "OK",
"error_one": "{n} erreur",
"error_many": "{n} erreurs",
"warn_one": "{n} avertissement",
"warn_many": "{n} avertissements",
"jump_first_error": "Aller à la première erreur",
"jump_first_warn": "Aller au premier avertissement",
"generated": "généré",
"source": "source",
"what_monitors": "Ce que cette page contrôle",
"what_monitors_title": "Déplier : ce que cette page vérifie",
"surface_title": "Inventaire de surface de l'écosystème (distant)",
"surface_summary": "Hôtes du catalogue public · DNS / TCP / HTTPS / TLS · version serveur · CVE",
"surface_i1": "Uniquement de l'extérieur : pas de SSH ni de podman exec sur les cibles distantes",
"surface_i2": "Catalogue (surface-catalog.conf) : taler.net, demo/test, gnunet.org, taler-ops.ch, taler-systems.com, deb.taler.net, ftp.gnu.org, …",
"surface_i3": "Par hôte : résolution DNS, ICMP (info), ports ouverts, sondes de protocole, expiration du certificat TLS, version logicielle (en-tête Server), CVE optionnel",
"surface_i4": "HTML publié uniquement sur taler.hacktivism.ch (hôte de cette page : {host})",
"surface_i5": "Minuterie : taler-monitoring-surface.timer (horaire)",
"apt_title": "Tests de déploiement apt-src marchand (podman sur koopa)",
"apt_summary": "4 conteneurs · pistes fresh + upgrade · trixie et trixie-testing",
"apt_i1": "koopa-taler-deploy-test-apt-src-trixie (fresh, suite trixie)",
"apt_i2": "koopa-taler-deploy-test-apt-src-trixie-testing (fresh, trixie-testing)",
"apt_i3": "koopa-taler-deploy-test-apt-src-trixie-upgrade (piste upgrade)",
"apt_i4": "koopa-taler-deploy-test-apt-src-trixie-testing-upgrade (piste upgrade)",
"apt_i5": "Contrôles : paquets, taler-merchant-httpd --version / ldd, unité systemd, sonde locale /config optionnelle — pas de nginx/HTTPS public",
"apt_i6": "HTML publié uniquement sur taler.hacktivism.ch (hôte de cette page : {host})",
"apt_i7": "Minuterie : taler-monitoring-aptdeploy.timer (4 h)",
"focus_bank": "Banque Libeufin HTTPS public + contrôles inside conteneur",
"focus_exchange": "Exchange HTTPS public + contrôles inside conteneur",
"focus_merchant": "Backend marchand / SPA HTTPS public + contrôles inside conteneur",
"focus_stack": "Pile Taler public + inside",
"stack_title": "Surveillance de pile · {host}",
"stack_summary": "{phases} · public + inside",
"stack_i1": "Focus : {focus}",
"stack_i2": "Phases de cette exécution : {phases}",
"stack_i3": "Typique : URL publiques (HTTPS, QR, perf), inside (podman/ssh), versions des paquets",
"stack_i4": "Hôte HTML : {host}",
"stack_i5": "Minuterie : host-agent path + 4 h · /monitoring sur bank, exchange, fronts marchands",
"stack_i_phases": "En-tête de journal phases= {phases}",
"footer": "Rendu console de taler-monitoring. Barre collante : vert = OK · jaune = avertissements · rouge = erreurs. Le commit fixe l'arbre exact de cette exécution.",
"redirect_fail": "a des échecs.",
"redirect_see": "Voir {link} pour le journal console complet, l'index d'erreurs et la barre de statut.",
"ago_s": "il y a {n}s",
"ago_m": "il y a {n} min",
"ago_h": "il y a {n} h",
"ago_d": "il y a {n} j",
"age_title": "âge de la page · mise à jour chaque seconde",
}
table = fr if lang == "fr" else en
s = table.get(key) or en.get(key) or key
try:
return s.format(**kwargs)
except Exception:
return s
# ANSI strip # ANSI strip
ANSI_RE = re.compile(r"\x1b\[[0-9;]*m") ANSI_RE = re.compile(r"\x1b\[[0-9;]*m")
ERR_BULLET_RE = re.compile(r"^\s*[•·*-]\s+(?P<body>.+)$") ERR_BULLET_RE = re.compile(r"^\s*[•·*-]\s+(?P<body>.+)$")
@ -192,82 +315,84 @@ def extract_phases(log_text: str) -> str:
return "" return ""
def monitoring_scope( def monitoring_scope(
page_label: str, page_label: str,
hostname: str, hostname: str,
log_text: str = "", log_text: str = "",
lang: str = "en",
) -> dict[str, object]: ) -> dict[str, object]:
""" """Human-readable description of what this page monitors (en default; fr for FP)."""
Human-readable description of what this monitoring page covers.
Returned keys: title, summary, items (list[str]), kind
"""
label = (page_label or "monitoring").lower().replace("_", "-") label = (page_label or "monitoring").lower().replace("_", "-")
phases = extract_phases(log_text) phases = extract_phases(log_text)
host = hostname or "?" host = hostname or "?"
# Auto-fr for FrancPaysan hosts if lang not forced
if lang == "en" and re.search(r"lefrancpaysan|francpaysan", host + " " + log_text, re.I):
import os
if not (os.environ.get("TALER_MON_LANG") or "").strip():
lang = "fr"
if "surface" in label: if "surface" in label:
return { return {
"kind": "surface", "kind": "surface",
"title": "Remote ecosystem surface inventory", "lang": lang,
"summary": "Public catalog hosts · DNS / TCP / HTTPS / TLS · Server version · CVE", "title": ui(lang, "surface_title"),
"summary": ui(lang, "surface_summary"),
"items": [ "items": [
"Outside-in only: no SSH and no podman exec on remote targets", ui(lang, "surface_i1"),
"Catalog (surface-catalog.conf): taler.net, demo/test, gnunet.org, " ui(lang, "surface_i2"),
"taler-ops.ch, taler-systems.com, deb.taler.net, ftp.gnu.org, …", ui(lang, "surface_i3"),
"Per host: DNS resolve, ICMP (info), open ports, protocol probes, " ui(lang, "surface_i4", host=host),
"TLS cert expiry, Server-header software version, optional CVE check", ui(lang, "surface_i5"),
f"HTML published only on taler.hacktivism.ch "
f"(this page host: {host})",
"Timer: taler-monitoring-surface.timer (hourly)",
], ],
} }
if "aptdeploy" in label or "apt-deploy" in label or "apt_src" in label: if "aptdeploy" in label or "apt-deploy" in label or "apt_src" in label:
return { return {
"kind": "aptdeploy", "kind": "aptdeploy",
"title": "apt-src merchant deploy tests (podman on koopa)", "lang": lang,
"summary": "4 containers · fresh + upgrade tracks · trixie & trixie-testing", "title": ui(lang, "apt_title"),
"summary": ui(lang, "apt_summary"),
"items": [ "items": [
"koopa-taler-deploy-test-apt-src-trixie (fresh, suite trixie)", ui(lang, "apt_i1"),
"koopa-taler-deploy-test-apt-src-trixie-testing (fresh, trixie-testing)", ui(lang, "apt_i2"),
"koopa-taler-deploy-test-apt-src-trixie-upgrade (upgrade track)", ui(lang, "apt_i3"),
"koopa-taler-deploy-test-apt-src-trixie-testing-upgrade (upgrade track)", ui(lang, "apt_i4"),
"Checks: packages, taler-merchant-httpd --version / ldd, systemd unit, " ui(lang, "apt_i5"),
"optional local /config probe — not public nginx/HTTPS", ui(lang, "apt_i6", host=host),
f"HTML published only on taler.hacktivism.ch (this page host: {host})", ui(lang, "apt_i7"),
"Timer: taler-monitoring-aptdeploy.timer (4h)",
], ],
} }
# Default: stack /monitoring pages (bank, exchange, merchant)
phase_txt = phases or "urls · inside · versions" phase_txt = phases or "urls · inside · versions"
role = "stack" role = "stack"
if host.startswith("bank."): if host.startswith("bank.") or "bank." in host:
role = "bank" role = "bank"
focus = "Libeufin bank public HTTPS + container inside checks" focus = ui(lang, "focus_bank")
elif host.startswith("exchange.") or host.startswith("stage.exchange."): elif "exchange." in host:
role = "exchange" role = "exchange"
focus = "Exchange public HTTPS + container inside checks" focus = ui(lang, "focus_exchange")
elif host.startswith("taler.") or "merchant" in host or host.startswith("monnaie."): elif host.startswith("taler.") or "merchant" in host or "monnaie." in host:
role = "merchant" role = "merchant"
focus = "Merchant backend / SPA public HTTPS + container inside checks" focus = ui(lang, "focus_merchant")
else: else:
focus = "Taler stack public + inside checks" focus = ui(lang, "focus_stack")
items = [ items = [
f"Focus: {focus}", ui(lang, "stack_i1", focus=focus),
f"Phases this run: {phase_txt}", ui(lang, "stack_i2", phases=phase_txt),
"Typical: public URLs (HTTPS, QR, perf), inside (podman/ssh), package versions", ui(lang, "stack_i3"),
f"HTML host: {host}", ui(lang, "stack_i4", host=host),
"Timer: taler-monitoring-hacktivism (path + 4h) · /monitoring on bank, exchange, taler", ui(lang, "stack_i5"),
] ]
if phases: if phases:
items.insert(2, f"Log header phases= {phases}") items.insert(2, ui(lang, "stack_i_phases", phases=phases))
return { return {
"kind": role, "kind": role,
"title": f"Stack monitoring · {host}", "lang": lang,
"summary": f"{phase_txt} · public + inside", "title": ui(lang, "stack_title", host=host),
"summary": ui(lang, "stack_summary", phases=phase_txt),
"items": items, "items": items,
} }
@ -289,28 +414,41 @@ def sticky_bar_html(
scope: dict[str, object] | None = None, scope: dict[str, object] | None = None,
) -> str: ) -> str:
level = status_level(n_err, n_warn) level = status_level(n_err, n_warn)
if level == "red": lang = "en"
status_txt = "ERRORS" if scope and scope.get("lang"):
elif level == "yellow": lang = str(scope.get("lang"))
status_txt = "WARNINGS"
else: else:
status_txt = "OK" lang = ui_lang()
# auto-fr for FP hostnames
if lang == "en" and re.search(r"lefrancpaysan|francpaysan", hostname or "", re.I):
lang = "fr"
if level == "red":
status_txt = ui(lang, "errors")
elif level == "yellow":
status_txt = ui(lang, "warnings")
else:
status_txt = ui(lang, "ok")
if first_err_href and n_err: if first_err_href and n_err:
err_lbl = ui(lang, "error_one", n=n_err) if n_err == 1 else ui(lang, "error_many", n=n_err)
err_stat = ( err_stat = (
f'<a class="stat err" href="{html.escape(first_err_href)}" ' f'<a class="stat err" href="{html.escape(first_err_href)}" '
f'title="Jump to first error">{n_err} error{"s" if n_err != 1 else ""}</a>' f'title="{html.escape(ui(lang, "jump_first_error"))}">{html.escape(err_lbl)}</a>'
) )
else: else:
err_stat = f'<span class="stat err muted">{n_err} errors</span>' err_lbl = ui(lang, "error_many", n=n_err)
err_stat = f'<span class="stat err muted">{html.escape(err_lbl)}</span>'
if first_warn_href and n_warn: if first_warn_href and n_warn:
warn_lbl = ui(lang, "warn_one", n=n_warn) if n_warn == 1 else ui(lang, "warn_many", n=n_warn)
warn_stat = ( warn_stat = (
f'<a class="stat warn" href="{html.escape(first_warn_href)}" ' f'<a class="stat warn" href="{html.escape(first_warn_href)}" '
f'title="Jump to first warning">{n_warn} warning{"s" if n_warn != 1 else ""}</a>' f'title="{html.escape(ui(lang, "jump_first_warn"))}">{html.escape(warn_lbl)}</a>'
) )
else: else:
warn_stat = f'<span class="stat warn muted">{n_warn} warnings</span>' warn_lbl = ui(lang, "warn_many", n=n_warn)
warn_stat = f'<span class="stat warn muted">{html.escape(warn_lbl)}</span>'
other = "" other = ""
if link_other: if link_other:
@ -320,7 +458,7 @@ def sticky_bar_html(
) )
if scope is None: if scope is None:
scope = monitoring_scope(page_label, hostname, "") scope = monitoring_scope(page_label, hostname, "", lang=ui_lang())
scope_title = html.escape(str(scope.get("title") or "Monitoring")) scope_title = html.escape(str(scope.get("title") or "Monitoring"))
scope_summary = html.escape(str(scope.get("summary") or "")) scope_summary = html.escape(str(scope.get("summary") or ""))
scope_kind = html.escape(str(scope.get("kind") or "monitoring")) scope_kind = html.escape(str(scope.get("kind") or "monitoring"))
@ -347,21 +485,21 @@ def sticky_bar_html(
<span class="generated" <span class="generated"
data-generated-iso="{html.escape(generated_iso)}" data-generated-iso="{html.escape(generated_iso)}"
title="{html.escape(generated_at)}"> title="{html.escape(generated_at)}">
generated <time datetime="{html.escape(generated_iso)}">{html.escape(generated_at)}</time> {html.escape(ui(lang, "generated"))} <time datetime="{html.escape(generated_iso)}">{html.escape(generated_at)}</time>
<span class="age" id="generated-age"></span> <span class="age" id="generated-age"></span>
</span> </span>
<button type="button" class="scope-toggle" id="scope-toggle" <button type="button" class="scope-toggle" id="scope-toggle"
aria-expanded="false" aria-controls="sticky-scope-panel" aria-expanded="false" aria-controls="sticky-scope-panel"
title="Aufklappen: was diese Seite prüft"> title="{html.escape(ui(lang, "what_monitors_title"))}">
<span class="scope-chevron" aria-hidden="true"></span> <span class="scope-chevron" aria-hidden="true"></span>
<span class="scope-toggle-label">Was geprüft wird</span> <span class="scope-toggle-label">{html.escape(ui(lang, "what_monitors"))}</span>
<span class="scope-toggle-hint">{scope_summary}</span> <span class="scope-toggle-hint">{scope_summary}</span>
</button> </button>
</div> </div>
<div class="sticky-scope-panel" id="sticky-scope-panel" hidden> <div class="sticky-scope-panel" id="sticky-scope-panel" hidden>
<div class="sticky-bar-row secondary"> <div class="sticky-bar-row secondary">
<span class="badge mode-{html.escape(mode)}">{html.escape(mode.upper())}</span> <span class="badge mode-{html.escape(mode)}">{html.escape(mode.upper())}</span>
source {commit_html}{suite_bit} {html.escape(ui(lang, "source"))} {commit_html}{suite_bit}
{(" · " + other) if other else ""} {(" · " + other) if other else ""}
</div> </div>
<div class="scope-body"> <div class="scope-body">
@ -531,10 +669,22 @@ STICKY_JS = """
(function () { (function () {
function fmtAge(sec) { function fmtAge(sec) {
if (sec < 0) sec = 0; if (sec < 0) sec = 0;
if (sec < 60) return sec + "s ago"; var lang = (document.documentElement.getAttribute("lang") || "en").toLowerCase();
if (sec < 3600) return Math.floor(sec / 60) + "m ago"; var n;
if (sec < 86400) return Math.floor(sec / 3600) + "h ago"; if (sec < 60) {
return Math.floor(sec / 86400) + "d ago"; n = sec;
return lang === "fr" ? ("il y a " + n + "s") : (n + "s ago");
}
if (sec < 3600) {
n = Math.floor(sec / 60);
return lang === "fr" ? ("il y a " + n + " min") : (n + "m ago");
}
if (sec < 86400) {
n = Math.floor(sec / 3600);
return lang === "fr" ? ("il y a " + n + " h") : (n + "h ago");
}
n = Math.floor(sec / 86400);
return lang === "fr" ? ("il y a " + n + " j") : (n + "d ago");
} }
function tick() { function tick() {
var el = document.querySelector("[data-generated-iso]"); var el = document.querySelector("[data-generated-iso]");
@ -546,7 +696,9 @@ STICKY_JS = """
if (isNaN(t)) return; if (isNaN(t)) return;
var sec = Math.floor((Date.now() - t) / 1000); var sec = Math.floor((Date.now() - t) / 1000);
age.textContent = fmtAge(sec); age.textContent = fmtAge(sec);
age.title = "page age · updates every second"; age.title = (document.documentElement.getAttribute("lang") || "en").toLowerCase() === "fr"
? "âge de la page · mise à jour chaque seconde"
: "page age · updates every second";
} }
tick(); tick();
setInterval(tick, 1000); setInterval(tick, 1000);
@ -679,8 +831,15 @@ def build_html(
first_err_href = "#first-error" if first_err_i is not None else None first_err_href = "#first-error" if first_err_i is not None else None
first_warn_href = "#first-warn" if first_warn_i is not None else None first_warn_href = "#first-warn" if first_warn_i is not None else None
scope = monitoring_scope(page_label, hostname, log_text) lang = ui_lang()
if re.search(r"lefrancpaysan|francpaysan", hostname + "\n" + log_text, re.I):
import os
if not (os.environ.get("TALER_MON_LANG") or "").strip():
lang = "fr"
scope = monitoring_scope(page_label, hostname, log_text, lang=lang)
page_lang = lang
footer_txt = ui(lang, "footer")
bar = sticky_bar_html( bar = sticky_bar_html(
generated_at=generated_at, generated_at=generated_at,
generated_iso=generated_iso, generated_iso=generated_iso,
@ -698,7 +857,7 @@ def build_html(
) )
return f"""<!DOCTYPE html> return f"""<!DOCTYPE html>
<html lang="en"> <html lang="{html.escape(lang if "lang" in dir() else "en")}">
<head> <head>
<meta charset="utf-8"/> <meta charset="utf-8"/>
<meta name="viewport" content="width=device-width, initial-scale=1"/> <meta name="viewport" content="width=device-width, initial-scale=1"/>
@ -775,9 +934,7 @@ footer {{
{"".join(body_lines)} {"".join(body_lines)}
</div> </div>
<footer> <footer>
Console-style render of taler-monitoring output. {html.escape(footer_txt)}
Sticky bar: green = clean · yellow = warnings · red = errors.
Commit pins the exact tree used for this run.
</footer> </footer>
</main> </main>
{STICKY_JS} {STICKY_JS}
@ -830,7 +987,7 @@ def build_redirect_html(
scope=scope, scope=scope,
) )
return f"""<!DOCTYPE html> return f"""<!DOCTYPE html>
<html lang="en"> <html lang="{html.escape(lang if "lang" in dir() else "en")}">
<head> <head>
<meta charset="utf-8"/> <meta charset="utf-8"/>
<meta http-equiv="refresh" content="2; url={html.escape(link)}"/> <meta http-equiv="refresh" content="2; url={html.escape(link)}"/>
@ -882,6 +1039,11 @@ def main() -> None:
default="/monitoring_err/", default="/monitoring_err/",
help="URL path for error page (redirect target)", help="URL path for error page (redirect target)",
) )
ap.add_argument(
"--lang",
default="",
help="UI language en|fr (default: env TALER_MON_LANG or auto)",
)
ap.add_argument( ap.add_argument(
"--page-label", "--page-label",
default="monitoring", default="monitoring",
@ -889,6 +1051,9 @@ def main() -> None:
) )
args = ap.parse_args() args = ap.parse_args()
if getattr(args, "lang", ""):
import os
os.environ["TALER_MON_LANG"] = args.lang
log_text = args.log.read_text(errors="replace") if args.log.is_file() else "" log_text = args.log.read_text(errors="replace") if args.log.is_file() else ""
now = datetime.now(timezone.utc) now = datetime.now(timezone.utc)
generated_iso = now.strftime("%Y-%m-%dT%H:%M:%SZ") generated_iso = now.strftime("%Y-%m-%dT%H:%M:%SZ")

51
site-gen/settings.conf Normal file
View file

@ -0,0 +1,51 @@
# monitoring site-gen — non-secret settings (safe as example)
# Copy: cp settings.conf.example settings.conf
# Never put passwords/tokens here.
# Runner: public DNS path (firecuda-external)
RUNNER_SSH=
RUNNER_SSH_FALLBACKS=
RUNNER_REMOTE_WORKDIR=/tmp/taler-monitoring-site-gen
MONITORING_ROOT=..
# Deploy: host Caddy on koopa (root service — not in container)
DEPLOY_SSH=koopa-external
DEPLOY_SSH_FALLBACKS=koopa
# Static files for Caddy file_server (created per hostname)
DEPLOY_WWW_ROOT=/var/www/monitoring-sites
DEPLOY_SUDO=sudo
CADDY_CONFIG=/etc/caddy/Caddyfile
CADDY_MIRROR_REL=configs/caddy/Caddyfile
# Working tree on deploy host for apply (optional)
KOOPA_CADDY_DIR=/home/hernani/koopa-caddy
# Source link for HTML footer (Forgejo)
SOURCE_REPO_WEB=https://git.hacktivism.ch/hernani/koopa-admin-log
SOURCE_COMMIT_URL_TMPL={repo}/src/commit/{commit}
SOURCE_SUITE_PATH=scripts/taler-monitoring
CONTINUE_ON_ERROR=1
PHASES_ERR=urls versions
PHASES_OK=urls versions
SKIP_E2E=1
# Local work dir for logs + HTML
WORK_ROOT=/tmp/taler-monitoring-sites-work
# Sites: hostname|domain_profile (9 fronts)
# Start with hacktivism; stage + prod LFP follow
SITES="
bank.hacktivism.ch|hacktivism.ch
exchange.hacktivism.ch|hacktivism.ch
taler.hacktivism.ch|hacktivism.ch
stage.bank.lefrancpaysan.ch|stage.lefrancpaysan.ch
stage.exchange.lefrancpaysan.ch|stage.lefrancpaysan.ch
stage.monnaie.lefrancpaysan.ch|stage.lefrancpaysan.ch
bank.lefrancpaysan.ch|lefrancpaysan.ch
exchange.lefrancpaysan.ch|lefrancpaysan.ch
monnaie.lefrancpaysan.ch|lefrancpaysan.ch
"
# Only process this subset (space-separated hostnames). Empty = all SITES.
# Example for first deploy: ONLY_HOSTS=bank.hacktivism.ch exchange.hacktivism.ch taler.hacktivism.ch
ONLY_HOSTS=

View file

@ -95,6 +95,7 @@ Env (same meaning):
SURFACE_CVE_LEVEL=warn|error bare Server-header versions default warn SURFACE_CVE_LEVEL=warn|error bare Server-header versions default warn
(Debian package versions default error) (Debian package versions default error)
SURFACE_CATALOG=path override surface-catalog.conf SURFACE_CATALOG=path override surface-catalog.conf
TALER_MON_LANG=en|fr console+HTML language (default en; auto fr for *lefrancpaysan*)
Full load (GOA / hacktivism): Full load (GOA / hacktivism):
./taler-monitoring.sh -d hacktivism.ch full ./taler-monitoring.sh -d hacktivism.ch full
@ -116,6 +117,8 @@ EOF
# shellcheck source=lib.sh # shellcheck source=lib.sh
source "$ROOT/lib.sh" source "$ROOT/lib.sh"
# ensure i18n after domain may change later
i18n_init 2>/dev/null || true
# Optional gitignored secrets.env next to this suite (or MONITORING_SECRETS_ENV=) # Optional gitignored secrets.env next to this suite (or MONITORING_SECRETS_ENV=)
load_monitoring_secrets_env 2>/dev/null || true load_monitoring_secrets_env 2>/dev/null || true
@ -235,6 +238,7 @@ export LADDER_MAX_AMOUNT LADDER_INCLUDE_ZERO LADDER_INCLUDE_MAX
export LADDER_HIGH_FROM LADDER_HIGH_RUNGS LADDER_LOAD export LADDER_HIGH_FROM LADDER_HIGH_RUNGS LADDER_LOAD
export LADDER_PAY LADDER_WITHDRAW_SCALE LADDER_PAY_SETTLE_ROUNDS export LADDER_PAY LADDER_WITHDRAW_SCALE LADDER_PAY_SETTLE_ROUNDS
export TALER_DOMAIN_APPLIED=1 export TALER_DOMAIN_APPLIED=1
i18n_init 2>/dev/null || true
# Optional: auth401 and other phases may honor CONTINUE_ON_ERROR # Optional: auth401 and other phases may honor CONTINUE_ON_ERROR
export CONTINUE_ON_ERROR="${CONTINUE_ON_ERROR:-}" export CONTINUE_ON_ERROR="${CONTINUE_ON_ERROR:-}"
export AUTH401_CONTINUE="${AUTH401_CONTINUE:-${CONTINUE_ON_ERROR:-}}" export AUTH401_CONTINUE="${AUTH401_CONTINUE:-${CONTINUE_ON_ERROR:-}}"