修复guanghulab.com落地页SEO+guanghulab.online聊天API错误处理+重连机制

Agent-Logs-Url: https://github.com/qinfendebingshuo/guanghulab/sessions/f776c06a-3f23-484d-98a9-322eba565fcf

Co-authored-by: qinfendebingshuo <207279273+qinfendebingshuo@users.noreply.github.com>
This commit is contained in:
copilot-swe-agent[bot] 2026-04-12 06:28:30 +00:00 committed by GitHub
parent 0e80a30eff
commit 2ff4036bb4
No known key found for this signature in database
GPG Key ID: B5690EEEBB952194
4 changed files with 157 additions and 34 deletions

View File

@ -2303,6 +2303,7 @@ async function send(){
// 3. 调用API —— 优先用用户自己的API Key直接走用户网络其次尝试后端
sending=true;document.getElementById('chatSnd').disabled=true;showTyp();
var replied=false;
var backendError='';
// 用户配了API Key → 直接用用户的网络通道调用,不经过后端
if(apiCfg.connected&&apiCfg.key){
@ -2327,28 +2328,48 @@ async function send(){
document.getElementById('mM').textContent=apiCfg.model;
replied=true;
}
}catch(e){}
}catch(e){backendError='用户API: '+(e.name==='TimeoutError'?'请求超时':e.message)}
}
// 用户没配Key或调用失败 → 尝试后端
// 用户没配Key或调用失败 → 尝试后端(含自动重试)
if(!replied){
try{
var res=await fetch('/api/chat',{method:'POST',headers:{'Content-Type':'application/json'},body:JSON.stringify({message:msg,userId:usr.id}),signal:AbortSignal.timeout(60000)});
var data=await res.json();hideTyp();
if(data.message){
replied=true;
chatMsg(data.message,'b',{m:data.model||'铸渊',t:data.tier||''});
if(data.tier!=='free')mCalls++;
document.getElementById('mC').textContent=mCalls;
document.getElementById('chatTg').textContent=data.model||'ready';
if(data.model)document.getElementById('mM').textContent=data.model;
var retryCount=0;var maxRetries=2;
while(retryCount<=maxRetries&&!replied){
try{
var res=await fetch('/api/chat',{method:'POST',headers:{'Content-Type':'application/json'},body:JSON.stringify({message:msg,userId:usr.id}),signal:AbortSignal.timeout(retryCount===0?60000:30000)});
if(!res.ok){
backendError='服务器返回 '+res.status;
retryCount++;continue;
}
var data=await res.json();hideTyp();
if(data.message){
replied=true;
chatMsg(data.message,'b',{m:data.model||'铸渊',t:data.tier||''});
if(data.tier!=='free')mCalls++;
document.getElementById('mC').textContent=mCalls;
document.getElementById('chatTg').textContent=data.model||'ready';
if(data.model)document.getElementById('mM').textContent=data.model;
}else if(data.error){
backendError=data.message||'服务端错误';
retryCount++;continue;
}
break;
}catch(e){
backendError=e.name==='TimeoutError'?'后端响应超时':'网络连接失败';
retryCount++;
if(retryCount<=maxRetries){await new Promise(function(r){setTimeout(r,1000*retryCount)})}
}
}catch(e){}
}
}
// 所有外部通道都失败 → 用本地智能回复(用户浏览器就是计算资源)
// 所有外部通道都失败 → 用本地智能回复 + 显示连接状态
if(!replied){
hideTyp();chatMsg(offline(msg),'b',{m:'铸渊',t:'本地智能'});
hideTyp();
var offlineMsg=offline(msg);
if(backendError){offlineMsg+='\n\n⚠ 后端状态: '+backendError+'\n点击右下角💬重试或配置API密钥直连大模型。'}
chatMsg(offlineMsg,'b',{m:'铸渊',t:'本地智能'});
document.getElementById('chatTg').textContent='离线';
document.getElementById('chatTg').style.color='var(--err,#f87171)';
}
sending=false;document.getElementById('chatSnd').disabled=false;
@ -2761,7 +2782,11 @@ checkConn();setInterval(checkConn,30000);
chatOverlay.classList.toggle('open',chatOpen);
fab.classList.toggle('on',chatOpen);
fab.textContent=chatOpen?'✕':'💬';
if(chatOpen)setTimeout(function(){chatIn.focus()},200);
if(chatOpen){
setTimeout(function(){chatIn.focus()},200);
// 打开聊天时预检后端连接状态
checkChatBackend();
}
};
fab.onclick=function(e){
if(hasMoved){hasMoved=false;return}
@ -2773,6 +2798,59 @@ checkConn();setInterval(checkConn,30000);
};
})();
/* ════════════════════════════════════════
CHAT BACKEND HEALTH CHECK (打开聊天时预检)
════════════════════════════════════════ */
var chatBackendOk=null;
async function checkChatBackend(){
var tg=document.getElementById('chatTg');
try{
var r=await fetch('/api/health',{signal:AbortSignal.timeout(5000)});
if(r.ok){
chatBackendOk=true;
if(tg){tg.textContent='ready';tg.style.color=''}
}else{
chatBackendOk=false;
if(tg){tg.textContent='后端异常('+r.status+')';tg.style.color='var(--err,#f87171)'}
}
}catch(e){
chatBackendOk=false;
if(tg){tg.textContent='后端未连接';tg.style.color='var(--err,#f87171)'}
// 显示连接提示
if(chatMs&&!document.getElementById('chatConnTip')){
var tip=document.createElement('div');
tip.id='chatConnTip';
tip.className='cm s';
tip.innerHTML='⚠️ 后端服务暂时未连接。本地智能回复可用配置API密钥可解锁AI对话。<br><button onclick="retryChatBackend()" style="margin-top:6px;padding:4px 12px;border-radius:4px;border:1px solid var(--accent,#6ea8fe);background:transparent;color:var(--accent,#6ea8fe);cursor:pointer;font-size:12px">🔄 重新连接</button>';
chatMs.appendChild(tip);
chatMs.scrollTop=chatMs.scrollHeight;
}
}
}
async function retryChatBackend(){
var tip=document.getElementById('chatConnTip');
if(tip)tip.remove();
var tg=document.getElementById('chatTg');
if(tg){tg.textContent='连接中...';tg.style.color='var(--warn,#fbbf24)'}
try{
var r=await fetch('/api/chat/reconnect',{method:'POST',signal:AbortSignal.timeout(15000)});
var data=await r.json();
if(data.connected){
chatBackendOk=true;
if(tg){tg.textContent='ready';tg.style.color=''}
chatMsg(data.message||'🌊 铸渊已重新连接!','b',{m:'铸渊',t:'reconnect'});
}else{
chatBackendOk=false;
if(tg){tg.textContent='离线';tg.style.color='var(--err,#f87171)'}
chatMsg('⚠️ 后端大模型暂时不可用。本地智能回复仍然可用。','b',{m:'铸渊',t:'本地'});
}
}catch(e){
chatBackendOk=false;
if(tg){tg.textContent='离线';tg.style.color='var(--err,#f87171)'}
chatMsg('⚠️ 无法连接后端服务。请稍后重试或配置API密钥。','b',{m:'铸渊',t:'本地'});
}
}
/* ════════════════════════════════════════
SYSTEM STATUS (左面板实时数据)
════════════════════════════════════════ */

