SPB Git

spb/drive Public

SPB Drive — self-hosted personal cloud drive (files, previews, sharing) on the MacLustr cluster.

JavaScript 82.7% CSS 10.6% Nunjucks 3.6% Shell 1.8% SQL 1.3%
2.8 KB · 67 lines shellscript
Raw Blame History
1#!/usr/bin/env bash2# ─────────────────────────────────────────────3#  SPB Drive — Personal Cloud Drive4# ─────────────────────────────────────────────5#  Author  : Simon-Pierre Boucher6#  Contact : contact@spboucher.ai7#  File    : deploy/backup.sh8#  Purpose : Nightly backup — sqlite snapshot + hardlink-incremental blobs;9#            retention 14 dailies + 8 weeklies; verify + log10#  License : MIT © Simon-Pierre Boucher11# ─────────────────────────────────────────────12#13# Restore procedure: see README.md § Backups & restore.1415set -euo pipefail1617DATA_DIR="$HOME/srv/drive"18BACKUP_ROOT="$DATA_DIR/backups"19TODAY="$(date +%Y-%m-%d)"20DEST="$BACKUP_ROOT/$TODAY"21LAST="$(ls -1d "$BACKUP_ROOT"/20* 2>/dev/null | tail -1 || true)"2223log() { printf '%s %s\n' "$(date '+%Y-%m-%d %H:%M:%S')" "$*"; }2425mkdir -p "$DEST"2627# 1 ── SQLite online snapshot (safe under WAL).28log "sqlite backup → $DEST/drive.sqlite"29sqlite3 "$DATA_DIR/db/drive.sqlite" ".backup '$DEST/drive.sqlite'"3031# 2 ── Blob store: rsync with hardlinks against the previous backup —32#      unchanged blobs cost zero extra bytes (content-addressed, immutable).33log "files rsync (link-dest: ${LAST:-none})"34if [ -n "$LAST" ] && [ -d "$LAST/files" ] && [ "$LAST" != "$DEST" ]; then35  rsync -a --delete --link-dest="$LAST/files" "$DATA_DIR/files/" "$DEST/files/"36else37  rsync -a --delete "$DATA_DIR/files/" "$DEST/files/"38fi3940# 3 ── Auth + keys (chmod 600 preserved).41cp -p "$DATA_DIR/auth.json" "$DATA_DIR/keys.json" "$DEST/" 2>/dev/null || true4243# 4 ── Verify: DB integrity + blob count matches.44log "verifying"45sqlite3 "$DEST/drive.sqlite" 'PRAGMA integrity_check;' | grep -q '^ok$' || { log "✗ DB integrity failed"; exit 1; }46SRC_COUNT=$(find "$DATA_DIR/files" -type f | wc -l | tr -d ' ')47DST_COUNT=$(find "$DEST/files" -type f | wc -l | tr -d ' ')48if [ "$SRC_COUNT" != "$DST_COUNT" ]; then49  log "✗ blob count mismatch (src=$SRC_COUNT dst=$DST_COUNT)"; exit 150fi5152# 5 ── Retention: keep 14 dailies; keep Sunday backups 8 weeks.53log "pruning old backups"54for dir in "$BACKUP_ROOT"/20*; do55  [ -d "$dir" ] || continue56  day="$(basename "$dir")"57  age_days=$(( ( $(date +%s) - $(date -j -f %Y-%m-%d "$day" +%s 2>/dev/null || date -d "$day" +%s) ) / 86400 ))58  weekday=$(date -j -f %Y-%m-%d "$day" +%u 2>/dev/null || date -d "$day" +%u)59  if [ "$age_days" -gt 56 ]; then60    rm -rf "$dir"; log "pruned $day (older than 8 weeks)"61  elif [ "$age_days" -gt 14 ] && [ "$weekday" != 7 ]; then62    rm -rf "$dir"; log "pruned $day (daily beyond 14 days)"63  fi64done6566log "✓ backup complete: $DEST ($SRC_COUNT blobs, $(du -sh "$DEST" | cut -f1))"67