#!/usr/bin/env bash set -Eeuo pipefail APP_NAME="${CC_WEB_PM2_NAME:-ccweb}" ENTRY_FILE="${CC_WEB_ENTRY:-server.js}" SCRIPT_DIR="$(cd "$(dirname "${BASH_SOURCE[0]}")" && pwd)" APP_DIR="${SCRIPT_DIR}" ENTRY_PATH="${APP_DIR}/${ENTRY_FILE}" log() { printf '[cc-web pm2] %s\n' "$*" } fail() { printf '[cc-web pm2] ERROR: %s\n' "$*" >&2 exit 1 } ensure_command() { local cmd="$1" local hint="$2" if ! command -v "${cmd}" >/dev/null 2>&1; then fail "${cmd} 未安装。${hint}" fi } install_pm2() { if command -v pm2 >/dev/null 2>&1; then log "PM2 已安装: $(command -v pm2)" return fi log "未检测到 PM2,开始安装..." if npm install -g pm2; then log "PM2 安装完成" return fi if command -v sudo >/dev/null 2>&1; then log "普通 npm 全局安装失败,尝试使用 sudo 安装 PM2..." sudo npm install -g pm2 log "PM2 安装完成" return fi fail "PM2 自动安装失败,且当前环境没有 sudo。请先手动安装: npm install -g pm2" } install_dependencies() { cd "${APP_DIR}" if [[ -f package-lock.json ]]; then log "安装项目依赖: npm ci" npm ci else log "安装项目依赖: npm install" npm install fi } start_or_restart_app() { cd "${APP_DIR}" if pm2 describe "${APP_NAME}" >/dev/null 2>&1; then log "PM2 应用已存在,执行重启: ${APP_NAME}" pm2 restart "${APP_NAME}" --update-env else log "启动 PM2 应用: ${APP_NAME}" pm2 start "${ENTRY_PATH}" --name "${APP_NAME}" --cwd "${APP_DIR}" fi log "保存 PM2 进程快照" pm2 save log "当前 PM2 状态" pm2 status "${APP_NAME}" } main() { if [[ "$(id -u)" -eq 0 ]]; then fail "请使用非 root 用户执行。该服务会启动 Claude/Codex 子进程,root 运行风险过高。" fi [[ -f "${ENTRY_PATH}" ]] || fail "找不到入口文件: ${ENTRY_PATH}" ensure_command node "请先安装 Node.js 18 或更高版本。" ensure_command npm "请先安装 npm。" local node_major node_major="$(node -p "Number(process.versions.node.split('.')[0])")" if [[ "${node_major}" -lt 18 ]]; then fail "Node.js 版本过低,当前为 $(node -v),要求 >= 18。" fi install_pm2 install_dependencies start_or_restart_app log "完成。若需要开机自启,请执行 pm2 startup,并按输出提示执行生成的命令。" } main "$@"