#!/bin/bash # ============================================================ # Bookworm Web API — 稳定性优化补丁部署脚本 # 用法: ssh root@8.138.11.105 'bash -s' < deploy-patches.sh # ============================================================ set -euo pipefail WEBDIR="/opt/bookworm-web" BACKUP="$WEBDIR/backups/pre-stability-$(date +%Y%m%d_%H%M%S)" echo "=========================================" echo " Bookworm API 稳定性补丁 v1.0" echo "=========================================" # 1. 备份当前文件 echo "[1/6] 备份当前文件..." mkdir -p "$BACKUP/src" "$BACKUP/routes" cp "$WEBDIR/src/proxy.js" "$BACKUP/src/" cp "$WEBDIR/src/llm-router.js" "$BACKUP/src/" cp "$WEBDIR/src/circuit-breaker.js" "$BACKUP/src/" cp "$WEBDIR/routes/admin.js" "$BACKUP/routes/" echo " 备份到: $BACKUP" # 2. 部署 proxy-v2 echo "[2/6] 部署 src/proxy.js (keepAlive + 重试 + SSE心跳)..." cat > "$WEBDIR/src/proxy.js" << 'PROXYEOF' PROXY_PLACEHOLDER PROXYEOF echo " [OK] proxy.js 已更新" # 3. 部署 llm-router-v2 echo "[3/6] 部署 src/llm-router.js (SSE心跳 + backpressure + 重试)..." cat > "$WEBDIR/src/llm-router.js" << 'ROUTEREOF' ROUTER_PLACEHOLDER ROUTEREOF echo " [OK] llm-router.js 已更新" # 4. 部署 circuit-breaker-v2 echo "[4/6] 部署 src/circuit-breaker.js (半衰期 + 管理端点 + 统计)..." cat > "$WEBDIR/src/circuit-breaker.js" << 'CBEOF' CB_PLACEHOLDER CBEOF echo " [OK] circuit-breaker.js 已更新" # 5. 追加 Circuit Breaker 管理端点到 admin.js echo "[5/6] 追加 Circuit Breaker 管理端点..." # 检查是否已有 circuit-breaker 路由 if grep -q 'circuit-breaker' "$WEBDIR/routes/admin.js" 2>/dev/null; then echo " [SKIP] admin.js 已含 circuit-breaker 路由" else # 在 admin.js 末尾的 }; 之前注入 # 策略: 在 server.js 中追加路由注册 cat >> "$WEBDIR/server.js.cb-patch" << 'PATCHEOF' // ─── Circuit Breaker 管理端点 (稳定性补丁) ─── try { const patchAdminRoutes = require('./patches/admin-cb-routes'); patchAdminRoutes(routes, deps); } catch (_e) { console.warn('[patch] CB admin routes failed:', _e.message); } PATCHEOF # 实际注入方式: 直接修改 admin.js 在最后的 }; 前插入 ADMIN_FILE="$WEBDIR/routes/admin.js" # 安全追加到函数体末尾 (最后一个 }; 前) INJECT=$(cat << 'INJECTEOF' // ─── Circuit Breaker 管理端点 (稳定性补丁) ─── routes['GET:/v1/admin/circuit-breaker'] = async (req, res) => { requireAdmin(req); const cb = deps.circuitBreaker; const cbStatus = cb.getStatus ? cb.getStatus() : {}; const cbLog = cb.getTransitionLog ? cb.getTransitionLog(20) : []; json(res, 200, { ok: true, breakers: cbStatus, recentTransitions: cbLog }); }; routes['POST:/v1/admin/circuit-breaker/reset'] = async (req, res) => { requireAdmin(req); const body = await parseJsonBody(req); const cb = deps.circuitBreaker; if (body.provider) { cb.reset(body.provider); json(res, 200, { ok: true, reset: body.provider }); } else if (cb.resetAll) { cb.resetAll(); json(res, 200, { ok: true, reset: 'all' }); } else { json(res, 400, { error: '请指定 provider' }); } }; INJECTEOF ) # 用 sed 在最后的 }; 前插入 (admin.js 最后一行是 };) sed -i '$ i\'"$INJECT" "$ADMIN_FILE" 2>/dev/null || { # sed 失败则用 python3 python3 -c " import re with open('$ADMIN_FILE', 'r') as f: content = f.read() inject = '''$INJECT''' # 在最后一个 }; 前插入 pos = content.rfind('};') if pos > 0: content = content[:pos] + inject + '\n' + content[pos:] with open('$ADMIN_FILE', 'w') as f: f.write(content) print(' [OK] admin.js 已注入 CB 端点 (python)') else: print(' [WARN] 未找到插入点') " } echo " [OK] admin.js 已注入 CB 管理端点" rm -f "$WEBDIR/server.js.cb-patch" fi # 6. 清理 Nginx 重复配置 echo "[6/6] 检查 Nginx 配置..." NGINX_CONF="/etc/nginx/conf.d/bookworm-web.conf" if [ -f "$NGINX_CONF" ]; then # 统计 server 块数量 SERVER_COUNT=$(grep -c 'server_name bookworm.letcareme.com;' "$NGINX_CONF" 2>/dev/null || echo 0) if [ "$SERVER_COUNT" -gt 2 ]; then echo " [WARN] 发现 $SERVER_COUNT 个 server 块 (应为 2: HTTP+HTTPS)" echo " 建议手动清理: vim $NGINX_CONF" else echo " [OK] Nginx 配置正常" fi fi echo "" echo "=========================================" echo " 补丁部署完成!" echo "=========================================" echo "" echo " 下一步:" echo " 1. 语法检查: cd $WEBDIR && node --check server.js" echo " 2. 重启服务: pm2 reload bookworm-web" echo " 3. 验证健康: curl http://127.0.0.1:3211/health" echo " 4. 验证CB端点: curl -H 'Authorization: Admin TOKEN' http://127.0.0.1:3211/v1/admin/circuit-breaker" echo "" echo " 回滚:" echo " cp $BACKUP/src/* $WEBDIR/src/" echo " cp $BACKUP/routes/* $WEBDIR/routes/" echo " pm2 reload bookworm-web" echo "========================================="