修复安装脚本的潜在问题
This commit is contained in:
333
install.sh
333
install.sh
@@ -1,155 +1,196 @@
|
|||||||
#!/bin/bash
|
#!/bin/bash
|
||||||
|
|
||||||
# PromdataPanel - Multi-Prometheus Monitoring Dashboard Installer
|
set -euo pipefail
|
||||||
# This script handles OS detection, Node.js installation, project setup, and Systemd configuration.
|
|
||||||
|
|
||||||
# Colors for output
|
|
||||||
RED='\033[0;31m'
|
RED='\033[0;31m'
|
||||||
GREEN='\033[0;32m'
|
GREEN='\033[0;32m'
|
||||||
YELLOW='\033[1;33m'
|
YELLOW='\033[1;33m'
|
||||||
BLUE='\033[0;34m'
|
BLUE='\033[0;34m'
|
||||||
NC='\033[0m' # No Color
|
NC='\033[0m'
|
||||||
|
|
||||||
# 0. Configuration
|
|
||||||
VERSION=${VERSION:-"v0.1.0"}
|
VERSION=${VERSION:-"v0.1.0"}
|
||||||
DOWNLOAD_URL="https://git.littlediary.cn/CN-JS-HuiBai/PromdataPanel/archive/${VERSION}.zip"
|
DOWNLOAD_URL="https://git.littlediary.cn/CN-JS-HuiBai/PromdataPanel/archive/${VERSION}.zip"
|
||||||
MIN_NODE_VERSION=18
|
MIN_NODE_VERSION=18
|
||||||
|
SERVICE_NAME="promdatapanel"
|
||||||
|
SERVICE_FILE="/etc/systemd/system/${SERVICE_NAME}.service"
|
||||||
|
|
||||||
|
OS_ID=""
|
||||||
|
OS_VER=""
|
||||||
|
PROJECT_DIR=""
|
||||||
|
REAL_USER=""
|
||||||
|
|
||||||
echo -e "${BLUE}================================================${NC}"
|
echo -e "${BLUE}================================================${NC}"
|
||||||
echo -e "${BLUE} PromdataPanel Auto-Installer ${NC}"
|
echo -e "${BLUE} PromdataPanel Auto-Installer ${NC}"
|
||||||
echo -e "${BLUE} Version: ${VERSION} ${NC}"
|
echo -e "${BLUE} Version: ${VERSION} ${NC}"
|
||||||
echo -e "${BLUE}================================================${NC}"
|
echo -e "${BLUE}================================================${NC}"
|
||||||
|
|
||||||
# 1. OS Detection
|
|
||||||
detect_os() {
|
detect_os() {
|
||||||
if [ -f /etc/os-release ]; then
|
if [ -f /etc/os-release ]; then
|
||||||
|
# shellcheck disable=SC1091
|
||||||
. /etc/os-release
|
. /etc/os-release
|
||||||
OS_ID=$ID
|
OS_ID="${ID:-}"
|
||||||
OS_VER=$VERSION_ID
|
OS_VER="${VERSION_ID:-}"
|
||||||
else
|
else
|
||||||
echo -e "${RED}Error: Cannot detect operating system type (/etc/os-release missing).${NC}"
|
echo -e "${RED}Error: Cannot detect operating system type (/etc/os-release missing).${NC}"
|
||||||
exit 1
|
exit 1
|
||||||
fi
|
fi
|
||||||
echo -e "Detected OS: ${GREEN}${OS_ID} ${OS_VER}${NC}"
|
|
||||||
}
|
|
||||||
|
|
||||||
# 2. Node.js Installation/Verification
|
if [ -z "$OS_ID" ]; then
|
||||||
install_node() {
|
echo -e "${RED}Error: Unable to determine operating system ID.${NC}"
|
||||||
echo -e "${BLUE}Verifying Node.js environment...${NC}"
|
|
||||||
|
|
||||||
NODE_INSTALLED=false
|
|
||||||
if command -v node &> /dev/null; then
|
|
||||||
CURRENT_NODE_VER=$(node -v | cut -d'v' -f2 | cut -d'.' -f1)
|
|
||||||
if [ "$CURRENT_NODE_VER" -ge "$MIN_NODE_VERSION" ]; then
|
|
||||||
echo -e "${GREEN}Node.js v$(node -v) is already installed.${NC}"
|
|
||||||
NODE_INSTALLED=true
|
|
||||||
else
|
|
||||||
echo -e "${YELLOW}Existing Node.js version (v$(node -v)) is too old (Requires >= $MIN_NODE_VERSION).${NC}"
|
|
||||||
fi
|
|
||||||
fi
|
|
||||||
|
|
||||||
if [ "$NODE_INSTALLED" = false ]; then
|
|
||||||
echo -e "${BLUE}Installing Node.js 20.x...${NC}"
|
|
||||||
case "$OS_ID" in
|
|
||||||
ubuntu|debian|raspbian)
|
|
||||||
sudo apt-get update
|
|
||||||
sudo apt-get install -y ca-certificates curl gnupg
|
|
||||||
curl -fsSL https://deb.nodesource.com/setup_20.x | sudo -E bash -
|
|
||||||
sudo apt-get install -y nodejs
|
|
||||||
;;
|
|
||||||
centos|rhel|almalinux|rocky)
|
|
||||||
curl -fsSL https://rpm.nodesource.com/setup_20.x | sudo bash -
|
|
||||||
sudo yum install -y nodejs
|
|
||||||
;;
|
|
||||||
fedora)
|
|
||||||
curl -fsSL https://rpm.nodesource.com/setup_20.x | sudo bash -
|
|
||||||
sudo dnf install -y nodejs
|
|
||||||
;;
|
|
||||||
*)
|
|
||||||
echo -e "${RED}Unsupported OS for automatic Node.js installation: $OS_ID${NC}"
|
|
||||||
echo -e "Please install Node.js >= 18 manually.${NC}"
|
|
||||||
exit 1
|
|
||||||
;;
|
|
||||||
esac
|
|
||||||
fi
|
|
||||||
}
|
|
||||||
|
|
||||||
# 3. Download and Extract (If needed)
|
|
||||||
if [ ! -f "server/index.js" ]; then
|
|
||||||
echo -e "${YELLOW}Project files not found. Starting download...${NC}"
|
|
||||||
|
|
||||||
if ! command -v curl &> /dev/null; then
|
|
||||||
echo -e "${BLUE}Installing curl...${NC}"
|
|
||||||
[ "$OS_ID" = "ubuntu" ] || [ "$OS_ID" = "debian" ] && sudo apt-get install -y curl
|
|
||||||
[ "$OS_ID" = "centos" ] || [ "$OS_ID" = "rhel" ] && sudo yum install -y curl
|
|
||||||
fi
|
|
||||||
|
|
||||||
if ! command -v unzip &> /dev/null; then
|
|
||||||
echo -e "${BLUE}Installing unzip...${NC}"
|
|
||||||
[ "$OS_ID" = "ubuntu" ] || [ "$OS_ID" = "debian" ] && sudo apt-get install -y unzip
|
|
||||||
[ "$OS_ID" = "centos" ] || [ "$OS_ID" = "rhel" ] && sudo yum install -y unzip
|
|
||||||
fi
|
|
||||||
|
|
||||||
TEMP_ZIP="promdatapanel_${VERSION}.zip"
|
|
||||||
echo -e "${BLUE}Downloading ${DOWNLOAD_URL}...${NC}"
|
|
||||||
curl -L "$DOWNLOAD_URL" -o "$TEMP_ZIP"
|
|
||||||
|
|
||||||
if [ $? -ne 0 ]; then
|
|
||||||
echo -e "${RED}Download failed.${NC}"
|
|
||||||
exit 1
|
exit 1
|
||||||
fi
|
fi
|
||||||
|
|
||||||
|
echo -e "Detected OS: ${GREEN}${OS_ID} ${OS_VER}${NC}"
|
||||||
|
}
|
||||||
|
|
||||||
|
require_cmd() {
|
||||||
|
local cmd="$1"
|
||||||
|
local hint="${2:-}"
|
||||||
|
if ! command -v "$cmd" >/dev/null 2>&1; then
|
||||||
|
echo -e "${RED}Missing required command: ${cmd}.${NC}"
|
||||||
|
if [ -n "$hint" ]; then
|
||||||
|
echo -e "${YELLOW}${hint}${NC}"
|
||||||
|
fi
|
||||||
|
exit 1
|
||||||
|
fi
|
||||||
|
}
|
||||||
|
|
||||||
|
install_packages() {
|
||||||
|
case "$OS_ID" in
|
||||||
|
ubuntu|debian|raspbian)
|
||||||
|
sudo apt-get update
|
||||||
|
sudo apt-get install -y "$@"
|
||||||
|
;;
|
||||||
|
centos|rhel|almalinux|rocky)
|
||||||
|
sudo yum install -y "$@"
|
||||||
|
;;
|
||||||
|
fedora)
|
||||||
|
sudo dnf install -y "$@"
|
||||||
|
;;
|
||||||
|
*)
|
||||||
|
echo -e "${RED}Unsupported OS for automatic package installation: ${OS_ID}${NC}"
|
||||||
|
echo -e "${YELLOW}Please install the following packages manually: $*${NC}"
|
||||||
|
exit 1
|
||||||
|
;;
|
||||||
|
esac
|
||||||
|
}
|
||||||
|
|
||||||
|
ensure_tooling() {
|
||||||
|
if ! command -v curl >/dev/null 2>&1; then
|
||||||
|
echo -e "${BLUE}Installing curl...${NC}"
|
||||||
|
install_packages curl
|
||||||
|
fi
|
||||||
|
|
||||||
|
if ! command -v unzip >/dev/null 2>&1; then
|
||||||
|
echo -e "${BLUE}Installing unzip...${NC}"
|
||||||
|
install_packages unzip
|
||||||
|
fi
|
||||||
|
}
|
||||||
|
|
||||||
|
configure_nodesource_apt_repo() {
|
||||||
|
sudo install -d -m 0755 /etc/apt/keyrings
|
||||||
|
curl -fsSL https://deb.nodesource.com/gpgkey/nodesource-repo.gpg.key | sudo gpg --dearmor -o /etc/apt/keyrings/nodesource.gpg
|
||||||
|
echo "deb [signed-by=/etc/apt/keyrings/nodesource.gpg] https://deb.nodesource.com/node_20.x nodistro main" | sudo tee /etc/apt/sources.list.d/nodesource.list >/dev/null
|
||||||
|
}
|
||||||
|
|
||||||
|
install_node() {
|
||||||
|
echo -e "${BLUE}Verifying Node.js environment...${NC}"
|
||||||
|
|
||||||
|
local node_installed=false
|
||||||
|
if command -v node >/dev/null 2>&1; then
|
||||||
|
local current_node_ver
|
||||||
|
current_node_ver=$(node -v | cut -d'v' -f2 | cut -d'.' -f1)
|
||||||
|
if [ "$current_node_ver" -ge "$MIN_NODE_VERSION" ]; then
|
||||||
|
echo -e "${GREEN}Node.js $(node -v) is already installed.${NC}"
|
||||||
|
node_installed=true
|
||||||
|
else
|
||||||
|
echo -e "${YELLOW}Existing Node.js $(node -v) is too old (requires >= ${MIN_NODE_VERSION}).${NC}"
|
||||||
|
fi
|
||||||
|
fi
|
||||||
|
|
||||||
|
if [ "$node_installed" = true ]; then
|
||||||
|
return
|
||||||
|
fi
|
||||||
|
|
||||||
|
echo -e "${BLUE}Installing Node.js 20.x...${NC}"
|
||||||
|
case "$OS_ID" in
|
||||||
|
ubuntu|debian|raspbian)
|
||||||
|
install_packages ca-certificates curl gnupg
|
||||||
|
configure_nodesource_apt_repo
|
||||||
|
sudo apt-get update
|
||||||
|
sudo apt-get install -y nodejs
|
||||||
|
;;
|
||||||
|
centos|rhel|almalinux|rocky)
|
||||||
|
install_packages nodejs
|
||||||
|
;;
|
||||||
|
fedora)
|
||||||
|
install_packages nodejs
|
||||||
|
;;
|
||||||
|
*)
|
||||||
|
echo -e "${RED}Unsupported OS for automatic Node.js installation: ${OS_ID}${NC}"
|
||||||
|
echo -e "${YELLOW}Please install Node.js >= ${MIN_NODE_VERSION} manually.${NC}"
|
||||||
|
exit 1
|
||||||
|
;;
|
||||||
|
esac
|
||||||
|
|
||||||
|
require_cmd node "Please install Node.js >= ${MIN_NODE_VERSION} manually and rerun the installer."
|
||||||
|
local installed_major
|
||||||
|
installed_major=$(node -v | cut -d'v' -f2 | cut -d'.' -f1)
|
||||||
|
if [ "$installed_major" -lt "$MIN_NODE_VERSION" ]; then
|
||||||
|
echo -e "${RED}Installed Node.js $(node -v) is still below the required version.${NC}"
|
||||||
|
echo -e "${YELLOW}Please upgrade Node.js manually to >= ${MIN_NODE_VERSION}.${NC}"
|
||||||
|
exit 1
|
||||||
|
fi
|
||||||
|
}
|
||||||
|
|
||||||
|
download_project_if_needed() {
|
||||||
|
if [ -f "server/index.js" ]; then
|
||||||
|
return
|
||||||
|
fi
|
||||||
|
|
||||||
|
echo -e "${YELLOW}Project files not found. Starting download...${NC}"
|
||||||
|
ensure_tooling
|
||||||
|
|
||||||
|
local temp_dir
|
||||||
|
temp_dir=$(mktemp -d "${TMPDIR:-/tmp}/promdatapanel-install-XXXXXX")
|
||||||
|
local temp_zip="${temp_dir}/promdatapanel_${VERSION}.zip"
|
||||||
|
|
||||||
|
echo -e "${BLUE}Downloading ${DOWNLOAD_URL}...${NC}"
|
||||||
|
curl -fL "$DOWNLOAD_URL" -o "$temp_zip"
|
||||||
|
|
||||||
echo -e "${BLUE}Extracting files...${NC}"
|
echo -e "${BLUE}Extracting files...${NC}"
|
||||||
unzip -q "$TEMP_ZIP"
|
unzip -q "$temp_zip" -d "$temp_dir"
|
||||||
|
|
||||||
EXTRACTED_DIR=$(ls -d */ | grep -E "^PromdataPanel" | head -n 1)
|
local extracted_dir
|
||||||
if [ -d "$EXTRACTED_DIR" ]; then
|
extracted_dir=$(find "$temp_dir" -mindepth 1 -maxdepth 1 -type d | head -n 1)
|
||||||
cd "$EXTRACTED_DIR" || exit 1
|
if [ -z "$extracted_dir" ] || [ ! -f "$extracted_dir/server/index.js" ]; then
|
||||||
|
echo -e "${RED}Download succeeded, but archive structure is invalid.${NC}"
|
||||||
|
exit 1
|
||||||
|
fi
|
||||||
|
|
||||||
|
cd "$extracted_dir"
|
||||||
|
}
|
||||||
|
|
||||||
|
detect_runtime_user() {
|
||||||
|
if [ "$EUID" -eq 0 ]; then
|
||||||
|
REAL_USER="${SUDO_USER:-${USER:-root}}"
|
||||||
else
|
else
|
||||||
EXTRACTED_DIR=$(ls -d */ | head -n 1)
|
REAL_USER="${USER}"
|
||||||
[ -d "$EXTRACTED_DIR" ] && cd "$EXTRACTED_DIR" || exit 1
|
|
||||||
fi
|
fi
|
||||||
rm "../$TEMP_ZIP" 2>/dev/null || rm "$TEMP_ZIP" 2>/dev/null
|
}
|
||||||
fi
|
|
||||||
|
|
||||||
# 4. Initialize Setup
|
write_service_file() {
|
||||||
# Permission check
|
local node_path
|
||||||
if [ "$EUID" -eq 0 ]; then
|
node_path=$(command -v node)
|
||||||
REAL_USER=${SUDO_USER:-$USER}
|
if [ -z "$node_path" ]; then
|
||||||
else
|
echo -e "${RED}Unable to locate node executable after installation.${NC}"
|
||||||
REAL_USER=$USER
|
exit 1
|
||||||
fi
|
|
||||||
|
|
||||||
detect_os
|
|
||||||
install_node
|
|
||||||
|
|
||||||
PROJECT_DIR=$(pwd)
|
|
||||||
echo -e "Project Directory: ${GREEN}$PROJECT_DIR${NC}"
|
|
||||||
echo -e "Running User: ${GREEN}$REAL_USER${NC}"
|
|
||||||
|
|
||||||
# Check for .env file
|
|
||||||
if [ ! -f ".env" ]; then
|
|
||||||
if [ -f ".env.example" ]; then
|
|
||||||
echo -e "${BLUE}Creating .env from .env.example...${NC}"
|
|
||||||
cp .env.example .env
|
|
||||||
fi
|
fi
|
||||||
fi
|
|
||||||
|
|
||||||
# 5. Install Dependencies
|
local tmp_service
|
||||||
echo -e "${BLUE}Installing NPM dependencies...${NC}"
|
tmp_service=$(mktemp "${TMPDIR:-/tmp}/${SERVICE_NAME}.service.XXXXXX")
|
||||||
npm install --production
|
|
||||||
|
|
||||||
if [ $? -ne 0 ]; then
|
cat > "$tmp_service" <<EOF
|
||||||
echo -e "${RED}NPM install failed.${NC}"
|
|
||||||
exit 1
|
|
||||||
fi
|
|
||||||
|
|
||||||
# 6. Create Systemd Service File
|
|
||||||
SERVICE_FILE="/etc/systemd/system/promdatapanel.service"
|
|
||||||
NODE_PATH=$(command -v node)
|
|
||||||
|
|
||||||
echo -e "${BLUE}Creating systemd service at $SERVICE_FILE...${NC}"
|
|
||||||
sudo bash -c "cat <<EOF > '$SERVICE_FILE'
|
|
||||||
[Unit]
|
[Unit]
|
||||||
Description=PromdataPanel Monitoring Dashboard
|
Description=PromdataPanel Monitoring Dashboard
|
||||||
After=network.target mysql.service redis-server.service valkey-server.service
|
After=network.target mysql.service redis-server.service valkey-server.service
|
||||||
@@ -157,39 +198,63 @@ Wants=mysql.service
|
|||||||
|
|
||||||
[Service]
|
[Service]
|
||||||
Type=simple
|
Type=simple
|
||||||
User=$REAL_USER
|
User=${REAL_USER}
|
||||||
WorkingDirectory=$PROJECT_DIR
|
WorkingDirectory=${PROJECT_DIR}
|
||||||
ExecStart=$NODE_PATH server/index.js
|
ExecStart=${node_path} server/index.js
|
||||||
Restart=always
|
Restart=always
|
||||||
RestartSec=10
|
RestartSec=10
|
||||||
StandardOutput=syslog
|
StandardOutput=journal
|
||||||
StandardError=syslog
|
StandardError=journal
|
||||||
SyslogIdentifier=promdatapanel
|
SyslogIdentifier=${SERVICE_NAME}
|
||||||
EnvironmentFile=-$PROJECT_DIR/.env
|
EnvironmentFile=-${PROJECT_DIR}/.env
|
||||||
Environment=NODE_ENV=production
|
Environment=NODE_ENV=production
|
||||||
|
|
||||||
[Install]
|
[Install]
|
||||||
WantedBy=multi-user.target
|
WantedBy=multi-user.target
|
||||||
EOF"
|
EOF
|
||||||
|
|
||||||
|
echo -e "${BLUE}Creating systemd service at ${SERVICE_FILE}...${NC}"
|
||||||
|
sudo install -m 0644 "$tmp_service" "$SERVICE_FILE"
|
||||||
|
rm -f "$tmp_service"
|
||||||
|
}
|
||||||
|
|
||||||
|
detect_os
|
||||||
|
download_project_if_needed
|
||||||
|
detect_runtime_user
|
||||||
|
install_node
|
||||||
|
|
||||||
|
PROJECT_DIR=$(pwd)
|
||||||
|
echo -e "Project Directory: ${GREEN}${PROJECT_DIR}${NC}"
|
||||||
|
echo -e "Running User: ${GREEN}${REAL_USER}${NC}"
|
||||||
|
|
||||||
|
if [ ! -f ".env" ] && [ -f ".env.example" ]; then
|
||||||
|
echo -e "${BLUE}Creating .env from .env.example...${NC}"
|
||||||
|
cp .env.example .env
|
||||||
|
fi
|
||||||
|
|
||||||
|
echo -e "${BLUE}Installing NPM dependencies...${NC}"
|
||||||
|
npm install --production
|
||||||
|
|
||||||
|
write_service_file
|
||||||
|
|
||||||
# 7. Reload and Start
|
|
||||||
echo -e "${BLUE}Reloading systemd and restarting service...${NC}"
|
echo -e "${BLUE}Reloading systemd and restarting service...${NC}"
|
||||||
sudo systemctl daemon-reload
|
sudo systemctl daemon-reload
|
||||||
sudo systemctl enable promdatapanel
|
sudo systemctl enable "$SERVICE_NAME"
|
||||||
sudo systemctl restart promdatapanel
|
sudo systemctl restart "$SERVICE_NAME"
|
||||||
|
|
||||||
# 8. Check Status
|
|
||||||
echo -e "${BLUE}Checking service status...${NC}"
|
echo -e "${BLUE}Checking service status...${NC}"
|
||||||
sleep 2
|
sleep 2
|
||||||
if sudo systemctl is-active --quiet promdatapanel; then
|
if sudo systemctl is-active --quiet "$SERVICE_NAME"; then
|
||||||
echo -e "${GREEN}SUCCESS: PromdataPanel is now running.${NC}"
|
echo -e "${GREEN}SUCCESS: PromdataPanel is now running.${NC}"
|
||||||
PORT=$(grep "^PORT=" .env | cut -d'=' -f2)
|
PORT=$(grep "^PORT=" .env 2>/dev/null | cut -d'=' -f2 || true)
|
||||||
PORT=${PORT:-3000}
|
PORT=${PORT:-3000}
|
||||||
IP_ADDR=$(hostname -I | awk '{print $1}')
|
IP_ADDR=$(hostname -I 2>/dev/null | awk '{print $1}')
|
||||||
echo -e "Dashboard URL: ${YELLOW}http://${IP_ADDR}:${PORT}${NC}"
|
if [ -n "${IP_ADDR:-}" ]; then
|
||||||
|
echo -e "Dashboard URL: ${YELLOW}http://${IP_ADDR}:${PORT}${NC}"
|
||||||
|
fi
|
||||||
else
|
else
|
||||||
echo -e "${RED}FAILED: Service failed to start.${NC}"
|
echo -e "${RED}FAILED: Service failed to start.${NC}"
|
||||||
echo -e "Check logs with: ${BLUE}journalctl -u promdatapanel -xe${NC}"
|
echo -e "Check logs with: ${BLUE}journalctl -u ${SERVICE_NAME} -xe${NC}"
|
||||||
fi
|
fi
|
||||||
|
|
||||||
echo -e "${BLUE}================================================${NC}"
|
echo -e "${BLUE}================================================${NC}"
|
||||||
|
|||||||
259
update.sh
259
update.sh
@@ -1,132 +1,187 @@
|
|||||||
#!/bin/bash
|
#!/bin/bash
|
||||||
|
|
||||||
# Update Script for PromdataPanel
|
set -euo pipefail
|
||||||
# -------------------------------
|
|
||||||
|
|
||||||
set -e
|
|
||||||
|
|
||||||
# Config
|
|
||||||
SERVICE_NAME="promdatapanel"
|
SERVICE_NAME="promdatapanel"
|
||||||
DEFAULT_APP_DIR="/opt/promdata-panel"
|
DEFAULT_APP_DIR="/opt/promdata-panel"
|
||||||
|
ZIP_URL="https://git.littlediary.cn/CN-JS-HuiBai/PromdataPanel/archive/main.zip"
|
||||||
|
|
||||||
# 1. Colors & Banner
|
|
||||||
GREEN='\033[0;32m'
|
GREEN='\033[0;32m'
|
||||||
BLUE='\033[0;34m'
|
BLUE='\033[0;34m'
|
||||||
RED='\033[0;31m'
|
RED='\033[0;31m'
|
||||||
NC='\033[0m' # No Color
|
YELLOW='\033[1;33m'
|
||||||
|
NC='\033[0m'
|
||||||
|
|
||||||
|
APP_DIR=""
|
||||||
|
TEMP_DIR=""
|
||||||
|
BACKUP_DIR=""
|
||||||
|
ROLLBACK_REQUIRED=false
|
||||||
|
|
||||||
echo -e "${BLUE}=== Starting PromdataPanel Update ===${NC}"
|
echo -e "${BLUE}=== Starting PromdataPanel Update ===${NC}"
|
||||||
|
|
||||||
# 2. Detect APP_DIR automatically from systemd service
|
cleanup() {
|
||||||
if systemctl list-unit-files | grep -q "^$SERVICE_NAME.service"; then
|
if [ -n "${TEMP_DIR}" ] && [ -d "${TEMP_DIR}" ]; then
|
||||||
echo "Detecting application directory from systemd service..."
|
rm -rf "${TEMP_DIR}"
|
||||||
# Get WorkingDirectory from systemd (using env to handle it safely)
|
|
||||||
SERVICE_DIR=$(systemctl show -p WorkingDirectory "$SERVICE_NAME" | cut -d= -f2-)
|
|
||||||
if [ -n "$SERVICE_DIR" ] && [ -d "$SERVICE_DIR" ]; then
|
|
||||||
APP_DIR="$SERVICE_DIR"
|
|
||||||
fi
|
fi
|
||||||
fi
|
}
|
||||||
|
|
||||||
# Fallback 1: Current directory if it contains package.json
|
rollback() {
|
||||||
if [ -z "$APP_DIR" ]; then
|
if [ "$ROLLBACK_REQUIRED" != true ] || [ -z "${BACKUP_DIR}" ] || [ ! -d "${BACKUP_DIR}" ]; then
|
||||||
if [ -f "package.json" ]; then
|
return
|
||||||
APP_DIR=$(pwd)
|
fi
|
||||||
else
|
|
||||||
|
echo -e "${YELLOW}Update failed. Restoring previous application state...${NC}"
|
||||||
|
rsync -a --delete --exclude '.env' "${BACKUP_DIR}/" "${APP_DIR}/"
|
||||||
|
}
|
||||||
|
|
||||||
|
trap 'rollback' ERR
|
||||||
|
trap cleanup EXIT
|
||||||
|
|
||||||
|
validate_app_dir() {
|
||||||
|
local dir="$1"
|
||||||
|
[ -n "$dir" ] || return 1
|
||||||
|
[ -d "$dir" ] || return 1
|
||||||
|
[ -f "$dir/package.json" ] || return 1
|
||||||
|
[ -f "$dir/server/index.js" ] || return 1
|
||||||
|
[ -f "$dir/public/index.html" ] || return 1
|
||||||
|
return 0
|
||||||
|
}
|
||||||
|
|
||||||
|
detect_app_dir() {
|
||||||
|
local service_dir=""
|
||||||
|
if command -v systemctl >/dev/null 2>&1 && systemctl list-unit-files | grep -q "^${SERVICE_NAME}\.service"; then
|
||||||
|
echo "Detecting application directory from systemd service..."
|
||||||
|
service_dir=$(systemctl show -p WorkingDirectory "$SERVICE_NAME" | cut -d= -f2-)
|
||||||
|
if validate_app_dir "$service_dir"; then
|
||||||
|
APP_DIR="$service_dir"
|
||||||
|
return
|
||||||
|
fi
|
||||||
|
fi
|
||||||
|
|
||||||
|
local current_dir
|
||||||
|
current_dir=$(pwd)
|
||||||
|
if validate_app_dir "$current_dir"; then
|
||||||
|
APP_DIR="$current_dir"
|
||||||
|
return
|
||||||
|
fi
|
||||||
|
|
||||||
|
if validate_app_dir "$DEFAULT_APP_DIR"; then
|
||||||
APP_DIR="$DEFAULT_APP_DIR"
|
APP_DIR="$DEFAULT_APP_DIR"
|
||||||
|
return
|
||||||
fi
|
fi
|
||||||
fi
|
|
||||||
|
|
||||||
echo -e "${BLUE}Application directory: $APP_DIR${NC}"
|
echo -e "${RED}Error: Could not locate a valid PromdataPanel application directory.${NC}"
|
||||||
|
echo -e "${YELLOW}Expected markers: package.json, server/index.js, public/index.html${NC}"
|
||||||
if [ ! -d "$APP_DIR" ]; then
|
|
||||||
echo -e "${RED}Error: Could not find application directory at $APP_DIR. Check if you installed it correctly.${NC}"
|
|
||||||
exit 1
|
exit 1
|
||||||
fi
|
}
|
||||||
|
|
||||||
cd "$APP_DIR"
|
ensure_tool() {
|
||||||
|
local cmd="$1"
|
||||||
# 2. Update logic
|
if command -v "$cmd" >/dev/null 2>&1; then
|
||||||
if [ -d ".git" ]; then
|
return
|
||||||
echo -e "${BLUE}Git repository detected. Pulling latest code...${NC}"
|
|
||||||
git pull
|
|
||||||
else
|
|
||||||
echo -e "${BLUE}No git repository found. Updating via ZIP archive...${NC}"
|
|
||||||
|
|
||||||
# URL for zip download
|
|
||||||
ZIP_URL="https://git.littlediary.cn/CN-JS-HuiBai/PromdataPanel/archive/main.zip"
|
|
||||||
TEMP_DIR="/tmp/promdata_update_$(date +%s)"
|
|
||||||
|
|
||||||
mkdir -p "$TEMP_DIR"
|
|
||||||
echo "Downloading latest version (main branch)..."
|
|
||||||
curl -L "$ZIP_URL" -o "$TEMP_DIR/latest.zip"
|
|
||||||
|
|
||||||
# Ensure unzip is available
|
|
||||||
if ! command -v unzip &> /dev/null; then
|
|
||||||
echo -e "${BLUE}unzip is not installed. Attempting to install it...${NC}"
|
|
||||||
if command -v apt-get &> /dev/null; then
|
|
||||||
sudo apt-get update && sudo apt-get install -y unzip
|
|
||||||
elif command -v dnf &> /dev/null; then
|
|
||||||
sudo dnf install -y unzip
|
|
||||||
elif command -v yum &> /dev/null; then
|
|
||||||
sudo yum install -y yum-utils && sudo yum install -y unzip
|
|
||||||
elif command -v apk &> /dev/null; then
|
|
||||||
sudo apk add unzip
|
|
||||||
fi
|
|
||||||
fi
|
fi
|
||||||
|
|
||||||
echo "Extracting archive..."
|
echo -e "${BLUE}${cmd} is not installed. Attempting to install it...${NC}"
|
||||||
unzip -q "$TEMP_DIR/latest.zip" -d "$TEMP_DIR"
|
if command -v apt-get >/dev/null 2>&1; then
|
||||||
|
sudo apt-get update
|
||||||
# The extracted folder is usually named project-main or similar
|
sudo apt-get install -y "$cmd"
|
||||||
EXTRACTED_FOLDER=$(ls -d "$TEMP_DIR"/*/ | head -n 1)
|
elif command -v dnf >/dev/null 2>&1; then
|
||||||
|
sudo dnf install -y "$cmd"
|
||||||
# Ensure rsync is available (Auto-install if missing)
|
elif command -v yum >/dev/null 2>&1; then
|
||||||
if ! command -v rsync &> /dev/null; then
|
sudo yum install -y "$cmd"
|
||||||
echo -e "${BLUE}rsync is not installed. Attempting to install it...${NC}"
|
elif command -v apk >/dev/null 2>&1; then
|
||||||
if command -v apt-get &> /dev/null; then
|
sudo apk add "$cmd"
|
||||||
sudo apt-get update && sudo apt-get install -y rsync
|
|
||||||
elif command -v dnf &> /dev/null; then
|
|
||||||
sudo dnf install -y rsync
|
|
||||||
elif command -v yum &> /dev/null; then
|
|
||||||
sudo yum install -y rsync
|
|
||||||
elif command -v apk &> /dev/null; then
|
|
||||||
sudo apk add rsync
|
|
||||||
else
|
|
||||||
echo -e "${RED}Error: 'rsync' is not installed and could not auto-install. Please install it manually.${NC}"
|
|
||||||
rm -rf "$TEMP_DIR"
|
|
||||||
exit 1
|
|
||||||
fi
|
|
||||||
fi
|
|
||||||
|
|
||||||
if [ -n "$EXTRACTED_FOLDER" ]; then
|
|
||||||
echo "Applying updates (preserving .env)..."
|
|
||||||
# Copy everything except .env
|
|
||||||
rsync -av --exclude '.env' --exclude 'node_modules' "$EXTRACTED_FOLDER" ./
|
|
||||||
else
|
else
|
||||||
echo -e "${RED}Extraction failed. Please check the archive structure.${NC}"
|
echo -e "${RED}Error: '${cmd}' is not installed and could not be auto-installed.${NC}"
|
||||||
exit 1
|
exit 1
|
||||||
fi
|
fi
|
||||||
|
}
|
||||||
# Cleanup
|
|
||||||
rm -rf "$TEMP_DIR"
|
|
||||||
fi
|
|
||||||
|
|
||||||
# 3. Update dependencies
|
update_from_git() {
|
||||||
echo -e "${BLUE}Updating npm dependencies...${NC}"
|
echo -e "${BLUE}Git repository detected. Pulling latest code...${NC}"
|
||||||
npm install --production
|
if [ -n "$(git status --porcelain)" ]; then
|
||||||
|
echo -e "${RED}Error: Working tree has local changes. Commit or stash them before updating.${NC}"
|
||||||
|
exit 1
|
||||||
|
fi
|
||||||
|
git pull --ff-only
|
||||||
|
}
|
||||||
|
|
||||||
# 4. Restart service
|
update_from_zip() {
|
||||||
if systemctl is-active --quiet "$SERVICE_NAME"; then
|
echo -e "${BLUE}No git repository found. Updating via ZIP archive with staging and rollback...${NC}"
|
||||||
echo -e "${BLUE}Restarting systemd service: $SERVICE_NAME...${NC}"
|
ensure_tool curl
|
||||||
sudo systemctl restart "$SERVICE_NAME"
|
ensure_tool unzip
|
||||||
echo -e "${GREEN}Update completed and service restarted!${NC}"
|
ensure_tool rsync
|
||||||
elif command -v pm2 &> /dev/null && pm2 list | grep -q "$SERVICE_NAME"; then
|
|
||||||
echo -e "${BLUE}Restarting with PM2...${NC}"
|
TEMP_DIR=$(mktemp -d "${TMPDIR:-/tmp}/promdatapanel-update-XXXXXX")
|
||||||
pm2 restart "$SERVICE_NAME"
|
BACKUP_DIR="${TEMP_DIR}/backup"
|
||||||
echo -e "${GREEN}Update completed and PM2 restarted!${NC}"
|
local archive_path="${TEMP_DIR}/latest.zip"
|
||||||
|
local extracted_folder=""
|
||||||
|
local staging_dir=""
|
||||||
|
|
||||||
|
echo "Downloading latest version (main branch)..."
|
||||||
|
curl -fL "$ZIP_URL" -o "$archive_path"
|
||||||
|
|
||||||
|
echo "Extracting archive..."
|
||||||
|
unzip -q "$archive_path" -d "$TEMP_DIR"
|
||||||
|
extracted_folder=$(find "$TEMP_DIR" -mindepth 1 -maxdepth 1 -type d ! -name backup | head -n 1)
|
||||||
|
|
||||||
|
if ! validate_app_dir "$extracted_folder"; then
|
||||||
|
echo -e "${RED}Extraction failed or archive structure is invalid.${NC}"
|
||||||
|
exit 1
|
||||||
|
fi
|
||||||
|
|
||||||
|
staging_dir="${TEMP_DIR}/staging"
|
||||||
|
mkdir -p "$staging_dir"
|
||||||
|
rsync -a --exclude '.git' "$extracted_folder/" "$staging_dir/"
|
||||||
|
|
||||||
|
if [ -f "${APP_DIR}/.env" ]; then
|
||||||
|
cp "${APP_DIR}/.env" "${staging_dir}/.env"
|
||||||
|
fi
|
||||||
|
|
||||||
|
echo "Installing dependencies in staging directory..."
|
||||||
|
(
|
||||||
|
cd "$staging_dir"
|
||||||
|
npm install --production
|
||||||
|
)
|
||||||
|
|
||||||
|
echo "Creating rollback backup..."
|
||||||
|
rsync -a --delete --exclude '.env' "${APP_DIR}/" "${BACKUP_DIR}/"
|
||||||
|
|
||||||
|
echo "Applying staged update..."
|
||||||
|
ROLLBACK_REQUIRED=true
|
||||||
|
rsync -a --delete --exclude '.env' "${staging_dir}/" "${APP_DIR}/"
|
||||||
|
}
|
||||||
|
|
||||||
|
restart_service() {
|
||||||
|
if command -v systemctl >/dev/null 2>&1 && systemctl is-active --quiet "$SERVICE_NAME"; then
|
||||||
|
echo -e "${BLUE}Restarting systemd service: ${SERVICE_NAME}...${NC}"
|
||||||
|
sudo systemctl restart "$SERVICE_NAME"
|
||||||
|
return
|
||||||
|
fi
|
||||||
|
|
||||||
|
if command -v pm2 >/dev/null 2>&1 && pm2 list | grep -q "$SERVICE_NAME"; then
|
||||||
|
echo -e "${BLUE}Restarting with PM2...${NC}"
|
||||||
|
pm2 restart "$SERVICE_NAME"
|
||||||
|
return
|
||||||
|
fi
|
||||||
|
|
||||||
|
echo -e "${YELLOW}Warning: Could not detect an active systemd service or PM2 process named '${SERVICE_NAME}'.${NC}"
|
||||||
|
echo -e "${YELLOW}Please restart the application manually.${NC}"
|
||||||
|
}
|
||||||
|
|
||||||
|
detect_app_dir
|
||||||
|
echo -e "${BLUE}Application directory: ${APP_DIR}${NC}"
|
||||||
|
cd "$APP_DIR"
|
||||||
|
|
||||||
|
if [ -d ".git" ]; then
|
||||||
|
update_from_git
|
||||||
|
echo -e "${BLUE}Updating npm dependencies...${NC}"
|
||||||
|
npm install --production
|
||||||
else
|
else
|
||||||
echo -e "${RED}Warning: Could not detect an active systemd service or PM2 process named '$SERVICE_NAME'.${NC}"
|
update_from_zip
|
||||||
echo -e "${RED}Please restart the application manually.${NC}"
|
|
||||||
fi
|
fi
|
||||||
|
|
||||||
|
restart_service
|
||||||
|
ROLLBACK_REQUIRED=false
|
||||||
|
|
||||||
echo -e "${GREEN}=== Update successfully finished ===${NC}"
|
echo -e "${GREEN}=== Update successfully finished ===${NC}"
|
||||||
|
|||||||
Reference in New Issue
Block a user