mirror of
https://codeberg.org/PostERG/xamxam.git
synced 2026-09-25 09:53:08 +02:00
- surface backup/cleanup cron logs + backup freshness status - email xamxam@erg.be when SQLite backups go stale (backup watchdog) - sync SQLite snapshots to Nextcloud WebDAV + remote-freshness watchdog - precise retention pruning, manual sync in check recipe, and Nextcloud-sync docs
55 lines
2.1 KiB
Bash
Executable File
55 lines
2.1 KiB
Bash
Executable File
#!/bin/bash
|
|
# backup-sqlite.sh — Safe hot backup of the XAMXAM SQLite database.
|
|
#
|
|
# Uses sqlite3's .backup command (WAL-safe) then gzip-compresses.
|
|
# Prunes backups older than RETENTION_DAYS.
|
|
#
|
|
# Usage:
|
|
# /usr/local/bin/backup-sqlite.sh # default: 30 days
|
|
# RETENTION_DAYS=90 /usr/local/bin/backup-sqlite.sh # 90 days
|
|
#
|
|
# Expected to be run from cron:
|
|
# 0 * * * * /usr/local/bin/backup-sqlite.sh >> /var/log/xamxam-backup-$(date +%Y-%m-%d).log 2>&1
|
|
# 0 2 * * * RETENTION_DAYS=90 /usr/local/bin/backup-sqlite.sh >> /var/log/xamxam-backup-$(date +%Y-%m-%d).log 2>&1
|
|
|
|
set -euo pipefail
|
|
|
|
DB_PATH="${DB_PATH:-/var/www/xamxam/storage/xamxam.db}"
|
|
BACKUP_DIR="${BACKUP_DIR:-/var/backups/xamxam}"
|
|
RETENTION_DAYS="${RETENTION_DAYS:-30}"
|
|
|
|
TIMESTAMP=$(date +"%Y-%m-%dT%H-%M-%S")
|
|
BACKUP_FILE="$BACKUP_DIR/db-$TIMESTAMP.db.gz"
|
|
TMP_SNAPSHOT="/tmp/xamxam-snapshot-$$.db"
|
|
|
|
mkdir -p "$BACKUP_DIR" 2>/dev/null || {
|
|
echo "ERROR: Cannot create backup directory '$BACKUP_DIR'. Run: just deploy-backup-cron" >&2
|
|
exit 1
|
|
}
|
|
|
|
# Safe hot backup using SQLite's online backup API
|
|
sqlite3 "$DB_PATH" ".backup $TMP_SNAPSHOT"
|
|
gzip -c "$TMP_SNAPSHOT" > "$BACKUP_FILE"
|
|
rm -f "$TMP_SNAPSHOT"
|
|
|
|
# Prune old backups.
|
|
# Use filename timestamps (deterministic) rather than -mtime, which has
|
|
# day-rounding ambiguity (-mtime +N means N+1 days). Files are named
|
|
# db-YYYY-MM-DDTHH-MM-SS.db.gz, so we delete anything whose date is more
|
|
# than RETENTION_DAYS in the past.
|
|
RETENTION_SECONDS=$((RETENTION_DAYS * 86400))
|
|
NOW_EPOCH=$(date +%s)
|
|
for f in "$BACKUP_DIR"/db-*.db.gz; do
|
|
[ -e "$f" ] || continue
|
|
base=$(basename "$f" .db.gz) # db-YYYY-MM-DDTHH-MM-SS
|
|
stamp=${base#db-} # YYYY-MM-DDTHH-MM-SS
|
|
# Reformat YYYY-MM-DDTHH-MM-SS → YYYY-MM-DD HH:MM:SS for date(1).
|
|
named=$(echo "$stamp" | sed 's/T/ /; s/-/:/3g') || continue
|
|
ts=$(date -d "$named" +%s 2>/dev/null) || continue
|
|
if [ $((NOW_EPOCH - ts)) -gt $RETENTION_SECONDS ]; then
|
|
rm -f "$f"
|
|
fi
|
|
done
|
|
|
|
echo "[$(date '+%Y-%m-%d %H:%M:%S')] Backup written: $BACKUP_FILE ($(du -h "$BACKUP_FILE" | cut -f1))"
|