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:
+28
@@ -144,6 +144,34 @@ sudo systemd-tmpfiles --create /etc/tmpfiles.d/svxportal-tglist.conf
|
|||||||
This creates `/etc/svxlink/tglist.json` owned by `www-data`. The path is
|
This creates `/etc/svxlink/tglist.json` owned by `www-data`. The path is
|
||||||
`tglist_file` in `config.php`; change both if you want it elsewhere.
|
`tglist_file` in `config.php`; change both if you want it elsewhere.
|
||||||
|
|
||||||
|
### 6b. Optional: follower box that mirrors another portal's talkgroup list
|
||||||
|
|
||||||
|
If you run several reflectors and want ONE talkgroup list, one portal is the
|
||||||
|
master (its list is edited in the web) and the others pull it four times a day
|
||||||
|
with a systemd timer. Nothing but `curl` and `python3` is involved.
|
||||||
|
|
||||||
|
```bash
|
||||||
|
sudo cp install/systemd/svxportal-tglist-sync.service install/systemd/svxportal-tglist-sync.timer /etc/systemd/system/
|
||||||
|
sudo cp install/systemd/tglist-sync.sh /etc/svxlink/tglist-sync.sh && sudo chmod 755 /etc/svxlink/tglist-sync.sh
|
||||||
|
sudo cp install/systemd/default-svxportal-tglist-sync /etc/default/svxportal-tglist-sync
|
||||||
|
sudoedit /etc/default/svxportal-tglist-sync # UPSTREAM = the master's https://.../tglist.php
|
||||||
|
sudo systemctl daemon-reload
|
||||||
|
sudo systemctl enable --now svxportal-tglist-sync.timer
|
||||||
|
sudo systemctl start svxportal-tglist-sync.service && journalctl -u svxportal-tglist-sync -n 3
|
||||||
|
```
|
||||||
|
|
||||||
|
On the master, or on a standalone box, do **not** install this (or leave
|
||||||
|
`UPSTREAM` empty; the script then exits without touching the file).
|
||||||
|
|
||||||
|
Other systemd notes:
|
||||||
|
|
||||||
|
- `svxreflector.service` is used **unmodified** from the SvxLink package. No
|
||||||
|
drop-in is needed for the portal.
|
||||||
|
- Redis runs from Debian's `redis-server@.service` template (step 3); no unit
|
||||||
|
file to add.
|
||||||
|
- There are no cron jobs. PHP session cleanup is Debian's stock
|
||||||
|
`phpsessionclean.timer`.
|
||||||
|
|
||||||
## 7. nginx + TLS
|
## 7. nginx + TLS
|
||||||
|
|
||||||
```bash
|
```bash
|
||||||
|
|||||||
@@ -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
|
||||||
@@ -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"
|
||||||
Reference in New Issue
Block a user