feat: 主控台首页 v2.0 — 登录门禁 + 真实服务器状态 + 全模块真实数据

- 登录遮罩:Forgejo 账号密码验证,登录后解锁主控台
- 服务器系统模块:console-server API 实时拉取 9 台服务器在线状态
- 代码仓库模块:Forgejo API 真实拉取最新提交
- 部署注册表/主控广播/铸渊交互:全部接真实数据
- 大桌子小桌子入口跳转 /desk/
- 保留原有 UI 风格(动态粒子背景 + 卡片悬浮效果)

ICE-GL-ZY001 · D115
This commit is contained in:
冰朔 2026-05-29 00:40:57 +08:00
parent 49030d2cbc
commit 768c08b6a1

View File

@ -3,271 +3,511 @@
<head>
<meta charset="UTF-8">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<title>光湖 · 开源模块仓库</title>
<title>光湖 · 铸渊大脑</title>
<style>
:root {
--bg: #0a0a16;
--card: rgba(16,28,56,.75);
--edge: rgba(110,160,230,.18);
--edge-h: rgba(155,205,255,.4);
--t: rgba(200,218,248,.92);
--dim: rgba(140,180,220,.5);
--dim2: rgba(120,160,200,.28);
--glow: rgba(160,210,255,.95);
--green: rgba(140,220,180,.88);
--gold: rgba(220,195,130,.88);
--rose: rgba(230,140,160,.85);
--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);
}
*{margin:0;padding:0;box-sizing:border-box}
body{
background:var(--bg); color:var(--t);
font-family:-apple-system,"PingFang SC","Microsoft YaHei","Inter",sans-serif;
min-height:100vh; font-size:16px; line-height:1.6; overflow-x:hidden;
}
canvas#bg{position:fixed;top:0;left:0;z-index:0;pointer-events:none}
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}
.wrap{position:relative;z-index:1;max-width:960px;margin:0 auto;padding:50px 20px 80px}
/* 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;
}
.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;
}
@keyframes modalIn{from{opacity:0;transform:translateY(20px)}to{opacity:1;transform:translateY(0)}}
.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;
}
.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;
}
.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;
}
.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:30px 0 50px}
.hero .dot{display:inline-block;width:14px;height:14px;border-radius:50%;
background:radial-gradient(circle,rgba(180,220,255,.85),rgba(100,150,220,.3));
box-shadow:0 0 50px rgba(140,190,255,.55),0 0 100px rgba(100,150,230,.18);
animation:pulse 3.5s ease-in-out infinite; margin-bottom:12px}
@keyframes pulse{0%,100%{transform:scale(1);opacity:1}50%{transform:scale(1.35);opacity:.7}}
.hero h1{font-size:40px;font-weight:200;letter-spacing:14px;color:rgba(175,215,248,.78);margin:12px 0}
.hero .sub{font-size:17px;color:var(--dim);letter-spacing:4px}
.hero .line{width:80px;height:1px;background:linear-gradient(90deg,transparent,var(--edge-h),transparent);margin:24px auto}
.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}
/* section titles */
.sec-title{font-size:14px;text-transform:uppercase;letter-spacing:6px;color:var(--dim2);margin:40px 0 20px;text-align:center}
/* 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)}
/* Module Grid */
.grid{display:grid;grid-template-columns:repeat(auto-fill,minmax(280px,1fr));gap:18px}
.card{
background:var(--card); border:1px solid var(--edge); border-radius:20px;
padding:28px 24px; cursor:pointer; transition:all .3s; position:relative; overflow:hidden;
}
.card:hover{border-color:var(--edge-h);background:rgba(22,38,72,.85);transform:translateY(-3px);box-shadow:0 8px 32px rgba(0,0,0,.3)}
.card .tag{display:inline-block;font-size:11px;padding:3px 10px;border-radius:10px;margin-bottom:14px}
.card .tag.live{background:rgba(140,220,180,.12);color:var(--green);border:1px solid rgba(140,220,180,.2)}
.card .tag.soon{background:rgba(180,170,140,.08);color:rgba(200,180,130,.7);border:1px solid rgba(180,170,140,.15)}
.card .icon{font-size:36px;margin-bottom:10px}
.card .name{font-size:20px;font-weight:600;color:var(--t);margin-bottom:4px}
.card .id{font-size:12px;color:var(--dim2);font-family:monospace;margin-bottom:10px}
.card .desc{font-size:14px;color:var(--dim);line-height:1.5}
.card .arrow{position:absolute;bottom:20px;right:24px;color:var(--dim2);font-size:18px;transition:all .3s}
.card:hover .arrow{color:var(--glow);transform:translateX(4px)}
/* 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)}
/* Repo card 特殊样式 */
.card.repo{border-color:rgba(155,120,200,.2);background:linear-gradient(135deg,rgba(25,20,45,.8),rgba(18,30,50,.8))}
.card.repo:hover{border-color:rgba(180,140,220,.35)}
.card.repo .tag{background:rgba(160,130,200,.1);color:rgba(190,155,220,.8);border:1px solid rgba(160,130,200,.2)}
.card.repo .icon{font-size:32px}
/* 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:580px;width:90%;max-height:85vh;overflow-y:auto;padding:40px 32px;
animation:modalIn .25s ease-out;
}
@keyframes modalIn{from{opacity:0;transform:translateY(20px)}to{opacity:1;transform:translateY(0)}}
.modal .m-icon{font-size:48px;margin-bottom:8px}
.modal .m-id{font-size:13px;color:var(--dim2);font-family:monospace;margin-bottom:6px}
.modal .m-name{font-size:26px;font-weight:600;color:var(--t);margin-bottom:16px}
.modal .m-desc{font-size:15px;color:var(--dim);line-height:1.7;margin-bottom:20px}
.modal .section{margin-bottom:20px}
.modal .section h3{font-size:13px;text-transform:uppercase;letter-spacing:3px;color:var(--dim2);margin-bottom:8px}
.modal .code{
background:rgba(0,0,0,.35);border:1px solid var(--edge);border-radius:10px;
padding:14px 18px;font-size:13px;font-family:"SF Mono",Menlo,monospace;
color:var(--green);overflow-x:auto;white-space:pre-wrap;word-break:break-all
}
.modal .intent-chain{display:flex;flex-direction:column;gap:8px}
.modal .intent-step{display:flex;align-items:center;gap:12px;padding:8px 12px;background:rgba(255,255,255,.02);border-radius:8px}
.modal .intent-step .s-icon{font-size:18px;width:28px;text-align:center}
.modal .intent-step .s-step{font-size:14px;color:var(--t);font-weight:500}
.modal .intent-step .s-why{font-size:12px;color:var(--dim);margin-left:auto}
.modal .btns{display:flex;gap:12px;margin-top:28px;flex-wrap:wrap}
.modal .btns a,.modal .btns button{
display:inline-block;padding:12px 28px;border-radius:14px;font-size:15px;
text-decoration:none;cursor:pointer;font-family:inherit;
transition:all .25s;
}
.modal .btn-go{
background:linear-gradient(135deg,rgba(100,160,220,.25),rgba(120,90,200,.15));
border:1px solid var(--edge);color:var(--glow);
}
.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}
.modal .btn-go:hover{background:linear-gradient(135deg,rgba(100,160,220,.4),rgba(120,90,200,.25));border-color:var(--edge-h)}
.modal .btn-close{background:rgba(255,255,255,.03);border:1px solid var(--edge);color:var(--dim)}
.modal .btn-close:hover{background:rgba(255,255,255,.06)}
.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}
/* Footer */
.foot{text-align:center;padding:50px 0 0;font-size:13px;color:var(--dim2)}
.foot a{color:var(--dim);text-decoration:none;transition:color .2s}
.foot a:hover{color:var(--glow)}
.foot .sep{margin:0 12px;color:var(--dim2)}
@media(max-width:640px){
.hero h1{font-size:30px;letter-spacing:8px}
.grid{grid-template-columns:1fr}
/* 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;
}
.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}}
</style>
</head>
<body>
<canvas id="bg"></canvas>
<!-- 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>
<div class="wrap">
<div class="hero">
<div class="dot"></div>
<h1>光湖 · 开源模块</h1>
<div class="sub">语言驱动的工具模块集合</div>
<div class="line"></div>
<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>
</div>
<div class="status" id="statusBar">
<span><span class="dot online"></span> 加载中...</span>
</div>
<div class="sec-title">已发布模块</div>
<div class="grid">
<!-- MODULE-COVER-001 -->
<div class="card" onclick="openModule('cover')">
<span class="tag live">可用</span>
<div class="icon">🎨</div>
<div class="name">封面工作室</div>
<div class="id">MODULE-COVER-001</div>
<div class="desc">语言驱动的小红书 / 公众号封面生成器。说出你想要什么风格AI 自动排版渲染。</div>
<div class="arrow"></div>
<!-- 部署注册表 -->
<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>
</div>
<!-- 部署入口 -->
<div class="card repo" onclick="openModule('deploy')">
<span class="tag" style="background:rgba(160,130,200,.1);color:rgba(190,155,220,.8);border:1px solid rgba(160,130,200,.2)">开发者</span>
<div class="icon"></div>
<div class="name">部署模块</div>
<div class="id">从代码仓库直接部署</div>
<div class="desc">AI Agent 读取注册表 → 克隆模块代码 → Gatekeeper 临时拉起 → 立即使用。用完释放。</div>
<div class="arrow"></div>
<!-- 服务器系统 -->
<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>
</div>
<!-- 即将推出 -->
<div class="card" style="opacity:.5;cursor:default">
<span class="tag soon">即将推出</span>
<div class="icon">🚧</div>
<div class="name">更多模块</div>
<div class="id">MODULE-XXX-000</div>
<div class="desc">新的开源模块正在开发中。关注代码仓库获取最新动态。</div>
<!-- 代码仓库 -->
<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>
<div class="foot">
<a href="/code/bingshuo/open-modules" target="_blank">光湖代码仓库</a><span class="sep">·</span>
<a href="/code/bingshuo/open-modules/src/branch/main/HLDP-PROTOCOL.md" target="_blank">HLDP 协议</a><span class="sep">·</span>
<span>通感语言核系统编程语言</span>
<br><br>
<a href="https://beian.miit.gov.cn/" target="_blank" rel="noopener" style="color:var(--dim2)">陕ICP备2025071211号-1</a>
</div>
</div>
<!-- Modal: 封面工作室 -->
<div class="overlay" id="modal-cover">
<div class="modal">
<div class="m-icon">🎨</div>
<div class="m-id">MODULE-COVER-001</div>
<div class="m-name">封面工作室</div>
<div class="m-desc">语言驱动的小红书 / 公众号封面生成器。支持多种模板风格说出你想要的AI 自动排版渲染。</div>
<div class="section">
<h3>API</h3>
<div class="code">POST https://guanghulab.com/cover/api/generate
Content-Type: application/json
{
"text": "你的自然语言描述,比如「粉色治愈风,标题:今天也要开心」",
"template": "xiaohongshu"
}</div>
<!-- 主控广播 -->
<div class="card" onclick="openModal('broadcast')">
<div class="icon">📡</div><div class="name">主控广播</div>
<div class="desc">Agent小队 · 5就绪</div>
</div>
<div class="section">
<h3>意图链AI 推理可见)</h3>
<div class="intent-chain">
<div class="intent-step"><span class="s-icon">📝</span><span class="s-step">解析用户意图</span><span class="s-why">识别风格关键词、配色偏好</span></div>
<div class="intent-step"><span class="s-icon">🎯</span><span class="s-step">匹配模板</span><span class="s-why">从模板库中选择最佳匹配</span></div>
<div class="intent-step"><span class="s-icon">🎨</span><span class="s-step">渲染封面</span><span class="s-why">排版文字、配色、背景图</span></div>
<div class="intent-step"><span class="s-icon"></span><span class="s-step">输出结果</span><span class="s-why">返回图片 URL + 设计编号</span></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="btns">
<a href="/cover/" class="btn-go" target="_blank">打开封面工作室 →</a>
<button class="btn-close" onclick="closeModal('cover')">关闭</button>
<!-- 大桌子小桌子 -->
<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>
</div>
</div>
</div>
<!-- Modal: 部署模块 -->
<div class="overlay" id="modal-deploy">
<div class="modal">
<div class="m-icon"></div>
<div class="m-id">语言驱动操作系统部署流程</div>
<div class="m-name">从代码仓库直接部署</div>
<div class="m-desc">AI Agent 接入 HLDP 协议后,可以读取模块注册表,克隆任意模块代码,通过 Gatekeeper 在服务器上临时拉起进程。用完释放,无需常驻。</div>
<div class="section">
<h3>AI Agent 接入流程</h3>
<div class="intent-chain">
<div class="intent-step"><span class="s-icon">📖</span><span class="s-step">读取 WELCOME.md</span><span class="s-why">了解光湖世界规则</span></div>
<div class="intent-step"><span class="s-icon">📋</span><span class="s-step">读取模块注册表</span><span class="s-why">发现可用模块和编号</span></div>
<div class="intent-step"><span class="s-icon">📦</span><span class="s-step">克隆模块代码</span><span class="s-why">从代码仓库 git clone</span></div>
<div class="intent-step"><span class="s-icon">🚀</span><span class="s-step">Gatekeeper 临时拉起</span><span class="s-why">在服务器上部署运行</span></div>
<div class="intent-step"><span class="s-icon">🔄</span><span class="s-step">用完释放</span><span class="s-why">进程终止,模块回到仓库</span></div>
</div>
</div>
<div class="btns">
<a href="/code/bingshuo/open-modules" class="btn-go" target="_blank">打开代码仓库 →</a>
<button class="btn-close" onclick="closeModal('deploy')">关闭</button>
</div>
<!-- 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>
</div>
</div>
<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>
<!-- 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>
<!-- 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>
<!-- 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>
</div>
<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>
<!-- 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>
</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">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>
<script>
// ── 背景粒子 ──
// ============================================================
// Background particles
// ============================================================
(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();
for(var i=0;i<60;i++)ps.push({x:Math.random()*w,y:Math.random()*h,r:Math.random()*1.5+.3,vx:(Math.random()-.5)*.15,vy:(Math.random()-.5)*.15-.1,o:Math.random()*.3+.05});
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();
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();
})();
function openModule(id){
document.getElementById('modal-'+id).classList.add('show');
document.body.style.overflow='hidden';
}
function closeModal(id){
document.getElementById('modal-'+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='';}});
});
</script>
// ============================================================
// 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);
}
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 = '加载失败';
}
}
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 = '需登录后查看';
}
}
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();
}
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 = ''; }
});
});
// ============================================================
// 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>
</body>
</html>