Public-cloud deployment of a single-user expense tracker: - 4-container stack: Caddy (HTTPS via Let's Encrypt), nginx (React/Vite SPA), Express API, Postgres 16 - Repeatable deployment via prepare-app.sh using only OCI CLI (no web console) - Persistent Postgres volume, auto-restart policies, healthchecks, backup + restore scripts - DuckDNS dynamic DNS for the public hostname; secrets isolated to gitignored .env Author: Gigi Saji Live URL: https://savesave.duckdns.org
43 lines
1.9 KiB
Bash
43 lines
1.9 KiB
Bash
#!/usr/bin/env bash
|
|
# =============================================================================
|
|
# restore.sh <backup-file.sql.gz> — restore a backup to the live database.
|
|
# WARNING: drops existing data.
|
|
# =============================================================================
|
|
|
|
set -euo pipefail
|
|
|
|
SCRIPT_DIR="$(cd "$(dirname "${BASH_SOURCE[0]}")" && pwd)"
|
|
ROOT_DIR="$(cd "$SCRIPT_DIR/.." && pwd)"
|
|
cd "$ROOT_DIR"
|
|
|
|
[ "${1:-}" ] || { echo "Usage: $0 backups/expenses-YYYYMMDD-HHMMSS.sql.gz"; exit 1; }
|
|
FILE="$1"
|
|
[ -f "$FILE" ] || { echo "ERROR: file not found: $FILE"; exit 1; }
|
|
|
|
[ -f .env ] || { echo "ERROR: .env not found"; exit 1; }
|
|
set -a; source .env; set +a
|
|
|
|
: "${OCI_VM_NAME:=sk1-expense-tracker}"
|
|
: "${POSTGRES_USER:?POSTGRES_USER must be set}"
|
|
: "${POSTGRES_DB:?POSTGRES_DB must be set}"
|
|
: "${OCI_SSH_PRIVATE_KEY_PATH:=$HOME/.ssh/id_ed25519}"
|
|
OCI_SSH_PRIVATE_KEY_PATH="${OCI_SSH_PRIVATE_KEY_PATH/#\~/$HOME}"
|
|
|
|
TENANCY_OCID="$(oci iam compartment list --query 'data[0]."compartment-id"' --raw-output)"
|
|
COMPARTMENT_ID="${OCI_COMPARTMENT_ID:-$TENANCY_OCID}"
|
|
INSTANCE_ID="$(oci compute instance list --compartment-id "$COMPARTMENT_ID" \
|
|
--display-name "$OCI_VM_NAME" --lifecycle-state RUNNING \
|
|
--query 'data[0].id' --raw-output)"
|
|
VNIC_ID="$(oci compute instance list-vnics --instance-id "$INSTANCE_ID" --query 'data[0].id' --raw-output)"
|
|
PUBLIC_IP="$(oci network vnic get --vnic-id "$VNIC_ID" --query 'data."public-ip"' --raw-output)"
|
|
|
|
read -p "Restore $FILE → $POSTGRES_DB on $PUBLIC_IP? This WIPES existing data. [y/N] " ans
|
|
[ "$ans" = "y" ] || [ "$ans" = "Y" ] || exit 1
|
|
|
|
echo "[restore] streaming dump..."
|
|
gunzip -c "$FILE" | ssh -i "$OCI_SSH_PRIVATE_KEY_PATH" -o StrictHostKeyChecking=no \
|
|
-o UserKnownHostsFile=/dev/null ubuntu@"$PUBLIC_IP" \
|
|
"sg docker -c 'docker exec -i sk1-db pg_restore -U $POSTGRES_USER -d $POSTGRES_DB --clean --if-exists'"
|
|
|
|
echo "[restore] done."
|