From 05002d72c3f29ca1efad052d6fdec076ffa63c11 Mon Sep 17 00:00:00 2001 From: "courtmanr@gmail.com" Date: Sun, 20 Apr 2025 21:25:27 +0100 Subject: [PATCH] fix: Update repository URL in script and README --- README.md | 41 ++++ scripts/install-pulse.sh | 418 +++++++++++++++++++++++++++++++++++++++ 2 files changed, 459 insertions(+) create mode 100644 scripts/install-pulse.sh diff --git a/README.md b/README.md index 33ba4a16e..72ce7149a 100644 --- a/README.md +++ b/README.md @@ -15,6 +15,7 @@ A lightweight monitoring application for Proxmox VE that displays real-time stat - [Running the Application](#-running-the-application) - [Running the Application (Node.js)](#️-running-the-application-nodejs) - [Running with Docker Compose](#-running-with-docker-compose) +- [Running with LXC Installation Script](#-running-with-lxc-installation-script) - [Features](#-features) - [System Requirements](#-system-requirements) - [Contributing](#-contributing) @@ -152,6 +153,46 @@ docker compose down *Note: If you modify the `server/.env` file after the container is already running, you may need to restart the container for the changes to take effect. You can do this by running `docker compose down` followed by `docker compose up -d`, or by using `docker compose up -d --force-recreate`.* +## 🚀 Running with LXC Installation Script (Recommended for Proxmox) + +For users running Proxmox VE, a convenient installation script is provided to set up Pulse inside an LXC container (Debian/Ubuntu based). This script automates dependency installation, configuration, and setting up a systemd service. + +**Prerequisites:** +- A running Proxmox VE environment. +- A Debian or Ubuntu based LXC container created in Proxmox. +- Network connectivity from the LXC to your Proxmox server. +- You will need your Proxmox API Token details ([See Creating a Proxmox API Token](#creating-a-proxmox-api-token)). + +**Steps:** + +1. **Access LXC Console:** Log in to the console of your newly created LXC container (e.g., via the Proxmox web UI or SSH). + +2. **Download and Run the Script:** Execute the following command in the LXC console. This downloads the script and runs it with `sudo`: + ```bash + bash -c "$(wget -qLO - https://raw.githubusercontent.com/rcourtman/Pulse/main/scripts/install-pulse.sh)" -- + ``` + * Alternatively, you can download it first and then run it: + ```bash + wget https://raw.githubusercontent.com/rcourtman/Pulse/main/scripts/install-pulse.sh + chmod +x install-pulse.sh + sudo ./install-pulse.sh + ``` + +3. **Follow Prompts:** The script will guide you through the installation process: + * It will update the container and install necessary packages (`git`, `curl`, `nodejs`, `npm`). + * It will ask for your Proxmox Host URL, API Token ID, and API Token Secret. + * It will ask about allowing self-signed certificates and optionally setting a custom port. + * It will configure Pulse and set it up as a `systemd` service (`pulse-proxmox.service`) to run automatically. + +4. **Access Pulse:** Once the script finishes, it will display the URL (using the LXC's IP address) where you can access the Pulse dashboard (e.g., `http://:7655`). + +**Managing the Service:** + +- **Check Status:** `sudo systemctl status pulse-proxmox.service` +- **Stop Service:** `sudo systemctl stop pulse-proxmox.service` +- **Start Service:** `sudo systemctl start pulse-proxmox.service` +- **View Logs:** `sudo journalctl -u pulse-proxmox.service -f` + ## ✨ Features - Lightweight monitoring for Proxmox VE nodes. diff --git a/scripts/install-pulse.sh b/scripts/install-pulse.sh new file mode 100644 index 000000000..2b20bc00b --- /dev/null +++ b/scripts/install-pulse.sh @@ -0,0 +1,418 @@ +#!/bin/bash + +# Pulse for Proxmox VE LXC Installation Script +# 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) +PULSE_DIR="/opt/pulse-proxmox" +PULSE_USER="pulse" # Dedicated user to run Pulse +SERVICE_NAME="pulse-proxmox.service" + +# --- Helper Functions --- +print_info() { + echo -e "\033[1;34m[INFO]\033[0m $1" +} + +print_success() { + echo -e "\033[1;32m[SUCCESS]\033[0m $1" +} + +print_warning() { + echo -e "\033[1;33m[WARNING]\033[0m $1" +} + +print_error() { + echo -e "\033[1;31m[ERROR]\033[0m $1" >&2 +} + +check_root() { + if [ "$(id -u)" -ne 0 ]; then + print_error "This script must be run as root. Please use sudo." + exit 1 + fi +} + +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." + else + print_error "Failed to update/upgrade system packages." + exit 1 + fi +} + +install_dependencies() { + print_info "Installing necessary dependencies (git, curl, sudo)..." + if apt-get install -y git curl sudo > /dev/null; then + print_success "Dependencies installed." + else + print_error "Failed to install dependencies." + exit 1 + fi +} + +setup_node() { + print_info "Setting up Node.js repository (NodeSource)..." + # Check if Node.js is already installed and meets version requirement (optional, for robustness) + # node_version=$(node -v 2>/dev/null | sed 's/v//' | cut -d. -f1) + # if [[ "$node_version" -ge "$NODE_MAJOR_VERSION" ]]; then + # print_info "Node.js version ${node_version} already installed and meets requirement (>=${NODE_MAJOR_VERSION}). Skipping setup." + # return 0 + # fi + + if ! command -v curl &> /dev/null; then + print_error "curl is required but not found. Please install it first." + exit 1 + fi + + # Add NodeSource repository GPG key and setup script + KEYRING_DIR="/usr/share/keyrings" + KEYRING_FILE="$KEYRING_DIR/nodesource.gpg" + if [ ! -d "$KEYRING_DIR" ]; then + mkdir -p "$KEYRING_DIR" || { print_error "Failed to create $KEYRING_DIR"; exit 1; } + fi + curl -fsSL https://deb.nodesource.com/gpgkey/nodesource-repo.gpg.key | gpg --dearmor -o "$KEYRING_FILE" + if [ $? -ne 0 ]; then + print_error "Failed to download or process NodeSource GPG key." + rm -f "$KEYRING_FILE" # Clean up partial file + exit 1 + fi + + # Add the repository configuration + echo "deb [signed-by=$KEYRING_FILE] https://deb.nodesource.com/node_$NODE_MAJOR_VERSION.x nodistro main" | tee /etc/apt/sources.list.d/nodesource.list > /dev/null + if [ $? -ne 0 ]; then + print_error "Failed to add NodeSource repository to sources list." + exit 1 + fi + + print_info "Updating package list after adding NodeSource repository..." + if ! apt-get update > /dev/null; then + print_error "Failed to update package list after adding NodeSource repository." + # Attempt cleanup + rm -f /etc/apt/sources.list.d/nodesource.list "$KEYRING_FILE" + exit 1 + fi + + 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)" + else + print_error "Failed to install Node.js." + # Attempt cleanup + rm -f /etc/apt/sources.list.d/nodesource.list "$KEYRING_FILE" + exit 1 + fi +} + +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) + useradd -r -s /bin/false "$PULSE_USER" + if [ $? -eq 0 ]; then + print_success "User '$PULSE_USER' created successfully." + else + print_error "Failed to create user '$PULSE_USER'." + # Decide if this is fatal. Maybe allow script to continue if user exists? + # For now, exiting as it might indicate a problem. + exit 1 + fi + fi +} + +clone_repository() { + print_info "Cloning Pulse repository into $PULSE_DIR..." + if [ -d "$PULSE_DIR" ]; then + print_warning "Directory $PULSE_DIR already exists. Assuming repo is already cloned or manually placed." + # Optionally, add logic here to pull latest changes if desired + # cd "$PULSE_DIR" && git pull origin main + else + # Clone the main branch. Consider adding --depth 1 for faster clone if history isn't needed. + if git clone https://github.com/rcourtman/Pulse.git "$PULSE_DIR" > /dev/null 2>&1; then + print_success "Repository cloned successfully." + else + print_error "Failed to clone repository." + exit 1 + fi + fi +} + +install_npm_deps() { + print_info "Installing npm dependencies..." + if [ ! -d "$PULSE_DIR" ]; then + print_error "Pulse directory $PULSE_DIR not found. Cannot install dependencies." + exit 1 + fi + + cd "$PULSE_DIR" || { print_error "Failed to change directory to $PULSE_DIR"; exit 1; } + + print_info "Installing root dependencies..." + # Use --unsafe-perm if running npm install as root, which might be necessary for some packages + # Use --omit=dev to skip development dependencies + if npm install --omit=dev --unsafe-perm > /dev/null 2>&1; then + print_success "Root dependencies installed." + else + print_error "Failed to install root npm dependencies." + exit 1 + fi + + print_info "Installing server dependencies..." + cd server || { print_error "Failed to change directory to $PULSE_DIR/server"; exit 1; } + if npm install --omit=dev --unsafe-perm > /dev/null 2>&1; then + print_success "Server dependencies installed." + else + print_error "Failed to install server npm dependencies." + exit 1 + fi + + # Return to script execution directory or root, if needed + cd .. +} + +set_permissions() { + print_info "Setting permissions for $PULSE_DIR..." + if chown -R "$PULSE_USER":"$PULSE_USER" "$PULSE_DIR"; then + print_success "Permissions set correctly." + else + print_error "Failed to set permissions for $PULSE_DIR." + # This might not be fatal, but could cause runtime issues. Log warning? + print_warning "Check ownership and permissions for $PULSE_DIR." + fi +} + +configure_environment() { + print_info "Configuring Pulse environment..." + local env_example_path="$PULSE_DIR/server/.env.example" + local env_path="$PULSE_DIR/server/.env" + + if [ ! -f "$env_example_path" ]; then + print_error "Environment example file not found at $env_example_path. Cannot configure." + exit 1 + fi + + # Check if .env already exists + if [ -f "$env_path" ]; then + print_warning "Configuration file $env_path already exists." + read -p "Overwrite existing configuration? (y/N): " overwrite_confirm + if [[ ! "$overwrite_confirm" =~ ^[Yy]$ ]]; then + print_info "Skipping environment configuration." + return 0 # Exit the function successfully without configuring + fi + print_info "Proceeding to overwrite existing configuration..." + fi + + # --- Gather Proxmox Details --- + echo "Please provide your Proxmox connection details:" + 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 + done + + 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 (e.g., user@pam!tokenid): " proxmox_token_id + done + + # Use -s for silent input for the secret + read -sp " -> Proxmox API Token Secret: " proxmox_token_secret + echo # Add a newline after secret input + while [ -z "$proxmox_token_secret" ]; do + print_warning "Proxmox Token Secret cannot be empty." + read -sp " -> Proxmox API Token Secret: " proxmox_token_secret + echo + done + + # --- Optional Settings --- + read -p "Allow self-signed certificates for Proxmox? (y/N): " allow_self_signed + local self_signed_value="false" + if [[ "$allow_self_signed" =~ ^[Yy]$ ]]; then + self_signed_value="true" + fi + + read -p "Port for Pulse server (leave blank for default 7655): " pulse_port + local port_value="7655" + if [ -n "$pulse_port" ]; then + # Basic validation: check if it's a number (optional) + if [[ "$pulse_port" =~ ^[0-9]+$ ]] && [ "$pulse_port" -ge 1 ] && [ "$pulse_port" -le 65535 ]; then + port_value="$pulse_port" + else + print_warning "Invalid port number entered. Using default 7655." + fi + fi + + # --- Create .env file --- + print_info "Creating $env_path from example..." + if cp "$env_example_path" "$env_path"; then + # Use sed to replace placeholders. Use a different delimiter for sed if URLs contain slashes + sed -i "s|^PROXMOX_HOST=.*|PROXMOX_HOST=$proxmox_host|" "$env_path" + sed -i "s|^PROXMOX_TOKEN_ID=.*|PROXMOX_TOKEN_ID=$proxmox_token_id|" "$env_path" + sed -i "s|^PROXMOX_TOKEN_SECRET=.*|PROXMOX_TOKEN_SECRET=$proxmox_token_secret|" "$env_path" + sed -i "s|^PROXMOX_ALLOW_SELF_SIGNED_CERTS=.*|PROXMOX_ALLOW_SELF_SIGNED_CERTS=$self_signed_value|" "$env_path" + sed -i "s|^PORT=.*|PORT=$port_value|" "$env_path" + + # Set ownership + chown "$PULSE_USER":"$PULSE_USER" "$env_path" + chmod 600 "$env_path" # Restrict permissions for security + + print_success "Environment configured successfully in $env_path." + else + print_error "Failed to copy $env_example_path to $env_path." + exit 1 + fi +} + +setup_systemd_service() { + print_info "Setting up systemd service ($SERVICE_NAME)..." + local service_file="/etc/systemd/system/$SERVICE_NAME" + + # Find Node path (needed for systemd ExecStart) + local node_path + node_path=$(command -v node) + if [ -z "$node_path" ]; then + print_error "Could not find Node.js executable path. Cannot create service." + exit 1 + fi + # Find npm path (needed for systemd ExecStart) + local npm_path + 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") + # 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 + print_error "Could not reliably find npm executable path. Cannot create service." + exit 1 + fi + print_info "Found npm at $npm_path" + fi + + print_info "Creating service file at $service_file..." + # Use a HEREDOC to write the service file contents + cat << EOF > "$service_file" +[Unit] +Description=Pulse for Proxmox VE Monitoring Application +After=network.target + +[Service] +Type=simple +User=$PULSE_USER +Group=$PULSE_USER +WorkingDirectory=$PULSE_DIR + +# Start command using absolute paths found earlier +ExecStart=$node_path $npm_path run start + +# Restart policy +Restart=on-failure +RestartSec=5 + +# Environment (optional, if needed, but .env should handle this) +# Environment="NODE_ENV=production" + +# Standard output/error logging +StandardOutput=journal +StandardError=journal + +[Install] +WantedBy=multi-user.target +EOF + + if [ $? -ne 0 ]; then + print_error "Failed to create systemd service file $service_file." + exit 1 + fi + + # Set permissions for the service file + chmod 644 "$service_file" + + print_info "Reloading systemd daemon..." + systemctl daemon-reload + + print_info "Enabling $SERVICE_NAME to start on boot..." + if systemctl enable "$SERVICE_NAME" > /dev/null 2>&1; then + print_success "Service enabled successfully." + else + print_error "Failed to enable systemd service." + # Attempt cleanup of service file? + # rm -f "$service_file" + exit 1 + fi + + print_info "Starting $SERVICE_NAME..." + if systemctl start "$SERVICE_NAME"; then + print_success "Service started successfully." + else + print_error "Failed to start systemd service." + print_warning "Please check the service status using: systemctl status $SERVICE_NAME" + print_warning "And check the logs using: journalctl -u $SERVICE_NAME" + # Don't necessarily exit, user might be able to fix it. + fi +} + +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 + local port_value + # Read the port from the .env file, fallback to default if not found/readable + local env_path="$PULSE_DIR/server/.env" + if [ -f "$env_path" ] && grep -q '^PORT=' "$env_path"; then + port_value=$(grep '^PORT=' "$env_path" | cut -d'=' -f2) + else + port_value="7655" # Default port + fi + + echo "" + print_success "Pulse for Proxmox VE installation and setup complete!" + echo "-------------------------------------------------------------" + print_info "You should be able to access the Pulse dashboard at:" + if [ -n "$ip_address" ]; then + echo " http://$ip_address:$port_value" + else + echo " http://:$port_value" + 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 "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/server/.env" + echo "-------------------------------------------------------------" +} + +# --- Main Execution --- +check_root +apt_update_upgrade +install_dependencies +setup_node +create_pulse_user +clone_repository +install_npm_deps +set_permissions +configure_environment +setup_systemd_service +final_instructions + +print_info "Script finished." + +# --- Placeholder for next steps --- +# 1. Create dedicated user +# 2. Clone repository +# 3. Install npm dependencies +# 4. Configure .env +# 5. Setup systemd service +# 6. Final instructions + +echo "" +print_success "Script execution will continue..." # Placeholder \ No newline at end of file