fix: address code review feedback - startup validation, error context, security notes

- Fail fast on startup if ZY_CN_RELAY_API_KEY is not configured
- Add model endpoint context to error messages for debugging
- Make API timeout configurable via environment variable
- Deduplicate health check curl in workflow
- Add security note about VPN-encrypted relay channel

Agent-Logs-Url: https://github.com/qinfendebingshuo/guanghulab/sessions/f6db708e-8b10-4339-b48f-db5f111ae5cd

Co-authored-by: qinfendebingshuo <207279273+qinfendebingshuo@users.noreply.github.com>
This commit is contained in:
copilot-swe-agent[bot] 2026-04-11 14:21:44 +00:00 committed by GitHub
parent 91c957d85e
commit 6146dd3aea
No known key found for this signature in database
GPG Key ID: B5690EEEBB952194
3 changed files with 25 additions and 8 deletions

View File

@ -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

View File

@ -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:2053SG: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}`));

View File

@ -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();
});