Files
ForbiddenStarsApp/scripts/backup.sh
T
NotBigGhostandClaude Opus 4.8 32c74abc43 Бэкап: полный оффсайт-бэкап прода на VPS (БД + uploads + achievements) + восстановление
- scripts/backup.sh: консистентный снимок SQLite (.backup) + архив fs_<TS>.tar.gz с uploads и
  achievements в backups/, локальная ротация; при заданном BACKUP_VPS_HOST — push на VPS (scp
  отдельным ключом/юзером) с удалённой ротацией. Настройки читаются из .env (BACKUP_*).
- scripts/restore.sh: восстановление архива в прод (stop app → очистка тома → docker cp → start).
- .env.example: блок BACKUP_* (host/user/dir/key/keep). .gitignore: id_backup* (ключ бэкапа).
- deploy/pi/README, deploy/vps/README: настройка ключа и юзера backup, /srv/fs-backups, cron, restore.

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
2026-06-26 23:36:58 +03:00

75 lines
3.9 KiB
Bash
Executable File
Raw Blame History

This file contains ambiguous Unicode characters
This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.
#!/usr/bin/env bash
# Полный консистентный онлайн-бэкап прода (БД + uploads + achievements) с работающего
# контейнера + оффсайт-копия на VPS. Запуск по cron на хосте Pi, например ежедневно в 04:00:
# 0 4 * * * /home/pi/ForbidenStarsApp/scripts/backup.sh >> /var/log/fs-backup.log 2>&1
#
# Настройки берутся из корневого .env (BACKUP_*), их можно переопределить переменными
# окружения. Если BACKUP_VPS_HOST пуст — делается только локальная копия в backups/.
set -euo pipefail
PROJECT_DIR="$(cd "$(dirname "$0")/.." && pwd)"
COMPOSE="docker compose -f $PROJECT_DIR/docker-compose.yml"
SERVICE="app"
TS="$(date +%Y%m%d_%H%M%S)"
# Безопасно прочитать ОДИН ключ из .env (НЕ source: в .env есть спецсимволы # & и т.п.).
read_env() { # $1=ключ $2=дефолт
local val
val="$(grep -E "^$1=" "$PROJECT_DIR/.env" 2>/dev/null | head -1 | cut -d= -f2- | tr -d '\r')"
val="${val%\"}"; val="${val#\"}" # снять обрамляющие кавычки, если есть
printf '%s' "${val:-$2}"
}
BACKUP_DIR="${BACKUP_DIR:-$PROJECT_DIR/backups}"
KEEP_LOCAL="${BACKUP_KEEP_LOCAL:-$(read_env BACKUP_KEEP_LOCAL 14)}"
KEEP_REMOTE="${BACKUP_KEEP_REMOTE:-$(read_env BACKUP_KEEP_REMOTE 30)}"
VPS_HOST="${BACKUP_VPS_HOST:-$(read_env BACKUP_VPS_HOST "")}"
VPS_USER="${BACKUP_VPS_USER:-$(read_env BACKUP_VPS_USER backup)}"
VPS_DIR="${BACKUP_VPS_DIR:-$(read_env BACKUP_VPS_DIR /srv/fs-backups)}"
VPS_KEY="${BACKUP_VPS_KEY:-$(read_env BACKUP_VPS_KEY "$PROJECT_DIR/deploy/backup/id_backup")}"
mkdir -p "$BACKUP_DIR"
STAGE="$(mktemp -d)"
trap 'rm -rf "$STAGE"' EXIT
CID="$($COMPOSE ps -q "$SERVICE")"
if [ -z "$CID" ]; then
echo "[backup] ОШИБКА: контейнер '$SERVICE' не запущен." >&2
exit 1
fi
echo "[backup] Консистентный снимок БД…"
$COMPOSE exec -T "$SERVICE" python -c "import sqlite3; s=sqlite3.connect('/data/forbidden_stars.db'); d=sqlite3.connect('/data/backup_tmp.db'); s.backup(d); d.close(); s.close()"
docker cp "$CID:/data/backup_tmp.db" "$STAGE/forbidden_stars.db"
$COMPOSE exec -T "$SERVICE" rm -f /data/backup_tmp.db
echo "[backup] Копирование медиа и титулов…"
docker cp "$CID:/data/uploads" "$STAGE/uploads"
docker cp "$CID:/data/achievements" "$STAGE/achievements"
ARCHIVE="$BACKUP_DIR/fs_$TS.tar.gz"
tar -czf "$ARCHIVE" -C "$STAGE" forbidden_stars.db uploads achievements
echo "[backup] Локальный архив: $ARCHIVE ($(du -h "$ARCHIVE" | cut -f1))"
# Локальная ротация: оставить последние $KEEP_LOCAL (архив только что создан → ≥1 файл).
ls -1t "$BACKUP_DIR"/fs_*.tar.gz 2>/dev/null | tail -n +"$((KEEP_LOCAL + 1))" | xargs -r rm -f
# Оффсайт-копия на VPS (push: Pi за CGNAT, входящих нет).
if [ -n "$VPS_HOST" ]; then
if [ ! -f "$VPS_KEY" ]; then
echo "[backup] ОШИБКА: ключ $VPS_KEY не найден — оффсайт-копия пропущена." >&2
exit 1
fi
echo "[backup] Отправка на VPS $VPS_USER@$VPS_HOST:$VPS_DIR …"
SSH_OPTS=(-i "$VPS_KEY" -o StrictHostKeyChecking=accept-new -o BatchMode=yes)
scp "${SSH_OPTS[@]}" "$ARCHIVE" "$VPS_USER@$VPS_HOST:$VPS_DIR/"
# Удалённая ротация (sh на VPS без pipefail → пустой ls не уронит команду).
ssh "${SSH_OPTS[@]}" "$VPS_USER@$VPS_HOST" \
"ls -1t '$VPS_DIR'/fs_*.tar.gz 2>/dev/null | tail -n +$((KEEP_REMOTE + 1)) | xargs -r rm -f"
echo "[backup] Оффсайт-копия отправлена."
else
echo "[backup] BACKUP_VPS_HOST не задан — только локальная копия."
fi
echo "[backup] Готово."