Files
shopdb-flask/scripts/backup/watch.sh
benjamin.baker 19071ec6c6 Add opt-in backup overlay with third-container restic store
Backups are per-site and not part of the base stack, so they ship as a separate compose overlay with an isolated restic REST server the app can only reach through the backup utility (no shell or db creds). The sidecar image trusts the corporate proxy root CA at build time so its Oracle yum, github, and PyPI fetches succeed behind TLS interception; shell scripts are pinned to LF via .gitattributes so the container entrypoints do not die on a CRLF shebang.
2026-09-03 20:06:45 -04:00

39 lines
1.4 KiB
Bash

#!/usr/bin/env bash
# ShopDB backup WATCHER - the resident half of the in-app backup utility.
#
# WHY this exists: the api container cannot run docker, so when an operator
# clicks "Back up now" the api only drops a sentinel file (.backup-requested)
# on the shared backups volume. This watcher is the thing that actuates that
# request: it polls for the sentinel and runs the one-shot backup.sh, which
# ships a restic snapshot to the backup-store container and clears the sentinel
# on success. That turns the UI button into a real, self-serve backup without
# giving the api container any docker or host privileges.
#
# ASCII only, no emojis - matches the repo style gate.
set -uo pipefail
BACKUPS_ROOT="${SHOPDB_BACKUPS_ROOT:-/backups}"
SENTINEL="${BACKUPS_ROOT}/.backup-requested"
INTERVAL="${BACKUP_POLL_SECONDS:-10}"
log() { echo "[backup-watch] $*"; }
mkdir -p "${BACKUPS_ROOT}"
log "watcher up; polling ${SENTINEL} every ${INTERVAL}s"
while true; do
if [ -f "${SENTINEL}" ]; then
log "backup requested - running one-shot"
if /opt/backup/backup.sh; then
log "backup completed"
else
# backup.sh only clears the sentinel on success, so a failed run
# would otherwise loop forever. Clear it here and wait for the next
# explicit request rather than hammering a broken backup.
log "backup FAILED - clearing request, awaiting next"
rm -f "${SENTINEL}" 2>/dev/null || true
fi
fi
sleep "${INTERVAL}"
done