#!/usr/bin/env bash # HIY restore script # # Restores a backup archive produced by infra/backup.sh. # # Usage: # ./infra/restore.sh /path/to/hiy-backup-20260101-030000.tar.gz # # What is restored: # 1. SQLite database (hiy.db) # 2. Env files and git repos # 3. Postgres databases (pg_dumpall dump) # 4. Forgejo data volume # 5. Caddy TLS certificates # 6. .env file (optional — skipped if already present unless --force is passed) # # ⚠ Run this with the stack STOPPED, then bring it back up afterwards: # podman compose -f infra/docker-compose.yml down # ./infra/restore.sh hiy-backup-*.tar.gz # podman compose -f infra/docker-compose.yml up -d set -euo pipefail ARCHIVE="${1:-}" FORCE="${2:-}" SCRIPT_DIR="$(cd "$(dirname "$0")" && pwd)" ENV_FILE="${SCRIPT_DIR}/../.env" HIY_DATA_DIR="${HIY_DATA_DIR:-/data}" log() { echo "[hiy-restore] $(date '+%H:%M:%S') $*"; } die() { log "ERROR: $*"; exit 1; } # ── Validate ─────────────────────────────────────────────────────────────────── [ -z "${ARCHIVE}" ] && die "Usage: $0 [--force]" [ -f "${ARCHIVE}" ] || die "Archive not found: ${ARCHIVE}" WORK_DIR=$(mktemp -d) trap 'rm -rf "${WORK_DIR}"' EXIT log "=== HIY Restore ===" log "Archive : ${ARCHIVE}" log "Work dir: ${WORK_DIR}" log "Extracting archive…" tar -xzf "${ARCHIVE}" -C "${WORK_DIR}" # ── Helper: find a running container by compose service label ────────────────── find_container() { local service="$1" podman ps --filter "label=com.docker.compose.service=${service}" \ --format '{{.Names}}' | head -1 } # ── 1. .env file ─────────────────────────────────────────────────────────────── log "--- .env ---" if [ -f "${WORK_DIR}/dot-env" ]; then if [ -f "${ENV_FILE}" ] && [ "${FORCE}" != "--force" ]; then log "SKIP: ${ENV_FILE} already exists (pass --force to overwrite)" else cp "${WORK_DIR}/dot-env" "${ENV_FILE}" log "Restored .env to ${ENV_FILE}" fi else log "No .env in archive — skipping" fi # ── 2. SQLite ────────────────────────────────────────────────────────────────── log "--- SQLite ---" if [ -f "${WORK_DIR}/hiy.sql" ]; then DB_PATH="${HIY_DATA_DIR}/hiy.db" mkdir -p "$(dirname "${DB_PATH}")" if [ -f "${DB_PATH}" ]; then log "Moving existing hiy.db to hiy.db.bak…" mv "${DB_PATH}" "${DB_PATH}.bak" fi log "Restoring hiy.db…" sqlite3 "${DB_PATH}" < "${WORK_DIR}/hiy.sql" log "SQLite restored." else log "No hiy.sql in archive — skipping" fi # ── 3. Env files & git repos ─────────────────────────────────────────────────── log "--- Env files ---" if [ -f "${WORK_DIR}/envs.tar.gz" ]; then log "Restoring envs/…" tar -xzf "${WORK_DIR}/envs.tar.gz" -C "${HIY_DATA_DIR}" fi log "--- Git repos ---" if [ -f "${WORK_DIR}/repos.tar.gz" ]; then log "Restoring repos/…" tar -xzf "${WORK_DIR}/repos.tar.gz" -C "${HIY_DATA_DIR}" fi # ── 4. Postgres ──────────────────────────────────────────────────────────────── log "--- Postgres ---" if [ -f "${WORK_DIR}/postgres.sql" ]; then PG_CTR=$(find_container postgres) if [ -n "${PG_CTR}" ]; then log "Restoring Postgres via container ${PG_CTR}…" # Drop existing connections then restore. podman exec -i "${PG_CTR}" psql -U hiy_admin -d postgres \ -c "SELECT pg_terminate_backend(pid) FROM pg_stat_activity WHERE datname IN ('hiy','forgejo') AND pid <> pg_backend_pid();" \ > /dev/null 2>&1 || true podman exec -i "${PG_CTR}" psql -U hiy_admin -d postgres \ < "${WORK_DIR}/postgres.sql" log "Postgres restored." else log "WARNING: postgres container not running" log " Start Postgres first, then run:" log " podman exec -i psql -U hiy_admin -d postgres < ${WORK_DIR}/postgres.sql" fi else log "No postgres.sql in archive — skipping" fi # ── 5. Forgejo data volume ───────────────────────────────────────────────────── log "--- Forgejo volume ---" if [ -f "${WORK_DIR}/forgejo-data.tar" ]; then log "Importing forgejo-data volume…" podman volume exists forgejo-data 2>/dev/null || podman volume create forgejo-data podman volume import forgejo-data "${WORK_DIR}/forgejo-data.tar" log "forgejo-data restored." else log "No forgejo-data.tar in archive — skipping" fi # ── 6. Caddy TLS certificates ────────────────────────────────────────────────── log "--- Caddy volume ---" if [ -f "${WORK_DIR}/caddy-data.tar" ]; then log "Importing caddy-data volume…" podman volume exists caddy-data 2>/dev/null || podman volume create caddy-data podman volume import caddy-data "${WORK_DIR}/caddy-data.tar" log "caddy-data restored." else log "No caddy-data.tar in archive — skipping" fi log "=== Restore complete ===" log "Bring the stack back up with:" log " podman compose -f ${SCRIPT_DIR}/docker-compose.yml up -d"