#!/bin/sh # Точка входа контейнера backup: первый снимок (если снимков ещё нет) и расписание. # Расписание — supercronic по BACKUP_SCHEDULE (бэкап) и BACKUP_VERIFY_SCHEDULE (проверка). set -eu log() { printf '[backup %s] %s\n' "$(date '+%Y-%m-%d %H:%M:%S')" "$*"; } # Telegram-бот (#83): отвечает владельцу на /backups и /status. Стартует раньше проверки # пароля — при выключенных бэкапах скажет, что они выключены. Упал — перезапуск через минуту. if [ -n "${BACKUP_TELEGRAM_BOT_TOKEN:-}" ] && [ -n "${BACKUP_TELEGRAM_CHAT_ID:-}" ]; then ( while :; do fs-backup telegram bot || true; sleep 60; done ) & else log "Telegram не настроен (BACKUP_TELEGRAM_CHAT_ID/токен бота) — отчёты в Telegram выключены." fi # Без пароля бэкапы невозможны. Не падаем (иначе restart-петля и спам в логах) — ждём, # пока пароль появится в .env; healthcheck при этом показывает unhealthy. if [ -z "${BACKUP_PASSWORD:-}" ]; then log "BACKUP_PASSWORD не задан в .env — бэкапы ОТКЛЮЧЕНЫ." log "Настройка по шагам: deploy/backup/README.md. После правки .env: docker compose up -d backup" exec tail -f /dev/null fi mkdir -p /tmp/fs-backup CRONTAB=/tmp/fs-backup/crontab : > "$CRONTAB" if [ -n "${BACKUP_SCHEDULE:-}" ]; then echo "${BACKUP_SCHEDULE} fs-backup run --scheduled" >> "$CRONTAB" fi if [ -n "${BACKUP_VERIFY_SCHEDULE:-}" ]; then echo "${BACKUP_VERIFY_SCHEDULE} fs-backup verify" >> "$CRONTAB" fi if [ ! -s "$CRONTAB" ]; then log "Расписание отключено — только ручной запуск: docker compose exec backup fs-backup run" exec tail -f /dev/null fi if ! fs-backup has-snapshots; then # Новый сервер: app и backup стартуют разом, и БД может быть ещё без таблиц. /api/health # отвечает только после миграций и bootstrap — ждём его (#74). Не дождались — пробуем всё # равно: guard_empty не сохранит БД без таблиц. _app="${BACKUP_APP_HOST:-app}" _wait="${BACKUP_APP_WAIT_SECONDS:-600}" log "Снимков в локальном репозитории ещё нет — жду приложение (до ${_wait} с) и делаю первый бэкап." _waited=0 until wget -q -T 3 -O /dev/null "http://$_app:8000/api/health" 2>/dev/null; do if [ "$_waited" -ge "$_wait" ]; then log "Приложение не ответило за ${_wait} с — пробую бэкап без него." break fi sleep 10 _waited=$((_waited + 10)) done fs-backup run --scheduled || log "Первый бэкап не удался — следующая попытка по расписанию." fi log "Расписание (TZ=${TZ:-UTC}):" sed 's/^/[backup] /' "$CRONTAB" exec supercronic -passthrough-logs "$CRONTAB"