@node-red/nodes 5.0.0-beta.1 → 5.0.0-beta.3

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.
@@ -20,13 +20,15 @@ module.exports = function(RED) {
20
20
  function StatusNode(n) {
21
21
  RED.nodes.createNode(this,n);
22
22
  var node = this;
23
- this.scope = n.scope || [];
23
+ this.scope = n.scope;
24
24
 
25
25
  // auto-filter out any directly connected nodes to avoid simple loopback
26
- const w = this.wires.flat();
27
- for (let i=0; i < this.scope.length; i++) {
28
- if (w.includes(this.scope[i])) {
29
- this.scope.splice(i, 1);
26
+ if (Array.isArray(this.scope)) {
27
+ const w = this.wires.flat();
28
+ for (let i = 0; i < this.scope.length; i++) {
29
+ if (w.includes(this.scope[i])) {
30
+ this.scope.splice(i, 1);
31
+ }
30
32
  }
31
33
  }
32
34
 
@@ -32,6 +32,9 @@ RED.debug = (function() {
32
32
  var numMessages = 100; // Hardcoded number of message to show in debug window scrollback
33
33
 
34
34
  var debugNodeTreeList;
35
+ var debugPaused = false;
36
+ let debugPausedMessage
37
+ let debugPausedMessageCount = 0
35
38
 
36
39
  function init(_config) {
37
40
  config = _config;
@@ -39,14 +42,18 @@ RED.debug = (function() {
39
42
  var content = $("<div>").css({"position":"relative","height":"100%"});
40
43
  var toolbar = $('<div class="red-ui-sidebar-header">'+
41
44
  '<span class="button-group">'+
42
- '<a id="red-ui-sidebar-debug-filter" style="padding-right: 5px" class="red-ui-sidebar-header-button" href="#"><i class="fa fa-filter"></i> <span></span> <i style="padding-left: 5px;" class="fa fa-caret-down"></i></a>'+
45
+ '<a id="red-ui-sidebar-debug-pause" class="red-ui-sidebar-header-button" href="#"><i class="fa fa-pause"></i></a>'+
43
46
  '</span>'+
44
47
  '<span class="button-group">'+
45
- '<a id="red-ui-sidebar-debug-clear" style="border-right: none; padding-right: 6px" class="red-ui-sidebar-header-button" href="#" data-clear-type="all"><i class="fa fa-trash"></i> <span>all</span></a>' +
46
- '<a id="red-ui-sidebar-debug-clear-opts" style="padding: 5px; border-left: none;" class="red-ui-sidebar-header-button" href="#"><i class="fa fa-caret-down"></i></a>'+
48
+ '<a id="red-ui-sidebar-debug-filter" style="padding-right: 3px" class="red-ui-sidebar-header-button" href="#"><i class="fa fa-filter"></i> <span></span> <i style="padding-left: 5px;" class="fa fa-caret-down"></i></a>'+
49
+ '</span>'+
50
+ '<span class="button-group">'+
51
+ '<a id="red-ui-sidebar-debug-clear" style="border-right: none; padding-right: 2px" class="red-ui-sidebar-header-button" href="#" data-clear-type="all"><i class="fa fa-trash"></i> <span>all</span></a>' +
52
+ '<a id="red-ui-sidebar-debug-clear-opts" style="padding: 3px; border-left: none;" class="red-ui-sidebar-header-button" href="#"><i class="fa fa-caret-down"></i></a>'+
47
53
  '</span></div>').appendTo(content);
48
54
 
49
55
  var footerToolbar = $('<div>'+
56
+ '<span id="red-ui-sidebar-debug-info" style="float: left;"></span>' +
50
57
  '<span class="button-group"><a id="red-ui-sidebar-debug-open" class="red-ui-footer-button" href="#"><i class="fa fa-desktop"></i></a></span> ' +
51
58
  '</div>');
52
59
 
@@ -185,6 +192,39 @@ RED.debug = (function() {
185
192
  });
186
193
  RED.popover.tooltip(toolbar.find('#red-ui-sidebar-debug-filter'),RED._('node-red:debug.sidebar.filterLog'));
187
194
 
195
+ toolbar.find("#red-ui-sidebar-debug-pause").on("click", function(e) {
196
+ e.preventDefault();
197
+ toggleDebugflow();
198
+ });
199
+ var pauseTooltip = RED.popover.tooltip(toolbar.find("#red-ui-sidebar-debug-pause"),RED._('node-red:debug.sidebar.pause'),"core:pause-debug-messages");
200
+ function toggleDebugflow() {
201
+ debugPaused = !debugPaused;
202
+ if (debugPaused) {
203
+ debugPausedMessageCount = 0
204
+ debugPausedMessage = $('<div class="red-ui-debug-msg red-ui-debug-msg-paused">')
205
+ $(`<div class="red-ui-debug-msg-meta">
206
+ <span class="red-ui-debug-msg-date">${getTimestamp()}</span>
207
+ <span class="red-ui-debug-msg-name">${RED._('node-red:debug.sidebar.paused')}</span>
208
+ </div>`).appendTo(debugPausedMessage);
209
+ $('<div class="red-ui-debug-msg-meta"><span class="red-ui-debug-msg-name">&nbsp;</span></div>').appendTo(debugPausedMessage);
210
+ const atBottom = (sbc.scrollHeight-messageList.height()-sbc.scrollTop) < 5;
211
+ messageList.append(debugPausedMessage);
212
+ if (atBottom) {
213
+ messageList.scrollTop(sbc.scrollHeight);
214
+ }
215
+
216
+ $("#red-ui-sidebar-debug-pause i").removeClass("fa-pause").addClass("fa-play");
217
+ pauseTooltip.setAction("core:resume-debug-messages");
218
+ pauseTooltip.setContent(RED._('node-red:debug.sidebar.resume'))
219
+ $("#red-ui-sidebar-debug-info").text(RED._('node-red:debug.sidebar.paused'));
220
+ } else {
221
+ $("#red-ui-sidebar-debug-pause i").removeClass("fa-play").addClass("fa-pause");
222
+ pauseTooltip.setAction("core:pause-debug-messages");
223
+ pauseTooltip.setContent(RED._('node-red:debug.sidebar.pause'))
224
+ $("#red-ui-sidebar-debug-info").text("");
225
+ }
226
+ }
227
+
188
228
  toolbar.find("#red-ui-sidebar-debug-clear").on("click", function(e) {
189
229
  e.preventDefault();
190
230
  var action = RED.settings.get("debug.clearType","all")
@@ -389,15 +429,36 @@ RED.debug = (function() {
389
429
  var stack = [];
390
430
  var busy = false;
391
431
  function handleDebugMessage(o) {
392
- if (o) { stack.push(o); }
393
- if (!busy && (stack.length > 0)) {
394
- busy = true;
395
- processDebugMessage(stack.shift());
396
- setTimeout(function() {
397
- busy = false;
398
- handleDebugMessage();
399
- }, 15); // every 15mS = 66 times a second
400
- if (stack.length > numMessages) { stack = stack.splice(-numMessages); }
432
+ if (!debugPaused) {
433
+ if (o) { stack.push(o); }
434
+ if (!busy && (stack.length > 0)) {
435
+ busy = true;
436
+ const message = stack.shift()
437
+ // call any preDebugLog hooks, allowing them to modify the message or block it from being displayed
438
+ RED.hooks.trigger('debugPreProcessMessage', { message }).then(result => {
439
+ if (result === false) {
440
+ return false; // A hook returned false - halt processing of this message
441
+ }
442
+ return processDebugMessage(message);
443
+ }).then(processArtifacts => {
444
+ if (processArtifacts === false) {
445
+ return false; // A hook returned false - halt processing of this message
446
+ }
447
+ const { message, element, payload } = processArtifacts || {};
448
+ return RED.hooks.trigger('debugPostProcessMessage', { message, element, payload });
449
+ }).catch(err => {
450
+ console.error("Error in debug process message hooks", err);
451
+ }).finally(() => {
452
+ setTimeout(function() {
453
+ busy = false;
454
+ handleDebugMessage();
455
+ }, 15); // every 15mS = 66 times a second
456
+ if (stack.length > numMessages) { stack = stack.splice(-numMessages); }
457
+ })
458
+ }
459
+ } else {
460
+ debugPausedMessageCount++
461
+ debugPausedMessage.find('.red-ui-debug-msg-name').last().text(RED._("node-red:debug.sidebar.messagesDropped",{count:debugPausedMessageCount}));
401
462
  }
402
463
  }
403
464
 
@@ -519,10 +580,13 @@ RED.debug = (function() {
519
580
  sourceId: sourceNode && sourceNode.id,
520
581
  rootPath: path,
521
582
  nodeSelector: config.messageSourceClick,
522
- enablePinning: true
583
+ enablePinning: true,
584
+ tools: o.tools // permit preDebugLog hooks to add extra tools to the <debugMessage> element
523
585
  });
524
586
  // Do this in a separate step so the element functions aren't stripped
525
587
  debugMessage.appendTo(el);
588
+ // add the meta row tools container, even if there are no tools, so that the postProcessDebugMessage hook can add tools
589
+ const tools = $('<span class="red-ui-debug-msg-tools button-group"></span>').appendTo(metaRow)
526
590
  // NOTE: relying on function error to have a "type" that all other msgs don't
527
591
  if (o.hasOwnProperty("type") && (o.type === "function")) {
528
592
  var errorLvlType = 'error';
@@ -534,7 +598,6 @@ RED.debug = (function() {
534
598
  msg.addClass('red-ui-debug-msg-level-' + errorLvl);
535
599
  $('<span class="red-ui-debug-msg-topic">function : (' + errorLvlType + ')</span>').appendTo(metaRow);
536
600
  } else {
537
- var tools = $('<span class="red-ui-debug-msg-tools button-group"></span>').appendTo(metaRow);
538
601
  var filterMessage = $('<button class="red-ui-button red-ui-button-small"><i class="fa fa-caret-down"></i></button>').appendTo(tools);
539
602
  filterMessage.on("click", function(e) {
540
603
  e.preventDefault();
@@ -590,6 +653,14 @@ RED.debug = (function() {
590
653
  if (atBottom) {
591
654
  messageList.scrollTop(sbc.scrollHeight);
592
655
  }
656
+
657
+ // return artifacts to permit postProcessDebugMessage hooks to modify the message element, access the
658
+ // processed payload or otherwise modify the message after it has been generated.
659
+ return {
660
+ message: o, // original debug message object, useful for any hook that might have tagged additional info onto it
661
+ element: msg, // the top-level element for this debug message
662
+ payload // the reconstructed debug message
663
+ }
593
664
  }
594
665
 
595
666
  function clearMessageList(clearFilter, filteredOnly) {
@@ -598,6 +669,9 @@ RED.debug = (function() {
598
669
  } else {
599
670
  $(".red-ui-debug-msg:not(.hide)").remove();
600
671
  }
672
+ if (debugPaused) {
673
+ messageList.append(debugPausedMessage);
674
+ }
601
675
  config.clear();
602
676
  if (!!clearFilter) {
603
677
  clearFilterSettings();
@@ -63,6 +63,7 @@
63
63
  <label></label>
64
64
  <select id="node-input-rate-type" style="width:270px !important">
65
65
  <option value="all" data-i18n="delay.limitall"></option>
66
+ <option value="burst" data-i18n="delay.limitburst"></option>
66
67
  <option value="topic" data-i18n="delay.limittopic"></option>
67
68
  </select>
68
69
  </div>
@@ -185,6 +186,8 @@
185
186
  return this._("delay.label.limit")+" "+rate;
186
187
  } else if (this.pauseType == "timed") {
187
188
  return this._("delay.label.limitTopic")+" "+rate;
189
+ } else if (this.pauseType == "burst") {
190
+ return this._("delay.label.burst")+" "+rate;
188
191
  } else {
189
192
  return this._("delay.label.limitTopic")+" "+rate;
190
193
  }
@@ -245,6 +248,9 @@
245
248
  $("#node-input-delay-action").val('rate');
246
249
  $("#node-input-rate-type").val('topic');
247
250
  $("#node-input-rate-topic-type").val('timed');
251
+ } else if (this.pauseType == "burst") {
252
+ $("#node-input-delay-action").val('rate');
253
+ $("#node-input-rate-type").val('burst');
248
254
  }
249
255
 
250
256
  if (!this.timeoutUnits) {
@@ -294,12 +300,17 @@
294
300
  if (this.value === "all") {
295
301
  $("#rate-details-per-topic").hide();
296
302
  $("#node-input-drop-select-queue").attr('disabled', false);
303
+ $("#rate-override").show();
304
+ } else if (this.value === "burst") {
305
+ $("#rate-details-per-topic").hide();
306
+ $("#node-input-drop-select-queue").attr('disabled', true);
307
+ $("#rate-override").hide();
297
308
  } else if (this.value === "topic") {
298
309
  if ($("#node-input-drop-select").val() === "queue") {
299
- $("#node-input-drop-select").val("drop");
300
310
  }
301
311
  $("#node-input-drop-select-queue").attr('disabled', true);
302
312
  $("#rate-details-per-topic").show();
313
+ $("#rate-override").show();
303
314
  }
304
315
  }).trigger("change");
305
316
  },
@@ -312,6 +323,8 @@
312
323
  action = $("#node-input-rate-type").val();
313
324
  if (action === "all") {
314
325
  this.pauseType = "rate";
326
+ } else if (action === "burst") {
327
+ this.pauseType = "burst";
315
328
  } else {
316
329
  this.pauseType = $("#node-input-rate-topic-type").val();
317
330
  }
@@ -42,6 +42,7 @@ module.exports = function(RED) {
42
42
  this.timeoutUnits = n.timeoutUnits;
43
43
  this.randomUnits = n.randomUnits;
44
44
  this.rateUnits = n.rateUnits;
45
+ this.burst = n.rate;
45
46
 
46
47
  if (n.timeoutUnits === "milliseconds") {
47
48
  this.timeout = n.timeout;
@@ -57,12 +58,16 @@ module.exports = function(RED) {
57
58
 
58
59
  if (n.rateUnits === "minute") {
59
60
  this.rate = (60 * 1000)/n.rate;
61
+ this.timer = n.nbRateUnits * (60 * 1000);
60
62
  } else if (n.rateUnits === "hour") {
61
63
  this.rate = (60 * 60 * 1000)/n.rate;
64
+ this.timer = n.nbRateUnits * (60 * 60 * 1000);
62
65
  } else if (n.rateUnits === "day") {
63
66
  this.rate = (24 * 60 * 60 * 1000)/n.rate;
67
+ this.timer = n.nbRateUnits * (24 * 60 * 60 * 1000);
64
68
  } else { // Default to seconds
65
69
  this.rate = 1000/n.rate;
70
+ this.timer = n.nbRateUnits * 1000;
66
71
  }
67
72
 
68
73
  this.rate *= (n.nbRateUnits > 0 ? n.nbRateUnits : 1);
@@ -159,7 +164,8 @@ module.exports = function(RED) {
159
164
  if (node.pauseType === "delay") {
160
165
  node.on("input", function(msg, send, done) {
161
166
  var id = ourTimeout(function() {
162
- node.idList.splice(node.idList.indexOf(id),1);
167
+ var idx = node.idList.indexOf(id);
168
+ if (idx !== -1) { node.idList.splice(idx, 1); }
163
169
  if (node.timeout > 1000) {
164
170
  node.status({fill:"blue",shape:"dot",text:node.idList.length});
165
171
  }
@@ -184,7 +190,8 @@ module.exports = function(RED) {
184
190
  }
185
191
  if (delayvar < 0) { delayvar = 0; }
186
192
  var id = ourTimeout(function() {
187
- node.idList.splice(node.idList.indexOf(id),1);
193
+ var idx = node.idList.indexOf(id);
194
+ if (idx !== -1) { node.idList.splice(idx, 1); }
188
195
  if (node.idList.length === 0) { node.status({}); }
189
196
  send(msg);
190
197
  if (delayvar >= 0) {
@@ -192,7 +199,8 @@ module.exports = function(RED) {
192
199
  }
193
200
  done();
194
201
  }, delayvar, () => done());
195
- node.idList.push(id);
202
+ if (Object.keys(msg).length === 2 && msg.hasOwnProperty("flush")) { id.clear(); }
203
+ else { node.idList.push(id); }
196
204
  if (msg.hasOwnProperty("reset")) { clearDelayList(true); }
197
205
  if (msg.hasOwnProperty("flush")) { flushDelayList(msg.flush); done(); }
198
206
  if (delayvar >= 0) {
@@ -206,7 +214,8 @@ module.exports = function(RED) {
206
214
  node.on("input", function(msg, send, done) {
207
215
  var wait = node.randomFirst + (node.diff * Math.random());
208
216
  var id = ourTimeout(function() {
209
- node.idList.splice(node.idList.indexOf(id),1);
217
+ var idx = node.idList.indexOf(id);
218
+ if (idx !== -1) { node.idList.splice(idx, 1); }
210
219
  send(msg);
211
220
  if (node.timeout >= 1000) {
212
221
  node.status({fill:"blue",shape:"dot",text:node.idList.length});
@@ -336,6 +345,42 @@ module.exports = function(RED) {
336
345
  });
337
346
  }
338
347
 
348
+ // Handle the burst mode
349
+ else if (node.pauseType === "burst") {
350
+ node.timers = [];
351
+ node.inflight = 0;
352
+ node.status({ fill: "green", shape: "ring", text: "" })
353
+ node.on("input", function(msg, send, done) {
354
+ if (msg.hasOwnProperty("reset")) {
355
+ node.timers.forEach((t) => clearTimeout(t));
356
+ node.timers = [];
357
+ node.inflight = 0;
358
+ node.status({ fill: "green", shape: "ring", text: "" });
359
+ done();
360
+ return;
361
+ }
362
+ if (node.inflight < node.burst) {
363
+ node.inflight += 1;
364
+ send(msg);
365
+ node.timers[node.inflight-1] = setTimeout(() => {
366
+ if (node.inflight == node.burst) {
367
+ node.status({ fill: "green", shape: "ring", text: "" });
368
+ }
369
+ node.inflight -= 1;
370
+ }, node.timer);
371
+ }
372
+ else {
373
+ if (node.outputs === 2) { send([null,msg]); }
374
+ node.status({ fill: "red", shape: "dot", text: "" });
375
+ }
376
+ done();
377
+ });
378
+ node.on("close", function() {
379
+ node.timers.forEach((t) => clearTimeout(t));
380
+ node.status({});
381
+ });
382
+ }
383
+
339
384
  // The topic based fair queue and last arrived on all topics queue
340
385
  else if ((node.pauseType === "queue") || (node.pauseType === "timed")) {
341
386
  node.intervalID = setInterval(function() {
@@ -105,18 +105,26 @@ module.exports = function(RED) {
105
105
  }
106
106
  node.activeProcesses[child.pid] = child;
107
107
  child.stdout.on('data', function (data) {
108
- if (node.activeProcesses.hasOwnProperty(child.pid) && node.activeProcesses[child.pid] !== null) {
109
- // console.log('[exec] stdout: ' + data,child.pid);
110
- if (isUtf8(data)) { msg.payload = data.toString(); }
111
- else { msg.payload = data; }
112
- nodeSend([RED.util.cloneMessage(msg),null,null]);
108
+ try {
109
+ if (node.activeProcesses.hasOwnProperty(child.pid) && node.activeProcesses[child.pid] !== null) {
110
+ // console.log('[exec] stdout: ' + data,child.pid);
111
+ if (isUtf8(data)) { msg.payload = data.toString(); }
112
+ else { msg.payload = data; }
113
+ nodeSend([RED.util.cloneMessage(msg),null,null]);
114
+ }
115
+ } catch (err) {
116
+ node.error(err.toString());
113
117
  }
114
118
  });
115
119
  child.stderr.on('data', function (data) {
116
- if (node.activeProcesses.hasOwnProperty(child.pid) && node.activeProcesses[child.pid] !== null) {
117
- if (isUtf8(data)) { msg.payload = data.toString(); }
118
- else { msg.payload = data; }
119
- nodeSend([null,RED.util.cloneMessage(msg),null]);
120
+ try {
121
+ if (node.activeProcesses.hasOwnProperty(child.pid) && node.activeProcesses[child.pid] !== null) {
122
+ if (isUtf8(data)) { msg.payload = data.toString(); }
123
+ else { msg.payload = data; }
124
+ nodeSend([null,RED.util.cloneMessage(msg),null]);
125
+ }
126
+ } catch (err) {
127
+ node.error(err.toString());
120
128
  }
121
129
  });
122
130
  child.on('close', function (code,signal) {
@@ -15,17 +15,18 @@
15
15
  -->
16
16
 
17
17
  <script type="text/html" data-template-name="tls-config">
18
- <div class="form-row" class="hide" id="node-config-row-uselocalfiles">
19
- <input type="checkbox" id="node-config-input-uselocalfiles" style="display: inline-block; width: auto; vertical-align: top;">
20
- <label for="node-config-input-uselocalfiles" style="width: 70%;"><span data-i18n="tls.label.use-local-files"></label>
21
- </div>
22
18
  <div class="form-row">
23
19
  <label style="width: 120px;" for="node-config-input-certType"><i class="fa fa-bars"></i> <span data-i18n="tls.label.certtype"></label>
24
20
  <select id="node-config-input-certType">
25
21
  <option value="files" data-i18n="tls.label.files"></option>
26
22
  <option value="pfx" data-i18n="tls.label.pfx"></option>
23
+ <option value="env" data-i18n="tls.label.env"></option>
27
24
  </select>
28
25
  </div>
26
+ <div class="form-row" class="hide" id="node-config-row-uselocalfiles">
27
+ <input type="checkbox" id="node-config-input-uselocalfiles" style="display: inline-block; width: auto; vertical-align: top;">
28
+ <label for="node-config-input-uselocalfiles" style="width: 70%;"><span data-i18n="tls.label.use-local-files"></label>
29
+ </div>
29
30
  <div class="form-row" id="node-tls-conf-cer">
30
31
  <label style="width: 120px;"><i class="fa fa-file-text-o"></i> <span data-i18n="tls.label.cert"></span></label>
31
32
  <span class="tls-config-input-data">
@@ -38,6 +39,10 @@
38
39
  <input type="hidden" id="node-config-input-certdata">
39
40
  <input class="hide tls-config-input-path" style="width: calc(100% - 170px);" type="text" id="node-config-input-cert" data-i18n="[placeholder]tls.placeholder.cert">
40
41
  </div>
42
+ <div class="form-row" id="node-tls-conf-cer-env">
43
+ <label style="width: 120px;"><i class="fa fa-file-text-o"></i> <span data-i18n="tls.label.cert"></span></label>
44
+ <input type="text" id="node-config-input-certEnv">
45
+ </div>
41
46
  <div class="form-row" id="node-tls-conf-key">
42
47
  <label style="width: 120px;" for="node-config-input-key"><i class="fa fa-file-text-o"></i> <span data-i18n="tls.label.key"></span></label>
43
48
  <span class="tls-config-input-data">
@@ -50,6 +55,10 @@
50
55
  <input type="hidden" id="node-config-input-keydata">
51
56
  <input class="hide tls-config-input-path" style="width: calc(100% - 170px);" type="text" id="node-config-input-key" data-i18n="[placeholder]tls.placeholder.key">
52
57
  </div>
58
+ <div class="form-row" id="node-tls-conf-key-env">
59
+ <label style="width: 120px;"><i class="fa fa-file-text-o"></i> <span data-i18n="tls.label.key"></span></label>
60
+ <input type="text" id="node-config-input-keyEnv">
61
+ </div>
53
62
  <div class="form-row" id="node-tls-conf-p12">
54
63
  <label style="width: 120px;" for="node-config-input-p12"><i class="fa fa-file-text-o"></i> <span data-i18n="tls.label.p12"></span></label>
55
64
  <span class="tls-config-input-data">
@@ -78,6 +87,10 @@
78
87
  <input type="hidden" id="node-config-input-cadata">
79
88
  <input class="hide tls-config-input-path" style="width: calc(100% - 170px);" type="text" id="node-config-input-ca" data-i18n="[placeholder]tls.placeholder.ca">
80
89
  </div>
90
+ <div class="form-row" id="node-tls-conf-ca-env">
91
+ <label style="width: 120px;"><i class="fa fa-file-text-o"></i> <span data-i18n="tls.label.ca"></span></label>
92
+ <input type="text" id="node-config-input-caEnv">
93
+ </div>
81
94
  <div class="form-row">
82
95
  <input type="checkbox" id="node-config-input-verifyservercert" style="display: inline-block; width: auto; vertical-align: top;">
83
96
  <label for="node-config-input-verifyservercert" style="width: calc(100% - 170px);" data-i18n="tls.label.verify-server-cert"></label>
@@ -131,7 +144,10 @@
131
144
  caname: {value:""},
132
145
  servername: {value:""},
133
146
  verifyservercert: {value: true},
134
- alpnprotocol: {value: ""}
147
+ alpnprotocol: {value: ""},
148
+ certEnv: {value: ""},
149
+ keyEnv: {value: ""},
150
+ caEnv: {value: ""}
135
151
  },
136
152
  credentials: {
137
153
  certdata: {type:"text"},
@@ -152,15 +168,45 @@
152
168
  $("#node-tls-conf-cer").hide();
153
169
  $("#node-tls-conf-key").hide();
154
170
  $("#node-tls-conf-ca").hide();
171
+ $("#node-tls-conf-cer-env").hide();
172
+ $("#node-tls-conf-key-env").hide();
173
+ $("#node-tls-conf-ca-env").hide();
155
174
  $("#node-tls-conf-p12").show();
175
+ $("#node-config-row-uselocalfiles").show();
156
176
  }
157
- else {
177
+ else if ($("#node-config-input-certType").val() === "env") {
178
+ $("#node-tls-conf-cer").hide();
179
+ $("#node-tls-conf-key").hide();
180
+ $("#node-tls-conf-ca").hide();
181
+ $("#node-tls-conf-cer-env").show();
182
+ $("#node-tls-conf-key-env").show();
183
+ $("#node-tls-conf-ca-env").show();
184
+ $("#node-tls-conf-p12").hide();
185
+ $("#node-config-row-uselocalfiles").hide();
186
+ } else {
158
187
  $("#node-tls-conf-cer").show();
159
188
  $("#node-tls-conf-key").show();
160
189
  $("#node-tls-conf-ca").show();
190
+ $("#node-tls-conf-cer-env").hide();
191
+ $("#node-tls-conf-key-env").hide();
192
+ $("#node-tls-conf-ca-env").hide();
161
193
  $("#node-tls-conf-p12").hide();
194
+ $("#node-config-row-uselocalfiles").show();
162
195
  }
163
196
  });
197
+
198
+ $("#node-config-input-certEnv").typedInput({
199
+ type:"env",
200
+ types:["env"]
201
+ });
202
+ $("#node-config-input-keyEnv").typedInput({
203
+ type:"env",
204
+ types:["env"]
205
+ });
206
+ $("#node-config-input-caEnv").typedInput({
207
+ type:"env",
208
+ types:["env"]
209
+ });
164
210
 
165
211
  function updateFileUpload() {
166
212
  if ($("#node-config-input-uselocalfiles").is(':checked')) {
@@ -218,15 +264,19 @@
218
264
  $("#tls-config-button-cert-clear").on("click", function() {
219
265
  clearNameData("cert");
220
266
  });
267
+ RED.popover.tooltip($("#tls-config-button-cert-clear"), RED._("common.label.delete"));
221
268
  $("#tls-config-button-key-clear").on("click", function() {
222
269
  clearNameData("key");
223
270
  });
271
+ RED.popover.tooltip($("#tls-config-button-key-clear"), RED._("common.label.delete"));
224
272
  $("#tls-config-button-ca-clear").on("click", function() {
225
273
  clearNameData("ca");
226
274
  });
275
+ RED.popover.tooltip($("#tls-config-button-ca-clear"), RED._("common.label.delete"));
227
276
  $("#tls-config-button-p12-clear").on("click", function() {
228
277
  clearNameData("p12");
229
278
  });
279
+ RED.popover.tooltip($("#tls-config-button-p12-clear"), RED._("common.label.delete"));
230
280
 
231
281
  if (RED.settings.tlsConfigDisableLocalFiles) {
232
282
  $("#node-config-row-uselocalfiles").hide();
@@ -23,10 +23,14 @@ module.exports = function(RED) {
23
23
  this.valid = true;
24
24
  this.verifyservercert = n.verifyservercert;
25
25
  var certPath, keyPath, caPath, p12Path;
26
+ var certEnv, keyEnv, caEnv;
26
27
  if (n.cert) { certPath = n.cert.trim(); }
27
28
  if (n.key) { keyPath = n.key.trim(); }
28
29
  if (n.ca) { caPath = n.ca.trim(); }
29
30
  if (n.p12) { p12Path = n.p12.trim(); }
31
+ if (n.certEnv) { certEnv = n.certEnv }
32
+ if (n.keyEnv) { keyEnv = n.keyEnv }
33
+ if (n.caEnv) { caEnv = n.caEnv }
30
34
  this.certType = n.certType || "files";
31
35
  this.servername = (n.servername||"").trim();
32
36
  this.alpnprotocol = (n.alpnprotocol||"").trim();
@@ -41,13 +45,23 @@ module.exports = function(RED) {
41
45
  return;
42
46
  }
43
47
  }
48
+ else if (this.certType === "env") {
49
+ if (certEnv) {
50
+ this.certEnv = Buffer.from(RED.util.evaluateNodeProperty(certEnv, 'env', this))
51
+ }
52
+ if (keyEnv) {
53
+ this.keyEnv = Buffer.from(RED.util.evaluateNodeProperty(keyEnv, 'env', this))
54
+ }
55
+ if (caEnv) {
56
+ this.caEnv = Buffer.from(RED.util.evaluateNodeProperty(caEnv, 'env', this))
57
+ }
58
+ }
44
59
  else if ((certPath && certPath.length > 0) || (keyPath && keyPath.length > 0) || (caPath && caPath.length > 0)) {
45
60
  if ( (certPath && certPath.length > 0) !== (keyPath && keyPath.length > 0)) {
46
61
  this.valid = false;
47
62
  this.error(RED._("tls.error.missing-file"));
48
63
  return;
49
64
  }
50
-
51
65
  try {
52
66
  if (certPath) {
53
67
  this.cert = fs.readFileSync(certPath);
@@ -122,6 +136,17 @@ module.exports = function(RED) {
122
136
  opts.ca = this.ca;
123
137
  }
124
138
  }
139
+ else if (this.certType === "env") {
140
+ if (this.keyEnv) {
141
+ opts.key = this.keyEnv
142
+ }
143
+ if (this.certEnv) {
144
+ opts.cert= this.certEnv
145
+ }
146
+ if (this.caEnv) {
147
+ opts.ca = this.caEnv
148
+ }
149
+ }
125
150
  else {
126
151
  if (this.pfx) {
127
152
  opts.pfx = this.pfx;
@@ -227,6 +227,7 @@ module.exports = function(RED) {
227
227
  * Handle the payload / packet recieved in MQTT In and MQTT Sub nodes
228
228
  */
229
229
  function subscriptionHandler(node, datatype ,topic, payload, packet) {
230
+ if (!packet) { packet = {}; }
230
231
  const msg = {topic:topic, payload:null, qos:packet.qos, retain:packet.retain};
231
232
  const v5 = (node && node.brokerConn)
232
233
  ? node.brokerConn.v5()
@@ -1074,12 +1075,16 @@ module.exports = function(RED) {
1074
1075
 
1075
1076
  if (!subscription.handler) {
1076
1077
  subscription.handler = function (mtopic, mpayload, mpacket) {
1077
- const sops = subscription.options ? subscription.options.properties : {}
1078
- const pops = mpacket.properties || {}
1079
- if (subIdsAvailable && pops.subscriptionIdentifier && sops.subscriptionIdentifier && (pops.subscriptionIdentifier !== sops.subscriptionIdentifier)) {
1080
- //do nothing as subscriptionIdentifier does not match
1081
- } else if (matchTopic(topic, mtopic)) {
1082
- subscription.callback && subscription.callback(mtopic, mpayload, mpacket)
1078
+ try {
1079
+ const sops = subscription.options ? subscription.options.properties : {}
1080
+ const pops = (mpacket && mpacket.properties) || {}
1081
+ if (subIdsAvailable && pops.subscriptionIdentifier && sops.subscriptionIdentifier && (pops.subscriptionIdentifier !== sops.subscriptionIdentifier)) {
1082
+ //do nothing as subscriptionIdentifier does not match
1083
+ } else if (matchTopic(topic, mtopic)) {
1084
+ subscription.callback && subscription.callback(mtopic, mpayload, mpacket)
1085
+ }
1086
+ } catch (err) {
1087
+ node.error("MQTT subscription handler error: " + err.toString());
1083
1088
  }
1084
1089
  }
1085
1090
  }