142 lines
4.9 KiB
Bash
142 lines
4.9 KiB
Bash
|
|
#!/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 "========================================="
|