mirror of
https://github.com/node-red/node-red.git
synced 2023-10-10 13:36:53 +02:00
Improve diagnostics content
This commit is contained in:
parent
3388f699a0
commit
a2fd705153
@ -1,27 +1,23 @@
|
||||
var apiUtils = require("@node-red/editor-api/lib/util");
|
||||
/** @type {runtime.RuntimeModule} */var runtimeAPI;
|
||||
|
||||
|
||||
let runtimeAPI;
|
||||
let settings;
|
||||
const apiUtil = require("../util");
|
||||
module.exports = {
|
||||
init: function(/** @type {runtime.RuntimeModule} */_runtimeAPI) {
|
||||
init: function(_settings, _runtimeAPI) {
|
||||
settings = _settings;
|
||||
runtimeAPI = _runtimeAPI;
|
||||
},
|
||||
getBasicReport: function(req, res) {
|
||||
var opts = {
|
||||
getReport: function(req, res) {
|
||||
const diagnosticsOptions = settings.diagnosticsOptions || {};
|
||||
const opts = {
|
||||
user: req.user,
|
||||
scope: "basic"
|
||||
scope: diagnosticsOptions.level || "basic"
|
||||
}
|
||||
runtimeAPI.diagnostics.get(opts).then(function(result) {
|
||||
res.json(result);
|
||||
});
|
||||
},
|
||||
getAdminReport: function(req, res) {
|
||||
var opts = {
|
||||
user: req.user,
|
||||
scope: "admin"
|
||||
if(diagnosticsOptions.enabled === false || diagnosticsOptions.enabled === "false") {
|
||||
apiUtil.rejectHandler(req, res, {message: "disabled", status: 403, code: "diagnosticsOptions.enabled" })
|
||||
} else {
|
||||
runtimeAPI.diagnostics.get(opts)
|
||||
.then(function(result) { res.json(result); })
|
||||
.catch(err => apiUtil.rejectHandler(req, res, err))
|
||||
}
|
||||
runtimeAPI.diagnostics.get(opts).then(function(result) {
|
||||
res.json(result);
|
||||
});
|
||||
}
|
||||
}
|
||||
|
@ -35,7 +35,7 @@ module.exports = {
|
||||
context.init(runtimeAPI);
|
||||
info.init(settings,runtimeAPI);
|
||||
plugins.init(runtimeAPI);
|
||||
diagnostics.init(runtimeAPI);
|
||||
diagnostics.init(settings, runtimeAPI);
|
||||
|
||||
var needsPermission = auth.needsPermission;
|
||||
|
||||
@ -97,8 +97,7 @@ module.exports = {
|
||||
adminApp.get("/plugins", needsPermission("plugins.read"), plugins.getAll, apiUtil.errorHandler);
|
||||
adminApp.get("/plugins/messages", needsPermission("plugins.read"), plugins.getCatalogs, apiUtil.errorHandler);
|
||||
|
||||
adminApp.get("/diagnostics/basic", needsPermission("settings.read"), diagnostics.getBasicReport, apiUtil.errorHandler);
|
||||
adminApp.get("/diagnostics/admin", needsPermission("flows.write"), diagnostics.getAdminReport, apiUtil.errorHandler);
|
||||
adminApp.get("/diagnostics", needsPermission("diagnostics.read"), diagnostics.getReport, apiUtil.errorHandler);
|
||||
|
||||
return adminApp;
|
||||
}
|
||||
|
@ -1,73 +1,176 @@
|
||||
|
||||
/**
|
||||
* @mixin @node-red/diagnostics
|
||||
* @namespace RED.runtime.diagnostics
|
||||
*/
|
||||
const os = require('os');
|
||||
const fs = require('fs');
|
||||
|
||||
var runtime;
|
||||
let runtime;
|
||||
let isContainerCached;
|
||||
let isWSLCached;
|
||||
|
||||
var util = require("@node-red/util").util;
|
||||
const isInWsl = () => {
|
||||
if (isWSLCached === undefined) {
|
||||
isWSLCached = getIsInWSL();
|
||||
}
|
||||
return isWSLCached;
|
||||
function getIsInWSL() {
|
||||
if (process.platform !== 'linux') {
|
||||
return false;
|
||||
}
|
||||
try {
|
||||
if (os.release().toLowerCase().includes('microsoft')) {
|
||||
if (isInContainer()) {
|
||||
return false;
|
||||
}
|
||||
return true;
|
||||
}
|
||||
return fs.readFileSync('/proc/version', 'utf8').toLowerCase().includes('microsoft') ? !isInContainer() : false;
|
||||
} catch (_) {
|
||||
return false;
|
||||
}
|
||||
}
|
||||
};
|
||||
|
||||
const isInContainer = () => {
|
||||
if (isContainerCached === undefined) {
|
||||
isContainerCached = hasDockerEnv() || hasDockerCGroup();
|
||||
}
|
||||
return isContainerCached;
|
||||
function hasDockerEnv() {
|
||||
try {
|
||||
fs.statSync('/.dockerenv');
|
||||
return true;
|
||||
} catch {
|
||||
return false;
|
||||
}
|
||||
}
|
||||
function hasDockerCGroup() {
|
||||
try {
|
||||
const s = fs.readFileSync('/proc/self/cgroup', 'utf8');
|
||||
if (s.includes('docker')) {
|
||||
return "docker"
|
||||
} else if (s.includes('kubepod')) {
|
||||
return "kubepod"
|
||||
} else if (s.includes('lxc')) {
|
||||
return "lxc"
|
||||
}
|
||||
} catch {
|
||||
return false;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
function buildDiagnosticReport(scope, callback) {
|
||||
var basic = {
|
||||
"report": "diagnostics",
|
||||
"scope": scope,
|
||||
"runtime": {
|
||||
version: runtime.settings.version,
|
||||
isStarted: runtime.isStarted()
|
||||
const modules = {};
|
||||
const nl = runtime.nodes.getNodeList();
|
||||
for (let i = 0; i < nl.length; i++) {
|
||||
if (modules[nl[i].module]) {
|
||||
continue;
|
||||
}
|
||||
modules[nl[i].module] = nl[i].version
|
||||
}
|
||||
|
||||
const now = new Date();
|
||||
const report = {
|
||||
report: "diagnostics",
|
||||
scope: scope,
|
||||
version: runtime.settings.version,
|
||||
isStarted: runtime.isStarted(),
|
||||
containerised: isInContainer(),
|
||||
wsl: isInWsl(),
|
||||
time: {
|
||||
timestamp: now.valueOf(),
|
||||
utc: "" + now,
|
||||
locale: now.toLocaleString(),
|
||||
},
|
||||
settings: {
|
||||
available: runtime.settings.available(),
|
||||
apiMaxLength: runtime.settings.apiMaxLength || "NO SETTING",
|
||||
coreNodesDir: runtime.settings.coreNodesDir,
|
||||
contextStorage: listContextModules(),
|
||||
debugMaxLength: runtime.settings.debugMaxLength,
|
||||
editorTheme: runtime.settings.editorTheme,
|
||||
flowFile: runtime.settings.flowFile,
|
||||
disableEditor:runtime.settings.disableEditor,
|
||||
debugMaxLength:runtime.settings.debugMaxLength,
|
||||
intl: Intl.DateTimeFormat().resolvedOptions(),
|
||||
nodejs: {
|
||||
version: process.version,
|
||||
arch: process.arch,
|
||||
platform: process.platform,
|
||||
memoryUsage: process.memoryUsage(),
|
||||
},
|
||||
os: {
|
||||
totalmem: os.totalmem(),
|
||||
freemem: os.freemem(),
|
||||
arch: os.arch(),
|
||||
loadavg: os.loadavg(),
|
||||
platform: os.platform(),
|
||||
release: os.release(),
|
||||
type: os.type(),
|
||||
uptime: os.uptime(),
|
||||
version: os.version(),
|
||||
},
|
||||
runtime: {
|
||||
modules: modules,
|
||||
settings: {
|
||||
available: runtime.settings.available(),
|
||||
apiMaxLength: runtime.settings.apiMaxLength || "NO SETTING",
|
||||
//coreNodesDir: runtime.settings.coreNodesDir,
|
||||
disableEditor: runtime.settings.disableEditor,
|
||||
contextStorage: listContextModules(),
|
||||
debugMaxLength: runtime.settings.debugMaxLength || "NO SETTING",
|
||||
editorTheme: runtime.settings.editorTheme || "NO SETTING",
|
||||
flowFile: runtime.settings.flowFile || "NO SETTING",
|
||||
mqttReconnectTime: runtime.settings.mqttReconnectTime || "NO SETTING",
|
||||
serialReconnectTime: runtime.settings.serialReconnectTime || "NO SETTING",
|
||||
|
||||
httpAdminRoot: runtime.settings.httpAdminRoot,
|
||||
httpAdminCors: runtime.settings.httpAdminCors ? "HAS SETTING": "NOT SET",
|
||||
httpNodeAuth: runtime.settings.httpNodeAuth ? "HAS SETTING": "NOT SET",
|
||||
adminAuth: runtime.settings.adminAuth ? "HAS SETTING" : "NO SETTING",
|
||||
|
||||
httpNodeRoot: runtime.settings.httpNodeRoot,
|
||||
httpNodeCors: runtime.settings.httpNodeCors ? "HAS SETTING": "NOT SET",
|
||||
httpAdminRoot: runtime.settings.adminAuth ? "HAS SETTING" : "NO SETTING",
|
||||
httpAdminCors: runtime.settings.httpAdminCors ? "HAS SETTING" : "NO SETTING",
|
||||
httpNodeAuth: runtime.settings.httpNodeAuth ? "HAS SETTING" : "NO SETTING",
|
||||
|
||||
httpStatic: runtime.settings.httpStatic,
|
||||
httpStaticCors: runtime.settings.httpStaticCors,
|
||||
httpAdminRoot: runtime.settings.httpAdminRoot || "NO SETTING",
|
||||
httpAdminCors: runtime.settings.httpAdminCors ? "HAS SETTING" : "NO SETTING",
|
||||
|
||||
mqttReconnectTime: runtime.settings.mqttReconnectTime,
|
||||
httpNodeRoot: runtime.settings.httpNodeRoot || "NO SETTING",
|
||||
httpNodeCors: runtime.settings.httpNodeCors ? "HAS SETTING" : "NO SETTING",
|
||||
|
||||
httpStatic: runtime.settings.httpStatic ? "HAS SETTING" : "NO SETTING",
|
||||
httpStatic: runtime.settings.httpStaticRoot || "NO SETTING",
|
||||
httpStaticCors: runtime.settings.httpStaticCors ? "HAS SETTING" : "NO SETTING",
|
||||
|
||||
uiHost: runtime.settings.uiHost ? "HAS SETTING": "NOT SET",
|
||||
uiPort: runtime.settings.uiPort ? "HAS SETTING": "NOT SET",
|
||||
userDir: runtime.settings.userDir ? "HAS SETTING": "NOT SET",
|
||||
|
||||
version: runtime.settings.version
|
||||
}
|
||||
}
|
||||
var admin = {};
|
||||
if(scope == "admin") {
|
||||
admin = {
|
||||
httpAdminCors: runtime.settings.httpAdminCors ? runtime.settings.httpAdminCors : "NOT SET",
|
||||
httpNodeCors: runtime.settings.httpNodeCors ? runtime.settings.httpNodeCors : "NOT SET",
|
||||
uiHost: runtime.settings.uiHost ? runtime.settings.uiHost : "NOT SET",
|
||||
uiPort: runtime.settings.uiPort ? runtime.settings.uiPort : "NOT SET",
|
||||
userDir: runtime.settings.userDir ? runtime.settings.userDir : "NOT SET",
|
||||
uiHost: runtime.settings.uiHost ? "HAS SETTING" : "NO SETTING",
|
||||
uiPort: runtime.settings.uiPort ? "HAS SETTING" : "NO SETTING",
|
||||
userDir: runtime.settings.userDir ? "HAS SETTING" : "NO SETTING",
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
var report = Object.assign({}, admin, basic);
|
||||
if (scope == "admin") {
|
||||
const moreSettings = {
|
||||
adminAuth_type: (runtime.settings.adminAuth && runtime.settings.adminAuth.type) ? runtime.settings.adminAuth.type : "NO SETTING",
|
||||
httpAdminCors: runtime.settings.httpAdminCors ? runtime.settings.httpAdminCors : "NO SETTING",
|
||||
httpNodeCors: runtime.settings.httpNodeCors ? runtime.settings.httpNodeCors : "NO SETTING",
|
||||
httpStaticCors: runtime.settings.httpStaticCors ? "HAS SETTING" : "NO SETTING",
|
||||
settingsFile: runtime.settings.settingsFile ? runtime.settings.settingsFile : "NO SETTING",
|
||||
uiHost: runtime.settings.uiHost ? runtime.settings.uiHost : "NO SETTING",
|
||||
uiPort: runtime.settings.uiPort ? runtime.settings.uiPort : "NO SETTING",
|
||||
userDir: runtime.settings.userDir ? runtime.settings.userDir : "NO SETTING",
|
||||
}
|
||||
const moreNodejs = {
|
||||
execPath: process.execPath,
|
||||
pid: process.pid,
|
||||
}
|
||||
const moreOs = {
|
||||
cpus: os.cpus(),
|
||||
homedir: os.homedir(),
|
||||
hostname: os.hostname(),
|
||||
networkInterfaces: os.networkInterfaces(),
|
||||
}
|
||||
report.runtime.settings = Object.assign({}, report.runtime.settings, moreSettings);
|
||||
report.nodejs = Object.assign({}, report.nodejs, moreNodejs);
|
||||
report.os = Object.assign({}, report.os, moreOs);
|
||||
}
|
||||
|
||||
callback(report);
|
||||
|
||||
/** gets a sanitised list containing only the module name */
|
||||
function listContextModules() {
|
||||
var keys = Object.keys(runtime.settings.contextStorage);
|
||||
var result = {};
|
||||
const keys = Object.keys(runtime.settings.contextStorage);
|
||||
const result = {};
|
||||
keys.forEach(e => {
|
||||
result[e] = {
|
||||
module: runtime.settings.contextStorage[e].module
|
||||
module: String(runtime.settings.contextStorage[e].module)
|
||||
}
|
||||
})
|
||||
return result;
|
||||
@ -75,40 +178,24 @@ function buildDiagnosticReport(scope, callback) {
|
||||
}
|
||||
|
||||
|
||||
var api = module.exports = {
|
||||
module.exports = {
|
||||
init: function (_runtime) {
|
||||
runtime = _runtime;
|
||||
},
|
||||
/**
|
||||
* Gets the node-red diagnostics report
|
||||
* @param {{scope: string}} - settings
|
||||
* @return {Promise} - the diagnostics information
|
||||
* @param {{scope: string}} opts - settings
|
||||
* @return {Promise} the diagnostics information
|
||||
* @memberof @node-red/diagnostics
|
||||
*/
|
||||
get: async function (opts) {
|
||||
return new Promise(function (resolve, reject) {
|
||||
opts = opts || {}
|
||||
var scope = opts.scope;
|
||||
try {
|
||||
if (scope === 'admin') {
|
||||
//admin level info
|
||||
runtime.log.audit({ event: "diagnostics.get", scope: "admin" }, opts.req);
|
||||
buildDiagnosticReport(scope, (report) => resolve(report));
|
||||
} else if (scope === 'detail') {
|
||||
//detail!
|
||||
runtime.log.audit({ event: "diagnostics.get", scope: "detail" }, opts.req);
|
||||
buildDiagnosticReport(scope, (report) => resolve(report));
|
||||
} else if (scope === 'basic') {
|
||||
//basic!
|
||||
runtime.log.audit({ event: "diagnostics.get", scope: "basic" }, opts.req);
|
||||
buildDiagnosticReport(scope, (report) => resolve(report));
|
||||
|
||||
} else {
|
||||
runtime.log.audit({ event: "diagnostics.get", scope: scope }, opts.req);
|
||||
resolve({});
|
||||
}
|
||||
runtime.log.audit({ event: "diagnostics.get", scope: opts.scope }, opts.req);
|
||||
buildDiagnosticReport(opts.scope, (report) => resolve(report));
|
||||
} catch (error) {
|
||||
runtime.log.audit({ event: "diagnostics.get", scope: scope }, opts.req);
|
||||
error.status = 500;
|
||||
reject(error);
|
||||
}
|
||||
})
|
||||
|
Loading…
Reference in New Issue
Block a user