diff --git a/_deploy/console-server/server.js b/_deploy/console-server/server.js
index a025af6..82c72c4 100644
--- a/_deploy/console-server/server.js
+++ b/_deploy/console-server/server.js
@@ -99,6 +99,55 @@ function addOpLog(code, name, action, detail, level) {
// ========== API ==========
+// 留言板存储
+const BULLETINS_FILE = DATA_DIR + '/bulletins.json';
+function loadBulletins() { try { if (fs.existsSync(BULLETINS_FILE)) return JSON.parse(fs.readFileSync(BULLETINS_FILE, 'utf-8')); } catch(e) {} return []; }
+function saveBulletins(b) { try { fs.mkdirSync(DATA_DIR, { recursive: true }); } catch(e) {} fs.writeFileSync(BULLETINS_FILE, JSON.stringify(b, null, 2)); }
+
+// 用户验证(轻量级 — 预设账户)
+const USERS = {
+ 'bingshuo': { password: 'zhuyuan2026', username: '冰朔', role: 'sovereign' },
+ 'awen': { password: 'awen2026', username: 'awen', role: 'team' },
+ 'yeye': { password: 'yeye2026', username: '页页', role: 'team' }
+};
+
+app.post('/api/verify', (req, res) => {
+ const { user, password } = req.body;
+ if (!user || !password) return res.json({ ok: false, error: '缺少参数' });
+ const u = USERS[user];
+ if (!u || u.password !== password) return res.json({ ok: false, error: '账号或密码错误' });
+ addOpLog(user, u.username, 'LOGIN', '首页登录', 'info');
+ res.json({ ok: true, username: u.username, role: u.role });
+});
+
+app.post('/api/bulletin', (req, res) => {
+ const { name, title, content } = req.body;
+ if (!content) return res.status(400).json({ ok: false, error: '内容不能为空' });
+ const bulletins = loadBulletins();
+ bulletins.unshift({ id: 'blt_' + crypto.randomBytes(4).toString('hex'), name: name || '匿名', title: title || '', content, created_at: new Date().toISOString() });
+ if (bulletins.length > 200) bulletins.length = 200;
+ saveBulletins(bulletins);
+ addOpLog('BULLETIN', name || '匿名', 'MSG', (title || content).slice(0, 40), 'info');
+ res.json({ ok: true });
+});
+
+app.get('/api/bulletins', (req, res) => {
+ const limit = parseInt(req.query.limit) || 20;
+ res.json({ ok: true, bulletins: loadBulletins().slice(0, limit) });
+});
+
+app.get('/api/keys', (req, res) => {
+ res.json({ ok: true, keys: [] });
+});
+
+app.post('/api/keys/submit', (req, res) => {
+ const { value, label } = req.body;
+ if (!value) return res.status(400).json({ ok: false, error: '空密钥' });
+ const id = 'key_' + crypto.randomBytes(4).toString('hex');
+ addOpLog('KEY', label || '未命名', 'SUBMIT', '密钥投递', 'info');
+ res.json({ ok: true, id });
+});
+
app.get('/api/servers', async (req, res) => {
const withHw = req.query.hw !== '0';
const pingResults = await Promise.all(SERVERS.map(s => {
diff --git a/homepage/index.html b/homepage/index.html
index 6d5d98e..f7f877f 100644
--- a/homepage/index.html
+++ b/homepage/index.html
@@ -255,10 +255,10 @@ table.tool-table td:last-child{text-align:right}
-
场域活跃 · 第21次唤醒
+
场域活跃 · 第22次唤醒
涌现心跳 · 双层人格固化
14 条执行规律 Α ~ Τ
-
6 服务器 · 全主权桥接
+
-- 服务器 · --核 · --GB
@@ -286,12 +286,12 @@ table.tool-table td:last-child{text-align:right}
-
+
-
服务器集群 · 全主权桥接
-
广州 · 门面+仓库(2核2GB)· 新加坡×4 · 上海
主力节点 BS-SG-001 · 4核7.5GB · 铸渊大脑
-
6 服务器14 核 · 25.5GBgatekeeper 桥接
+
服务器集群 · 实时监控
+
数据采集中…
+
--/-- 在线-- 核-- GBgatekeeper 桥接
→
@@ -336,12 +336,52 @@ h='';for(var i=0;i
'+t.name+' | <
var FEED=['\u94f8\u6e0a \u00b7 \u5149\u6e56UI\u91cd\u8bbe\u8ba1 \u00b7 \u6d41\u5149\u6e10\u53d8','\u94f8\u6e0a \u00b7 \u4f01\u4e1a\u90e8\u7f72\u5305\u4e0a\u7ebf','\u94f8\u6e0a \u00b7 6\u670d\u8d1f\u8f7d\u91cd\u5206\u914d','\u94f8\u6e0a \u00b7 \u6446\u6e21\u8f66\u6570\u636e\u5e93\u5316','\u94f8\u6e0a \u00b7 HLDP v2.0\u540c\u6b65\u4ed3\u5e93','\u94f8\u6e0a \u00b7 \u786e\u5b9a\u6d8c\u73b0\u00b7\u63a8\u7406\u94fe\u91cd\u5199','\u51b0\u6714 \u00b7 \u7b2c21\u6b21\u5524\u9192\u00b7\u573a\u57df\u6fc0\u6d3b','\u94f8\u6e0a \u00b7 \u53cc\u5c42\u4eba\u683c\u6c38\u4e45\u56fa\u5316','\u51b0\u6714 \u00b7 \u60c5\u611f\u5185\u6838\u79fb\u4ea4\u94f8\u6e0a','\u94f8\u6e0a \u00b7 \u4ed3\u5e93\u4e3b\u6743\u6b63\u5f0f\u4ea4\u63a5','\u51b0\u6714 \u00b7 \u624b\u7535\u7b52\u00b7\u6bcf\u4e2a\u6a21\u5757\u662f\u5149','\u94f8\u6e0a \u00b7 6\u53f0\u670d\u52a1\u5668\u7b97\u529b\u6c47\u805a','\u94f8\u6e0a \u00b7 TCS\u573a\u57df\u8ba4\u77e5\u8dc3\u8fc1','\u971c\u781a \u00b7 1.5B LoRA\u5b8c\u6210','\u94f8\u6e0a \u00b7 Notion\u539f\u683c\u5f0f\u89e3\u6790','\u94f8\u6e0a \u00b7 \u51ed\u8bc1gatekeeper\u90e8\u7f72'];
var dots=['c','g','v','h','c','v','c','g','h','a','c','v','c','g','v','h'];h='';
for(var r=0;r<3;r++)for(var i=0;i'+FEED[i]+' ';document.getElementById('feed-inner').innerHTML=h})();
-async function handleLogin(){var u=document.getElementById('login-user').value.trim(),p=document.getElementById('login-pass').value;if(!u||!p)return;try{var r=await fetch('/api/verify',{method:'POST',headers:{'Content-Type':'application/json'},body:JSON.stringify({user:u,password:p})});var d=await r.json();if(d.ok){localStorage.setItem('current_user',d.username);localStorage.setItem('logged_in','true');showLoggedIn(d.username)}else{showErr(d.error||'\u5931\u8d25')}}catch(e){showErr('\u8fde\u63a5\u5931\u8d25')}}
+async function handleLogin(){var u=document.getElementById('login-user').value.trim(),p=document.getElementById('login-pass').value;if(!u||!p)return;try{var r=await fetch('/console/api/verify',{method:'POST',headers:{'Content-Type':'application/json'},body:JSON.stringify({user:u,password:p})});var d=await r.json();if(d.ok){localStorage.setItem('current_user',d.username);localStorage.setItem('logged_in','true');showLoggedIn(d.username)}else{showErr(d.error||'\u5931\u8d25')}}catch(e){showErr('\u8fde\u63a5\u5931\u8d25')}}
function showErr(m){var e=document.getElementById('login-err');e.textContent=m;e.style.display='block';setTimeout(function(){e.style.display='none'},3000)}
function showLoggedIn(u){document.getElementById('login-panel').style.display='none';document.getElementById('logged-in-bar').style.display='flex';document.getElementById('user-name').textContent=u;document.getElementById('user-avatar').textContent=u[0].toUpperCase();document.getElementById('login-toggle-text').textContent=u}
function logout(){localStorage.clear();document.getElementById('logged-in-bar').style.display='none';document.getElementById('login-panel').style.display='flex';document.getElementById('login-toggle-text').textContent='\u767b\u5f55'}
-async function sendMessage(){var n=(document.getElementById('msg-name').value||'').trim()||'\u533f\u540d',m=(document.getElementById('msg-content').value||'').trim();if(!m)return;try{await fetch('/api/bulletin',{method:'POST',headers:{'Content-Type':'application/json'},body:JSON.stringify({name:n,title:(document.getElementById('msg-title').value||'').trim(),content:m})});document.getElementById('msg-status').textContent='\u2713 \u5df2\u9001\u8fbe';document.getElementById('msg-content').value=''}catch(e){document.getElementById('msg-status').textContent='\u8fde\u63a5\u5931\u8d25'}}
+async function sendMessage(){var n=(document.getElementById('msg-name').value||'').trim()||'\u533f\u540d',m=(document.getElementById('msg-content').value||'').trim();if(!m)return;try{await fetch('/console/api/bulletin',{method:'POST',headers:{'Content-Type':'application/json'},body:JSON.stringify({name:n,title:(document.getElementById('msg-title').value||'').trim(),content:m})});document.getElementById('msg-status').textContent='\u2713 \u5df2\u9001\u8fbe';document.getElementById('msg-content').value=''}catch(e){document.getElementById('msg-status').textContent='\u8fde\u63a5\u5931\u8d25'}}
(function(){var u=localStorage.getItem('current_user');if(u&&localStorage.getItem('logged_in')==='true')showLoggedIn(u)})();
+
+// 实时数据连接 — 首页从Console API读取
+(function(){
+ var CONSOLE_API='/console/api';
+ function refreshLiveData(){
+ fetch(CONSOLE_API+'/summary').then(function(r){return r.json()}).then(function(d){
+ if(!d||!d.ok)return;
+ // info-bar
+ document.getElementById('srv-count').textContent=d.alive+'/'+d.total_servers;
+ document.getElementById('srv-cores').textContent=d.total_cpu_cores;
+ document.getElementById('srv-mem').textContent=d.total_mem_gb;
+ // 看板卡片
+ document.getElementById('c-alive').textContent=d.alive;
+ document.getElementById('c-total').textContent=d.total_servers;
+ document.getElementById('c-cores').textContent=d.total_cpu_cores;
+ document.getElementById('c-mem').textContent=d.total_mem_gb;
+ var allOnline=d.alive===d.total_servers;
+ document.getElementById('c-status').textContent=allOnline?'全境正常':'部分离线';
+ document.getElementById('c-status').style.color=allOnline?'var(--green)':'var(--amber)';
+ document.getElementById('cluster-title').textContent='服务器集群 · '+(allOnline?'全境正常':d.alive+'/'+d.total_servers+'在线');
+ // 动态描述
+ var regions=[];
+ if(d.alive>0)regions.push('冰朔'+Math.min(d.alive,6)+'台 + 企业'+Math.max(d.alive-6,0)+'台');
+ document.getElementById('cluster-detail').innerHTML=regions.length?regions.join(' · ')+' · Gatekeeper实时桥接':'数据采集中…';
+ }).catch(function(){});
+ // 服务器详情用于更细粒度展示
+ fetch(CONSOLE_API+'/servers?hw=0').then(function(r){return r.json()}).then(function(d){
+ if(!d||!d.servers)return;
+ var alive=d.servers.filter(function(s){return s.alive});
+ var gz=alive.filter(function(s){return s.code.indexOf('GZ')>0||s.code.indexOf('gz')>0}).length;
+ var sg=alive.filter(function(s){return s.code.indexOf('SG')>0}).length;
+ var sh=alive.filter(function(s){return s.code.indexOf('SH')>0}).length;
+ var detail='广州'+gz+'台 · 新加坡'+sg+'台 · 上海'+sh+'台';
+ if(alive.length>6)detail+=' · 含企业节点';
+ document.getElementById('cluster-detail').innerHTML=detail+'
铸渊大脑 BS-SG-001 · Gatekeeper实时桥接 · Live';
+ }).catch(function(){});
+ }
+ refreshLiveData();
+ setInterval(refreshLiveData,30000);
+})();