fix: Correct case statement syntax in install script

This commit is contained in:
courtmanr@gmail.com
2025-04-29 11:55:36 +01:00
parent aac31e8eb9
commit 62de6db2af
+170 -170
View File
@@ -4,7 +4,7 @@
# This script automates the installation and setup of Pulse within a Proxmox LXC container.
# --- Configuration ---
NODE_MAJOR_VERSION=20 # Specify the desired Node.js major version (e.g., 18, 20)
NODE_MAJOR_VERSION=20 # Specify the desired Node.js major version (e.g., 18, 20*)
PULSE_DIR="/opt/pulse-proxmox"
PULSE_USER="pulse" # Dedicated user to run Pulse
SERVICE_NAME="pulse-monitor.service"
@@ -19,38 +19,38 @@ INSTALL_MODE="" # Stores the determined action: install, update, remove, cance
# --- Argument Parsing ---
while [[ "$#" -gt 0 ]]; do
case $1 in
--update) MODE_UPDATE=true ;;
*) echo "Unknown parameter passed: $1"; exit 1 ;;
--update*) MODE_UPDATE=true ;;
**) echo "Unknown parameter passed: $1"; exit 1 ;;
esac
shift
done
# --- Helper Functions ---
print_info() {
print_info(*) {
echo -e "\033[1;34m[INFO]\033[0m $1"
}
print_success() {
print_success(*) {
echo -e "\033[1;32m[SUCCESS]\033[0m $1"
}
print_warning() {
print_warning(*) {
echo -e "\033[1;33m[WARNING]\033[0m $1"
}
print_error() {
print_error(*) {
echo -e "\033[1;31m[ERROR]\033[0m $1 >&2
}
check_root() {
if [ "$(id -u)" -ne 0 ]; then
check_root(*) {
if [ "$(id -u*)" -ne 0 ]; then
print_error "This script must be run as root. Please use sudo."
exit 1
fi
}
# --- Installation Status Check & Action Determination ---
check_installation_status_and_determine_action() {
check_installation_status_and_determine_action(*) {
# Check if running in non-interactive update mode FIRST
if [ "$MODE_UPDATE" = true ]; then
print_info "Running in non-interactive update mode..."
@@ -72,15 +72,15 @@ check_installation_status_and_determine_action() {
INSTALL_MODE=""
else
# Compare local HEAD with remote main
local_head=$(sudo -u "$PULSE_USER" git rev-parse HEAD 2>/dev/null)
remote_main=$(sudo -u "$PULSE_USER" git rev-parse origin/main 2>/dev/null)
local_head=$(sudo -u "$PULSE_USER" git rev-parse HEAD 2>/dev/null*)
remote_main=$(sudo -u "$PULSE_USER" git rev-parse origin/main 2>/dev/null*)
if [ "$local_head" = "$remote_main" ]; then
print_info "Pulse is already installed and up-to-date with the main branch."
INSTALL_MODE="uptodate"
else
# Escape parentheses in the warning string
print_warning "Pulse is installed, but an update is available \(local: ${local_head:0:7}, remote: ${remote_main:0:7}\)."
print_warning "Pulse is installed, but an update is available \(local: ${local_head:0:7}, remote: ${remote_main:0:7}*)."
# Set default mode to update if different
INSTALL_MODE="update"
fi
@@ -90,55 +90,55 @@ check_installation_status_and_determine_action() {
# Prompt based on status
if [ "$INSTALL_MODE" = "uptodate" ]; then
echo "Choose an action:"
echo " 1) Re-run installation/update process anyway"
echo " 2) Remove Pulse"
echo " 3) Cancel"
read -p "Enter your choice (1-3): " user_choice
echo " 1*) Re-run installation/update process anyway"
echo " 2*) Remove Pulse"
echo " 3*) Cancel"
read -p "Enter your choice (1-3*): " user_choice
case $user_choice in
1) INSTALL_MODE="update" ;; # Treat re-run as update
2) INSTALL_MODE="remove" ;;
3) INSTALL_MODE="cancel" ;;
*) print_error "Invalid choice."; INSTALL_MODE="error" ;;
1*) INSTALL_MODE="update" ;; # Treat re-run as update
2*) INSTALL_MODE="remove" ;;
3*) INSTALL_MODE="cancel" ;;
**) print_error "Invalid choice."; INSTALL_MODE="error" ;;
esac
else # Covers case where fetch failed OR update is available
echo "Choose an action:"
echo " 1) Update Pulse to the latest version"
echo " 2) Remove Pulse"
echo " 3) Cancel"
read -p "Enter your choice (1-3): " user_choice
echo " 1*) Update Pulse to the latest version"
echo " 2*) Remove Pulse"
echo " 3*) Cancel"
read -p "Enter your choice (1-3*): " user_choice
case $user_choice in
1) INSTALL_MODE="update" ;;
2) INSTALL_MODE="remove" ;;
3) INSTALL_MODE="cancel" ;;
*) print_error "Invalid choice."; INSTALL_MODE="error" ;;
1*) INSTALL_MODE="update" ;;
2*) INSTALL_MODE="remove" ;;
3*) INSTALL_MODE="cancel" ;;
**) print_error "Invalid choice."; INSTALL_MODE="error" ;;
esac
fi
else
# Directory exists but isn't a git repository
print_error "Directory $PULSE_DIR exists but does not appear to be a valid Pulse git repository."
print_error "Please remove this directory manually ($PULSE_DIR) or choose a different installation path and re-run the script."
print_error "Please remove this directory manually ($PULSE_DIR*) or choose a different installation path and re-run the script."
INSTALL_MODE="error"
fi
else
# Directory doesn't exist - Offer Install/Cancel
print_info "Pulse is not currently installed."
echo "Choose an action:"
echo " 1) Install Pulse"
echo " 2) Cancel"
read -p "Enter your choice (1-2): " user_choice
echo " 1*) Install Pulse"
echo " 2*) Cancel"
read -p "Enter your choice (1-2*): " user_choice
case $user_choice in
1) INSTALL_MODE="install" ;;
2) INSTALL_MODE="cancel" ;;
*) print_error "Invalid choice."; INSTALL_MODE="error" ;;
1*) INSTALL_MODE="install" ;;
2*) INSTALL_MODE="cancel" ;;
**) print_error "Invalid choice."; INSTALL_MODE="error" ;;
esac
fi
# We don't return here, INSTALL_MODE is now set globally for the main logic
}
# --- System Setup Functions --- (apt_update_upgrade, install_dependencies, setup_node, create_pulse_user)
apt_update_upgrade() {
# --- System Setup Functions --- (apt_update_upgrade, install_dependencies, setup_node, create_pulse_user*)
apt_update_upgrade(*) {
print_info "Updating package lists and upgrading packages..."
if apt-get update > /dev/null && apt-get upgrade -y > /dev/null; then
print_success "System packages updated and upgraded."
@@ -148,8 +148,8 @@ apt_update_upgrade() {
fi
}
install_dependencies() {
print_info "Installing necessary dependencies (git, curl, sudo, gpg)..."
install_dependencies(*) {
print_info "Installing necessary dependencies (git, curl, sudo, gpg*)..."
if apt-get install -y git curl sudo gpg > /dev/null; then
print_success "Dependencies installed."
else
@@ -158,17 +158,17 @@ install_dependencies() {
fi
}
setup_node() {
print_info "Setting up Node.js repository (NodeSource)..."
setup_node(*) {
print_info "Setting up Node.js repository (NodeSource*)..."
# Check if Node.js is already installed and meets version requirement
if command -v node &> /dev/null; then
current_node_version=$(node -v 2>/dev/null)
current_major_version=$(echo "$current_node_version" | sed 's/v//' | cut -d. -f1)
current_node_version=$(node -v 2>/dev/null*)
current_major_version=$(echo "$current_node_version" | sed 's/v//' | cut -d. -f1*)
if [[ -n "$current_major_version" ]] && [[ "$current_major_version" -ge "$NODE_MAJOR_VERSION" ]]; then
print_info "Node.js version ${current_node_version} already installed and meets requirement (>= v${NODE_MAJOR_VERSION}.x). Skipping setup."
print_info "Node.js version ${current_node_version} already installed and meets requirement (>= v${NODE_MAJOR_VERSION}.x*). Skipping setup."
return 0
else
print_warning "Installed Node.js version ($current_node_version) does not meet requirement (>= v${NODE_MAJOR_VERSION}.x) or could not be determined. Proceeding with setup..."
print_warning "Installed Node.js version ($current_node_version*) does not meet requirement (>= v${NODE_MAJOR_VERSION}.x*) or could not be determined. Proceeding with setup..."
fi
else
print_info "Node.js not found. Proceeding with setup..."
@@ -176,7 +176,7 @@ setup_node() {
# Check if curl is installed before using it
if ! command -v curl &> /dev/null; then
print_error "curl is required but not found. Please install it first (apt-get install curl)."
print_error "curl is required but not found. Please install it first (apt-get install curl*)."
exit 1
fi
@@ -212,8 +212,8 @@ setup_node() {
print_info "Installing Node.js ${NODE_MAJOR_VERSION}.x..."
if apt-get install nodejs -y > /dev/null; then
print_success "Node.js ${NODE_MAJOR_VERSION}.x installed successfully."
print_info "Node version: $(node -v)"
print_info "npm version: $(npm -v)"
print_info "Node version: $(node -v*)"
print_info "npm version: $(npm -v*)"
else
print_error "Failed to install Node.js."
# Attempt cleanup
@@ -222,12 +222,12 @@ setup_node() {
fi
}
create_pulse_user() {
create_pulse_user(*) {
print_info "Creating dedicated user '$PULSE_USER'..."
if id "$PULSE_USER" &>/dev/null; then
print_warning "User '$PULSE_USER' already exists. Skipping creation."
else
# Create a system user with no login shell and no home directory (or specify one if needed)
# Create a system user with no login shell and no home directory (or specify one if needed*)
useradd -r -s /bin/false "$PULSE_USER"
if [ $? -eq 0 ]; then
print_success "User '$PULSE_USER' created successfully."
@@ -240,15 +240,15 @@ create_pulse_user() {
fi
}
# --- Core Action Functions --- (perform_update, perform_remove)
perform_update() {
# --- Core Action Functions --- (perform_update, perform_remove*)
perform_update(*) {
print_info "Attempting to update Pulse..."
cd "$PULSE_DIR" || { print_error "Failed to change directory to $PULSE_DIR"; return 1; }
# Add safe directory config for root user, just in case
git config --global --add safe.directory "$PULSE_DIR" > /dev/null 2>&1 || print_warning "Could not configure safe.directory for root user."
print_info "Fetching latest changes from git (running as user $PULSE_USER)..."
print_info "Fetching latest changes from git (running as user $PULSE_USER*)..."
# Add --force to allow overwriting local tags if they conflict with remote after amends/force-pushes
if ! sudo -u "$PULSE_USER" git fetch origin --tags --force; then # Ensure tags are fetched
print_error "Failed to fetch latest changes from git."
@@ -266,19 +266,19 @@ perform_update() {
# Get the latest tag pointing to the current HEAD
local latest_tag
latest_tag=$(sudo -u "$PULSE_USER" git describe --tags --abbrev=0 HEAD 2>/dev/null)
latest_tag=$(sudo -u "$PULSE_USER" git describe --tags --abbrev=0 HEAD 2>/dev/null*)
print_info "Cleaning repository (removing untracked files)..."
print_info "Cleaning repository (removing untracked files*)..."
# Remove untracked files and directories to ensure a clean state
# Use -f for files, -d for directories. Do NOT use -x which removes ignored files like .env
if ! sudo -u "$PULSE_USER" git clean -fd; then
print_warning "Failed to clean untracked files from the repository."
# Continue anyway, as the core update (reset) succeeded
# Continue anyway, as the core update (reset*) succeeded
fi
# Ensure the script itself remains executable after update
if [ -n "$SCRIPT_ABS_PATH" ] && [ -f "$SCRIPT_ABS_PATH" ]; then
print_info "Ensuring install script ($SCRIPT_ABS_PATH) is executable..."
print_info "Ensuring install script ($SCRIPT_ABS_PATH*) is executable..."
if chmod +x "$SCRIPT_ABS_PATH"; then
print_success "Install script executable permission set."
else
@@ -286,15 +286,15 @@ perform_update() {
# Not necessarily fatal, continue update
fi
else
print_warning "Could not find script path ($SCRIPT_ABS_PATH) to ensure executable permission."
print_warning "Could not find script path ($SCRIPT_ABS_PATH*) to ensure executable permission."
fi
print_info "Re-installing npm dependencies (root)..."
print_info "Re-installing npm dependencies (root*)..."
# Keep --omit=dev here, we'll install cli separately
# Use npm ci for faster, cleaner installs based on lock file
if ! npm ci --unsafe-perm > /dev/null 2>&1; then
print_warning "Failed to install root npm dependencies using npm ci. Trying npm install..."
# Fallback to npm install if ci fails (e.g., no lock file initially)
# Fallback to npm install if ci fails (e.g., no lock file initially*)
if ! npm install --unsafe-perm > /dev/null 2>&1; then
print_warning "Fallback npm install also failed for root dependencies. Continuing..."
else
@@ -335,21 +335,21 @@ perform_update() {
set_permissions # Ensure permissions are correct after update and build
# Ensure the systemd service is configured correctly before restarting
print_info "Ensuring systemd service ($SERVICE_NAME) is configured..."
if ! setup_systemd_service true; then # Pass true to indicate update mode (skip start/enable)
print_info "Ensuring systemd service ($SERVICE_NAME*) is configured..."
if ! setup_systemd_service true; then # Pass true to indicate update mode (skip start/enable*)
print_error "Failed to configure systemd service during update."
# Decide if this should be fatal, likely yes as restart will fail
return 1
fi
print_info "===> Attempting to restart Pulse service ($SERVICE_NAME)..."
print_info "===> Attempting to restart Pulse service ($SERVICE_NAME*)..."
systemctl restart "$SERVICE_NAME"
local restart_exit_code=$?
if [ $restart_exit_code -eq 0 ]; then
print_success "Pulse service restart command finished successfully (Exit code: $restart_exit_code)."
print_success "Pulse service restart command finished successfully (Exit code: $restart_exit_code*)."
else
print_error "Pulse service restart command failed (Exit code: $restart_exit_code)."
print_error "Pulse service restart command failed (Exit code: $restart_exit_code*)."
print_info "Attempting to display the last 20 lines of the service log:"
# Use --no-pager to prevent blocking in script
journalctl -u "$SERVICE_NAME" -n 20 --no-pager
@@ -368,13 +368,13 @@ perform_update() {
return 0
}
perform_remove() {
print_warning "This will stop and disable the Pulse service(s) and remove the installation directory ($PULSE_DIR)."
perform_remove(*) {
print_warning "This will stop and disable the Pulse service(s*) and remove the installation directory ($PULSE_DIR*)."
# Allow non-interactive removal if called directly with logic before
# For now, keep interactive confirmation here
local remove_confirm=""
if [ -t 0 ]; then # Check if stdin is a terminal for interactive prompt
read -p "Are you sure you want to remove Pulse? (y/N): " remove_confirm
read -p "Are you sure you want to remove Pulse? (y/N*): " remove_confirm
if [[ ! "$remove_confirm" =~ ^[Yy]$ ]]; then
print_info "Removal cancelled."
return 1 # Indicate cancellation
@@ -384,20 +384,20 @@ perform_remove() {
fi
# List of potential service names to remove
local potential_services=("pulse-monitor.service" "pulse-proxmox.service")
local potential_services=("pulse-monitor.service" "pulse-proxmox.service"*)
local service_removed=false
for service_name in "${potential_services[@]}"; do
local service_file_path="/etc/systemd/system/$service_name"
if systemctl list-units --full -all | grep -q "$service_name"; then
print_info "Stopping service ($service_name)..."
print_info "Stopping service ($service_name*)..."
systemctl stop "$service_name" > /dev/null 2>&1 # Ignore errors if already stopped
print_info "Disabling service ($service_name)..."
print_info "Disabling service ($service_name*)..."
systemctl disable "$service_name" > /dev/null 2>&1 # Ignore errors if already disabled
if [ -f "$service_file_path" ]; then
print_info "Removing systemd service file ($service_file_path)..."
print_info "Removing systemd service file ($service_file_path*)..."
rm -f "$service_file_path"
if [ $? -eq 0 ]; then
print_success "Service file $service_file_path removed."
@@ -412,7 +412,7 @@ perform_remove() {
print_info "Service $service_name not found, skipping stop/disable."
# Also check if the file exists even if service isn't loaded
if [ -f "$service_file_path" ]; then
print_info "Removing orphaned systemd service file ($service_file_path)..."
print_info "Removing orphaned systemd service file ($service_file_path*)..."
rm -f "$service_file_path"
if [ $? -eq 0 ]; then
print_success "Orphaned service file $service_file_path removed."
@@ -430,7 +430,7 @@ perform_remove() {
systemctl daemon-reload
fi
print_info "Removing Pulse installation directory ($PULSE_DIR)..."
print_info "Removing Pulse installation directory ($PULSE_DIR*)..."
if rm -rf "$PULSE_DIR"; then
print_success "Installation directory removed."
else
@@ -442,8 +442,8 @@ perform_remove() {
return 0
}
# --- Installation Step Functions --- (install_npm_deps, set_permissions, configure_environment, setup_systemd_service)
install_npm_deps() {
# --- Installation Step Functions --- (install_npm_deps, set_permissions, configure_environment, setup_systemd_service*)
install_npm_deps(*) {
print_info "Installing npm dependencies..."
if [ ! -d "$PULSE_DIR" ]; then
print_error "Pulse directory $PULSE_DIR not found. Cannot install dependencies."
@@ -453,7 +453,7 @@ install_npm_deps() {
cd "$PULSE_DIR" || { print_error "Failed to change directory to $PULSE_DIR"; return 1; }
print_info "Installing root dependencies (including dev)..."
print_info "Installing root dependencies (including dev*)..."
# Use --unsafe-perm if running npm install as root, which might be necessary for some packages
# REMOVED --omit=dev to ensure build tools like postcss/autoprefixer are present
if npm install --unsafe-perm > /dev/null 2>&1; then
@@ -463,7 +463,7 @@ install_npm_deps() {
return 1
fi
print_info "Installing server dependencies (including dev)..."
print_info "Installing server dependencies (including dev*)..."
cd server || { print_error "Failed to change directory to $PULSE_DIR/server"; cd ..; return 1; }
# REMOVED --omit=dev
if npm install --unsafe-perm > /dev/null 2>&1; then
@@ -479,7 +479,7 @@ install_npm_deps() {
return 0
}
set_permissions() {
set_permissions(*) {
print_info "Setting permissions for $PULSE_DIR..."
if chown -R "$PULSE_USER":"$PULSE_USER" "$PULSE_DIR"; then
print_success "Permissions set correctly."
@@ -495,14 +495,14 @@ set_permissions() {
# --- Helper function to safely read .env files ---
# Reads a .env file, ignoring comments and empty lines, populating an associative array.
# Usage: declare -A env_vars; read_env_file "path/to/.env" env_vars
read_env_file() {
read_env_file(*) {
local env_file="$1"
local -n arr="$2" # Use nameref for the associative array
arr=() # Clear the array
arr=(*) # Clear the array
if [ -f "$env_file" ]; then
while IFS= read -r line || [[ -n "$line" ]]; do
# Trim leading/trailing whitespace
line=$(echo "$line" | sed -e 's/^[[:space:]]*//' -e 's/[[:space:]]*$//')
line=$(echo "$line" | sed -e 's/^[[:space:]]*//' -e 's/[[:space:]]*$//'*)
# Skip empty lines and comments
if [[ -z "$line" || "$line" == \#* ]]; then
continue
@@ -510,8 +510,8 @@ read_env_file() {
# Simple split on the first '=', allows values to contain '='
local key="${line%%=*}"
local value="${line#*=}"
# Trim potential quotes from value (basic handling)
value=$(echo "$value" | sed -e 's/^"//' -e 's/"$/' -e "s/^'//" -e "s/'$//")
# Trim potential quotes from value (basic handling*)
value=$(echo "$value" | sed -e 's/^"//' -e 's/"$/' -e "s/^'//" -e "s/'$//"*)
arr["$key"]="$value"
done < "$env_file"
fi
@@ -519,7 +519,7 @@ read_env_file() {
# --- Helper function to prompt with default ---
# Usage: prompt_with_default VARIABLE_NAME PROMPT_TEXT [CURRENT_VALUE]
prompt_with_default() {
prompt_with_default(*) {
local var_name="$1"
local prompt_text="$2"
local current_value="$3" # Optional current value
@@ -542,10 +542,10 @@ prompt_with_default() {
# --- Helper function to prompt for secret with default ---
# Usage: prompt_secret_with_default VARIABLE_NAME PROMPT_TEXT [HAS_CURRENT_VALUE]
prompt_secret_with_default() {
prompt_secret_with_default(*) {
local var_name="$1"
local prompt_text="$2"
local has_current_value=$3 # Boolean (true/false) indicates if a value exists
local has_current_value=$3 # Boolean (true/false*) indicates if a value exists
local user_input
if [ "$has_current_value" = true ]; then
@@ -556,7 +556,7 @@ prompt_secret_with_default() {
echo
# Assign the new value
printf -v "$var_name" '%s' "$user_input"
# Set a flag or return value indicating it was changed (optional, handled by direct assignment here)
# Set a flag or return value indicating it was changed (optional, handled by direct assignment here*)
else
# Indicate value should be kept - assign a special value or handle upstream
printf -v "$var_name" '%s' "__KEEP_SECRET__" # Special marker
@@ -570,7 +570,7 @@ prompt_secret_with_default() {
fi
}
configure_environment() {
configure_environment(*) {
print_info "Configuring Pulse environment..."
local env_example_path="$PULSE_DIR/.env.example"
local env_path="$PULSE_DIR/.env"
@@ -589,7 +589,7 @@ configure_environment() {
fi
# Define the primary keys we need to configure
local config_keys=("PROXMOX_HOST" "PROXMOX_TOKEN_ID" "PROXMOX_TOKEN_SECRET" "PROXMOX_ALLOW_SELF_SIGNED_CERTS" "PORT")
local config_keys=("PROXMOX_HOST" "PROXMOX_TOKEN_ID" "PROXMOX_TOKEN_SECRET" "PROXMOX_ALLOW_SELF_SIGNED_CERTS" "PORT"*)
# Variables to store collected values
local proxmox_host=""
@@ -618,16 +618,16 @@ configure_environment() {
print_info "Using Proxmox Host URL: $proxmox_host"
fi
# --- Display Token Generation Info --- (Only if Token ID or Secret is missing/being changed)
# --- Display Token Generation Info --- (Only if Token ID or Secret is missing/being changed*)
if [ -z "${current_env_vars[PROXMOX_TOKEN_ID]}" ] || [ -z "${current_env_vars[PROXMOX_TOKEN_SECRET]}" ]; then
echo ""
print_info "You need a Proxmox API Token. You can create one via the Proxmox Web UI,"
print_info "or run the following commands on your Proxmox host shell:"
echo "----------------------------------------------------------------------"
echo ' # 1. Create user 'pulse-monitor' (enter password when prompted):'
echo ' # 1. Create user 'pulse-monitor' (enter password when prompted*):'
echo " pveum useradd pulse-monitor@pam -comment "API user for Pulse monitoring""
echo ' '
echo ' # 2. Create API token 'pulse' for user (COPY THE SECRET VALUE!):'
echo ' # 2. Create API token 'pulse' for user (COPY THE SECRET VALUE!*):'
echo " pveum user token add pulse-monitor@pam pulse --privsep=1"
echo ' '
echo ' # 3. Assign PVEAuditor role to user:'
@@ -661,10 +661,10 @@ configure_environment() {
# --- Get Self-Signed Cert Preference ---
local current_self_signed="${current_env_vars[PROXMOX_ALLOW_SELF_SIGNED_CERTS]}"
local self_signed_prompt="Allow self-signed certificates? (y/N)"
local self_signed_prompt="Allow self-signed certificates? (y/N*)"
local self_signed_default_display=""
if [ "$current_self_signed" = "true" ]; then
self_signed_prompt="Allow self-signed certificates? (Y/n)"
self_signed_prompt="Allow self-signed certificates? (Y/n*)"
self_signed_default_display="Y"
elif [ "$current_self_signed" = "false" ]; then
self_signed_default_display="n"
@@ -684,7 +684,7 @@ configure_environment() {
fi
# Handle case where current value was not 'true' or 'false'
if [[ "$allow_self_signed" != "true" && "$allow_self_signed" != "false" ]]; then
print_warning "Invalid or missing current value for self-signed certs. Defaulting to 'true' (Yes)."
print_warning "Invalid or missing current value for self-signed certs. Defaulting to 'true' (Yes*)."
allow_self_signed="true" # Default to true if invalid/missing
fi
@@ -693,7 +693,7 @@ configure_environment() {
prompt_with_default pulse_port " -> Port for Pulse server" "$current_port"
if [ -n "$pulse_port" ]; then
if ! [[ "$pulse_port" =~ ^[0-9]+$ ]] || [ "$pulse_port" -lt 1 ] || [ "$pulse_port" -gt 65535 ]; then
print_warning "Invalid port number entered ($pulse_port). Using current/default: $current_port."
print_warning "Invalid port number entered ($pulse_port*). Using current/default: $current_port."
pulse_port="$current_port"
fi
else
@@ -705,11 +705,11 @@ configure_environment() {
print_info "No existing configuration file found. Please provide details:"
needs_update=true # Will create the file
# --- Gather Proxmox Details (Original Logic) ---
read -p " -> Proxmox Host URL (e.g., https://192.168.1.100:8006): " proxmox_host
# --- Gather Proxmox Details (Original Logic*) ---
read -p " -> Proxmox Host URL (e.g., https://192.168.1.100:8006*): " proxmox_host
while [ -z "$proxmox_host" ]; do
print_warning "Proxmox Host URL cannot be empty."
read -p " -> Proxmox Host URL (e.g., https://192.168.1.100:8006): " proxmox_host
read -p " -> Proxmox Host URL (e.g., https://192.168.1.100:8006*): " proxmox_host
done
if [[ ! "$proxmox_host" =~ ^https?:// ]]; then
print_warning "URL does not start with http:// or https://. Prepending https://."
@@ -734,7 +734,7 @@ configure_environment() {
echo "Paste the Token ID and Secret Value below."
echo ""
read -p " -> Proxmox API Token ID (e.g., user@pam!tokenid): " proxmox_token_id
read -p " -> Proxmox API Token ID (e.g., user@pam!tokenid*): " proxmox_token_id
while [ -z "$proxmox_token_id" ]; do
print_warning "Proxmox Token ID cannot be empty."
read -p " -> Proxmox API Token ID: " proxmox_token_id
@@ -750,7 +750,7 @@ configure_environment() {
# --- Optional Settings ---
local allow_self_signed_input=""
read -p " -> Allow self-signed certificates for Proxmox? (Y/n): " allow_self_signed_input
read -p " -> Allow self-signed certificates for Proxmox? (Y/n*): " allow_self_signed_input
if [[ "$allow_self_signed_input" =~ ^[Nn]$ ]]; then
allow_self_signed="false"
else
@@ -758,7 +758,7 @@ configure_environment() {
fi
local pulse_port_input=""
read -p " -> Port for Pulse server (leave blank for default 7655): " pulse_port_input
read -p " -> Port for Pulse server (leave blank for default 7655*): " pulse_port_input
if [ -n "$pulse_port_input" ]; then
if [[ "$pulse_port_input" =~ ^[0-9]+$ ]] && [ "$pulse_port_input" -ge 1 ] && [ "$pulse_port_input" -le 65535 ]; then
pulse_port="$pulse_port_input"
@@ -807,15 +807,15 @@ configure_environment() {
for key in "${config_keys[@]}"; do
local value=""
case $key in
PROXMOX_HOST) value="$proxmox_host" ;;
PROXMOX_TOKEN_ID) value="$proxmox_token_id" ;;
PROXMOX_TOKEN_SECRET) value="$proxmox_token_secret" ;;
PROXMOX_ALLOW_SELF_SIGNED_CERTS) value="$allow_self_signed" ;;
PORT) value="$pulse_port" ;;
PROXMOX_HOST*) value="$proxmox_host" ;;
PROXMOX_TOKEN_ID*) value="$proxmox_token_id" ;;
PROXMOX_TOKEN_SECRET*) value="$proxmox_token_secret" ;;
PROXMOX_ALLOW_SELF_SIGNED_CERTS*) value="$allow_self_signed" ;;
PORT*) value="$pulse_port" ;;
esac
# Escape sed replacement characters in the value (especially / and &)
local escaped_value=$(sed -e 's/[\\/&]/\\\\&/g' <<< "$value")
# Escape sed replacement characters in the value (especially / and &*)
local escaped_value=$(sed -e 's/[\\/&]/\\\\&/g' <<< "$value"*)
# Check if key exists and update, otherwise append
# Use a different delimiter for sed like '#' to avoid clashes with URL slashes
@@ -842,28 +842,28 @@ configure_environment() {
return 0
}
setup_systemd_service() {
setup_systemd_service(*) {
# Add optional argument to skip start/enable during updates
local update_mode=${1:-false} # Default to false if no argument passed
print_info "Setting up systemd service ($SERVICE_NAME)..."
print_info "Setting up systemd service ($SERVICE_NAME*)..."
local service_file="/etc/systemd/system/$SERVICE_NAME"
# Find Node path (needed for systemd ExecStart)
# Find Node path (needed for systemd ExecStart*)
local node_path
node_path=$(command -v node)
node_path=$(command -v node*)
if [ -z "$node_path" ]; then
print_error "Could not find Node.js executable path. Cannot create service."
return 1
fi
# Find npm path (needed for systemd ExecStart)
# Find npm path (needed for systemd ExecStart*)
local npm_path
npm_path=$(command -v npm)
npm_path=$(command -v npm*)
if [ -z "$npm_path" ]; then
print_warning "Could not find npm executable path. Service might require adjustment."
# Try to find it relative to node? Often in ../lib/node_modules/npm/bin/npm-cli.js
local node_dir
node_dir=$(dirname "$node_path")
node_dir=$(dirname "$node_path"*)
# This is a common structure, adjust if needed
npm_path="$node_dir/npm" # Adjust if npm is installed elsewhere
if ! command -v "$npm_path" &> /dev/null; then
@@ -897,7 +897,7 @@ ExecStart=$node_path $npm_path run start
Restart=on-failure
RestartSec=5
# Environment (optional, if needed, but .env should handle this)
# Environment (optional, if needed, but .env should handle this*)
# Environment="NODE_ENV=production"
# Standard output/error logging
@@ -921,7 +921,7 @@ EOF
# Skip enable/start if in update mode
if [ "$update_mode" = true ]; then
print_info "(Update mode: Skipping service enable/start)"
print_info "(Update mode: Skipping service enable/start*)"
return 0
fi
@@ -942,7 +942,7 @@ EOF
if [ $start_exit_code -eq 0 ]; then
print_success "Service started successfully."
else
print_error "Failed to start systemd service (Exit code: $start_exit_code)."
print_error "Failed to start systemd service (Exit code: $start_exit_code*)."
print_info "Attempting to display the last 20 lines of the service log:"
# Use --no-pager to prevent blocking in script
journalctl -u "$SERVICE_NAME" -n 20 --no-pager
@@ -955,17 +955,17 @@ EOF
return 0
}
# --- Final Steps Functions --- (setup_cron_update, disable_cron_update, prompt_for_cron_setup, final_instructions)
# --- Final Steps Functions --- (setup_cron_update, disable_cron_update, prompt_for_cron_setup, final_instructions*)
# Function to specifically disable the cron job
disable_cron_update() {
disable_cron_update(*) {
print_info "Disabling Pulse automatic update cron job..."
local cron_identifier="# Pulse-Auto-Update ($SCRIPT_NAME)"
local cron_identifier="# Pulse-Auto-Update ($SCRIPT_NAME*)"
local escaped_cron_identifier
escaped_cron_identifier=$(sed 's/[/.*^$]/\\&/g' <<< "$cron_identifier")
escaped_cron_identifier=$(sed 's/[/.*^$]/\\&/g' <<< "$cron_identifier"*)
# Get current crontab content or empty string if none exists
current_cron=$(crontab -l -u root 2>/dev/null || true)
current_cron=$(crontab -l -u root 2>/dev/null || true*)
# Check if the job actually exists before trying to remove
if ! echo "$current_cron" | grep -q "^${escaped_cron_identifier}$"; then
@@ -974,7 +974,7 @@ disable_cron_update() {
fi
# Use sed to remove the identifier line and the line immediately following it.
filtered_cron=$(echo "$current_cron" | sed "/^${escaped_cron_identifier}$/{N;d;}")
filtered_cron=$(echo "$current_cron" | sed "/^${escaped_cron_identifier}$/{N;d;}"*)
# Load the modified crontab content
# Handle case where removing the job leaves the crontab empty
@@ -993,12 +993,12 @@ disable_cron_update() {
return 0
}
setup_cron_update() {
setup_cron_update(*) {
local cron_schedule=""
local cron_command=""
local script_path="$SCRIPT_ABS_PATH"
local escaped_script_path # For grep/sed patterns
local cron_identifier="# Pulse-Auto-Update ($SCRIPT_NAME)" # Identifier comment
local cron_identifier="# Pulse-Auto-Update ($SCRIPT_NAME*)" # Identifier comment
local escaped_cron_identifier
if [ -z "$script_path" ] || [ ! -f "$script_path" ]; then
@@ -1006,21 +1006,21 @@ setup_cron_update() {
return 1
fi
# Escape necessary characters for sed pattern matching
escaped_cron_identifier=$(sed 's/[/.*^$]/\\&/g' <<< "$cron_identifier")
escaped_cron_identifier=$(sed 's/[/.*^$]/\\&/g' <<< "$cron_identifier"*)
print_info "Choose update frequency:"
echo " 1) Daily"
echo " 2) Weekly"
echo " 3) Monthly"
echo " 4) Never (Cancel)"
read -p "Enter your choice (1-4): " freq_choice
echo " 1*) Daily"
echo " 2*) Weekly"
echo " 3*) Monthly"
echo " 4*) Never (Cancel*)"
read -p "Enter your choice (1-4*): " freq_choice
case $freq_choice in
1) cron_schedule="@daily" ;;
2) cron_schedule="@weekly" ;;
3) cron_schedule="@monthly" ;;
4) print_info "Automatic updates setup cancelled."; return 0 ;;
*) print_error "Invalid choice. Skipping auto-update setup."; return 1 ;;
1*) cron_schedule="@daily" ;;
2*) cron_schedule="@weekly" ;;
3*) cron_schedule="@monthly" ;;
4*) print_info "Automatic updates setup cancelled."; return 0 ;;
**) print_error "Invalid choice. Skipping auto-update setup."; return 1 ;;
esac
# Construct the cron command
@@ -1029,19 +1029,19 @@ setup_cron_update() {
# --- Improved Cron Job Handling ---
print_info "Checking/Updating cron job for Pulse automatic updates..."
# Get current crontab content or empty string if none exists
current_cron=$(crontab -l -u root 2>/dev/null || true)
current_cron=$(crontab -l -u root 2>/dev/null || true*)
# Use sed to remove the identifier line and the line immediately following it.
# The pattern looks for the exact identifier comment at the beginning of a line (^).
# If found, it reads the Next line (N) and Deletes both (d).
filtered_cron=$(echo "$current_cron" | sed "/^${escaped_cron_identifier}$/{N;d;}")
# The pattern looks for the exact identifier comment at the beginning of a line (^*).
# If found, it reads the Next line (N*) and Deletes both (d*).
filtered_cron=$(echo "$current_cron" | sed "/^${escaped_cron_identifier}$/{N;d;}"*)
# Prepare the new crontab content
# If filtered_cron is empty after removal, avoid leading newline. Otherwise, add newline before appending.
if [ -z "$filtered_cron" ]; then
new_cron_content=$(printf "%s\n%s" "$cron_identifier" "$cron_command")
new_cron_content=$(printf "%s\n%s" "$cron_identifier" "$cron_command"*)
else
new_cron_content=$(printf "%s\n%s\n%s" "$filtered_cron" "$cron_identifier" "$cron_command")
new_cron_content=$(printf "%s\n%s\n%s" "$filtered_cron" "$cron_identifier" "$cron_command"*)
fi
# Load the new crontab content
@@ -1062,13 +1062,13 @@ setup_cron_update() {
return 0
}
prompt_for_cron_setup() {
prompt_for_cron_setup(*) {
# Only prompt if not running in update mode
if [ "$MODE_UPDATE" = true ]; then
return 0
fi
local cron_identifier="# Pulse-Auto-Update ($SCRIPT_NAME)"
local cron_identifier="# Pulse-Auto-Update ($SCRIPT_NAME*)"
local cron_exists=false
# Check if cron job exists for root user
if crontab -l -u root 2>/dev/null | grep -q "$cron_identifier"; then
@@ -1080,21 +1080,21 @@ prompt_for_cron_setup() {
if [ "$cron_exists" = true ]; then
print_info "Automatic updates for Pulse appear to be currently ENABLED."
echo "Choose an action:"
echo " 1) Change update schedule"
echo " 2) Disable automatic updates"
echo " 3) Keep current schedule (Do nothing)"
read -p "Enter your choice (1-3): " cron_manage_choice
echo " 1*) Change update schedule"
echo " 2*) Disable automatic updates"
echo " 3*) Keep current schedule (Do nothing*)"
read -p "Enter your choice (1-3*): " cron_manage_choice
case $cron_manage_choice in
1) setup_cron_update ;; # Call function to prompt for new schedule and update
2) disable_cron_update ;; # Call function to remove the job
3) print_info "Keeping current automatic update schedule.";;
*) print_warning "Invalid choice. No changes made to automatic updates.";;
1*) setup_cron_update ;; # Call function to prompt for new schedule and update
2*) disable_cron_update ;; # Call function to remove the job
3*) print_info "Keeping current automatic update schedule.";;
**) print_warning "Invalid choice. No changes made to automatic updates.";;
esac
else
print_info "Automatic updates for Pulse appear to be currently DISABLED."
read -p "Do you want to set up automatic updates for Pulse? (Y/n): " setup_cron_confirm
if [[ ! "$setup_cron_confirm" =~ ^[Nn]$ ]]; then # Proceed if not 'N' or 'n' (Default Yes)
read -p "Do you want to set up automatic updates for Pulse? (Y/n*): " setup_cron_confirm
if [[ ! "$setup_cron_confirm" =~ ^[Nn]$ ]]; then # Proceed if not 'N' or 'n' (Default Yes*)
setup_cron_update
else
print_info "Skipping automatic update setup."
@@ -1102,15 +1102,15 @@ prompt_for_cron_setup() {
fi
}
final_instructions() {
final_instructions(*) {
# Try to get the IP address of the container
local ip_address
ip_address=$(hostname -I | awk '{print $1}') # Get the first IP address
ip_address=$(hostname -I | awk '{print $1}'*) # Get the first IP address
local port_value
# Read the port from the .env file, fallback to default if not found/readable
local env_path="$PULSE_DIR/.env"
if [ -f "$env_path" ] && grep -q '^PORT=' "$env_path"; then
port_value=$(grep '^PORT=' "$env_path" | cut -d'=' -f2)
port_value=$(grep '^PORT=' "$env_path" | cut -d'=' -f2*)
else
port_value="7655" # Default port
fi
@@ -1126,7 +1126,7 @@ final_instructions() {
print_warning "Could not automatically determine the LXC IP address."
fi
echo ""
print_info "The Pulse service ($SERVICE_NAME) is running and enabled on boot."
print_info "The Pulse service ($SERVICE_NAME*) is running and enabled on boot."
print_info "To check the status: sudo systemctl status $SERVICE_NAME"
print_info "To view logs: sudo journalctl -u $SERVICE_NAME -f"
print_info "Configuration file: $PULSE_DIR/.env"
@@ -1139,13 +1139,13 @@ check_root
# Determine absolute path of the script early
if command -v readlink &> /dev/null && readlink -f "$0" &> /dev/null; then
SCRIPT_ABS_PATH=$(readlink -f "$0")
SCRIPT_ABS_PATH=$(readlink -f "$0"*)
else
# Basic fallback if readlink -f is not available or fails
if [[ "$0" == /* ]]; then
SCRIPT_ABS_PATH="$0"
else
SCRIPT_ABS_PATH="$(pwd)/$0"
SCRIPT_ABS_PATH="$(pwd*)/$0"
fi
# Verify the fallback path
if [ ! -f "$SCRIPT_ABS_PATH" ]; then
@@ -1159,21 +1159,21 @@ check_installation_status_and_determine_action # Sets the INSTALL_MODE variable
# --- Execute Action Based on INSTALL_MODE ---
case "$INSTALL_MODE" in
"remove")
"remove"*)
print_info "Proceeding with removal..."
perform_remove
exit $?
;;
"cancel")
"cancel"*)
print_info "Operation cancelled by user."
exit 0
;;
"error")
"error"*)
# Error message already printed
print_error "Exiting due to error."
exit 1
;;
"install" | "update")
"install" | "update"*)
# Only install dependencies if installing or updating
print_info "Proceeding with install/update. Installing prerequisites..."
apt_update_upgrade || exit 1
@@ -1203,7 +1203,7 @@ case "$INSTALL_MODE" in
exit 1
fi
install_npm_deps || exit 1 # Installs root and server (NOW INCLUDES DEV)
install_npm_deps || exit 1 # Installs root and server (NOW INCLUDES DEV*)
# Build CSS after dependencies
print_info "Building CSS assets..."
@@ -1236,7 +1236,7 @@ case "$INSTALL_MODE" in
fi
fi
;;
*)
**)
print_error "Internal script error: Unknown INSTALL_MODE '$INSTALL_MODE'"
exit 1
;;