featchoremiscmiscmisc
This commit is contained in:
parent
fb3d904f5b
commit
44f345fff0
8 changed files with 914 additions and 0 deletions
108
backup-deployed.sh
Executable file
108
backup-deployed.sh
Executable file
|
|
@ -0,0 +1,108 @@
|
|||
#!/usr/bin/env bash
|
||||
|
||||
set -Eeuo pipefail
|
||||
|
||||
ACTIVATE=false
|
||||
if [[ "${1:-}" == "--activate" ]]; then
|
||||
ACTIVATE=true
|
||||
shift
|
||||
fi
|
||||
if [[ "$#" -ne 0 ]]; then
|
||||
echo "Usage: $0 [--activate]" >&2
|
||||
exit 2
|
||||
fi
|
||||
|
||||
REMOTE="${BLISS_BACKUP_REMOTE:-root@134.122.14.193}"
|
||||
APP_DIR="${BLISS_BACKUP_APP_DIR:-/home/nodejs/bliss2}"
|
||||
LOCAL_DIR="${BLISS_BACKUP_LOCAL_DIR:-$(pwd)/backups}"
|
||||
STAMP="$(date +%Y%m%d-%H%M%S)"
|
||||
ARCHIVE="$LOCAL_DIR/bliss-backup-$STAMP.tar.gz"
|
||||
PARTIAL="$ARCHIVE.partial"
|
||||
|
||||
mkdir -p "$LOCAL_DIR"
|
||||
rm -f "$PARTIAL"
|
||||
|
||||
cleanup() {
|
||||
rm -f "$PARTIAL"
|
||||
}
|
||||
trap cleanup EXIT
|
||||
|
||||
echo "Backing up $REMOTE:$APP_DIR"
|
||||
echo "You may be prompted for the droplet passcode."
|
||||
|
||||
ssh -o ConnectTimeout=15 "$REMOTE" bash -s -- "$APP_DIR" >"$PARTIAL" <<'REMOTE_SCRIPT'
|
||||
set -Eeuo pipefail
|
||||
|
||||
APP_DIR="$1"
|
||||
test -d "$APP_DIR/public"
|
||||
test -d "$APP_DIR/dbs"
|
||||
test -f "$APP_DIR/package.json"
|
||||
|
||||
TMP_DIR="$(mktemp -d /root/bliss-backup.XXXXXX)"
|
||||
cleanup_remote() {
|
||||
rm -rf "$TMP_DIR"
|
||||
}
|
||||
trap cleanup_remote EXIT
|
||||
|
||||
mkdir -p "$TMP_DIR/dbs"
|
||||
cd "$APP_DIR"
|
||||
|
||||
echo "Creating consistent SQLite snapshots..." >&2
|
||||
BACKUP_DIR="$TMP_DIR/dbs" node <<'NODE'
|
||||
const fs = require("fs");
|
||||
const path = require("path");
|
||||
const Database = require("better-sqlite3");
|
||||
|
||||
(async () => {
|
||||
const names = fs.readdirSync("dbs").filter(name => name.endsWith(".sqlite"));
|
||||
if (names.length === 0) throw new Error("No SQLite databases found in dbs/");
|
||||
|
||||
for (const name of names) {
|
||||
const source = new Database(path.resolve("dbs", name), { readonly: true });
|
||||
const destination = path.join(process.env.BACKUP_DIR, name);
|
||||
await source.backup(destination);
|
||||
source.close();
|
||||
|
||||
const snapshot = new Database(destination, { readonly: true });
|
||||
const result = snapshot.pragma("quick_check", { simple: true });
|
||||
snapshot.close();
|
||||
if (result !== "ok") throw new Error(`${name}: SQLite quick_check returned ${result}`);
|
||||
console.error(` ${name}: OK`);
|
||||
}
|
||||
})().catch(error => {
|
||||
console.error(error);
|
||||
process.exit(1);
|
||||
});
|
||||
NODE
|
||||
|
||||
echo "Copying static files..." >&2
|
||||
cp -a public "$TMP_DIR/public"
|
||||
|
||||
printf 'source=%s\ncreated_utc=%s\n' \
|
||||
"$APP_DIR" "$(date -u +%Y-%m-%dT%H:%M:%SZ)" >"$TMP_DIR/MANIFEST.txt"
|
||||
|
||||
echo "Streaming compressed archive..." >&2
|
||||
tar -C "$TMP_DIR" -czf - MANIFEST.txt public dbs
|
||||
REMOTE_SCRIPT
|
||||
|
||||
echo "Verifying downloaded archive..."
|
||||
gzip -t "$PARTIAL"
|
||||
tar -tzf "$PARTIAL" >/dev/null
|
||||
|
||||
DB_COUNT="$(tar -tzf "$PARTIAL" | awk '/^dbs\/[^/]+\.sqlite$/ {count++} END {print count+0}')"
|
||||
if [[ "$DB_COUNT" -eq 0 ]]; then
|
||||
echo "Backup verification failed: archive contains no SQLite databases" >&2
|
||||
exit 1
|
||||
fi
|
||||
|
||||
mv "$PARTIAL" "$ARCHIVE"
|
||||
trap - EXIT
|
||||
|
||||
echo "Backup complete: $ARCHIVE"
|
||||
echo "SQLite databases: $DB_COUNT"
|
||||
du -h "$ARCHIVE"
|
||||
|
||||
if [[ "$ACTIVATE" == true ]]; then
|
||||
echo
|
||||
"$(dirname "$0")/switch-bliss-data.sh" "$ARCHIVE"
|
||||
fi
|
||||
Loading…
Add table
Add a link
Reference in a new issue