47 lines
2.4 KiB
JavaScript
47 lines
2.4 KiB
JavaScript
|
|
#!/usr/bin/env node
|
||
|
|
"use strict";
|
||
|
|
const fs = require("node:fs");
|
||
|
|
const os = require("node:os");
|
||
|
|
const path = require("node:path");
|
||
|
|
const { execFileSync } = require("node:child_process");
|
||
|
|
const { buildRegistry } = require("../module-registry/registry");
|
||
|
|
|
||
|
|
function run(file, args, timeout = 8000) {
|
||
|
|
try { return { ok:true, output:execFileSync(file,args,{encoding:"utf8",timeout,maxBuffer:1024*1024}).trim() }; }
|
||
|
|
catch (error) { return { ok:false, error:String(error.message).slice(0,300) }; }
|
||
|
|
}
|
||
|
|
function gitState(repo) {
|
||
|
|
const branch=run("/usr/bin/git",["-C",repo,"branch","--show-current"]);
|
||
|
|
const commit=run("/usr/bin/git",["-C",repo,"rev-parse","HEAD"]);
|
||
|
|
const status=run("/usr/bin/git",["-C",repo,"status","--porcelain"]);
|
||
|
|
return { path:repo, branch:branch.output||null, commit:commit.output||null, dirty:status.ok?Boolean(status.output):null, error:commit.error };
|
||
|
|
}
|
||
|
|
function redact(value) {
|
||
|
|
function visit(node) {
|
||
|
|
if (Array.isArray(node)) return node.map(visit);
|
||
|
|
if (node && typeof node === "object") {
|
||
|
|
const out={};
|
||
|
|
for (const [key,val] of Object.entries(node)) out[key]=/(token|secret|password|credential|private.?key)/i.test(key)?"[REDACTED]":visit(val);
|
||
|
|
return out;
|
||
|
|
}
|
||
|
|
if (typeof node === "string") return node.replace(/[\w.+-]+@[\w.-]+\.[A-Za-z]{2,}/g,"[REDACTED_EMAIL]");
|
||
|
|
return node;
|
||
|
|
}
|
||
|
|
return visit(value);
|
||
|
|
}
|
||
|
|
function capture({repoRoot=path.resolve(__dirname,"../.."),repoPaths=[path.resolve(__dirname,"../..")]}={}) {
|
||
|
|
const pm2=run("/usr/bin/env",["pm2","jlist"]);
|
||
|
|
const sockets=run("/usr/bin/ss",["-lntp"]);
|
||
|
|
const snapshot={
|
||
|
|
schema:"ZL-SERVER-MIRROR-1",captured_at:new Date().toISOString(),
|
||
|
|
server:{hostname:os.hostname(),platform:os.platform(),release:os.release(),arch:os.arch(),uptime_seconds:os.uptime(),load:os.loadavg(),memory:{total:os.totalmem(),free:os.freemem()}},
|
||
|
|
repositories:repoPaths.filter(p=>fs.existsSync(path.join(p,".git"))).map(gitState),
|
||
|
|
processes:pm2.ok?JSON.parse(pm2.output||"[]").map(p=>({name:p.name,status:p.pm2_env?.status,script:p.pm2_env?.pm_exec_path,restarts:p.pm2_env?.restart_time})):[],
|
||
|
|
listening:sockets.ok?sockets.output.split("\n").slice(1).map(line=>line.replace(/users:\(.*\)$/,"users:[REDACTED]")):[],
|
||
|
|
registry:buildRegistry(repoRoot)
|
||
|
|
};
|
||
|
|
return redact(snapshot);
|
||
|
|
}
|
||
|
|
if(require.main===module) process.stdout.write(JSON.stringify(capture(),null,2)+"\n");
|
||
|
|
module.exports={capture,redact};
|