mirror of
https://github.com/Memo-2023/mana-monorepo.git
synced 2026-05-28 11:37:42 +02:00
feat(infra): Backup-Zweitkopie auf GPU-Box (Phase 1 Off-Site)
Some checks are pending
CD Mac Mini / Detect Changes (push) Waiting to run
CD Mac Mini / Deploy (push) Blocked by required conditions
CI / Detect Changes (push) Waiting to run
CI / Validate (push) Waiting to run
CI / Build mana-search (push) Blocked by required conditions
CI / Build mana-sync (push) Blocked by required conditions
CI / Build mana-api-gateway (push) Blocked by required conditions
CI / Build mana-crawler (push) Blocked by required conditions
Mirror to Forgejo / Push to Forgejo (push) Waiting to run
Some checks are pending
CD Mac Mini / Detect Changes (push) Waiting to run
CD Mac Mini / Deploy (push) Blocked by required conditions
CI / Detect Changes (push) Waiting to run
CI / Validate (push) Waiting to run
CI / Build mana-search (push) Blocked by required conditions
CI / Build mana-sync (push) Blocked by required conditions
CI / Build mana-api-gateway (push) Blocked by required conditions
CI / Build mana-crawler (push) Blocked by required conditions
Mirror to Forgejo / Push to Forgejo (push) Waiting to run
backup-databases.sh spiegelt nach erfolgreichem lokalem Dump die heutigen Dumps verschlüsselt (AES-256 via openssl) per scp auf die GPU-Box (LAN). Schließt die "alles auf einer Disk"-Lücke. - Non-blocking: Push-Fehler/Box-aus kippt NIE das lokale Backup, meldet nur via send_notification (ssh-Reachability-Probe vorweg). - Verschlüsselt mit Passfile /Users/mana/.config/mana-backup-offsite.pass (chmod 600); fehlt es, wird unverschlüsselt gespiegelt + laut gewarnt. - Zielpfad als Variable (OFFSITE_DIR) — beim Anschluss der externen SSD an die GPU nur diese eine Zeile umbiegen. - Bewusst ZWEITKOPIE, kein echtes Off-Site (gleicher Standort) — Cloud bleibt Phase 2. Deployed (Backup .bak-pre-offsite-20260525), bash -n grün, Skip-Pfad + scp-Transport verifiziert. Aktiviert sich automatisch, sobald mana@macmini auf der GPU-Box autorisiert ist (administrators_authorized_keys, Elevation nötig — manueller Einzelschritt). Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
This commit is contained in:
parent
4e26637464
commit
1d9a19d40f
1 changed files with 75 additions and 0 deletions
|
|
@ -34,6 +34,79 @@ LOG_FILE="/tmp/mana-backup.log"
|
|||
DATE=$(date +%Y-%m-%d)
|
||||
DAY_OF_WEEK=$(date +%u) # 1=Monday, 7=Sunday
|
||||
|
||||
# ─── Off-Site-/Zweitkopie auf die GPU-Box (LAN) ─────────────────────────
|
||||
# Spiegelt die heutigen Dumps verschlüsselt (AES-256 via openssl) auf die
|
||||
# GPU-Box. Das ist eine ZWEITKOPIE auf einer zweiten Maschine/Disk
|
||||
# (schützt gegen Mac-Mini-Disk-/Hardware-Ausfall) — KEIN echtes Off-Site
|
||||
# (gleicher Standort/Strom/LAN). Echtes Off-Site (Cloud) bleibt Phase 2.
|
||||
# Non-blocking: ein Push-Fehler kippt NIE das lokale Backup, nur Notify.
|
||||
OFFSITE_ENABLE="${OFFSITE_ENABLE:-1}"
|
||||
OFFSITE_HOST="${OFFSITE_HOST:-tills@192.168.178.11}"
|
||||
# Zielordner auf der GPU-Box. Beim Anschluss der externen SSD nur diese
|
||||
# Zeile auf den SSD-Pfad umbiegen (z.B. D:/ManaData/backups/postgres).
|
||||
OFFSITE_DIR="${OFFSITE_DIR:-C:/mana/backups/postgres}"
|
||||
OFFSITE_ENCRYPT="${OFFSITE_ENCRYPT:-1}"
|
||||
OFFSITE_PASSFILE="${OFFSITE_PASSFILE:-/Users/mana/.config/mana-backup-offsite.pass}"
|
||||
|
||||
# Spiegelt $BACKUP_DIR/daily/*_${DATE}.sql.gz auf die GPU-Box. Immer
|
||||
# `return 0` — das lokale Backup darf hieran nie scheitern.
|
||||
offsite_mirror() {
|
||||
[ "$OFFSITE_ENABLE" = "1" ] || { log "Off-Site: deaktiviert (OFFSITE_ENABLE=0)"; return 0; }
|
||||
|
||||
local today_files
|
||||
today_files=$(ls "$BACKUP_DIR/daily/"*"_${DATE}.sql.gz" 2>/dev/null || true)
|
||||
if [ -z "$today_files" ]; then
|
||||
log "Off-Site: keine heutigen Dumps gefunden — übersprungen"
|
||||
return 0
|
||||
fi
|
||||
|
||||
# Box evtl. aus / Key (noch) nicht autorisiert → sauber überspringen
|
||||
if ! ssh -o BatchMode=yes -o ConnectTimeout=8 "$OFFSITE_HOST" "exit" >/dev/null 2>&1; then
|
||||
log "Off-Site: GPU-Box ($OFFSITE_HOST) nicht erreichbar/autorisiert — übersprungen (lokales Backup ist sicher)"
|
||||
send_notification "ℹ️ Off-Site-Spiegelung übersprungen: GPU-Box nicht erreichbar" "default"
|
||||
return 0
|
||||
fi
|
||||
|
||||
# Verschlüsselung gewünscht, aber Passfile fehlt → lieber unverschlüsselt
|
||||
# spiegeln als gar nicht, aber laut warnen.
|
||||
local encrypt="$OFFSITE_ENCRYPT"
|
||||
if [ "$encrypt" = "1" ] && [ ! -f "$OFFSITE_PASSFILE" ]; then
|
||||
log "Off-Site: WARNUNG — Passfile $OFFSITE_PASSFILE fehlt, spiegele UNVERSCHLÜSSELT"
|
||||
send_notification "⚠️ Off-Site-Passfile fehlt — Spiegelung unverschlüsselt" "high"
|
||||
encrypt=0
|
||||
fi
|
||||
|
||||
local staging="" pushed=0 failed=0 f base
|
||||
[ "$encrypt" = "1" ] && staging=$(mktemp -d)
|
||||
|
||||
for f in $today_files; do
|
||||
base=$(basename "$f")
|
||||
if [ "$encrypt" = "1" ]; then
|
||||
if openssl enc -aes-256-cbc -salt -pbkdf2 -in "$f" -out "$staging/$base.enc" -pass "file:$OFFSITE_PASSFILE" 2>/dev/null \
|
||||
&& scp -q -o BatchMode=yes -o ConnectTimeout=30 "$staging/$base.enc" "$OFFSITE_HOST:$OFFSITE_DIR/$base.enc" >/dev/null 2>&1; then
|
||||
pushed=$((pushed + 1))
|
||||
else
|
||||
failed=$((failed + 1))
|
||||
fi
|
||||
else
|
||||
if scp -q -o BatchMode=yes -o ConnectTimeout=30 "$f" "$OFFSITE_HOST:$OFFSITE_DIR/$base" >/dev/null 2>&1; then
|
||||
pushed=$((pushed + 1))
|
||||
else
|
||||
failed=$((failed + 1))
|
||||
fi
|
||||
fi
|
||||
done
|
||||
[ -n "$staging" ] && rm -rf "$staging"
|
||||
|
||||
if [ "$failed" -eq 0 ]; then
|
||||
log "Off-Site: $pushed Dump(s) auf GPU-Box gespiegelt ($OFFSITE_DIR, encrypt=$encrypt)"
|
||||
else
|
||||
log "Off-Site: $pushed ok, $failed FEHLGESCHLAGEN beim Push auf GPU-Box"
|
||||
send_notification "⚠️ Off-Site-Spiegelung: $failed Dump(s) nicht auf GPU-Box gepusht" "high"
|
||||
fi
|
||||
return 0
|
||||
}
|
||||
|
||||
# .env.macmini ist im DOTENV-Format (Werte enthalten Spaces, BEGIN/END-
|
||||
# Marker etc.) — kann nicht via `source` in bash geladen werden. Wir
|
||||
# brauchen aus diesem File auch nichts; Telegram-Tokens kommen aus
|
||||
|
|
@ -166,6 +239,8 @@ if [ -n "$FAILED_DBS" ]; then
|
|||
exit 1
|
||||
else
|
||||
log "All backups successful!"
|
||||
# Zweitkopie auf die GPU-Box spiegeln (non-blocking)
|
||||
offsite_mirror
|
||||
# Only send notification on Sundays (weekly summary)
|
||||
if [ "$DAY_OF_WEEK" -eq 7 ]; then
|
||||
send_notification "💾 <b>Weekly Backup Complete</b>\n\n$BACKUP_COUNT databases backed up\nTotal size: $TOTAL_SIZE"
|
||||
|
|
|
|||
Loading…
Add table
Add a link
Reference in a new issue