refactor(install): 统一安装脚本日志风格与UI样式
- 新增日志图标、分隔符和格式化工具函数 - 统一所有安装脚本的输出格式与视觉风格 - 使用统一的图标和配色方案优化交互体验 - 重构命令行输入提示样式,添加视觉标识
This commit is contained in:
+63
-94
@@ -20,41 +20,51 @@
|
|||||||
|
|
||||||
set -e
|
set -e
|
||||||
|
|
||||||
# 颜色定义
|
|
||||||
RED='\033[0;31m'
|
RED='\033[0;31m'
|
||||||
GREEN='\033[0;32m'
|
GREEN='\033[0;32m'
|
||||||
YELLOW='\033[0;33m'
|
YELLOW='\033[0;33m'
|
||||||
CYAN='\033[0;36m'
|
CYAN='\033[0;36m'
|
||||||
BRIGHT='\033[1m'
|
BRIGHT='\033[1m'
|
||||||
|
DIM='\033[2m'
|
||||||
|
MAGENTA='\033[0;35m'
|
||||||
|
WHITE='\033[0;37m'
|
||||||
RESET='\033[0m'
|
RESET='\033[0m'
|
||||||
|
|
||||||
# 日志函数
|
PIPE="│"
|
||||||
log_info() { echo -e "${CYAN}ℹ${RESET} $1"; }
|
ARROW="▶"
|
||||||
log_success() { echo -e "${GREEN}✓${RESET} $1"; }
|
POINTER="❯"
|
||||||
log_warning() { echo -e "${YELLOW}⚠${RESET} $1"; }
|
DIAMOND="◆"
|
||||||
log_error() { echo -e "${RED}✗${RESET} $1"; }
|
CHECK="✔"
|
||||||
log_step() { echo -e "\n${BRIGHT}${CYAN}▶ $1${RESET}"; }
|
CROSS="✖"
|
||||||
log_divider() { echo -e "${YELLOW}$(printf '─%.0s' {1..60})${RESET}"; }
|
WARN="⚠"
|
||||||
|
INFO="ℹ"
|
||||||
|
|
||||||
|
log_info() { echo -e " ${CYAN}${INFO}${RESET} $1"; }
|
||||||
|
log_success() { echo -e " ${GREEN}${CHECK}${RESET} $1"; }
|
||||||
|
log_warning() { echo -e " ${YELLOW}${WARN}${RESET} $1"; }
|
||||||
|
log_error() { echo -e " ${RED}${CROSS}${RESET} $1"; }
|
||||||
|
log_step() { echo -e "\n ${BRIGHT}${CYAN}${ARROW}${RESET} ${BRIGHT}$1${RESET}"; }
|
||||||
|
log_divider() { echo -e " ${DIM}$(printf '─%.0s' {1..56})${RESET}"; }
|
||||||
|
log_thick_divider() { echo -e " ${DIM}$(printf '═%.0s' {1..56})${RESET}"; }
|
||||||
|
|
||||||
# 脚本版本
|
|
||||||
SCRIPT_VERSION="1.0.0"
|
SCRIPT_VERSION="1.0.0"
|
||||||
MIN_NODE_VERSION=16
|
MIN_NODE_VERSION=16
|
||||||
|
|
||||||
# 获取脚本所在目录
|
|
||||||
SCRIPT_DIR="$(cd "$(dirname "${BASH_SOURCE[0]}")" && pwd)"
|
SCRIPT_DIR="$(cd "$(dirname "${BASH_SOURCE[0]}")" && pwd)"
|
||||||
|
|
||||||
# 显示欢迎信息
|
|
||||||
show_banner() {
|
show_banner() {
|
||||||
echo -e "
|
local width=58
|
||||||
${BRIGHT}${CYAN}
|
local line=$(printf '═%.0s' $(seq 1 $width))
|
||||||
╔══════════════════════════════════════════════════════════╗
|
|
||||||
║ IDC设备管理系统 - Linux 安装引导脚本 v${SCRIPT_VERSION} ║
|
echo -e ""
|
||||||
║ Linux Bootstrap Installation Script ║
|
echo -e " ${CYAN}╔${line}╗${RESET}"
|
||||||
╚══════════════════════════════════════════════════════════╝
|
echo -e " ${CYAN}║${BRIGHT}${WHITE} IDC 设备管理系统 - Linux 安装引导脚本${RESET} ${CYAN}║${RESET}"
|
||||||
${RESET}"
|
echo -e " ${CYAN}║${DIM} Linux Bootstrap Installation Script${RESET} ${CYAN}║${RESET}"
|
||||||
|
echo -e " ${CYAN}║$(printf '%*s' $width '')║${RESET}" | sed "s/ / v${SCRIPT_VERSION} /2"
|
||||||
|
echo -e " ${CYAN}╚${line}╝${RESET}"
|
||||||
|
echo -e ""
|
||||||
}
|
}
|
||||||
|
|
||||||
# 检测 Linux 发行版
|
|
||||||
detect_distro() {
|
detect_distro() {
|
||||||
if [ -f /etc/os-release ]; then
|
if [ -f /etc/os-release ]; then
|
||||||
. /etc/os-release
|
. /etc/os-release
|
||||||
@@ -66,12 +76,10 @@ detect_distro() {
|
|||||||
fi
|
fi
|
||||||
}
|
}
|
||||||
|
|
||||||
# 检查是否为 root 用户
|
|
||||||
is_root() {
|
is_root() {
|
||||||
[ "$(id -u)" -eq 0 ]
|
[ "$(id -u)" -eq 0 ]
|
||||||
}
|
}
|
||||||
|
|
||||||
# 获取 sudo 前缀
|
|
||||||
get_sudo() {
|
get_sudo() {
|
||||||
if is_root; then
|
if is_root; then
|
||||||
echo ""
|
echo ""
|
||||||
@@ -80,12 +88,10 @@ get_sudo() {
|
|||||||
fi
|
fi
|
||||||
}
|
}
|
||||||
|
|
||||||
# 检查命令是否存在
|
|
||||||
command_exists() {
|
command_exists() {
|
||||||
command -v "$1" >/dev/null 2>&1
|
command -v "$1" >/dev/null 2>&1
|
||||||
}
|
}
|
||||||
|
|
||||||
# 获取 Node.js 版本号
|
|
||||||
get_node_version() {
|
get_node_version() {
|
||||||
if command_exists node; then
|
if command_exists node; then
|
||||||
node -e "console.log(process.versions.node.split('.')[0])"
|
node -e "console.log(process.versions.node.split('.')[0])"
|
||||||
@@ -94,33 +100,27 @@ get_node_version() {
|
|||||||
fi
|
fi
|
||||||
}
|
}
|
||||||
|
|
||||||
# 检查 Node.js 版本
|
|
||||||
check_node_version() {
|
check_node_version() {
|
||||||
local version=$(get_node_version)
|
local version=$(get_node_version)
|
||||||
[ "$version" -ge "$MIN_NODE_VERSION" ]
|
[ "$version" -ge "$MIN_NODE_VERSION" ]
|
||||||
}
|
}
|
||||||
|
|
||||||
# 安装 Node.js - Ubuntu/Debian
|
|
||||||
install_node_debian() {
|
install_node_debian() {
|
||||||
local sudo=$(get_sudo)
|
local sudo=$(get_sudo)
|
||||||
log_info "使用 NodeSource 安装 Node.js 20.x..."
|
log_info "使用 NodeSource 安装 Node.js 20.x..."
|
||||||
|
|
||||||
# 安装 curl(如果没有)
|
|
||||||
if ! command_exists curl; then
|
if ! command_exists curl; then
|
||||||
log_info "安装 curl..."
|
log_info "安装 curl..."
|
||||||
$sudo apt-get update -qq
|
$sudo apt-get update -qq
|
||||||
$sudo apt-get install -y -qq curl
|
$sudo apt-get install -y -qq curl
|
||||||
fi
|
fi
|
||||||
|
|
||||||
# 添加 NodeSource 源
|
|
||||||
log_info "添加 NodeSource 源..."
|
log_info "添加 NodeSource 源..."
|
||||||
curl -fsSL https://deb.nodesource.com/setup_20.x | $sudo -E bash -
|
curl -fsSL https://deb.nodesource.com/setup_20.x | $sudo -E bash -
|
||||||
|
|
||||||
# 安装 Node.js
|
|
||||||
log_info "安装 Node.js..."
|
log_info "安装 Node.js..."
|
||||||
$sudo apt-get install -y -qq nodejs
|
$sudo apt-get install -y -qq nodejs
|
||||||
|
|
||||||
# 验证安装
|
|
||||||
if command_exists node; then
|
if command_exists node; then
|
||||||
log_success "Node.js $(node -v) 安装成功"
|
log_success "Node.js $(node -v) 安装成功"
|
||||||
return 0
|
return 0
|
||||||
@@ -129,26 +129,21 @@ install_node_debian() {
|
|||||||
fi
|
fi
|
||||||
}
|
}
|
||||||
|
|
||||||
# 安装 Node.js - CentOS/RHEL/Fedora
|
|
||||||
install_node_rhel() {
|
install_node_rhel() {
|
||||||
local sudo=$(get_sudo)
|
local sudo=$(get_sudo)
|
||||||
log_info "使用 NodeSource 安装 Node.js 20.x..."
|
log_info "使用 NodeSource 安装 Node.js 20.x..."
|
||||||
|
|
||||||
# 安装 curl(如果没有)
|
|
||||||
if ! command_exists curl; then
|
if ! command_exists curl; then
|
||||||
log_info "安装 curl..."
|
log_info "安装 curl..."
|
||||||
$sudo yum install -y -q curl
|
$sudo yum install -y -q curl
|
||||||
fi
|
fi
|
||||||
|
|
||||||
# 添加 NodeSource 源
|
|
||||||
log_info "添加 NodeSource 源..."
|
log_info "添加 NodeSource 源..."
|
||||||
curl -fsSL https://rpm.nodesource.com/setup_20.x | $sudo bash -
|
curl -fsSL https://rpm.nodesource.com/setup_20.x | $sudo bash -
|
||||||
|
|
||||||
# 安装 Node.js
|
|
||||||
log_info "安装 Node.js..."
|
log_info "安装 Node.js..."
|
||||||
$sudo yum install -y -q nodejs
|
$sudo yum install -y -q nodejs
|
||||||
|
|
||||||
# 验证安装
|
|
||||||
if command_exists node; then
|
if command_exists node; then
|
||||||
log_success "Node.js $(node -v) 安装成功"
|
log_success "Node.js $(node -v) 安装成功"
|
||||||
return 0
|
return 0
|
||||||
@@ -157,7 +152,6 @@ install_node_rhel() {
|
|||||||
fi
|
fi
|
||||||
}
|
}
|
||||||
|
|
||||||
# 安装 Node.js - Arch Linux
|
|
||||||
install_node_arch() {
|
install_node_arch() {
|
||||||
local sudo=$(get_sudo)
|
local sudo=$(get_sudo)
|
||||||
log_info "使用 pacman 安装 Node.js..."
|
log_info "使用 pacman 安装 Node.js..."
|
||||||
@@ -172,21 +166,17 @@ install_node_arch() {
|
|||||||
fi
|
fi
|
||||||
}
|
}
|
||||||
|
|
||||||
# 安装 Node.js - 使用 nvm
|
|
||||||
install_node_nvm() {
|
install_node_nvm() {
|
||||||
log_info "使用 nvm 安装 Node.js..."
|
log_info "使用 nvm 安装 Node.js..."
|
||||||
|
|
||||||
# 安装 nvm
|
|
||||||
if [ ! -d "$HOME/.nvm" ]; then
|
if [ ! -d "$HOME/.nvm" ]; then
|
||||||
log_info "安装 nvm..."
|
log_info "安装 nvm..."
|
||||||
curl -o- https://raw.githubusercontent.com/nvm-sh/nvm/v0.39.7/install.sh | bash
|
curl -o- https://raw.githubusercontent.com/nvm-sh/nvm/v0.39.7/install.sh | bash
|
||||||
fi
|
fi
|
||||||
|
|
||||||
# 加载 nvm
|
|
||||||
export NVM_DIR="$HOME/.nvm"
|
export NVM_DIR="$HOME/.nvm"
|
||||||
[ -s "$NVM_DIR/nvm.sh" ] && \. "$NVM_DIR/nvm.sh"
|
[ -s "$NVM_DIR/nvm.sh" ] && \. "$NVM_DIR/nvm.sh"
|
||||||
|
|
||||||
# 安装 Node.js 20
|
|
||||||
log_info "安装 Node.js 20..."
|
log_info "安装 Node.js 20..."
|
||||||
nvm install 20
|
nvm install 20
|
||||||
nvm use 20
|
nvm use 20
|
||||||
@@ -200,7 +190,6 @@ install_node_nvm() {
|
|||||||
fi
|
fi
|
||||||
}
|
}
|
||||||
|
|
||||||
# 自动安装 Node.js
|
|
||||||
auto_install_node() {
|
auto_install_node() {
|
||||||
local distro=$(detect_distro)
|
local distro=$(detect_distro)
|
||||||
|
|
||||||
@@ -224,37 +213,33 @@ auto_install_node() {
|
|||||||
esac
|
esac
|
||||||
}
|
}
|
||||||
|
|
||||||
# 显示手动安装指引
|
|
||||||
show_manual_install_guide() {
|
show_manual_install_guide() {
|
||||||
echo -e "
|
echo -e ""
|
||||||
${BRIGHT}Node.js 手动安装指引:${RESET}
|
echo -e " ${BRIGHT}${MAGENTA}${DIAMOND}${RESET} ${BRIGHT}Node.js 手动安装指引${RESET}"
|
||||||
|
log_divider()
|
||||||
|
|
||||||
${YELLOW}Ubuntu/Debian:${RESET}
|
echo -e " ${PIPE} ${YELLOW}Ubuntu/Debian:${RESET}"
|
||||||
curl -fsSL https://deb.nodesource.com/setup_20.x | sudo -E bash -
|
echo -e " ${PIPE} ${CYAN}curl -fsSL https://deb.nodesource.com/setup_20.x | sudo -E bash -${RESET}"
|
||||||
sudo apt-get install -y nodejs
|
echo -e " ${PIPE} ${CYAN}sudo apt-get install -y nodejs${RESET}"
|
||||||
|
echo -e ""
|
||||||
${YELLOW}CentOS/RHEL/Fedora:${RESET}
|
echo -e " ${PIPE} ${YELLOW}CentOS/RHEL/Fedora:${RESET}"
|
||||||
curl -fsSL https://rpm.nodesource.com/setup_20.x | sudo bash -
|
echo -e " ${PIPE} ${CYAN}curl -fsSL https://rpm.nodesource.com/setup_20.x | sudo bash -${RESET}"
|
||||||
sudo yum install -y nodejs
|
echo -e " ${PIPE} ${CYAN}sudo yum install -y nodejs${RESET}"
|
||||||
|
echo -e ""
|
||||||
${YELLOW}Arch Linux:${RESET}
|
echo -e " ${PIPE} ${YELLOW}Arch Linux:${RESET}"
|
||||||
sudo pacman -S nodejs npm
|
echo -e " ${PIPE} ${CYAN}sudo pacman -S nodejs npm${RESET}"
|
||||||
|
echo -e ""
|
||||||
${YELLOW}使用 nvm(推荐开发者):${RESET}
|
echo -e " ${PIPE} ${YELLOW}使用 nvm(推荐开发者):${RESET}"
|
||||||
curl -o- https://raw.githubusercontent.com/nvm-sh/nvm/v0.39.7/install.sh | bash
|
echo -e " ${PIPE} ${CYAN}curl -o- https://raw.githubusercontent.com/nvm-sh/nvm/v0.39.7/install.sh | bash${RESET}"
|
||||||
source ~/.bashrc
|
echo -e " ${PIPE} ${CYAN}source ~/.bashrc && nvm install 20 && nvm use 20${RESET}"
|
||||||
nvm install 20
|
echo -e ""
|
||||||
nvm use 20
|
echo -e " ${PIPE} ${YELLOW}验证安装:${RESET}"
|
||||||
|
echo -e " ${PIPE} ${CYAN}node -v${RESET} # 应显示 v20.x.x"
|
||||||
${YELLOW}验证安装:${RESET}
|
echo -e " ${PIPE} ${CYAN}npm -v${RESET} # 应显示 10.x.x"
|
||||||
node -v # 应显示 v20.x.x
|
log_divider()
|
||||||
npm -v # 应显示 10.x.x
|
log_info "安装完成后,请重新运行此脚本继续安装 IDC 设备管理系统"
|
||||||
|
|
||||||
${CYAN}安装完成后,请重新运行此脚本继续安装 IDC设备管理系统${RESET}
|
|
||||||
"
|
|
||||||
}
|
}
|
||||||
|
|
||||||
# 检查并安装依赖
|
|
||||||
check_and_install_dependencies() {
|
check_and_install_dependencies() {
|
||||||
log_step "检查系统依赖"
|
log_step "检查系统依赖"
|
||||||
|
|
||||||
@@ -262,17 +247,14 @@ check_and_install_dependencies() {
|
|||||||
local distro=$(detect_distro)
|
local distro=$(detect_distro)
|
||||||
local needs_install=()
|
local needs_install=()
|
||||||
|
|
||||||
# 检查 git
|
|
||||||
if ! command_exists git; then
|
if ! command_exists git; then
|
||||||
needs_install+=("git")
|
needs_install+=("git")
|
||||||
fi
|
fi
|
||||||
|
|
||||||
# 检查 curl
|
|
||||||
if ! command_exists curl; then
|
if ! command_exists curl; then
|
||||||
needs_install+=("curl")
|
needs_install+=("curl")
|
||||||
fi
|
fi
|
||||||
|
|
||||||
# 检查 wget(可选)
|
|
||||||
if ! command_exists wget; then
|
if ! command_exists wget; then
|
||||||
needs_install+=("wget")
|
needs_install+=("wget")
|
||||||
fi
|
fi
|
||||||
@@ -303,18 +285,15 @@ check_and_install_dependencies() {
|
|||||||
fi
|
fi
|
||||||
}
|
}
|
||||||
|
|
||||||
# 主函数
|
|
||||||
main() {
|
main() {
|
||||||
show_banner
|
show_banner
|
||||||
|
|
||||||
# 检查是否在正确的目录
|
|
||||||
if [ ! -f "$SCRIPT_DIR/install.js" ]; then
|
if [ ! -f "$SCRIPT_DIR/install.js" ]; then
|
||||||
log_error "未找到 install.js 文件"
|
log_error "未找到 install.js 文件"
|
||||||
log_info "请在项目根目录运行此脚本"
|
log_info "请在项目根目录运行此脚本"
|
||||||
exit 1
|
exit 1
|
||||||
fi
|
fi
|
||||||
|
|
||||||
# 检查 Node.js
|
|
||||||
log_step "检查 Node.js 环境"
|
log_step "检查 Node.js 环境"
|
||||||
|
|
||||||
if command_exists node; then
|
if command_exists node; then
|
||||||
@@ -326,12 +305,11 @@ main() {
|
|||||||
else
|
else
|
||||||
log_warning "Node.js 版本过低 (v$node_version < v$MIN_NODE_VERSION)"
|
log_warning "Node.js 版本过低 (v$node_version < v$MIN_NODE_VERSION)"
|
||||||
|
|
||||||
echo -e "\n${YELLOW}请选择操作:${RESET}"
|
echo -e " ${POINTER} ${CYAN}1${RESET}) 自动升级 Node.js 到 v20(推荐)"
|
||||||
echo " 1) 自动升级 Node.js 到 v20(推荐)"
|
echo -e " ${DIM}2${RESET}) 显示手动安装指引"
|
||||||
echo " 2) 显示手动安装指引"
|
echo -e " ${DIM}3${RESET}) 退出"
|
||||||
echo " 3) 退出"
|
|
||||||
|
|
||||||
read -p "请选择 [1-3]: " choice
|
read -p " ❯ 请选择 [1-3]: " choice
|
||||||
|
|
||||||
case $choice in
|
case $choice in
|
||||||
1)
|
1)
|
||||||
@@ -340,7 +318,6 @@ main() {
|
|||||||
show_manual_install_guide
|
show_manual_install_guide
|
||||||
exit 1
|
exit 1
|
||||||
}
|
}
|
||||||
# 重新加载环境
|
|
||||||
export PATH="/usr/bin:$HOME/.nvm/versions/node/v20*/bin:$PATH"
|
export PATH="/usr/bin:$HOME/.nvm/versions/node/v20*/bin:$PATH"
|
||||||
hash -r
|
hash -r
|
||||||
;;
|
;;
|
||||||
@@ -361,12 +338,11 @@ main() {
|
|||||||
else
|
else
|
||||||
log_warning "未检测到 Node.js"
|
log_warning "未检测到 Node.js"
|
||||||
|
|
||||||
echo -e "\n${YELLOW}请选择操作:${RESET}"
|
echo -e " ${POINTER} ${CYAN}1${RESET}) 自动安装 Node.js v20(推荐)"
|
||||||
echo " 1) 自动安装 Node.js v20(推荐)"
|
echo -e " ${DIM}2${RESET}) 显示手动安装指引"
|
||||||
echo " 2) 显示手动安装指引"
|
echo -e " ${DIM}3${RESET}) 退出"
|
||||||
echo " 3) 退出"
|
|
||||||
|
|
||||||
read -p "请选择 [1-3]: " choice
|
read -p " ❯ 请选择 [1-3]: " choice
|
||||||
|
|
||||||
case $choice in
|
case $choice in
|
||||||
1)
|
1)
|
||||||
@@ -376,7 +352,6 @@ main() {
|
|||||||
show_manual_install_guide
|
show_manual_install_guide
|
||||||
exit 1
|
exit 1
|
||||||
}
|
}
|
||||||
# 重新加载环境
|
|
||||||
export PATH="/usr/bin:$HOME/.nvm/versions/node/v20*/bin:$PATH"
|
export PATH="/usr/bin:$HOME/.nvm/versions/node/v20*/bin:$PATH"
|
||||||
hash -r
|
hash -r
|
||||||
;;
|
;;
|
||||||
@@ -395,7 +370,6 @@ main() {
|
|||||||
esac
|
esac
|
||||||
fi
|
fi
|
||||||
|
|
||||||
# 检查 npm
|
|
||||||
log_step "检查 npm"
|
log_step "检查 npm"
|
||||||
if command_exists npm; then
|
if command_exists npm; then
|
||||||
log_success "npm $(npm -v)"
|
log_success "npm $(npm -v)"
|
||||||
@@ -417,23 +391,20 @@ main() {
|
|||||||
$sudo pacman -S --noconfirm npm
|
$sudo pacman -S --noconfirm npm
|
||||||
;;
|
;;
|
||||||
*)
|
*)
|
||||||
# 尝试使用 Node.js 自带的 corepack 启用 npm
|
|
||||||
if command_exists corepack; then
|
if command_exists corepack; then
|
||||||
log_info "尝试使用 corepack 启用 npm..."
|
log_info "尝试使用 corepack 启用 npm..."
|
||||||
corepack enable
|
corepack enable
|
||||||
corepack prepare npm@latest --activate
|
corepack prepare npm@latest --activate
|
||||||
else
|
else
|
||||||
log_error "无法自动安装 npm,请手动安装"
|
log_error "无法自动安装 npm,请手动安装"
|
||||||
echo -e "\n${YELLOW}手动安装 npm:${RESET}"
|
echo -e " ${PIPE} ${YELLOW}手动安装 npm:${RESET}"
|
||||||
echo " Ubuntu/Debian: sudo apt-get install npm"
|
echo -e " ${PIPE} Ubuntu/Debian: ${CYAN}sudo apt-get install npm${RESET}"
|
||||||
echo " CentOS/RHEL: sudo yum install npm"
|
echo -e " ${PIPE} CentOS/RHEL: ${CYAN}sudo yum install npm${RESET}"
|
||||||
echo " 或重新安装 Node.js: curl -fsSL https://deb.nodesource.com/setup_20.x | sudo -E bash - && sudo apt-get install -y nodejs"
|
|
||||||
exit 1
|
exit 1
|
||||||
fi
|
fi
|
||||||
;;
|
;;
|
||||||
esac
|
esac
|
||||||
|
|
||||||
# 再次检查 npm
|
|
||||||
if command_exists npm; then
|
if command_exists npm; then
|
||||||
log_success "npm $(npm -v) 安装成功"
|
log_success "npm $(npm -v) 安装成功"
|
||||||
else
|
else
|
||||||
@@ -445,7 +416,6 @@ main() {
|
|||||||
log_divider
|
log_divider
|
||||||
log_success "环境检查通过!"
|
log_success "环境检查通过!"
|
||||||
|
|
||||||
# 运行 install.js
|
|
||||||
log_step "启动安装程序"
|
log_step "启动安装程序"
|
||||||
log_info "正在运行 install.js..."
|
log_info "正在运行 install.js..."
|
||||||
echo ""
|
echo ""
|
||||||
@@ -454,5 +424,4 @@ main() {
|
|||||||
node install.js "$@"
|
node install.js "$@"
|
||||||
}
|
}
|
||||||
|
|
||||||
# 运行主函数
|
|
||||||
main "$@"
|
main "$@"
|
||||||
|
|||||||
@@ -1,7 +1,7 @@
|
|||||||
const fs = require('fs');
|
const fs = require('fs');
|
||||||
const { execSync } = require('child_process');
|
const { execSync } = require('child_process');
|
||||||
const { MIN_NODE_VERSION } = require('./constants');
|
const { MIN_NODE_VERSION } = require('./constants');
|
||||||
const { colors, log } = require('./logger');
|
const { colors, ICONS, log } = require('./logger');
|
||||||
const { runCommand, commandExists } = require('./utils');
|
const { runCommand, commandExists } = require('./utils');
|
||||||
const { ask } = require('./ui');
|
const { ask } = require('./ui');
|
||||||
|
|
||||||
@@ -75,7 +75,7 @@ function showNodeInstallGuide() {
|
|||||||
const isMac = platform === 'darwin';
|
const isMac = platform === 'darwin';
|
||||||
const isLinux = platform === 'linux';
|
const isLinux = platform === 'linux';
|
||||||
|
|
||||||
console.log('\n' + colors.bright + 'Node.js 安装指引:' + colors.reset);
|
log.section('Node.js 安装指引');
|
||||||
|
|
||||||
if (isLinux) {
|
if (isLinux) {
|
||||||
const distro = detectLinuxDistro();
|
const distro = detectLinuxDistro();
|
||||||
@@ -293,9 +293,8 @@ async function installNodeViaNvm() {
|
|||||||
async function handleNodeNotInstalledLinux() {
|
async function handleNodeNotInstalledLinux() {
|
||||||
log.error('未检测到 Node.js');
|
log.error('未检测到 Node.js');
|
||||||
|
|
||||||
console.log('\n' + colors.yellow + '您可以选择:' + colors.reset);
|
console.log(` ${ICONS.pointer} ${colors.cyan}1.${colors.reset} 自动安装 Node.js 20.x(推荐,需要 sudo 权限)`);
|
||||||
console.log(` ${colors.cyan}1.${colors.reset} 自动安装 Node.js 20.x(推荐,需要 sudo 权限)`);
|
console.log(` ${colors.dim}2.${colors.reset} 显示手动安装指引,退出脚本`);
|
||||||
console.log(` ${colors.cyan}2.${colors.reset} 显示手动安装指引,退出脚本`);
|
|
||||||
|
|
||||||
const choice = await ask('请选择', '1');
|
const choice = await ask('请选择', '1');
|
||||||
|
|
||||||
@@ -362,9 +361,8 @@ async function checkEnvironment() {
|
|||||||
log.error(`Node.js 版本过低 (${version}),需要 >= 14.0.0`);
|
log.error(`Node.js 版本过低 (${version}),需要 >= 14.0.0`);
|
||||||
|
|
||||||
if (isLinux) {
|
if (isLinux) {
|
||||||
console.log('\n' + colors.yellow + '您可以选择:' + colors.reset);
|
console.log(` ${ICONS.pointer} ${colors.cyan}1.${colors.reset} 自动升级到 Node.js 20.x`);
|
||||||
console.log(` ${colors.cyan}1.${colors.reset} 自动升级到 Node.js 20.x`);
|
console.log(` ${colors.dim}2.${colors.reset} 显示手动升级指引,退出脚本`);
|
||||||
console.log(` ${colors.cyan}2.${colors.reset} 显示手动升级指引,退出脚本`);
|
|
||||||
|
|
||||||
const upgradeChoice = await ask('请选择', '1');
|
const upgradeChoice = await ask('请选择', '1');
|
||||||
if (upgradeChoice === '1') {
|
if (upgradeChoice === '1') {
|
||||||
|
|||||||
+18
-14
@@ -1,6 +1,6 @@
|
|||||||
const fs = require('fs');
|
const fs = require('fs');
|
||||||
const path = require('path');
|
const path = require('path');
|
||||||
const { colors, log } = require('./logger');
|
const { colors, ICONS, log } = require('./logger');
|
||||||
const { generateSecretKey } = require('./utils');
|
const { generateSecretKey } = require('./utils');
|
||||||
const { config } = require('./config');
|
const { config } = require('./config');
|
||||||
const { ask } = require('./ui');
|
const { ask } = require('./ui');
|
||||||
@@ -213,8 +213,8 @@ server {
|
|||||||
|
|
||||||
if (isWindows) {
|
if (isWindows) {
|
||||||
log.info('Windows Nginx 配置路径示例:');
|
log.info('Windows Nginx 配置路径示例:');
|
||||||
console.log(` 将配置文件复制到: ${colors.cyan}C:/nginx/conf/conf.d/idc.conf${colors.reset}`);
|
console.log(` ${ICONS.pipe} 将配置文件复制到: ${colors.cyan}C:/nginx/conf/conf.d/idc.conf${colors.reset}`);
|
||||||
console.log(` 或修改主配置 include: ${colors.cyan}C:/nginx/conf/nginx.conf${colors.reset}`);
|
console.log(` ${ICONS.pipe} 或修改主配置 include: ${colors.cyan}C:/nginx/conf/nginx.conf${colors.reset}`);
|
||||||
}
|
}
|
||||||
} catch (error) {
|
} catch (error) {
|
||||||
log.error(`Nginx 配置文件生成失败: ${error.message}`);
|
log.error(`Nginx 配置文件生成失败: ${error.message}`);
|
||||||
@@ -225,22 +225,26 @@ server {
|
|||||||
async function confirmConfiguration() {
|
async function confirmConfiguration() {
|
||||||
log.step('配置确认');
|
log.step('配置确认');
|
||||||
|
|
||||||
console.log('\n' + colors.bright + '部署配置摘要:' + colors.reset);
|
console.log(` ${colors.bright}部署配置摘要${colors.reset}`);
|
||||||
console.log(` 数据库类型: ${colors.cyan}${config.dbType}${colors.reset}`);
|
log.thickDivider();
|
||||||
|
|
||||||
|
log.keyValue('数据库类型', config.dbType);
|
||||||
if (config.dbType === 'mysql') {
|
if (config.dbType === 'mysql') {
|
||||||
console.log(` MySQL主机: ${colors.cyan}${config.dbConfig.host}:${config.dbConfig.port}${colors.reset}`);
|
log.keyValue('MySQL 主机', `${config.dbConfig.host}:${config.dbConfig.port}`);
|
||||||
console.log(` 数据库名: ${colors.cyan}${config.dbConfig.database}${colors.reset}`);
|
log.keyValue('数据库名', config.dbConfig.database);
|
||||||
console.log(` 用户名: ${colors.cyan}${config.dbConfig.username}${colors.reset}`);
|
log.keyValue('用户名', config.dbConfig.username);
|
||||||
}
|
}
|
||||||
console.log(` 后端端口: ${colors.cyan}${config.backendPort}${colors.reset}`);
|
log.keyValue('后端端口', String(config.backendPort));
|
||||||
console.log(` 运行环境: ${colors.cyan}${config.nodeEnv}${colors.reset}`);
|
log.keyValue('运行环境', config.nodeEnv);
|
||||||
console.log(` 前端部署: ${colors.cyan}${config.frontendDeploy}${colors.reset}`);
|
log.keyValue('前端部署', config.frontendDeploy);
|
||||||
console.log(` 前端端口: ${colors.cyan}${config.frontendPort}${colors.reset}`);
|
log.keyValue('前端端口', String(config.frontendPort));
|
||||||
if (config.frontendDeploy === 'nginx') {
|
if (config.frontendDeploy === 'nginx') {
|
||||||
console.log(` 域名: ${colors.cyan}${config.domain}${colors.reset}`);
|
log.keyValue('域名', config.domain);
|
||||||
}
|
}
|
||||||
|
|
||||||
const confirm = await ask('\n确认以上配置并开始部署? (Y/n)', 'Y');
|
log.thickDivider();
|
||||||
|
|
||||||
|
const confirm = await ask('确认以上配置并开始部署? (Y/n)', 'Y');
|
||||||
if (confirm.toLowerCase() !== 'y') {
|
if (confirm.toLowerCase() !== 'y') {
|
||||||
log.info('已取消部署');
|
log.info('已取消部署');
|
||||||
process.exit(0);
|
process.exit(0);
|
||||||
|
|||||||
+46
-38
@@ -1,5 +1,5 @@
|
|||||||
const { SCRIPT_VERSION, LOG_DIR } = require('./constants');
|
const { SCRIPT_VERSION, LOG_DIR } = require('./constants');
|
||||||
const { colors, log, initLogFile, closeLogFile } = require('./logger');
|
const { colors, ICONS, log, padCenter } = require('./logger');
|
||||||
const { ask, closeReadline } = require('./ui');
|
const { ask, closeReadline } = require('./ui');
|
||||||
const { config, saveConfig, loadSavedConfig, applySavedConfig, parseArgs, showHelp } = require('./config');
|
const { config, saveConfig, loadSavedConfig, applySavedConfig, parseArgs, showHelp } = require('./config');
|
||||||
const { checkEnvironment } = require('./env-check');
|
const { checkEnvironment } = require('./env-check');
|
||||||
@@ -15,33 +15,43 @@ let installStartTime = Date.now();
|
|||||||
function printSummary() {
|
function printSummary() {
|
||||||
const duration = ((Date.now() - installStartTime) / 1000).toFixed(1);
|
const duration = ((Date.now() - installStartTime) / 1000).toFixed(1);
|
||||||
|
|
||||||
console.log(`
|
log.banner(
|
||||||
${colors.bright}${colors.magenta}
|
'安装部署完成',
|
||||||
╔══════════════════════════════════════════════════════════╗
|
'Installation Complete',
|
||||||
║ 安装摘要 ║
|
null
|
||||||
╚══════════════════════════════════════════════════════════╝${colors.reset}`);
|
);
|
||||||
|
|
||||||
console.log(`\n ${colors.cyan}安装耗时:${colors.reset} ${duration} 秒`);
|
console.log(` ${ICONS.pipe} ${colors.dim}安装耗时${colors.reset} ${colors.cyan}${duration} 秒${colors.reset}`);
|
||||||
console.log(` ${colors.cyan}数据库类型:${colors.reset} ${config.dbType}`);
|
console.log(` ${ICONS.pipe} ${colors.dim}数据库类型${colors.reset} ${colors.cyan}${config.dbType}${colors.reset}`);
|
||||||
console.log(` ${colors.cyan}后端端口:${colors.reset} ${config.backendPort}`);
|
console.log(` ${ICONS.pipe} ${colors.dim}后端端口${colors.reset} ${colors.cyan}${config.backendPort}${colors.reset}`);
|
||||||
console.log(` ${colors.cyan}前端部署:${colors.reset} ${config.frontendDeploy}`);
|
console.log(` ${ICONS.pipe} ${colors.dim}前端部署${colors.reset} ${colors.cyan}${config.frontendDeploy}${colors.reset}`);
|
||||||
|
|
||||||
console.log(`\n${colors.bright}服务管理命令:${colors.reset}`);
|
|
||||||
console.log(` ${colors.cyan}pm2 status${colors.reset} 查看服务状态`);
|
|
||||||
console.log(` ${colors.cyan}pm2 logs idc-backend${colors.reset} 查看后端日志`);
|
|
||||||
console.log(` ${colors.cyan}pm2 restart idc-backend${colors.reset} 重启后端`);
|
|
||||||
console.log(` ${colors.cyan}pm2 stop idc-backend${colors.reset} 停止后端`);
|
|
||||||
|
|
||||||
console.log(`\n${colors.bright}访问地址:${colors.reset}`);
|
|
||||||
console.log(` 后端API: ${colors.cyan}http://localhost:${config.backendPort}/api${colors.reset}`);
|
|
||||||
if (config.frontendDeploy === 'nginx') {
|
if (config.frontendDeploy === 'nginx') {
|
||||||
console.log(` 前端页面: ${colors.cyan}http://${config.domain}:${config.frontendPort}${colors.reset}`);
|
console.log(` ${ICONS.pipe} ${colors.dim}前端端口${colors.reset} ${colors.cyan}${config.frontendPort}${colors.reset}`);
|
||||||
} else {
|
console.log(` ${ICONS.pipe} ${colors.dim}域名${colors.reset} ${colors.cyan}${config.domain}${colors.reset}`);
|
||||||
console.log(` 前端页面: ${colors.cyan}http://localhost:${config.frontendPort}${colors.reset}`);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
console.log(`\n${colors.bright}更新升级:${colors.reset}`);
|
log.divider();
|
||||||
console.log(` ${colors.cyan}node update.js${colors.reset} 一键更新`);
|
|
||||||
|
log.section('服务管理命令');
|
||||||
|
console.log(` ${ICONS.pipe} ${colors.cyan}pm2 status${colors.reset} 查看服务状态`);
|
||||||
|
console.log(` ${ICONS.pipe} ${colors.cyan}pm2 logs idc-backend${colors.reset} 查看后端日志`);
|
||||||
|
console.log(` ${ICONS.pipe} ${colors.cyan}pm2 restart idc-backend${colors.reset} 重启后端`);
|
||||||
|
console.log(` ${ICONS.pipe} ${colors.cyan}pm2 stop idc-backend${colors.reset} 停止后端`);
|
||||||
|
|
||||||
|
log.divider();
|
||||||
|
|
||||||
|
log.section('访问地址');
|
||||||
|
console.log(` ${ICONS.pipe} ${colors.dim}后端 API${colors.reset} ${colors.cyan}http://localhost:${config.backendPort}/api${colors.reset}`);
|
||||||
|
if (config.frontendDeploy === 'nginx') {
|
||||||
|
console.log(` ${ICONS.pipe} ${colors.dim}前端页面${colors.reset} ${colors.cyan}http://${config.domain}:${config.frontendPort}${colors.reset}`);
|
||||||
|
} else {
|
||||||
|
console.log(` ${ICONS.pipe} ${colors.dim}前端页面${colors.reset} ${colors.cyan}http://localhost:${config.frontendPort}${colors.reset}`);
|
||||||
|
}
|
||||||
|
|
||||||
|
log.divider();
|
||||||
|
|
||||||
|
log.section('更新升级');
|
||||||
|
console.log(` ${ICONS.pipe} ${colors.cyan}node update.js${colors.reset} 一键更新`);
|
||||||
|
|
||||||
log.divider();
|
log.divider();
|
||||||
log.success('安装部署完成!');
|
log.success('安装部署完成!');
|
||||||
@@ -58,13 +68,11 @@ async function main() {
|
|||||||
|
|
||||||
initLogFile(LOG_DIR);
|
initLogFile(LOG_DIR);
|
||||||
|
|
||||||
console.log(`
|
log.banner(
|
||||||
${colors.bright}${colors.cyan}
|
'IDC 设备管理系统',
|
||||||
╔══════════════════════════════════════════════════════════╗
|
'安装部署脚本',
|
||||||
║ IDC设备管理系统 - 安装部署脚本 v${SCRIPT_VERSION} ║
|
SCRIPT_VERSION
|
||||||
║ Interactive Installation & Deployment Script ║
|
);
|
||||||
╚══════════════════════════════════════════════════════════╝
|
|
||||||
${colors.reset}`);
|
|
||||||
|
|
||||||
if (cmdArgs.nonInteractive) {
|
if (cmdArgs.nonInteractive) {
|
||||||
log.info('运行模式: 非交互式(使用默认配置)');
|
log.info('运行模式: 非交互式(使用默认配置)');
|
||||||
@@ -94,19 +102,19 @@ ${colors.reset}`);
|
|||||||
|
|
||||||
if (useSavedConfig) {
|
if (useSavedConfig) {
|
||||||
log.step('数据库配置');
|
log.step('数据库配置');
|
||||||
log.info(`数据库类型: ${config.dbType}`);
|
log.keyValue('数据库类型', config.dbType);
|
||||||
if (config.dbType === 'mysql') {
|
if (config.dbType === 'mysql') {
|
||||||
log.info(`MySQL: ${config.dbConfig.host}:${config.dbConfig.port}/${config.dbConfig.database}`);
|
log.keyValue('MySQL 地址', `${config.dbConfig.host}:${config.dbConfig.port}/${config.dbConfig.database}`);
|
||||||
}
|
}
|
||||||
log.divider();
|
log.divider();
|
||||||
|
|
||||||
log.step('服务配置');
|
log.step('服务配置');
|
||||||
log.info(`后端端口: ${config.backendPort}`);
|
log.keyValue('后端端口', String(config.backendPort));
|
||||||
log.info(`运行环境: ${config.nodeEnv}`);
|
log.keyValue('运行环境', config.nodeEnv);
|
||||||
log.info(`前端部署: ${config.frontendDeploy}`);
|
log.keyValue('前端部署', config.frontendDeploy);
|
||||||
log.info(`前端端口: ${config.frontendPort}`);
|
log.keyValue('前端端口', String(config.frontendPort));
|
||||||
if (config.frontendDeploy === 'nginx') {
|
if (config.frontendDeploy === 'nginx') {
|
||||||
log.info(`域名: ${config.domain}`);
|
log.keyValue('域名', config.domain);
|
||||||
}
|
}
|
||||||
log.divider();
|
log.divider();
|
||||||
} else {
|
} else {
|
||||||
|
|||||||
+101
-11
@@ -6,31 +6,119 @@ let logFileStream = null;
|
|||||||
const colors = {
|
const colors = {
|
||||||
reset: '\x1b[0m',
|
reset: '\x1b[0m',
|
||||||
bright: '\x1b[1m',
|
bright: '\x1b[1m',
|
||||||
|
dim: '\x1b[2m',
|
||||||
green: '\x1b[32m',
|
green: '\x1b[32m',
|
||||||
yellow: '\x1b[33m',
|
yellow: '\x1b[33m',
|
||||||
red: '\x1b[31m',
|
red: '\x1b[31m',
|
||||||
cyan: '\x1b[36m',
|
cyan: '\x1b[36m',
|
||||||
gray: '\x1b[90m',
|
gray: '\x1b[90m',
|
||||||
magenta: '\x1b[35m'
|
magenta: '\x1b[35m',
|
||||||
|
blue: '\x1b[34m',
|
||||||
|
bgCyan: '\x1b[46m',
|
||||||
|
bgBlue: '\x1b[44m',
|
||||||
|
white: '\x1b[37m',
|
||||||
};
|
};
|
||||||
|
|
||||||
|
const ICONS = {
|
||||||
|
info: 'ℹ',
|
||||||
|
success: '✔',
|
||||||
|
warning: '⚠',
|
||||||
|
error: '✖',
|
||||||
|
arrow: '▶',
|
||||||
|
bullet: '●',
|
||||||
|
pointer: '❯',
|
||||||
|
check: '✓',
|
||||||
|
cross: '✗',
|
||||||
|
dash: '─',
|
||||||
|
doubleDash: '═',
|
||||||
|
corner: '└',
|
||||||
|
pipe: '│',
|
||||||
|
diamond: '◆',
|
||||||
|
star: '★',
|
||||||
|
};
|
||||||
|
|
||||||
|
function padCenter(text, width) {
|
||||||
|
const stripped = text.replace(/\x1b\[[0-9;]*m/g, '');
|
||||||
|
const len = stripped.length;
|
||||||
|
const pad = Math.max(0, width - len);
|
||||||
|
const left = Math.floor(pad / 2);
|
||||||
|
const right = pad - left;
|
||||||
|
return ' '.repeat(left) + text + ' '.repeat(right);
|
||||||
|
}
|
||||||
|
|
||||||
|
function progressBar(current, total, width = 20) {
|
||||||
|
const filled = Math.round((current / total) * width);
|
||||||
|
const empty = width - filled;
|
||||||
|
const bar = `${colors.cyan}${'━'.repeat(filled)}${colors.dim}${'╌'.repeat(empty)}${colors.reset}`;
|
||||||
|
return bar;
|
||||||
|
}
|
||||||
|
|
||||||
const log = {
|
const log = {
|
||||||
info: (msg) => console.log(`${colors.cyan}ℹ${colors.reset} ${msg}`),
|
info: (msg) => console.log(` ${colors.cyan}${ICONS.info}${colors.reset} ${msg}`),
|
||||||
success: (msg) => console.log(`${colors.green}✓${colors.reset} ${msg}`),
|
success: (msg) => console.log(` ${colors.green}${ICONS.success}${colors.reset} ${msg}`),
|
||||||
warning: (msg) => console.log(`${colors.yellow}⚠${colors.reset} ${msg}`),
|
warning: (msg) => console.log(` ${colors.yellow}${ICONS.warning}${colors.reset} ${msg}`),
|
||||||
error: (msg) => console.log(`${colors.red}✗${colors.reset} ${msg}`),
|
error: (msg) => console.log(` ${colors.red}${ICONS.error}${colors.reset} ${msg}`),
|
||||||
|
|
||||||
step: (msg) => {
|
step: (msg) => {
|
||||||
const idx = INSTALL_STEPS.indexOf(msg);
|
const idx = INSTALL_STEPS.indexOf(msg);
|
||||||
if (idx >= 0) {
|
if (idx >= 0) {
|
||||||
currentStepIndex = idx;
|
currentStepIndex = idx;
|
||||||
}
|
}
|
||||||
const progress = currentStepIndex < INSTALL_STEPS.length
|
const current = currentStepIndex + 1;
|
||||||
? `${colors.gray}[${currentStepIndex + 1}/${INSTALL_STEPS.length}]${colors.reset} `
|
const total = INSTALL_STEPS.length;
|
||||||
: '';
|
const bar = progressBar(current, total, 15);
|
||||||
console.log(`\n${progress}${colors.bright}${colors.cyan}▶ ${msg}${colors.reset}`);
|
const progress = `${colors.dim}[${current}/${total}]${colors.reset} ${bar}`;
|
||||||
|
console.log('');
|
||||||
|
console.log(`${progress}`);
|
||||||
|
console.log(` ${colors.bright}${colors.cyan}${ICONS.arrow}${colors.reset} ${colors.bright}${msg}${colors.reset}`);
|
||||||
|
},
|
||||||
|
|
||||||
|
divider: () => console.log(` ${colors.dim}${ICONS.dash.repeat(56)}${colors.reset}`),
|
||||||
|
thickDivider: () => console.log(` ${colors.dim}${ICONS.doubleDash.repeat(56)}${colors.reset}`),
|
||||||
|
|
||||||
|
subStep: (msg) => console.log(` ${colors.dim}${ICONS.corner}${colors.reset} ${msg}`),
|
||||||
|
|
||||||
|
keyValue: (key, value) => {
|
||||||
|
const keyStr = `${colors.dim}${key}${colors.reset}`;
|
||||||
|
const valueStr = `${colors.cyan}${value}${colors.reset}`;
|
||||||
|
console.log(` ${ICONS.pipe} ${keyStr} ${valueStr}`);
|
||||||
|
},
|
||||||
|
|
||||||
|
banner: (title, subtitle, version) => {
|
||||||
|
const width = 58;
|
||||||
|
const top = `${colors.bright}${colors.cyan}${ICONS.doubleDash.repeat(width + 2)}${colors.reset}`;
|
||||||
|
const bottom = top;
|
||||||
|
const titleLine = padCenter(`${colors.bright}${colors.white}${title}${colors.reset}`, width);
|
||||||
|
const subtitleLine = padCenter(`${colors.dim}${subtitle}${colors.reset}`, width);
|
||||||
|
const versionLine = version ? padCenter(`${colors.cyan}v${version}${colors.reset}`, width) : '';
|
||||||
|
|
||||||
|
console.log('');
|
||||||
|
console.log(` ${colors.cyan}╔${top.substring(2)}╗${colors.reset}`);
|
||||||
|
console.log(` ${colors.cyan}║${titleLine}║${colors.reset}`);
|
||||||
|
if (subtitle) {
|
||||||
|
console.log(` ${colors.cyan}║${subtitleLine}║${colors.reset}`);
|
||||||
|
}
|
||||||
|
if (version) {
|
||||||
|
console.log(` ${colors.cyan}║${versionLine}║${colors.reset}`);
|
||||||
|
}
|
||||||
|
console.log(` ${colors.cyan}╚${bottom.substring(2)}╝${colors.reset}`);
|
||||||
|
console.log('');
|
||||||
|
},
|
||||||
|
|
||||||
|
section: (title) => {
|
||||||
|
console.log('');
|
||||||
|
console.log(` ${colors.bright}${colors.magenta}${ICONS.diamond}${colors.reset} ${colors.bright}${title}${colors.reset}`);
|
||||||
|
log.divider();
|
||||||
|
},
|
||||||
|
|
||||||
|
tableRow: (items, widths) => {
|
||||||
|
const parts = items.map((item, i) => {
|
||||||
|
const stripped = item.replace(/\x1b\[[0-9;]*m/g, '');
|
||||||
|
const pad = Math.max(0, (widths[i] || 20) - stripped.length);
|
||||||
|
return item + ' '.repeat(pad);
|
||||||
|
});
|
||||||
|
console.log(` ${ICONS.pipe} ${parts.join(' ')}`);
|
||||||
},
|
},
|
||||||
divider: () => console.log(`${colors.gray}${'─'.repeat(60)}${colors.reset}`),
|
|
||||||
subStep: (msg) => console.log(` ${colors.gray}└${colors.reset} ${msg}`)
|
|
||||||
};
|
};
|
||||||
|
|
||||||
function initLogFile(LOG_DIR) {
|
function initLogFile(LOG_DIR) {
|
||||||
@@ -62,7 +150,9 @@ function closeLogFile() {
|
|||||||
|
|
||||||
module.exports = {
|
module.exports = {
|
||||||
colors,
|
colors,
|
||||||
|
ICONS,
|
||||||
log,
|
log,
|
||||||
|
padCenter,
|
||||||
initLogFile,
|
initLogFile,
|
||||||
closeLogFile,
|
closeLogFile,
|
||||||
};
|
};
|
||||||
|
|||||||
+3
-3
@@ -1,7 +1,7 @@
|
|||||||
const fs = require('fs');
|
const fs = require('fs');
|
||||||
const path = require('path');
|
const path = require('path');
|
||||||
const { execSync } = require('child_process');
|
const { execSync } = require('child_process');
|
||||||
const { colors, log } = require('./logger');
|
const { colors, ICONS, log } = require('./logger');
|
||||||
const { ask, select } = require('./ui');
|
const { ask, select } = require('./ui');
|
||||||
const { config } = require('./config');
|
const { config } = require('./config');
|
||||||
const { runCommand } = require('./utils');
|
const { runCommand } = require('./utils');
|
||||||
@@ -12,9 +12,9 @@ function showNginxInstallGuideLinux() {
|
|||||||
const root = isRootUser();
|
const root = isRootUser();
|
||||||
const sudoPrefix = root ? '' : 'sudo ';
|
const sudoPrefix = root ? '' : 'sudo ';
|
||||||
|
|
||||||
console.log('\n' + colors.bright + 'Nginx 安装命令:' + colors.reset);
|
log.section('Nginx 安装命令');
|
||||||
if (root) {
|
if (root) {
|
||||||
console.log(colors.gray + '(当前以 root 用户运行,无需 sudo)' + colors.reset);
|
console.log(` ${ICONS.pipe} ${colors.gray}(当前以 root 用户运行,无需 sudo)${colors.reset}`);
|
||||||
}
|
}
|
||||||
|
|
||||||
switch (distro) {
|
switch (distro) {
|
||||||
|
|||||||
+22
-8
@@ -1,5 +1,5 @@
|
|||||||
const readline = require('readline');
|
const readline = require('readline');
|
||||||
const { log } = require('./logger');
|
const { colors, ICONS, log } = require('./logger');
|
||||||
|
|
||||||
const rl = readline.createInterface({
|
const rl = readline.createInterface({
|
||||||
input: process.stdin,
|
input: process.stdin,
|
||||||
@@ -17,7 +17,8 @@ rl.on('error', (err) => {
|
|||||||
|
|
||||||
function ask(question, defaultValue = '') {
|
function ask(question, defaultValue = '') {
|
||||||
return new Promise((resolve) => {
|
return new Promise((resolve) => {
|
||||||
const prompt = defaultValue ? `${question} (${defaultValue}): ` : `${question}: `;
|
const defaultHint = defaultValue ? `${colors.dim}(${defaultValue})${colors.reset}` : '';
|
||||||
|
const prompt = ` ${colors.cyan}${ICONS.pointer}${colors.reset} ${colors.bright}${question}${colors.reset} ${defaultHint}${colors.dim}:${colors.reset} `;
|
||||||
rl.question(prompt, (answer) => {
|
rl.question(prompt, (answer) => {
|
||||||
resolve(answer.trim() || defaultValue);
|
resolve(answer.trim() || defaultValue);
|
||||||
});
|
});
|
||||||
@@ -25,10 +26,10 @@ function ask(question, defaultValue = '') {
|
|||||||
}
|
}
|
||||||
|
|
||||||
function askPassword(question) {
|
function askPassword(question) {
|
||||||
return new Promise((resolve) => {
|
const prompt = ` ${colors.cyan}${ICONS.pointer}${colors.reset} ${colors.bright}${question}${colors.reset} ${colors.dim}:${colors.reset} `;
|
||||||
const prompt = `${question}: `;
|
|
||||||
process.stdout.write(prompt);
|
process.stdout.write(prompt);
|
||||||
|
|
||||||
|
return new Promise((resolve) => {
|
||||||
let password = '';
|
let password = '';
|
||||||
const stdin = process.stdin;
|
const stdin = process.stdin;
|
||||||
const stdout = process.stdout;
|
const stdout = process.stdout;
|
||||||
@@ -52,7 +53,8 @@ function askPassword(question) {
|
|||||||
resolve(password);
|
resolve(password);
|
||||||
} else if (code === 3) {
|
} else if (code === 3) {
|
||||||
cleanup();
|
cleanup();
|
||||||
stdout.write('\n已取消\n');
|
stdout.write('\n');
|
||||||
|
log.warning('已取消');
|
||||||
process.exit(0);
|
process.exit(0);
|
||||||
} else if (code === 127 || code === 8) {
|
} else if (code === 127 || code === 8) {
|
||||||
if (password.length > 0) {
|
if (password.length > 0) {
|
||||||
@@ -78,10 +80,22 @@ function askPassword(question) {
|
|||||||
}
|
}
|
||||||
|
|
||||||
async function select(question, options, defaultIndex = 1) {
|
async function select(question, options, defaultIndex = 1) {
|
||||||
const { colors } = require('./logger');
|
console.log('');
|
||||||
console.log(`\n${question}`);
|
console.log(` ${colors.bright}${question}${colors.reset}`);
|
||||||
|
log.divider();
|
||||||
|
|
||||||
options.forEach((opt, idx) => {
|
options.forEach((opt, idx) => {
|
||||||
console.log(` ${colors.cyan}${idx + 1}.${colors.reset} ${opt.label}`);
|
const isDefault = idx + 1 === defaultIndex;
|
||||||
|
const marker = isDefault
|
||||||
|
? `${colors.cyan}${ICONS.pointer}${colors.reset}`
|
||||||
|
: ` `;
|
||||||
|
const label = isDefault
|
||||||
|
? `${colors.bright}${opt.label}${colors.reset}`
|
||||||
|
: `${colors.dim}${opt.label}${colors.reset}`;
|
||||||
|
const num = isDefault
|
||||||
|
? `${colors.cyan}${idx + 1}${colors.reset}`
|
||||||
|
: `${colors.dim}${idx + 1}${colors.reset}`;
|
||||||
|
console.log(` ${marker} ${num}. ${label}`);
|
||||||
});
|
});
|
||||||
|
|
||||||
const answer = await ask('请选择', String(defaultIndex));
|
const answer = await ask('请选择', String(defaultIndex));
|
||||||
|
|||||||
Reference in New Issue
Block a user