mirror of
https://github.com/MCSManager/Script.git
synced 2025-11-27 23:10:26 +08:00
1357 lines
No EOL
39 KiB
Bash
1357 lines
No EOL
39 KiB
Bash
#!/bin/bash
|
||
# MCSManager官方安装脚本.
|
||
# 这个脚本将会把MCSManager服务端和节点服务端更新/安装至最新发布版本.
|
||
# ------------------------------------------------------------------------------
|
||
# 受支持的Linux:
|
||
# 此脚本支持以下Linux发行版:
|
||
# - Ubuntu: 18.04, 20.04, 22.04, 24.04
|
||
# - Debian: 10, 11, 12, 13
|
||
# - CentOS: 7, 8 Stream, 9 Stream, 10 Stream
|
||
# - RHEL: 7, 8, 9, 10
|
||
# - Arch Linux: Support planned (TBD)
|
||
# ------------------------------------------------------------------------------
|
||
|
||
# Target installation directory (can be overridden with --install-dir)
|
||
install_dir="/opt/mcsmanager"
|
||
|
||
# Primary download URL bas. Full package URL = download_base_url + package_name
|
||
download_base_url="https://download.mcsmanager.com/"
|
||
|
||
# Fallback download URL (can also be a local directory or mirror)
|
||
download_fallback_url="https://github.com/MCSManager/MCSManager/releases/latest/download/mcsmanager_linux_release.tar.gz"
|
||
|
||
# Name of the release package to download/detect
|
||
package_name="mcsmanager_linux_release.tar.gz"
|
||
|
||
# Node.js version to be installed
|
||
# Keep the leading "v"
|
||
node_version="v20.12.2"
|
||
|
||
# Node download base URL - primary
|
||
node_download_url_base="https://nodejs.org/dist/"
|
||
|
||
# Node download URL - fallback.
|
||
# This is the URL points directly to the file, not the base. This can also be a local absolute path.
|
||
# Only supports https:// or http:// for web locations.
|
||
node_download_fallback=""
|
||
|
||
# Node.js installation path (defaults to the MCSManager installation path. Can be overridden with --node-install-dir)
|
||
node_install_dir="$install_dir"
|
||
|
||
# Temp dir for file extraction
|
||
tmp_dir="/tmp"
|
||
|
||
# Bypass installed user permission check, override by --force-permission
|
||
force_permission=false
|
||
|
||
|
||
# --------------- Global Variables ---------------#
|
||
# DO NOT MODIFY #
|
||
|
||
|
||
# Component installation options.
|
||
# For fresh installs, both daemon and web components are installed by default.
|
||
# For updates, behavior depends on detected existing components.
|
||
# Can be overridden with --install daemon/web/all
|
||
install_daemon=true
|
||
install_web=true
|
||
|
||
# Install MCSM as (default: root).
|
||
# To install as a general user (e.g., "mcsm"), use the --user option: --user mcsm
|
||
# To ensure compatibility, only user mcsm is supported.
|
||
install_user="root"
|
||
# Installed user, for permission check
|
||
web_installed=false
|
||
daemon_installed=false
|
||
web_installed_user=""
|
||
daemon_installed_user=""
|
||
|
||
# Service file locations
|
||
# the final dir = systemd_file + {web/daemon} + ".service"
|
||
systemd_file="/etc/systemd/system/mcsm-"
|
||
# Optional: Override the default installation source file.
|
||
# If --install-source is specified, the installer will use the provided
|
||
# "mcsmanager_linux_release.tar.gz" file instead of downloading it.
|
||
# Only support local absolute path.
|
||
install_source_path=""
|
||
|
||
# temp path for extracted file(s)
|
||
install_tmp_dir="/opt/mcsmanager/mcsm_abcd"
|
||
|
||
# dir name for data dir backup
|
||
# e.g. /opt/mcsmanager/daemon/data -> /opt/mcsmanager/data_bak_data
|
||
# only valid for when during an update
|
||
backup_prefix="data_bak_"
|
||
|
||
# System architecture (detected automatically)
|
||
arch=""
|
||
version=""
|
||
distro=""
|
||
|
||
|
||
|
||
# Supported OS versions (map-style structure)
|
||
# Format: supported_os["distro_name"]="version1 version2 version3 ..."
|
||
declare -A supported_os
|
||
supported_os["Ubuntu"]="18 20 22 24"
|
||
supported_os["Debian"]="10 11 12 13"
|
||
supported_os["CentOS"]="7 8 8-stream 9-stream 10-stream"
|
||
supported_os["RHEL"]="7 8 9 10"
|
||
supported_os["Arch"]="rolling"
|
||
|
||
# Required system commands for installation
|
||
# These will be checked before logic process
|
||
required_commands=(
|
||
chmod
|
||
chown
|
||
wget
|
||
tar
|
||
stat
|
||
useradd
|
||
usermod
|
||
date
|
||
)
|
||
|
||
# Node.js related sections
|
||
# Enable strict version checking (exact match)
|
||
# enabled -> strict requriement for defined node version
|
||
# false -> newer version allowed
|
||
# Older version is NEVER allowed
|
||
strict_node_version_check=true
|
||
|
||
# Will be set based on actual node status
|
||
install_node=true
|
||
# Remove leading "v" from defined version
|
||
required_node_ver="${node_version#v}"
|
||
|
||
# Holds absolute path for node & npm
|
||
node_bin_path=""
|
||
npm_bin_path=""
|
||
# Hold Node.js arch name, e.g. x86_64 -> x64
|
||
node_arch=""
|
||
# Hold Node.js intallation path, e.g. ${node_install_dir}/node-${node_version}-linux-${arch}
|
||
node_path=""
|
||
|
||
# For installation result
|
||
daemon_key=""
|
||
daemon_port=""
|
||
web_port=""
|
||
daemon_key_config_subpath="data/Config/global.json"
|
||
web_port_config_subpath="data/SystemConfig/config.json"
|
||
|
||
# Terminal color & style related
|
||
# Default to false, auto check later
|
||
SUPPORTS_COLOR=false
|
||
SUPPORTS_STYLE=false
|
||
# Declare ANSI reset
|
||
RESET="\033[0m"
|
||
|
||
# Foreground colors
|
||
declare -A FG_COLORS=(
|
||
[black]="\033[0;30m"
|
||
[red]="\033[0;31m"
|
||
[green]="\033[0;32m"
|
||
[yellow]="\033[0;33m"
|
||
[blue]="\033[0;34m"
|
||
[magenta]="\033[0;35m"
|
||
[cyan]="\033[0;36m"
|
||
[white]="\033[0;37m"
|
||
)
|
||
|
||
# Font styles
|
||
declare -A STYLES=(
|
||
[bold]="\033[1m"
|
||
[underline]="\033[4m"
|
||
[italic]="\033[3m" # Often ignored
|
||
[clear_line]="\r\033[2K"
|
||
[strikethrough]="\033[9m"
|
||
)
|
||
|
||
|
||
### Helper Functions
|
||
# Execution wrapper, avoid unexpected crashes.
|
||
safe_run() {
|
||
local func="$1"
|
||
local err_msg="$2"
|
||
shift 2
|
||
|
||
if ! "$func" "$@"; then
|
||
echo "Error: $err_msg"
|
||
exit 1
|
||
fi
|
||
}
|
||
|
||
# Function to ensure the script is run as root
|
||
check_root() {
|
||
# Using Bash's built-in EUID variable
|
||
if [ -n "$EUID" ]; then
|
||
if [ "$EUID" -ne 0 ]; then
|
||
cprint red "错误: 这个脚本只能运行在root或sudo模式下,请尝试切换用户或者使用sudo."
|
||
exit 1
|
||
fi
|
||
else
|
||
# Fallback to using id -u if EUID is unavailable (e.g., non-Bash shell or misconfigured environment)
|
||
if [ "$(id -u)" -ne 0 ]; then
|
||
cprint red "错误: 这个脚本只能运行在root或sudo模式下,请尝试切换用户或者使用sudo."
|
||
exit 1
|
||
fi
|
||
fi
|
||
}
|
||
|
||
# Function to check whether current terminal support color & style
|
||
detect_terminal_capabilities() {
|
||
SUPPORTS_COLOR=false
|
||
SUPPORTS_STYLE=false
|
||
|
||
if [ -t 1 ] && command -v tput >/dev/null 2>&1; then
|
||
if [ "$(tput colors)" -ge 8 ]; then
|
||
SUPPORTS_COLOR=true
|
||
fi
|
||
if tput bold >/dev/null 2>&1 && tput smul >/dev/null 2>&1; then
|
||
SUPPORTS_STYLE=true
|
||
fi
|
||
fi
|
||
|
||
if [ "$SUPPORTS_COLOR" = true ]; then
|
||
cprint green "[OK] 这个终端支持彩色输出."
|
||
else
|
||
cprint yellow "注:终端不支持彩色输出。不格式化继续."
|
||
fi
|
||
|
||
if [ "$SUPPORTS_STYLE" = true ]; then
|
||
cprint green "[OK] 终端支持粗体和下划线格式."
|
||
else
|
||
cprint yellow "注意:终端不支持高级文本样式."
|
||
fi
|
||
}
|
||
|
||
# Check whether daemon or web is installed
|
||
is_component_installed() {
|
||
local component_name="$1"
|
||
local component_path="${install_dir}/${component_name}"
|
||
|
||
if [[ -d "$component_path" ]]; then
|
||
cprint green "组件 '$component_name' 已经被安装在 $component_path"
|
||
|
||
# Set corresponding global variable
|
||
if [[ "$component_name" == "daemon" ]]; then
|
||
daemon_installed=true
|
||
elif [[ "$component_name" == "web" ]]; then
|
||
web_installed=true
|
||
fi
|
||
|
||
return 0
|
||
else
|
||
cprint yellow "组件 '$component_name' 未被安装"
|
||
|
||
# Set corresponding global variable
|
||
if [[ "$component_name" == "daemon" ]]; then
|
||
daemon_installed=false
|
||
elif [[ "$component_name" == "web" ]]; then
|
||
web_installed=false
|
||
fi
|
||
|
||
return 1
|
||
fi
|
||
}
|
||
|
||
check_component_permission() {
|
||
local component="$1"
|
||
local service_file="${systemd_file}${component}.service"
|
||
|
||
if [[ ! -f "$service_file" ]]; then
|
||
cprint yellow "找不到服务文件: $service_file"
|
||
return 0 # nothing changed
|
||
fi
|
||
|
||
# Extract the User= line if it exists
|
||
local user_line
|
||
user_line=$(grep -E '^User=' "$service_file" 2>/dev/null | head -1)
|
||
|
||
local user
|
||
if [[ -z "$user_line" ]]; then
|
||
user="root" # default if no User= is defined
|
||
else
|
||
user="${user_line#User=}"
|
||
fi
|
||
|
||
# Validate user
|
||
if [[ "$user" != "root" && "$user" != "mcsm" ]]; then
|
||
cprint red bold "不支持的用户 '$user' 在 $service_file. 使用 'root' 或 'mcsm'."
|
||
exit 1
|
||
fi
|
||
|
||
# Assign to appropriate global
|
||
if [[ "$component" == "web" ]]; then
|
||
web_installed_user="$user"
|
||
elif [[ "$component" == "daemon" ]]; then
|
||
daemon_installed_user="$user"
|
||
fi
|
||
|
||
cprint cyan "已删除 $component 以用户身份安装: $user"
|
||
return 0
|
||
}
|
||
|
||
|
||
|
||
parse_args() {
|
||
local explicit_install_flag=false
|
||
|
||
while [[ $# -gt 0 ]]; do
|
||
case "$1" in
|
||
--install-dir)
|
||
if [[ -n "$2" ]]; then
|
||
install_dir="$2"
|
||
shift 2
|
||
else
|
||
echo "错误:--install-dir需要一个路径参数."
|
||
exit 1
|
||
fi
|
||
;;
|
||
--node-install-dir)
|
||
if [[ -n "$2" ]]; then
|
||
node_install_dir="$2"
|
||
shift 2
|
||
else
|
||
echo "错误:--node-install-dir需要一个路径参数."
|
||
exit 1
|
||
fi
|
||
;;
|
||
--install)
|
||
explicit_install_flag=true
|
||
if [[ -n "$2" && "$2" != --* ]]; then
|
||
case "$2" in
|
||
daemon)
|
||
install_daemon=true
|
||
is_component_installed "daemon"
|
||
install_web=false
|
||
check_component_permission "daemon"
|
||
;;
|
||
web)
|
||
install_daemon=false
|
||
is_component_installed "web"
|
||
install_web=true
|
||
check_component_permission "web"
|
||
;;
|
||
all)
|
||
install_daemon=true
|
||
install_web=true
|
||
is_component_installed "daemon"
|
||
is_component_installed "web"
|
||
check_component_permission "daemon"
|
||
check_component_permission "web"
|
||
;;
|
||
*)
|
||
echo "错误:--install的值无效。期望‘daemon’, ‘web’或‘all’."
|
||
echo "Usage: --install daemon|web|all"
|
||
exit 1
|
||
;;
|
||
esac
|
||
shift 2
|
||
else
|
||
echo "错误:提供了--install标志,但没有值。请指定:daemon、web或all."
|
||
echo "使用方法: --install daemon|web|all"
|
||
exit 1
|
||
fi
|
||
;;
|
||
--user)
|
||
if [[ -n "$2" ]]; then
|
||
case "$2" in
|
||
root)
|
||
install_user="root"
|
||
;;
|
||
mcsm)
|
||
install_user="mcsm"
|
||
;;
|
||
*)
|
||
echo "错误:无效用户 '$2'. 只有 'root' 和 'mcsm' 受支持."
|
||
echo "使用方法: --user root|mcsm"
|
||
exit 1
|
||
;;
|
||
esac
|
||
shift 2
|
||
else
|
||
echo "错误:--user需要一个值 (root 或 mcsm)."
|
||
exit 1
|
||
fi
|
||
;;
|
||
--install-source)
|
||
if [[ -n "$2" ]]; then
|
||
install_source_path="$2"
|
||
shift 2
|
||
else
|
||
echo "错误:--install-source需要文件路径."
|
||
exit 1
|
||
fi
|
||
;;
|
||
--force-permission)
|
||
force_permission=true
|
||
shift
|
||
;;
|
||
*)
|
||
echo "错误:未知参数t: $1"
|
||
exit 1
|
||
;;
|
||
esac
|
||
done
|
||
|
||
# Auto-detect branch: only run if --install was not explicitly passed
|
||
if [[ "$explicit_install_flag" == false ]]; then
|
||
daemon_installed=false
|
||
web_installed=false
|
||
|
||
if is_component_installed "daemon"; then
|
||
daemon_installed=true
|
||
check_component_permission "daemon"
|
||
fi
|
||
if is_component_installed "web"; then
|
||
web_installed=true
|
||
check_component_permission "web"
|
||
fi
|
||
|
||
# When only one component installed, we wanted to process that one only.
|
||
if [[ "$daemon_installed" == true && "$web_installed" == false ]]; then
|
||
install_daemon=true
|
||
install_web=false
|
||
elif [[ "$daemon_installed" == false && "$web_installed" == true ]]; then
|
||
install_daemon=false
|
||
install_web=true
|
||
else
|
||
install_daemon=true
|
||
install_web=true
|
||
fi
|
||
fi
|
||
}
|
||
|
||
|
||
# Get Distribution & Architecture Info
|
||
detect_os_info() {
|
||
distro="Unknown"
|
||
version="Unknown"
|
||
arch=$(uname -m)
|
||
|
||
# Try primary source
|
||
if [ -f /etc/os-release ]; then
|
||
. /etc/os-release
|
||
distro_id="${ID,,}"
|
||
version_id="${VERSION_ID,,}"
|
||
|
||
case "$distro_id" in
|
||
ubuntu)
|
||
distro="Ubuntu"
|
||
version="$version_id"
|
||
;;
|
||
debian)
|
||
distro="Debian"
|
||
version="$version_id"
|
||
;;
|
||
centos)
|
||
distro="CentOS"
|
||
version="$version_id"
|
||
;;
|
||
rhel*)
|
||
distro="RHEL"
|
||
version="$version_id"
|
||
;;
|
||
arch)
|
||
distro="Arch"
|
||
version="rolling"
|
||
;;
|
||
*)
|
||
distro="${ID:-Unknown}"
|
||
version="$version_id"
|
||
;;
|
||
esac
|
||
fi
|
||
|
||
# Fallbacks for missing or invalid version
|
||
if [[ -z "$version" || "$version" == "unknown" || "$version" == "" ]]; then
|
||
if [ -f /etc/issue ]; then
|
||
version_guess=$(grep -oP '[0-9]+(\.[0-9]+)*' /etc/issue | head -1)
|
||
if [[ -n "$version_guess" ]]; then
|
||
version="$version_guess"
|
||
fi
|
||
fi
|
||
fi
|
||
|
||
# Normalize version: keep only major version
|
||
version_full="$version"
|
||
cprint cyan "检测到操作系统: $distro $version_full"
|
||
cprint cyan "检测到架构: $arch"
|
||
}
|
||
|
||
# Check if all required commands are available
|
||
check_required_commands() {
|
||
local missing=0
|
||
|
||
for cmd in "${required_commands[@]}"; do
|
||
if ! command -v "$cmd" >/dev/null 2>&1; then
|
||
echo "错误:必需的命令 '$cmd' 在PATH中不可用."
|
||
missing=1
|
||
fi
|
||
done
|
||
|
||
if [ "$missing" -ne 0 ]; then
|
||
echo "缺少一个或多个必需的命令。请安装后再试."
|
||
return 1
|
||
fi
|
||
|
||
cprint green "所有必需的命令都可用."
|
||
return 0
|
||
}
|
||
|
||
# Print with specified color and style, fallback to RESET if not supported.
|
||
# Supported colors*: black|red|green|yellow|blue|magenta|cyan|white
|
||
# Supported styles*: bold|underline|italic|clear_line|strikethrough
|
||
# *Note: some style may not necessarily work on all terminals.
|
||
# Example usage:
|
||
# cprint green bold "Installation completed successfully."
|
||
# cprint red underline "Failed to detect required command: wget"
|
||
# cprint yellow "Warning: Disk space is low."
|
||
# cprint underline "Failed to detect required command: wget"
|
||
# cprint bold green underline"Installation completed successfully."
|
||
|
||
cprint() {
|
||
local color=""
|
||
local text=""
|
||
local styles=""
|
||
local disable_prefix=false
|
||
local disable_newline=false
|
||
|
||
while [[ $# -gt 1 ]]; do
|
||
case "$1" in
|
||
black|red|green|yellow|blue|magenta|cyan|white)
|
||
color="$1"
|
||
;;
|
||
bold|underline|italic|clear_line|strikethrough)
|
||
styles+="${STYLES[$1]}"
|
||
;;
|
||
noprefix)
|
||
disable_prefix=true
|
||
;;
|
||
nonl)
|
||
disable_newline=true
|
||
;;
|
||
esac
|
||
shift
|
||
done
|
||
|
||
text="$1"
|
||
|
||
local prefix_text=""
|
||
if [[ "$disable_prefix" != true ]]; then
|
||
local timestamp="[$(date +%H:%M:%S)]"
|
||
local label="[MCSM Installer]"
|
||
prefix_text="${FG_COLORS[white]}$timestamp $label${RESET} "
|
||
fi
|
||
|
||
local prefix=""
|
||
if [[ -n "$color" && "$SUPPORTS_COLOR" = true ]]; then
|
||
prefix+="${FG_COLORS[$color]}"
|
||
fi
|
||
if [[ "$SUPPORTS_STYLE" = true || "$styles" == *"${STYLES[clear_line]}"* ]]; then
|
||
prefix="$styles$prefix"
|
||
fi
|
||
|
||
if [[ "$disable_newline" == true ]]; then
|
||
printf "%b%b%s%b" "$prefix_text" "$prefix" "$text" "$RESET"
|
||
else
|
||
printf "%b%b%s%b\n" "$prefix_text" "$prefix" "$text" "$RESET"
|
||
fi
|
||
}
|
||
|
||
|
||
|
||
|
||
# Permission check before proceed with installation
|
||
permission_barrier() {
|
||
if [[ "$web_installed" == false && "$daemon_installed" == false ]]; then
|
||
cprint cyan "当前没有安装组件-跳过权限检查."
|
||
return 0
|
||
fi
|
||
|
||
for component in web daemon; do
|
||
local is_installed_var="${component}_installed"
|
||
local installed_user_var="${component}_installed_user"
|
||
|
||
if [[ "${!is_installed_var}" == true ]]; then
|
||
local installed_user="${!installed_user_var}"
|
||
|
||
# Step 0: Ensure installed user is detected
|
||
if [[ -z "$installed_user" ]]; then
|
||
cprint red bold "检测到 '$component' 已安装,但无法从其systemd服务文件确定用户."
|
||
cprint red "这可能表示自定义或不支持的服务文件设置."
|
||
cprint red "拒绝执行以避免潜在的冲突."
|
||
exit 1
|
||
fi
|
||
|
||
# Step 1: User match check with optional force override
|
||
if [[ "$installed_user" != "$install_user" ]]; then
|
||
if [[ "$force_permission" == true ]]; then
|
||
cprint yellow bold "权限不匹配 '$component':"
|
||
cprint yellow "以用户身份安装: $installed_user"
|
||
cprint yellow "目标安装用户: $install_user"
|
||
cprint yellow "用户不匹配,但设置了--force-permission。继续和更新权限…"
|
||
sleep 3
|
||
else
|
||
cprint red bold "权限不匹配 '$component':"
|
||
cprint red "以用户身份安装: $installed_user"
|
||
cprint red "目标安装用户: $install_user"
|
||
cprint red "用户不匹配,但设置了--force-permission。继续和更新权限..."
|
||
exit 1
|
||
fi
|
||
else
|
||
cprint green bold "权限检查已通过: '$installed_user' 匹配目标用户."
|
||
fi
|
||
|
||
fi
|
||
done
|
||
|
||
# Step 2: Directory ownership check
|
||
local dir_owner
|
||
dir_owner=$(stat -c '%U' "$install_dir" 2>/dev/null)
|
||
|
||
if [[ -z "$dir_owner" ]]; then
|
||
cprint red bold "无法确定安装目录的所有者: $install_dir"
|
||
exit 1
|
||
fi
|
||
|
||
if [[ "$dir_owner" != "$install_user" ]]; then
|
||
if [[ "$force_permission" == true ]]; then
|
||
cprint yellow bold "安装目录所有权不匹配:"
|
||
cprint yellow " 目录: $install_dir"
|
||
cprint yellow " 归: $dir_owner"
|
||
cprint yellow " 预期: $install_user"
|
||
cprint yellow " --force-permission设置。尽管不匹配,但继续."
|
||
sleep 3
|
||
else
|
||
cprint red bold "安装目录所有权不匹配:"
|
||
cprint red " 目录: $install_dir"
|
||
cprint red " 归: $dir_owner"
|
||
cprint red " 预期: $install_user"
|
||
exit 1
|
||
fi
|
||
else
|
||
cprint green bold "安装目录所有权检查通过: '$install_dir' is owned by '$install_user'."
|
||
fi
|
||
|
||
cprint green bold "验证了权限和所有权。继续."
|
||
return 0
|
||
}
|
||
|
||
|
||
|
||
# Map OS arch with actual Node.js Arch name
|
||
# This function should be placed after var arch has been assigned a valid value.
|
||
resolve_node_arch() {
|
||
case "$arch" in
|
||
x86_64)
|
||
node_arch="x64"
|
||
;;
|
||
aarch64)
|
||
node_arch="arm64"
|
||
;;
|
||
armv7l)
|
||
node_arch="armv7l"
|
||
;;
|
||
*)
|
||
cprint red bold "Node.js不支持的架构: $arch"
|
||
return 1
|
||
;;
|
||
esac
|
||
|
||
# Assign node_path based on resolved arch and current version/install dir
|
||
node_path="${node_install_dir}/node-${node_version}-linux-${node_arch}"
|
||
|
||
cprint cyan "解析了Node.js架构: $node_arch"
|
||
cprint cyan "Node.js安装路径: $node_path"
|
||
}
|
||
|
||
# Check if Node.js at PATH is valid.
|
||
# This function check Node.js version + NPM (if Node.js valid)
|
||
verify_node_at_path() {
|
||
local node_path="$1"
|
||
node_bin_path="$node_path/bin/node"
|
||
npm_bin_path="$node_path/bin/npm"
|
||
|
||
# Node binary missing
|
||
if [ ! -x "$node_bin_path" ]; then
|
||
return 1
|
||
fi
|
||
|
||
local installed_ver
|
||
installed_ver="$("$node_bin_path" -v 2>/dev/null | sed 's/^v//')"
|
||
|
||
if [[ -z "$installed_ver" ]]; then
|
||
return 1
|
||
fi
|
||
|
||
if [ "$strict_node_version_check" = true ]; then
|
||
if [[ "$installed_ver" != "$required_node_ver" ]]; then
|
||
return 3
|
||
fi
|
||
else
|
||
local cmp
|
||
cmp=$(printf "%s\n%s\n" "$required_node_ver" "$installed_ver" | sort -V | head -1)
|
||
if [[ "$cmp" != "$required_node_ver" ]]; then
|
||
return 2
|
||
fi
|
||
fi
|
||
|
||
# Check if npm exists and works using node (not $PATH/npm)
|
||
if [ ! -x "$npm_bin_path" ]; then
|
||
return 4
|
||
fi
|
||
|
||
# Use node to run npm.js directly, in case env is broken
|
||
local npm_version
|
||
npm_version="$("$node_bin_path" "$npm_bin_path" --version 2>/dev/null)"
|
||
if [[ -z "$npm_version" ]]; then
|
||
return 4
|
||
fi
|
||
|
||
return 0
|
||
}
|
||
|
||
|
||
# Node.js pre-check. check if we need to install Node.js before installer run.
|
||
# Use postcheck_node_after_install() to check after install.
|
||
check_node_installed() {
|
||
verify_node_at_path "$node_path"
|
||
local result=$?
|
||
|
||
case $result in
|
||
0)
|
||
cprint green bold "Node.js和npm在 $node_path (版本 $required_node_ver 或兼容)"
|
||
install_node=false
|
||
;;
|
||
1)
|
||
cprint yellow bold "Node.js二进制文件未找到或无法使用 $node_path"
|
||
install_node=true
|
||
;;
|
||
2)
|
||
cprint red bold "Node.js版本 $node_path 太老. 要求: >= $required_node_ver"
|
||
install_node=true
|
||
;;
|
||
3)
|
||
cprint red bold "Node.js版本不匹配。要求: $required_node_ver, 发现了其他的Node.js版本."
|
||
install_node=true
|
||
;;
|
||
4)
|
||
cprint red bold "Node.js存在,但npm缺失或损坏."
|
||
install_node=true
|
||
;;
|
||
*)
|
||
cprint red bold "Node验证中出现意外错误."
|
||
install_node=true
|
||
;;
|
||
esac
|
||
}
|
||
|
||
# Node.js post-check. check if Node.js is valid after install.
|
||
postcheck_node_after_install() {
|
||
verify_node_at_path "$node_path"
|
||
if [[ $? -ne 0 ]]; then
|
||
cprint red bold "Node.js安装失败或无效 $node_path"
|
||
return 1
|
||
else
|
||
cprint green bold "Node.js的安装和运行在 $node_path"
|
||
return 0
|
||
fi
|
||
}
|
||
|
||
# Install Node.js and check
|
||
install_node() {
|
||
local archive_name="node-${node_version}-linux-${node_arch}.tar.xz"
|
||
local target_dir="${node_install_dir}/node-${node_version}-linux-${node_arch}"
|
||
local archive_path="${node_install_dir}/${archive_name}"
|
||
local download_url="${node_download_url_base}${node_version}/${archive_name}"
|
||
local fallback="$node_download_fallback"
|
||
|
||
cprint cyan bold "安装Node.js $node_version 架构: $node_arch"
|
||
|
||
mkdir -p "$node_install_dir" || {
|
||
cprint red bold "创建Node安装目录失败: $node_install_dir"
|
||
return 1
|
||
}
|
||
|
||
# Download
|
||
cprint cyan "下载Node.js: $download_url"
|
||
if ! wget --progress=bar:force -O "$archive_path" "$download_url"; then
|
||
cprint yellow "主下载失败。尝试备用下载……"
|
||
|
||
if [[ -n "$fallback" ]]; then
|
||
if [[ "$fallback" =~ ^https?:// ]]; then
|
||
cprint cyan "从备用URL下载: $fallback"
|
||
if ! wget --progress=bar:force -O "$archive_path" "$fallback"; then
|
||
cprint red bold "备用下载失败: $fallback"
|
||
return 1
|
||
fi
|
||
elif [ -f "$fallback" ]; then
|
||
cprint cyan "从本地备份进行复制: $fallback"
|
||
cp "$fallback" "$archive_path" || {
|
||
cprint red bold "复制备用Node.js存档失败 $fallback"
|
||
return 1
|
||
}
|
||
else
|
||
cprint red bold "无效的备用路径: $fallback"
|
||
return 1
|
||
fi
|
||
else
|
||
cprint red bold "没有配置备用源。不能继续进行."
|
||
return 1
|
||
fi
|
||
fi
|
||
|
||
# Extract archive
|
||
cprint cyan "提取Node.js存档..."
|
||
if ! tar -xf "$archive_path" -C "$node_install_dir"; then
|
||
cprint red bold "提取Node.js文件失败."
|
||
return 1
|
||
fi
|
||
|
||
chmod -R a+rx "$target_dir" || {
|
||
cprint red bold "在Node.js文件上设置执行权限失败."
|
||
return 1
|
||
}
|
||
|
||
verify_node_at_path "$target_dir"
|
||
local result=$?
|
||
if [[ $result -ne 0 ]]; then
|
||
cprint red bold "Node.js安装验证失败."
|
||
return 1
|
||
fi
|
||
|
||
cprint cyan "清理文件……"
|
||
rm -f "$archive_path"
|
||
|
||
cprint green bold "Node.js $node_version 安装成功 $target_dir"
|
||
# Save resolved binary paths to global variables
|
||
node_bin_path="${target_dir}/bin/node"
|
||
npm_bin_path="${target_dir}/bin/npm"
|
||
|
||
cprint green "Node.js 二进制文件: $node_bin_path"
|
||
cprint green "npm 二进制文件: $npm_bin_path"
|
||
return 0
|
||
}
|
||
|
||
# Function to download MCSM package. fetch from primary URL first, then fallback URL.
|
||
# This function only put extracted file(s) into install_dir, it does not perform the actual update.
|
||
download_mcsm() {
|
||
local archive_name="$package_name"
|
||
local archive_path="${tmp_dir}/${archive_name}"
|
||
local primary_url="${download_base_url}${archive_name}"
|
||
local fallback="$download_fallback_url"
|
||
|
||
cprint cyan bold "下载MCSManager安装包…"
|
||
|
||
# Step 1: Try downloading from primary URL
|
||
if ! wget --progress=bar:force -O "$archive_path" "$primary_url"; then
|
||
cprint yellow "主下载失败。尝试备用资源…"
|
||
|
||
if [[ -z "$fallback" ]]; then
|
||
cprint red bold "没有指定备用URL或路径."
|
||
return 1
|
||
fi
|
||
|
||
if [[ "$fallback" =~ ^https?:// ]]; then
|
||
if ! wget --progress=bar:force -O "$archive_path" "$fallback"; then
|
||
cprint red bold "备用下载失败 $fallback"
|
||
return 1
|
||
fi
|
||
elif [[ -f "$fallback" ]]; then
|
||
cp "$fallback" "$archive_path" || {
|
||
cprint red bold "未能复制备用文件 $fallback"
|
||
return 1
|
||
}
|
||
else
|
||
cprint red bold "备用路径无效: $fallback"
|
||
return 1
|
||
fi
|
||
fi
|
||
|
||
# Step 2: Generate extract directory
|
||
local suffix
|
||
suffix=$(tr -dc 'a-z0-9' </dev/urandom | head -c 4)
|
||
local extracted_tmp_path="${tmp_dir}/mcsm_${suffix}"
|
||
|
||
if [[ -e "$extracted_tmp_path" ]]; then
|
||
cprint red bold "临时提取路径已存在: $extracted_tmp_path"
|
||
return 1
|
||
fi
|
||
|
||
mkdir -p "$extracted_tmp_path" || {
|
||
cprint red bold "创建临时解压目录失败: $extracted_tmp_path"
|
||
return 1
|
||
}
|
||
|
||
cprint cyan "将存档提取到 $extracted_tmp_path..."
|
||
if ! tar -xzf "$archive_path" -C "$extracted_tmp_path"; then
|
||
cprint red bold "提取存档失败."
|
||
rm -rf "$extracted_tmp_path"
|
||
return 1
|
||
fi
|
||
|
||
rm -f "$archive_path"
|
||
|
||
# Step 3: Move the entire extracted directory to install_dir
|
||
install_tmp_dir="${install_dir}/mcsm_${suffix}"
|
||
|
||
if [[ -e "$install_tmp_dir" ]]; then
|
||
cprint red bold "安装目标已经存在 $install_tmp_dir"
|
||
cprint red " 请在继续之前删除或重命名它."
|
||
return 1
|
||
fi
|
||
|
||
mv "$extracted_tmp_path" "$install_tmp_dir" || {
|
||
cprint red bold "未能将解压缩的文件移动到 $install_tmp_dir"
|
||
return 1
|
||
}
|
||
|
||
cprint green bold "MCSManager源提取并移动到: $install_tmp_dir"
|
||
return 0
|
||
}
|
||
|
||
# Prepare user if needed
|
||
prepare_user() {
|
||
if [[ "$install_user" == "root" ]]; then
|
||
cprint cyan "安装用户是'root' -跳过用户创建."
|
||
return 0
|
||
fi
|
||
|
||
# Check if user already exists
|
||
if id "$install_user" &>/dev/null; then
|
||
cprint green "用户 '$install_user' 已经存在."
|
||
else
|
||
cprint cyan "创建系统用户: $install_user (无登录,无密码)..."
|
||
if ! useradd --system --home "$install_dir" --shell /usr/sbin/nologin "$install_user"; then
|
||
cprint red bold "创建用户失败: $install_user"
|
||
exit 1
|
||
fi
|
||
cprint green "用户 '$install_user' 已创建."
|
||
fi
|
||
|
||
|
||
# Docker integration
|
||
if command -v docker &>/dev/null; then
|
||
cprint cyan "Docker已被安装 -检查组分配…"
|
||
|
||
if getent group docker &>/dev/null; then
|
||
if id -nG "$install_user" | grep -qw docker; then
|
||
cprint green "用户 '$install_user' 已经在“docker”组中."
|
||
else
|
||
cprint cyan "添加用户 '$install_user' 到 'docker' 组..."
|
||
if usermod -aG docker "$install_user"; then
|
||
cprint green "授予的Docker组访问权限 '$install_user'."
|
||
else
|
||
cprint red "未能添加 '$install_user' 给“Docker”组。这个用户可能无法使用Docker."
|
||
fi
|
||
fi
|
||
else
|
||
cprint red "安装了Docker,但没有找到Docker组。跳过组分配."
|
||
fi
|
||
else
|
||
cprint yellow "未安装Docker -跳过Docker组配置."
|
||
fi
|
||
|
||
return 0
|
||
}
|
||
# Function to stop MCSM services if they exist
|
||
stop_mcsm_services() {
|
||
cprint yellow bold "试图停止mcsm-web和mcsm-daemon服务..."
|
||
|
||
# Attempt to stop mcsm-web
|
||
cprint blue "正在停止 mcsm-web..."
|
||
if systemctl stop mcsm-web; then
|
||
cprint green "mcsm-web 已停止."
|
||
else
|
||
cprint red bold "警告:未能停止mcsm-web(可能不存在或已停止)."
|
||
fi
|
||
|
||
# Attempt to stop mcsm-daemon
|
||
cprint blue "正在停止 mcsm-daemon..."
|
||
if systemctl stop mcsm-daemon; then
|
||
cprint green "mcsm-daemon 已停止."
|
||
else
|
||
cprint red bold "警告:未能停止mcsm-daemon(可能不存在或已停止)."
|
||
fi
|
||
}
|
||
# Prepare file & permissions before install.
|
||
mcsm_install_prepare() {
|
||
|
||
# Stop service if existed
|
||
stop_mcsm_services
|
||
|
||
if [[ ! -d "$install_tmp_dir" ]]; then
|
||
cprint red bold "临时安装目录不存在: $install_tmp_dir"
|
||
exit 1
|
||
fi
|
||
|
||
cprint cyan "改变所有权 $install_tmp_dir 到用户 '$install_user'..."
|
||
chown -R "$install_user":"$install_user" "$install_tmp_dir" || {
|
||
cprint red bold "所有权变更失败 $install_tmp_dir"
|
||
cleanup_install_tmp
|
||
exit 1
|
||
}
|
||
|
||
# Normalize install_dir to ensure it ends with a slash
|
||
[[ "${install_dir}" != */ ]] && install_dir="${install_dir}/"
|
||
|
||
if [[ "$web_installed" == false && "$daemon_installed" == false ]]; then
|
||
cprint cyan "没有检测到现有组件-跳过数据备份/清理."
|
||
return 0
|
||
fi
|
||
|
||
cprint green bold "已成功准备现有组件."
|
||
return 0
|
||
}
|
||
|
||
# Install or update a component
|
||
install_component() {
|
||
local component="$1"
|
||
local target_path="${install_dir}${component}"
|
||
local backup_data_path="${install_dir}${backup_prefix}${component}"
|
||
local source_path="${install_tmp_dir}/mcsmanager/${component}"
|
||
|
||
cprint cyan bold "安装/更新组件: $component"
|
||
|
||
# Step 1: Move new component to install_dir
|
||
if [[ ! -d "$source_path" ]]; then
|
||
cprint red bold "找不到源目录: $source_path"
|
||
cleanup_install_tmp
|
||
exit 1
|
||
fi
|
||
|
||
if cp -a "$source_path"/. "$target_path"; then
|
||
cprint green "更新的文件 $source_path → $target_path"
|
||
rm -rf "$source_path"
|
||
else
|
||
cprint red bold "更新文件失败 $source_path → $target_path"
|
||
cleanup_install_tmp
|
||
exit 1
|
||
fi
|
||
cprint green "已移动 $component 到 $target_path"
|
||
|
||
|
||
# Step 3: Install NPM dependencies
|
||
if [[ ! -x "$npm_bin_path" ]]; then
|
||
cprint red bold "找不到npm二进制文件或无法执行: $npm_bin_path"
|
||
cleanup_install_tmp
|
||
exit 1
|
||
fi
|
||
|
||
cprint cyan "正在使用npm安装依赖库 $component ..."
|
||
pushd "$target_path" >/dev/null || {
|
||
cprint red bold "更改目录失败 $target_path"
|
||
cleanup_install_tmp
|
||
exit 1
|
||
}
|
||
|
||
if ! "$node_bin_path" "$npm_bin_path" install --no-audit --no-fund --loglevel=warn; then
|
||
cprint red bold "NPM依赖项安装失败 $component"
|
||
popd >/dev/null
|
||
cleanup_install_tmp
|
||
exit 1
|
||
fi
|
||
|
||
popd >/dev/null
|
||
cprint green bold "组件 '$component' 安装/更新成功."
|
||
}
|
||
|
||
# Create systemd service for a given component.
|
||
# This will overwrite the existing service file.
|
||
create_systemd_service() {
|
||
local component="$1"
|
||
local service_path="${systemd_file}${component}.service"
|
||
local working_dir="${install_dir}${component}"
|
||
local exec="${node_bin_path} app.js"
|
||
|
||
if [[ ! -d "$working_dir" ]]; then
|
||
cprint red bold "找不到组件目录: $working_dir"
|
||
cleanup_install_tmp
|
||
return 1
|
||
fi
|
||
|
||
cprint cyan "创建systemd服务 '$component'..."
|
||
|
||
cat > "$service_path" <<EOF
|
||
[Unit]
|
||
Description=MCSManager-${component^}
|
||
After=network.target
|
||
|
||
[Service]
|
||
Type=simple
|
||
WorkingDirectory=${working_dir}
|
||
ExecStart=${exec}
|
||
ExecReload=/bin/kill -s HUP \$MAINPID
|
||
ExecStop=/bin/kill -s TERM \$MAINPID
|
||
Restart=on-failure
|
||
User=${install_user}
|
||
Environment="PATH=${PATH}"
|
||
Environment="NODE_ENV=production"
|
||
|
||
[Install]
|
||
WantedBy=multi-user.target
|
||
EOF
|
||
|
||
if [[ $? -ne 0 ]]; then
|
||
cprint red bold "Failed to write service file: $service_path"
|
||
cleanup_install_tmp
|
||
return 1
|
||
fi
|
||
|
||
chmod 644 "$service_path"
|
||
cprint green "创建的systemd单元: $service_path"
|
||
return 0
|
||
}
|
||
|
||
# Extract daemon key and/or http port
|
||
extract_component_info() {
|
||
# DAEMON SECTION
|
||
if [[ "$install_daemon" == true ]]; then
|
||
local daemon_service="mcsm-daemon.service"
|
||
local daemon_path="${install_dir}/daemon"
|
||
local daemon_config_path="${daemon_path}/${daemon_key_config_subpath}"
|
||
|
||
cprint cyan bold "启动守护进程服务..."
|
||
if systemctl restart "$daemon_service"; then
|
||
cprint green "守护进程服务已启动."
|
||
|
||
sleep 3 # Allow service to init and write configs
|
||
|
||
if [[ -f "$daemon_config_path" ]]; then
|
||
daemon_key=$(grep -oP '"key"\s*:\s*"\K[^"]+' "$daemon_config_path")
|
||
daemon_port=$(grep -oP '"port"\s*:\s*\K[0-9]+' "$daemon_config_path")
|
||
|
||
if [[ -n "$daemon_key" ]]; then
|
||
cprint green "提取的守护进程密钥: $daemon_key"
|
||
else
|
||
cprint red "提取守护进程密钥失败: $daemon_config_path"
|
||
fi
|
||
|
||
if [[ -n "$daemon_port" ]]; then
|
||
cprint green "提取的守护进程端口: $daemon_port"
|
||
else
|
||
cprint red "提取守护进程端口失败: $daemon_config_path"
|
||
fi
|
||
else
|
||
cprint red "没有找到守护进程配置文件: $daemon_config_path"
|
||
fi
|
||
else
|
||
cprint red bold "启动守护进程服务失败: $daemon_service"
|
||
fi
|
||
fi
|
||
|
||
# WEB SECTION
|
||
if [[ "$install_web" == true ]]; then
|
||
local web_service="mcsm-web.service"
|
||
local web_path="${install_dir}/web"
|
||
local web_config_path="${web_path}/${web_port_config_subpath}"
|
||
|
||
cprint cyan bold "正在启动面板服务..."
|
||
if systemctl restart "$web_service"; then
|
||
cprint green "面板服务已启动."
|
||
|
||
sleep 3 # Allow time to populate config
|
||
|
||
if [[ -f "$web_config_path" ]]; then
|
||
web_port=$(grep -oP '"httpPort"\s*:\s*\K[0-9]+' "$web_config_path")
|
||
if [[ -n "$web_port" ]]; then
|
||
cprint green "提取的面板端口: $web_port"
|
||
else
|
||
cprint red "提取面板端口失败: $web_config_path"
|
||
fi
|
||
else
|
||
cprint red "面板配置文件未找到: $web_config_path"
|
||
fi
|
||
else
|
||
cprint red bold "启动面板服务失败: $web_service"
|
||
fi
|
||
fi
|
||
}
|
||
|
||
cleanup_install_tmp() {
|
||
if [[ -n "$install_tmp_dir" && -d "$install_tmp_dir" ]]; then
|
||
if rm -rf "$install_tmp_dir"; then
|
||
cprint green "已清理临时安装文件夹: $install_tmp_dir"
|
||
else
|
||
cprint red "删除临时文件夹失败: $install_tmp_dir"
|
||
fi
|
||
fi
|
||
}
|
||
|
||
print_install_result() {
|
||
# 清空屏幕
|
||
clear || true
|
||
|
||
# 打印 ASCII 横幅
|
||
cprint white noprefix "______ _______________________ ___"
|
||
cprint white noprefix "___ |/ /_ ____/_ ___/__ |/ /_____ _____________ _______ _____________"
|
||
cprint white noprefix "__ /|_/ /_ / _____ \__ /|_/ /_ __ \`/_ __ \ __ \`/_ __ \`/ _ \_ ___/"
|
||
cprint white noprefix "_ / / / / /___ ____/ /_ / / / / /_/ /_ / / / /_/ /_ /_/ // __/ /"
|
||
cprint white noprefix "/_/ /_/ \____/ /____/ /_/ /_/ \__,_/ /_/ /_/\__,_/ _\__, / \___//_/"
|
||
echo ""
|
||
# status summary
|
||
cprint yellow noprefix "安装/更新组件:"
|
||
if [[ "$install_daemon" == true && -n "$daemon_key" && -n "$daemon_port" ]]; then
|
||
cprint white noprefix "Daemon"
|
||
elif [[ "$install_daemon" == true ]]; then
|
||
cprint white noprefix nonl "Daemon "
|
||
cprint yellow noprefix "(部分,未完全检测到配置)"
|
||
fi
|
||
|
||
if [[ "$install_web" == true && -n "$web_port" ]]; then
|
||
cprint white noprefix "Web"
|
||
elif [[ "$install_web" == true ]]; then
|
||
cprint white noprefix nonl "Web "
|
||
cprint yellow noprefix "(部分,未完全检测到配置)"
|
||
fi
|
||
|
||
echo ""
|
||
|
||
# Local IP detection
|
||
local ip_address
|
||
ip_address=$(hostname -I 2>/dev/null | awk '{print $1}')
|
||
[[ -z "$ip_address" ]] && ip_address="你的IP"
|
||
|
||
# Daemon info
|
||
if [[ "$install_daemon" == true ]]; then
|
||
local daemon_address="ws://$ip_address:${daemon_port:-Failed to Retrieve from Config file}"
|
||
local daemon_key_display="${daemon_key:-Failed to Retrieve from Config file}"
|
||
|
||
cprint yellow noprefix "守护进程地址:"
|
||
cprint white noprefix " $daemon_address"
|
||
cprint yellow noprefix "守护进程秘钥:"
|
||
cprint white noprefix " $daemon_key_display"
|
||
echo ""
|
||
fi
|
||
|
||
# Web info
|
||
if [[ "$install_web" == true ]]; then
|
||
local web_address="http://$ip_address:${web_port:-Failed to Retrieve from Config file}"
|
||
cprint yellow noprefix "HTTP面板地址:"
|
||
cprint white noprefix nonl " $web_address "
|
||
cprint yellow noprefix "(在你的浏览器中打开)"
|
||
echo ""
|
||
fi
|
||
|
||
# Port guidance
|
||
cprint yellow noprefix "注意:"
|
||
cprint white noprefix " 确保防火墙放行上述端口."
|
||
cprint white noprefix " 如果从外部网络访问,您可能需要在路由器上配置端口转发."
|
||
echo ""
|
||
|
||
# Service management help
|
||
cprint yellow noprefix "MCSManager管理命令:"
|
||
if [[ "$install_daemon" == true ]]; then
|
||
cprint white noprefix nonl " systemctl start "
|
||
cprint yellow noprefix "mcsm-daemon.service"
|
||
cprint white noprefix nonl " systemctl stop "
|
||
cprint yellow noprefix "mcsm-daemon.service"
|
||
cprint white noprefix nonl " systemctl restart "
|
||
cprint yellow noprefix "mcsm-daemon.service"
|
||
cprint white noprefix nonl " systemctl status "
|
||
cprint yellow noprefix "mcsm-daemon.service"
|
||
fi
|
||
if [[ "$install_web" == true ]]; then
|
||
cprint white noprefix nonl " systemctl start "
|
||
cprint yellow noprefix "mcsm-web.service"
|
||
cprint white noprefix nonl " systemctl stop "
|
||
cprint yellow noprefix "mcsm-web.service"
|
||
cprint white noprefix nonl " systemctl restart "
|
||
cprint yellow noprefix "mcsm-web.service"
|
||
cprint white noprefix nonl " systemctl status "
|
||
cprint yellow noprefix "mcsm-web.service"
|
||
fi
|
||
echo ""
|
||
|
||
# Official doc
|
||
cprint yellow noprefix "官方文档:"
|
||
cprint white noprefix " https://docs.mcsmanager.com/zh_cn/"
|
||
echo ""
|
||
|
||
# HTTPS support
|
||
cprint yellow noprefix "需要HTTPS?"
|
||
cprint white noprefix " 为了开启HTTPS安全访问,需要配置反向代理:"
|
||
cprint white noprefix " https://docs.mcsmanager.com/zh_cn/ops/proxy_https.html"
|
||
echo ""
|
||
|
||
if [[ "$force_permission" == true ]]; then
|
||
cprint red noprefix "[重点] 您选择在安装期间重写权限."
|
||
cprint red noprefix " 你可能需要运行: chown -R $install_user <path> 手动更新权限."
|
||
fi
|
||
|
||
# Closing message
|
||
cprint green noprefix "安装完成。享受使用MCSManager管理服务器的乐趣!"
|
||
echo ""
|
||
}
|
||
|
||
install_mcsm() {
|
||
local components=()
|
||
|
||
if [[ "$install_web" == true ]]; then
|
||
install_component "web"
|
||
create_systemd_service "web"
|
||
components+=("web")
|
||
fi
|
||
|
||
if [[ "$install_daemon" == true ]]; then
|
||
install_component "daemon"
|
||
create_systemd_service "daemon"
|
||
components+=("daemon")
|
||
fi
|
||
|
||
# Reload systemd after any service file changes
|
||
if (( ${#components[@]} > 0 )); then
|
||
cprint cyan "重新加载systemd守护进程..."
|
||
# systemctl daemon-reexec
|
||
systemctl daemon-reload
|
||
|
||
for comp in "${components[@]}"; do
|
||
local svc="mcsm-${comp}.service"
|
||
|
||
cprint cyan "启用服务: $svc"
|
||
if systemctl enable "$svc" &>/dev/null; then
|
||
cprint green "已启用服务: $svc"
|
||
else
|
||
cprint red bold "启用服务失败: $svc"
|
||
cleanup_install_tmp
|
||
exit 1
|
||
fi
|
||
done
|
||
fi
|
||
|
||
# Clean tmp dir
|
||
cleanup_install_tmp
|
||
# Extract installed component info
|
||
safe_run extract_component_info "未能从已安装的服务中提取运行时信息"
|
||
safe_run print_install_result "未能打印安装结果"
|
||
|
||
}
|
||
|
||
main() {
|
||
trap 'echo "发生意外错误."; exit 99' ERR
|
||
safe_run detect_terminal_capabilities "检测终端功能失败"
|
||
safe_run check_root "脚本必须以root身份运行"
|
||
safe_run parse_args "解析参数失败" "$@"
|
||
safe_run detect_os_info "OS检测失败"
|
||
# To be moved to a master pre check function.
|
||
safe_run resolve_node_arch "解析Node.js架构失败"
|
||
|
||
safe_run check_required_commands "缺少必要的系统命令"
|
||
|
||
safe_run check_node_installed "在预期目录上检测到Node.js或npm失败。Node.js将被安装."
|
||
if [ "$install_node" = true ]; then
|
||
safe_run install_node "Node.js安装失败"
|
||
fi
|
||
|
||
safe_run permission_barrier "权限验证失败-中止安装"
|
||
|
||
safe_run prepare_user "准备用户权限失败。处理步骤."
|
||
|
||
safe_run download_mcsm "获取MCSManager源失败。处理步骤"
|
||
safe_run mcsm_install_prepare "准备安装时出错"
|
||
|
||
safe_run install_mcsm "未能安装 MCSManager"
|
||
}
|
||
main "$@" |