#!/usr/bin/env bash # # Sichert Datenbank und Nutzdaten nach backups/. # # Rotation: 7 Tages-, 4 Wochen-, 12 Monats-Backups. Ein flock verhindert, dass # sich zwei Läufe überholen. # # ./scripts/backup.sh set -euo pipefail cd "$(dirname "${BASH_SOURCE[0]}")/.." ZIEL="backups" mkdir -p "$ZIEL" exec 9>"$ZIEL/.lock" if ! flock -n 9; then echo "Ein Backup läuft bereits." >&2 exit 0 fi STEMPEL="$(date +%Y%m%d_%H%M%S)" DB_PASS="${DB_PASS:-finanzmanager}" echo "Sichere Datenbank …" docker compose exec -T db mariadb-dump \ -u finanzmanager -p"$DB_PASS" \ --single-transaction --routines --events \ finanzmanager > "$ZIEL/db_${STEMPEL}.sql" echo "Sichere Nutzdaten …" # data/mariadb bleibt außen vor – die Datenbank steckt bereits im Dump. tar czf "$ZIEL/daten_${STEMPEL}.tar.gz" \ --exclude='data/mariadb' \ data 2>/dev/null || true # ── Rotation ──────────────────────────────────────────────────────────────── # Behalten: die letzten 7 Tage vollständig, danach je ein Backup pro Woche # (4 Wochen) und pro Monat (12 Monate). python3 - "$ZIEL" <<'PY' import os, re, sys, time from datetime import datetime ziel = sys.argv[1] muster = re.compile(r'^(db|daten)_(\d{8})_(\d{6})\.(sql|tar\.gz)$') dateien = [] for name in os.listdir(ziel): treffer = muster.match(name) if treffer: stempel = datetime.strptime(treffer.group(2) + treffer.group(3), '%Y%m%d%H%M%S') dateien.append((stempel, name)) dateien.sort(reverse=True) jetzt = datetime.now() behalten, wochen, monate = set(), set(), set() for stempel, name in dateien: alter = (jetzt - stempel).days if alter <= 7: behalten.add(name) continue woche = stempel.isocalendar()[:2] monat = (stempel.year, stempel.month) if alter <= 35 and woche not in wochen: wochen.add(woche); behalten.add(name) elif alter <= 400 and monat not in monate: monate.add(monat); behalten.add(name) for _, name in dateien: if name not in behalten: os.remove(os.path.join(ziel, name)) print(f"Entfernt: {name}") PY echo "Backup abgeschlossen: $ZIEL/db_${STEMPEL}.sql"