修复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:
parent
0e80a30eff
commit
2ff4036bb4
110
docs/index.html
110
docs/index.html
|
|
@ -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 (左面板实时数据)
|
||||
════════════════════════════════════════ */
|
||||
|
|
|
|||
|
|
@ -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: '聊天服务暂时异常,请稍后重试' });
|
||||
}
|
||||
});
|
||||
|
||||
|
|
|
|||
|
|
@ -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>
|
||||
|
|
|
|||
|
|
@ -0,0 +1,8 @@
|
|||
# guanghulab.com — 光湖实验室 ICP备案落地页
|
||||
# 允许搜索引擎索引首页
|
||||
User-agent: *
|
||||
Allow: /
|
||||
Disallow: /health
|
||||
|
||||
# 站点地图(落地页仅有一个页面)
|
||||
# Sitemap: https://guanghulab.com/sitemap.xml
|
||||
Loading…
Reference in New Issue