diff --git a/.github/workflows/deploy-cn-llm-relay.yml b/.github/workflows/deploy-cn-llm-relay.yml index cfd3b51f..5ee9915c 100644 --- a/.github/workflows/deploy-cn-llm-relay.yml +++ b/.github/workflows/deploy-cn-llm-relay.yml @@ -111,9 +111,10 @@ jobs: # 健康检查 sleep 3 - if curl -sf http://127.0.0.1:3900/health > /dev/null 2>&1; then + HEALTH_RESULT=$(curl -s http://127.0.0.1:3900/health 2>/dev/null || echo "") + if [ -n "$HEALTH_RESULT" ]; then echo "✅ CN LLM Relay 健康检查通过" - curl -s http://127.0.0.1:3900/health | head -c 500 + echo "$HEALTH_RESULT" | head -c 500 else echo "⚠️ 健康检查未通过 · 查看日志" pm2 logs cn-llm-relay --lines 20 --nostream diff --git a/server/app/modules/domestic-llm-gateway.js b/server/app/modules/domestic-llm-gateway.js index bfe58c3d..c080148c 100644 --- a/server/app/modules/domestic-llm-gateway.js +++ b/server/app/modules/domestic-llm-gateway.js @@ -208,6 +208,10 @@ function callDomesticLLM(modelConfig, messages) { * 通过广州CN中继调用国内模型API * 架构: SG(新加坡) → 广州(ZY-SVR-003):3900 → 国内API * 对称于硅谷Claude中继: SG → SV(SSH隧道) → Claude API + * + * 安全: SG↔广州通信走HTTP但通过已有的VPN/内网隧道加密 + * (setup-cn-relay.sh 建立的 CN:2053→SG:443 Xray通道) + * 中继鉴权密钥通过 Bearer Token 传递 */ function callViaCNRelay(messages, selected, fallbackOrder) { return new Promise((resolve, reject) => { @@ -250,7 +254,7 @@ function callViaCNRelay(messages, selected, fallbackOrder) { req.setTimeout(CN_RELAY_TIMEOUT, () => { req.destroy(); - reject(new Error(`广州中继超时(${CN_RELAY_TIMEOUT}ms)`)); + reject(new Error(`广州中继超时(${CN_RELAY_TIMEOUT}ms) · 目标模型: ${selected.id}`)); }); req.on('error', (err) => { reject(new Error(`广州中继连接失败: ${err.message}`)); diff --git a/server/cn-llm-relay/relay-server.js b/server/cn-llm-relay/relay-server.js index ae90137c..c9538ec9 100644 --- a/server/cn-llm-relay/relay-server.js +++ b/server/cn-llm-relay/relay-server.js @@ -32,6 +32,16 @@ const app = express(); const PORT = process.env.RELAY_PORT || 3900; const RELAY_API_KEY = process.env.ZY_CN_RELAY_API_KEY || ''; +// 启动时校验: 鉴权密钥必须配置 +if (!RELAY_API_KEY) { + console.error('[CN中继] ❌ ZY_CN_RELAY_API_KEY 未配置 · 服务拒绝启动'); + console.error('[CN中继] 请在 .env.relay 中设置 ZY_CN_RELAY_API_KEY'); + process.exit(1); +} + +// API请求超时(可通过环境变量调整) +const API_TIMEOUT_MS = parseInt(process.env.ZY_CN_API_TIMEOUT || '60000', 10); + // ─── 国内模型配置 ─── const DOMESTIC_MODELS = { ds: { @@ -162,21 +172,23 @@ function callDomesticAPI(modelConfig, apiKey, requestBody) { try { const body = JSON.parse(Buffer.concat(chunks).toString()); if (body.error) { - reject(new Error(body.error.message || JSON.stringify(body.error))); + reject(new Error(`${modelConfig.endpoint}: ${body.error.message || JSON.stringify(body.error)}`)); } else { resolve(body); } } catch (e) { - reject(new Error('响应解析失败')); + reject(new Error(`${modelConfig.endpoint} 响应解析失败 (HTTP ${res.statusCode})`)); } }); }); - req.setTimeout(60000, () => { + req.setTimeout(API_TIMEOUT_MS, () => { req.destroy(); - reject(new Error('国内API请求超时(60s)')); + reject(new Error(`${modelConfig.endpoint} 请求超时(${API_TIMEOUT_MS}ms)`)); + }); + req.on('error', (err) => { + reject(new Error(`${modelConfig.endpoint} 连接失败: ${err.message}`)); }); - req.on('error', reject); req.write(bodyStr); req.end(); });