feat(deploy): установщик, docker-скелет, шаблоны Caddy/LiveKit и CLI
- deploy/install.sh: идемпотентная установка, режимы встроенного и внешнего прокси (§10.13), --dry-run/--skip-tls/--reconfigure, auto-детект профиля (§10.10) - deploy/compose.yaml.tpl: app, livekit, caddy с лимитами, healthcheck, ротацией логов, cap_drop и no-new-privileges (§4.3) - deploy/caddy: Caddyfile.tpl (встроенный) и external-proxy.conf.tpl (наш стенд) - deploy/livekit: конфиг с TURN, dynacast и лимитами комнат (§10.6) - deploy/backup.sh + restore.sh: sqlite .backup, tar, age-шифрование (§10.8) - deploy/update.sh: бэкап → pull/сборка → up → миграции → smoke (§9.3) - deploy/smoke.sh: проверки §10.9 (healthz/readyz/meta, порты, UFW, fail2ban, бэкап) - deploy/security-check.sh: аудит SSH, UFW, fail2ban, прав .env и ключей - deploy/ufw-docker.sh, nftables и fail2ban-конфиги (sshd, glchat-auth, recidive) - deploy/glchat-cli.sh: команды §10.7, systemd-юнит glchat.service - scripts/test-install.sh: прогон установщика в чистом Ubuntu 24.04 (systemd-контейнер) - Dockerfile: multi-stage (node → golang CGO → debian-slim), non-root, healthcheck - Makefile: lint/test/build/dev/generate/security/smoke и фиксация версий инструментов
This commit is contained in:
Executable
+83
@@ -0,0 +1,83 @@
|
||||
#!/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)"
|
||||
Reference in New Issue
Block a user