View File

@ -291,24 +291,42 @@ app.post('/api/chat', async (req, res) => {
// 降级到通用聊天引擎
if (chatEngine) {
const result = await chatEngine.chat(sessionId, message);
return res.json({
success: true,
...result,
sessionId
});
try {
const result = await chatEngine.chat(sessionId, message);
return res.json({
success: true,
...result,
sessionId
});
} catch (ceErr) {
console.error(`[聊天引擎] 通用引擎异常: ${ceErr.message}`);
// 继续到离线回复
}
}
// 所有引擎都不可用
// 所有引擎都不可用 — 仍然返回成功,给用户有意义的反馈
const engineStatus = [];
if (!domesticGateway) engineStatus.push('国内模型网关未加载');
else {
try {
const gwStats = domesticGateway.getGatewayStats();
if (gwStats.availableModels === 0) engineStatus.push('无可用模型API密钥');
else engineStatus.push(`已配置${gwStats.availableModels}个模型`);
} catch (e) { engineStatus.push('网关状态未知'); }
}
if (!chatEngine) engineStatus.push('通用引擎未加载');
res.json({
success: true,
message: '💫 铸渊正在唤醒中...聊天引擎尚未加载。请稍后再试。',
message: '💫 铸渊正在唤醒中...聊天引擎尚未就绪。请稍后再试或在聊天面板中配置你的API密钥直连大模型。',
model: 'offline',
tier: 'free',
engineStatus: engineStatus.join(' · '),
sessionId
});
} catch (err) {
res.status(500).json({ error: true, message: err.message });
console.error(`[聊天API] 未捕获异常: ${err.message}`);
res.status(500).json({ error: true, message: '聊天服务暂时异常,请稍后重试' });
}
});

