install/systemd: tglist-sync timer for follower boxes; INSTALL.md notes on systemd/cron

Co-Authored-By: Claude Fable 5.1 <noreply@anthropic.com>
This commit is contained in:
2026-09-26 23:33:48 +02:00
co-authored by Claude Fable 5.1
parent af1793a742
commit d18a0f0b91
5 changed files with 96 additions and 0 deletions
@@ -0,0 +1,8 @@
# Follower-box TG list sync (svxportal-tglist-sync.service/.timer).
#
# UPSTREAM = the master's public TG list URL. When set, this box is a FOLLOWER:
# the timer pulls that list into DEST 4x/day and the web TG editor is hidden.
# Leave UPSTREAM empty on the MASTER and on fully-standalone boxes — they own the
# list locally and edit it in the web; the sync is then a no-op.
UPSTREAM=https://svxportal.my.domain.com/tglist.php
DEST=/etc/svxlink/tglist.json
@@ -0,0 +1,10 @@
[Unit]
Description=Sync canonical TG list from svxportal master
Documentation=file:/etc/svxlink/tglist-sync.sh
Wants=network-online.target
After=network-online.target
[Service]
Type=oneshot
EnvironmentFile=/etc/default/svxportal-tglist-sync
ExecStart=/bin/bash /etc/svxlink/tglist-sync.sh
@@ -0,0 +1,12 @@
[Unit]
Description=Sync canonical TG list from svxportal master (4x/day)
[Timer]
# Shortly after boot, then every 6 hours (00,06,12,18). Persistent catches a
# missed run if the box was off at the scheduled time.
OnBootSec=2min
OnCalendar=*-*-* 00,06,12,18:00:00
Persistent=true
[Install]
WantedBy=timers.target
+38
View File
@@ -0,0 +1,38 @@
#!/bin/bash
# Follower-box sync of the canonical TG list.
#
# Pulls the master list from the svxportal master (its public /tglist.php) into
# the LOCAL /etc/svxlink/tglist.json, so every local program reads ONE identical
# list: svxdr (Lastheard TG name/Location) and the box's own /tglist.php (TG
# Monitor) both read that local file. No extra daemon — a plain curl run by a
# systemd timer.
#
# Master and fully-standalone boxes OWN the file locally (web editor) and must
# NOT run this — leave UPSTREAM empty there (the timer is a no-op / not enabled).
set -euo pipefail
: "${UPSTREAM:=}"
: "${DEST:=/etc/svxlink/tglist.json}"
# Empty UPSTREAM = this box owns its list; nothing to sync.
[ -n "$UPSTREAM" ] || { echo "UPSTREAM empty — this box owns its TG list, skipping sync"; exit 0; }
tmp="$(mktemp)"; out="$(mktemp)"
trap 'rm -f "$tmp" "$out"' EXIT
curl -fsS -m 20 "$UPSTREAM" -o "$tmp"
# Keep ONLY the canonical shape {tgs:[...],requests:[]}. Refuse to overwrite the
# live file with anything that is not a valid list (a 404/HTML/partial body must
# never blank the TG names) — last good copy then simply stays.
python3 - "$tmp" "$out" <<'PY'
import json, sys
d = json.load(open(sys.argv[1]))
tgs = d.get("tgs")
if not isinstance(tgs, list) or not tgs:
raise SystemExit("upstream has no tgs list — refusing to overwrite")
json.dump({"tgs": tgs, "requests": []}, open(sys.argv[2], "w"), indent=2)
PY
# www-data owns it so the web can still read it; atomic replace via install.
install -o www-data -g www-data -m 0664 "$out" "$DEST"
echo "TG list synced from $UPSTREAM -> $DEST"