Files
glchat/deploy/restore.sh
T
grendervill e97f260113 fix(ops): проверка готовности учитывает адрес привязки приложения
`glchat update` на стенде с внешним прокси всегда сообщал «приложение не
поднялось»: readyz проверялся на 127.0.0.1, а приложение слушает адрес в
WireGuard (GLCHAT_APP_BIND). Тот же дефект был в restore.sh и в `glchat status`
(health показывался как unreachable). Добавлен общий помощник app_check_url.
2026-09-22 20:49:13 +03:00

109 lines
4.2 KiB
Bash
Executable File

#!/usr/bin/env bash
# glchat: восстановление из бэкапа (AGENT.md 10.8).
set -euo pipefail
GLCHAT_ETC_DIR="${GLCHAT_ETC_DIR:-/opt/glchat}"
GLCHAT_DATA_DIR="${GLCHAT_DATA_DIR:-${GLCHAT_ETC_DIR}/data}"
ENV_FILE="${GLCHAT_ETC_DIR}/.env"
COMPOSE_FILE="${GLCHAT_ETC_DIR}/compose.yaml"
log() { printf '[restore] %s\n' "$*"; }
# env_get читает значение из .env инстанса (пусто, если ключа нет).
env_get() {
[ -f "$ENV_FILE" ] || return 0
sed -n "s/^$1=//p" "$ENV_FILE" | tail -1
}
# app_check_url печатает адрес /readyz с учётом привязки приложения: во внешнем
# режиме приложение слушает WireGuard-адрес, и проверка localhost не работает.
app_check_url() {
local port="$1" host="${2:-}"
case "$host" in
0.0.0.0 | "" | "::") host="127.0.0.1" ;;
esac
printf 'http://%s:%s/readyz' "$host" "$port"
}
die() {
printf '[restore] ошибка: %s\n' "$*" >&2
exit 1
}
ARCHIVE="${1:-}"
[ -n "$ARCHIVE" ] || die "использование: glchat restore <файл.tar.gz[.age]>"
[ -f "$ARCHIVE" ] || die "файл не найден: $ARCHIVE"
[ -f "$ENV_FILE" ] || die "не найден ${ENV_FILE}"
[ -f "$COMPOSE_FILE" ] || die "не найден ${COMPOSE_FILE}"
# shellcheck disable=SC1090
set -a && . "$ENV_FILE" && set +a
WORK_DIR="$(mktemp -d)"
cleanup() { rm -rf "$WORK_DIR"; }
trap cleanup EXIT
case "$ARCHIVE" in
*.age)
command -v age >/dev/null 2>&1 || die "age не установлен"
KEY_FILE="${AGE_KEY_FILE:-${GLCHAT_DATA_DIR}/backups/age-key.txt}"
[ -f "$KEY_FILE" ] || die "не найден приватный age-ключ (${KEY_FILE}); укажите AGE_KEY_FILE"
log "расшифровываем ${ARCHIVE}"
age -d -i "$KEY_FILE" -o "${WORK_DIR}/backup.tar.gz" "$ARCHIVE" || die "не удалось расшифровать бэкап"
;;
*) cp "$ARCHIVE" "${WORK_DIR}/backup.tar.gz" ;;
esac
log "распаковываем"
tar -xzf "${WORK_DIR}/backup.tar.gz" -C "$WORK_DIR"
[ -f "${WORK_DIR}/glchat.db" ] || die "в архиве нет glchat.db — это не бэкап glchat"
log "текущее состояние"
(cd "$GLCHAT_ETC_DIR" && docker compose ps) || true
log "создаём страховочный бэкап текущих данных"
if [ "${GLCHAT_SKIP_PREBACKUP:-0}" != "1" ]; then
GLCHAT_ETC_DIR="$GLCHAT_ETC_DIR" bash "${GLCHAT_ETC_DIR}/backup.sh" || die "страховочный бэкап не удался; восстановление остановлено"
fi
printf 'Восстановление заменит текущую БД и файлы. Продолжить? (yes/no): '
read -r reply
[ "$reply" = "yes" ] || {
log "отменено"
exit 0
}
log "останавливаем приложение"
(cd "$GLCHAT_ETC_DIR" && docker compose stop app) || true
rollback_dir="${GLCHAT_DATA_DIR}/pre-restore-$(date -u +%Y%m%dT%H%M%SZ)"
mkdir -p "$rollback_dir"
cp -a "${GLCHAT_DATA_DIR}/db" "$rollback_dir/" 2>/dev/null || true
log "текущие данные скопированы в ${rollback_dir}"
log "заменяем базу"
install -m 640 "${WORK_DIR}/glchat.db" "${GLCHAT_DATA_DIR}/db/glchat.db"
rm -f "${GLCHAT_DATA_DIR}/db/glchat.db-wal" "${GLCHAT_DATA_DIR}/db/glchat.db-shm"
if [ -d "${WORK_DIR}/files" ]; then
log "заменяем файлы"
rm -rf "${GLCHAT_DATA_DIR}/files"
mkdir -p "${GLCHAT_DATA_DIR}/files"
cp -a "${WORK_DIR}/files/." "${GLCHAT_DATA_DIR}/files/"
fi
log "запускаем приложение"
(cd "$GLCHAT_ETC_DIR" && docker compose start app) || (cd "$GLCHAT_ETC_DIR" && docker compose up -d app)
for _ in $(seq 1 30); do
if curl -fsS "$(app_check_url "${APP_PORT:-8080}" "$(env_get GLCHAT_APP_BIND)")" >/dev/null 2>&1; then
log "приложение готово"
GLCHAT_ETC_DIR="$GLCHAT_ETC_DIR" bash "${GLCHAT_ETC_DIR}/smoke.sh" || true
log "восстановление завершено"
exit 0
fi
sleep 2
done
die "приложение не поднялось после восстановления; откат: ${rollback_dir}"