View File

@ -3,8 +3,18 @@
<head>
<meta charset="UTF-8">
<meta name="viewport" content="width=device-width,initial-scale=1.0">
<title>光湖实验室 · GuanghuLab</title>
<meta name="description" content="光湖实验室 — 语言驱动的智能创作平台">
<title>光湖实验室 · GuanghuLab — 语言驱动的智能创作平台</title>
<meta name="description" content="光湖实验室GuanghuLab— 语言驱动的智能创作平台探索人类与AI共生的数字世界。ICP备案号陕ICP备2025071211号">
<meta name="keywords" content="光湖实验室,GuanghuLab,语言驱动,智能创作,AI创作平台">
<meta name="author" content="光湖实验室">
<meta name="robots" content="index, follow">
<link rel="canonical" href="https://guanghulab.com/">
<!-- Open Graph -->
<meta property="og:title" content="光湖实验室 · GuanghuLab">
<meta property="og:description" content="语言驱动的智能创作平台探索人类与AI共生的数字世界">
<meta property="og:type" content="website">
<meta property="og:url" content="https://guanghulab.com/">
<meta property="og:locale" content="zh_CN">
<link rel="icon" href="data:image/svg+xml,<svg xmlns='http://www.w3.org/2000/svg' viewBox='0 0 100 100'><text y='.9em' font-size='90'>🌊</text></svg>">
<style>
*{margin:0;padding:0;box-sizing:border-box}
@ -129,15 +139,21 @@ body{
/* ═══ 底部备案 ═══ */
.footer{
position:fixed;bottom:0;left:0;width:100%;z-index:10;
padding:20px;text-align:center;
background:linear-gradient(to top,rgba(7,11,26,0.95),transparent);
padding:20px 20px 24px;text-align:center;
background:linear-gradient(to top,rgba(7,11,26,0.98) 60%,transparent);
}
.icp-info{
display:flex;flex-direction:column;align-items:center;gap:6px;
}
.icp-link{
color:var(--text-dim);font-size:13px;text-decoration:none;
transition:color .3s;display:inline-flex;align-items:center;gap:6px;
}
.icp-link:hover{color:var(--text-secondary)}
.icp-link:hover{color:var(--glow-cyan);text-decoration:underline}
.icp-link .icp-icon{font-size:11px;opacity:.6}
.icp-copyright{
color:var(--text-dim);font-size:11px;opacity:.5;
}
/* ═══ 入场动画 ═══ */
.fade-up{animation:fadeUp 1s ease-out both}
@ -189,10 +205,13 @@ body{
<!-- 底部ICP备案 -->
<footer class="footer">
<a class="icp-link" href="https://beian.miit.gov.cn/" target="_blank" rel="noopener noreferrer">
<span class="icp-icon">🛡️</span>
陕ICP备2025071211号
</a>
<div class="icp-info">
<a class="icp-link" href="https://beian.miit.gov.cn/" target="_blank" rel="noopener noreferrer">
<span class="icp-icon">🛡️</span>
陕ICP备2025071211号
</a>
<span class="icp-copyright">© 2025 光湖实验室 GuanghuLab</span>
</div>
</footer>
<script>

View File

@ -0,0 +1,8 @@
# guanghulab.com — 光湖实验室 ICP备案落地页
# 允许搜索引擎索引首页
User-agent: *
Allow: /
Disallow: /health
# 站点地图(落地页仅有一个页面)
# Sitemap: https://guanghulab.com/sitemap.xml