mirror of
https://github.com/rcourtman/Pulse.git
synced 2026-09-22 11:13:26 +00:00
4348a3db28
The server is hardcoded to use port 7655, not 3000
591 lines
16 KiB
Bash
Executable File
591 lines
16 KiB
Bash
Executable File
#!/bin/bash
|
|
|
|
# Pulse for Proxmox VE - Installer
|
|
# https://github.com/rcourtman/Pulse
|
|
|
|
NODE_MAJOR_VERSION=20
|
|
PULSE_DIR="/opt/pulse"
|
|
OLD_PULSE_DIR="/opt/pulse-proxmox"
|
|
PULSE_USER="pulse"
|
|
SERVICE_NAME="pulse-monitor.service"
|
|
REPO_BASE_URL="https://github.com/rcourtman/Pulse"
|
|
|
|
MODE_UPDATE=""
|
|
INSTALL_MODE=""
|
|
SPECIFIED_VERSION_TAG=""
|
|
TARGET_TAG=""
|
|
|
|
# Parse command line arguments
|
|
while [[ "$#" -gt 0 ]]; do
|
|
case $1 in
|
|
--update) MODE_UPDATE="true"; shift ;;
|
|
--version)
|
|
if [[ -n "$2" ]] && [[ "$2" != --* ]]; then
|
|
SPECIFIED_VERSION_TAG="$2"
|
|
shift 2
|
|
else
|
|
echo "Error: --version requires a tag name (e.g., v3.16.0)" >&2
|
|
exit 1
|
|
fi
|
|
;;
|
|
*) echo "Unknown parameter: $1"; exit 1 ;;
|
|
esac
|
|
done
|
|
|
|
# Color output functions
|
|
print_info() {
|
|
echo -e "\033[0;36m➜\033[0m $1"
|
|
}
|
|
|
|
print_success() {
|
|
echo -e "\033[0;32m✓\033[0m $1"
|
|
}
|
|
|
|
print_warning() {
|
|
echo -e "\033[0;33m⚠\033[0m $1"
|
|
}
|
|
|
|
print_error() {
|
|
echo -e "\033[0;31m✗\033[0m $1" >&2
|
|
}
|
|
|
|
# Check if running as root
|
|
check_root() {
|
|
if [ "$(id -u)" -ne 0 ]; then
|
|
print_error "This script must be run as root"
|
|
exit 1
|
|
fi
|
|
}
|
|
|
|
# Self-update check
|
|
self_update_check() {
|
|
# Skip if running from pipe or no curl available
|
|
if [ ! -t 0 ] || ! command -v curl &>/dev/null; then
|
|
return 0
|
|
fi
|
|
|
|
print_info "Checking for installer updates..."
|
|
|
|
local current_script="$0"
|
|
local temp_script="/tmp/install-pulse-new.sh"
|
|
local script_url="https://raw.githubusercontent.com/rcourtman/Pulse/main/scripts/install-pulse.sh"
|
|
|
|
# Download latest version
|
|
if curl -sL "$script_url" -o "$temp_script" 2>/dev/null; then
|
|
# Compare with current script
|
|
if ! diff -q "$current_script" "$temp_script" >/dev/null 2>&1; then
|
|
print_warning "A newer version of the installer is available"
|
|
read -p "Update installer and restart? [Y/n]: " update_confirm
|
|
|
|
if [[ ! "$update_confirm" =~ ^[Nn]$ ]]; then
|
|
print_info "Updating installer..."
|
|
cp "$temp_script" "$current_script"
|
|
chmod +x "$current_script"
|
|
rm -f "$temp_script"
|
|
|
|
print_success "Installer updated, restarting..."
|
|
exec "$current_script" "$@"
|
|
fi
|
|
else
|
|
print_success "Installer is up to date"
|
|
fi
|
|
rm -f "$temp_script"
|
|
fi
|
|
}
|
|
|
|
# Print welcome banner
|
|
print_welcome() {
|
|
echo ""
|
|
echo -e "\033[1;34mPulse for Proxmox VE Installer\033[0m"
|
|
echo "━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━"
|
|
echo ""
|
|
}
|
|
|
|
# Get latest release tag from GitHub
|
|
get_latest_release_tag() {
|
|
local api_url="https://api.github.com/repos/rcourtman/Pulse/releases/latest"
|
|
local latest_tag
|
|
|
|
latest_tag=$(curl -s "$api_url" | grep '"tag_name"' | sed -E 's/.*"tag_name": "([^"]+)".*/\1/')
|
|
|
|
if [ -z "$latest_tag" ] || [ "$latest_tag" = "null" ]; then
|
|
return 1
|
|
fi
|
|
|
|
echo "$latest_tag"
|
|
}
|
|
|
|
# Check if a release exists
|
|
check_release_exists() {
|
|
local tag=$1
|
|
local api_url="https://api.github.com/repos/rcourtman/Pulse/releases/tags/$tag"
|
|
|
|
curl -s --fail "$api_url" > /dev/null 2>&1
|
|
}
|
|
|
|
# Download and extract tarball
|
|
download_and_extract_tarball() {
|
|
local version=$1
|
|
local temp_dir="/tmp/pulse-install-$$"
|
|
local tarball_url="${REPO_BASE_URL}/releases/download/${version}/pulse-${version}.tar.gz"
|
|
|
|
print_info "Downloading Pulse ${version}..."
|
|
|
|
# Create temporary directory
|
|
mkdir -p "$temp_dir"
|
|
cd "$temp_dir" || exit 1
|
|
|
|
# Download tarball with progress
|
|
if ! curl -L --progress-bar -o "pulse.tar.gz" "$tarball_url"; then
|
|
print_error "Failed to download release"
|
|
rm -rf "$temp_dir"
|
|
return 1
|
|
fi
|
|
|
|
# Extract tarball
|
|
print_info "Extracting files..."
|
|
if ! tar -xzf "pulse.tar.gz"; then
|
|
print_error "Failed to extract files"
|
|
rm -rf "$temp_dir"
|
|
return 1
|
|
fi
|
|
|
|
# Find extracted directory
|
|
local extracted_dir=$(find . -maxdepth 1 -type d -name "pulse-*" | head -1)
|
|
if [ -z "$extracted_dir" ]; then
|
|
print_error "Invalid release archive"
|
|
rm -rf "$temp_dir"
|
|
return 1
|
|
fi
|
|
|
|
# Backup existing data if updating
|
|
if [ -d "$PULSE_DIR/data" ]; then
|
|
print_info "Backing up user data..."
|
|
cp -r "$PULSE_DIR/data" "$temp_dir/data-backup"
|
|
fi
|
|
|
|
# Remove old installation
|
|
if [ -d "$PULSE_DIR" ]; then
|
|
rm -rf "$PULSE_DIR"
|
|
fi
|
|
|
|
# Move to installation directory
|
|
mv "$extracted_dir" "$PULSE_DIR"
|
|
|
|
# Restore data if it existed
|
|
if [ -d "$temp_dir/data-backup" ]; then
|
|
print_info "Restoring user data..."
|
|
cp -r "$temp_dir/data-backup"/* "$PULSE_DIR/data/" 2>/dev/null || true
|
|
fi
|
|
|
|
# Cleanup
|
|
cd /
|
|
rm -rf "$temp_dir"
|
|
|
|
print_success "Installation files ready"
|
|
return 0
|
|
}
|
|
|
|
# Get current installed version
|
|
get_current_version() {
|
|
if [ -f "$PULSE_DIR/package.json" ]; then
|
|
grep '"version"' "$PULSE_DIR/package.json" | head -1 | sed -E 's/.*"version": "([^"]+)".*/\1/'
|
|
fi
|
|
}
|
|
|
|
# Check installation status
|
|
check_installation() {
|
|
local pulse_exists=false
|
|
local old_pulse_exists=false
|
|
local service_exists=false
|
|
|
|
[ -d "$PULSE_DIR" ] && pulse_exists=true
|
|
[ -d "$OLD_PULSE_DIR" ] && old_pulse_exists=true
|
|
systemctl list-unit-files | grep -q "^$SERVICE_NAME" && service_exists=true
|
|
|
|
if [ "$old_pulse_exists" = true ]; then
|
|
print_info "Old installation detected at $OLD_PULSE_DIR"
|
|
INSTALL_MODE="migrate"
|
|
elif [ "$pulse_exists" = true ] || [ "$service_exists" = true ]; then
|
|
local current_version=$(get_current_version)
|
|
if [ -n "$current_version" ]; then
|
|
print_info "Pulse v$current_version is installed"
|
|
else
|
|
print_info "Pulse is installed"
|
|
fi
|
|
|
|
if [ -n "$MODE_UPDATE" ]; then
|
|
INSTALL_MODE="update"
|
|
else
|
|
echo ""
|
|
echo "Choose an action:"
|
|
echo " 1) Update to latest version"
|
|
echo " 2) Remove Pulse"
|
|
echo " 3) Cancel"
|
|
echo ""
|
|
read -p "Your choice [1-3]: " choice
|
|
|
|
case $choice in
|
|
1) INSTALL_MODE="update" ;;
|
|
2) INSTALL_MODE="remove" ;;
|
|
3) print_info "Cancelled"; exit 0 ;;
|
|
*) print_error "Invalid choice"; exit 1 ;;
|
|
esac
|
|
fi
|
|
else
|
|
INSTALL_MODE="install"
|
|
print_info "Pulse is not installed"
|
|
fi
|
|
}
|
|
|
|
# Install system dependencies
|
|
install_dependencies() {
|
|
print_info "Checking system dependencies..."
|
|
|
|
# Update package lists quietly
|
|
apt-get update -qq || { print_error "Failed to update package lists"; exit 1; }
|
|
|
|
# Check and install missing dependencies
|
|
local deps="curl sudo gpg tar"
|
|
local missing_deps=""
|
|
|
|
for dep in $deps; do
|
|
if ! command -v $dep &> /dev/null; then
|
|
missing_deps="$missing_deps $dep"
|
|
fi
|
|
done
|
|
|
|
if [ -n "$missing_deps" ]; then
|
|
print_info "Installing:$missing_deps"
|
|
apt-get install -y -qq $missing_deps || { print_error "Failed to install dependencies"; exit 1; }
|
|
fi
|
|
|
|
print_success "System dependencies ready"
|
|
}
|
|
|
|
# Setup Node.js
|
|
setup_node() {
|
|
if command -v node &> /dev/null; then
|
|
local current_version=$(node -v | sed 's/v//' | cut -d. -f1)
|
|
if [ "$current_version" -ge "$NODE_MAJOR_VERSION" ]; then
|
|
print_success "Node.js $(node -v) is installed"
|
|
return 0
|
|
fi
|
|
fi
|
|
|
|
print_info "Installing Node.js v$NODE_MAJOR_VERSION..."
|
|
|
|
# Install NodeSource repository
|
|
mkdir -p /etc/apt/keyrings
|
|
curl -fsSL https://deb.nodesource.com/gpgkey/nodesource-repo.gpg.key | gpg --dearmor -o /etc/apt/keyrings/nodesource.gpg
|
|
echo "deb [signed-by=/etc/apt/keyrings/nodesource.gpg] https://deb.nodesource.com/node_$NODE_MAJOR_VERSION.x nodistro main" > /etc/apt/sources.list.d/nodesource.list
|
|
|
|
apt-get update -qq || { print_error "Failed to update package lists"; exit 1; }
|
|
apt-get install -y -qq nodejs || { print_error "Failed to install Node.js"; exit 1; }
|
|
|
|
print_success "Node.js $(node -v) installed"
|
|
}
|
|
|
|
# Create pulse user
|
|
create_pulse_user() {
|
|
if id "$PULSE_USER" &>/dev/null; then
|
|
print_success "User '$PULSE_USER' exists"
|
|
else
|
|
print_info "Creating user '$PULSE_USER'..."
|
|
useradd -r -s /bin/false -d /nonexistent -U "$PULSE_USER" || {
|
|
print_error "Failed to create user"
|
|
exit 1
|
|
}
|
|
print_success "User created"
|
|
fi
|
|
}
|
|
|
|
# Set file permissions
|
|
set_permissions() {
|
|
print_info "Setting permissions..."
|
|
|
|
chown -R "$PULSE_USER:$PULSE_USER" "$PULSE_DIR"
|
|
chmod -R 755 "$PULSE_DIR"
|
|
|
|
# Ensure data directory exists
|
|
mkdir -p "$PULSE_DIR/data"
|
|
chown "$PULSE_USER:$PULSE_USER" "$PULSE_DIR/data"
|
|
chmod 755 "$PULSE_DIR/data"
|
|
}
|
|
|
|
# Configure environment
|
|
configure_environment() {
|
|
local env_file="$PULSE_DIR/.env"
|
|
if [ ! -f "$env_file" ]; then
|
|
cat > "$env_file" << EOF
|
|
NODE_ENV=production
|
|
PORT=7655
|
|
EOF
|
|
chown "$PULSE_USER:$PULSE_USER" "$env_file"
|
|
chmod 600 "$env_file"
|
|
fi
|
|
}
|
|
|
|
# Setup systemd service
|
|
setup_systemd_service() {
|
|
print_info "Configuring service..."
|
|
|
|
cat > "/etc/systemd/system/$SERVICE_NAME" << EOF
|
|
[Unit]
|
|
Description=Pulse Monitor for Proxmox VE
|
|
After=network.target
|
|
|
|
[Service]
|
|
Type=simple
|
|
User=$PULSE_USER
|
|
WorkingDirectory=$PULSE_DIR
|
|
ExecStart=/usr/bin/node $PULSE_DIR/server/index.js
|
|
Restart=always
|
|
RestartSec=10
|
|
StandardOutput=journal
|
|
StandardError=journal
|
|
SyslogIdentifier=pulse-monitor
|
|
Environment="NODE_ENV=production"
|
|
Environment="PORT=7655"
|
|
|
|
[Install]
|
|
WantedBy=multi-user.target
|
|
EOF
|
|
|
|
systemctl daemon-reload
|
|
systemctl enable "$SERVICE_NAME" &>/dev/null
|
|
systemctl start "$SERVICE_NAME"
|
|
|
|
# Wait a moment and check if service started
|
|
sleep 2
|
|
if systemctl is-active --quiet "$SERVICE_NAME"; then
|
|
print_success "Service started"
|
|
else
|
|
print_error "Service failed to start"
|
|
print_info "Check logs: journalctl -u $SERVICE_NAME -n 50"
|
|
exit 1
|
|
fi
|
|
}
|
|
|
|
# Perform installation
|
|
perform_install() {
|
|
print_info "Installing Pulse..."
|
|
echo ""
|
|
|
|
install_dependencies
|
|
setup_node
|
|
create_pulse_user
|
|
|
|
# Determine target version
|
|
if [ -n "$SPECIFIED_VERSION_TAG" ]; then
|
|
TARGET_TAG="$SPECIFIED_VERSION_TAG"
|
|
if ! check_release_exists "$TARGET_TAG"; then
|
|
print_error "Version $TARGET_TAG does not exist"
|
|
exit 1
|
|
fi
|
|
else
|
|
TARGET_TAG=$(get_latest_release_tag)
|
|
if [ -z "$TARGET_TAG" ]; then
|
|
print_error "Could not determine latest version"
|
|
exit 1
|
|
fi
|
|
fi
|
|
|
|
download_and_extract_tarball "$TARGET_TAG" || exit 1
|
|
|
|
# Fix Express version if needed
|
|
print_info "Checking dependencies..."
|
|
cd "$PULSE_DIR"
|
|
if grep -q '"express": "4.19.2"' package.json; then
|
|
local current_express=$(npm list express --depth=0 2>/dev/null | grep express@ | sed 's/.*express@//' || echo "")
|
|
if [[ "$current_express" != "4.19.2" ]]; then
|
|
print_warning "Fixing Express version mismatch..."
|
|
rm -rf node_modules
|
|
npm install --omit=dev || {
|
|
print_error "Failed to install dependencies"
|
|
exit 1
|
|
}
|
|
fi
|
|
fi
|
|
|
|
set_permissions
|
|
configure_environment
|
|
setup_systemd_service
|
|
|
|
echo ""
|
|
print_success "Installation complete!"
|
|
}
|
|
|
|
# Perform update
|
|
perform_update() {
|
|
# Stop service
|
|
if systemctl is-active --quiet "$SERVICE_NAME"; then
|
|
print_info "Stopping service..."
|
|
systemctl stop "$SERVICE_NAME"
|
|
fi
|
|
|
|
# Determine target version
|
|
if [ -n "$SPECIFIED_VERSION_TAG" ]; then
|
|
TARGET_TAG="$SPECIFIED_VERSION_TAG"
|
|
if ! check_release_exists "$TARGET_TAG"; then
|
|
print_error "Version $TARGET_TAG does not exist"
|
|
exit 1
|
|
fi
|
|
else
|
|
TARGET_TAG=$(get_latest_release_tag)
|
|
if [ -z "$TARGET_TAG" ]; then
|
|
print_error "Could not determine latest version"
|
|
exit 1
|
|
fi
|
|
fi
|
|
|
|
local current_version=$(get_current_version)
|
|
if [ "$current_version" = "${TARGET_TAG#v}" ]; then
|
|
print_info "Already running latest version"
|
|
systemctl start "$SERVICE_NAME"
|
|
return 0
|
|
fi
|
|
|
|
print_info "Updating to $TARGET_TAG..."
|
|
echo ""
|
|
|
|
download_and_extract_tarball "$TARGET_TAG" || exit 1
|
|
|
|
# Fix Express version if needed
|
|
print_info "Checking dependencies..."
|
|
cd "$PULSE_DIR"
|
|
if grep -q '"express": "4.19.2"' package.json; then
|
|
local current_express=$(npm list express --depth=0 2>/dev/null | grep express@ | sed 's/.*express@//' || echo "")
|
|
if [[ "$current_express" != "4.19.2" ]]; then
|
|
print_warning "Fixing Express version mismatch..."
|
|
rm -rf node_modules
|
|
npm install --omit=dev || {
|
|
print_error "Failed to install dependencies"
|
|
exit 1
|
|
}
|
|
fi
|
|
fi
|
|
|
|
set_permissions
|
|
|
|
# Restart service
|
|
systemctl start "$SERVICE_NAME"
|
|
|
|
sleep 2
|
|
if systemctl is-active --quiet "$SERVICE_NAME"; then
|
|
echo ""
|
|
print_success "Update complete!"
|
|
else
|
|
print_error "Service failed to start after update"
|
|
exit 1
|
|
fi
|
|
}
|
|
|
|
# Remove Pulse
|
|
perform_remove() {
|
|
print_warning "This will completely remove Pulse"
|
|
read -p "Are you sure? [y/N]: " confirm
|
|
|
|
if [[ ! "$confirm" =~ ^[Yy]$ ]]; then
|
|
print_info "Cancelled"
|
|
exit 0
|
|
fi
|
|
|
|
echo ""
|
|
|
|
# Stop and disable service
|
|
if systemctl is-active --quiet "$SERVICE_NAME"; then
|
|
print_info "Stopping service..."
|
|
systemctl stop "$SERVICE_NAME"
|
|
fi
|
|
systemctl disable "$SERVICE_NAME" &>/dev/null || true
|
|
rm -f "/etc/systemd/system/$SERVICE_NAME"
|
|
systemctl daemon-reload
|
|
|
|
# Remove directories
|
|
[ -d "$PULSE_DIR" ] && rm -rf "$PULSE_DIR"
|
|
[ -d "$OLD_PULSE_DIR" ] && rm -rf "$OLD_PULSE_DIR"
|
|
|
|
print_success "Pulse has been removed"
|
|
}
|
|
|
|
# Migrate from old installation
|
|
perform_migration() {
|
|
print_info "Migrating old installation..."
|
|
|
|
# Stop old service
|
|
if systemctl is-active --quiet "pulse-proxmox.service"; then
|
|
systemctl stop "pulse-proxmox.service"
|
|
fi
|
|
systemctl disable "pulse-proxmox.service" &>/dev/null || true
|
|
|
|
# Backup old data
|
|
local backup_dir="/tmp/pulse-migration-$$"
|
|
if [ -d "$OLD_PULSE_DIR/data" ]; then
|
|
mkdir -p "$backup_dir"
|
|
cp -r "$OLD_PULSE_DIR/data" "$backup_dir/"
|
|
fi
|
|
|
|
# Remove old installation
|
|
rm -rf "$OLD_PULSE_DIR"
|
|
rm -f "/etc/systemd/system/pulse-proxmox.service"
|
|
|
|
# Perform fresh install
|
|
INSTALL_MODE="install"
|
|
perform_install
|
|
|
|
# Restore old data
|
|
if [ -d "$backup_dir/data" ]; then
|
|
print_info "Restoring data..."
|
|
cp -r "$backup_dir/data"/* "$PULSE_DIR/data/" 2>/dev/null || true
|
|
chown -R "$PULSE_USER:$PULSE_USER" "$PULSE_DIR/data"
|
|
rm -rf "$backup_dir"
|
|
fi
|
|
}
|
|
|
|
# Show final instructions
|
|
show_final_instructions() {
|
|
local ip=$(hostname -I | awk '{print $1}')
|
|
echo ""
|
|
echo "━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━"
|
|
echo -e "Access Pulse: \033[1;36mhttp://${ip}:7655\033[0m"
|
|
echo ""
|
|
echo "Useful commands:"
|
|
echo " • systemctl status $SERVICE_NAME"
|
|
echo " • journalctl -u $SERVICE_NAME -f"
|
|
echo "━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━"
|
|
echo ""
|
|
}
|
|
|
|
# Main execution
|
|
main() {
|
|
check_root
|
|
self_update_check
|
|
print_welcome
|
|
check_installation
|
|
|
|
case "$INSTALL_MODE" in
|
|
install)
|
|
perform_install
|
|
show_final_instructions
|
|
;;
|
|
update)
|
|
perform_update
|
|
show_final_instructions
|
|
;;
|
|
remove)
|
|
perform_remove
|
|
;;
|
|
migrate)
|
|
perform_migration
|
|
show_final_instructions
|
|
;;
|
|
*)
|
|
print_error "Invalid operation"
|
|
exit 1
|
|
;;
|
|
esac
|
|
}
|
|
|
|
# Run main function
|
|
main |