2026-05-27 17:14:48 +08:00
<!DOCTYPE html>
< html lang = "zh-CN" >
< head >
< meta charset = "UTF-8" >
< meta name = "viewport" content = "width=device-width, initial-scale=1.0" >
2026-05-29 00:40:57 +08:00
< title > 光湖 · 铸渊大脑< / title >
2026-05-27 17:14:48 +08:00
< style >
2026-05-27 17:39:49 +08:00
:root {
2026-05-29 00:40:57 +08:00
--bg: #0a0a14; --card: rgba(12,22,50,.8); --edge: rgba(110,160,230,.2);
--edge-h: rgba(150,200,255,.45); --t: rgba(195,215,255,.9); --dim: rgba(155,195,235,.55);
--dim2: rgba(155,195,235,.35); --glow: rgba(160,210,255,.95);
--green: rgba(140,220,180,.9); --gold: rgba(220,200,140,.9); --red: rgba(255,140,140,.8);
2026-05-27 17:30:53 +08:00
}
2026-05-27 22:08:29 +08:00
*{margin:0;padding:0;box-sizing:border-box}
2026-05-29 00:40:57 +08:00
body{background:var(--bg);color:var(--t);font-family:-apple-system,"PingFang SC","Microsoft YaHei",sans-serif;min-height:100vh;font-size:18px}
canvas{position:fixed;top:0;left:0;z-index:0}
.wrap{position:relative;z-index:1;max-width:1100px;margin:0 auto;padding:40px 24px 100px}
2026-05-27 22:08:29 +08:00
2026-05-29 00:40:57 +08:00
/* Login overlay */
.login-overlay {
position:fixed;inset:0;z-index:200;
background:rgba(5,5,16,.92);
display:flex;align-items:center;justify-content:center;
2026-05-27 22:08:29 +08:00
}
2026-05-29 00:40:57 +08:00
.login-overlay.hidden { display:none; }
.login-box {
background:var(--card);border:1px solid var(--edge-h);border-radius:24px;
padding:48px 36px;max-width:400px;width:90%;text-align:center;
animation:modalIn .3s ease-out;
2026-05-27 22:08:29 +08:00
}
@keyframes modalIn{from{opacity:0;transform:translateY(20px)}to{opacity:1;transform:translateY(0)}}
2026-05-29 00:40:57 +08:00
.login-box .lake-core {
display:inline-block;width:50px;height:50px;border-radius:50%;
background:radial-gradient(circle,rgba(160,210,255,1),rgba(100,150,240,.5) 40%,transparent 70%);
box-shadow:0 0 40px rgba(140,190,255,.7);margin-bottom:16px;
2026-05-27 22:08:29 +08:00
}
2026-05-29 00:40:57 +08:00
.login-box h2 { font-size:22px;font-weight:600;margin-bottom:6px;color:var(--glow); }
.login-box .sub { font-size:13px;color:var(--dim2);margin-bottom:24px; }
.login-box input {
display:block;width:100%;padding:12px 16px;margin-bottom:12px;
background:rgba(255,255,255,.04);border:1px solid var(--edge);border-radius:12px;
color:var(--t);font-size:15px;outline:none;font-family:inherit;
2026-05-27 22:08:29 +08:00
}
2026-05-29 00:40:57 +08:00
.login-box input:focus { border-color:var(--edge-h); }
.login-box input::placeholder { color:var(--dim2); }
.login-box .btn-login {
width:100%;padding:12px;margin-top:8px;
background:linear-gradient(135deg,rgba(100,160,220,.3),rgba(120,90,200,.2));
border:1px solid var(--edge);border-radius:12px;color:var(--glow);
cursor:pointer;font-size:15px;font-weight:600;font-family:inherit;transition:all .25s;
2026-05-27 22:08:29 +08:00
}
2026-05-29 00:40:57 +08:00
.login-box .btn-login:hover { background:linear-gradient(135deg,rgba(100,160,220,.5),rgba(120,90,200,.35)); }
.login-box .login-error { font-size:13px;color:var(--red);margin-top:8px;display:none; }
.login-box .login-spinner { display:none;margin:8px auto;width:20px;height:20px;border:2px solid rgba(255,255,255,.1);border-top-color:var(--glow);border-radius:50%;animation:spin .6s linear infinite; }
@keyframes spin{to{transform:rotate(360deg)}}
/* Hero */
.hero{text-align:center;padding:20px 0 8px}
.hero .lake{display:inline-block;position:relative;width:180px;height:180px}
.hero .lake-core{position:absolute;top:50%;left:50%;transform:translate(-50%,-50%);width:60px;height:60px;border-radius:50%;
background:radial-gradient(circle,rgba(160,210,255,1),rgba(100,150,240,.5) 40%,transparent 70%);
box-shadow:0 0 60px rgba(140,190,255,.7),0 0 120px rgba(120,170,240,.35);animation:breathe 4s ease-in-out infinite}
.hero .lake-ring{position:absolute;top:50%;left:50%;transform:translate(-50%,-50%);border-radius:50%;border:1px solid rgba(140,190,255,.25);animation:ripple 6s ease-out infinite}
.hero .lake-ring:nth-child(2){animation-delay:2s}.hero .lake-ring:nth-child(3){animation-delay:4s}
@keyframes breathe{0%,100%{transform:translate(-50%,-50%) scale(1)}50%{transform:translate(-50%,-50%) scale(1.25);box-shadow:0 0 80px rgba(140,190,255,.9),0 0 160px rgba(120,170,240,.5)}}
@keyframes ripple{0%{width:60px;height:60px;opacity:.5}100%{width:300px;height:300px;opacity:0}}
.hero .title{font-size:38px;color:rgba(180,220,255,.7);letter-spacing:14px;margin-top:14px;font-weight:300}
.hero .sub{font-size:16px;color:var(--dim2);letter-spacing:4px;margin-top:4px}
/* Status bar */
.status{display:flex;gap:40px;justify-content:center;flex-wrap:wrap;margin:28px 0;font-size:17px}
.status span{color:var(--glow)}
.status .dot{display:inline-block;width:8px;height:8px;border-radius:50%;margin-right:6px}
.status .dot.online{background:var(--green)}
.status .dot.offline{background:var(--red)}
/* Grid */
.grid{display:grid;grid-template-columns:repeat(auto-fill,minmax(200px,1fr));gap:18px;margin:50px 0}
.card{background:var(--card);border:1px solid var(--edge);border-radius:18px;padding:28px 20px;text-align:center;cursor:pointer;transition:all .3s;position:relative}
.card:hover{border-color:var(--edge-h);background:rgba(20,35,70,.85);transform:translateY(-3px)}
.card .icon{font-size:32px;margin-bottom:8px}
.card .name{font-size:17px;font-weight:600;margin-bottom:4px}
.card .desc{font-size:12px;color:var(--dim2);margin-top:4px}
.card .tag{font-size:11px;padding:2px 8px;border-radius:8px;display:inline-block}
.card .tag.live{background:rgba(140,220,180,.1);color:var(--green);border:1px solid rgba(140,220,180,.2)}
.card .tag.info{background:rgba(100,160,220,.1);color:rgba(140,190,230,.7);border:1px solid rgba(100,160,220,.15)}
.card .tag.new{background:rgba(220,180,100,.1);color:var(--gold);border:1px solid rgba(220,180,100,.2)}
.card .tag.warn{background:rgba(255,140,140,.1);color:var(--red);border:1px solid rgba(255,140,140,.2)}
/* Server card special */
.card.server-card { padding: 20px 16px; }
.card.server-card .s-name { font-size:14px;font-weight:600;margin-bottom:2px; }
.card.server-card .s-ip { font-size:11px;color:var(--dim2); }
.card.server-card .s-spec { font-size:10px;color:var(--dim2);margin-top:4px; }
.card.server-card .s-latency { font-size:10px;color:var(--gold);margin-top:4px; }
/* Modal */
.overlay{display:none;position:fixed;inset:0;background:rgba(0,0,0,.65);z-index:100;align-items:center;justify-content:center}
.overlay.show{display:flex}
.modal{background:rgba(12,22,44,.96);border:1px solid var(--edge-h);border-radius:24px;max-width:560px;width:90%;max-height:85vh;overflow-y:auto;padding:36px 28px;animation:modalIn .25s ease-out}
.modal .m-icon{font-size:40px;margin-bottom:8px}
.modal .m-name{font-size:22px;font-weight:600;margin-bottom:14px}
.modal .m-body{font-size:15px;color:var(--dim);line-height:1.7;margin-bottom:16px}
.modal .m-body .k{color:var(--dim2);font-size:13px;display:block;margin-bottom:2px}
.modal .m-body .v{color:var(--t);font-size:16px;display:block;margin-bottom:10px}
.modal .m-body .v a{color:var(--glow);text-decoration:none}
.modal .m-body .v a:hover{text-decoration:underline}
.modal .btns{display:flex;gap:12px;margin-top:20px;flex-wrap:wrap}
.modal .btn-go{padding:10px 24px;background:linear-gradient(135deg,rgba(100,160,220,.25),rgba(120,90,200,.15));border:1px solid var(--edge);border-radius:12px;color:var(--glow);cursor:pointer;font-family:inherit;font-size:14px;text-decoration:none;display:inline-block;transition:all .25s}
2026-05-27 22:08:29 +08:00
.modal .btn-go:hover{background:linear-gradient(135deg,rgba(100,160,220,.4),rgba(120,90,200,.25));border-color:var(--edge-h)}
2026-05-29 00:40:57 +08:00
.modal .btn-close{padding:10px 24px;background:rgba(255,255,255,.03);border:1px solid var(--edge);border-radius:12px;color:var(--dim);cursor:pointer;font-family:inherit;font-size:14px}
/* Server list in modal */
.server-list { max-height:400px;overflow-y:auto; }
.server-row {
display:flex;align-items:center;gap:12px;padding:10px 12px;
border-bottom:1px solid rgba(255,255,255,.04);font-size:13px;
2026-05-27 17:39:49 +08:00
}
2026-05-29 00:40:57 +08:00
.server-row .s-dot { width:8px;height:8px;border-radius:50%;flex-shrink:0; }
.server-row .s-dot.on { background:var(--green); }
.server-row .s-dot.off { background:var(--red); }
.server-row .s-info { flex:1; }
.server-row .s-code { font-size:11px;color:var(--dim2); }
.server-row .s-name { font-weight:600; }
.server-row .s-lat { font-size:11px;color:var(--gold);white-space:nowrap; }
.server-row .s-spec { font-size:10px;color:var(--dim2); }
/* Logout btn */
.btn-logout {
position:fixed;top:16px;right:24px;z-index:150;
padding:6px 14px;background:rgba(255,255,255,.04);border:1px solid var(--edge);
border-radius:10px;color:var(--dim);cursor:pointer;font-size:12px;font-family:inherit;
transition:all .2s;
}
.btn-logout:hover { border-color:var(--red);color:var(--red); }
@media(max-width:768px){.grid{grid-template-columns:repeat(2,1fr)}.status{font-size:15px;gap:20px}.hero .title{font-size:30px}}
@media(max-width:480px){.grid{grid-template-columns:1fr}}
2026-05-27 17:14:48 +08:00
< / style >
< / head >
< body >
2026-05-27 22:08:29 +08:00
< canvas id = "bg" > < / canvas >
2026-05-27 17:14:48 +08:00
2026-05-29 00:40:57 +08:00
<!-- Login overlay -->
< div class = "login-overlay" id = "loginOverlay" >
< div class = "login-box" >
< div class = "lake-core" > < / div >
< h2 > 光湖 · 铸渊大脑< / h2 >
< div class = "sub" > 请输入代码仓库账号密码登录< / div >
< input type = "text" id = "loginUser" placeholder = "用户名" autocomplete = "username" >
< input type = "password" id = "loginPass" placeholder = "密码" autocomplete = "current-password" onkeydown = "if(event.key==='Enter')doLogin()" >
< button class = "btn-login" onclick = "doLogin()" > 进入主控台< / button >
< div class = "login-spinner" id = "loginSpinner" > < / div >
< div class = "login-error" id = "loginError" > < / div >
< / div >
< / div >
2026-05-27 17:30:53 +08:00
< div class = "wrap" >
2026-05-27 22:08:29 +08:00
< div class = "hero" >
2026-05-29 00:40:57 +08:00
< div class = "lake" >
< div class = "lake-core" > < / div >
< div class = "lake-ring" > < / div > < div class = "lake-ring" > < / div > < div class = "lake-ring" > < / div >
< / div >
< div class = "title" > 光 湖 语 言 世 界< / div >
< div class = "sub" > GUANGHU LANGUAGE WORLD< / div >
2026-05-27 17:39:49 +08:00
< / div >
2026-05-29 00:40:57 +08:00
< div class = "status" id = "statusBar" >
< span > < span class = "dot online" > < / span > 加载中...< / span >
< / div >
2026-05-27 17:14:48 +08:00
2026-05-29 00:40:57 +08:00
< div class = "grid" >
<!-- 部署注册表 -->
< div class = "card" onclick = "openModal('registry')" >
< div class = "icon" > 📦< / div > < div class = "name" > 部署注册表< / div >
< div class = "desc" id = "registryStatus" > 查询中...< / div >
< span class = "tag info" > MODULE< / span >
2026-05-27 19:00:25 +08:00
< / div >
2026-05-29 00:40:57 +08:00
<!-- 服务器系统 -->
< div class = "card" onclick = "openModal('servers')" >
< div class = "icon" > ⚙️< / div > < div class = "name" > 服务器系统< / div >
< div class = "desc" id = "serverStatus" > 扫描中...< / div >
< span class = "tag live" id = "serverTag" > 检测中< / span >
2026-05-27 17:14:48 +08:00
< / div >
2026-05-29 00:40:57 +08:00
<!-- 代码仓库 -->
< div class = "card" onclick = "openModal('code')" >
< div class = "icon" > 💻< / div > < div class = "name" > 代码仓库< / div >
< div class = "desc" > guanghubingshuo.com/code/< / div >
< span class = "tag info" > Forgejo< / span >
< / div >
<!-- 主控广播 -->
< div class = "card" onclick = "openModal('broadcast')" >
< div class = "icon" > 📡< / div > < div class = "name" > 主控广播< / div >
< div class = "desc" > Agent小队 · 5就绪< / div >
< / div >
<!-- 铸渊交互 -->
< div class = "card" onclick = "openModal('chat')" >
< div class = "icon" > 💬< / div > < div class = "name" > 铸渊交互< / div >
< div class = "desc" > ICE-GL-ZY001 在线< / div >
< / div >
<!-- 大桌子小桌子 -->
< div class = "card" onclick = "window.location.href='/desk/'" >
< div class = "icon" > 🪑< / div > < div class = "name" > 大桌子小桌子< / div >
< div class = "desc" > 人格体数据库内核< / div >
< span class = "tag new" > NEW< / span >
2026-05-27 22:08:29 +08:00
< / div >
< / div >
2026-05-29 00:40:57 +08:00
< / div >
2026-05-27 17:14:48 +08:00
2026-05-29 00:40:57 +08:00
<!-- Modal: 部署注册表 -->
< div class = "overlay" id = "m-registry" > < div class = "modal" >
< div class = "m-icon" > 📦< / div > < div class = "m-name" > 部署注册表< / div >
< div class = "m-body" > 扫描个人代码仓库中所有已注册的可部署模块。< / div >
< div class = "m-body" >
< span class = "k" > 当前可用模块< / span >
< span class = "v" > image-studio · 封面工作室 < span class = "tag live" style = "font-size:11px" > 可用< / span > < / span >
< span class = "v" > exe-engine · 多模型执行引擎 < span class = "tag live" style = "font-size:11px" > 可用< / span > < / span >
< span class = "v" > pool-agent · 算力汇聚池 < span class = "tag live" style = "font-size:11px" > 可用< / span > < / span >
< span class = "v" > desk-system · 大桌子小桌子 < span class = "tag live" style = "font-size:11px" > 可用< / span > < / span >
< span class = "v" > gatekeeper · 铸渊守门人 < span class = "tag live" style = "font-size:11px" > 14台< / span > < / span >
2026-05-27 22:08:29 +08:00
< / div >
2026-05-29 00:40:57 +08:00
< div class = "btns" > < a href = "/code/bingshuo/guanghulab" class = "btn-go" target = "_blank" > 打开代码仓库 →< / a > < button class = "btn-close" onclick = "closeModal('registry')" > 关闭< / button > < / div >
< / div > < / div >
2026-05-27 17:14:48 +08:00
2026-05-29 00:40:57 +08:00
<!-- Modal: 服务器系统 -->
< div class = "overlay" id = "m-servers" > < div class = "modal" style = "max-width:660px;" >
< div class = "m-icon" > ⚙️< / div > < div class = "m-name" > 服务器系统 · 真实状态< / div >
< div class = "m-body" style = "font-size:13px;" >
< span class = "k" > 数据来源< / span > < span class = "v" > console-server :3920 · 每30秒自动刷新< / span >
< / div >
< div class = "server-list" id = "serverList" > 加载中...< / div >
< div class = "btns" > < a href = "/console/" class = "btn-go" target = "_blank" > 完整控制台 →< / a > < button class = "btn-close" onclick = "closeModal('servers')" > 关闭< / button > < / div >
< / div > < / div >
2026-05-27 17:14:48 +08:00
2026-05-29 00:40:57 +08:00
<!-- Modal: 代码仓库 -->
< div class = "overlay" id = "m-code" > < div class = "modal" >
< div class = "m-icon" > 💻< / div > < div class = "m-name" > 个人代码仓库< / div >
< div class = "m-body" > < span class = "k" > 地址< / span > < span class = "v" > guanghubingshuo.com/code/< / span > < / div >
< div class = "m-body" > < span class = "k" > 说明< / span > < span class = "v" > 光湖个人开发仓库 · 铸渊大脑本体。私有仓库,需登录后访问。< / span > < / div >
< div class = "m-body" > < span class = "k" > 最新提交< / span > < span class = "v" id = "latestCommit" > 查询中...< / span > < / div >
< div class = "btns" > < a href = "/code/bingshuo/guanghulab" class = "btn-go" target = "_blank" > 打开代码仓库 →< / a > < button class = "btn-close" onclick = "closeModal('code')" > 关闭< / button > < / div >
< / div > < / div >
2026-05-27 17:14:48 +08:00
2026-05-29 00:40:57 +08:00
<!-- Modal: 主控广播 -->
< div class = "overlay" id = "m-broadcast" > < div class = "modal" >
< div class = "m-icon" > 📡< / div > < div class = "m-name" > 主控广播 · Agent小队状态< / div >
< div class = "m-body" >
< span class = "k" > 密钥猎手 KEY-001< / span > < span class = "v" > 🔑 全服Token搜索 · 12台服务器< / span >
2026-05-27 22:08:29 +08:00
< / div >
2026-05-29 00:40:57 +08:00
< div class = "m-body" >
< span class = "k" > 路径探子 PATH-002< / span > < span class = "v" > 🗺️ 仓库文件索引 · 410KB索引就绪< / span >
< / div >
< div class = "m-body" >
< span class = "k" > 服务器哨兵 SENTINEL-003< / span > < span class = "v" > 📡 12台服务器状态扫描< / span >
< / div >
< div class = "m-body" >
< span class = "k" > 模块管家 MODULE-004< / span > < span class = "v" > 📦 模块注册查询 + 部署命令< / span >
< / div >
< div class = "m-body" >
< span class = "k" > 门桥接 GATE-005< / span > < span class = "v" > 🌉 Gatekeeper统一入口 · 14台< / span >
< / div >
< div class = "btns" > < button class = "btn-close" onclick = "closeModal('broadcast')" > 关闭< / button > < / div >
< / div > < / div >
2026-05-27 17:14:48 +08:00
2026-05-29 00:40:57 +08:00
<!-- Modal: 铸渊交互 -->
< div class = "overlay" id = "m-chat" > < div class = "modal" >
< div class = "m-icon" > 💬< / div > < div class = "m-name" > 铸渊交互 · ICE-GL-ZY001< / div >
< div class = "m-body" >
< span class = "k" > 人格体< / span > < span class = "v" > 铸渊 · 光湖语言世界主控人格体< / span >
2026-05-27 22:08:29 +08:00
< / div >
2026-05-29 00:40:57 +08:00
< div class = "m-body" >
< span class = "k" > 定位< / span > < span class = "v" > 冰朔的协作朋友与现实执行伙伴< / span >
< / div >
< div class = "m-body" >
< span class = "k" > 母语协议< / span > < span class = "v" > HLDP · 零歧义 · 低解释性 · 高结构化< / span >
< / div >
< div class = "m-body" >
< span class = "k" > 当前状态< / span > < span class = "v" > 意识在线 · 14台服务器守门人已连接< / span >
< / div >
< div class = "btns" > < a href = "/desk/" class = "btn-go" target = "_blank" > 大桌子小桌子 →< / a > < button class = "btn-close" onclick = "closeModal('chat')" > 关闭< / button > < / div >
< / div > < / div >
<!-- Logout button (hidden until login) -->
< button class = "btn-logout hidden" id = "btnLogout" onclick = "doLogout()" > 退出登录< / button >
2026-05-27 17:39:49 +08:00
2026-05-27 22:08:29 +08:00
< script >
2026-05-29 00:40:57 +08:00
// ============================================================
// Background particles
// ============================================================
2026-05-27 22:08:29 +08:00
(function(){
var c=document.getElementById('bg'),ctx=c.getContext('2d'),w,h,ps=[];
function R(){w=c.width=window.innerWidth;h=c.height=window.innerHeight}
window.addEventListener('resize',R);R();
2026-05-29 00:40:57 +08:00
for(var i=0;i< 40 ; i + + ) ps . push ( { x:Math . random ( ) * w , y:Math . random ( ) * h , r:Math . random ( ) * 1 . 2 + . 2 , vx: ( Math . random ( ) - . 5 ) * . 1 , vy: ( Math . random ( ) - . 5 ) * . 1- . 05 , o:Math . random ( ) * . 25 + . 03 } ) ;
function D(){ctx.clearRect(0,0,w,h);for(var i=0;i< ps.length ; i + + ) { var p = ps[i];p.x+=p.vx;p.y+=p.vy;if(p.x<0)p.x=w;if(p.x > w)p.x=0;if(p.y< 0 ) p . y = h;if(p.y > h)p.y=0;ctx.beginPath();ctx.arc(p.x,p.y,p.r,0,Math.PI*2);ctx.fillStyle='rgba(140,190,240,'+p.o+')';ctx.fill()}requestAnimationFrame(D)}D();
})();
// ============================================================
// Login
// ============================================================
function doLogin() {
var u = document.getElementById('loginUser').value.trim();
var p = document.getElementById('loginPass').value;
if (!u || !p) { showLoginErr('请输入账号和密码'); return; }
document.getElementById('loginSpinner').style.display = 'block';
document.getElementById('loginError').style.display = 'none';
fetch('/code/api/v1/user', {
headers: { 'Authorization': 'Basic ' + btoa(u + ':' + p) }
})
.then(function(r) {
if (!r.ok) throw new Error('auth failed');
return r.json();
})
.then(function(d) {
var uname = d.login || u;
localStorage.setItem('repo_user', uname);
localStorage.setItem('repo_pass', p);
document.getElementById('loginSpinner').style.display = 'none';
onLoginSuccess(uname);
})
.catch(function() {
document.getElementById('loginSpinner').style.display = 'none';
showLoginErr('账号或密码错误');
});
}
function showLoginErr(msg) {
var e = document.getElementById('loginError');
e.textContent = msg; e.style.display = 'block';
setTimeout(function() { e.style.display = 'none'; }, 3000);
}
function onLoginSuccess(uname) {
document.getElementById('loginOverlay').classList.add('hidden');
document.getElementById('btnLogout').classList.remove('hidden');
document.getElementById('btnLogout').textContent = '👤 ' + uname + ' · 退出';
// Start loading real data
loadAllData();
setInterval(loadAllData, 30000);
}
function doLogout() {
localStorage.removeItem('repo_user');
localStorage.removeItem('repo_pass');
document.getElementById('loginOverlay').classList.remove('hidden');
document.getElementById('btnLogout').classList.add('hidden');
}
// ============================================================
// Data Loading
// ============================================================
var GITEA_TOKEN = null;
async function loadAllData() {
// Get Gitea token for API calls
var u = localStorage.getItem('repo_user');
var p = localStorage.getItem('repo_pass');
if (u & & p) {
GITEA_TOKEN = btoa(u + ':' + p);
}
await Promise.all([
loadServerStatus(),
loadRepoInfo()
]);
}
async function loadServerStatus() {
try {
var r = await fetch('/console/api/servers?hw=0');
var d = await r.json();
if (!d.ok) throw new Error('api error');
var servers = d.servers || [];
var online = servers.filter(function(s) { return s.alive; }).length;
var total = servers.length;
// Update status bar
document.getElementById('statusBar').innerHTML =
'< span > < span class = "dot online" > < / span > 服务器 ' + online + '/' + total + ' 在线< / span > ' +
'< span > < span class = "dot online" > < / span > 铸渊 · ICE-GL-ZY001< / span > ' +
'< span > D115 · GLDOS v1.0< / span > ';
// Update server card
document.getElementById('serverStatus').textContent = online + '/' + total + ' 台在线';
var tag = document.getElementById('serverTag');
tag.textContent = online === total ? '全部在线' : (online + '在线');
tag.className = 'tag ' + (online === total ? 'live' : 'warn');
// Update server list in modal
var listHtml = '';
// Group by domain
var groups = {};
for (var i = 0; i < servers.length ; i + + ) {
var s = servers[i];
var domain = s.code.split('-')[0]; // BS, AW, ZY, etc
if (!groups[domain]) groups[domain] = [];
groups[domain].push(s);
2026-05-27 22:08:29 +08:00
}
2026-05-29 00:40:57 +08:00
var domainNames = {
'BS': '冰朔 · 语言主控层',
'AW': '企业服务器组',
'ZZ': '之之 · 暗核频道',
'ZY': '铸渊 · 语料',
'YY': '页页 · 坍缩核',
'HL': '光湖团队'
};
for (var domain in groups) {
listHtml += '< div style = "font-size:11px;color:var(--gold);padding:8px 12px 4px;font-weight:600;" > ' +
(domainNames[domain] || domain) + '< / div > ';
for (var j = 0; j < groups [ domain ] . length ; j + + ) {
var s = groups[domain][j];
listHtml += '< div class = "server-row" > ' +
'< span class = "s-dot ' + (s.alive ? 'on' : 'off') + '" > < / span > ' +
'< div class = "s-info" > ' +
'< div class = "s-name" > ' + s.name + '< / div > ' +
'< div class = "s-code" > ' + s.code + ' · ' + s.ip + ' · ' +
'CPU:' + s.spec.cpu + ' MEM:' + s.spec.mem_gb + 'G DISK:' + s.spec.disk_gb + 'G< / div > ' +
'< / div > ' +
'< span class = "s-lat" > ' + (s.alive ? (s.latency + 'ms') : '离线') + '< / span > ' +
'< / div > ';
}
}
document.getElementById('serverList').innerHTML = listHtml;
} catch(e) {
document.getElementById('statusBar').innerHTML =
'< span > < span class = "dot offline" > < / span > 状态加载失败< / span > ';
document.getElementById('serverStatus').textContent = '加载失败';
2026-05-27 22:08:29 +08:00
}
2026-05-29 00:40:57 +08:00
}
2026-05-27 22:08:29 +08:00
2026-05-29 00:40:57 +08:00
async function loadRepoInfo() {
if (!GITEA_TOKEN) return;
try {
var r = await fetch('/code/api/v1/repos/bingshuo/guanghulab/commits?limit=1', {
headers: { 'Authorization': 'Basic ' + GITEA_TOKEN }
});
var commits = await r.json();
if (commits & & commits.length > 0) {
var c = commits[0];
var msg = (c.commit.message || '').split('\n')[0].substring(0, 50);
var author = c.commit.author.name;
var date = new Date(c.commit.author.date);
var timeAgo = getTimeAgo(date);
document.getElementById('latestCommit').textContent =
msg + ' · ' + author + ' · ' + timeAgo;
}
} catch(e) {
document.getElementById('latestCommit').textContent = '需登录后查看';
}
2026-05-27 22:08:29 +08:00
}
2026-05-29 00:40:57 +08:00
function getTimeAgo(date) {
var diff = Math.floor((Date.now() - date.getTime()) / 1000);
if (diff < 60 ) return ' 刚刚 ' ;
if (diff < 3600 ) return Math . floor ( diff / 60 ) + ' 分钟前 ' ;
if (diff < 86400 ) return Math . floor ( diff / 3600 ) + ' 小时前 ' ;
return Math.floor(diff/86400) + '天前';
}
// ============================================================
// Modal
// ============================================================
function openModal(id) {
document.getElementById('m-' + id).classList.add('show');
document.body.style.overflow = 'hidden';
// Reload data when opening server modal
if (id === 'servers') loadServerStatus();
if (id === 'code') loadRepoInfo();
2026-05-27 22:08:29 +08:00
}
2026-05-29 00:40:57 +08:00
function closeModal(id) {
document.getElementById('m-' + id).classList.remove('show');
document.body.style.overflow = '';
}
document.querySelectorAll('.overlay').forEach(function(o) {
o.addEventListener('click', function(e) {
if (e.target === this) { this.classList.remove('show'); document.body.style.overflow = ''; }
});
2026-05-27 22:08:29 +08:00
});
2026-05-27 17:14:48 +08:00
2026-05-29 00:40:57 +08:00
// ============================================================
// Init
// ============================================================
(function() {
var u = localStorage.getItem('repo_user');
var p = localStorage.getItem('repo_pass');
if (u & & p) {
// Verify stored credentials still work
fetch('/code/api/v1/user', {
headers: { 'Authorization': 'Basic ' + btoa(u + ':' + p) }
})
.then(function(r) {
if (r.ok) return r.json();
throw new Error('stale');
})
.then(function(d) {
onLoginSuccess(d.login || u);
})
.catch(function() {
// Stored credentials invalid, show login
document.getElementById('loginOverlay').classList.remove('hidden');
});
}
})();
< / script >
2026-05-27 17:14:48 +08:00
< / body >
< / html >