diff --git a/packages/node_modules/@node-red/editor-api/lib/editor/index.js b/packages/node_modules/@node-red/editor-api/lib/editor/index.js index 42be1f270..648daa09b 100644 --- a/packages/node_modules/@node-red/editor-api/lib/editor/index.js +++ b/packages/node_modules/@node-red/editor-api/lib/editor/index.js @@ -51,7 +51,7 @@ module.exports = { var ui = require("./ui"); - ui.init(runtimeAPI); + ui.init(settings, runtimeAPI); const editorApp = apiUtil.createExpressApp(settings) diff --git a/packages/node_modules/@node-red/editor-api/lib/editor/ui.js b/packages/node_modules/@node-red/editor-api/lib/editor/ui.js index 998816f5e..e7bf15069 100644 --- a/packages/node_modules/@node-red/editor-api/lib/editor/ui.js +++ b/packages/node_modules/@node-red/editor-api/lib/editor/ui.js @@ -13,6 +13,7 @@ * See the License for the specific language governing permissions and * limitations under the License. **/ +const crypto = require('crypto') var express = require('express'); var fs = require("fs"); var path = require("path"); @@ -24,13 +25,16 @@ var apiUtils = require("../util"); var theme = require("./theme"); var runtimeAPI; +let settings; var editorClientDir = path.dirname(require.resolve("@node-red/editor-client")); var defaultNodeIcon = path.join(editorClientDir,"public","red","images","icons","arrow-in.svg"); var editorTemplatePath = path.join(editorClientDir,"templates","index.mst"); var editorTemplate; +let cacheBuster module.exports = { - init: function(_runtimeAPI) { + init: function(_settings, _runtimeAPI) { + settings = _settings; runtimeAPI = _runtimeAPI; editorTemplate = fs.readFileSync(editorTemplatePath,"utf8"); Mustache.parse(editorTemplate); @@ -91,6 +95,12 @@ module.exports = { }, editor: async function(req,res) { + if (!cacheBuster) { + // settings.instanceId is set asynchronously to the editor-api + // being initiaised. So we defer calculating the cacheBuster hash + // until the first load of the editor + cacheBuster = crypto.createHash('md5').update(`${settings.version || 'version'}-${settings.instanceId || 'instanceId'}`).digest("hex").substring(0,12) + } let sessionMessages; if (req.session && req.session.messages) { @@ -99,6 +109,7 @@ module.exports = { } res.send(Mustache.render(editorTemplate,{ sessionMessages, + cacheBuster, ...await theme.context() })); }, diff --git a/packages/node_modules/@node-red/editor-client/locales/de/editor.json b/packages/node_modules/@node-red/editor-client/locales/de/editor.json index f2955c266..bb811eae4 100644 --- a/packages/node_modules/@node-red/editor-client/locales/de/editor.json +++ b/packages/node_modules/@node-red/editor-client/locales/de/editor.json @@ -109,7 +109,6 @@ "selectionToSubflow": "Auswahl in Subflow umwandeln", "flows": "Flow", "add": "Hinzufügen", - "rename": "Umbenennen", "delete": "Löschen", "keyboardShortcuts": "Tastenkürzel", "login": "Anmelden", diff --git a/packages/node_modules/@node-red/editor-client/locales/en-US/editor.json b/packages/node_modules/@node-red/editor-client/locales/en-US/editor.json index 6babde28d..b09c4393d 100644 --- a/packages/node_modules/@node-red/editor-client/locales/en-US/editor.json +++ b/packages/node_modules/@node-red/editor-client/locales/en-US/editor.json @@ -122,7 +122,6 @@ "selectionToSubflow": "Selection to Subflow", "flows": "Flows", "add": "Add", - "rename": "Rename", "delete": "Delete", "keyboardShortcuts": "Keyboard shortcuts", "login": "Login", @@ -1224,6 +1223,7 @@ "invalid-expr": "Invalid JSONata expression: __error__", "invalid-prop": "Invalid property expression", "invalid-num": "Invalid number", + "invalid-num-prop": "__prop__: invalid number", "invalid-regexp": "Invalid input pattern", "invalid-regex-prop": "__prop__: invalid input pattern", "missing-required-prop": "__prop__: property value missing", diff --git a/packages/node_modules/@node-red/editor-client/locales/fr/editor.json b/packages/node_modules/@node-red/editor-client/locales/fr/editor.json index b58bed283..3503c883f 100644 --- a/packages/node_modules/@node-red/editor-client/locales/fr/editor.json +++ b/packages/node_modules/@node-red/editor-client/locales/fr/editor.json @@ -122,7 +122,6 @@ "selectionToSubflow": "Convertir en sous-flux", "flows": "Flux", "add": "Ajouter", - "rename": "Renommer", "delete": "Supprimer", "keyboardShortcuts": "Raccourcis clavier", "login": "Se connecter", @@ -1218,6 +1217,7 @@ "invalid-expr": "Expression JSONata invalide : __error__", "invalid-prop": "Expression de propriété invalide", "invalid-num": "Numéro invalide", + "invalid-num-prop": "__prop__: numéro invalide", "invalid-regexp": "Modèle d'entrée non valide", "invalid-regex-prop": "__prop__: modèle d'entrée non valide", "missing-required-prop": "__prop__: valeur de la propriété manquante", diff --git a/packages/node_modules/@node-red/editor-client/locales/ja/editor.json b/packages/node_modules/@node-red/editor-client/locales/ja/editor.json index ceb001a10..dfb3c2a04 100644 --- a/packages/node_modules/@node-red/editor-client/locales/ja/editor.json +++ b/packages/node_modules/@node-red/editor-client/locales/ja/editor.json @@ -122,7 +122,6 @@ "selectionToSubflow": "選択部分をサブフロー化", "flows": "フロー", "add": "フローを新規追加", - "rename": "フロー名を変更", "delete": "フローを削除", "keyboardShortcuts": "ショートカットキーの説明", "login": "ログイン", @@ -130,6 +129,11 @@ "editPalette": "パレットの管理", "other": "その他", "showTips": "ヒントを表示", + "showNodeHelp": "ノードのヘルプを表示", + "enableSelectedNodes": "選択したノードを有効化", + "disableSelectedNodes": "選択したノードを無効化", + "showSelectedNodeLabels": "選択したノードのラベル表示", + "hideSelectedNodeLabels": "選択したノードのラベル非表示", "showWelcomeTours": "新バージョンのガイドツアーを表示", "help": "Node-REDウェブサイト", "projects": "プロジェクト", @@ -511,7 +515,7 @@ "selectAllConnected": "接続されたノードを選択", "addRemoveNode": "ノードの選択、選択解除", "editSelected": "選択したノードを編集", - "deleteSelected": "選択したノードや接続を削除", + "deleteSelected": "選択部分を削除", "deleteReconnect": "削除と再接続", "importNode": "フローの読み込み", "exportNode": "フローの書き出し", @@ -1215,8 +1219,10 @@ "validator": { "errors": { "invalid-json": "JSONデータが不正: __error__", + "invalid-expr": "不正なJSONata式: __error__", "invalid-prop": "プロパティ式が不正", "invalid-num": "数値が不正", + "invalid-num-prop": "__prop__: 数値が不正", "invalid-regexp": "入力パターンが不正", "invalid-regex-prop": "__prop__: 入力パターンが不正", "missing-required-prop": "__prop__: プロパティが未設定", @@ -1226,6 +1232,7 @@ } }, "contextMenu": { + "showActionList": "動作一覧を表示", "insert": "挿入", "node": "ノード", "junction": "分岐点", diff --git a/packages/node_modules/@node-red/editor-client/locales/ko/editor.json b/packages/node_modules/@node-red/editor-client/locales/ko/editor.json index ad4f4354f..4de2cb5d2 100644 --- a/packages/node_modules/@node-red/editor-client/locales/ko/editor.json +++ b/packages/node_modules/@node-red/editor-client/locales/ko/editor.json @@ -79,7 +79,6 @@ "selectionToSubflow": "서브 플로우 선택", "flows": "플로우", "add": "추가", - "rename": "이름변경", "delete": "삭제", "keyboardShortcuts": "단축키", "login": "로그인", diff --git a/packages/node_modules/@node-red/editor-client/locales/pt-BR/editor.json b/packages/node_modules/@node-red/editor-client/locales/pt-BR/editor.json index f65ec62e9..8b5367f29 100644 --- a/packages/node_modules/@node-red/editor-client/locales/pt-BR/editor.json +++ b/packages/node_modules/@node-red/editor-client/locales/pt-BR/editor.json @@ -109,7 +109,6 @@ "selectionToSubflow": "Seleção para subfluxo", "flows": "Fluxos", "add": "Adicionar", - "rename": "Renomear", "delete": "Apagar", "keyboardShortcuts": "Atalhos do teclado", "login": "Ingressar", @@ -1188,6 +1187,7 @@ "invalid-json": "Dados JSON inválidos: __error__", "invalid-prop": "Expressão de propriedade inválida", "invalid-num": "Número inválido", + "invalid-num-prop": "__prop__: número inválido", "invalid-regexp": "Padrão de entrada inválido", "invalid-regex-prop": "__prop__: Padrão de entrada inválido", "missing-required-prop": "__prop__: valor de propriedade ausente", diff --git a/packages/node_modules/@node-red/editor-client/locales/ru/editor.json b/packages/node_modules/@node-red/editor-client/locales/ru/editor.json index 8cfea1bde..69562f806 100644 --- a/packages/node_modules/@node-red/editor-client/locales/ru/editor.json +++ b/packages/node_modules/@node-red/editor-client/locales/ru/editor.json @@ -95,7 +95,6 @@ "selectionToSubflow": "Выделение в подпоток", "flows": "Потоки", "add": "Добавить", - "rename": "Переименовать", "delete": "Удалить", "keyboardShortcuts": "Сочетания клавиш", "login": "Войти", diff --git a/packages/node_modules/@node-red/editor-client/locales/zh-CN/editor.json b/packages/node_modules/@node-red/editor-client/locales/zh-CN/editor.json index 271326d05..51efee65e 100644 --- a/packages/node_modules/@node-red/editor-client/locales/zh-CN/editor.json +++ b/packages/node_modules/@node-red/editor-client/locales/zh-CN/editor.json @@ -120,7 +120,6 @@ "selectionToSubflow": "将选择部分更改为子流程", "flows": "流程", "add": "增加", - "rename": "重命名", "delete": "删除", "keyboardShortcuts": "键盘快捷方式", "login": "登录", @@ -1221,6 +1220,7 @@ "invalid-expr": "无效的 JSONata 表达式: __error__", "invalid-prop": "无效的属性表达式", "invalid-num": "无效的数字", + "invalid-num-prop": "__prop__: 无效的数字", "invalid-regexp": "输入格式无效", "invalid-regex-prop": "__prop__: 输入格式无效", "missing-required-prop": "__prop__: 缺少属性值", diff --git a/packages/node_modules/@node-red/editor-client/locales/zh-TW/editor.json b/packages/node_modules/@node-red/editor-client/locales/zh-TW/editor.json index 022205a70..485ce3c2f 100644 --- a/packages/node_modules/@node-red/editor-client/locales/zh-TW/editor.json +++ b/packages/node_modules/@node-red/editor-client/locales/zh-TW/editor.json @@ -120,7 +120,6 @@ "selectionToSubflow": "將選擇部分更改為子流程", "flows": "流程", "add": "增加", - "rename": "重新命名", "delete": "刪除", "keyboardShortcuts": "鍵盤快速鍵", "login": "登入", diff --git a/packages/node_modules/@node-red/editor-client/src/js/events.js b/packages/node_modules/@node-red/editor-client/src/js/events.js index bd2abd8d0..943854393 100644 --- a/packages/node_modules/@node-red/editor-client/src/js/events.js +++ b/packages/node_modules/@node-red/editor-client/src/js/events.js @@ -39,15 +39,16 @@ console.warn(evt,args); } if (handlers[evt]) { - for (var i=0;i n.type !== 'group').length === 0 - const hasGroup = hasSelection && selection.nodes.filter(n => n.type === 'group' ).length > 0 + let hasGroup, isAllGroups = true, hasDisabledNode, hasEnabledNode, hasLabeledNode, hasUnlabeledNode; + if (hasSelection) { + selection.nodes.forEach(n => { + if (n.type === 'group') { + hasGroup = true; + } else { + isAllGroups = false; + } + if (n.d) { + hasDisabledNode = true; + } else { + hasEnabledNode = true; + } + if (n.l === undefined || n.l) { + hasLabeledNode = true; + } else { + hasUnlabeledNode = true; + } + }); + } const offset = $("#red-ui-workspace-chart").offset() let addX = options.x - offset.left + $("#red-ui-workspace-chart").scrollLeft() @@ -55,7 +73,7 @@ RED.contextMenu = (function () { onselect: function () { RED.view.showQuickAddDialog({ position: [addX, addY], - touchTrigger: true, + touchTrigger: 'ontouchstart' in window, splice: isSingleLink ? selection.links[0] : undefined, // spliceMultiple: isMultipleLinks }) @@ -113,11 +131,11 @@ RED.contextMenu = (function () { ) } nodeOptions.push( - { onselect: 'core:enable-selected-nodes', label: RED._('menu.label.enableSelectedNodes') }, - { onselect: 'core:disable-selected-nodes', label: RED._('menu.label.disableSelectedNodes') }, + { onselect: 'core:enable-selected-nodes', label: RED._('menu.label.enableSelectedNodes'), disabled: !hasDisabledNode }, + { onselect: 'core:disable-selected-nodes', label: RED._('menu.label.disableSelectedNodes'), disabled: !hasEnabledNode }, null, - { onselect: 'core:show-selected-node-labels', label: RED._('menu.label.showSelectedNodeLabels') }, - { onselect: 'core:hide-selected-node-labels', label: RED._('menu.label.hideSelectedNodeLabels') } + { onselect: 'core:show-selected-node-labels', label: RED._('menu.label.showSelectedNodeLabels'), disabled: !hasUnlabeledNode }, + { onselect: 'core:hide-selected-node-labels', label: RED._('menu.label.hideSelectedNodeLabels'), disabled: !hasLabeledNode } ) menuItems.push({ label: RED._('sidebar.info.node'), diff --git a/packages/node_modules/@node-red/editor-client/src/js/ui/editor.js b/packages/node_modules/@node-red/editor-client/src/js/ui/editor.js index 4908373c7..11fdac279 100644 --- a/packages/node_modules/@node-red/editor-client/src/js/ui/editor.js +++ b/packages/node_modules/@node-red/editor-client/src/js/ui/editor.js @@ -1231,7 +1231,11 @@ RED.editor = (function() { }) if (node_def.hasUsers !== false) { - $(' ').css("margin-left", "10px").appendTo(trayFooterLeft); + // $(' ').css("margin-left", "10px").appendTo(trayFooterLeft); + $('').on('click', function() { + RED.sidebar.info.outliner.search('uses:'+editing_config_node.id) + RED.sidebar.info.show() + }).appendTo(trayFooterLeft); } trayFooter.append(''); @@ -1289,7 +1293,8 @@ RED.editor = (function() { }); } if (node_def.hasUsers !== false) { - $("#red-ui-editor-config-user-count").text(RED._("editor.nodesUse", {count:editing_config_node.users.length})).parent().show(); + $("#red-ui-editor-config-user-count").text(editing_config_node.users.length).parent().show(); + RED.popover.tooltip($("#red-ui-editor-config-user-count").parent(), function() { return RED._('editor.nodesUse',{count:editing_config_node.users.length})}); } trayBody.i18n(); trayFooter.i18n(); diff --git a/packages/node_modules/@node-red/editor-client/src/js/ui/env-var.js b/packages/node_modules/@node-red/editor-client/src/js/ui/env-var.js index 998484858..79c626af4 100644 --- a/packages/node_modules/@node-red/editor-client/src/js/ui/env-var.js +++ b/packages/node_modules/@node-red/editor-client/src/js/ui/env-var.js @@ -71,7 +71,7 @@ RED.envVar = (function() { }; if (item.name.trim() !== "") { new_env.push(item); - if ((item.type === "cred") && (item.value !== "__PWRD__")) { + if (item.type === "cred") { credentials.map[item.name] = item.value; credentials.map["has_"+item.name] = (item.value !== ""); item.value = "__PWRD__"; diff --git a/packages/node_modules/@node-red/editor-client/src/js/ui/palette.js b/packages/node_modules/@node-red/editor-client/src/js/ui/palette.js index db915fd8b..23f30fc61 100644 --- a/packages/node_modules/@node-red/editor-client/src/js/ui/palette.js +++ b/packages/node_modules/@node-red/editor-client/src/js/ui/palette.js @@ -484,7 +484,7 @@ RED.palette = (function() { var currentLabel = paletteNode.attr("data-palette-label"); var currentInfo = paletteNode.attr("data-palette-info"); - if (currentLabel !== sf.name || currentInfo !== sf.info) { + if (currentLabel !== sf.name || currentInfo !== sf.info || sf.in.length > 0 || sf.out.length > 0) { paletteNode.attr("data-palette-info",sf.info); setLabel(sf.type+":"+sf.id,paletteNode,sf.name,RED.utils.renderMarkdown(sf.info||"")); } diff --git a/packages/node_modules/@node-red/editor-client/src/js/ui/tab-config.js b/packages/node_modules/@node-red/editor-client/src/js/ui/tab-config.js index 90ecf6093..e2c8185cb 100644 --- a/packages/node_modules/@node-red/editor-client/src/js/ui/tab-config.js +++ b/packages/node_modules/@node-red/editor-client/src/js/ui/tab-config.js @@ -158,6 +158,7 @@ RED.sidebar.config = (function() { entry.data('node',node.id); nodeDiv.data('node',node.id); var label = $('
').text(labelText).appendTo(nodeDiv); + if (node.d) { nodeDiv.addClass("red-ui-palette-node-config-disabled"); $('').prependTo(label); @@ -179,6 +180,20 @@ RED.sidebar.config = (function() { nodeDiv.addClass("red-ui-palette-node-config-unused"); } } + + if (!node.valid) { + nodeDiv.addClass("red-ui-palette-node-config-invalid") + const nodeDivAnnotations = $('').appendTo(nodeDiv) + const errorBadge = document.createElementNS("http://www.w3.org/2000/svg","path"); + errorBadge.setAttribute("d","M 0,9 l 10,0 -5,-8 z"); + nodeDivAnnotations.append($(errorBadge)) + RED.popover.tooltip(nodeDivAnnotations, function () { + if (node.validationErrors && node.validationErrors.length > 0) { + return RED._("editor.errors.invalidProperties")+"
- "+node.validationErrors.join("
- ") + } + }) + } + nodeDiv.on('click',function(e) { e.stopPropagation(); RED.view.select(false); diff --git a/packages/node_modules/@node-red/editor-client/src/js/ui/tab-context.js b/packages/node_modules/@node-red/editor-client/src/js/ui/tab-context.js index 0d8ba103f..6cb034ccc 100644 --- a/packages/node_modules/@node-red/editor-client/src/js/ui/tab-context.js +++ b/packages/node_modules/@node-red/editor-client/src/js/ui/tab-context.js @@ -232,7 +232,7 @@ RED.sidebar.context = (function() { typeHint: data.format, sourceId: id+"."+k, tools: tools, - path: "" + path: k }).appendTo(propRow.children()[1]); } }) @@ -278,7 +278,7 @@ RED.sidebar.context = (function() { typeHint: data.format, sourceId: id+"."+k, tools: tools, - path: "" + path: k }).appendTo(propRow.children()[1]); } }); @@ -299,7 +299,7 @@ RED.sidebar.context = (function() { typeHint: v.format, sourceId: id+"."+k, tools: tools, - path: "" + path: k }).appendTo(propRow.children()[1]); if (contextStores.length > 1) { $("",{class:"red-ui-sidebar-context-property-storename"}).text(v.store).appendTo($(propRow.children()[0])) diff --git a/packages/node_modules/@node-red/editor-client/src/js/ui/typeSearch.js b/packages/node_modules/@node-red/editor-client/src/js/ui/typeSearch.js index 872169828..3d05fd1c8 100644 --- a/packages/node_modules/@node-red/editor-client/src/js/ui/typeSearch.js +++ b/packages/node_modules/@node-red/editor-client/src/js/ui/typeSearch.js @@ -186,8 +186,15 @@ RED.typeSearch = (function() { var iconContainer = $('
',{class:"red-ui-palette-icon-container"}).appendTo(nodeDiv); RED.utils.createIconElement(icon_url, iconContainer, false); - - if (!/^_action_:/.test(object.type) && object.type !== "junction") { + if (/^subflow:/.test(object.type)) { + var sf = RED.nodes.subflow(object.type.substring(8)); + if (sf.in.length > 0) { + $('
',{class:"red-ui-search-result-node-port"}).appendTo(nodeDiv); + } + if (sf.out.length > 0) { + $('
',{class:"red-ui-search-result-node-port red-ui-search-result-node-output"}).appendTo(nodeDiv); + } + } else if (!/^_action_:/.test(object.type) && object.type !== "junction") { if (def.inputs > 0) { $('
',{class:"red-ui-search-result-node-port"}).appendTo(nodeDiv); } diff --git a/packages/node_modules/@node-red/editor-client/src/js/ui/view.js b/packages/node_modules/@node-red/editor-client/src/js/ui/view.js index 56a29e421..fa20f3f61 100644 --- a/packages/node_modules/@node-red/editor-client/src/js/ui/view.js +++ b/packages/node_modules/@node-red/editor-client/src/js/ui/view.js @@ -4155,10 +4155,15 @@ RED.view = (function() { scaleFactor = 30/largestEdge; } var width = img.width * scaleFactor; + if (width > 20) { + scalefactor *= 20/width; + width = 20; + } var height = img.height * scaleFactor; icon.attr("width",width); icon.attr("height",height); icon.attr("x",15-width/2); + icon.attr("y",(30-height)/2); } icon.attr("xlink:href",iconUrl); icon.style("display",null); diff --git a/packages/node_modules/@node-red/editor-client/src/sass/tab-config.scss b/packages/node_modules/@node-red/editor-client/src/sass/tab-config.scss index c1f151ca6..9d678daa1 100644 --- a/packages/node_modules/@node-red/editor-client/src/sass/tab-config.scss +++ b/packages/node_modules/@node-red/editor-client/src/sass/tab-config.scss @@ -36,7 +36,7 @@ ul.red-ui-sidebar-node-config-list { text-align: center; } .red-ui-palette-node { - overflow: hidden; + // overflow: hidden; cursor: default; &.selected { border-color: transparent; @@ -113,6 +113,15 @@ ul.red-ui-sidebar-node-config-list li.red-ui-palette-node-config-type { margin-right: 5px; } } +.red-ui-palette-node-config-invalid { + border-color: var(--red-ui-form-input-border-error-color) +} +.red-ui-palette-node-annotations { + position: absolute; + left: calc(100% - 15px); + top: -8px; + display: block; +} .red-ui-sidebar-node-config-filter-info { position: absolute; top: 0; diff --git a/packages/node_modules/@node-red/editor-client/templates/index.mst b/packages/node_modules/@node-red/editor-client/templates/index.mst index 01bf06545..cc144c35f 100644 --- a/packages/node_modules/@node-red/editor-client/templates/index.mst +++ b/packages/node_modules/@node-red/editor-client/templates/index.mst @@ -22,26 +22,26 @@ limitations under the License. --> {{ page.title }} - - - - - + + + + + {{#page.css}} {{/page.css}} {{#asset.vendorMonaco}} - + {{/asset.vendorMonaco}}
- + {{#asset.vendorMonaco}} - + {{/asset.vendorMonaco}} - - + + {{# page.scripts }} {{/ page.scripts }} diff --git a/packages/node_modules/@node-red/nodes/core/network/21-httprequest.js b/packages/node_modules/@node-red/nodes/core/network/21-httprequest.js index fbdf284fc..62b4d396b 100644 --- a/packages/node_modules/@node-red/nodes/core/network/21-httprequest.js +++ b/packages/node_modules/@node-red/nodes/core/network/21-httprequest.js @@ -141,15 +141,7 @@ in your Node-RED user directory (${RED.settings.userDir}). }); } } - /** - * @param {Object} headersObject - * @param {string} name - * @return {any} value - */ - const getHeaderValue = (headersObject, name) => { - const asLowercase = name.toLowercase(); - return headersObject[Object.keys(headersObject).find(k => k.toLowerCase() === asLowercase)]; - } + this.on("input",function(msg,nodeSend,nodeDone) { checkNodeAgentPatch(); //reset redirectList on each request @@ -300,7 +292,7 @@ in your Node-RED user directory (${RED.settings.userDir}). } opts.headers = {}; - //add msg.headers + //add msg.headers //NOTE: ui headers will take precidence over msg.headers if (msg.headers) { if (msg.headers.hasOwnProperty('x-node-red-request-node')) { @@ -633,7 +625,7 @@ in your Node-RED user directory (${RED.settings.userDir}). msg.payload = msg.payload.toString('utf8'); // txt if (node.ret === "obj") { - if (msg.statusCode == 204){msg.payload= "{}"}; + if (msg.statusCode == 204){msg.payload= "{}"}; try { msg.payload = JSON.parse(msg.payload); } // obj catch(e) { node.warn(RED._("httpin.errors.json-error")); } } @@ -740,7 +732,7 @@ in your Node-RED user directory (${RED.settings.userDir}). * * If the algorithm directive's value ends with "-sess", then HA1 is * HA1=digestCompute(digestCompute(username:realm:password):nonce:cnonce) - * + * * If the algorithm directive's value does not end with "-sess", then HA1 is * HA1=digestCompute(username:realm:password) */ diff --git a/packages/node_modules/@node-red/runtime/lib/flows/Flow.js b/packages/node_modules/@node-red/runtime/lib/flows/Flow.js index 2833341c6..b541a9d95 100644 --- a/packages/node_modules/@node-red/runtime/lib/flows/Flow.js +++ b/packages/node_modules/@node-red/runtime/lib/flows/Flow.js @@ -485,7 +485,7 @@ class Flow { } if (!key.startsWith("$parent.")) { if (this._env.hasOwnProperty(key)) { - return this._env[key] + return (Object.hasOwn(this._env[key], 'value') && this._env[key].__clone__) ? clone(this._env[key].value) : this._env[key] } } else { key = key.substring(8); diff --git a/packages/node_modules/@node-red/runtime/lib/flows/Group.js b/packages/node_modules/@node-red/runtime/lib/flows/Group.js index dc05211a1..521b6ceda 100644 --- a/packages/node_modules/@node-red/runtime/lib/flows/Group.js +++ b/packages/node_modules/@node-red/runtime/lib/flows/Group.js @@ -41,7 +41,7 @@ class Group { } if (!key.startsWith("$parent.")) { if (this._env.hasOwnProperty(key)) { - return this._env[key] + return (Object.hasOwn(this._env[key], 'value') && this._env[key].__clone__) ? clone(this._env[key].value) : this._env[key] } } else { key = key.substring(8); diff --git a/packages/node_modules/@node-red/runtime/lib/flows/Subflow.js b/packages/node_modules/@node-red/runtime/lib/flows/Subflow.js index 15d8d6c37..031e75c36 100644 --- a/packages/node_modules/@node-red/runtime/lib/flows/Subflow.js +++ b/packages/node_modules/@node-red/runtime/lib/flows/Subflow.js @@ -375,7 +375,7 @@ class Subflow extends Flow { } if (!key.startsWith("$parent.")) { if (this._env.hasOwnProperty(key)) { - return this._env[key] + return (Object.hasOwn(this._env[key], 'value') && this._env[key].__clone__) ? clone(this._env[key].value) : this._env[key] } } else { key = key.substring(8); diff --git a/packages/node_modules/@node-red/runtime/lib/flows/util.js b/packages/node_modules/@node-red/runtime/lib/flows/util.js index 2559fd1da..76dbe2223 100644 --- a/packages/node_modules/@node-red/runtime/lib/flows/util.js +++ b/packages/node_modules/@node-red/runtime/lib/flows/util.js @@ -102,6 +102,9 @@ async function evaluateEnvProperties(flow, env, credentials) { pendingEvaluations.push(new Promise((resolve, _) => { redUtil.evaluateNodeProperty(value, 'jsonata', {_flow: flow}, null, (err, result) => { if (!err) { + if (typeof result === 'object') { + result = { value: result, __clone__: true} + } evaluatedEnv[name] = result } resolve() @@ -109,6 +112,9 @@ async function evaluateEnvProperties(flow, env, credentials) { })) } else { value = redUtil.evaluateNodeProperty(value, type, {_flow: flow}, null, null); + if (typeof value === 'object') { + value = { value: value, __clone__: true} + } } evaluatedEnv[name] = value } @@ -138,8 +144,13 @@ async function evaluateEnvProperties(flow, env, credentials) { } }}, null, null); } + if (typeof value === 'object' && !value.__clone__) { + value = { value: value, __clone__: true} + } evaluatedEnv[name] = value + } + // console.log(evaluatedEnv) return evaluatedEnv } diff --git a/packages/node_modules/@node-red/runtime/lib/index.js b/packages/node_modules/@node-red/runtime/lib/index.js index 74f03c55c..39b2025c5 100644 --- a/packages/node_modules/@node-red/runtime/lib/index.js +++ b/packages/node_modules/@node-red/runtime/lib/index.js @@ -27,6 +27,7 @@ var express = require("express"); var path = require('path'); var fs = require("fs"); var os = require("os"); +const crypto = require("crypto") const {log,i18n,events,exec,util,hooks} = require("@node-red/util"); @@ -51,7 +52,7 @@ var adminApi = { var nodeApp; var adminApp; var server; - +let userSettings; /** * Initialise the runtime module. @@ -61,8 +62,9 @@ var server; * better abstracted. * @memberof @node-red/runtime */ -function init(userSettings,httpServer,_adminApi) { +function init(_userSettings,httpServer,_adminApi) { server = httpServer; + userSettings = _userSettings if (server && server.on) { // Add a listener to the upgrade event so that we can properly timeout connection @@ -134,7 +136,12 @@ function start() { .then(function() { return settings.load(storage)}) .then(function() { return library.init(runtime)}) .then(function() { - + if (settings.available()) { + if (settings.get('instanceId') === undefined) { + settings.set('instanceId', crypto.randomBytes(8).toString('hex')) + } + userSettings.instanceId = settings.get('instanceId') || '' + } if (log.metric()) { runtimeMetricInterval = setInterval(function() { reportMetrics(); diff --git a/packages/node_modules/@node-red/runtime/lib/nodes/credentials.js b/packages/node_modules/@node-red/runtime/lib/nodes/credentials.js index 73567f7c4..856e5561b 100644 --- a/packages/node_modules/@node-red/runtime/lib/nodes/credentials.js +++ b/packages/node_modules/@node-red/runtime/lib/nodes/credentials.js @@ -384,10 +384,27 @@ var api = module.exports = { } } } else if (nodeType === "global-config") { - if (JSON.stringify(savedCredentials.map) !== JSON.stringify(newCreds.map)) { - savedCredentials.map = newCreds.map; - dirty = true; - } + const existingCredentialKeys = Object.keys(savedCredentials?.map || []) + const newCredentialKeys = Object.keys(newCreds?.map || []) + existingCredentialKeys.forEach(key => { + if (!newCreds.map?.[key]) { + // This key doesn't exist in the new credentials list - remove + delete savedCredentials.map[key] + delete savedCredentials.map[`has_${key}`] + dirty = true + } + }) + newCredentialKeys.forEach(key => { + if (!/^has_/.test(key)) { + if (!savedCredentials.map?.[key] || newCreds.map[key] !== '__PWRD__') { + // This key either doesn't exist in current saved, or the + // value has been changed + savedCredentials.map[key] = newCreds.map[key] + savedCredentials.map[`has_${key}`] = newCreds.map[`has_${key}`] + dirty = true + } + } + }) } else { var dashedType = nodeType.replace(/\s+/g, '-'); var definition = credentialsDef[dashedType]; diff --git a/test/unit/@node-red/editor-api/lib/editor/ui_spec.js b/test/unit/@node-red/editor-api/lib/editor/ui_spec.js index 0380adcde..beb562650 100644 --- a/test/unit/@node-red/editor-api/lib/editor/ui_spec.js +++ b/test/unit/@node-red/editor-api/lib/editor/ui_spec.js @@ -29,7 +29,7 @@ describe("api/editor/ui", function() { var app; before(function() { - ui.init({ + ui.init({}, { nodes: { getIcon: function(opts) { return new Promise(function(resolve,reject) {