deploy/fly-backup.sh
Rama main · réplica de lectura del repositorio original.
#!/usr/bin/env bash
# Custom off-machine backup of the cloud SQLite DB running on Fly.io.
#
# Takes a *consistent* online snapshot inside the machine (better-sqlite3
# .backup(), not a raw cp of a live WAL file), pulls it out over Fly SSH,
# gzips it, and rotates local copies. Complements Fly's own daily volume
# snapshots with an independent, off-machine copy.
#
# For a high-write production DB, upgrade to continuous replication with
# Litestream → object storage (near-zero RPO). This script is the simple,
# no-extra-infra option that is right while write volume is low.
#
# Env overrides:
# IMPRI_FLY_APP Fly app name (default impri-api)
# IMPRI_BACKUP_DIR local backup dir (default ~/.impri/backups)
# IMPRI_BACKUP_KEEP days to keep (default 14)
# FLYCTL flyctl path (default ~/.fly/bin/flyctl)
set -euo pipefail
APP="${IMPRI_FLY_APP:-impri-api}"
DEST="${IMPRI_BACKUP_DIR:-$HOME/.impri/backups}"
KEEP="${IMPRI_BACKUP_KEEP:-14}"
FLY="${FLYCTL:-$HOME/.fly/bin/flyctl}"
mkdir -p "$DEST"
TS="$(date +%F-%H%M%S)"
REMOTE="/data/_bk_${TS}.db"
LOCAL="$DEST/impri-${TS}.db"
# 1. Consistent online backup inside the machine's volume.
"$FLY" ssh console -a "$APP" -C \
"node -e \"require('/app/node_modules/better-sqlite3')('/data/impri.db').backup('${REMOTE}').then(()=>process.exit(0)).catch(e=>{console.error(e.message);process.exit(1)})\""
# 2. Pull it out, then remove the temp copy from the volume.
"$FLY" ssh sftp get "$REMOTE" "$LOCAL" -a "$APP"
"$FLY" ssh console -a "$APP" -C "rm -f ${REMOTE}"
# 3. Compress + rotate.
gzip -f "$LOCAL"
find "$DEST" -name 'impri-*.db.gz' -mtime "+${KEEP}" -delete 2>/dev/null || true
echo "backup ok: ${LOCAL}.gz ($(du -h "${LOCAL}.gz" | cut -f1))"