| 1 |
|
| 2 |
|
| 3 |
|
| 4 |
|
| 5 |
|
| 6 |
|
| 7 |
|
| 8 |
|
| 9 |
|
| 10 |
|
| 11 |
|
| 12 |
|
| 13 |
|
| 14 |
|
| 15 |
|
| 16 |
|
| 17 |
|
| 18 |
|
| 19 |
set -euo pipefail |
| 20 |
|
| 21 |
DB_NAME="${1:?usage: sync-backup-offsite.sh <db_name> <backup_file>}" |
| 22 |
BACKUP_FILE="${2:?usage: sync-backup-offsite.sh <db_name> <backup_file>}" |
| 23 |
|
| 24 |
OFFSITE_HOST="100.106.221.39" |
| 25 |
OFFSITE_USER="max" |
| 26 |
OFFSITE_DIR="/opt/backups/mnw/${DB_NAME}" |
| 27 |
OFFSITE_RETENTION_DAYS=30 |
| 28 |
WAM_URL="${WAM_URL:-http://127.0.0.1:7890}" |
| 29 |
|
| 30 |
wam_alert() { |
| 31 |
local title="$1" |
| 32 |
local body="${2:-}" |
| 33 |
curl -sf -X POST "$WAM_URL/tickets" \ |
| 34 |
-H "Content-Type: application/json" \ |
| 35 |
-d "{\"title\": \"$title\", \"body\": \"$body\", \"priority\": \"high\", \"source\": \"backup-offsite\"}" \ |
| 36 |
>/dev/null 2>&1 || true |
| 37 |
} |
| 38 |
|
| 39 |
if [ ! -f "$BACKUP_FILE" ]; then |
| 40 |
echo "[$(date -Iseconds)] OFFSITE(${DB_NAME}): backup file missing: $BACKUP_FILE" |
| 41 |
exit 0 |
| 42 |
fi |
| 43 |
|
| 44 |
BASENAME=$(basename "$BACKUP_FILE") |
| 45 |
SSH_OPTS="-o ConnectTimeout=10 -o StrictHostKeyChecking=accept-new" |
| 46 |
|
| 47 |
echo "[$(date -Iseconds)] OFFSITE(${DB_NAME}): Syncing ${BASENAME} to ${OFFSITE_HOST}:${OFFSITE_DIR}" |
| 48 |
|
| 49 |
|
| 50 |
ssh ${SSH_OPTS} "${OFFSITE_USER}@${OFFSITE_HOST}" "mkdir -p '${OFFSITE_DIR}'" |
| 51 |
|
| 52 |
if rsync -e "ssh ${SSH_OPTS}" --timeout=120 \ |
| 53 |
"$BACKUP_FILE" \ |
| 54 |
"${OFFSITE_USER}@${OFFSITE_HOST}:${OFFSITE_DIR}/"; then |
| 55 |
echo "[$(date -Iseconds)] OFFSITE(${DB_NAME}): Transfer complete" |
| 56 |
else |
| 57 |
echo "[$(date -Iseconds)] OFFSITE(${DB_NAME}): Transfer FAILED (astra unreachable or SSH error)" |
| 58 |
wam_alert "Offsite backup sync failed (${DB_NAME})" "rsync to ${OFFSITE_HOST}:${OFFSITE_DIR} failed for ${BASENAME}. Check Tailscale connectivity and SSH auth." |
| 59 |
exit 0 |
| 60 |
fi |
| 61 |
|
| 62 |
|
| 63 |
ssh ${SSH_OPTS} "${OFFSITE_USER}@${OFFSITE_HOST}" " |
| 64 |
set -e |
| 65 |
cd '${OFFSITE_DIR}' |
| 66 |
ln -f '${BASENAME}' latest.sql.gz.new |
| 67 |
mv -Tf latest.sql.gz.new latest.sql.gz |
| 68 |
" || echo "[$(date -Iseconds)] OFFSITE(${DB_NAME}): WARNING — failed to refresh latest.sql.gz" |
| 69 |
|
| 70 |
|
| 71 |
DELETED=$(ssh ${SSH_OPTS} "${OFFSITE_USER}@${OFFSITE_HOST}" \ |
| 72 |
"find ${OFFSITE_DIR} -name '${DB_NAME}-*.sql.gz' -mtime +${OFFSITE_RETENTION_DAYS} -delete -print 2>/dev/null | wc -l" \ |
| 73 |
2>/dev/null || echo "0") |
| 74 |
if [ "$DELETED" -gt 0 ]; then |
| 75 |
echo "[$(date -Iseconds)] OFFSITE(${DB_NAME}): Pruned ${DELETED} backup(s) older than ${OFFSITE_RETENTION_DAYS} days" |
| 76 |
fi |
| 77 |
|
| 78 |
TOTAL=$(ssh ${SSH_OPTS} "${OFFSITE_USER}@${OFFSITE_HOST}" \ |
| 79 |
"ls ${OFFSITE_DIR}/${DB_NAME}-*.sql.gz 2>/dev/null | wc -l" \ |
| 80 |
2>/dev/null || echo "?") |
| 81 |
echo "[$(date -Iseconds)] OFFSITE(${DB_NAME}): Total backups on astra: ${TOTAL}" |
| 82 |
|