mirror of
				https://github.com/node-red/node-red.git
				synced 2025-03-01 10:36:34 +00:00 
			
		
		
		
	| @@ -165,6 +165,7 @@ module.exports = function(grunt) { | ||||
|                     "packages/node_modules/@node-red/editor-client/src/js/ui/common/autoComplete.js", | ||||
|                     "packages/node_modules/@node-red/editor-client/src/js/ui/actions.js", | ||||
|                     "packages/node_modules/@node-red/editor-client/src/js/ui/deploy.js", | ||||
|                     "packages/node_modules/@node-red/editor-client/src/js/ui/diagnostics.js", | ||||
|                     "packages/node_modules/@node-red/editor-client/src/js/ui/diff.js", | ||||
|                     "packages/node_modules/@node-red/editor-client/src/js/ui/keyboard.js", | ||||
|                     "packages/node_modules/@node-red/editor-client/src/js/ui/workspaces.js", | ||||
|   | ||||
							
								
								
									
										23
									
								
								packages/node_modules/@node-red/editor-api/lib/admin/diagnostics.js
									
									
									
									
										vendored
									
									
										Normal file
									
								
							
							
						
						
									
										23
									
								
								packages/node_modules/@node-red/editor-api/lib/admin/diagnostics.js
									
									
									
									
										vendored
									
									
										Normal file
									
								
							| @@ -0,0 +1,23 @@ | ||||
| let runtimeAPI; | ||||
| let settings; | ||||
| const apiUtil = require("../util"); | ||||
| module.exports = { | ||||
|     init: function(_settings, _runtimeAPI) { | ||||
|         settings = _settings; | ||||
|         runtimeAPI = _runtimeAPI; | ||||
|     }, | ||||
|     getReport: function(req, res) { | ||||
|         const diagnosticsOpts = settings.diagnostics || {}; | ||||
|         const opts = { | ||||
|             user: req.user, | ||||
|             scope: diagnosticsOpts.level || "basic" | ||||
|         } | ||||
|         if(diagnosticsOpts.enabled === false || diagnosticsOpts.enabled === "false") { | ||||
|             apiUtil.rejectHandler(req, res, {message: "diagnostics are disabled", status: 403, code: "diagnostics.disabled" }) | ||||
|         } else { | ||||
|             runtimeAPI.diagnostics.get(opts) | ||||
|             .then(function(result) { res.json(result); }) | ||||
|             .catch(err => apiUtil.rejectHandler(req, res, err)) | ||||
|         } | ||||
|     } | ||||
| } | ||||
| @@ -23,6 +23,7 @@ var context = require("./context"); | ||||
| var auth = require("../auth"); | ||||
| var info = require("./settings"); | ||||
| var plugins = require("./plugins"); | ||||
| var diagnostics = require("./diagnostics"); | ||||
|  | ||||
| var apiUtil = require("../util"); | ||||
|  | ||||
| @@ -34,6 +35,7 @@ module.exports = { | ||||
|         context.init(runtimeAPI); | ||||
|         info.init(settings,runtimeAPI); | ||||
|         plugins.init(runtimeAPI); | ||||
|         diagnostics.init(settings, runtimeAPI); | ||||
|  | ||||
|         var needsPermission = auth.needsPermission; | ||||
|  | ||||
| @@ -95,6 +97,8 @@ 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", needsPermission("diagnostics.read"), diagnostics.getReport, apiUtil.errorHandler); | ||||
|  | ||||
|         return adminApp; | ||||
|     } | ||||
| } | ||||
|   | ||||
| @@ -940,6 +940,8 @@ | ||||
|         "format": "format JSON", | ||||
|         "rawMode": "Edit JSON", | ||||
|         "uiMode": "Visual editor", | ||||
|         "rawMode-readonly": "JSON", | ||||
|         "uiMode-readonly": "Visual", | ||||
|         "insertAbove": "Insert above", | ||||
|         "insertBelow": "Insert below", | ||||
|         "addItem": "Add item", | ||||
| @@ -1154,6 +1156,9 @@ | ||||
|         "start": "Start", | ||||
|         "next": "Next" | ||||
|     }, | ||||
|     "diagnostics": { | ||||
|         "title": "System Info" | ||||
|     }, | ||||
|     "languages" : { | ||||
|         "de": "German", | ||||
|         "en-US": "English", | ||||
|   | ||||
| @@ -940,6 +940,8 @@ | ||||
|         "format": "JSONフォーマット", | ||||
|         "rawMode": "JSONを編集", | ||||
|         "uiMode": "ビジュアルエディタ", | ||||
|         "rawMode-readonly": "JSON", | ||||
|         "uiMode-readonly": "ビジュアル", | ||||
|         "insertAbove": "上に挿入", | ||||
|         "insertBelow": "下に挿入", | ||||
|         "addItem": "要素を追加", | ||||
| @@ -1298,22 +1300,23 @@ | ||||
|         "zoom-in": "ズームイン", | ||||
|         "zoom-out": "ズームアウト", | ||||
|         "zoom-reset": "ズームリセット", | ||||
|         "toggle-navigator": "ナビゲータ表示切替" | ||||
|         "toggle-navigator": "ナビゲータ表示切替", | ||||
|         "show-system-info": "システムインフォメーション" | ||||
|     }, | ||||
|     "validator": { | ||||
|         "errors": { | ||||
| 	    "invalid-json": "JSONデータが不正: __error__", | ||||
| 	    "invalid-json-prop": "__prop__: JSONデータが不正: __error__", | ||||
| 	    "invalid-prop": "プロパティ式が不正", | ||||
| 	    "invalid-prop-prop": "__prop__: プロパティ式が不正", | ||||
| 	    "invalid-num": "数値が不正", | ||||
| 	    "invalid-num-prop": "__prop__: 数値が不正", | ||||
| 	    "invalid-regexp": "入力パターンが不正", | ||||
| 	    "invalid-regex-prop": "__prop__: 入力パターンが不正", | ||||
| 	    "missing-required-prop": "__prop__: プロパティが未設定", | ||||
| 	    "invalid-config": "__prop__: 設定ノードが不正", | ||||
| 	    "missing-config": "__prop__: 設定ノードが存在しません", | ||||
| 	    "validation-error": "__prop__: チェックエラー: __node__, __id__: __error__" | ||||
| 	} | ||||
|             "invalid-json": "JSONデータが不正: __error__", | ||||
|             "invalid-json-prop": "__prop__: JSONデータが不正: __error__", | ||||
|             "invalid-prop": "プロパティ式が不正", | ||||
|             "invalid-prop-prop": "__prop__: プロパティ式が不正", | ||||
|             "invalid-num": "数値が不正", | ||||
|             "invalid-num-prop": "__prop__: 数値が不正", | ||||
|             "invalid-regexp": "入力パターンが不正", | ||||
|             "invalid-regex-prop": "__prop__: 入力パターンが不正", | ||||
|             "missing-required-prop": "__prop__: プロパティが未設定", | ||||
|             "invalid-config": "__prop__: 設定ノードが不正", | ||||
|             "missing-config": "__prop__: 設定ノードが存在しません", | ||||
|             "validation-error": "__prop__: チェックエラー: __node__, __id__: __error__" | ||||
| 	      } | ||||
|     } | ||||
| } | ||||
|   | ||||
| @@ -730,6 +730,7 @@ var RED = (function() { | ||||
|         RED.search.init(); | ||||
|         RED.actionList.init(); | ||||
|         RED.editor.init(); | ||||
|         RED.diagnostics.init(); | ||||
|         RED.diff.init(); | ||||
|  | ||||
|  | ||||
|   | ||||
							
								
								
									
										61
									
								
								packages/node_modules/@node-red/editor-client/src/js/ui/diagnostics.js
									
									
									
									
										vendored
									
									
										Normal file
									
								
							
							
						
						
									
										61
									
								
								packages/node_modules/@node-red/editor-client/src/js/ui/diagnostics.js
									
									
									
									
										vendored
									
									
										Normal file
									
								
							| @@ -0,0 +1,61 @@ | ||||
|  | ||||
| RED.diagnostics = (function () { | ||||
|  | ||||
|     function init() { | ||||
|         if (RED.settings.get('diagnostics.ui', true) === false) { | ||||
|             return; | ||||
|         } | ||||
|         RED.actions.add("core:show-system-info", function () { show(); }); | ||||
|     } | ||||
|  | ||||
|     function show() { | ||||
|         $.ajax({ | ||||
|             headers: { | ||||
|                 "Accept": "application/json" | ||||
|             }, | ||||
|             cache: false, | ||||
|             url: 'diagnostics', | ||||
|             success: function (data) { | ||||
|                 var json = JSON.stringify(data || {}, "", 4); | ||||
|                 if (json === "{}") { | ||||
|                     json = "{\n\n}"; | ||||
|                 } | ||||
|                 RED.editor.editJSON({ | ||||
|                     title: RED._('diagnostics.title'), | ||||
|                     value: json, | ||||
|                     requireValid: true, | ||||
|                     readOnly: true, | ||||
|                     toolbarButtons: [ | ||||
|                         { | ||||
|                             text: RED._('clipboard.export.copy'), | ||||
|                             icon: 'fa fa-copy', | ||||
|                             click: function () { | ||||
|                                 RED.clipboard.copyText(json, $(this), RED._('clipboard.copyMessageValue')) | ||||
|                             } | ||||
|                         }, | ||||
|                         { | ||||
|                             text: RED._('clipboard.download'), | ||||
|                             icon: 'fa fa-download', | ||||
|                             click: function () { | ||||
|                                 var element = document.createElement('a'); | ||||
|                                 element.setAttribute('href', 'data:text/plain;charset=utf-8,' + encodeURIComponent(json)); | ||||
|                                 element.setAttribute('download', "system-info.json"); | ||||
|                                 element.style.display = 'none'; | ||||
|                                 document.body.appendChild(element); | ||||
|                                 element.click(); | ||||
|                                 document.body.removeChild(element); | ||||
|                             } | ||||
|                         }, | ||||
|                     ] | ||||
|                 }); | ||||
|             }, | ||||
|             error: function (jqXHR, textStatus, errorThrown) { | ||||
|                 console.log("Unexpected error loading system info:", jqXHR.status, textStatus, errorThrown); | ||||
|             } | ||||
|         }); | ||||
|     } | ||||
|  | ||||
|     return { | ||||
|         init: init, | ||||
|     }; | ||||
| })(); | ||||
| @@ -21,7 +21,9 @@ | ||||
|         '<ul id="red-ui-editor-type-json-tabs"></ul>'+ | ||||
|         '<div id="red-ui-editor-type-json-tab-raw" class="red-ui-editor-type-json-tab-content hide">'+ | ||||
|             '<div class="form-row" style="margin-bottom: 3px; text-align: right;">'+ | ||||
|                 '<button id="node-input-json-reformat" class="red-ui-button red-ui-button-small"><span data-i18n="jsonEditor.format"></span></button>'+ | ||||
|                 '<span class="button-group">'+ | ||||
|                     '<button id="node-input-json-reformat" class="red-ui-button red-ui-button-small"><span data-i18n="jsonEditor.format"></span></button>'+ | ||||
|                 '<span class="button-group">'+ | ||||
|             '</div>'+ | ||||
|             '<div class="form-row node-text-editor-row">'+ | ||||
|                 '<div style="height: 200px;min-height: 150px;" class="node-text-editor" id="node-input-json"></div>'+ | ||||
| @@ -34,7 +36,7 @@ | ||||
|  | ||||
|     var activeTab; | ||||
|  | ||||
|     function insertNewItem(parent,index,copyIndex) { | ||||
|     function insertNewItem(parent,index,copyIndex,readOnly) { | ||||
|         var newValue = ""; | ||||
|  | ||||
|         if (parent.children.length > 0) { | ||||
| @@ -60,26 +62,26 @@ | ||||
|                 newKey = keyRoot+"-"+(keySuffix++); | ||||
|             } | ||||
|         } | ||||
|         var newItem = handleItem(newKey,newValue,parent.depth+1,parent); | ||||
|         var newItem = handleItem(newKey,newValue,parent.depth+1,parent,readOnly); | ||||
|         parent.treeList.insertChildAt(newItem, index, true); | ||||
|         parent.treeList.expand(); | ||||
|     } | ||||
|     function showObjectMenu(button,item) { | ||||
|     function showObjectMenu(button,item,readOnly) { | ||||
|         var elementPos = button.offset(); | ||||
|         var options = []; | ||||
|         if (item.parent) { | ||||
|             options.push({id:"red-ui-editor-type-json-menu-insert-above", icon:"fa fa-toggle-up", label:RED._('jsonEditor.insertAbove'),onselect:function(){ | ||||
|                 var index = item.parent.children.indexOf(item); | ||||
|                 insertNewItem(item.parent,index,index); | ||||
|                 insertNewItem(item.parent,index,index,readOnly); | ||||
|             }}); | ||||
|             options.push({id:"red-ui-editor-type-json-menu-insert-below", icon:"fa fa-toggle-down", label:RED._('jsonEditor.insertBelow'),onselect:function(){ | ||||
|                 var index = item.parent.children.indexOf(item)+1; | ||||
|                 insertNewItem(item.parent,index,index-1); | ||||
|                 insertNewItem(item.parent,index,index-1,readOnly); | ||||
|             }}); | ||||
|         } | ||||
|         if (item.type === 'array' || item.type === 'object') { | ||||
|             options.push({id:"red-ui-editor-type-json-menu-add-child", icon:"fa fa-plus", label:RED._('jsonEditor.addItem'),onselect:function(){ | ||||
|                 insertNewItem(item,item.children.length,item.children.length-1); | ||||
|                 insertNewItem(item,item.children.length,item.children.length-1,readOnly); | ||||
|             }}); | ||||
|         } | ||||
|         if (item.parent) { | ||||
| @@ -121,7 +123,7 @@ | ||||
|                         newKey = keyRoot+"-"+(keySuffix++); | ||||
|                     } | ||||
|                 } | ||||
|                 var newItem = handleItem(newKey,convertToObject(item),item.parent.depth+1,item.parent); | ||||
|                 var newItem = handleItem(newKey,convertToObject(item),item.parent.depth+1,item.parent,readOnly); | ||||
|                 var index = item.parent.children.indexOf(item)+1; | ||||
|  | ||||
|                 item.parent.treeList.insertChildAt(newItem, index, true); | ||||
| @@ -171,24 +173,24 @@ | ||||
|         menuOptionMenu.show(); | ||||
|     } | ||||
|  | ||||
|     function parseObject(obj,depth,parent) { | ||||
|     function parseObject(obj,depth,parent,readOnly) { | ||||
|         var result = []; | ||||
|         for (var prop in obj) { | ||||
|             if (obj.hasOwnProperty(prop)) { | ||||
|                 result.push(handleItem(prop,obj[prop],depth,parent)); | ||||
|                 result.push(handleItem(prop,obj[prop],depth,parent,readOnly)); | ||||
|             } | ||||
|         } | ||||
|         return result; | ||||
|     } | ||||
|     function parseArray(obj,depth,parent) { | ||||
|     function parseArray(obj,depth,parent,readOnly) { | ||||
|         var result = []; | ||||
|         var l = obj.length; | ||||
|         for (var i=0;i<l;i++) { | ||||
|             result.push(handleItem(i,obj[i],depth,parent)); | ||||
|             result.push(handleItem(i,obj[i],depth,parent,readOnly)); | ||||
|         } | ||||
|         return result; | ||||
|     } | ||||
|     function handleItem(key,val,depth,parent) { | ||||
|     function handleItem(key,val,depth,parent,readOnly) { | ||||
|         var item = {depth:depth, type: typeof val}; | ||||
|         var container = $('<span class="red-ui-editor-type-json-editor-label">'); | ||||
|         if (key != null) { | ||||
| @@ -204,11 +206,14 @@ | ||||
|             if (parent && parent.type === "array") { | ||||
|                 keyLabel.addClass("red-ui-editor-type-json-editor-label-array-key") | ||||
|             } | ||||
|  | ||||
|             if(readOnly) { | ||||
|                 keyLabel.addClass("readonly") | ||||
|             } | ||||
|             keyLabel.on("click", function(evt) { | ||||
|                 if (item.parent.type === 'array') { | ||||
|                     return; | ||||
|                 } | ||||
|                 if (readOnly) { return; } | ||||
|                 evt.preventDefault(); | ||||
|                 evt.stopPropagation(); | ||||
|                 var w = Math.max(150,keyLabel.width()); | ||||
| @@ -253,10 +258,10 @@ | ||||
|             item.expanded = depth < 2; | ||||
|             item.type = "array"; | ||||
|             item.deferBuild = depth >= 2; | ||||
|             item.children = parseArray(val,depth+1,item); | ||||
|             item.children = parseArray(val,depth+1,item,readOnly); | ||||
|         } else if (val !== null && item.type === "object") { | ||||
|             item.expanded = depth < 2; | ||||
|             item.children = parseObject(val,depth+1,item); | ||||
|             item.children = parseObject(val,depth+1,item,readOnly); | ||||
|             item.deferBuild = depth >= 2; | ||||
|         } else { | ||||
|             item.value = val; | ||||
| @@ -287,7 +292,11 @@ | ||||
|         // | ||||
|         var orphanedChildren; | ||||
|         var valueLabel = $('<span class="red-ui-editor-type-json-editor-label-value">').addClass(valClass).text(valValue).appendTo(container); | ||||
|         if (readOnly) { | ||||
|             valueLabel.addClass("readonly") | ||||
|         } | ||||
|         valueLabel.on("click", function(evt) { | ||||
|             if (readOnly) { return; } | ||||
|             evt.preventDefault(); | ||||
|             evt.stopPropagation(); | ||||
|             if (valType === 'str') { | ||||
| @@ -395,17 +404,19 @@ | ||||
|             valueLabel.hide(); | ||||
|         }) | ||||
|         item.gutter = $('<span class="red-ui-editor-type-json-editor-item-gutter"></span>'); | ||||
|  | ||||
|         if (parent) {//red-ui-editor-type-json-editor-item-handle | ||||
|             $('<span class="red-ui-editor-type-json-editor-item-handle"><i class="fa fa-bars"></span>').appendTo(item.gutter); | ||||
|         } else { | ||||
|             $('<span></span>').appendTo(item.gutter); | ||||
|         if(!readOnly) { | ||||
|             if (parent) { | ||||
|                 $('<span class="red-ui-editor-type-json-editor-item-handle"><i class="fa fa-bars"></span>').appendTo(item.gutter); | ||||
|             } else { | ||||
|                 $('<span></span>').appendTo(item.gutter); | ||||
|             } | ||||
|             $('<button type="button" class="editor-button editor-button-small"><i class="fa fa-caret-down"></button>').appendTo(item.gutter).on("click", function(evt) { | ||||
|                 evt.preventDefault(); | ||||
|                 evt.stopPropagation(); | ||||
|                 showObjectMenu($(this), item, readOnly); | ||||
|             }); | ||||
|         } | ||||
|         $('<button type="button" class="editor-button editor-button-small"><i class="fa fa-caret-down"></button>').appendTo(item.gutter).on("click", function(evt) { | ||||
|             evt.preventDefault(); | ||||
|             evt.stopPropagation(); | ||||
|             showObjectMenu($(this), item); | ||||
|         }); | ||||
|  | ||||
|         item.element = container; | ||||
|         return item; | ||||
|     } | ||||
| @@ -501,7 +512,25 @@ | ||||
|                 open: function(tray) { | ||||
|                     var trayBody = tray.find('.red-ui-tray-body'); | ||||
|                     var dialogForm = RED.editor.buildEditForm(tray.find('.red-ui-tray-body'),'dialog-form',type,'editor'); | ||||
|  | ||||
|                     var toolbarButtons = options.toolbarButtons || []; | ||||
|                     if (toolbarButtons.length) { | ||||
|                         toolbarButtons.forEach(function (button) { | ||||
|                             var element = $('<button type="button" class="red-ui-button red-ui-button-small"> </button>') | ||||
|                                 .insertBefore("#node-input-json-reformat") | ||||
|                                 .on("click", function (evt) { | ||||
|                                     evt.preventDefault(); | ||||
|                                     if (button.click !== undefined) { | ||||
|                                         button.click.call(element, evt); | ||||
|                                     } | ||||
|                                 }); | ||||
|                             if (button.id) { element.attr("id", button.id); } | ||||
|                             if (button.title) { element.attr("title", button.title); } | ||||
|                             if (button.icon) { element.append($("<i></i>").attr("class", button.icon)); } | ||||
|                             if (button.label || button.text) { | ||||
|                                 element.append($("<span></span>").text(" " + (button.label || button.text))); | ||||
|                             } | ||||
|                         }); | ||||
|                     } | ||||
|                     var container = $("#red-ui-editor-type-json-tab-ui-container").css({"height":"100%"}); | ||||
|                     var filterDepth = Infinity; | ||||
|                     var list = $('<div class="red-ui-debug-msg-payload red-ui-editor-type-json-editor">').appendTo(container).treeList({ | ||||
| @@ -531,11 +560,13 @@ | ||||
|                         }) | ||||
|                     }); | ||||
|  | ||||
|  | ||||
|                     expressionEditor = RED.editor.createEditor({ | ||||
|                         id: 'node-input-json', | ||||
|                         value: "", | ||||
|                         mode:"ace/mode/json", | ||||
|                         value: value||"", | ||||
|                         mode:"ace/mode/json", | ||||
|                         readOnly: !!options.readOnly, | ||||
|                         stateId: options.stateId, | ||||
|                         focus: true | ||||
|                     }); | ||||
| @@ -576,7 +607,7 @@ | ||||
|                                     var raw = expressionEditor.getValue().trim() ||"{}"; | ||||
|                                     try { | ||||
|                                         var parsed = JSON.parse(raw); | ||||
|                                         rootNode = handleItem(null,parsed,0,null); | ||||
|                                         rootNode = handleItem(null,parsed,0,null,options.readOnly); | ||||
|                                         rootNode.class = "red-ui-editor-type-json-root-node" | ||||
|                                         list.treeList('data',[rootNode]); | ||||
|                                     } catch(err) { | ||||
| @@ -594,12 +625,12 @@ | ||||
|  | ||||
|                     tabs.addTab({ | ||||
|                         id: 'json-raw', | ||||
|                         label: RED._('jsonEditor.rawMode'), | ||||
|                         label: options.readOnly ? RED._('jsonEditor.rawMode-readonly') : RED._('jsonEditor.rawMode'), | ||||
|                         content: $("#red-ui-editor-type-json-tab-raw") | ||||
|                     }); | ||||
|                     tabs.addTab({ | ||||
|                         id: 'json-ui', | ||||
|                         label: RED._('jsonEditor.uiMode'), | ||||
|                         label: options.readOnly ? RED._('jsonEditor.uiMode-readonly') : RED._('jsonEditor.uiMode'), | ||||
|                         content: $("#red-ui-editor-type-json-tab-ui") | ||||
|                     }); | ||||
|                     finishedBuild = true; | ||||
|   | ||||
| @@ -701,6 +701,10 @@ div.red-ui-button-small.red-ui-color-picker-opacity-slider-handle { | ||||
|         border-color: $list-item-background-hover; | ||||
|         border-style: dashed; | ||||
|     } | ||||
|     &.readonly { | ||||
|         cursor: pointer; | ||||
|         pointer-events: none; | ||||
|     } | ||||
| } | ||||
| .red-ui-editor-type-json-editor-item-gutter { | ||||
|     width: 48px; | ||||
| @@ -720,6 +724,10 @@ div.red-ui-button-small.red-ui-color-picker-opacity-slider-handle { | ||||
|     > span, > button { | ||||
|         display: none; | ||||
|     } | ||||
|     &.readonly { | ||||
|         cursor: pointer; | ||||
|         pointer-events: none; | ||||
|     } | ||||
| } | ||||
|  | ||||
|  | ||||
|   | ||||
							
								
								
									
										202
									
								
								packages/node_modules/@node-red/runtime/lib/api/diagnostics.js
									
									
									
									
										vendored
									
									
										Normal file
									
								
							
							
						
						
									
										202
									
								
								packages/node_modules/@node-red/runtime/lib/api/diagnostics.js
									
									
									
									
										vendored
									
									
										Normal file
									
								
							| @@ -0,0 +1,202 @@ | ||||
|  | ||||
| const os = require('os'); | ||||
| const fs = require('fs'); | ||||
|  | ||||
| let runtime; | ||||
| let isContainerCached; | ||||
| let isWSLCached; | ||||
|  | ||||
| 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) { | ||||
|     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 {locale, timeZone} = Intl.DateTimeFormat().resolvedOptions(); | ||||
|     const report = { | ||||
|         report: "diagnostics", | ||||
|         scope: scope, | ||||
|         time: { | ||||
|             utc: now.toUTCString(), | ||||
|             local: now.toLocaleString(), | ||||
|         }, | ||||
|         intl: { | ||||
|             locale, timeZone | ||||
|         }, | ||||
|         nodejs: { | ||||
|             version: process.version, | ||||
|             arch: process.arch, | ||||
|             platform: process.platform, | ||||
|             memoryUsage: process.memoryUsage(), | ||||
|         }, | ||||
|         os: { | ||||
|             containerised: isInContainer(), | ||||
|             wsl: isInWsl(), | ||||
|             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: { | ||||
|             isStarted: runtime.isStarted(), | ||||
|             modules: modules, | ||||
|             version: runtime.settings.version, | ||||
|             settings: { | ||||
|                 available: runtime.settings.available(), | ||||
|                 apiMaxLength: runtime.settings.apiMaxLength || "UNSET", | ||||
|                 //coreNodesDir: runtime.settings.coreNodesDir, | ||||
|                 disableEditor: runtime.settings.disableEditor, | ||||
|                 contextStorage: listContextModules(), | ||||
|                 debugMaxLength: runtime.settings.debugMaxLength || "UNSET", | ||||
|                 editorTheme: runtime.settings.editorTheme || "UNSET", | ||||
|                 flowFile: runtime.settings.flowFile || "UNSET", | ||||
|                 mqttReconnectTime: runtime.settings.mqttReconnectTime || "UNSET", | ||||
|                 serialReconnectTime: runtime.settings.serialReconnectTime || "UNSET", | ||||
|  | ||||
|                 adminAuth: runtime.settings.adminAuth ? "SET" : "UNSET", | ||||
|  | ||||
|                 httpAdminRoot: runtime.settings.httpAdminRoot || "UNSET", | ||||
|                 httpAdminCors: runtime.settings.httpAdminCors ? "SET" : "UNSET", | ||||
|                 httpNodeAuth: runtime.settings.httpNodeAuth ? "SET" : "UNSET", | ||||
|  | ||||
|                 httpNodeRoot: runtime.settings.httpNodeRoot || "UNSET", | ||||
|                 httpNodeCors: runtime.settings.httpNodeCors ? "SET" : "UNSET", | ||||
|  | ||||
|                 httpStatic: runtime.settings.httpStatic ? "SET" : "UNSET", | ||||
|                 httpStaticRoot: runtime.settings.httpStaticRoot || "UNSET", | ||||
|                 httpStaticCors: runtime.settings.httpStaticCors ? "SET" : "UNSET", | ||||
|  | ||||
|                 uiHost: runtime.settings.uiHost ? "SET" : "UNSET", | ||||
|                 uiPort: runtime.settings.uiPort ? "SET" : "UNSET", | ||||
|                 userDir: runtime.settings.userDir ? "SET" : "UNSET", | ||||
|             } | ||||
|         } | ||||
|     } | ||||
|  | ||||
|     // if (scope == "admin") { | ||||
|     //     const moreSettings = { | ||||
|     //         adminAuth_type: (runtime.settings.adminAuth && runtime.settings.adminAuth.type) ? runtime.settings.adminAuth.type : "UNSET", | ||||
|     //         httpAdminCors: runtime.settings.httpAdminCors ? runtime.settings.httpAdminCors : "UNSET", | ||||
|     //         httpNodeCors: runtime.settings.httpNodeCors ? runtime.settings.httpNodeCors : "UNSET", | ||||
|     //         httpStaticCors: runtime.settings.httpStaticCors ? "SET" : "UNSET", | ||||
|     //         settingsFile: runtime.settings.settingsFile ? runtime.settings.settingsFile : "UNSET", | ||||
|     //         uiHost: runtime.settings.uiHost ? runtime.settings.uiHost : "UNSET", | ||||
|     //         uiPort: runtime.settings.uiPort ? runtime.settings.uiPort : "UNSET", | ||||
|     //         userDir: runtime.settings.userDir ? runtime.settings.userDir : "UNSET", | ||||
|     //     } | ||||
|     //     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() { | ||||
|         const keys = Object.keys(runtime.settings.contextStorage); | ||||
|         const result = {}; | ||||
|         keys.forEach(e => { | ||||
|             result[e] = { | ||||
|                 module: String(runtime.settings.contextStorage[e].module) | ||||
|             } | ||||
|         }) | ||||
|         return result; | ||||
|     } | ||||
| } | ||||
|  | ||||
|  | ||||
| module.exports = { | ||||
|     init: function (_runtime) { | ||||
|         runtime = _runtime; | ||||
|     }, | ||||
|     /** | ||||
|     * Gets the node-red diagnostics report | ||||
|     * @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 || {} | ||||
|             try { | ||||
|                 runtime.log.audit({ event: "diagnostics.get", scope: opts.scope }, opts.req); | ||||
|                 buildDiagnosticReport(opts.scope, (report) => resolve(report)); | ||||
|             } catch (error) { | ||||
|                 error.status = 500; | ||||
|                 reject(error); | ||||
|             } | ||||
|         }) | ||||
|     }, | ||||
| } | ||||
| @@ -29,6 +29,7 @@ var api = module.exports = { | ||||
|         api.projects.init(runtime); | ||||
|         api.context.init(runtime); | ||||
|         api.plugins.init(runtime); | ||||
|         api.diagnostics.init(runtime); | ||||
|     }, | ||||
|  | ||||
|     comms: require("./comms"), | ||||
| @@ -39,6 +40,7 @@ var api = module.exports = { | ||||
|     projects: require("./projects"), | ||||
|     context: require("./context"), | ||||
|     plugins: require("./plugins"), | ||||
|     diagnostics: require("./diagnostics"), | ||||
|  | ||||
|     isStarted: async function(opts) { | ||||
|         return runtime.isStarted(); | ||||
|   | ||||
| @@ -142,6 +142,13 @@ var api = module.exports = { | ||||
|             } | ||||
|  | ||||
|             safeSettings.flowEncryptionType = runtime.nodes.getCredentialKeyType(); | ||||
|  | ||||
|             safeSettings.diagnostics = { | ||||
|                 //unless diagnostics.ui and diagnostics.enabled are explicitly false, they will default to true. | ||||
|                 enabled: (runtime.settings.diagnostics && runtime.settings.diagnostics.enabled === false) ? false : true, | ||||
|                 ui: (runtime.settings.diagnostics && runtime.settings.diagnostics.ui === false) ? false : true | ||||
|             } | ||||
|  | ||||
|             runtime.settings.exportNodeSettings(safeSettings); | ||||
|             runtime.plugins.exportPluginSettings(safeSettings); | ||||
|         } | ||||
|   | ||||
| @@ -399,7 +399,12 @@ module.exports = { | ||||
|     * @memberof @node-red/runtime | ||||
|     */ | ||||
|     version: externalAPI.version, | ||||
|  | ||||
|      | ||||
|     /** | ||||
|     * @memberof @node-red/diagnostics | ||||
|     */ | ||||
|     diagnostics:externalAPI.diagnostics, | ||||
|      | ||||
|     storage: storage, | ||||
|     events: events, | ||||
|     hooks: hooks, | ||||
|   | ||||
							
								
								
									
										9
									
								
								packages/node_modules/node-red/lib/red.js
									
									
									
									
										vendored
									
									
								
							
							
						
						
									
										9
									
								
								packages/node_modules/node-red/lib/red.js
									
									
									
									
										vendored
									
									
								
							| @@ -229,5 +229,12 @@ module.exports = { | ||||
|      * @see @node-red/editor-api_auth | ||||
|      * @memberof node-red | ||||
|      */ | ||||
|     auth: api.auth | ||||
|     auth: api.auth, | ||||
|  | ||||
|     /** | ||||
|      * The editor authentication api. | ||||
|      * @see @node-red/editor-api_auth | ||||
|      * @memberof node-red | ||||
|      */ | ||||
|     get diagnostics() { return api.diagnostics } | ||||
| }; | ||||
|   | ||||
							
								
								
									
										14
									
								
								packages/node_modules/node-red/settings.js
									
									
									
									
										vendored
									
									
								
							
							
						
						
									
										14
									
								
								packages/node_modules/node-red/settings.js
									
									
									
									
										vendored
									
									
								
							| @@ -242,6 +242,7 @@ module.exports = { | ||||
| /******************************************************************************* | ||||
|  * Runtime Settings | ||||
|  *  - lang | ||||
|  *  - diagnostics | ||||
|  *  - logging | ||||
|  *  - contextStorage | ||||
|  *  - exportGlobalContextKeys | ||||
| @@ -254,6 +255,19 @@ module.exports = { | ||||
|       */ | ||||
|      // lang: "de", | ||||
|  | ||||
|     /** Configure diagnostics options  | ||||
|      * - enabled:  When `enabled` is `true` (or unset), diagnostics data will | ||||
|      *   be available at http://localhost:1880/diagnostics   | ||||
|      * - ui: When `ui` is `true` (or unset), the action `show-system-info` will  | ||||
|      *   be available to logged in users of node-red editor   | ||||
|     */  | ||||
|     diagnostics: { | ||||
|         /** enable or disable diagnostics endpoint. Must be set to `false` to disable */ | ||||
|         enabled: true, | ||||
|         /** enable or disable diagnostics display in the node-red editor. Must be set to `false` to disable */ | ||||
|         ui: true, | ||||
|     }, | ||||
|  | ||||
|      /** Configure the logging output */ | ||||
|      logging: { | ||||
|          /** Only console logging is currently supported */ | ||||
|   | ||||
							
								
								
									
										119
									
								
								test/unit/@node-red/editor-api/lib/admin/diagnostics_spec.js
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										119
									
								
								test/unit/@node-red/editor-api/lib/admin/diagnostics_spec.js
									
									
									
									
									
										Normal file
									
								
							| @@ -0,0 +1,119 @@ | ||||
| const should = require("should"); | ||||
| const request = require('supertest'); | ||||
| const express = require('express'); | ||||
| const bodyParser = require("body-parser"); | ||||
| const sinon = require('sinon'); | ||||
|  | ||||
| let app; | ||||
|  | ||||
| const NR_TEST_UTILS = require("nr-test-utils"); | ||||
| const diagnostics = NR_TEST_UTILS.require("@node-red/editor-api/lib/admin/diagnostics"); | ||||
|  | ||||
| describe("api/editor/diagnostics", function() { | ||||
|     before(function() { | ||||
|         app = express(); | ||||
|         app.use(bodyParser.json()); | ||||
|         app.get("/diagnostics",diagnostics.getReport); | ||||
|     }); | ||||
|  | ||||
|     it('returns the diagnostics report when explicitly enabled', function(done) { | ||||
|         const settings = { diagnostics: { ui: true, enabled: true } } | ||||
|         const runtimeAPI  = { | ||||
|             diagnostics: { | ||||
|                 get: async function (opts) { | ||||
|                     return new Promise(function (resolve, reject) { | ||||
|                         opts = opts || {} | ||||
|                         try { | ||||
|                             resolve({ opts: opts, a:1, b:2}); | ||||
|                         } catch (error) { | ||||
|                             error.status = 500; | ||||
|                             reject(error); | ||||
|                         } | ||||
|                     }) | ||||
|                 } | ||||
|             } | ||||
|         } | ||||
|  | ||||
|         diagnostics.init(settings, runtimeAPI); | ||||
|  | ||||
|         request(app) | ||||
|         .get("/diagnostics") | ||||
|         .expect(200) | ||||
|         .end(function(err,res) { | ||||
|             if (err || typeof res.error === "object") { | ||||
|                 return done(err || res.error); | ||||
|             } | ||||
|             res.should.have.property("statusCode",200); | ||||
|             res.body.should.have.property("a",1); | ||||
|             res.body.should.have.property("b",2); | ||||
|             done(); | ||||
|         }); | ||||
|     }); | ||||
|     it('returns the diagnostics report when not explicitly enabled (implicitly enabled)', function(done) { | ||||
|         const settings = { diagnostics: { enabled: undefined } } | ||||
|         const runtimeAPI  = { | ||||
|             diagnostics: { | ||||
|                 get: async function (opts) { | ||||
|                     return new Promise(function (resolve, reject) { | ||||
|                         opts = opts || {} | ||||
|                         try { | ||||
|                             resolve({ opts: opts, a:3, b:4}); | ||||
|                         } catch (error) { | ||||
|                             error.status = 500; | ||||
|                             reject(error); | ||||
|                         } | ||||
|                     }) | ||||
|                 } | ||||
|             } | ||||
|         } | ||||
|  | ||||
|         diagnostics.init(settings, runtimeAPI); | ||||
|  | ||||
|         request(app) | ||||
|         .get("/diagnostics") | ||||
|         .expect(200) | ||||
|         .end(function(err,res) { | ||||
|             if (err || typeof res.error === "object") { | ||||
|                 return done(err || res.error); | ||||
|             } | ||||
|             res.should.have.property("statusCode",200); | ||||
|             res.body.should.have.property("a",3); | ||||
|             res.body.should.have.property("b",4); | ||||
|             done(); | ||||
|         }); | ||||
|     }); | ||||
|     it('should error when setting is disabled', function(done) { | ||||
|         const settings = { diagnostics: { ui: true, enabled: false } } | ||||
|         const runtimeAPI  = { | ||||
|             diagnostics: { | ||||
|                 get: async function (opts) { | ||||
|                     return new Promise(function (resolve, reject) { | ||||
|                         opts = opts || {} | ||||
|                         try { | ||||
|                             resolve({ opts: opts}); | ||||
|                         } catch (error) { | ||||
|                             error.status = 500; | ||||
|                             reject(error); | ||||
|                         } | ||||
|                     }) | ||||
|                 } | ||||
|             } | ||||
|         } | ||||
|  | ||||
|         diagnostics.init(settings, runtimeAPI); | ||||
|  | ||||
|         request(app) | ||||
|         .get("/diagnostics") | ||||
|         .expect(403) | ||||
|         .end(function(err,res) { | ||||
|             if (!err && typeof res.error !== "object") { | ||||
|                 return done(new Error("accessing diagnostics endpoint while disabled should raise error")); | ||||
|             } | ||||
|             res.should.have.property("statusCode",403); | ||||
|             res.body.should.have.property("message","diagnostics are disabled"); | ||||
|             res.body.should.have.property("code","diagnostics.disabled"); | ||||
|             done(); | ||||
|         }); | ||||
|     }); | ||||
|  | ||||
| }); | ||||
							
								
								
									
										126
									
								
								test/unit/@node-red/runtime/lib/api/diagnostics_spec.js
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										126
									
								
								test/unit/@node-red/runtime/lib/api/diagnostics_spec.js
									
									
									
									
									
										Normal file
									
								
							| @@ -0,0 +1,126 @@ | ||||
|  | ||||
| var should = require("should"); | ||||
| var sinon = require("sinon"); | ||||
| var NR_TEST_UTILS = require("nr-test-utils"); | ||||
| var diagnostics = NR_TEST_UTILS.require("@node-red/runtime/lib/api/diagnostics") | ||||
|  | ||||
| var mockLog = () => ({ | ||||
|     log: sinon.stub(), | ||||
|     debug: sinon.stub(), | ||||
|     trace: sinon.stub(), | ||||
|     warn: sinon.stub(), | ||||
|     info: sinon.stub(), | ||||
|     metric: sinon.stub(), | ||||
|     audit: sinon.stub(), | ||||
|     _: function() { return "abc"} | ||||
| }) | ||||
|  | ||||
| describe("runtime-api/diagnostics", function() { | ||||
|  | ||||
|     describe("get", function() { | ||||
|         before(function() { | ||||
|             diagnostics.init({ | ||||
|                 isStarted: () => true, | ||||
|                 nodes: { | ||||
|                     getNodeList: () => [{module:"node-red", version:"9.9.9"},{module:"node-red-node-inject", version:"8.8.8"}] | ||||
|                 }, | ||||
|                 settings: { | ||||
|                     version: "7.7.7", | ||||
|                     available: () => true, | ||||
|                     //apiMaxLength: xxx, deliberately left blank. Should arrive in report as "UNSET" | ||||
|                     debugMaxLength: 1111, | ||||
|                     disableEditor: false, | ||||
|                     flowFile: "flows.json", | ||||
|                     mqttReconnectTime: 321, | ||||
|                     serialReconnectTime: 432, | ||||
|                     adminAuth: {},//should be sanitised to "SET" | ||||
|                     httpAdminRoot: "/admin/root/", | ||||
|                     httpAdminCors: {},//should be sanitised to "SET" | ||||
|                     httpNodeAuth: {},//should be sanitised to "SET" | ||||
|                     httpNodeRoot: "/node/root/", | ||||
|                     httpNodeCors: {},//should be sanitised to "SET" | ||||
|                     httpStatic: "/var/static/",//should be sanitised to "SET" | ||||
|                     httpStaticRoot: "/static/root/", | ||||
|                     httpStaticCors: {},//should be sanitised to "SET" | ||||
|                     uiHost: "something.secret.com",//should be sanitised to "SET" | ||||
|                     uiPort: 1337,//should be sanitised to "SET" | ||||
|                     userDir: "/var/super/secret/",//should be sanitised to "SET", | ||||
|                     contextStorage: { | ||||
|                         default    : { module: "memory" }, | ||||
|                         file: { module: "localfilesystem" }, | ||||
|                         secured: { module: "secure_store", user: "fred", pass: "super-duper-secret" }, | ||||
|                     }, | ||||
|                     editorTheme: {} | ||||
|                 }, | ||||
|                 log: mockLog() | ||||
|             }); | ||||
|         }) | ||||
|         it("returns basic user settings", function() { | ||||
|             return diagnostics.get({scope:"fake_scope"}).then(result => { | ||||
|                 should(result).be.type("object"); | ||||
|  | ||||
|                 //result.xxxxx | ||||
|                 Object.keys(result) | ||||
|                 const reportPropCount = Object.keys(result).length; | ||||
|                 reportPropCount.should.eql(7);//ensure no more than 7 keys are present in the report (avoid leakage of extra info) | ||||
|                 result.should.have.property("report","diagnostics"); | ||||
|                 result.should.have.property("scope","fake_scope"); | ||||
|                 result.should.have.property("time").type("object"); | ||||
|                 result.should.have.property("intl").type("object"); | ||||
|                 result.should.have.property("nodejs").type("object"); | ||||
|                 result.should.have.property("os").type("object"); | ||||
|                 result.should.have.property("runtime").type("object"); | ||||
|  | ||||
|                 //result.runtime.xxxxx | ||||
|                 const runtimeCount = Object.keys(result.runtime).length; | ||||
|                 runtimeCount.should.eql(4);//ensure no more than 4 keys are present in runtime  | ||||
|                 result.runtime.should.have.property('isStarted',true) | ||||
|                 result.runtime.should.have.property('modules').type("object"); | ||||
|                 result.runtime.should.have.property('settings').type("object"); | ||||
|                 result.runtime.should.have.property('version','7.7.7'); | ||||
|  | ||||
|                 //result.runtime.modules.xxxxx | ||||
|                 const moduleCount = Object.keys(result.runtime.modules).length; | ||||
|                 moduleCount.should.eql(2);//ensure no more than the 2 modules specified are present | ||||
|                 result.runtime.modules.should.have.property('node-red','9.9.9'); | ||||
|                 result.runtime.modules.should.have.property('node-red-node-inject','8.8.8'); | ||||
|  | ||||
|                 //result.runtime.settings.xxxxx | ||||
|                 const settingsCount = Object.keys(result.runtime.settings).length; | ||||
|                 settingsCount.should.eql(21);//ensure no more than the 21 settings listed below are present in the settings object | ||||
|                 result.runtime.settings.should.have.property('available',true); | ||||
|                 result.runtime.settings.should.have.property('apiMaxLength', "UNSET");//deliberately disabled to ensure UNSET is returned | ||||
|                 result.runtime.settings.should.have.property('debugMaxLength', 1111); | ||||
|                 result.runtime.settings.should.have.property('disableEditor', false); | ||||
|                 result.runtime.settings.should.have.property('editorTheme', {}); | ||||
|                 result.runtime.settings.should.have.property('flowFile', "flows.json"); | ||||
|                 result.runtime.settings.should.have.property('mqttReconnectTime', 321); | ||||
|                 result.runtime.settings.should.have.property('serialReconnectTime', 432); | ||||
|                 result.runtime.settings.should.have.property("adminAuth", "SET"); //should be sanitised to "SET" | ||||
|                 result.runtime.settings.should.have.property("httpAdminCors", "SET"); //should be sanitised to "SET" | ||||
|                 result.runtime.settings.should.have.property('httpAdminRoot', "/admin/root/"); | ||||
|                 result.runtime.settings.should.have.property("httpNodeAuth", "SET"); //should be sanitised to "SET" | ||||
|                 result.runtime.settings.should.have.property("httpNodeCors", "SET"); //should be sanitised to "SET" | ||||
|                 result.runtime.settings.should.have.property('httpNodeRoot', "/node/root/"); | ||||
|                 result.runtime.settings.should.have.property("httpStatic", "SET"); //should be sanitised to "SET" | ||||
|                 result.runtime.settings.should.have.property('httpStaticRoot', "/static/root/"); | ||||
|                 result.runtime.settings.should.have.property("httpStaticCors", "SET"); //should be sanitised to "SET" | ||||
|                 result.runtime.settings.should.have.property("uiHost", "SET"); //should be sanitised to "SET" | ||||
|                 result.runtime.settings.should.have.property("uiPort", "SET"); //should be sanitised to "SET" | ||||
|                 result.runtime.settings.should.have.property("userDir", "SET"); //should be sanitised to "SET" | ||||
|                 result.runtime.settings.should.have.property('contextStorage').type("object"); | ||||
|  | ||||
|                 //result.runtime.settings.contextStorage.xxxxx | ||||
|                 const contextCount = Object.keys(result.runtime.settings.contextStorage).length; | ||||
|                 contextCount.should.eql(3);//ensure no more than the 3 settings listed below are present in the contextStorage object | ||||
|                 result.runtime.settings.contextStorage.should.have.property('default', {module:"memory"}); | ||||
|                 result.runtime.settings.contextStorage.should.have.property('file', {module:"localfilesystem"}); | ||||
|                 result.runtime.settings.contextStorage.should.have.property('secured', {module:"secure_store"}); //only module should be present, other fields are dropped for security | ||||
|  | ||||
|             }) | ||||
|         }) | ||||
|  | ||||
|     }); | ||||
|  | ||||
|  | ||||
| }); | ||||
		Reference in New Issue
	
	Block a user