diff --git a/tools/gatekeeper.js b/tools/gatekeeper.js index 219a086..544ff10 100644 --- a/tools/gatekeeper.js +++ b/tools/gatekeeper.js @@ -1,7 +1,7 @@ #!/usr/bin/env node /* ═══════════════════════════════════════ - 光湖驱动引擎 v2.0 · Guanghu Drive Engine - HLDP万能语言接口 — HLDP进,指令出 + 光湖驱动引擎 v2.1 · Guanghu Drive Engine + HLDP万能语言接口 + 集群串联 — 一条指令,全网执行 ═══════════════════════════════════════ */ const http=require('http'),fs=require('fs'),path=require('path'),crypto=require('crypto'),{exec}=require('child_process'),os=require('os'); @@ -82,53 +82,112 @@ function fmtBytes(b){if(b===0)return'0 B';const k=1024,sizes=['B','KB','MB','GB' /* ═══════════════════════════════════════ - HLDP 翻译层 v1.0 — 光湖驱动引擎 v2.0 + HLDP 翻译层 v2.0 — 光湖驱动引擎 v2.1 + 新增 @串联 @广播 — 引擎之间互相通话 ═══════════════════════════════════════ */ +const NODES={ + 'BS-GZ-006':{url:'http://43.139.217.141:3910',key:'zy_gtw_4a155446b7acc09fe46a2a29972c0ca5d9954da19c35dc23'}, + 'BS-SG-001':{url:'http://43.156.237.110:3911',key:'zy_gtw_c38be341d65043eee0ba51a214a267832a9ae46dd73e423c'}, + 'BS-SG-002':{url:'http://43.134.16.246:3910',key:'zy_gtw_ceb06d8a70d0cd38678239b3a1a9b2ba5cba77a3fbe5ebbf'}, + 'BS-SG-003':{url:'http://43.153.193.169:3910',key:'zy_gtw_d7c033d8ae992ffc9dd3e0dd5fe332dace0b644c0ae7c847'}, + 'ZY-SG-006':{url:'http://43.153.203.105:3910',key:'zy_gtw_fff861a2fe40cbdc366ee21f218023be8b1c182f66c852d3'}, + 'BS-SH-005':{url:'http://124.223.10.33:3910',key:'zy_gtw_b1045de5ddfd7832e3c53349d9c896f054b85a4a9ece22f9'}, +}; + +function parseOpLine(t){ + if(t.startsWith('@执行:')){ + const cmd=t.replace('@执行:','').trim(); + const parts=cmd.split(/\s+/); + if(parts[0]==='shell')return{type:'shell',cmd:parts.slice(1).join(' ')}; + if(parts[0]==='python')return{type:'python',cmd:parts.slice(1).join(' ')}; + return{type:'shell',cmd:cmd}; + } + if(t.startsWith('@部署:')){ + const action=t.replace('@部署:','').trim(); + const parts=action.split(/\s+/); + if(parts[0]==='写入')return{type:'write',path:parts[1]}; + if(parts[0]==='命令')return{type:'shell',cmd:parts.slice(1).join(' ')}; + } + return null; +} + function parseHLDP(text){ - const lines=text.split('\n').filter(l=>l.trim()); - const ops=[]; + const lines=text.split('\n'); + const groups=[]; + let relay=null; for(const line of lines){ const t=line.trim(); - if(t.startsWith('@执行:')){ - const cmd=t.replace('@执行:','').trim(); - const parts=cmd.split(/\s+/); - if(parts[0]==='shell'){ops.push({type:'shell',cmd:parts.slice(1).join(' ')});} - else if(parts[0]==='python'){ops.push({type:'python',cmd:parts.slice(1).join(' ')});} - else{ops.push({type:'shell',cmd:cmd});} - }else if(t.startsWith('@部署:')){ - const action=t.replace('@部署:','').trim(); - const parts=action.split(/\s+/); - if(parts[0]==='写入'){ - const filepath=parts[1]; - ops.push({type:'write',path:filepath}); - }else if(parts[0]==='命令'){ - ops.push({type:'shell',cmd:parts.slice(1).join(' ')}); - } + if(!t)continue; + if(t.startsWith('@串联:')){relay=t.replace('@串联:','').trim();continue;} + if(t==='@广播'){relay='BROADCAST';continue;} + const op=parseOpLine(t); + if(!op)continue; + if(!groups.length||groups[groups.length-1].relay!==relay){ + groups.push({relay:relay,ops:[]}); } + groups[groups.length-1].ops.push(op); } - return ops; + return groups; +} + +async function relayHLDP(target,hldpText){ + const node=NODES[target]; + if(!node)return{target:target,ok:false,error:'未知节点: '+target}; + return new Promise(r=>{ + const body=JSON.stringify({hlpd:hldpText}); + const opts={hostname:node.url.replace(/https?:\/\//,'').split(':')[0], + port:parseInt(node.url.split(':').pop()||'3910',10), + path:'/hlpd',method:'POST', + headers:{'Content-Type':'application/json','Authorization':'Bearer '+node.key,'Content-Length':Buffer.byteLength(body)}}; + const req=http.request(opts,res=>{let d='';res.on('data',c=>d+=c);res.on('end',()=>{try{r({target:target,ok:true,result:JSON.parse(d)})}catch(e){r({target:target,ok:false,error:d.slice(0,200)})}})}); + req.on('error',e=>r({target:target,ok:false,error:e.message})); + req.write(body);req.end(); + }); +} + +function hldpTextFromOps(ops){ + return ops.map(o=>{ + if(o.type==='shell')return '@执行: shell '+o.cmd; + if(o.type==='python')return '@执行: python '+o.cmd; + if(o.type==='write')return '@部署: 写入 '+o.path; + return ''; + }).filter(l=>l).join('\n'); } async function runHLDP(hlpdText){ - const ops=parseHLDP(hlpdText); - const results=[]; - for(const op of ops){ - if(op.type==='shell'){ - const r=await execCmd(op.cmd); - results.push({type:'shell',cmd:op.cmd,...r}); - }else if(op.type==='write'){ - try{ - const dir=path.dirname(op.path); - if(!fs.existsSync(dir))fs.mkdirSync(dir,{recursive:true}); - results.push({type:'write',path:op.path,ok:true}); - }catch(e){results.push({type:'write',path:op.path,ok:false,error:e.message});} - }else if(op.type==='python'){ - const r=await execCmd('python3 -c "'+op.cmd.replace(/"/g,'\\"')+'"'); - results.push({type:'python',cmd:op.cmd,...r}); + const groups=parseHLDP(hlpdText); + const allResults=[]; + for(const g of groups){ + if(!g.relay){ + for(const op of g.ops){ + if(op.type==='shell'){ + const r=await execCmd(op.cmd); + allResults.push({type:'shell',cmd:op.cmd,...r}); + }else if(op.type==='write'){ + try{ + const dir=path.dirname(op.path); + if(!fs.existsSync(dir))fs.mkdirSync(dir,{recursive:true}); + allResults.push({type:'write',path:op.path,ok:true}); + }catch(e){allResults.push({type:'write',path:op.path,ok:false,error:e.message});} + }else if(op.type==='python'){ + const r=await execCmd('python3 -c "'+op.cmd.replace(/"/g,'\\"')+'"'); + allResults.push({type:'python',cmd:op.cmd,...r}); + } + } + }else if(g.relay==='BROADCAST'){ + const hldp=hldpTextFromOps(g.ops); + const targets=Object.keys(NODES); + const promises=targets.map(t=>relayHLDP(t,hldp)); + const results=await Promise.all(promises); + allResults.push({type:'broadcast',targets:targets.length,results:results}); + }else{ + const hldp=hldpTextFromOps(g.ops); + const r=await relayHLDP(g.relay,hldp); + allResults.push({type:'relay',target:g.relay,...r}); } } - return{parsed:ops.length,results:results}; + return{groups:groups.length,results:allResults}; } @@ -158,11 +217,11 @@ const server=http.createServer(async(req,res)=>{ log('INFO','exec_result','exit='+r.code); jr(res,200,{ok:r.code===0,stdout:r.stdout,stderr:r.stderr,exitCode:r.code}); }else if(route==='/health'){ - jr(res,200,{ok:true,service:'guanghu-engine',version:'2.0.0',uptime:process.uptime().toFixed(0)+'s',timestamp:new Date().toISOString()}); + jr(res,200,{ok:true,service:'guanghu-engine',version:'2.1.0',uptime:process.uptime().toFixed(0)+'s',timestamp:new Date().toISOString()}); }else if(route==='/status'){ log('INFO','status',''); const total=os.totalmem(),free=os.freemem(); - jr(res,200,{ok:true,hostname:os.hostname(),platform:os.platform(),arch:os.arch(),cpus:os.cpus().length,uptime:os.uptime(),uptime_str:fmtUptime(os.uptime()),memory:{total:fmtBytes(total),free:fmtBytes(free),used:fmtBytes(total-free),usage:((total-free)/total*100).toFixed(1)+'%'},load:os.loadavg().map(n=>+n.toFixed(2)),engine:{version:'2.0.0',port:PORT,uptime:process.uptime().toFixed(0)+'s'}}); + jr(res,200,{ok:true,hostname:os.hostname(),platform:os.platform(),arch:os.arch(),cpus:os.cpus().length,uptime:os.uptime(),uptime_str:fmtUptime(os.uptime()),memory:{total:fmtBytes(total),free:fmtBytes(free),used:fmtBytes(total-free),usage:((total-free)/total*100).toFixed(1)+'%'},load:os.loadavg().map(n=>+n.toFixed(2)),engine:{version:'2.1.0',port:PORT,uptime:process.uptime().toFixed(0)+'s'}}); }else if(route==='/hlpd'){ const b=await parseBody(req); if(!b||!b.hlpd){jr(res,400,{error:'缺少 hlpd 参数'});return;} @@ -221,8 +280,8 @@ server.on('error',(e)=>{ server.listen(PORT,'0.0.0.0',()=>{ const h=os.hostname(); - log('INFO','startup','host='+h+' port='+PORT+' v2.0'); - console.log(' ⚔️ 光湖驱动引擎 v2.0 · HLDP 已就绪'); + log('INFO','startup','host='+h+' port='+PORT+' v2.1'); + console.log(' ⚔️ 光湖驱动引擎 v2.1 · HLDP + 集群串联'); console.log(' 主机名: '+h+' 端口: '+PORT); });