#!/usr/bin/env bash set -Eeuo pipefail # proxmox-subscription-popup.sh # # Safe-ish Proxmox VE 9 subscription popup suppressor using sed. # # Usage: # sudo bash proxmox-subscription-popup.sh # sudo bash proxmox-subscription-popup.sh --status # sudo bash proxmox-subscription-popup.sh --undo # sudo bash proxmox-subscription-popup.sh --repair JS_FILE="/usr/share/javascript/proxmox-widget-toolkit/proxmoxlib.js" STATE_DIR="/root/.proxmox-subscription-popup" BACKUP_FILE="${STATE_DIR}/proxmoxlib.js.bak" DO_STATUS=0 DO_UNDO=0 DO_REPAIR=0 for arg in "$@"; do case "$arg" in --status) DO_STATUS=1 ;; --undo) DO_UNDO=1 ;; --repair) DO_REPAIR=1 ;; -h|--help) cat <<'EOF' Usage: sudo bash proxmox-subscription-popup.sh Apply popup suppression patch sudo bash proxmox-subscription-popup.sh --status Show status sudo bash proxmox-subscription-popup.sh --undo Restore original file sudo bash proxmox-subscription-popup.sh --repair Reinstall the owning package EOF exit 0 ;; *) echo "Unknown argument: $arg" >&2 exit 2 ;; esac done if [ "$(id -u)" -ne 0 ]; then echo "Please run as root." >&2 exit 1 fi mkdir -p "$STATE_DIR" if [[ -t 1 ]]; then C_RESET=$'\033[0m' C_BOLD=$'\033[1m' C_RED=$'\033[1;31m' C_GREEN=$'\033[1;32m' C_YELLOW=$'\033[1;33m' C_CYAN=$'\033[1;36m' C_MAGENTA=$'\033[1;35m' else C_RESET="" C_BOLD="" C_RED="" C_GREEN="" C_YELLOW="" C_CYAN="" C_MAGENTA="" fi say() { printf "%b\n" "$*"; } info() { say "${C_CYAN}▶${C_RESET} $*"; } ok() { say "${C_GREEN}✔${C_RESET} $*"; } warn() { say "${C_YELLOW}⚠${C_RESET} $*"; } fail() { say "${C_RED}✖${C_RESET} $*" >&2; } title() { say "${C_BOLD}${C_MAGENTA}$*${C_RESET}"; } trap 'fail "Script failed on line ${LINENO}"' ERR require_file() { [[ -f "$JS_FILE" ]] || { fail "Missing file: $JS_FILE" exit 1 } } detect_owner_pkg() { dpkg -S "$JS_FILE" 2>/dev/null | head -n1 | cut -d: -f1 } restart_proxy() { info "Restarting pveproxy..." systemctl restart pveproxy ok "pveproxy restarted" } show_hint() { say say "${C_BOLD}Hard refresh the browser afterwards:${C_RESET}" say " Mac: Cmd+Shift+R" say " Other: Ctrl+Shift+R" say } is_patched() { grep -Fq "orig_cmd();" "$JS_FILE" && grep -Fq "return;" "$JS_FILE" } has_expected_original() { grep -Fq "checked_command: function (orig_cmd) {" "$JS_FILE" && grep -Fq "url: '/nodes/localhost/subscription'" "$JS_FILE" && grep -Fq "res.data.status.toLowerCase() !== 'active'" "$JS_FILE" } status() { title "Proxmox subscription popup status" require_file if has_expected_original; then ok "Expected original code pattern found" else warn "Expected original code pattern NOT found" fi if is_patched; then ok "Patch appears to be installed" else warn "Patch does not appear to be installed" fi if [[ -f "$BACKUP_FILE" ]]; then ok "Backup exists at $BACKUP_FILE" else warn "No backup exists" fi } undo() { title "Restoring original file" [[ -f "$BACKUP_FILE" ]] || { fail "No backup file found at $BACKUP_FILE" exit 1 } cp -f "$BACKUP_FILE" "$JS_FILE" ok "Original file restored" restart_proxy show_hint } repair() { title "Repairing Proxmox UI" local owner owner="$(detect_owner_pkg)" [[ -n "$owner" ]] || { fail "Could not determine owning package" exit 1 } info "Reinstalling package: $owner" apt-get update apt-get install --reinstall -y "$owner" restart_proxy ok "Repair complete" show_hint } apply_patch() { title "Applying subscription popup suppression" require_file if is_patched; then ok "Patch already appears to be installed" show_hint return 0 fi if ! has_expected_original; then fail "Expected original code pattern not found." fail "Refusing to patch because the file does not look like the expected Proxmox VE 9 file." fail "Run with --repair first if the file is damaged." exit 1 fi if [[ ! -f "$BACKUP_FILE" ]]; then info "Creating backup at $BACKUP_FILE" cp -a "$JS_FILE" "$BACKUP_FILE" ok "Backup created" else warn "Backup already exists; leaving it untouched" fi info "Inserting short-circuit into checked_command()" sed -i '/checked_command: function (orig_cmd) {/a\ orig_cmd();\ return;' "$JS_FILE" if ! is_patched; then warn "Patch verification failed; restoring backup" cp -f "$BACKUP_FILE" "$JS_FILE" fail "Patch did not verify cleanly. Original restored." exit 1 fi ok "Patch successfully applied" info "Snippet after patch:" grep -A4 'checked_command: function (orig_cmd)' "$JS_FILE" || true restart_proxy ok "Subscription popup suppression is active" show_hint } case 1 in $DO_STATUS) status ;; $DO_UNDO) undo ;; $DO_REPAIR) repair ;; *) apply_patch ;; esac