@node-red/runtime 1.3.3 → 1.3.7
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/lib/api/nodes.js +3 -3
- package/lib/api/plugins.js +1 -1
- package/lib/flows/Flow.js +2 -2
- package/lib/flows/index.js +3 -1
- package/lib/flows/util.js +5 -2
- package/lib/index.js +22 -1
- package/lib/nodes/context/index.js +33 -4
- package/lib/nodes/credentials.js +5 -1
- package/lib/storage/localfilesystem/projects/git/index.js +1 -1
- package/lib/storage/localfilesystem/projects/index.js +9 -2
- package/package.json +3 -3
package/lib/api/nodes.js
CHANGED
|
@@ -272,7 +272,7 @@ var api = module.exports = {
|
|
|
272
272
|
} catch(error) {
|
|
273
273
|
runtime.log.audit({event: "nodes.remove",module:opts.module,error:error.code||"unexpected_error",message:error.toString()}, opts.req);
|
|
274
274
|
error.status = 400;
|
|
275
|
-
throw
|
|
275
|
+
throw error;
|
|
276
276
|
}
|
|
277
277
|
},
|
|
278
278
|
|
|
@@ -319,7 +319,7 @@ var api = module.exports = {
|
|
|
319
319
|
} catch(error) {
|
|
320
320
|
runtime.log.audit({event: "nodes.module.set",module:mod,enabled:opts.enabled,error:error.code||"unexpected_error",message:error.toString()}, opts.req);
|
|
321
321
|
error.status = 400;
|
|
322
|
-
throw
|
|
322
|
+
throw error;
|
|
323
323
|
}
|
|
324
324
|
},
|
|
325
325
|
|
|
@@ -366,7 +366,7 @@ var api = module.exports = {
|
|
|
366
366
|
} catch(error) {
|
|
367
367
|
runtime.log.audit({event: "nodes.info.set",id:id,enabled:enabled,error:error.code||"unexpected_error",message:error.toString()}, opts.req);
|
|
368
368
|
error.status = 400;
|
|
369
|
-
throw
|
|
369
|
+
throw error;
|
|
370
370
|
}
|
|
371
371
|
},
|
|
372
372
|
|
package/lib/api/plugins.js
CHANGED
|
@@ -58,7 +58,7 @@ var api = module.exports = {
|
|
|
58
58
|
* @memberof @node-red/runtime_plugins
|
|
59
59
|
*/
|
|
60
60
|
getPluginConfigs: async function(opts) {
|
|
61
|
-
if (/[^
|
|
61
|
+
if (/[^0-9a-z=\-\*]/i.test(opts.lang)) {
|
|
62
62
|
throw new Error("Invalid language: "+opts.lang)
|
|
63
63
|
return;
|
|
64
64
|
}
|
package/lib/flows/Flow.js
CHANGED
|
@@ -369,12 +369,12 @@ class Flow {
|
|
|
369
369
|
return undefined;
|
|
370
370
|
}
|
|
371
371
|
// console.log((new Error().stack).toString().split("\n").slice(1,3).join("\n"))
|
|
372
|
-
if ((this.flow.configs && this.flow.configs[id]) || (this.flow.nodes && this.flow.nodes[id])) {
|
|
372
|
+
if ((this.flow.configs && this.flow.configs[id]) || (this.flow.nodes && this.flow.nodes[id] && this.flow.nodes[id].type.substring(0,8) != "subflow:")) {
|
|
373
373
|
// This is a node owned by this flow, so return whatever we have got
|
|
374
374
|
// During a stop/restart, activeNodes could be null for this id
|
|
375
375
|
return this.activeNodes[id];
|
|
376
376
|
} else if (this.activeNodes[id]) {
|
|
377
|
-
// TEMP: this is a subflow internal node within this flow
|
|
377
|
+
// TEMP: this is a subflow internal node within this flow or subflow instance node
|
|
378
378
|
return this.activeNodes[id];
|
|
379
379
|
} else if (this.subflowInstanceNodes[id]) {
|
|
380
380
|
return this.subflowInstanceNodes[id];
|
package/lib/flows/index.js
CHANGED
package/lib/flows/util.js
CHANGED
|
@@ -95,6 +95,9 @@ function createNode(flow,config) {
|
|
|
95
95
|
} else if (nodeTypeConstructor) {
|
|
96
96
|
// console.log(nodeTypeConstructor)
|
|
97
97
|
var subflowConfig = parseConfig([nodeTypeConstructor.subflow].concat(nodeTypeConstructor.subflow.flow));
|
|
98
|
+
var subflowInstanceConfig = subflowConfig.subflows[nodeTypeConstructor.subflow.id];
|
|
99
|
+
delete subflowConfig.subflows[nodeTypeConstructor.subflow.id];
|
|
100
|
+
subflowInstanceConfig.subflows = subflowConfig.subflows;
|
|
98
101
|
var instanceConfig = clone(config);
|
|
99
102
|
instanceConfig.env = clone(nodeTypeConstructor.subflow.env);
|
|
100
103
|
|
|
@@ -107,7 +110,7 @@ function createNode(flow,config) {
|
|
|
107
110
|
switch(typeof config[nodeProp.name]) {
|
|
108
111
|
case "string": nodePropType = "str"; break;
|
|
109
112
|
case "number": nodePropType = "num"; break;
|
|
110
|
-
case "boolean": nodePropType = "bool"; nodePropValue
|
|
113
|
+
case "boolean": nodePropType = "bool"; nodePropValue == nodeProp?"true":"false"; break;
|
|
111
114
|
default:
|
|
112
115
|
nodePropType = config[nodeProp.name].type;
|
|
113
116
|
nodePropValue = config[nodeProp.name].value;
|
|
@@ -124,7 +127,7 @@ function createNode(flow,config) {
|
|
|
124
127
|
nodeTypeConstructor.type,
|
|
125
128
|
flow,
|
|
126
129
|
flow.global,
|
|
127
|
-
|
|
130
|
+
subflowInstanceConfig,
|
|
128
131
|
instanceConfig
|
|
129
132
|
);
|
|
130
133
|
subflow.start();
|
package/lib/index.js
CHANGED
|
@@ -64,6 +64,27 @@ var server;
|
|
|
64
64
|
*/
|
|
65
65
|
function init(userSettings,httpServer,_adminApi) {
|
|
66
66
|
server = httpServer;
|
|
67
|
+
|
|
68
|
+
if (server && server.on) {
|
|
69
|
+
// Add a listener to the upgrade event so that we can properly timeout connection
|
|
70
|
+
// attempts that do not get handled by any nodes in the user's flow.
|
|
71
|
+
// See #2956
|
|
72
|
+
server.on('upgrade',(request, socket, head) => {
|
|
73
|
+
// Add a no-op handler to the error event in case nothing upgrades this socket
|
|
74
|
+
// before the remote end closes it. This ensures we don't get as uncaughtException
|
|
75
|
+
socket.on("error", err => {})
|
|
76
|
+
setTimeout(function() {
|
|
77
|
+
// If this request has been handled elsewhere, the upgrade will have
|
|
78
|
+
// been completed and bytes written back to the client.
|
|
79
|
+
// If nothing has been written on the socket, nothing has handled the
|
|
80
|
+
// upgrade, so we can consider this an unhandled upgrade.
|
|
81
|
+
if (socket.bytesWritten === 0) {
|
|
82
|
+
socket.destroy();
|
|
83
|
+
}
|
|
84
|
+
},userSettings.inboundWebSocketTimeout || 5000)
|
|
85
|
+
});
|
|
86
|
+
}
|
|
87
|
+
|
|
67
88
|
userSettings.version = getVersion();
|
|
68
89
|
settings.init(userSettings);
|
|
69
90
|
|
|
@@ -197,7 +218,7 @@ var reinstallTimeout;
|
|
|
197
218
|
function reinstallModules(moduleList) {
|
|
198
219
|
const promises = [];
|
|
199
220
|
const reinstallList = [];
|
|
200
|
-
|
|
221
|
+
var installRetry = 30000;
|
|
201
222
|
if (settings.hasOwnProperty('autoInstallModulesRetry')) {
|
|
202
223
|
log.warn(log._("server.deprecatedOption",{old:"autoInstallModulesRetry", new:"externalModules.autoInstallRetry"}));
|
|
203
224
|
installRetry = settings.autoInstallModulesRetry;
|
|
@@ -215,6 +215,22 @@ function followParentContext(parent, key) {
|
|
|
215
215
|
return null;
|
|
216
216
|
}
|
|
217
217
|
|
|
218
|
+
function validateContextKey(key) {
|
|
219
|
+
try {
|
|
220
|
+
const keys = Array.isArray(key) ? key : [key];
|
|
221
|
+
if(!keys.length) { return false }; //no key to get/set
|
|
222
|
+
for (let index = 0; index < keys.length; index++) {
|
|
223
|
+
const k = keys[index];
|
|
224
|
+
if (typeof k !== "string" || !k.length) {
|
|
225
|
+
return false; //not string or zero-length
|
|
226
|
+
}
|
|
227
|
+
}
|
|
228
|
+
} catch (error) {
|
|
229
|
+
return false;
|
|
230
|
+
}
|
|
231
|
+
return true;
|
|
232
|
+
}
|
|
233
|
+
|
|
218
234
|
function createContext(id,seed,parent) {
|
|
219
235
|
// Seed is only set for global context - sourced from functionGlobalContext
|
|
220
236
|
var scope = id;
|
|
@@ -251,11 +267,11 @@ function createContext(id,seed,parent) {
|
|
|
251
267
|
}
|
|
252
268
|
}
|
|
253
269
|
}
|
|
270
|
+
|
|
254
271
|
Object.defineProperties(obj, {
|
|
255
272
|
get: {
|
|
256
273
|
value: function(key, storage, callback) {
|
|
257
274
|
var context;
|
|
258
|
-
|
|
259
275
|
if (!callback && typeof storage === 'function') {
|
|
260
276
|
callback = storage;
|
|
261
277
|
storage = undefined;
|
|
@@ -263,7 +279,14 @@ function createContext(id,seed,parent) {
|
|
|
263
279
|
if (callback && typeof callback !== 'function'){
|
|
264
280
|
throw new Error("Callback must be a function");
|
|
265
281
|
}
|
|
266
|
-
|
|
282
|
+
if (!validateContextKey(key)) {
|
|
283
|
+
var err = Error("Invalid context key");
|
|
284
|
+
if(callback) {
|
|
285
|
+
return callback(err);
|
|
286
|
+
} else {
|
|
287
|
+
throw err;
|
|
288
|
+
}
|
|
289
|
+
}
|
|
267
290
|
if (!Array.isArray(key)) {
|
|
268
291
|
var keyParts = util.parseContextStore(key);
|
|
269
292
|
key = keyParts.key;
|
|
@@ -337,7 +360,6 @@ function createContext(id,seed,parent) {
|
|
|
337
360
|
set: {
|
|
338
361
|
value: function(key, value, storage, callback) {
|
|
339
362
|
var context;
|
|
340
|
-
|
|
341
363
|
if (!callback && typeof storage === 'function') {
|
|
342
364
|
callback = storage;
|
|
343
365
|
storage = undefined;
|
|
@@ -345,7 +367,14 @@ function createContext(id,seed,parent) {
|
|
|
345
367
|
if (callback && typeof callback !== 'function'){
|
|
346
368
|
throw new Error("Callback must be a function");
|
|
347
369
|
}
|
|
348
|
-
|
|
370
|
+
if (!validateContextKey(key)) {
|
|
371
|
+
var err = Error("Invalid context key");
|
|
372
|
+
if(callback) {
|
|
373
|
+
return callback(err);
|
|
374
|
+
} else {
|
|
375
|
+
throw err;
|
|
376
|
+
}
|
|
377
|
+
}
|
|
349
378
|
if (!Array.isArray(key)) {
|
|
350
379
|
var keyParts = util.parseContextStore(key);
|
|
351
380
|
key = keyParts.key;
|
package/lib/nodes/credentials.js
CHANGED
|
@@ -343,7 +343,11 @@ var api = module.exports = {
|
|
|
343
343
|
if (newCreds) {
|
|
344
344
|
delete node.credentials;
|
|
345
345
|
var savedCredentials = credentialCache[nodeID] || {};
|
|
346
|
-
|
|
346
|
+
// Need to check the type of constructor for this node.
|
|
347
|
+
// - Function : regular node
|
|
348
|
+
// - !Function: subflow module
|
|
349
|
+
|
|
350
|
+
if (/^subflow(:|$)/.test(nodeType) || typeof runtime.nodes.getType(nodeType) !== 'function') {
|
|
347
351
|
for (cred in newCreds) {
|
|
348
352
|
if (newCreds.hasOwnProperty(cred)) {
|
|
349
353
|
if (newCreds[cred] === "__PWRD__") {
|
|
@@ -291,7 +291,7 @@ function parseLog(log) {
|
|
|
291
291
|
currentCommit = {}
|
|
292
292
|
return;
|
|
293
293
|
}
|
|
294
|
-
var m = /^(
|
|
294
|
+
var m = /^(.*?): (.*)$/.exec(l);
|
|
295
295
|
if (m) {
|
|
296
296
|
// git 2.1.4 (Debian Stable) doesn't support %D for refs - so filter out
|
|
297
297
|
if (m[1] === 'refs' && m[2]) {
|
|
@@ -608,8 +608,15 @@ async function saveFlows(flows, user) {
|
|
|
608
608
|
var workflowMode = (gitSettings.workflow||{}).mode || settings.editorTheme.projects.workflow.mode;
|
|
609
609
|
if (workflowMode === 'auto') {
|
|
610
610
|
return activeProject.stageFile([flowsFullPath, credentialsFile]).then(() => {
|
|
611
|
-
return activeProject.
|
|
612
|
-
|
|
611
|
+
return activeProject.status(user, false).then((result) => {
|
|
612
|
+
const items = Object.values(result.files || {});
|
|
613
|
+
// check if saved flow make modification to repository
|
|
614
|
+
if (items.findIndex((item) => (item.status === "M ")) < 0) {
|
|
615
|
+
return Promise.resolve();
|
|
616
|
+
}
|
|
617
|
+
return activeProject.commit(user,{message:"Update flow files"})
|
|
618
|
+
});
|
|
619
|
+
});
|
|
613
620
|
}
|
|
614
621
|
}
|
|
615
622
|
});
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@node-red/runtime",
|
|
3
|
-
"version": "1.3.
|
|
3
|
+
"version": "1.3.7",
|
|
4
4
|
"license": "Apache-2.0",
|
|
5
5
|
"main": "./lib/index.js",
|
|
6
6
|
"repository": {
|
|
@@ -16,8 +16,8 @@
|
|
|
16
16
|
}
|
|
17
17
|
],
|
|
18
18
|
"dependencies": {
|
|
19
|
-
"@node-red/registry": "1.3.
|
|
20
|
-
"@node-red/util": "1.3.
|
|
19
|
+
"@node-red/registry": "1.3.7",
|
|
20
|
+
"@node-red/util": "1.3.7",
|
|
21
21
|
"async-mutex": "0.3.1",
|
|
22
22
|
"clone": "2.1.2",
|
|
23
23
|
"express": "4.17.1",
|