82 lines
3.6 KiB
Bash
Executable File
82 lines
3.6 KiB
Bash
Executable File
#!/usr/bin/env bash
|
|
# Online SQLite backup using the .backup API.
|
|
# Safe to run while the bot is live — SQLite's backup API acquires only a short
|
|
# shared lock per page, never blocking the application for the full duration.
|
|
#
|
|
# Usage:
|
|
# backup.sh --config /etc/mnemosyne/mnemosyne.conf [--keep-days N]
|
|
#
|
|
# Environment overrides:
|
|
# MNEMOSYNE_BACKUP_DIR — destination directory (default: /var/backups/mnemosyne)
|
|
# MNEMOSYNE_DB_PATH — database path (overrides config)
|
|
|
|
set -euo pipefail
|
|
|
|
# ── Defaults ─────────────────────────────────────────────────────────────────
|
|
CONFIG_FILE=""
|
|
KEEP_DAYS=30
|
|
BACKUP_DIR="${MNEMOSYNE_BACKUP_DIR:-/var/backups/mnemosyne}"
|
|
|
|
# ── Argument parsing ─────────────────────────────────────────────────────────
|
|
while [[ $# -gt 0 ]]; do
|
|
case "$1" in
|
|
--config) CONFIG_FILE="$2"; shift 2 ;;
|
|
--keep-days) KEEP_DAYS="$2"; shift 2 ;;
|
|
*) echo "Unknown argument: $1" >&2; exit 1 ;;
|
|
esac
|
|
done
|
|
|
|
# ── Resolve DB path ──────────────────────────────────────────────────────────
|
|
# Priority: env var > config file > error
|
|
if [[ -n "${MNEMOSYNE_DB_PATH:-}" ]]; then
|
|
DB_PATH="$MNEMOSYNE_DB_PATH"
|
|
elif [[ -n "$CONFIG_FILE" ]]; then
|
|
if [[ ! -f "$CONFIG_FILE" ]]; then
|
|
echo "Config file not found: $CONFIG_FILE" >&2
|
|
exit 1
|
|
fi
|
|
# Parse db.path from the INI config (section [db], key path)
|
|
DB_PATH="$(awk -F'=' '/^\[db\]/{in_db=1; next} /^\[/{in_db=0} in_db && /^[[:space:]]*path[[:space:]]*=/{gsub(/[[:space:]#].*/, "", $2); print $2; exit}' "$CONFIG_FILE")"
|
|
DB_PATH="${DB_PATH// /}" # strip spaces
|
|
if [[ -z "$DB_PATH" ]]; then
|
|
echo "Could not read db.path from $CONFIG_FILE" >&2
|
|
exit 1
|
|
fi
|
|
else
|
|
echo "Provide --config or set MNEMOSYNE_DB_PATH" >&2
|
|
exit 1
|
|
fi
|
|
|
|
if [[ ! -f "$DB_PATH" ]]; then
|
|
echo "Database not found: $DB_PATH" >&2
|
|
exit 1
|
|
fi
|
|
|
|
# ── Ensure backup directory exists ───────────────────────────────────────────
|
|
mkdir -p "$BACKUP_DIR"
|
|
|
|
# ── Run backup ───────────────────────────────────────────────────────────────
|
|
TIMESTAMP="$(date +%Y%m%d-%H%M%S)"
|
|
BACKUP_FILE="$BACKUP_DIR/mnemosyne-${TIMESTAMP}.db"
|
|
|
|
# sqlite3 .backup uses the SQLite Online Backup API — consistent snapshot,
|
|
# safe under concurrent writes, no application downtime required.
|
|
sqlite3 "$DB_PATH" ".backup '$BACKUP_FILE'"
|
|
|
|
# Verify the backup is a valid SQLite database
|
|
if ! sqlite3 "$BACKUP_FILE" "PRAGMA integrity_check;" | grep -q "^ok$"; then
|
|
echo "Backup integrity check failed: $BACKUP_FILE" >&2
|
|
rm -f "$BACKUP_FILE"
|
|
exit 1
|
|
fi
|
|
|
|
BYTES="$(wc -c < "$BACKUP_FILE")"
|
|
echo "Backup written: $BACKUP_FILE (${BYTES} bytes)"
|
|
|
|
# ── Prune old backups ─────────────────────────────────────────────────────────
|
|
if [[ "$KEEP_DAYS" -gt 0 ]]; then
|
|
PRUNED="$(find "$BACKUP_DIR" -maxdepth 1 -name 'mnemosyne-*.db' \
|
|
-mtime "+${KEEP_DAYS}" -print -delete | wc -l)"
|
|
[[ "$PRUNED" -gt 0 ]] && echo "Pruned $PRUNED backup(s) older than ${KEEP_DAYS} days"
|
|
fi
|