diff --git a/misc/backup.func b/misc/backup.func new file mode 100644 index 0000000..bfefde3 --- /dev/null +++ b/misc/backup.func @@ -0,0 +1,139 @@ +# backup.func – Snapshot/Backup-Logik mit Rollback via trap + Rotation + flexible Pfaderkennung + +# Globale Variable (automatisch gesetzt) +SNAPSHOT_DIR="" + +create_snapshot() { + local app_name=$1 + local base_dir + + # Autodetect base_dir, bevorzugt /opt/, sonst Arbeitsverzeichnis oder APP_DIR gesetzt vom Script + base_dir="${APP_DIR:-/opt/$app_name}" + if [[ ! -d "$base_dir" ]]; then + msg_error "Cannot determine base directory for $app_name" + return 1 + fi + + local snapshot_base="${base_dir}-snapshot" + SNAPSHOT_DIR="${snapshot_base}-$(date +%F_%T | tr ':' '-')" + + msg_info "Creating snapshot for $app_name" + + mkdir -p "$SNAPSHOT_DIR" + cp -a "$base_dir" "$SNAPSHOT_DIR/base" || { + msg_error "Failed to backup base directory" + return 1 + } + + mkdir -p "$SNAPSHOT_DIR/systemd" + cp -a /etc/systemd/system/${app_name}-*.service "$SNAPSHOT_DIR/systemd/" 2>/dev/null || true + + [[ -f "/etc/default/$app_name" ]] && cp "/etc/default/$app_name" "$SNAPSHOT_DIR/" + [[ -f "$base_dir/${app_name}_version.txt" ]] && cp "$base_dir/${app_name}_version.txt" "$SNAPSHOT_DIR/" + + rotate_snapshots "$snapshot_base" + + msg_ok "Snapshot created at $SNAPSHOT_DIR" + return 0 +} + +rotate_snapshots() { + local snapshot_base=$1 + local snapshots + + # Sortiert nach Datum absteigend, behalte nur die 3 neuesten + mapfile -t snapshots < <(ls -dt ${snapshot_base}-* 2>/dev/null) + if ((${#snapshots[@]} > 3)); then + for ((i = 3; i < ${#snapshots[@]}; i++)); do + rm -rf "${snapshots[$i]}" + msg_info "Old snapshot removed: ${snapshots[$i]}" + done + fi +} + +rollback_snapshot() { + local app_name=$1 + local base_dir + + base_dir="${APP_DIR:-/opt/$app_name}" + if [[ -z "$SNAPSHOT_DIR" || ! -d "$SNAPSHOT_DIR" ]]; then + msg_error "No snapshot found. Cannot rollback." + return 1 + fi + + msg_info "Rolling back $app_name from snapshot" + + systemctl stop ${app_name}-* 2>/dev/null || true + + rm -rf "$base_dir" + cp -a "$SNAPSHOT_DIR/base" "$base_dir" || { + msg_error "Failed to restore base directory" + return 1 + } + + if [[ -d "$SNAPSHOT_DIR/systemd" ]]; then + cp "$SNAPSHOT_DIR/systemd/"*.service /etc/systemd/system/ 2>/dev/null || true + systemctl daemon-reload + fi + + [[ -f "$SNAPSHOT_DIR/$app_name" ]] && cp "$SNAPSHOT_DIR/$app_name" "/etc/default/$app_name" + [[ -f "$SNAPSHOT_DIR/${app_name}_version.txt" ]] && cp "$SNAPSHOT_DIR/${app_name}_version.txt" "$base_dir/" + + systemctl start ${app_name}-* 2>/dev/null || true + + msg_ok "Rollback for $app_name completed" + return 0 +} + +cleanup_snapshot() { + if [[ -n "$SNAPSHOT_DIR" && -d "$SNAPSHOT_DIR" ]]; then + rm -rf "$SNAPSHOT_DIR" + msg_ok "Cleaned up snapshot at $SNAPSHOT_DIR" + fi +} + +handle_failure() { + local app_name=$1 + local line=$2 + msg_error "Update failed at line $line. Rolling back..." + rollback_snapshot "$app_name" + exit 1 +} + +safe_run_update_script() { + local app_name="${APP:-paperless}" + + if ! create_snapshot "$app_name"; then + msg_error "Snapshot creation failed. Aborting update." + exit 1 + fi + + trap 'handle_failure "$app_name" $LINENO' ERR + set -eE + + update_script + + cleanup_snapshot +} + +wrap_update_script_with_snapshot() { + local original_func + original_func=$(declare -f update_script) || return 1 + + eval " + original_update_script() { + ${original_func#*\{} + } + update_script() { + local app_name=\"\${APP:-paperless}\" + if ! create_snapshot \"\$app_name\"; then + msg_error \"Snapshot creation failed. Aborting update.\" + exit 1 + fi + trap 'handle_failure \"\$app_name\" \$LINENO' ERR + set -eE + original_update_script + cleanup_snapshot + } + " +}