#!/bin/bash # netcup-standby-guard — wuzapi-multiplexer op netcup automatisch naar STANDBY flippen # zodra netcup weer bereikbaar is, MAAR alleen als nextcloud actief is. # # Waarom: bij een netcup-uitval kan de netcup-unit niet op STANDBY gezet worden. Na de # handmatige nextcloud-flip (zie vault "WhatsApp Failover netcup") zijn beide nodes # actief zodra netcup terugkomt -> dubbel-antwoorden in de groepen. Deze guard detecteert # netcup-comeback en zet de netcup-multiplexer binnen een poll-interval (60 s) op STANDBY. # Invariant: nooit beide nodes tegelijk forwardend. Staat nextcloud op STANDBY, dan is # netcup-actief een bewuste failover (nextcloud-onderhoud) -> geen flip. # # Draait als Mac LaunchDaemon nl.eddydevink.netcup-standby-guard (UserName=eddy). # Installatie/werking: deploy/README.md + vault "WhatsApp Failover netcup" -> guard-sectie. set -uo pipefail NETCUP="aiaiaiaiagent@netcup" NEXTCLOUD="aiaiaiaiagent@nextcloud" UNIT=/etc/systemd/system/wuzapi-multiplexer.service STATEFILE="$HOME/.netcup-guard-state" LOG="$HOME/Library/Logs/netcup-standby-guard.log" LOCK=/tmp/netcup-standby-guard.lock log() { printf '[%s] %s\n' "$(date '+%Y-%m-%d %H:%M:%S %z')" "$*" >>"$LOG"; } ssh_nc() { ssh -o ConnectTimeout=6 -o BatchMode=yes "$NETCUP" "$@"; } ssh_nccl() { ssh -o ConnectTimeout=6 -o BatchMode=yes "$NEXTCLOUD" "$@"; } state() { cat "$STATEFILE" 2>/dev/null || echo initial; } notify() { local title="$1" msg="$2" if [ ! -f "$HOME/.netcup-guard.env" ]; then log "notificatie overgeslagen (geen ~/.netcup-guard.env): $title — $msg" return fi # shellcheck source=/dev/null . "$HOME/.netcup-guard.env" # GOTIFY_TOKEN= if curl -s -m10 -X POST "https://gotify.eddydevink.nl/message?token=${GOTIFY_TOKEN:-}" \ -H 'Content-Type: application/json' \ -d "{\"title\":\"$title\",\"message\":\"$msg\",\"priority\":5}" >/dev/null 2>&1; then log "notificatie verstuurd: $title" else log "notificatie MISLUKT (gotify niet bereikbaar?): $title" fi } # single-instance (StartInterval kan overlappen bij trage ssh-runs) if ! mkdir "$LOCK" 2>/dev/null; then # stale lock opruimen: proces weg + lock ouder dan 10 min if [ -f "$LOCK/pid" ] && ! kill -0 "$(cat "$LOCK/pid" 2>/dev/null)" 2>/dev/null \ && [ -n "$(find "$LOCK" -mmin +10 2>/dev/null)" ]; then rm -rf "$LOCK" mkdir "$LOCK" 2>/dev/null || exit 0 else exit 0 fi fi echo $$ >"$LOCK/pid" trap 'rm -rf "$LOCK"' EXIT # 1) netcup bereikbaar? if ! ssh_nc 'true' >/dev/null 2>&1; then [ "$(state)" = "down" ] || log "netcup onbereikbaar — wacht op comeback" echo down >"$STATEFILE" exit 0 fi # 2) netcup-multiplexer al op STANDBY? if ssh_nc "grep -q '^Environment=STANDBY=1' $UNIT 2>/dev/null"; then [ "$(state)" = "flipped" ] || log "netcup-multiplexer staat al op STANDBY — niets te doen" echo flipped >"$STATEFILE" exit 0 fi # 3) nextcloud moet actief zijn; anders is netcup-actief een bewuste failover. # rc: 0=STANDBY aanwezig, 1=bereikbaar+zonder STANDBY (actief), 255=onbereikbaar, 2=unit onleesbaar ssh_nccl "grep -q '^Environment=STANDBY=1' $UNIT 2>/dev/null" nccl_rc=$? if [ "$nccl_rc" -eq 0 ]; then [ "$(state)" = "deliberate" ] || log "nextcloud staat op STANDBY -> netcup actief is de bewuste richting; geen flip" echo deliberate >"$STATEFILE" exit 0 fi if [ "$nccl_rc" -ne 1 ]; then [ "$(state)" = "unknown" ] || log "nextcloud niet bevestigd actief (ssh rc=$nccl_rc) — netcup niet aanraken (kan bewuste failover zijn)" echo unknown >"$STATEFILE" exit 0 fi # rc==1: nextcloud bereikbaar + géén STANDBY = actief # 4) FLIP: netcup zonder STANDBY terwijl nextcloud actief is log "FLIP: netcup terug + nextcloud actief -> netcup-multiplexer op STANDBY zetten" if ssh_nc "sudo -A sed -i '/^Environment=MERPLUS_WEBHOOK=.*/a Environment=STANDBY=1' $UNIT && sudo -A systemctl daemon-reload && sudo -A systemctl restart wuzapi-multiplexer"; then sleep 2 ok=1 ssh_nc "grep -q '^Environment=STANDBY=1' $UNIT" || ok=0 ssh_nc "systemctl is-active wuzapi-multiplexer | grep -q active" || ok=0 # neutrale webhook-test: STANDBY-melding in het multiplexer-log = bewijs ssh_nc "curl -s -X POST http://127.0.0.1:3007/webhook -H 'Content-Type: application/json' -d '{\"type\":\"Message\",\"event\":{\"Info\":{\"Chat\":\"999999999999999999@g.us\"}}}' >/dev/null" sleep 1 ssh_nc "sudo -A journalctl -u wuzapi-multiplexer -n 3 -o cat | grep -q 'STANDBY — webhook genegeerd'" || ok=0 if [ "$ok" -eq 1 ]; then log "FLIP OK — netcup-multiplexer STANDBY + active + webhook wordt genegeerd" notify "✅ netcup is terug online" "wuzapi-multiplexer op netcup staat nu op STANDBY — nextcloud blijft de actieve WhatsApp-node." else log "FLIP VERIFICATIE MISLUKT — handmatig checken vereist!" [ "$(state)" = "flipfail" ] || notify "⚠️ netcup-standby-guard: verificatie mislukt" "netcup is terug, maar de standby-flip kon niet geverifieerd worden. Log: $LOG" fi echo flipped >"$STATEFILE" else log "FLIP MISLUKT (ssh/sudo) — volgende poll probeert opnieuw" [ "$(state)" = "flipfail" ] || notify "⚠️ netcup is terug, maar standby-flip faalde" "ssh/sudo naar netcup mislukte. Log: $LOG" echo flipfail >"$STATEFILE" fi exit 0