diff --git a/backend-integration/api-proxy.js b/backend-integration/api-proxy.js index b14c03cb..611f0cd2 100644 --- a/backend-integration/api-proxy.js +++ b/backend-integration/api-proxy.js @@ -40,7 +40,7 @@ const { URL } = require('url'); // 配置 // ═══════════════════════════════════════════════════════ const PORT = parseInt(process.env.PROXY_PORT || '3721', 10); -const ALLOWED_ORIGINS = (process.env.ALLOWED_ORIGINS || '*').split(',').map(s => s.trim()); +const ALLOWED_ORIGINS = (process.env.ALLOWED_ORIGINS || 'https://qinfendebingshuo.github.io,https://guanghulab.com,http://localhost:8765').split(',').map(s => s.trim()); const RATE_LIMIT_RPM = parseInt(process.env.RATE_LIMIT_RPM || '10', 10); // 模型端点映射 @@ -243,8 +243,8 @@ function proxyStream(upstreamUrl, apiKey, requestBody, res) { reject(err); }); - // Set timeout (30 seconds for connection, but streaming can be longer) - upstream.setTimeout(30000, () => { + // Connection timeout only — once response headers arrive, streaming can run longer + upstream.setTimeout(60000, () => { upstream.destroy(new Error('连接超时')); }); @@ -312,6 +312,10 @@ async function handleChat(req, res) { return; } + // Validate numeric parameters + const temp = typeof temperature === 'number' ? Math.max(0, Math.min(2, temperature)) : 0.8; + const maxTok = typeof max_tokens === 'number' ? Math.max(1, Math.min(8192, Math.floor(max_tokens))) : 2000; + // Determine provider const prov = provider || 'deepseek'; const config = MODEL_CONFIG[prov]; @@ -341,8 +345,8 @@ async function handleChat(req, res) { model: mdl, messages: messages, stream: stream !== false, // Default to streaming - temperature: temperature ?? 0.8, - max_tokens: max_tokens ?? 2000 + temperature: temp, + max_tokens: maxTok }; // If system prompt provided, prepend as system message @@ -352,7 +356,7 @@ async function handleChat(req, res) { const upstreamUrl = config.base + '/chat/completions'; - console.log(`[代理] ${clientId} → ${prov}/${mdl} (${messages.length}条消息)`); + console.log(`[代理] ${prov}/${mdl}`); try { await proxyStream(upstreamUrl, apiKey, upstreamBody, res); diff --git a/backend-integration/nginx-api-proxy.conf b/backend-integration/nginx-api-proxy.conf index d326c6b9..cc36436b 100644 --- a/backend-integration/nginx-api-proxy.conf +++ b/backend-integration/nginx-api-proxy.conf @@ -20,7 +20,7 @@ location /api/ { proxy_cache off; chunked_transfer_encoding on; - # 长连接超时(AI 生成可能需要较长时间) - proxy_read_timeout 120s; - proxy_send_timeout 120s; + # 长连接超时(与 Node.js api-proxy 的 60s 连接超时对齐,流式响应可更长) + proxy_read_timeout 90s; + proxy_send_timeout 60s; } diff --git a/docs/index.html b/docs/index.html index 5cacc221..c1c4cf94 100644 --- a/docs/index.html +++ b/docs/index.html @@ -323,13 +323,13 @@ footer{padding:12px 18px 16px;background:var(--s1);border-top:1px solid var(--bo
@@ -515,13 +515,13 @@ footer{padding:12px 18px 16px;background:var(--s1);border-top:1px solid var(--bo
@@ -1188,7 +1188,7 @@ function onProv(pv,ctx){ } // Proxy mode: API key is optional (managed server-side) const ki = document.getElementById(keyInp); - if(ki) ki.placeholder = isProxy ? '后端代理模式 — 无需填写 API 密钥(服务器端统一管理)' : (ctx==='s'?'粘贴任意 API 密钥,点击右侧按钮自动识别提供商和模型':'留空 = 保持当前密钥不变;粘贴新密钥即替换'); + if(ki) ki.placeholder = isProxy ? '后端代理模式(无需密钥)' : (ctx==='s'?'粘贴任意 API 密钥,点击右侧按钮自动识别提供商和模型':'留空 = 保持当前密钥不变;粘贴新密钥即替换'); } function fillModels(selId, pv, cur){ @@ -1584,9 +1584,10 @@ async function streamReply(txt){ const url = isProxy ? (A.base+'/chat') : (A.base+'/chat/completions'); const headers = {'Content-Type':'application/json'}; if(!isProxy && A.key) headers['Authorization'] = 'Bearer '+A.key; + const allMsgs = [{role:'system',content:sysPrompt()},...A.msgs]; const reqBody = isProxy - ? {provider:A.prov==='proxy'?undefined:A.prov, model:A.mdl, messages:[{role:'system',content:sysPrompt()},...A.msgs], stream:true, temperature:0.8, max_tokens:2000, system:sysPrompt()} - : {model:A.mdl, messages:[{role:'system',content:sysPrompt()},...A.msgs], stream:true, temperature:0.8, max_tokens:2000}; + ? {model:A.mdl, messages:allMsgs, stream:true, temperature:0.8, max_tokens:2000} + : {model:A.mdl, messages:allMsgs, stream:true, temperature:0.8, max_tokens:2000}; const res=await fetch(url,{ method:'POST', headers:headers,