mirror of
https://github.com/node-red/node-red.git
synced 2025-03-01 10:36:34 +00:00
Compare commits
49 Commits
Author | SHA1 | Date | |
---|---|---|---|
|
ade4679e8c | ||
|
410b938442 | ||
|
19dcc3a683 | ||
|
20d067c1ea | ||
|
9526566799 | ||
|
0b9dd82c91 | ||
|
19213434f9 | ||
|
014691346a | ||
|
6738b95c29 | ||
|
6a8230ec1e | ||
|
5679d264b6 | ||
|
37265cf4ef | ||
|
8a63275989 | ||
|
7fc64a84e8 | ||
|
02f7cdd5aa | ||
|
d7dcceef60 | ||
|
ae5e1570ae | ||
|
3ca045394a | ||
|
179032cd4d | ||
|
6a6f0d04d6 | ||
|
add4d9758c | ||
|
a0d3ea62b2 | ||
|
7447e88a50 | ||
|
a193b79d3d | ||
|
da380f7464 | ||
|
269cf02c0b | ||
|
fb50e2772a | ||
|
058c97138a | ||
|
828ae29aed | ||
|
6a0f45140c | ||
|
50a267528d | ||
|
220786be60 | ||
|
fa78bb3d78 | ||
|
9a32ebd0c0 | ||
|
4643f5e8cc | ||
|
7de0984d6d | ||
|
635334f096 | ||
|
f0d0990b5a | ||
|
43b3589451 | ||
|
016a19ba7c | ||
|
aeb79bce2a | ||
|
0ab9b9a5fd | ||
|
56e58521bd | ||
|
b10ef4c98c | ||
|
3ff038fb98 | ||
|
adb498af24 | ||
|
fc67a2efc2 | ||
|
55771c7241 | ||
|
109fa5f04e |
39
CHANGELOG.md
39
CHANGELOG.md
@@ -1,3 +1,42 @@
|
||||
#### 3.1.8: Maintenance Release
|
||||
|
||||
- Add validation and error handling on subflow instance properties (#4632) @knolleary
|
||||
- Hide import/export context menu if disabled in theme (#4633) @knolleary
|
||||
- Show change indicator on subflow tabs (#4631) @knolleary
|
||||
- Bump dependencies (#4630) @knolleary
|
||||
- Reset workspace index when clearing nodes (#4619) @knolleary
|
||||
- Remove typo in global config (#4613) @kazuhitoyokoi
|
||||
|
||||
#### 3.1.7: Maintenance Release
|
||||
|
||||
- Add Japanese translation for v3.1.6 (#4603) @kazuhitoyokoi
|
||||
- Update jsonata version (#4593) @hardillb
|
||||
|
||||
#### 3.1.6: Maintenance Release
|
||||
|
||||
Editor
|
||||
|
||||
- Do not flag env var in num typedInput as error (#4582) @knolleary
|
||||
- Fix example flow name in import dialog (#4578) @kazuhitoyokoi
|
||||
- Fix missing node icons in workspace (#4570) @knolleary
|
||||
|
||||
Runtime
|
||||
|
||||
- Handle undefined env vars (#4581) @knolleary
|
||||
- fix: Removed offending MD5 crypto hash and replaced with SHA1 and SHA256 … (#4568) @JaysonHurst
|
||||
- chore: remove never use import code (#4580) @giscafer
|
||||
|
||||
Nodes
|
||||
|
||||
- fix: template node zh-CN translation (#4575) @giscafer
|
||||
|
||||
#### 3.1.5: Maintenance Release
|
||||
|
||||
Runtime
|
||||
|
||||
- Fix require of dns module (#4562) @knolleary
|
||||
- Ensure global creds object is initialised when adding first cred (#4561) @knolleary
|
||||
|
||||
#### 3.1.4: Maintenance Release
|
||||
|
||||
Editor
|
||||
|
10
package.json
10
package.json
@@ -1,6 +1,6 @@
|
||||
{
|
||||
"name": "node-red",
|
||||
"version": "3.1.4",
|
||||
"version": "3.1.8",
|
||||
"description": "Low-code programming for event-driven applications",
|
||||
"homepage": "https://nodered.org",
|
||||
"license": "Apache-2.0",
|
||||
@@ -41,7 +41,7 @@
|
||||
"cors": "2.8.5",
|
||||
"cronosjs": "1.7.1",
|
||||
"denque": "2.1.0",
|
||||
"express": "4.18.2",
|
||||
"express": "4.19.2",
|
||||
"express-session": "1.17.3",
|
||||
"form-data": "4.0.0",
|
||||
"fs-extra": "11.1.1",
|
||||
@@ -54,7 +54,7 @@
|
||||
"is-utf8": "0.2.1",
|
||||
"js-yaml": "4.1.0",
|
||||
"json-stringify-safe": "5.0.1",
|
||||
"jsonata": "1.8.6",
|
||||
"jsonata": "1.8.7",
|
||||
"lodash.clonedeep": "^4.5.0",
|
||||
"media-typer": "1.1.0",
|
||||
"memorystore": "1.6.7",
|
||||
@@ -64,7 +64,7 @@
|
||||
"mqtt": "4.3.7",
|
||||
"multer": "1.4.5-lts.1",
|
||||
"mustache": "4.2.0",
|
||||
"node-red-admin": "^3.1.2",
|
||||
"node-red-admin": "^3.1.3",
|
||||
"node-watch": "0.7.4",
|
||||
"nopt": "5.0.0",
|
||||
"oauth2orize": "1.11.1",
|
||||
@@ -112,7 +112,7 @@
|
||||
"mermaid": "^10.4.0",
|
||||
"minami": "1.2.3",
|
||||
"mocha": "9.2.2",
|
||||
"node-red-node-test-helper": "^0.3.2",
|
||||
"node-red-node-test-helper": "^0.3.3",
|
||||
"nodemon": "2.0.20",
|
||||
"proxy": "^1.0.2",
|
||||
"sass": "1.62.1",
|
||||
|
@@ -13,7 +13,6 @@
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
**/
|
||||
var apiUtils = require("../util");
|
||||
var runtimeAPI;
|
||||
var settings;
|
||||
var theme = require("../editor/theme");
|
||||
|
@@ -18,7 +18,6 @@ var BearerStrategy = require('passport-http-bearer').Strategy;
|
||||
var ClientPasswordStrategy = require('passport-oauth2-client-password').Strategy;
|
||||
|
||||
var passport = require("passport");
|
||||
var crypto = require("crypto");
|
||||
var util = require("util");
|
||||
|
||||
var Tokens = require("./tokens");
|
||||
|
@@ -14,11 +14,9 @@
|
||||
* limitations under the License.
|
||||
**/
|
||||
|
||||
var express = require("express");
|
||||
var path = require('path');
|
||||
|
||||
var comms = require("./comms");
|
||||
var library = require("./library");
|
||||
var info = require("./settings");
|
||||
|
||||
var auth = require("../auth");
|
||||
|
@@ -15,8 +15,6 @@
|
||||
**/
|
||||
|
||||
var apiUtils = require("../util");
|
||||
var fs = require('fs');
|
||||
var fspath = require('path');
|
||||
|
||||
var runtimeAPI;
|
||||
|
||||
|
@@ -13,9 +13,6 @@
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
**/
|
||||
var fs = require('fs');
|
||||
var path = require('path');
|
||||
// var apiUtil = require('../util');
|
||||
|
||||
var i18n = require("@node-red/util").i18n; // TODO: separate module
|
||||
|
||||
|
@@ -15,7 +15,6 @@
|
||||
**/
|
||||
|
||||
var apiUtils = require("../util");
|
||||
var express = require("express");
|
||||
var runtimeAPI;
|
||||
var settings;
|
||||
|
||||
|
@@ -14,7 +14,6 @@
|
||||
* limitations under the License.
|
||||
**/
|
||||
|
||||
var express = require("express");
|
||||
var util = require("util");
|
||||
var path = require("path");
|
||||
var fs = require("fs");
|
||||
|
@@ -99,7 +99,7 @@ module.exports = {
|
||||
// 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)
|
||||
cacheBuster = crypto.createHash('sha1').update(`${settings.version || 'version'}-${settings.instanceId || 'instanceId'}`).digest("hex").substring(0,12)
|
||||
}
|
||||
|
||||
let sessionMessages;
|
||||
|
@@ -24,11 +24,8 @@
|
||||
* @namespace @node-red/editor-api
|
||||
*/
|
||||
|
||||
var express = require("express");
|
||||
var bodyParser = require("body-parser");
|
||||
var util = require('util');
|
||||
var passport = require('passport');
|
||||
var cors = require('cors');
|
||||
|
||||
var auth = require("./auth");
|
||||
var apiUtil = require("./util");
|
||||
|
@@ -1,6 +1,6 @@
|
||||
{
|
||||
"name": "@node-red/editor-api",
|
||||
"version": "3.1.4",
|
||||
"version": "3.1.8",
|
||||
"license": "Apache-2.0",
|
||||
"main": "./lib/index.js",
|
||||
"repository": {
|
||||
@@ -16,14 +16,14 @@
|
||||
}
|
||||
],
|
||||
"dependencies": {
|
||||
"@node-red/util": "3.1.4",
|
||||
"@node-red/editor-client": "3.1.4",
|
||||
"@node-red/util": "3.1.8",
|
||||
"@node-red/editor-client": "3.1.8",
|
||||
"bcryptjs": "2.4.3",
|
||||
"body-parser": "1.20.2",
|
||||
"clone": "2.1.2",
|
||||
"cors": "2.8.5",
|
||||
"express-session": "1.17.3",
|
||||
"express": "4.18.2",
|
||||
"express": "4.19.2",
|
||||
"memorystore": "1.6.7",
|
||||
"mime": "3.0.0",
|
||||
"multer": "1.4.5-lts.1",
|
||||
|
@@ -303,7 +303,8 @@
|
||||
"missingType": "不正なフロー - __index__ 番目の要素に'type'プロパティがありません"
|
||||
},
|
||||
"conflictNotification1": "読み込もうとしているノードのいくつかは、既にワークスペース内に存在しています。",
|
||||
"conflictNotification2": "読み込むノードを選択し、また既存のノードを置き換えるか、もしくはそれらのコピーを読み込むかも選択してください。"
|
||||
"conflictNotification2": "読み込むノードを選択し、また既存のノードを置き換えるか、もしくはそれらのコピーを読み込むかも選択してください。",
|
||||
"alreadyExists": "本ノードは既に存在"
|
||||
},
|
||||
"copyMessagePath": "パスをコピーしました",
|
||||
"copyMessageValue": "値をコピーしました",
|
||||
|
@@ -1,6 +1,6 @@
|
||||
{
|
||||
"name": "@node-red/editor-client",
|
||||
"version": "3.1.4",
|
||||
"version": "3.1.8",
|
||||
"license": "Apache-2.0",
|
||||
"repository": {
|
||||
"type": "git",
|
||||
|
@@ -547,12 +547,16 @@ RED.nodes = (function() {
|
||||
* @param {String} z tab id
|
||||
*/
|
||||
checkTabState: function (z) {
|
||||
const ws = workspaces[z]
|
||||
const ws = workspaces[z] || subflows[z]
|
||||
if (ws) {
|
||||
const contentsChanged = tabDirtyMap[z].size > 0 || tabDeletedNodesMap[z].size > 0
|
||||
if (Boolean(ws.contentsChanged) !== contentsChanged) {
|
||||
ws.contentsChanged = contentsChanged
|
||||
RED.events.emit("flows:change", ws);
|
||||
if (ws.type === 'tab') {
|
||||
RED.events.emit("flows:change", ws);
|
||||
} else {
|
||||
RED.events.emit("subflows:change", ws);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -1025,7 +1029,22 @@ RED.nodes = (function() {
|
||||
RED.nodes.registerType("subflow:"+sf.id, {
|
||||
defaults:{
|
||||
name:{value:""},
|
||||
env:{value:[]}
|
||||
env:{value:[], validate: function(value) {
|
||||
const errors = []
|
||||
if (value) {
|
||||
value.forEach(env => {
|
||||
const r = RED.utils.validateTypedProperty(env.value, env.type)
|
||||
if (r !== true) {
|
||||
errors.push(env.name+': '+r)
|
||||
}
|
||||
})
|
||||
}
|
||||
if (errors.length === 0) {
|
||||
return true
|
||||
} else {
|
||||
return errors
|
||||
}
|
||||
}}
|
||||
},
|
||||
icon: function() { return sf.icon||"subflow.svg" },
|
||||
category: sf.category || "subflows",
|
||||
|
@@ -118,10 +118,16 @@ RED.contextMenu = (function () {
|
||||
onselect: 'core:split-wire-with-link-nodes',
|
||||
disabled: !canEdit || !hasLinks
|
||||
},
|
||||
null,
|
||||
{ onselect: 'core:show-import-dialog', label: RED._('common.label.import')},
|
||||
{ onselect: 'core:show-examples-import-dialog', label: RED._('menu.label.importExample') }
|
||||
null
|
||||
)
|
||||
if (RED.settings.theme("menu.menu-item-import-library", true)) {
|
||||
insertOptions.push(
|
||||
{ onselect: 'core:show-import-dialog', label: RED._('common.label.import')},
|
||||
{ onselect: 'core:show-examples-import-dialog', label: RED._('menu.label.importExample') }
|
||||
)
|
||||
}
|
||||
|
||||
|
||||
if (hasSelection && canEdit) {
|
||||
const nodeOptions = []
|
||||
if (!hasMultipleSelection && !isGroup) {
|
||||
@@ -194,8 +200,14 @@ RED.contextMenu = (function () {
|
||||
{ onselect: 'core:paste-from-internal-clipboard', label: RED._("keyboard.pasteNode"), disabled: !canEdit || !RED.view.clipboard() },
|
||||
{ onselect: 'core:delete-selection', label: RED._('keyboard.deleteSelected'), disabled: !canEdit || !canDelete },
|
||||
{ onselect: 'core:delete-selection-and-reconnect', label: RED._('keyboard.deleteReconnect'), disabled: !canEdit || !canDelete },
|
||||
{ onselect: 'core:show-export-dialog', label: RED._("menu.label.export") },
|
||||
{ onselect: 'core:select-all-nodes', label: RED._("keyboard.selectAll") },
|
||||
)
|
||||
if (RED.settings.theme("menu.menu-item-export-library", true)) {
|
||||
menuItems.push(
|
||||
{ onselect: 'core:show-export-dialog', label: RED._("menu.label.export") }
|
||||
)
|
||||
}
|
||||
menuItems.push(
|
||||
{ onselect: 'core:select-all-nodes', label: RED._("keyboard.selectAll") }
|
||||
)
|
||||
}
|
||||
|
||||
|
@@ -906,7 +906,10 @@ RED.utils = (function() {
|
||||
* @returns true if valid, String if invalid
|
||||
*/
|
||||
function validateTypedProperty(propertyValue, propertyType, opt) {
|
||||
|
||||
if (propertyValue && /^\${[^}]+}$/.test(propertyValue)) {
|
||||
// Allow ${ENV_VAR} value
|
||||
return true
|
||||
}
|
||||
let error
|
||||
if (propertyType === 'json') {
|
||||
try {
|
||||
|
@@ -4156,7 +4156,7 @@ RED.view = (function() {
|
||||
}
|
||||
var width = img.width * scaleFactor;
|
||||
if (width > 20) {
|
||||
scalefactor *= 20/width;
|
||||
scaleFactor *= 20/width;
|
||||
width = 20;
|
||||
}
|
||||
var height = img.height * scaleFactor;
|
||||
|
@@ -491,6 +491,11 @@ RED.workspaces = (function() {
|
||||
createWorkspaceTabs();
|
||||
RED.events.on("sidebar:resize",workspace_tabs.resize);
|
||||
|
||||
RED.events.on("workspace:clear", () => {
|
||||
// Reset the index used to generate new flow names
|
||||
workspaceIndex = 0
|
||||
})
|
||||
|
||||
RED.actions.add("core:show-next-tab",function() {
|
||||
var oldActive = activeWorkspace;
|
||||
workspace_tabs.nextTab();
|
||||
@@ -657,6 +662,9 @@ RED.workspaces = (function() {
|
||||
RED.events.on("flows:change", (ws) => {
|
||||
$("#red-ui-tab-"+(ws.id.replace(".","-"))).toggleClass('red-ui-workspace-changed',!!(ws.contentsChanged || ws.changed || ws.added));
|
||||
})
|
||||
RED.events.on("subflows:change", (ws) => {
|
||||
$("#red-ui-tab-"+(ws.id.replace(".","-"))).toggleClass('red-ui-workspace-changed',!!(ws.contentsChanged || ws.changed || ws.added));
|
||||
})
|
||||
|
||||
hideWorkspace();
|
||||
}
|
||||
|
@@ -16,8 +16,20 @@
|
||||
RED.validators = {
|
||||
number: function(blankAllowed,mopt){
|
||||
return function(v, opt) {
|
||||
if ((blankAllowed&&(v===''||v===undefined)) || (v!=='' && !isNaN(v))) {
|
||||
return true;
|
||||
if (blankAllowed && (v === '' || v === undefined)) {
|
||||
return true
|
||||
}
|
||||
if (v !== '') {
|
||||
if (/^NaN$|^[+-]?[0-9]*\.?[0-9]*([eE][-+]?[0-9]+)?$|^[+-]?(0b|0B)[01]+$|^[+-]?(0o|0O)[0-7]+$|^[+-]?(0x|0X)[0-9a-fA-F]+$/.test(v)) {
|
||||
return true
|
||||
}
|
||||
if (/^\${[^}]+}$/.test(v)) {
|
||||
// Allow ${ENV_VAR} value
|
||||
return true
|
||||
}
|
||||
}
|
||||
if (!isNaN(v)) {
|
||||
return true
|
||||
}
|
||||
if (opt && opt.label) {
|
||||
return RED._("validator.errors.invalid-num-prop", {
|
||||
|
@@ -227,34 +227,42 @@
|
||||
name: {value:""},
|
||||
props:{value:[{p:"payload"},{p:"topic",vt:"str"}], validate:function(v, opt) {
|
||||
if (!v || v.length === 0) { return true }
|
||||
const errors = []
|
||||
for (var i=0;i<v.length;i++) {
|
||||
if (/^\${[^}]+}$/.test(v[i].v)) {
|
||||
// Allow ${ENV_VAR} value
|
||||
continue
|
||||
}
|
||||
if (/msg|flow|global/.test(v[i].vt)) {
|
||||
if (!RED.utils.validatePropertyExpression(v[i].v)) {
|
||||
return RED._("node-red:inject.errors.invalid-prop", { prop: 'msg.'+v[i].p, error: v[i].v });
|
||||
errors.push(RED._("node-red:inject.errors.invalid-prop", { prop: 'msg.'+v[i].p, error: v[i].v }))
|
||||
}
|
||||
} else if (v[i].vt === "jsonata") {
|
||||
try{ jsonata(v[i].v); }
|
||||
catch(e){
|
||||
return RED._("node-red:inject.errors.invalid-jsonata", { prop: 'msg.'+v[i].p, error: e.message });
|
||||
errors.push(RED._("node-red:inject.errors.invalid-jsonata", { prop: 'msg.'+v[i].p, error: e.message }))
|
||||
}
|
||||
} else if (v[i].vt === "json") {
|
||||
try{ JSON.parse(v[i].v); }
|
||||
catch(e){
|
||||
return RED._("node-red:inject.errors.invalid-json", { prop: 'msg.'+v[i].p, error: e.message });
|
||||
errors.push(RED._("node-red:inject.errors.invalid-json", { prop: 'msg.'+v[i].p, error: e.message }))
|
||||
}
|
||||
} else if (v[i].vt === "num"){
|
||||
if (!/^[+-]?[0-9]*\.?[0-9]*([eE][-+]?[0-9]+)?$/.test(v[i].v)) {
|
||||
return RED._("node-red:inject.errors.invalid-prop", { prop: 'msg.'+v[i].p, error: v[i].v });
|
||||
errors.push(RED._("node-red:inject.errors.invalid-prop", { prop: 'msg.'+v[i].p, error: v[i].v }))
|
||||
}
|
||||
}
|
||||
}
|
||||
if (errors.length > 0) {
|
||||
return errors
|
||||
}
|
||||
return true;
|
||||
}
|
||||
},
|
||||
repeat: {
|
||||
value:"", validate: function(v, opt) {
|
||||
if ((v === "") ||
|
||||
(RED.validators.number(v) &&
|
||||
(RED.validators.number()(v) &&
|
||||
(v >= 0) && (v <= 2147483))) {
|
||||
return true;
|
||||
}
|
||||
@@ -263,7 +271,7 @@
|
||||
},
|
||||
crontab: {value:""},
|
||||
once: {value:false},
|
||||
onceDelay: {value:0.1},
|
||||
onceDelay: {value:0.1, validate: RED.validators.number(true)},
|
||||
topic: {value:""},
|
||||
payload: {value:"", validate: RED.validators.typedInput("payloadType", false) },
|
||||
payloadType: {value:"date"},
|
||||
|
@@ -1,3 +1,3 @@
|
||||
<script type="text/html" data-help-name="global-config">
|
||||
<p>大域的なフローの設定を保持するノード。大域的な環境変数の定義を含みます。</p>
|
||||
</script>p
|
||||
</script>
|
||||
|
@@ -23,7 +23,7 @@
|
||||
<dt class="optional">template <span class="property-type">string</span></dt>
|
||||
<dd>由<code>msg.payload</code>填充的模板。如果未在编辑面板中配置,则可以将设为msg的属性。</dd>
|
||||
</dl>
|
||||
<h3>Outputs</h3>
|
||||
<h3>输出</h3>
|
||||
<dl class="message-properties">
|
||||
<dt>msg <span class="property-type">object</span></dt>
|
||||
<dd>由来自传入msg的属性来填充已配置的模板后输出的带有属性的msg。</dd>
|
||||
@@ -32,7 +32,7 @@
|
||||
<p>默认情况下使用<i><a href="http://mustache.github.io/mustache.5.html" target="_blank">mustache</a></i>格式。如有需要也可以切换其他格式。</p>
|
||||
<p>例如:
|
||||
<pre>Hello {{payload.name}}. Today is {{date}}</pre>
|
||||
<p>receives a message containing:
|
||||
<p>接收一条消息,其中包含:
|
||||
<pre>{
|
||||
date: "Monday",
|
||||
payload: {
|
||||
|
@@ -1,6 +1,6 @@
|
||||
{
|
||||
"name": "@node-red/nodes",
|
||||
"version": "3.1.4",
|
||||
"version": "3.1.8",
|
||||
"license": "Apache-2.0",
|
||||
"repository": {
|
||||
"type": "git",
|
||||
|
@@ -36,7 +36,7 @@ async function getFlowsFromPath(path) {
|
||||
promises.push(getFlowsFromPath(fullPath));
|
||||
} else if (/\.json$/.test(file)){
|
||||
validFiles.push(file);
|
||||
promises.push(Promise.resolve(file.split(".")[0]))
|
||||
promises.push(Promise.resolve(file.replace(/\.json$/, '')))
|
||||
}
|
||||
})
|
||||
}
|
||||
|
@@ -1,6 +1,6 @@
|
||||
{
|
||||
"name": "@node-red/registry",
|
||||
"version": "3.1.4",
|
||||
"version": "3.1.8",
|
||||
"license": "Apache-2.0",
|
||||
"main": "./lib/index.js",
|
||||
"repository": {
|
||||
@@ -16,7 +16,7 @@
|
||||
}
|
||||
],
|
||||
"dependencies": {
|
||||
"@node-red/util": "3.1.4",
|
||||
"@node-red/util": "3.1.8",
|
||||
"clone": "2.1.2",
|
||||
"fs-extra": "11.1.1",
|
||||
"semver": "7.5.4",
|
||||
|
@@ -485,7 +485,7 @@ class Flow {
|
||||
}
|
||||
if (!key.startsWith("$parent.")) {
|
||||
if (this._env.hasOwnProperty(key)) {
|
||||
return (Object.hasOwn(this._env[key], 'value') && this._env[key].__clone__) ? clone(this._env[key].value) : this._env[key]
|
||||
return (this._env[key] && Object.hasOwn(this._env[key], 'value') && this._env[key].__clone__) ? clone(this._env[key].value) : this._env[key]
|
||||
}
|
||||
} else {
|
||||
key = key.substring(8);
|
||||
|
@@ -41,7 +41,7 @@ class Group {
|
||||
}
|
||||
if (!key.startsWith("$parent.")) {
|
||||
if (this._env.hasOwnProperty(key)) {
|
||||
return (Object.hasOwn(this._env[key], 'value') && this._env[key].__clone__) ? clone(this._env[key].value) : this._env[key]
|
||||
return (this._env[key] && Object.hasOwn(this._env[key], 'value') && this._env[key].__clone__) ? clone(this._env[key].value) : this._env[key]
|
||||
}
|
||||
} else {
|
||||
key = key.substring(8);
|
||||
|
@@ -376,7 +376,7 @@ class Subflow extends Flow {
|
||||
}
|
||||
if (!key.startsWith("$parent.")) {
|
||||
if (this._env.hasOwnProperty(key)) {
|
||||
return (Object.hasOwn(this._env[key], 'value') && this._env[key].__clone__) ? clone(this._env[key].value) : this._env[key]
|
||||
return (this._env[key] && Object.hasOwn(this._env[key], 'value') && this._env[key].__clone__) ? clone(this._env[key].value) : this._env[key]
|
||||
}
|
||||
} else {
|
||||
key = key.substring(8);
|
||||
|
@@ -106,14 +106,22 @@ async function evaluateEnvProperties(flow, env, credentials) {
|
||||
result = { value: result, __clone__: true}
|
||||
}
|
||||
evaluatedEnv[name] = result
|
||||
} else {
|
||||
evaluatedEnv[name] = undefined
|
||||
flow.error(`Error evaluating env property '${name}': ${err.toString()}`)
|
||||
}
|
||||
resolve()
|
||||
});
|
||||
}))
|
||||
} else {
|
||||
value = redUtil.evaluateNodeProperty(value, type, {_flow: flow}, null, null);
|
||||
if (typeof value === 'object') {
|
||||
value = { value: value, __clone__: true}
|
||||
try {
|
||||
value = redUtil.evaluateNodeProperty(value, type, {_flow: flow}, null, null);
|
||||
if (typeof value === 'object') {
|
||||
value = { value: value, __clone__: true}
|
||||
}
|
||||
} catch (err) {
|
||||
value = undefined
|
||||
flow.error(`Error evaluating env property '${name}': ${err.toString()}`)
|
||||
}
|
||||
}
|
||||
evaluatedEnv[name] = value
|
||||
|
@@ -384,7 +384,8 @@ var api = module.exports = {
|
||||
}
|
||||
}
|
||||
} else if (nodeType === "global-config") {
|
||||
const existingCredentialKeys = Object.keys(savedCredentials?.map || [])
|
||||
savedCredentials.map = savedCredentials.map || {}
|
||||
const existingCredentialKeys = Object.keys(savedCredentials.map)
|
||||
const newCredentialKeys = Object.keys(newCreds?.map || [])
|
||||
existingCredentialKeys.forEach(key => {
|
||||
if (!newCreds.map?.[key]) {
|
||||
@@ -396,7 +397,7 @@ var api = module.exports = {
|
||||
})
|
||||
newCredentialKeys.forEach(key => {
|
||||
if (!/^has_/.test(key)) {
|
||||
if (!savedCredentials.map?.[key] || newCreds.map[key] !== '__PWRD__') {
|
||||
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]
|
||||
|
@@ -77,7 +77,7 @@ var storageModuleInterface = {
|
||||
flows: flows,
|
||||
credentials: creds
|
||||
};
|
||||
result.rev = crypto.createHash('md5').update(JSON.stringify(result.flows)).digest("hex");
|
||||
result.rev = crypto.createHash('sha256').update(JSON.stringify(result.flows)).digest("hex");
|
||||
return result;
|
||||
})
|
||||
});
|
||||
@@ -95,7 +95,7 @@ var storageModuleInterface = {
|
||||
|
||||
return credentialSavePromise.then(function() {
|
||||
return storageModule.saveFlows(flows, user).then(function() {
|
||||
return crypto.createHash('md5').update(JSON.stringify(config.flows)).digest("hex");
|
||||
return crypto.createHash('sha256').update(JSON.stringify(config.flows)).digest("hex");
|
||||
})
|
||||
});
|
||||
},
|
||||
|
@@ -1,6 +1,6 @@
|
||||
{
|
||||
"name": "@node-red/runtime",
|
||||
"version": "3.1.4",
|
||||
"version": "3.1.8",
|
||||
"license": "Apache-2.0",
|
||||
"main": "./lib/index.js",
|
||||
"repository": {
|
||||
@@ -16,11 +16,11 @@
|
||||
}
|
||||
],
|
||||
"dependencies": {
|
||||
"@node-red/registry": "3.1.4",
|
||||
"@node-red/util": "3.1.4",
|
||||
"@node-red/registry": "3.1.8",
|
||||
"@node-red/util": "3.1.8",
|
||||
"async-mutex": "0.4.0",
|
||||
"clone": "2.1.2",
|
||||
"express": "4.18.2",
|
||||
"express": "4.19.2",
|
||||
"fs-extra": "11.1.1",
|
||||
"json-stringify-safe": "5.0.1"
|
||||
}
|
||||
|
@@ -1,6 +1,6 @@
|
||||
{
|
||||
"name": "@node-red/util",
|
||||
"version": "3.1.4",
|
||||
"version": "3.1.8",
|
||||
"license": "Apache-2.0",
|
||||
"repository": {
|
||||
"type": "git",
|
||||
@@ -18,7 +18,7 @@
|
||||
"fs-extra": "11.1.1",
|
||||
"i18next": "21.10.0",
|
||||
"json-stringify-safe": "5.0.1",
|
||||
"jsonata": "1.8.6",
|
||||
"jsonata": "1.8.7",
|
||||
"lodash.clonedeep": "^4.5.0",
|
||||
"moment": "2.29.4",
|
||||
"moment-timezone": "0.5.43"
|
||||
|
4
packages/node_modules/node-red/lib/red.js
vendored
4
packages/node_modules/node-red/lib/red.js
vendored
@@ -26,8 +26,8 @@ var server = null;
|
||||
var apiEnabled = false;
|
||||
|
||||
const NODE_MAJOR_VERSION = process.versions.node.split('.')[0];
|
||||
if (NODE_MAJOR_VERSION > 14) {
|
||||
const dns = require('node:dns');
|
||||
if (NODE_MAJOR_VERSION >= 16) {
|
||||
const dns = require('dns');
|
||||
dns.setDefaultResultOrder('ipv4first');
|
||||
}
|
||||
|
||||
|
14
packages/node_modules/node-red/package.json
vendored
14
packages/node_modules/node-red/package.json
vendored
@@ -1,6 +1,6 @@
|
||||
{
|
||||
"name": "node-red",
|
||||
"version": "3.1.4",
|
||||
"version": "3.1.8",
|
||||
"description": "Low-code programming for event-driven applications",
|
||||
"homepage": "https://nodered.org",
|
||||
"license": "Apache-2.0",
|
||||
@@ -31,15 +31,15 @@
|
||||
"flow"
|
||||
],
|
||||
"dependencies": {
|
||||
"@node-red/editor-api": "3.1.4",
|
||||
"@node-red/runtime": "3.1.4",
|
||||
"@node-red/util": "3.1.4",
|
||||
"@node-red/nodes": "3.1.4",
|
||||
"@node-red/editor-api": "3.1.8",
|
||||
"@node-red/runtime": "3.1.8",
|
||||
"@node-red/util": "3.1.8",
|
||||
"@node-red/nodes": "3.1.8",
|
||||
"basic-auth": "2.0.1",
|
||||
"bcryptjs": "2.4.3",
|
||||
"express": "4.18.2",
|
||||
"express": "4.19.2",
|
||||
"fs-extra": "11.1.1",
|
||||
"node-red-admin": "^3.1.2",
|
||||
"node-red-admin": "^3.1.3",
|
||||
"nopt": "5.0.0",
|
||||
"semver": "7.5.4"
|
||||
},
|
||||
|
@@ -60,6 +60,7 @@ describe('HTTP Request Node', function() {
|
||||
function startServer(done) {
|
||||
testPort += 1;
|
||||
testServer = stoppable(http.createServer(testApp));
|
||||
const promises = []
|
||||
testServer.listen(testPort,function(err) {
|
||||
testSslPort += 1;
|
||||
console.log("ssl port", testSslPort);
|
||||
@@ -81,13 +82,17 @@ describe('HTTP Request Node', function() {
|
||||
*/
|
||||
};
|
||||
testSslServer = stoppable(https.createServer(sslOptions,testApp));
|
||||
testSslServer.listen(testSslPort, function(err){
|
||||
if (err) {
|
||||
console.log(err);
|
||||
} else {
|
||||
console.log("started testSslServer");
|
||||
}
|
||||
});
|
||||
console.log('> start testSslServer')
|
||||
promises.push(new Promise((resolve, reject) => {
|
||||
testSslServer.listen(testSslPort, function(err){
|
||||
console.log(' done testSslServer')
|
||||
if (err) {
|
||||
reject(err)
|
||||
} else {
|
||||
resolve()
|
||||
}
|
||||
});
|
||||
}))
|
||||
|
||||
testSslClientPort += 1;
|
||||
var sslClientOptions = {
|
||||
@@ -97,10 +102,17 @@ describe('HTTP Request Node', function() {
|
||||
requestCert: true
|
||||
};
|
||||
testSslClientServer = stoppable(https.createServer(sslClientOptions, testApp));
|
||||
testSslClientServer.listen(testSslClientPort, function(err){
|
||||
console.log("ssl-client", err)
|
||||
});
|
||||
|
||||
console.log('> start testSslClientServer')
|
||||
promises.push(new Promise((resolve, reject) => {
|
||||
testSslClientServer.listen(testSslClientPort, function(err){
|
||||
console.log(' done testSslClientServer')
|
||||
if (err) {
|
||||
reject(err)
|
||||
} else {
|
||||
resolve()
|
||||
}
|
||||
});
|
||||
}))
|
||||
testProxyPort += 1;
|
||||
testProxyServer = stoppable(httpProxy(http.createServer()))
|
||||
|
||||
@@ -109,7 +121,17 @@ describe('HTTP Request Node', function() {
|
||||
res.setHeader("x-testproxy-header", "foobar")
|
||||
}
|
||||
})
|
||||
testProxyServer.listen(testProxyPort)
|
||||
console.log('> testProxyServer')
|
||||
promises.push(new Promise((resolve, reject) => {
|
||||
testProxyServer.listen(testProxyPort, function(err) {
|
||||
console.log(' done testProxyServer')
|
||||
if (err) {
|
||||
reject(err)
|
||||
} else {
|
||||
resolve()
|
||||
}
|
||||
})
|
||||
}))
|
||||
|
||||
testProxyAuthPort += 1
|
||||
testProxyServerAuth = stoppable(httpProxy(http.createServer()))
|
||||
@@ -131,9 +153,19 @@ describe('HTTP Request Node', function() {
|
||||
res.setHeader("x-testproxy-header", "foobar")
|
||||
}
|
||||
})
|
||||
testProxyServerAuth.listen(testProxyAuthPort)
|
||||
console.log('> testProxyServerAuth')
|
||||
promises.push(new Promise((resolve, reject) => {
|
||||
testProxyServerAuth.listen(testProxyAuthPort, function(err) {
|
||||
console.log(' done testProxyServerAuth')
|
||||
if (err) {
|
||||
reject(err)
|
||||
} else {
|
||||
resolve()
|
||||
}
|
||||
})
|
||||
}))
|
||||
|
||||
done(err);
|
||||
Promise.all(promises).then(() => { done() }).catch(done)
|
||||
});
|
||||
}
|
||||
|
||||
@@ -429,7 +461,11 @@ describe('HTTP Request Node', function() {
|
||||
if (err) {
|
||||
done(err);
|
||||
}
|
||||
helper.startServer(done);
|
||||
console.log('> helper.startServer')
|
||||
helper.startServer(function(err) {
|
||||
console.log('> helper started')
|
||||
done(err)
|
||||
});
|
||||
});
|
||||
});
|
||||
|
||||
|
@@ -33,16 +33,15 @@ describe("library api", function() {
|
||||
should.not.exist(library.getExampleFlowPath('foo','bar'));
|
||||
});
|
||||
|
||||
it('returns a valid example path', function(done) {
|
||||
it('returns valid example paths', function(done) {
|
||||
library.init();
|
||||
library.addExamplesDir("test-module",path.resolve(__dirname+'/resources/examples')).then(function() {
|
||||
try {
|
||||
var flows = library.getExampleFlows();
|
||||
flows.should.deepEqual({"test-module":{"f":["one"]}});
|
||||
flows.should.deepEqual({"test-module":{"f":["1.2.3","one"]}});
|
||||
|
||||
var examplePath = library.getExampleFlowPath('test-module','one');
|
||||
examplePath.should.eql(path.resolve(__dirname+'/resources/examples/one.json'))
|
||||
|
||||
examplePath.should.eql(path.resolve(__dirname+'/resources/examples/one.json'));
|
||||
|
||||
library.removeExamplesDir('test-module');
|
||||
|
||||
@@ -57,6 +56,5 @@ describe("library api", function() {
|
||||
done(err);
|
||||
}
|
||||
});
|
||||
|
||||
})
|
||||
});
|
||||
});
|
||||
|
Reference in New Issue
Block a user