#!/bin/bash
# Бэкап серверов на rpi (вариант «малина забирает»): ssh к серверу → forced command отдаёт
# tar|zstd|age, зашифрованный ОТКРЫТЫМ ключом. Закрытого ключа на малине нет — расшифровать
# здесь ничего нельзя (ключ у владельца: Mac ~/.config/age/servers-backup.key + резервная копия).
#
#   servers-backup all      — ежедневный прогон (таймер): fr1 daily, ru1 daily, по воскресеньям fr1 weekly
#   servers-backup verify   — сверка sha256 всех хранимых архивов (порча на диске)
#
# Хранение: <host>/daily — 14 последних, <host>/weekly — 8, <host>/monthly — 12 (хардлинк
# первого daily за месяц, места не занимает).
set -uo pipefail
ROOT=/mnt/backup/servers
KEY=/etc/server-backup/id_ed25519
KH=/etc/server-backup/known_hosts
declare -A TARGET=([fr1]=rus@161.97.93.252 [ru1]=mik@85.198.109.86)
declare -A KEEP=([daily]=14 [weekly]=8 [monthly]=12)

log() { echo "$*"; }

pull() {   # pull <host> <profile>
  local host=$1 profile=$2 dir="$ROOT/$1/$2" ts f rc want got
  mkdir -p "$dir"
  ts=$(date +%F_%H%M); f="$dir/$host-$profile-$ts.tar.zst.age"
  ssh -i "$KEY" -o UserKnownHostsFile="$KH" -o StrictHostKeyChecking=yes -o BatchMode=yes \
      -o ConnectTimeout=30 -o ServerAliveInterval=30 -o ServerAliveCountMax=6 \
      "${TARGET[$host]}" "$profile" > "$f.part" 2> "$f.stderr"
  rc=$?
  want=$(grep '^SHA256 ' "$f.stderr" | tail -1 | cut -d' ' -f2)
  got=$(sha256sum "$f.part" | cut -d' ' -f1)
  if [ $rc -ne 0 ] || [ -z "$want" ] || [ "$want" != "$got" ] || [ ! -s "$f.part" ]; then
    log "FAIL $host/$profile: ssh rc=$rc, sha сервера=${want:-нет}, sha принятого=$got"
    sed 's/^/  stderr: /' "$f.stderr" | tail -20
    rm -f "$f.part" "$f.stderr"
    return 1
  fi
  mv "$f.part" "$f"
  echo "$got  $(basename "$f")" > "$f.sha256"
  grep -v '^SHA256 ' "$f.stderr" | tail -50 > "$f.log"; [ -s "$f.log" ] || rm -f "$f.log"
  rm -f "$f.stderr"
  log "OK   $host/$profile: $(du -h "$f" | cut -f1)  $(basename "$f")"
  # первый daily за месяц → monthly (хардлинк)
  if [ "$profile" = daily ] && ! ls "$ROOT/$host/monthly/$host-daily-$(date +%Y-%m)"-* >/dev/null 2>&1; then
    mkdir -p "$ROOT/$host/monthly"; ln "$f" "$f.sha256" "$ROOT/$host/monthly/"
  fi
}

prune() {
  local d keep
  for d in "$ROOT"/*/{daily,weekly,monthly}; do
    [ -d "$d" ] || continue
    keep=${KEEP[$(basename "$d")]}
    ls -1t "$d"/*.age 2>/dev/null | tail -n +$((keep + 1)) | while read -r old; do
      rm -f "$old" "$old.sha256" "$old.log"; log "prune $(basename "$old")"
    done
  done
}

verify() {
  local bad=0 n=0 s
  while read -r s; do
    n=$((n + 1))
    (cd "$(dirname "$s")" && sha256sum --quiet -c "$(basename "$s")") || { log "BAD $s"; bad=1; }
  done < <(find "$ROOT" -name '*.sha256')
  log "verify: проверено $n архивов, $([ $bad = 0 ] && echo 'всё цело' || echo 'ЕСТЬ ПОВРЕЖДЁННЫЕ')"
  return $bad
}

mountpoint -q /mnt/backup || { echo "/mnt/backup не смонтирован — пропуск"; exit 1; }
install -d -m 700 "$ROOT"
case "${1:-all}" in
  all)
    rc=0
    pull fr1 daily || rc=1
    pull ru1 daily || rc=1
    [ "$(date +%u)" = 7 ] && { pull fr1 weekly || rc=1; }
    prune
    [ "$(date +%u)" = 7 ] && { verify || rc=1; }
    exit $rc ;;
  verify) verify ;;
  pull) pull "$2" "$3" ;;
  *) echo "usage: servers-backup all|verify|pull <host> <profile>" >&2; exit 2 ;;
esac
