571 lines
14 KiB
Bash
571 lines
14 KiB
Bash
#!/bin/bash
|
|
|
|
# 确保以 bash 运行
|
|
if [ -z "$BASH_VERSION" ]; then
|
|
exec bash "$0" "$@"
|
|
fi
|
|
|
|
# VoiceHub 日常维护管理脚本
|
|
# 支持上下键选择菜单
|
|
|
|
set -e
|
|
|
|
# 颜色定义
|
|
RED='\033[0;31m'
|
|
GREEN='\033[0;32m'
|
|
YELLOW='\033[1;33m'
|
|
BLUE='\033[0;34m'
|
|
CYAN='\033[0;36m'
|
|
NC='\033[0m' # No Color
|
|
|
|
# 默认安装目录
|
|
PROJECT_DIR="/opt/voicehub"
|
|
DIRECT_LOG_DIR="$PROJECT_DIR/logs"
|
|
DIRECT_PID_FILE="$DIRECT_LOG_DIR/voicehub.pid"
|
|
DIRECT_LOG_FILE="$DIRECT_LOG_DIR/voicehub-out.log"
|
|
|
|
ensure_pnpm() {
|
|
export PNPM_HOME="${PNPM_HOME:-$HOME/.local/share/pnpm}"
|
|
export COREPACK_ENABLE_DOWNLOAD_PROMPT=0
|
|
mkdir -p "$PNPM_HOME"
|
|
export PATH="$PNPM_HOME:$PATH"
|
|
|
|
if ! command -v corepack &> /dev/null; then
|
|
sudo npm install -g corepack
|
|
hash -r
|
|
fi
|
|
|
|
corepack enable
|
|
|
|
local package_manager=""
|
|
if [[ -f "$PROJECT_DIR/package.json" ]]; then
|
|
package_manager=$(node -p "try { JSON.parse(require('fs').readFileSync('$PROJECT_DIR/package.json', 'utf8')).packageManager || '' } catch { '' }")
|
|
fi
|
|
|
|
if [[ -n "$package_manager" ]]; then
|
|
corepack prepare "$package_manager" --activate
|
|
fi
|
|
|
|
hash -r
|
|
|
|
if ! command -v pnpm &> /dev/null; then
|
|
echo -e "${RED}错误: pnpm 不可用${NC}"
|
|
exit 1
|
|
fi
|
|
}
|
|
|
|
has_working_pm2() {
|
|
local pm2_root
|
|
local pm2_bin
|
|
|
|
pm2_root=$(pnpm root -g 2>/dev/null || true)
|
|
pm2_bin="$pm2_root/pm2/bin/pm2"
|
|
|
|
if [[ -f "$pm2_bin" ]]; then
|
|
node "$pm2_bin" -v > /dev/null 2>&1
|
|
return $?
|
|
fi
|
|
|
|
if command -v pm2 &> /dev/null; then
|
|
pm2 -v > /dev/null 2>&1
|
|
return $?
|
|
fi
|
|
|
|
return 1
|
|
}
|
|
|
|
run_pm2() {
|
|
local pm2_root
|
|
local pm2_bin
|
|
|
|
pm2_root=$(pnpm root -g 2>/dev/null || true)
|
|
pm2_bin="$pm2_root/pm2/bin/pm2"
|
|
|
|
if [[ -f "$pm2_bin" ]]; then
|
|
node "$pm2_bin" "$@"
|
|
return $?
|
|
fi
|
|
|
|
if command -v pm2 &> /dev/null; then
|
|
pm2 "$@"
|
|
return $?
|
|
fi
|
|
|
|
echo -e "${RED}错误: PM2 不可用,请重新运行部署脚本修复 PM2${NC}"
|
|
return 1
|
|
}
|
|
|
|
is_direct_service_running() {
|
|
if [[ ! -f "$DIRECT_PID_FILE" ]]; then
|
|
return 1
|
|
fi
|
|
|
|
local pid
|
|
pid=$(cat "$DIRECT_PID_FILE" 2>/dev/null)
|
|
|
|
if [[ -z "$pid" ]]; then
|
|
rm -f "$DIRECT_PID_FILE"
|
|
return 1
|
|
fi
|
|
|
|
if kill -0 "$pid" 2>/dev/null; then
|
|
return 0
|
|
fi
|
|
|
|
rm -f "$DIRECT_PID_FILE"
|
|
return 1
|
|
}
|
|
|
|
start_direct_service() {
|
|
mkdir -p "$DIRECT_LOG_DIR"
|
|
|
|
if is_direct_service_running; then
|
|
echo -e "${YELLOW}VoiceHub 已在直接运行模式下启动${NC}"
|
|
return 0
|
|
fi
|
|
|
|
if [[ ! -f "$PROJECT_DIR/.output/server/index.mjs" ]]; then
|
|
echo -e "${RED}错误: 未找到构建产物,请先执行 voicehub build${NC}"
|
|
exit 1
|
|
fi
|
|
|
|
nohup node "$PROJECT_DIR/.output/server/index.mjs" >> "$DIRECT_LOG_FILE" 2>&1 &
|
|
local pid=$!
|
|
echo "$pid" > "$DIRECT_PID_FILE"
|
|
sleep 2
|
|
|
|
if is_direct_service_running; then
|
|
echo -e "${GREEN}✓ 直接运行模式已启动 (PID: $pid)${NC}"
|
|
else
|
|
echo -e "${RED}错误: 直接运行模式启动失败,请查看日志: $DIRECT_LOG_FILE${NC}"
|
|
exit 1
|
|
fi
|
|
}
|
|
|
|
stop_direct_service() {
|
|
if ! is_direct_service_running; then
|
|
echo -e "${YELLOW}直接运行模式未启动${NC}"
|
|
return 0
|
|
fi
|
|
|
|
local pid
|
|
pid=$(cat "$DIRECT_PID_FILE")
|
|
kill "$pid" 2>/dev/null || true
|
|
|
|
for _ in {1..10}; do
|
|
if ! kill -0 "$pid" 2>/dev/null; then
|
|
rm -f "$DIRECT_PID_FILE"
|
|
echo -e "${GREEN}✓ 直接运行模式已停止${NC}"
|
|
return 0
|
|
fi
|
|
sleep 1
|
|
done
|
|
|
|
kill -9 "$pid" 2>/dev/null || true
|
|
rm -f "$DIRECT_PID_FILE"
|
|
echo -e "${GREEN}✓ 直接运行模式已强制停止${NC}"
|
|
}
|
|
|
|
# 检查项目目录
|
|
check_project() {
|
|
if [[ ! -d "$PROJECT_DIR" ]]; then
|
|
echo -e "${RED}错误: 项目目录不存在: $PROJECT_DIR${NC}"
|
|
echo -e "${RED}请先运行部署脚本安装 VoiceHub${NC}"
|
|
exit 1
|
|
fi
|
|
cd "$PROJECT_DIR"
|
|
ensure_pnpm
|
|
}
|
|
|
|
# 检测服务管理器类型
|
|
detect_service_manager() {
|
|
if [[ -f "$PROJECT_DIR/ecosystem.config.cjs" || -f "$PROJECT_DIR/ecosystem.config.js" ]]; then
|
|
echo "pm2"
|
|
elif [[ -f "/etc/systemd/system/voicehub.service" ]]; then
|
|
echo "systemd"
|
|
elif [[ -f "$PROJECT_DIR/.output/server/index.mjs" ]]; then
|
|
echo "direct"
|
|
else
|
|
echo "none"
|
|
fi
|
|
}
|
|
|
|
# 查看状态
|
|
cmd_status() {
|
|
check_project
|
|
echo -e "${BLUE}检查服务状态...${NC}"
|
|
|
|
local service_type=$(detect_service_manager)
|
|
|
|
if [[ "$service_type" == "pm2" ]]; then
|
|
if has_working_pm2 && run_pm2 list | grep -q "voicehub"; then
|
|
echo -e "${GREEN}PM2 服务状态:${NC}"
|
|
run_pm2 list | grep voicehub
|
|
else
|
|
echo -e "${YELLOW}PM2 服务未运行或 PM2 命令异常${NC}"
|
|
fi
|
|
elif [[ "$service_type" == "systemd" ]]; then
|
|
echo -e "${GREEN}systemctl 服务状态:${NC}"
|
|
sudo systemctl status voicehub
|
|
elif [[ "$service_type" == "direct" ]]; then
|
|
if is_direct_service_running; then
|
|
echo -e "${GREEN}直接运行模式状态: 运行中${NC}"
|
|
echo -e "${BLUE}PID: $(cat "$DIRECT_PID_FILE")${NC}"
|
|
echo -e "${BLUE}日志: $DIRECT_LOG_FILE${NC}"
|
|
else
|
|
echo -e "${YELLOW}直接运行模式未启动${NC}"
|
|
echo -e "${BLUE}可使用 voicehub start 启动${NC}"
|
|
fi
|
|
else
|
|
echo -e "${YELLOW}未检测到服务配置${NC}"
|
|
fi
|
|
}
|
|
|
|
# 启动服务
|
|
cmd_start() {
|
|
check_project
|
|
echo -e "${BLUE}启动服务...${NC}"
|
|
|
|
local service_type=$(detect_service_manager)
|
|
|
|
if [[ "$service_type" == "pm2" ]]; then
|
|
if [[ -f "ecosystem.config.cjs" ]]; then
|
|
run_pm2 start ecosystem.config.cjs
|
|
else
|
|
run_pm2 start ecosystem.config.js
|
|
fi
|
|
echo -e "${GREEN}✓ PM2 服务已启动${NC}"
|
|
elif [[ "$service_type" == "systemd" ]]; then
|
|
sudo systemctl start voicehub
|
|
echo -e "${GREEN}✓ systemctl 服务已启动${NC}"
|
|
elif [[ "$service_type" == "direct" ]]; then
|
|
start_direct_service
|
|
else
|
|
echo -e "${YELLOW}未找到服务配置,请先运行部署脚本配置服务${NC}"
|
|
exit 1
|
|
fi
|
|
}
|
|
|
|
# 停止服务
|
|
cmd_stop() {
|
|
check_project
|
|
echo -e "${BLUE}停止服务...${NC}"
|
|
|
|
local service_type=$(detect_service_manager)
|
|
|
|
if [[ "$service_type" == "pm2" ]]; then
|
|
run_pm2 stop voicehub
|
|
echo -e "${GREEN}✓ PM2 服务已停止${NC}"
|
|
elif [[ "$service_type" == "systemd" ]]; then
|
|
sudo systemctl stop voicehub
|
|
echo -e "${GREEN}✓ systemctl 服务已停止${NC}"
|
|
elif [[ "$service_type" == "direct" ]]; then
|
|
stop_direct_service
|
|
else
|
|
echo -e "${YELLOW}未找到服务配置${NC}"
|
|
fi
|
|
}
|
|
|
|
# 重启服务
|
|
cmd_restart() {
|
|
check_project
|
|
echo -e "${BLUE}重启服务...${NC}"
|
|
|
|
local service_type=$(detect_service_manager)
|
|
|
|
if [[ "$service_type" == "pm2" ]]; then
|
|
run_pm2 restart voicehub
|
|
echo -e "${GREEN}✓ PM2 服务已重启${NC}"
|
|
elif [[ "$service_type" == "systemd" ]]; then
|
|
sudo systemctl restart voicehub
|
|
echo -e "${GREEN}✓ systemctl 服务已重启${NC}"
|
|
elif [[ "$service_type" == "direct" ]]; then
|
|
stop_direct_service
|
|
start_direct_service
|
|
else
|
|
echo -e "${YELLOW}未找到服务配置,请先运行部署脚本配置服务${NC}"
|
|
exit 1
|
|
fi
|
|
}
|
|
|
|
# 更新
|
|
cmd_update() {
|
|
check_project
|
|
if [[ -f "$PROJECT_DIR/sh/update.sh" ]]; then
|
|
bash "$PROJECT_DIR/sh/update.sh"
|
|
else
|
|
echo -e "${RED}错误: 找不到更新脚本 $PROJECT_DIR/sh/update.sh${NC}"
|
|
exit 1
|
|
fi
|
|
}
|
|
|
|
# 重装依赖
|
|
cmd_deps() {
|
|
check_project
|
|
echo -e "${BLUE}重新安装依赖...${NC}"
|
|
|
|
rm -rf node_modules
|
|
pnpm install --frozen-lockfile || pnpm install
|
|
|
|
echo -e "${GREEN}✓ 依赖重装完成${NC}"
|
|
}
|
|
|
|
# 重新编译
|
|
cmd_build() {
|
|
check_project
|
|
echo -e "${BLUE}重新编译项目...${NC}"
|
|
|
|
pnpm run build
|
|
|
|
echo -e "${GREEN}✓ 编译完成${NC}"
|
|
}
|
|
|
|
# 重新安装
|
|
cmd_reinstall() {
|
|
check_project
|
|
echo -e "${BLUE}========================================${NC}"
|
|
echo -e "${BLUE} VoiceHub 重新安装${NC}"
|
|
echo -e "${BLUE}========================================${NC}"
|
|
echo ""
|
|
|
|
cmd_stop
|
|
|
|
echo -e "${YELLOW}[1/4] 更新代码...${NC}"
|
|
git fetch origin main
|
|
git stash
|
|
git reset --hard origin/main
|
|
echo -e "${GREEN}✓ 代码更新完成${NC}"
|
|
|
|
echo -e "${YELLOW}[2/4] 重装依赖...${NC}"
|
|
rm -rf node_modules
|
|
pnpm install --frozen-lockfile || pnpm install
|
|
echo -e "${GREEN}✓ 依赖重装完成${NC}"
|
|
|
|
echo -e "${YELLOW}[3/4] 重新编译...${NC}"
|
|
pnpm run build
|
|
echo -e "${GREEN}✓ 编译完成${NC}"
|
|
|
|
echo -e "${YELLOW}[4/4] 启动服务...${NC}"
|
|
cmd_start
|
|
|
|
echo ""
|
|
echo -e "${GREEN}========================================${NC}"
|
|
echo -e "${GREEN} 重新安装完成!${NC}"
|
|
echo -e "${GREEN}========================================${NC}"
|
|
}
|
|
|
|
# 查看日志
|
|
cmd_logs() {
|
|
check_project
|
|
|
|
local service_type=$(detect_service_manager)
|
|
|
|
if [[ "$service_type" == "pm2" ]]; then
|
|
run_pm2 logs voicehub
|
|
elif [[ "$service_type" == "systemd" ]]; then
|
|
sudo journalctl -u voicehub -f
|
|
elif [[ "$service_type" == "direct" ]]; then
|
|
mkdir -p "$DIRECT_LOG_DIR"
|
|
touch "$DIRECT_LOG_FILE"
|
|
tail -f "$DIRECT_LOG_FILE"
|
|
else
|
|
echo -e "${YELLOW}未找到服务配置,无法查看日志${NC}"
|
|
exit 1
|
|
fi
|
|
}
|
|
|
|
# 交互式菜单
|
|
show_menu() {
|
|
# 菜单选项
|
|
local options=(
|
|
"查看服务状态"
|
|
"启动服务"
|
|
"停止服务"
|
|
"重启服务"
|
|
"更新"
|
|
"重新安装"
|
|
"重装依赖"
|
|
"重新编译"
|
|
"查看日志"
|
|
"退出"
|
|
)
|
|
|
|
local selected=0
|
|
local total=${#options[@]}
|
|
|
|
# 打印菜单
|
|
print_menu() {
|
|
clear
|
|
echo -e "${BLUE}========================================${NC}"
|
|
echo -e "${BLUE} VoiceHub 管理面板${NC}"
|
|
echo -e "${BLUE}========================================${NC}"
|
|
echo ""
|
|
|
|
for i in "${!options[@]}"; do
|
|
if [[ $i -eq $selected ]]; then
|
|
echo -e "${GREEN}► ${options[$i]}${NC}"
|
|
else
|
|
echo -e " ${options[$i]}"
|
|
fi
|
|
done
|
|
|
|
echo ""
|
|
echo -e "${YELLOW}使用 ↑↓ 选择,Enter 确认${NC}"
|
|
}
|
|
|
|
# 临时关闭 set -e,防止 read 超时或非零返回导致脚本退出
|
|
set +e
|
|
|
|
while true; do
|
|
print_menu
|
|
|
|
# 读取 1 个字符
|
|
read -rsn1 key
|
|
|
|
# 检查是否是转义序列 (方向键)
|
|
if [[ "$key" == $'\x1b' ]]; then
|
|
# 使用 || true 防止超时导致 set -e 触发(虽然已关闭,但保持习惯)
|
|
read -rsn2 -t 0.01 chars || true
|
|
key="$key$chars"
|
|
fi
|
|
|
|
case "$key" in
|
|
$'\x1b[A'|$'\x1bOA'|k|K) # 上
|
|
((selected--))
|
|
if [[ $selected -lt 0 ]]; then
|
|
selected=$((total - 1))
|
|
fi
|
|
;;
|
|
$'\x1b[B'|$'\x1bOB'|j|J) # 下
|
|
((selected++))
|
|
if [[ $selected -ge $total ]]; then
|
|
selected=0
|
|
fi
|
|
;;
|
|
"") # Enter
|
|
break
|
|
;;
|
|
esac
|
|
done
|
|
|
|
# 恢复 set -e
|
|
set -e
|
|
|
|
echo ""
|
|
|
|
# 执行选择的操作
|
|
case $selected in
|
|
0)
|
|
cmd_status
|
|
;;
|
|
1)
|
|
cmd_start
|
|
;;
|
|
2)
|
|
cmd_stop
|
|
;;
|
|
3)
|
|
cmd_restart
|
|
;;
|
|
4)
|
|
cmd_update
|
|
;;
|
|
5)
|
|
cmd_reinstall
|
|
;;
|
|
6)
|
|
cmd_deps
|
|
;;
|
|
7)
|
|
cmd_build
|
|
;;
|
|
8)
|
|
cmd_logs
|
|
;;
|
|
9)
|
|
echo -e "${CYAN}退出${NC}"
|
|
exit 0
|
|
;;
|
|
esac
|
|
}
|
|
|
|
# 检查是否有交互式终端
|
|
if [[ ! -t 0 ]]; then
|
|
# 非交互模式,显示帮助
|
|
echo -e "${BLUE}VoiceHub 管理脚本${NC}"
|
|
echo ""
|
|
echo "用法: voicehub"
|
|
echo ""
|
|
echo "运行脚本后使用上下键选择操作"
|
|
echo ""
|
|
echo "或在非交互环境中使用以下命令:"
|
|
echo " bash $0 status # 查看状态"
|
|
echo " bash $0 start # 启动服务"
|
|
echo " bash $0 stop # 停止服务"
|
|
echo " bash $0 restart # 重启服务"
|
|
echo " bash $0 update # 更新代码"
|
|
echo " bash $0 reinstall # 重新安装"
|
|
echo " bash $0 deps # 重装依赖"
|
|
echo " bash $0 build # 重新编译"
|
|
echo " bash $0 logs # 查看日志"
|
|
exit 0
|
|
fi
|
|
|
|
# 如果有参数,直接执行对应命令
|
|
if [[ -n "$1" ]]; then
|
|
case "$1" in
|
|
status)
|
|
cmd_status
|
|
;;
|
|
start)
|
|
cmd_start
|
|
;;
|
|
stop)
|
|
cmd_stop
|
|
;;
|
|
restart)
|
|
cmd_restart
|
|
;;
|
|
update)
|
|
cmd_update
|
|
;;
|
|
reinstall)
|
|
cmd_reinstall
|
|
;;
|
|
deps)
|
|
cmd_deps
|
|
;;
|
|
build)
|
|
cmd_build
|
|
;;
|
|
logs)
|
|
cmd_logs
|
|
;;
|
|
help|--help|-h)
|
|
echo -e "${BLUE}VoiceHub 管理脚本${NC}"
|
|
echo ""
|
|
echo "用法: voicehub"
|
|
echo ""
|
|
echo "直接运行脚本可使用交互式菜单(上下键选择)"
|
|
echo ""
|
|
echo "或使用以下命令:"
|
|
echo " voicehub status # 查看状态"
|
|
echo " voicehub start # 启动服务"
|
|
echo " voicehub stop # 停止服务"
|
|
echo " voicehub restart # 重启服务"
|
|
echo " voicehub update # 更新代码"
|
|
echo " voicehub reinstall # 重新安装"
|
|
echo " voicehub deps # 重装依赖"
|
|
echo " voicehub build # 重新编译"
|
|
echo " voicehub logs # 查看日志"
|
|
;;
|
|
*)
|
|
echo -e "${RED}未知命令: $1${NC}"
|
|
exit 1
|
|
;;
|
|
esac
|
|
else
|
|
# 无参数,显示交互式菜单
|
|
show_menu
|
|
fi
|