#!/bin/bash # Nightly ledger backup to DigitalOcean Spaces, beside wizwar's in the same bucket. # current/ - exact mirror of /var/lib/waving-hands # snapshots/ - one dated copy per day, pruned after 90 days # Ledgers are append-only JSONL; the duel server never needs stopping. # Sentry Crons check-in: /root/.waving-hands-sentry-cron holds the check-in # URL (absent file = no check-ins). set -u BUCKET="kestrel-wizwar-backups" PREFIX="waving-hands" SRC="/var/lib/waving-hands" LOG="/var/log/waving-hands/backup.log" STAMP=$(date +%Y-%m-%d) mkdir -p "$(dirname "$LOG")" CRON_URL=$(cat /root/.waving-hands-sentry-cron 2>/dev/null || true) checkin() { [ -n "$CRON_URL" ] && curl -sf -o /dev/null "$CRON_URL?status=$1" || true; } if [ ! -s /root/.config/rclone/rclone.conf ]; then echo "$(date -Is) skipped: rclone is not configured" >> "$LOG" exit 0 fi { echo "=== $(date -Is) backup start" checkin in_progress if rclone sync "$SRC" "spaces:$BUCKET/$PREFIX/current" --exclude 'rollup.jsonl' 2>&1 && rclone copy "$SRC" "spaces:$BUCKET/$PREFIX/snapshots/$STAMP" 2>&1; then checkin ok else checkin error fi CUTOFF=$(date -d "90 days ago" +%Y-%m-%d) rclone lsf "spaces:$BUCKET/$PREFIX/snapshots/" --dirs-only 2>/dev/null | \ while read -r d; do day="${d%/}" if [[ "$day" < "$CUTOFF" ]]; then echo "pruning snapshot $day" rclone purge "spaces:$BUCKET/$PREFIX/snapshots/$day" 2>&1 fi done echo "=== $(date -Is) backup done" } >> "$LOG" 2>&1