Files
glchat/deploy/backup.sh
T

84 lines
2.9 KiB
Bash
Raw Normal View History

#!/usr/bin/env bash
# glchat: бэкап БД и файлов с age-шифрованием (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"
BACKUP_DIR="${GLCHAT_DATA_DIR}/backups"
KEEP_DAYS_DEFAULT=7
log() { printf '[backup] %s\n' "$*"; }
die() {
printf '[backup] ошибка: %s\n' "$*" >&2
exit 1
}
[ -f "$ENV_FILE" ] || die "не найден ${ENV_FILE}"
# shellcheck disable=SC1090
set -a && . "$ENV_FILE" && set +a
KEEP_DAYS="${BACKUP_KEEP_DAYS:-$KEEP_DAYS_DEFAULT}"
AGE_RECIPIENT="${AGE_RECIPIENT:-}"
STAMP="$(date -u +%Y%m%dT%H%M%SZ)"
WORK_DIR="$(mktemp -d)"
ARCHIVE="${BACKUP_DIR}/glchat-${STAMP}.tar.gz"
TARGET="${ARCHIVE}.age"
cleanup() { rm -rf "$WORK_DIR"; }
trap cleanup EXIT
mkdir -p "$BACKUP_DIR"
chmod 700 "$BACKUP_DIR"
log "дамп SQLite (sqlite3 .backup)"
if command -v sqlite3 >/dev/null 2>&1; then
sqlite3 "${GLCHAT_DATA_DIR}/db/glchat.db" ".backup '${WORK_DIR}/glchat.db'" ||
die "не удалось сделать дамп базы"
else
docker exec glchat-app true 2>/dev/null || die "sqlite3 не установлен и контейнер недоступен"
docker cp glchat-app:/app/data/db/glchat.db "${WORK_DIR}/glchat.db"
fi
log "копируем файлы"
mkdir -p "${WORK_DIR}/files"
if [ -d "${GLCHAT_DATA_DIR}/files" ]; then
if command -v rsync >/dev/null 2>&1; then
rsync -a --delete "${GLCHAT_DATA_DIR}/files/" "${WORK_DIR}/files/"
else
cp -a "${GLCHAT_DATA_DIR}/files/." "${WORK_DIR}/files/"
fi
fi
log "сохраняем конфиги (без секретов)"
mkdir -p "${WORK_DIR}/config"
cp -a "${GLCHAT_ETC_DIR}/compose.yaml" "${WORK_DIR}/config/" 2>/dev/null || true
cp -a "${GLCHAT_ETC_DIR}/livekit" "${WORK_DIR}/config/" 2>/dev/null || true
cp -a "${GLCHAT_ETC_DIR}/external-proxy.conf" "${WORK_DIR}/config/" 2>/dev/null || true
cat >"${WORK_DIR}/MANIFEST.txt" <<EOF
glchat backup
created_at=${STAMP}
domain=${DOMAIN:-unknown}
version=${APP_VERSION:-unknown}
profile=${PROFILE:-unknown}
EOF
log "упаковываем"
tar -czf "$ARCHIVE" -C "$WORK_DIR" .
if [ -n "$AGE_RECIPIENT" ]; then
command -v age >/dev/null 2>&1 || die "age не установлен, а AGE_RECIPIENT задан"
age -r "$AGE_RECIPIENT" -o "$TARGET" "$ARCHIVE" || die "age-шифрование не удалось"
rm -f "$ARCHIVE"
chmod 600 "$TARGET"
log "готово: ${TARGET} ($(du -h "$TARGET" | cut -f1))"
else
chmod 600 "$ARCHIVE"
log "готово (БЕЗ шифрования, AGE_RECIPIENT не задан): ${ARCHIVE}"
fi
log "ротация: оставляем ${KEEP_DAYS} дн."
find "$BACKUP_DIR" -maxdepth 1 -name 'glchat-*.tar.gz*' -mtime "+${KEEP_DAYS}" -print -delete || true
log "бэкапов в каталоге: $(find "$BACKUP_DIR" -maxdepth 1 -name 'glchat-*.tar.gz*' | wc -l)"