From 1ffe3059afdfd8ee8a5db16cf7ec6cc16958003b Mon Sep 17 00:00:00 2001 From: zijunchiang Date: Tue, 15 Sep 2026 00:27:27 +0800 Subject: [PATCH] =?UTF-8?q?fix(deploy):=20install.sh=20=E4=B8=8D=E5=86=8D?= =?UTF-8?q?=E4=BE=9D=E8=B5=96=20sudo?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit 用户实测报错:deploy/install.sh: line 173: sudo: command not found 本脚本已经强制要求 root 权限,用 sudo 降权到服务用户本就多余, 且部分服务器(尤其精简系统/容器)根本没有安装 sudo。 - 新增 as_service_user():runuser → setpriv → su 依次回退, 并在三者都不可用时给出明确错误 - 替换全部 sudo -u 调用(npm ci / npm install / npm run build / 迁移) - 新增 http_get() / http_ok():curl → wget → node fetch 依次回退, 健康检查不再强依赖 curl - 启动前检查 systemctl(本部署依赖 systemd),缺失时给出可操作提示 - 修正 install.sh / uninstall.sh 中所有「用 sudo 执行」的提示文案 实测(桩替换 runuser 验证分派与参数传递): 有 runuser:runuser -u opendance -- npm ci --omit=dev --loglevel=error 仅 su:正确回退到 su HTTP:curl 路径与 node fetch 回退路径返回一致 --- deploy/install.sh | 58 ++++++++++++++++++++++++++++++++++++++------- deploy/uninstall.sh | 9 +++---- 2 files changed, 54 insertions(+), 13 deletions(-) diff --git a/deploy/install.sh b/deploy/install.sh index 63d3c41..5e376e7 100755 --- a/deploy/install.sh +++ b/deploy/install.sh @@ -37,8 +37,46 @@ log() { printf '\033[1;34m[install]\033[0m %s\n' "$*"; } warn() { printf '\033[1;33m[warn]\033[0m %s\n' "$*"; } die() { printf '\033[1;31m[error]\033[0m %s\n' "$*" >&2; exit 1; } +# 以服务用户身份执行命令。 +# 本脚本已要求 root,因此**不使用 sudo**(部分服务器根本没装 sudo, +# 且 root 降权本来也不需要它)。优先 runuser,其次 setpriv,最后 su。 +as_service_user() { + if command -v runuser >/dev/null 2>&1; then + runuser -u "$SERVICE_USER" -- "$@" + elif command -v setpriv >/dev/null 2>&1; then + setpriv --reuid="$SERVICE_USER" --regid="$SERVICE_USER" --init-groups -- "$@" + elif command -v su >/dev/null 2>&1; then + su -s /bin/bash "$SERVICE_USER" -c "$(printf '%q ' "$@")" + else + die "无法以 $SERVICE_USER 身份执行命令:runuser / setpriv / su 均不可用" + fi +} + +# HTTP GET,输出响应体。curl → wget → node(node 必有,且 22+ 自带 fetch) +http_get() { + if command -v curl >/dev/null 2>&1; then + curl -fsS -m 5 "$1" + elif command -v wget >/dev/null 2>&1; then + wget -q -O - -T 5 "$1" + else + "$NODE_BIN" -e 'fetch(process.argv[1]).then(r=>r.text()).then(t=>process.stdout.write(t)).catch(()=>process.exit(1))' "$1" + fi +} + +# 健康检查(只看成功与否) +http_ok() { + if command -v curl >/dev/null 2>&1; then + curl -fsS -m 3 "$1" >/dev/null 2>&1 + elif command -v wget >/dev/null 2>&1; then + wget -q -O /dev/null -T 3 "$1" + else + "$NODE_BIN" -e 'fetch(process.argv[1]).then(r=>process.exit(r.ok?0:1)).catch(()=>process.exit(1))' "$1" + fi +} + # ---------- 0. 前置检查 ---------- -[ "$(id -u)" -eq 0 ] || die "需要 root 权限(安装 systemd 服务与 nginx 站点):sudo bash deploy/install.sh" +[ "$(id -u)" -eq 0 ] || die "需要 root 权限(安装 systemd 服务与 nginx 站点)。 + 请以 root 身份执行,例如: su -c 'bash deploy/install.sh' 或 sudo bash deploy/install.sh" [ -f deploy/.env ] || die "缺少 deploy/.env,请先: cp deploy/.env.example deploy/.env && vi deploy/.env" @@ -54,6 +92,8 @@ API_PORT="${PORT:-3000}" [ -n "${DB_PASS:-}" ] || die "deploy/.env 中 DB_PASS 未设置" [ -n "${DB_NAME:-}" ] || die "deploy/.env 中 DB_NAME 未设置" +command -v systemctl >/dev/null 2>&1 || die "未找到 systemctl。本部署依赖 systemd 托管 Node 进程; + 若目标系统没有 systemd(如容器/WSL1),请改用 SKIP_NGINX=1 手动以进程方式运行,或换一台服务器。" command -v node >/dev/null 2>&1 || die "未找到 node。请安装 Node.js 22 或更高版本。" NODE_BIN="$(command -v node)" NODE_MAJOR="$(node -p 'process.versions.node.split(".")[0]')" @@ -83,7 +123,7 @@ if [ "$SRC_REAL" = "$APP_REAL" ]; then die "安装目录与源目录相同($APP_REAL)。 本脚本会先清空安装目录下的 web-server/ admin/ 再复制,直接在这里执行会删掉源文件。 请把发布包解压到别处,或用 APP_DIR=... 指定另一个安装目录: - sudo APP_DIR=/opt/opendance bash $0" + APP_DIR=/opt/opendance bash $0 (root 身份下执行)" fi case "$APP_REAL/" in "$SRC_REAL"/*) @@ -170,15 +210,15 @@ cd "$APP_DIR/web-server" log "安装运行期依赖(仅生产依赖,可能需要 1-3 分钟…)" if [ -f package-lock.json ]; then - sudo -u "$SERVICE_USER" npm ci --omit=dev --no-audit --no-fund --loglevel=error + as_service_user npm ci --omit=dev --no-audit --no-fund --loglevel=error else - sudo -u "$SERVICE_USER" npm install --omit=dev --no-audit --no-fund --loglevel=error + as_service_user npm install --omit=dev --no-audit --no-fund --loglevel=error fi if [ ! -f dist/main.js ]; then warn "未找到构建产物 dist/main.js,改为在服务器上构建(需要 devDependencies)" - sudo -u "$SERVICE_USER" npm install --no-audit --no-fund - sudo -u "$SERVICE_USER" npm run build + as_service_user npm install --no-audit --no-fund + as_service_user npm run build fi [ -f dist/main.js ] || die "构建产物缺失:$APP_DIR/web-server/dist/main.js" [ -f "$APP_DIR/admin/index.html" ] || die "管理端构建产物缺失:$APP_DIR/admin/index.html" @@ -187,7 +227,7 @@ fi log "执行 TypeORM 迁移(不使用 DB_SYNCHRONIZE)" set -a; . "$APP_DIR/deploy/.env"; set +a cd "$APP_DIR/web-server" -sudo -u "$SERVICE_USER" env \ +as_service_user env \ "PATH=$(dirname "$NODE_BIN"):/usr/local/bin:/usr/bin:/bin" \ "DB_HOST=${DB_HOST}" "DB_PORT=${DB_PORT:-3306}" "DB_USER=${DB_USER}" \ "DB_PASS=${DB_PASS}" "DB_NAME=${DB_NAME}" "DB_LOGGING=${DB_LOGGING:-false}" \ @@ -210,7 +250,7 @@ systemctl restart opendance-api log "等待 API 就绪…" READY=0 for _ in $(seq 1 30); do - if curl -fsS -m 3 "http://${API_HOST}:${API_PORT}/health" >/dev/null 2>&1; then READY=1; break; fi + if http_ok "http://${API_HOST}:${API_PORT}/health"; then READY=1; break; fi sleep 1 done if [ "$READY" -ne 1 ]; then @@ -218,7 +258,7 @@ if [ "$READY" -ne 1 ]; then journalctl -u opendance-api -n 40 --no-pager || true die "API 启动失败" fi -log "API 已就绪:$(curl -fsS -m 5 "http://${API_HOST}:${API_PORT}/health" | head -c 200)" +log "API 已就绪:$(http_get "http://${API_HOST}:${API_PORT}/health" | head -c 200)" # ---------- 5. nginx ---------- if [ "${SKIP_NGINX:-0}" = "1" ]; then diff --git a/deploy/uninstall.sh b/deploy/uninstall.sh index fc90944..5fe71fd 100755 --- a/deploy/uninstall.sh +++ b/deploy/uninstall.sh @@ -6,8 +6,8 @@ # 数据库从来不由本脚本管理,永不删除。 # # 用法: -# sudo bash deploy/uninstall.sh # 停服务、禁用站点,保留数据 -# sudo bash deploy/uninstall.sh --purge # 同时删除程序目录与上传文件 +# bash deploy/uninstall.sh # 停服务、禁用站点,保留数据(需 root) +# bash deploy/uninstall.sh --purge # 同时删除程序目录与上传文件 # ============================================================ set -euo pipefail @@ -21,7 +21,8 @@ log() { printf '\033[1;34m[uninstall]\033[0m %s\n' "$*"; } warn() { printf '\033[1;33m[warn]\033[0m %s\n' "$*"; } die() { printf '\033[1;31m[error]\033[0m %s\n' "$*" >&2; exit 1; } -[ "$(id -u)" -eq 0 ] || die "需要 root 权限:sudo bash deploy/uninstall.sh" +[ "$(id -u)" -eq 0 ] || die "需要 root 权限。请以 root 身份执行,例如: + su -c 'bash deploy/uninstall.sh' 或 sudo bash deploy/uninstall.sh" log "停止并禁用 opendance-api 服务" systemctl stop opendance-api 2>/dev/null || true @@ -47,6 +48,6 @@ else log "完成。已保留:" echo " 程序目录:$APP_DIR" echo " 上传目录:$STORAGE_DIR" - echo " 如需一并删除:sudo bash deploy/uninstall.sh --purge" + echo " 如需一并删除(root 身份):bash deploy/uninstall.sh --purge" fi echo " 数据库:始终由部署方自行管理,本脚本不做任何改动"