#!/bin/sh # ── LearnSpace SQLite backup ────────────────────────────────────────────── # Uses VACUUM INTO for a safe, consistent snapshot (no WAL/SHM issues). # Usage: # ./backup.sh # default paths # ./backup.sh /path/to/db /path/to/backups # KEEP=14 ./backup.sh # keep last 14 backups (default: 7) # ───────────────────────────────────────────────────────────────────────── set -e DB_PATH="${1:-${DB_PATH:-$(dirname "$0")/../data/learnspace.db}}" BACKUP_DIR="${2:-${BACKUP_DIR:-$(dirname "$0")/../../backups}}" KEEP="${KEEP:-7}" if [ ! -f "$DB_PATH" ]; then echo "[backup] ERROR: database not found at $DB_PATH" >&2 exit 1 fi mkdir -p "$BACKUP_DIR" TIMESTAMP=$(date +%Y%m%d_%H%M%S) BACKUP_FILE="$BACKUP_DIR/learnspace_${TIMESTAMP}.db" echo "[backup] Creating snapshot: $BACKUP_FILE" sqlite3 "$DB_PATH" "VACUUM INTO '$BACKUP_FILE';" # Verify backup is not empty SIZE=$(wc -c < "$BACKUP_FILE" | tr -d ' ') if [ "$SIZE" -lt 1024 ]; then echo "[backup] ERROR: backup file is suspiciously small (${SIZE} bytes)" >&2 rm -f "$BACKUP_FILE" exit 1 fi echo "[backup] OK — $(echo "scale=1; $SIZE / 1048576" | bc 2>/dev/null || echo "$SIZE bytes")" # Rotate: keep only the last $KEEP backups COUNT=$(ls -1 "$BACKUP_DIR"/learnspace_*.db 2>/dev/null | wc -l) if [ "$COUNT" -gt "$KEEP" ]; then REMOVE=$((COUNT - KEEP)) ls -1t "$BACKUP_DIR"/learnspace_*.db | tail -n "$REMOVE" | while read -r f; do echo "[backup] Removing old: $(basename "$f")" rm -f "$f" done fi echo "[backup] Done. Backups retained: $KEEP"