| 1 | #!/usr/bin/env bash
|
|---|
| 2 | #
|
|---|
| 3 | # PrutCMS v10 — nightly backup script.
|
|---|
| 4 | #
|
|---|
| 5 | # Snapshots:
|
|---|
| 6 | # • SQLite database (uses .backup so it's safe while the app is running)
|
|---|
| 7 | # • storage/audio/ (uploaded MP3s — files outside /media for security)
|
|---|
| 8 | # • storage/media/ (avatars, audio covers — public files)
|
|---|
| 9 | # • storage/.audio-secret (HMAC secret — needed to verify old signed URLs)
|
|---|
| 10 | #
|
|---|
| 11 | # Output: a single tar.gz per run in $BACKUP_DIR, named by timestamp.
|
|---|
| 12 | # Rotation: keeps the last $KEEP_DAYS dumps, prunes older.
|
|---|
| 13 | #
|
|---|
| 14 | # Recommended cron:
|
|---|
| 15 | # 0 3 * * * /home/robin/prutcms/deploy/backup.sh >> /home/robin/prutcms/logs/backup.log 2>&1
|
|---|
| 16 |
|
|---|
| 17 | set -euo pipefail
|
|---|
| 18 |
|
|---|
| 19 | # ── Config ────────────────────────────────────────────────────────
|
|---|
| 20 | APP_DIR="${APP_DIR:-/home/robin/prutcms}"
|
|---|
| 21 | BACKUP_DIR="${BACKUP_DIR:-/home/robin/backups/prutcms}"
|
|---|
| 22 | KEEP_DAYS="${KEEP_DAYS:-14}"
|
|---|
| 23 | TS="$(date +%Y%m%d-%H%M%S)"
|
|---|
| 24 |
|
|---|
| 25 | mkdir -p "$BACKUP_DIR"
|
|---|
| 26 |
|
|---|
| 27 | # ── DB snapshot via sqlite3 .backup (consistent under WAL) ────────
|
|---|
| 28 | DB_FILE="$APP_DIR/storage/database.sqlite"
|
|---|
| 29 | DB_SNAPSHOT="$BACKUP_DIR/db-$TS.sqlite"
|
|---|
| 30 | if [ -f "$DB_FILE" ]; then
|
|---|
| 31 | sqlite3 "$DB_FILE" ".backup '$DB_SNAPSHOT'"
|
|---|
| 32 | else
|
|---|
| 33 | echo "WARN: $DB_FILE not found, skipping DB backup" >&2
|
|---|
| 34 | fi
|
|---|
| 35 |
|
|---|
| 36 | # ── Tar the snapshot + storage subdirs ────────────────────────────
|
|---|
| 37 | ARCHIVE="$BACKUP_DIR/prutcms-$TS.tar.gz"
|
|---|
| 38 | tar -czf "$ARCHIVE" \
|
|---|
| 39 | -C "$BACKUP_DIR" "$(basename "$DB_SNAPSHOT")" \
|
|---|
| 40 | -C "$APP_DIR/storage" \
|
|---|
| 41 | $( [ -d "$APP_DIR/storage/audio" ] && echo audio ) \
|
|---|
| 42 | $( [ -d "$APP_DIR/storage/media" ] && echo media ) \
|
|---|
| 43 | $( [ -f "$APP_DIR/storage/.audio-secret" ] && echo .audio-secret )
|
|---|
| 44 |
|
|---|
| 45 | # Remove the loose db-XXX.sqlite (it's inside the tarball now)
|
|---|
| 46 | rm -f "$DB_SNAPSHOT"
|
|---|
| 47 |
|
|---|
| 48 | # ── Rotation ───────────────────────────────────────────────────────
|
|---|
| 49 | find "$BACKUP_DIR" -type f -name 'prutcms-*.tar.gz' -mtime "+$KEEP_DAYS" -delete
|
|---|
| 50 |
|
|---|
| 51 | SIZE="$(du -h "$ARCHIVE" | cut -f1)"
|
|---|
| 52 | echo "[$TS] backup OK: $ARCHIVE ($SIZE)"
|
|---|