188 lines
5.3 KiB
Bash
188 lines
5.3 KiB
Bash
#!/bin/bash
|
|
|
|
set -euo pipefail
|
|
|
|
SERVICE_NAME="promdatapanel"
|
|
DEFAULT_APP_DIR="/opt/promdata-panel"
|
|
ZIP_URL="https://git.littlediary.cn/CN-JS-HuiBai/PromdataPanel/archive/main.zip"
|
|
|
|
GREEN='\033[0;32m'
|
|
BLUE='\033[0;34m'
|
|
RED='\033[0;31m'
|
|
YELLOW='\033[1;33m'
|
|
NC='\033[0m'
|
|
|
|
APP_DIR=""
|
|
TEMP_DIR=""
|
|
BACKUP_DIR=""
|
|
ROLLBACK_REQUIRED=false
|
|
|
|
echo -e "${BLUE}=== Starting PromdataPanel Update ===${NC}"
|
|
|
|
cleanup() {
|
|
if [ -n "${TEMP_DIR}" ] && [ -d "${TEMP_DIR}" ]; then
|
|
rm -rf "${TEMP_DIR}"
|
|
fi
|
|
}
|
|
|
|
rollback() {
|
|
if [ "$ROLLBACK_REQUIRED" != true ] || [ -z "${BACKUP_DIR}" ] || [ ! -d "${BACKUP_DIR}" ]; then
|
|
return
|
|
fi
|
|
|
|
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"
|
|
return
|
|
fi
|
|
|
|
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}"
|
|
exit 1
|
|
}
|
|
|
|
ensure_tool() {
|
|
local cmd="$1"
|
|
if command -v "$cmd" >/dev/null 2>&1; then
|
|
return
|
|
fi
|
|
|
|
echo -e "${BLUE}${cmd} is not installed. Attempting to install it...${NC}"
|
|
if command -v apt-get >/dev/null 2>&1; then
|
|
sudo apt-get update
|
|
sudo apt-get install -y "$cmd"
|
|
elif command -v dnf >/dev/null 2>&1; then
|
|
sudo dnf install -y "$cmd"
|
|
elif command -v yum >/dev/null 2>&1; then
|
|
sudo yum install -y "$cmd"
|
|
elif command -v apk >/dev/null 2>&1; then
|
|
sudo apk add "$cmd"
|
|
else
|
|
echo -e "${RED}Error: '${cmd}' is not installed and could not be auto-installed.${NC}"
|
|
exit 1
|
|
fi
|
|
}
|
|
|
|
update_from_git() {
|
|
echo -e "${BLUE}Git repository detected. Pulling latest code...${NC}"
|
|
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
|
|
}
|
|
|
|
update_from_zip() {
|
|
echo -e "${BLUE}No git repository found. Updating via ZIP archive with staging and rollback...${NC}"
|
|
ensure_tool curl
|
|
ensure_tool unzip
|
|
ensure_tool rsync
|
|
|
|
TEMP_DIR=$(mktemp -d "${TMPDIR:-/tmp}/promdatapanel-update-XXXXXX")
|
|
BACKUP_DIR="${TEMP_DIR}/backup"
|
|
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
|
|
update_from_zip
|
|
fi
|
|
|
|
restart_service
|
|
ROLLBACK_REQUIRED=false
|
|
|
|
echo -e "${GREEN}=== Update successfully finished ===${NC}"
|