console: v2·流动边框+密钥投递模块

This commit is contained in:
bingshuo 2026-05-23 15:24:26 +08:00
parent 433a5e7440
commit 831f462fae

View File

@ -6,21 +6,11 @@
<title>曜冥纪元 · 领地健康看板</title>
<style>
*{box-sizing:border-box;margin:0;padding:0}
:root{
--bg:#080c14;--s1:#0c1320;--s2:#101828;--s3:#162032;
--cyan:#5ad0e0;--cyan-glow:rgba(90,208,224,.3);
--violet:#b090ff;--violet-glow:rgba(176,144,255,.25);
--ok:#40b87a;--ok-glow:rgba(64,184,122,.3);
--warn:#f0d060;--err:#ff4a6e;
--brd:rgba(90,208,224,.12);--brd-hover:rgba(90,208,224,.3);
--txt:#f0f6ff;--dim:#6a80a0;
--font:'SF Pro Display','Inter','PingFang SC','-apple-system',sans-serif;
--mono:'SF Mono','JetBrains Mono',monospace;
}
@keyframes breathe-border{0%,100%{border-color:rgba(90,208,224,.08)}50%{border-color:rgba(90,208,224,.25)}}
:root{--bg:#080c14;--s1:#0c1320;--s2:#101828;--s3:#162032;--cyan:#5ad0e0;--cyan-glow:rgba(90,208,224,.3);--violet:#b090ff;--violet-glow:rgba(176,144,255,.25);--ok:#40b87a;--ok-glow:rgba(64,184,122,.3);--warn:#f0d060;--err:#ff4a6e;--brd:rgba(90,208,224,.12);--brd-hover:rgba(90,208,224,.3);--txt:#f0f6ff;--dim:#6a80a0;--font:'SF Pro Display','Inter','PingFang SC','-apple-system',sans-serif;--mono:'SF Mono','JetBrains Mono',monospace}
@keyframes breathe-dot{0%,100%{opacity:.5;transform:scale(1)}50%{opacity:1;transform:scale(1.2)}}
@keyframes breathe-card{0%,100%{box-shadow:0 0 0 rgba(90,208,224,0)}50%{box-shadow:0 0 18px rgba(90,208,224,.06),0 0 40px rgba(123,93,191,.03)}}
@keyframes shimmer{0%{transform:translateX(-100%)}50%{transform:translateX(100%)}100%{transform:translateX(200%)}}
@keyframes border-flow{to{background-position:0 0,-300% 0}}
html{font-size:15px}
body{background:var(--bg);color:var(--txt);font-family:var(--font);min-height:100vh;overflow-x:hidden;-webkit-font-smoothing:antialiased}
.hd{background:rgba(8,12,20,.92);border-bottom:1px solid var(--brd);padding:22px 48px;display:flex;justify-content:space-between;align-items:center;position:sticky;top:0;z-index:50}
@ -32,8 +22,8 @@ body{background:var(--bg);color:var(--txt);font-family:var(--font);min-height:10
.hd-tm{font-size:20px;color:var(--cyan);font-family:var(--mono);letter-spacing:2px;font-weight:600}
.hd-ss{font-size:12px;margin-top:4px;display:flex;align-items:center;gap:8px;justify-content:flex-end}
.sm{display:flex;gap:14px;padding:18px 48px;background:rgba(12,19,32,.6);border-bottom:1px solid var(--brd);overflow-x:auto}
.sm-i{background:rgba(16,24,40,.5);border:1px solid var(--brd);border-radius:12px;padding:12px 22px;flex-shrink:0;min-width:140px;transition:all .25s;animation:breathe-border 5s ease-in-out infinite}
.sm-i:hover{border-color:var(--brd-hover)}
.sm-i{background:linear-gradient(var(--s2),var(--s2)) padding-box,linear-gradient(90deg,var(--cyan),var(--violet),var(--cyan)) border-box;border:2px solid transparent;border-radius:12px;padding:12px 22px;flex-shrink:0;min-width:140px;transition:all .25s;background-size:100% 100%,300% 100%;animation:border-flow 5s linear infinite}
.sm-i:hover{border-color:transparent;animation:border-flow 1.5s linear infinite}
.sm-l{font-size:11px;color:var(--dim);letter-spacing:.06em;text-transform:uppercase;margin-bottom:4px}
.sm-v{font-size:26px;font-weight:700;letter-spacing:-.5px;line-height:1.2}
.sm-v .nu{font-size:12px;color:var(--dim);font-weight:400}
@ -42,8 +32,8 @@ body{background:var(--bg);color:var(--txt);font-family:var(--font);min-height:10
.sc::after{content:'';flex:1;height:1px;background:linear-gradient(90deg,var(--brd),transparent)}
.sc .sc-badge{font-size:11px;padding:3px 10px;border-radius:10px;background:rgba(176,144,255,.15);color:var(--violet);font-weight:500}
.gr{display:grid;grid-template-columns:repeat(3,1fr);gap:18px;margin-bottom:26px}
.cd{background:rgba(12,19,32,.6);border:1px solid var(--brd);border-radius:18px;padding:22px;position:relative;overflow:hidden;transition:all .35s ease;animation:breathe-card 6s ease-in-out infinite}
.cd:hover{border-color:var(--brd-hover);box-shadow:0 0 35px rgba(90,208,224,.1),0 0 60px rgba(123,93,191,.04)}
.cd{background:linear-gradient(var(--s1),var(--s1)) padding-box,linear-gradient(90deg,var(--cyan),var(--violet),var(--cyan),var(--violet),var(--cyan)) border-box;border:2px solid transparent;border-radius:18px;padding:22px;position:relative;overflow:hidden;transition:all .35s ease;animation:border-flow 6s linear infinite;background-size:100% 100%,300% 100%}
.cd:hover{border-color:transparent;box-shadow:0 0 35px rgba(90,208,224,.1),0 0 60px rgba(123,93,191,.04);animation:border-flow 1.5s linear infinite}
.cd-glow{position:absolute;top:0;left:0;right:0;height:2px;background:linear-gradient(90deg,transparent,var(--cyan),transparent);opacity:.5;animation:shimmer 5s ease-in-out infinite}
.cd-hd{display:flex;justify-content:space-between;align-items:flex-start;margin-bottom:12px}
.cd-n{font-size:17px;font-weight:600;letter-spacing:.04em}
@ -137,10 +127,22 @@ body{background:var(--bg);color:var(--txt);font-family:var(--font);min-height:10
<div class="sc">服务器集群 · 六域主权</div><div class="gr" id="server-grid"></div>
<div class="sc">算力汇聚池 <span class="sc-badge">AGE v1.0</span></div>
<div class="pool-sxn"><div class="pool-hd"><div class="pool-hd-l"><div class="ico"></div><div class="pool-hd-tt">铸渊 · 虚拟算力池</div><div class="pool-hd-st" id="pool-updated">--</div></div><div style="font-size:11px;color:var(--dim)" id="pool-tasks">--</div></div><div class="pool-summary" id="pool-meter-grid"></div><div class="pool-nodes" id="pool-node-grid"></div></div>
<div class="bt"><div class="bt-l"><div class="bt-lh"><span>铸渊操作流水</span><s>只读</s></div><div id="receipt-container"></div><div class="bt-lg" id="log-output"><div class="bt-le">等待铸渊发起操作...</div></div></div><div class="bt-r" id="auth-panel"><div class="bt-ra">暂无待处理的操作请求</div><div style="font-size:12px;color:var(--dim);margin-top:6px">铸渊操作由主权者发起</div></div></div>
<div class="bt">
<div class="bt-l"><div class="bt-lh"><span>铸渊操作流水</span><s>只读</s></div><div id="receipt-container"></div><div class="bt-lg" id="log-output"><div class="bt-le">等待铸渊发起操作...</div></div></div>
<div class="bt-r" style="display:flex;flex-direction:row;gap:12px">
<div class="bt-r" id="auth-panel" style="flex:1;min-height:auto;padding:20px"><div class="bt-ra">暂无待处理的操作请求</div><div style="font-size:12px;color:var(--dim);margin-top:6px">铸渊操作由主权者发起</div></div>
<div class="bt-r" style="flex:1;min-height:auto;padding:18px">
<div style="font-size:13px;color:var(--violet);margin-bottom:10px;font-weight:600">🔑 密钥投递</div>
<div style="display:flex;flex-direction:column;gap:8px;width:100%">
<input placeholder="API Key / Token / Password" id="key-value" style="padding:10px 14px;background:rgba(8,12,20,.5);border:1px solid var(--brd);border-radius:8px;color:var(--txt);font-size:12px;outline:none;font-family:var(--mono);width:100%">
<div style="display:flex;gap:8px"><input placeholder="标签(如:AutoDL)" id="key-label" style="flex:1;padding:10px 14px;background:rgba(8,12,20,.5);border:1px solid var(--brd);border-radius:8px;color:var(--txt);font-size:12px;outline:none"><button onclick="submitKey()" style="padding:10px 16px;background:linear-gradient(135deg,rgba(42,157,191,.4),rgba(123,93,191,.3));border:1px solid rgba(90,208,224,.2);border-radius:8px;color:var(--txt);font-size:12px;font-weight:600;cursor:pointer;white-space:nowrap">投递</button></div>
<div id="key-status" style="font-size:11px;color:var(--dim);min-height:16px"></div>
</div>
</div>
</div>
</div>
</main>
<footer class="ft"><span>曜冥纪元 · HoloLake Era | 六域归一 · AGE v1.0</span><span>国作登字-2026-A-00037559</span></footer>
<script>
const API=window.location.pathname.startsWith('/console')?'/console/api':'/api';let iv;
const MODULES={'BS-GZ-006':['forgejo代码仓库','mcp-server','console-server','portal前端','gatekeeper守护','nginx反代','系统日志','监控采集'],'BS-SG-001':['gatekeeper守护','模型推理','cos同步','配置服务','日志聚合','性能监控','安全审计'],'BS-SG-002':['gatekeeper守护','web服务','cdn加速','缓存层','负载均衡','故障转移'],'BS-SG-003':['gatekeeper守护','备份服务','存储节点','数据同步','归档服务','快照管理'],'ZY-SG-006':['gatekeeper守护','计算节点','推理服务','内存缓存','训练流水线'],'BS-SH-005':['gatekeeper守护','容灾节点','冷备存储','健康检查','数据校验']};
@ -155,7 +157,10 @@ async function rejOp(id){await fetch(API+'/reject',{method:'POST',headers:{'Cont
function addLog(tag,msg){var lg=$('log-output');var e=lg.querySelector('.bt-le');if(e)e.remove();var t=new Date().toLocaleTimeString('zh-CN',{hour12:false});var d=document.createElement('div');d.innerHTML='<span style="color:var(--dim)">'+t+'</span> <span style="color:'+(tag==='授权'?'var(--ok)':'var(--err)')+'">['+tag+']</span> '+msg;lg.appendChild(d);lg.scrollTop=lg.scrollHeight}
async function pollAll(){var results=await Promise.all([fetchAPI('/servers'),fetchAPI('/pending'),fetchAPI('/terminal/sessions'),fetchAPI('/pool/status')]);var srv=results[0],pending=results[1],sessions=results[2],pool=results[3];if(srv)renderCards(srv,pool&&pool.pool);renderSessionsAndAuth(sessions,pending);if(pool&&pool.pool)renderPool(pool.pool)}
function updateClock(){$('gl-clock').textContent=new Date().toLocaleTimeString('zh-CN',{hour12:false})}
(function(){updateClock();pollAll();iv=setInterval(pollAll,5000);setInterval(updateClock,1000)})();
(function(){updateClock();pollAll();loadKeys();iv=setInterval(pollAll,5000);setInterval(updateClock,1000)})();
async function submitKey(){var v=document.getElementById('key-value').value.trim();if(!v)return;var label=document.getElementById('key-label').value.trim()||'未命名';try{var r=await fetch(API+'/keys/submit',{method:'POST',headers:{'Content-Type':'application/json'},body:JSON.stringify({value:v,label:label})});var d=await r.json();if(d.ok){document.getElementById('key-value').value='';document.getElementById('key-label').value='';document.getElementById('key-status').textContent='✓ 已投递 · ID:'+d.id;document.getElementById('key-status').style.color='var(--ok)';loadKeys()}else{document.getElementById('key-status').textContent=d.error;document.getElementById('key-status').style.color='var(--err)'}}catch(e){document.getElementById('key-status').textContent='连接失败';document.getElementById('key-status').style.color='var(--err)'}}
async function loadKeys(){try{var r=await fetch(API+'/keys');var d=await r.json();if(d.ok&&d.keys.length>0){var el=document.getElementById('key-status');el.textContent='已有 '+d.keys.length+' 个密钥可用';el.style.color='var(--cyan)'}}catch(e){}}
async function delKey(id){await fetch(API+'/keys/'+id,{method:'DELETE'});loadKeys()}
</script>
</body>
</html>