Baseline: wave 1A server complete, wave 1B Android scaffold + design system green
assembleDebug, testDebugUnitTest, and recordPaparazziDebug all pass. Co-Authored-By: Claude Opus 5 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_016bThmkmyUUdqQpy3MXFFe5
This commit is contained in:
Executable
+83
@@ -0,0 +1,83 @@
|
||||
#!/usr/bin/env bash
|
||||
#
|
||||
# backup.sh — SQLite-safe backup of a running PocketBase pb_data directory.
|
||||
#
|
||||
# PocketBase's data.db and auxiliary.db are SQLite databases opened in WAL
|
||||
# mode. Copying the .db files directly with `cp` while the server is running
|
||||
# can grab an inconsistent snapshot if a checkpoint happens mid-copy. Using
|
||||
# sqlite3's `.backup` command (or PocketBase's own "backups" API) takes a
|
||||
# consistent snapshot safely without stopping the server.
|
||||
#
|
||||
# This script uses `sqlite3 .backup`, which is the simplest option that needs
|
||||
# no PocketBase superuser credentials. It backs up pb_data (including
|
||||
# uploaded files) into timestamped tarballs and prunes old ones.
|
||||
#
|
||||
# Usage:
|
||||
# ./backup.sh [PB_DATA_DIR] [BACKUP_DIR]
|
||||
# PB_DATA_DIR defaults to ../pb_data (relative to this script)
|
||||
# BACKUP_DIR defaults to ./backups (relative to this script)
|
||||
#
|
||||
# Suggested cron (nightly at 3am, keep last 14):
|
||||
# 0 3 * * * /opt/bookshelf/deploy/backup.sh >> /var/log/bookshelf-backup.log 2>&1
|
||||
|
||||
set -euo pipefail
|
||||
|
||||
SCRIPT_DIR="$(cd "$(dirname "${BASH_SOURCE[0]}")" && pwd)"
|
||||
PB_DATA_DIR="${1:-$SCRIPT_DIR/../pb_data}"
|
||||
BACKUP_DIR="${2:-$SCRIPT_DIR/backups}"
|
||||
KEEP_LAST="${KEEP_LAST:-14}"
|
||||
|
||||
log() { printf '[%s] %s\n' "$(date -u '+%Y-%m-%dT%H:%M:%SZ')" "$*"; }
|
||||
fail() { log "ERROR: $*"; exit 1; }
|
||||
|
||||
command -v sqlite3 >/dev/null 2>&1 || fail "sqlite3 is required (apt install sqlite3 / apk add sqlite)."
|
||||
[[ -d "$PB_DATA_DIR" ]] || fail "pb_data directory not found: $PB_DATA_DIR"
|
||||
|
||||
TIMESTAMP="$(date -u '+%Y%m%d_%H%M%S')"
|
||||
WORKDIR="$(mktemp -d)"
|
||||
trap 'rm -rf "$WORKDIR"' EXIT
|
||||
|
||||
mkdir -p "$BACKUP_DIR"
|
||||
|
||||
log "Backing up pb_data from $PB_DATA_DIR"
|
||||
|
||||
# Consistent SQLite snapshots via the .backup command (safe for a live,
|
||||
# WAL-mode database — it's the same mechanism `sqlite3 db.sqlite .backup`
|
||||
# uses, which briefly locks for the copy but never corrupts).
|
||||
for db in data.db auxiliary.db; do
|
||||
src="$PB_DATA_DIR/$db"
|
||||
if [[ -f "$src" ]]; then
|
||||
log " snapshotting $db"
|
||||
sqlite3 "$src" ".backup '$WORKDIR/$db'"
|
||||
fi
|
||||
done
|
||||
|
||||
# Everything else under pb_data that isn't a sqlite file/WAL/SHM (i.e.
|
||||
# uploaded covers under storage/) gets copied as-is — these are immutable
|
||||
# blobs once written, so a plain copy is safe.
|
||||
log " copying uploaded files (storage/)"
|
||||
if [[ -d "$PB_DATA_DIR/storage" ]]; then
|
||||
cp -a "$PB_DATA_DIR/storage" "$WORKDIR/storage"
|
||||
fi
|
||||
|
||||
ARCHIVE="$BACKUP_DIR/bookshelf-backup-$TIMESTAMP.tar.gz"
|
||||
tar -czf "$ARCHIVE" -C "$WORKDIR" .
|
||||
log "Wrote $ARCHIVE ($(du -h "$ARCHIVE" | cut -f1))"
|
||||
|
||||
# Prune old backups, keep the most recent $KEEP_LAST.
|
||||
mapfile -t old < <(ls -1t "$BACKUP_DIR"/bookshelf-backup-*.tar.gz 2>/dev/null | tail -n +$((KEEP_LAST + 1)))
|
||||
if [[ ${#old[@]} -gt 0 ]]; then
|
||||
log "Pruning ${#old[@]} backup(s) older than the last $KEEP_LAST"
|
||||
rm -f "${old[@]}"
|
||||
fi
|
||||
|
||||
log "Done."
|
||||
|
||||
# --- Restore ---------------------------------------------------------------
|
||||
# 1. Stop the server: sudo systemctl stop bookshelf
|
||||
# 2. Move aside the live data: mv /opt/bookshelf/pb_data /opt/bookshelf/pb_data.bak
|
||||
# 3. Extract the chosen backup: mkdir /opt/bookshelf/pb_data && \
|
||||
# tar -xzf bookshelf-backup-TIMESTAMP.tar.gz -C /opt/bookshelf/pb_data
|
||||
# 4. Fix ownership: sudo chown -R bookshelf:bookshelf /opt/bookshelf/pb_data
|
||||
# 5. Start the server again: sudo systemctl start bookshelf
|
||||
# 6. Once you've confirmed it's healthy, delete pb_data.bak.
|
||||
Reference in New Issue
Block a user