Files
ZBrain/scripts/restore.sh
T
zuevav f4bca8449e main
2026-05-20 19:33:02 +03:00

154 lines
6.4 KiB
Bash
Executable File

#!/usr/bin/env bash
#
# ZBrain Restore
# ===============
# Восстанавливает ZBrain из бэкапа созданного backup.sh.
#
# Использование:
# sudo bash scripts/restore.sh <backup-timestamp>
#
# Где <backup-timestamp> - имя папки в BACKUP_DIR (например, 20260520-031500)
#
# ВАЖНО: для расшифровки нужен приватный age ключ.
# Положи его на VM перед запуском restore (например, /tmp/backup.age).
# Скрипт спросит путь к ключу.
set -euo pipefail
# ============================================================
# Конфигурация
# ============================================================
BACKUP_DIR="${BACKUP_DIR:-/var/backups/zbrain}"
LOG_FILE="/var/log/zbrain/restore.log"
log() {
echo "[$(date '+%Y-%m-%d %H:%M:%S')] $*" | tee -a "$LOG_FILE"
}
err() {
echo "[$(date '+%Y-%m-%d %H:%M:%S')] ERROR: $*" | tee -a "$LOG_FILE" >&2
}
# ============================================================
# Аргументы
# ============================================================
if [[ $# -lt 1 ]]; then
echo "Usage: $0 <backup-timestamp>"
echo ""
echo "Available backups:"
ls -1 "$BACKUP_DIR" 2>/dev/null | grep -E '^[0-9]{8}-[0-9]{6}$' | sort -r | head -10
exit 1
fi
TIMESTAMP="$1"
BACKUP_PATH="$BACKUP_DIR/$TIMESTAMP"
[[ -d "$BACKUP_PATH" ]] || { err "Бэкап не найден: $BACKUP_PATH"; exit 1; }
[[ $EUID -eq 0 ]] || { err "Требуется sudo"; exit 1; }
# ============================================================
# Age ключ для расшифровки
# ============================================================
read -p "Путь к age приватному ключу: " AGE_KEY_PATH
[[ -f "$AGE_KEY_PATH" ]] || { err "Файл не найден: $AGE_KEY_PATH"; exit 1; }
# Тест ключа
if ! age -d -i "$AGE_KEY_PATH" "$BACKUP_PATH/_globals.sql.age" > /dev/null 2>&1; then
err "Не могу расшифровать с этим ключом"
exit 1
fi
# ============================================================
# Подтверждение
# ============================================================
cat <<EOF
╔════════════════════════════════════════════════════════════╗
║ ВНИМАНИЕ ║
║ ║
║ Восстановление перезапишет все существующие БД ZBrain! ║
║ ║
║ Бэкап: $TIMESTAMP ║
║ Файлы: ║
$(ls "$BACKUP_PATH" | sed 's/^/║ /' | awk '{printf "%-60s║\n", $0}')
║ ║
╚════════════════════════════════════════════════════════════╝
EOF
read -p "Введите 'YES' для продолжения: " CONFIRM
[[ "$CONFIRM" == "YES" ]] || { log "Отменено пользователем"; exit 0; }
log "=== Начинаю restore из $TIMESTAMP ==="
# ============================================================
# Восстановление глобальных объектов
# ============================================================
log "Восстанавливаю глобальные Postgres объекты..."
age -d -i "$AGE_KEY_PATH" -o /tmp/_globals.sql "$BACKUP_PATH/_globals.sql.age"
sudo -u postgres psql -f /tmp/_globals.sql 2>&1 | tee -a "$LOG_FILE" | grep -v "already exists" || true
rm /tmp/_globals.sql
log "✓ Globals"
# ============================================================
# Восстановление БД
# ============================================================
for encrypted_dump in "$BACKUP_PATH"/*.sql.dump.age; do
[[ -f "$encrypted_dump" ]] || continue
db_name=$(basename "$encrypted_dump" .sql.dump.age)
[[ "$db_name" == "_globals" ]] && continue
log "Восстанавливаю $db_name..."
# Дропаем существующую БД (если есть)
sudo -u postgres dropdb --if-exists "$db_name"
# Создаём пустую
sudo -u postgres createdb "$db_name"
# Расшифровываем dump
dump_file="/tmp/${db_name}.dump"
age -d -i "$AGE_KEY_PATH" -o "$dump_file" "$encrypted_dump"
# Восстанавливаем
sudo -u postgres pg_restore --dbname="$db_name" --no-owner --role=postgres "$dump_file" 2>&1 | tee -a "$LOG_FILE" || true
rm "$dump_file"
log "$db_name"
done
# ============================================================
# Конфиги (опционально)
# ============================================================
read -p "Восстановить конфиги? (включая /etc/zbrain) [y/N]: " RESTORE_CONFIG
if [[ "$RESTORE_CONFIG" =~ ^[Yy]$ ]]; then
log "Восстанавливаю конфиги..."
age -d -i "$AGE_KEY_PATH" -o /tmp/config.tar.gz "$BACKUP_PATH/config.tar.gz.age"
tar xzf /tmp/config.tar.gz -C /
rm /tmp/config.tar.gz
log "✓ Конфиги"
if [[ -f "$BACKUP_PATH/brains-meta.tar.gz.age" ]]; then
log "Восстанавливаю метаданные брейнов..."
age -d -i "$AGE_KEY_PATH" -o /tmp/brains-meta.tar.gz "$BACKUP_PATH/brains-meta.tar.gz.age"
tar xzf /tmp/brains-meta.tar.gz -C /var/lib/zbrain/
rm /tmp/brains-meta.tar.gz
log "✓ Brain configs"
fi
fi
# ============================================================
# Финал
# ============================================================
log "=== Restore завершён ==="
log ""
log "Следующие шаги:"
log " 1. Перезапусти системные сервисы:"
log " sudo systemctl restart 'zbrain-gbrain-*.service'"
log " 2. Перезапусти brainhub:"
log " cd /opt/zbrain/deploy/docker && docker compose restart"
log " 3. Проверь health endpoint каждого сервиса"
log ""
log "ВАЖНО: удали age приватный ключ с этой VM после restore!"
log " shred -u $AGE_KEY_PATH"