# 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 } " }