node-red-contrib-aedes 0.14.0 → 0.15.0

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/CHANGELOG.md CHANGED
@@ -1,5 +1,9 @@
1
1
  # node-red-contrib-aedes Changelog
2
2
 
3
+ ## Jul 22, 2025, Version 0.15.0
4
+ ### Notable changes
5
+ - TLS configuration with local filess
6
+
3
7
  ## Jul 02, 2025, Version 0.14.0
4
8
  ### Breaking changes
5
9
  - Dependencies require NodeJS 20
package/aedes.html CHANGED
@@ -14,89 +14,142 @@
14
14
  limitations under the License.
15
15
  -->
16
16
 
17
- <script type="text/x-red" data-template-name="aedes broker">
18
- <div class="form-row">
19
- <label for="node-input-name"><i class="fa fa-tag"></i> <span data-i18n="node-red:common.label.name"></span></label>
20
- <input type="text" id="node-input-name" data-i18n="[placeholder]node-red:common.label.name">
21
- </div>
22
- <div class="form-row">
23
- <ul style="min-width: 600px; margin-bottom: 20px;" id="node-config-aedes-broker-tabs"></ul>
24
- </div>
25
- <div id="node-config-aedes-broker-tabs-content" style="min-height:150px;">
26
- <div id="aedes-broker-tab-connection" style="display:none">
27
- <div class="form-row">
28
- <label for="node-input-mqtt_port"><i class="fa fa-globe"></i> <span data-i18n="aedes-mqtt-broker.label.mqtt_port"></span></label>
29
- <input type="text" id="node-input-mqtt_port" data-i18n="[placeholder]aedes-mqtt-broker.placeholder.mqtt_port">
30
- </div>
31
- <div class="form-row">
32
- <label for="node-input-mqtt_ws_bind"><i class="fa fa-globe"></i> WS bind</label>
33
- <select id="node-input-mqtt_ws_bind" type="text" style="width:30%" text-center>
34
- <option value="path" default>path</option>
35
- <option value="port">port</option>
36
- </select>
37
- </div>
38
- <div class="form-row" id="node-input-mqtt_ws_path-label">
39
- <label for="node-input-mqtt_ws_path"><i class="fa fa-globe"></i> <span data-i18n="aedes-mqtt-broker.label.mqtt_ws_path"></label>
40
- <input type="text" id="node-input-mqtt_ws_path" data-i18n="[placeholder]aedes-mqtt-broker.placeholder.mqtt_ws_path">
41
- </div>
42
- <div class="form-row" id="node-input-mqtt_ws_port-label">
43
- <label for="node-input-mqtt_ws_port"><i class="fa fa-globe"></i> <span data-i18n="aedes-mqtt-broker.label.mqtt_ws_port"></label>
44
- <input type="text" id="node-input-mqtt_ws_port" data-i18n="[placeholder]aedes-mqtt-broker.placeholder.mqtt_ws_port">
45
- </div>
46
- <div class="form-row">
47
- <input type="checkbox" id="node-input-usetls" style="display: inline-block; width: auto; vertical-align: top;">
48
- <label for="node-input-usetls" style="width: auto" data-i18n="aedes-mqtt-broker.label.use-tls"></label>
49
- <div id="node-input-tls" class="hide">
50
- <div class="form-row">
51
- <label style="width: 120px;"><i class="fa fa-file-text-o"></i> <span data-i18n="aedes-mqtt-broker.label.cert"></span></label>
52
- <span class="tls-input-data">
53
- <label class="red-ui-button" for="node-input-certfile"><i class="fa fa-upload"></i> <span data-i18n="aedes-mqtt-broker.label.upload"></span></label>
54
- <input class="hide" type="file" id="node-input-certfile">
55
- <span id="tls-certname" style="width: calc(100% - 280px); overflow: hidden; line-height:34px; height:34px; text-overflow: ellipsis; white-space: nowrap; display: inline-block; vertical-align: middle;"> </span>
56
- <button class="red-ui-button red-ui-button-small" id="tls-button-cert-clear" style="margin-left: 10px"><i class="fa fa-times"></i></button>
57
- </span>
58
- <input type="hidden" id="node-input-certname">
59
- <input type="hidden" id="node-input-certdata">
60
- <input class="hide tls-input-path" style="width: calc(100% - 170px);" type="text" id="node-input-cert" data-i18n="[placeholder]aedes-mqtt-broker.placeholder.cert">
61
- </div>
62
- <div class="form-row">
63
- <label style="width: 120px;" for="node-input-key"><i class="fa fa-file-text-o"></i> <span data-i18n="aedes-mqtt-broker.label.key"></span></label>
64
- <span class="tls-input-data">
65
- <label class="red-ui-button" for="node-input-keyfile"><i class="fa fa-upload"></i> <span data-i18n="aedes-mqtt-broker.label.upload"></span></label>
66
- <input class="hide" type="file" id="node-input-keyfile">
67
- <span id="tls-keyname" style="width: calc(100% - 280px); overflow: hidden; line-height:34px; height:34px; text-overflow: ellipsis; white-space: nowrap; display: inline-block; vertical-align: middle;"> </span>
68
- <button class="red-ui-button red-ui-button-small" id="tls-button-key-clear" style="margin-left: 10px"><i class="fa fa-times"></i></button>
69
- </span>
70
- <input type="hidden" id="node-input-keyname">
71
- <input type="hidden" id="node-input-keydata">
72
- <input class="hide tls-input-path" style="width: calc(100% - 170px);" type="text" id="node-input-key" data-i18n="[placeholder]tls.placeholder.key">
73
- </div>
74
- </div>
75
- </div>
76
- <div class="form-row">
77
- <label for="node-input-persistence_bind"><i class="fa fa-globe"></i> <span data-i18n="aedes-mqtt-broker.label.persistence_bind"></label>
17
+ <script type="text/html" data-template-name="aedes broker">
18
+ <div class="form-row">
19
+ <label for="node-input-name"><i class="fa fa-tag"></i> <span data-i18n="node-red:common.label.name"></span></label>
20
+ <input type="text" id="node-input-name" data-i18n="[placeholder]node-red:common.label.name">
21
+ </div>
22
+ <div class="form-row">
23
+ <ul style="min-width: 600px; margin-bottom: 20px;" id="node-config-aedes-broker-tabs"></ul>
24
+ </div>
25
+ <div id="node-config-aedes-broker-tabs-content" style="min-height:150px;">
26
+ <div id="aedes-broker-tab-connection" style="display:none">
27
+ <div class="form-row">
28
+ <label for="node-input-mqtt_port"><i class="fa fa-globe"></i> <span
29
+ data-i18n="aedes-mqtt-broker.label.mqtt_port"></span></label>
30
+ <input type="text" id="node-input-mqtt_port"
31
+ data-i18n="[placeholder]aedes-mqtt-broker.placeholder.mqtt_port">
32
+ </div>
33
+ <div class="form-row">
34
+ <label for="node-input-mqtt_ws_bind"><i class="fa fa-globe"></i> WS bind</label>
35
+ <select id="node-input-mqtt_ws_bind" type="text" style="width:30%" text-center>
36
+ <option value="path" default>path</option>
37
+ <option value="port">port</option>
38
+ </select>
39
+ </div>
40
+ <div class="form-row" id="node-input-mqtt_ws_path-label">
41
+ <label for="node-input-mqtt_ws_path"><i class="fa fa-globe"></i> <span
42
+ data-i18n="aedes-mqtt-broker.label.mqtt_ws_path"></label>
43
+ <input type="text" id="node-input-mqtt_ws_path"
44
+ data-i18n="[placeholder]aedes-mqtt-broker.placeholder.mqtt_ws_path">
45
+ </div>
46
+ <div class="form-row" id="node-input-mqtt_ws_port-label">
47
+ <label for="node-input-mqtt_ws_port"><i class="fa fa-globe"></i> <span
48
+ data-i18n="aedes-mqtt-broker.label.mqtt_ws_port"></label>
49
+ <input type="text" id="node-input-mqtt_ws_port"
50
+ data-i18n="[placeholder]aedes-mqtt-broker.placeholder.mqtt_ws_port">
51
+ </div>
52
+ <div class="form-row">
53
+ <input type="checkbox" id="node-input-usetls"
54
+ style="display: inline-block; width: auto; vertical-align: top;">
55
+ <label for="node-input-usetls" style="width: auto" data-i18n="aedes-mqtt-broker.label.use-tls"></label>
56
+ <div id="node-input-tls" class="hide">
57
+ <div class="form-row" id="node-row-uselocalfiles">
58
+ <input type="checkbox" id="node-input-uselocalfiles"
59
+ style="display: inline-block; width: auto; vertical-align: top;">
60
+ <label for="node-input-uselocalfiles" style="width: 70%;"><span
61
+ data-i18n="aedes-mqtt-broker.label.use-local-files"></label>
62
+ </div>
63
+
64
+ <div class="form-row">
65
+ <label style="width: 120px;"><i class="fa fa-file-text-o"></i> <span
66
+ data-i18n="aedes-mqtt-broker.label.cert"></span></label>
67
+ <span class="tls-input-data">
68
+ <label class="red-ui-button" for="node-input-certfile"><i class="fa fa-upload"></i> <span
69
+ data-i18n="aedes-mqtt-broker.label.upload"></span></label>
70
+ <input class="hide" type="file" id="node-input-certfile">
71
+ <span id="tls-certname"
72
+ style="width: calc(100% - 280px); overflow: hidden; line-height:34px; height:34px; text-overflow: ellipsis; white-space: nowrap; display: inline-block; vertical-align: middle;">
73
+ </span>
74
+ <button type="button" class="red-ui-button red-ui-button-small" id="tls-button-cert-clear"
75
+ style="margin-left: 10px"><i class="fa fa-times"></i></button>
76
+ </span>
77
+ <input type="hidden" id="node-input-certname">
78
+ <input type="hidden" id="node-input-certdata">
79
+ <input class="hide tls-input-path" style="width: calc(100% - 170px);" type="text"
80
+ id="node-input-cert" data-i18n="[placeholder]aedes-mqtt-broker.placeholder.cert">
81
+ </div>
82
+
83
+ <div class="form-row">
84
+ <label style="width: 120px;" for="node-input-key"><i class="fa fa-file-text-o"></i> <span
85
+ data-i18n="aedes-mqtt-broker.label.key"></span></label>
86
+ <span class="tls-input-data">
87
+ <label class="red-ui-button" for="node-input-keyfile"><i class="fa fa-upload"></i> <span
88
+ data-i18n="aedes-mqtt-broker.label.upload"></span></label>
89
+ <input class="hide" type="file" id="node-input-keyfile">
90
+ <span id="tls-keyname"
91
+ style="width: calc(100% - 280px); overflow: hidden; line-height:34px; height:34px; text-overflow: ellipsis; white-space: nowrap; display: inline-block; vertical-align: middle;">
92
+ </span>
93
+ <button type="button" class="red-ui-button red-ui-button-small" id="tls-button-key-clear"
94
+ style="margin-left: 10px"><i class="fa fa-times"></i></button>
95
+ </span>
96
+ <input type="hidden" id="node-input-keyname">
97
+ <input type="hidden" id="node-input-keydata">
98
+ <input class="hide tls-input-path" style="width: calc(100% - 170px);" type="text"
99
+ id="node-input-key" data-i18n="[placeholder]aedes-mqtt-broker.placeholder.key">
100
+ </div>
101
+
102
+ <div class="form-row">
103
+ <label style="width: 120px;" for="node-input-ca"><i class="fa fa-file-text-o"></i> <span
104
+ data-i18n="aedes-mqtt-broker.label.ca"></span></label>
105
+ <span class="tls-input-data">
106
+ <label class="red-ui-button" for="node-input-cafile"><i class="fa fa-upload"></i> <span
107
+ data-i18n="aedes-mqtt-broker.label.upload"></span></label>
108
+ <input class="hide" type="file" title=" " id="node-input-cafile">
109
+ <span id="tls-caname"
110
+ style="width: calc(100% - 280px); overflow: hidden; line-height:34px; height:34px; text-overflow: ellipsis; white-space: nowrap; display: inline-block; vertical-align: middle;">
111
+ </span>
112
+ <button type="button" class="red-ui-button red-ui-button-small" id="tls-button-ca-clear"
113
+ style="margin-left: 10px"><i class="fa fa-times"></i></button>
114
+ </span>
115
+ <input type="hidden" id="node-input-caname">
116
+ <input type="hidden" id="node-input-cadata">
117
+ <input class="hide tls-input-path" style="width: calc(100% - 170px);" type="text" id="node-input-ca"
118
+ data-i18n="[placeholder]aedes-mqtt-broker.placeholder.ca">
119
+ </div>
120
+ </div>
121
+ </div>
122
+ </div>
123
+ <div id="aedes-broker-tab-persistence" style="display:none">
124
+ <div class="form-row">
125
+ <label for="node-input-persistence_bind"><i class="fa fa-globe"></i> <span
126
+ data-i18n="aedes-mqtt-broker.label.persistence_bind"></label>
78
127
  <select id="node-input-persistence_bind" type="text" style="width:30%" text-center>
79
128
  <option value="memory" data-i18n="aedes-mqtt-broker.label.persistence_memory" default></option>
80
129
  <option value="mongodb" data-i18n="aedes-mqtt-broker.label.persistence_mongodb"></option>
81
- <option value="level" data-i18n="aedes-mqtt-broker.label.persistence_level"></option>
130
+ <!-- <option value="level" data-i18n="aedes-mqtt-broker.label.persistence_level"></option> -->
82
131
  </select>
83
132
  </div>
84
- <div class="form-row" id="node-row-dburl" class="hide">
85
- <label for="node-input-dburl"><i class="fa fa-database"></i> <span data-i18n="aedes-mqtt-broker.label.dburl"></label>
86
- <input type="text" id="node-input-dburl" data-i18n="[placeholder]aedes-mqtt-broker.placeholder.dburl">
87
- </div>
88
- </div>
89
- <div id="aedes-broker-tab-security" style="display:none">
90
- <div class="form-row">
91
- <label for="node-input-username"><i class="fa fa-user"></i> <span data-i18n="node-red:common.label.username"></span></label>
92
- <input type="text" id="node-input-username" data-i18n="[placeholder]aedes-mqtt-broker.placeholder.username">
93
- </div>
94
- <div class="form-row">
95
- <label for="node-input-password"><i class="fa fa-lock"></i> <span data-i18n="node-red:common.label.password"></span></label>
96
- <input type="password" id="node-input-password" data-i18n="[placeholder]aedes-mqtt-broker.placeholder.password">
97
- </div>
98
- </div>
99
- </div>
133
+ <div class="form-row" id="node-row-dburl" class="hide">
134
+ <label for="node-input-dburl"><i class="fa fa-database"></i> <span
135
+ data-i18n="aedes-mqtt-broker.label.dburl"></label>
136
+ <input type="text" id="node-input-dburl" data-i18n="[placeholder]aedes-mqtt-broker.placeholder.dburl">
137
+ </div>
138
+ </div>
139
+ <div id="aedes-broker-tab-security" style="display:none">
140
+ <div class="form-row">
141
+ <label for="node-input-username"><i class="fa fa-user"></i> <span
142
+ data-i18n="node-red:common.label.username"></span></label>
143
+ <input type="text" id="node-input-username" data-i18n="[placeholder]aedes-mqtt-broker.placeholder.username">
144
+ </div>
145
+ <div class="form-row">
146
+ <label for="node-input-password"><i class="fa fa-lock"></i> <span
147
+ data-i18n="node-red:common.label.password"></span></label>
148
+ <input type="password" id="node-input-password"
149
+ data-i18n="[placeholder]aedes-mqtt-broker.placeholder.password">
150
+ </div>
151
+ </div>
152
+ </div>
100
153
  </script>
101
154
 
102
155
  <script type="text/javascript">
@@ -128,8 +181,10 @@
128
181
  return currentCert === '' || v !== '';
129
182
  }
130
183
  },
184
+ ca: {value:""},
131
185
  certname: { value: '' },
132
186
  keyname: { value: '' },
187
+ caname: {value:""},
133
188
  persistence_bind: { value: 'memory', required: true },
134
189
  dburl: { value: '', required: false },
135
190
  usetls: { value: false }
@@ -138,7 +193,8 @@
138
193
  username: { type: 'text' },
139
194
  password: { type: 'password' },
140
195
  certdata: { type: 'text' },
141
- keydata: { type: 'text' }
196
+ keydata: { type: 'text' },
197
+ cadata: {type:"text"}
142
198
  },
143
199
  color: '#d8bfd8',
144
200
  inputs: 0,
@@ -163,6 +219,10 @@
163
219
  id: 'aedes-broker-tab-connection',
164
220
  label: this._('aedes-mqtt-broker.tabs-label.connection')
165
221
  });
222
+ tabs.addTab({
223
+ id: 'aedes-broker-tab-persistence',
224
+ label: this._('aedes-mqtt-broker.tabs-label.persistence')
225
+ })
166
226
  tabs.addTab({
167
227
  id: 'aedes-broker-tab-security',
168
228
  label: this._('aedes-mqtt-broker.tabs-label.security')
@@ -226,7 +286,7 @@
226
286
  this.usetls = false;
227
287
  $('#node-input-usetls').prop('checked', false);
228
288
  }
229
- function updateTLSOptions () {
289
+ function updateTLSOptions() {
230
290
  if ($('#node-input-usetls').is(':checked')) {
231
291
  $('#node-input-tls').show();
232
292
  } else {
@@ -257,6 +317,9 @@
257
317
  $('#node-input-keyfile').on('change', function () {
258
318
  saveFile('key', this.files[0]);
259
319
  });
320
+ $("#node-input-cafile" ).on("change", function() {
321
+ saveFile("ca", this.files[0]);
322
+ });
260
323
  function clearNameData(prop) {
261
324
  $('#tls-' + prop + 'name').text('');
262
325
  $('#node-input-' + prop + 'data').val('');
@@ -268,10 +331,24 @@
268
331
  $('#tls-button-key-clear').on('click', function () {
269
332
  clearNameData('key');
270
333
  });
334
+ $("#tls-config-button-ca-clear").on("click", function() {
335
+ clearNameData("ca");
336
+ });
271
337
 
272
338
  function updateFileUpload() {
273
- $('.tls-input-data').show();
274
- $('.tls-input-path').hide();
339
+ if ($("#node-input-uselocalfiles").is(':checked')) {
340
+ $(".tls-input-path").show();
341
+ $(".tls-input-data").hide();
342
+ } else {
343
+ $(".tls-input-data").show();
344
+ $(".tls-input-path").hide();
345
+ }
346
+ }
347
+ $("#node-input-uselocalfiles").on("click",function() {
348
+ updateFileUpload();
349
+ });
350
+ if(this.cert || this.key || this.ca) {
351
+ $("#node-input-uselocalfiles").prop('checked',true);
275
352
  }
276
353
  $('#tls-certname').text(this.certname);
277
354
  $('#tls-keyname').text(this.keyname);
@@ -282,8 +359,15 @@
282
359
  if (!$('#node-input-usetls').is(':checked')) {
283
360
  $('#node-input-tls').val('');
284
361
  }
285
- $('#node-input-cert').val('');
286
- $('#node-input-key').val('');
362
+ if ($("#node-input-uselocalfiles").is(':checked')) {
363
+ clearNameData("ca");
364
+ clearNameData("cert");
365
+ clearNameData("key");
366
+ } else {
367
+ $("#node-input-ca").val("");
368
+ $("#node-input-cert").val("");
369
+ $("#node-input-key").val("");
370
+ }
287
371
  }
288
372
  });
289
- </script>
373
+ </script>
package/aedes.js CHANGED
@@ -17,11 +17,10 @@
17
17
  module.exports = function (RED) {
18
18
  'use strict';
19
19
  const MongoPersistence = require('aedes-persistence-mongodb');
20
- /*
21
- const { Level } = require('level');
22
- const LevelPersistence = require('aedes-persistence-level');
23
- */
20
+ // const { Level } = require('level');
21
+ // const LevelPersistence = require('aedes-persistence-level');
24
22
  const aedes = require('aedes');
23
+ const fs = require('fs');
25
24
  const net = require('net');
26
25
  const tls = require('tls');
27
26
  const http = require('http');
@@ -30,7 +29,6 @@ module.exports = function (RED) {
30
29
 
31
30
  let serverUpgradeAdded = false;
32
31
  const listenerNodes = {};
33
- let db;
34
32
 
35
33
  /**
36
34
  * Handles a server upgrade.
@@ -42,9 +40,14 @@ module.exports = function (RED) {
42
40
  function handleServerUpgrade (request, socket, head) {
43
41
  const pathname = new URL(request.url, 'http://example.org').pathname;
44
42
  if (Object.prototype.hasOwnProperty.call(listenerNodes, pathname)) {
45
- listenerNodes[pathname].server.handleUpgrade(request, socket, head, function done (conn) {
46
- listenerNodes[pathname].server.emit('connection', conn, request);
47
- });
43
+ listenerNodes[pathname].server.handleUpgrade(
44
+ request,
45
+ socket,
46
+ head,
47
+ function done (conn) {
48
+ listenerNodes[pathname].server.emit('connection', conn, request);
49
+ }
50
+ );
48
51
  }
49
52
  }
50
53
 
@@ -56,17 +59,50 @@ module.exports = function (RED) {
56
59
  this.mqtt_ws_bind = config.mqtt_ws_bind;
57
60
  this.usetls = config.usetls;
58
61
 
62
+ const certPath = config.cert ? config.cert.trim() : '';
63
+ const keyPath = config.key ? config.key.trim() : '';
64
+ const caPath = config.ca ? config.ca.trim() : '';
65
+
66
+ this.uselocalfiles = config.uselocalfiles;
67
+ this.dburl = config.dburl;
68
+
59
69
  if (this.mqtt_ws_bind === 'path') {
60
70
  this.mqtt_ws_port = 0;
61
71
  } else {
62
72
  this.mqtt_ws_path = '';
63
73
  }
64
74
 
75
+ if (certPath.length > 0 || keyPath.length > 0 || caPath.length > 0) {
76
+ if ((certPath.length > 0) !== (keyPath.length > 0)) {
77
+ this.valid = false;
78
+ this.error(RED._('tls.error.missing-file'));
79
+ return;
80
+ }
81
+ try {
82
+ if (certPath) {
83
+ this.cert = fs.readFileSync(certPath);
84
+ }
85
+ if (keyPath) {
86
+ this.key = fs.readFileSync(keyPath);
87
+ }
88
+ if (caPath) {
89
+ this.ca = fs.readFileSync(caPath);
90
+ }
91
+ } catch (err) {
92
+ this.valid = false;
93
+ this.error(err.toString());
94
+ return;
95
+ }
96
+ } else {
97
+ if (this.credentials) {
98
+ this.cert = this.credentials.certdata || '';
99
+ this.key = this.credentials.keydata || '';
100
+ this.ca = this.credentials.cadata || '';
101
+ }
102
+ }
65
103
  if (this.credentials) {
66
104
  this.username = this.credentials.username;
67
105
  this.password = this.credentials.password;
68
- this.cert = this.credentials.certdata || '';
69
- this.key = this.credentials.keydata || '';
70
106
  }
71
107
 
72
108
  if (typeof this.usetls === 'undefined') {
@@ -78,29 +114,22 @@ module.exports = function (RED) {
78
114
  const aedesSettings = {};
79
115
  const serverOptions = {};
80
116
 
81
- if ((config.persistence_bind === 'mongodb') && config.dburl) {
117
+ if (config.persistence_bind === 'mongodb' && config.dburl) {
82
118
  aedesSettings.persistence = MongoPersistence({
83
119
  url: config.dburl
84
120
  });
85
121
  node.log('Start persistence to MongeDB');
86
- } else if (config.persistence_bind === 'level') {
87
122
  /*
88
- db = new Level('leveldb', { valueEncoding: 'json' });
89
- aedesSettings.persistence = LevelPersistence(db);
123
+ } else if (config.persistence_bind === 'level') {
124
+ aedesSettings.persistence = LevelPersistence(new Level('leveldb'));
90
125
  node.log('Start persistence to LevelDB');
91
- db.open(function (err) {
92
- if (err) {
93
- node.error('Error opening LevelDB: ' + err);
94
- } else {
95
- node.log('LevelDB successful opened');
96
- }
97
- });
98
126
  */
99
127
  }
100
128
 
101
- if ((this.cert) && (this.key) && (this.usetls)) {
129
+ if (this.cert && this.key && this.usetls) {
102
130
  serverOptions.cert = this.cert;
103
131
  serverOptions.key = this.key;
132
+ serverOptions.ca = this.ca;
104
133
  }
105
134
 
106
135
  const broker = aedes.createBroker(aedesSettings);
@@ -119,9 +148,16 @@ module.exports = function (RED) {
119
148
  const testServer = net.createServer();
120
149
  testServer.once('error', function (err) {
121
150
  if (err.code === 'EADDRINUSE') {
122
- node.error('Error: Port ' + config.mqtt_ws_port + ' is already in use');
151
+ node.error(
152
+ 'Error: Port ' + config.mqtt_ws_port + ' is already in use'
153
+ );
123
154
  } else {
124
- node.error('Error creating net server on port ' + config.mqtt_ws_port + ', ' + err.toString());
155
+ node.error(
156
+ 'Error creating net server on port ' +
157
+ config.mqtt_ws_port +
158
+ ', ' +
159
+ err.toString()
160
+ );
125
161
  }
126
162
  });
127
163
  testServer.once('listening', function () {
@@ -134,11 +170,16 @@ module.exports = function (RED) {
134
170
  } else {
135
171
  httpServer = http.createServer();
136
172
  }
137
- wss = ws.createServer({
138
- server: httpServer
139
- }, broker.handle);
173
+ wss = ws.createServer(
174
+ {
175
+ server: httpServer
176
+ },
177
+ broker.handle
178
+ );
140
179
  httpServer.listen(config.mqtt_ws_port, function () {
141
- node.log('Binding aedes mqtt server on ws port: ' + config.mqtt_ws_port);
180
+ node.log(
181
+ 'Binding aedes mqtt server on ws port: ' + config.mqtt_ws_port
182
+ );
142
183
  });
143
184
  });
144
185
  testServer.listen(config.mqtt_ws_port, function () {
@@ -153,11 +194,18 @@ module.exports = function (RED) {
153
194
  }
154
195
 
155
196
  let path = RED.settings.httpNodeRoot || '/';
156
- path = path + (path.slice(-1) === '/' ? '' : '/') + (node.mqtt_ws_path.charAt(0) === '/' ? node.mqtt_ws_path.substring(1) : node.mqtt_ws_path);
197
+ path =
198
+ path +
199
+ (path.slice(-1) === '/' ? '' : '/') +
200
+ (node.mqtt_ws_path.charAt(0) === '/'
201
+ ? node.mqtt_ws_path.substring(1)
202
+ : node.mqtt_ws_path);
157
203
  node.fullPath = path;
158
204
 
159
205
  if (Object.prototype.hasOwnProperty.call(listenerNodes, path)) {
160
- node.error(RED._('websocket.errors.duplicate-path', { path: node.mqtt_ws_path }));
206
+ node.error(
207
+ RED._('websocket.errors.duplicate-path', { path: node.mqtt_ws_path })
208
+ );
161
209
  return;
162
210
  }
163
211
  listenerNodes[node.fullPath] = node;
@@ -168,9 +216,12 @@ module.exports = function (RED) {
168
216
  serverOptions_.verifyClient = RED.settings.webSocketNodeVerifyClient;
169
217
  }
170
218
 
171
- node.server = ws.createServer({
172
- noServer: true
173
- }, broker.handle);
219
+ node.server = ws.createServer(
220
+ {
221
+ noServer: true
222
+ },
223
+ broker.handle
224
+ );
174
225
 
175
226
  node.log('Binding aedes mqtt server on ws path: ' + node.fullPath);
176
227
  }
@@ -178,23 +229,38 @@ module.exports = function (RED) {
178
229
  server.once('error', function (err) {
179
230
  if (err.code === 'EADDRINUSE') {
180
231
  node.error('Error: Port ' + config.mqtt_port + ' is already in use');
181
- node.status({ fill: 'red', shape: 'ring', text: 'node-red:common.status.disconnected' });
232
+ node.status({
233
+ fill: 'red',
234
+ shape: 'ring',
235
+ text: 'node-red:common.status.disconnected'
236
+ });
182
237
  } else {
183
238
  node.error('Error: Port ' + config.mqtt_port + ' ' + err.toString());
184
- node.status({ fill: 'red', shape: 'ring', text: 'node-red:common.status.disconnected' });
239
+ node.status({
240
+ fill: 'red',
241
+ shape: 'ring',
242
+ text: 'node-red:common.status.disconnected'
243
+ });
185
244
  }
186
245
  });
187
246
 
188
247
  if (this.mqtt_port) {
189
248
  server.listen(this.mqtt_port, function () {
190
249
  node.log('Binding aedes mqtt server on port: ' + config.mqtt_port);
191
- node.status({ fill: 'green', shape: 'dot', text: 'node-red:common.status.connected' });
250
+ node.status({
251
+ fill: 'green',
252
+ shape: 'dot',
253
+ text: 'node-red:common.status.connected'
254
+ });
192
255
  });
193
256
  }
194
257
 
195
258
  if (this.credentials && this.username && this.password) {
196
259
  broker.authenticate = function (client, username, password, callback) {
197
- const authorized = (username === node.username && password && password.toString() === node.password);
260
+ const authorized =
261
+ username === node.username &&
262
+ password &&
263
+ password.toString() === node.password;
198
264
  if (authorized) {
199
265
  client.user = username;
200
266
  }
@@ -222,7 +288,9 @@ module.exports = function (RED) {
222
288
  node.status({
223
289
  fill: 'green',
224
290
  shape: 'dot',
225
- text: RED._('aedes-mqtt-broker.status.connected', { count: broker.connectedClients })
291
+ text: RED._('aedes-mqtt-broker.status.connected', {
292
+ count: broker.connectedClients
293
+ })
226
294
  });
227
295
  node.send([msg, null]);
228
296
  });
@@ -238,7 +306,9 @@ module.exports = function (RED) {
238
306
  node.status({
239
307
  fill: 'green',
240
308
  shape: 'dot',
241
- text: RED._('aedes-mqtt-broker.status.connected', { count: broker.connectedClients })
309
+ text: RED._('aedes-mqtt-broker.status.connected', {
310
+ count: broker.connectedClients
311
+ })
242
312
  });
243
313
  });
244
314
 
@@ -254,7 +324,9 @@ module.exports = function (RED) {
254
324
  node.status({
255
325
  fill: 'green',
256
326
  shape: 'dot',
257
- text: RED._('aedes-mqtt-broker.status.connected', { count: broker.connectedClients })
327
+ text: RED._('aedes-mqtt-broker.status.connected', {
328
+ count: broker.connectedClients
329
+ })
258
330
  });
259
331
  });
260
332
 
@@ -270,7 +342,9 @@ module.exports = function (RED) {
270
342
  node.status({
271
343
  fill: 'green',
272
344
  shape: 'dot',
273
- text: RED._('aedes-mqtt-broker.status.connected', { count: broker.connectedClients })
345
+ text: RED._('aedes-mqtt-broker.status.connected', {
346
+ count: broker.connectedClients
347
+ })
274
348
  });
275
349
  });
276
350
 
@@ -285,7 +359,9 @@ module.exports = function (RED) {
285
359
  node.status({
286
360
  fill: 'green',
287
361
  shape: 'dot',
288
- text: RED._('aedes-mqtt-broker.status.connected', { count: broker.connectedClients })
362
+ text: RED._('aedes-mqtt-broker.status.connected', {
363
+ count: broker.connectedClients
364
+ })
289
365
  });
290
366
  });
291
367
 
@@ -335,7 +411,9 @@ module.exports = function (RED) {
335
411
  process.nextTick(function onCloseDelayed () {
336
412
  function wsClose () {
337
413
  if (wss) {
338
- node.log('Unbinding aedes mqtt server from ws port: ' + config.mqtt_ws_port);
414
+ node.log(
415
+ 'Unbinding aedes mqtt server from ws port: ' + config.mqtt_ws_port
416
+ );
339
417
  wss.close(function () {
340
418
  node.debug('after wss.close(): ');
341
419
  httpServer.close(function () {
@@ -350,11 +428,15 @@ module.exports = function (RED) {
350
428
 
351
429
  function brokerClose () {
352
430
  broker.close(function () {
353
- node.log('Unbinding aedes mqtt server from port: ' + config.mqtt_port);
431
+ node.log(
432
+ 'Unbinding aedes mqtt server from port: ' + config.mqtt_port
433
+ );
354
434
  server.close(function () {
355
435
  node.debug('after server.close(): ');
356
436
  if (node.mqtt_ws_path !== '') {
357
- node.log('Unbinding aedes mqtt server from ws path: ' + node.fullPath);
437
+ node.log(
438
+ 'Unbinding aedes mqtt server from ws path: ' + node.fullPath
439
+ );
358
440
  delete listenerNodes[node.fullPath];
359
441
  node.server.close(function () {
360
442
  wsClose();
@@ -365,14 +447,7 @@ module.exports = function (RED) {
365
447
  });
366
448
  });
367
449
  }
368
-
369
- if (db) {
370
- // db.close(function () {
371
- brokerClose();
372
- // });
373
- } else {
374
- brokerClose();
375
- }
450
+ brokerClose();
376
451
  });
377
452
  });
378
453
  }
@@ -382,6 +457,7 @@ module.exports = function (RED) {
382
457
  username: { type: 'text' },
383
458
  password: { type: 'password' },
384
459
  certdata: { type: 'text' },
460
+ cadata: { type: 'text' },
385
461
  keydata: { type: 'text' }
386
462
  }
387
463
  });
@@ -9,10 +9,12 @@
9
9
  "mqtt_ws_port": "WS port",
10
10
  "mqtt_ws_path": "WS path",
11
11
  "use-tls": "Enable secure (SSL/TLS) connection",
12
+ "use-local-files": "Use local files",
12
13
  "tls-config": "TLS Configuration",
13
14
  "upload": "Upload",
14
15
  "cert": "Certificate",
15
16
  "key": "Private Key",
17
+ "ca": "CA Certificate",
16
18
  "persistence_bind": "Persistence",
17
19
  "persistence_memory": "Memory",
18
20
  "persistence_mongodb": "MongoDB",
@@ -25,12 +27,14 @@
25
27
  "mqtt_ws_path": "Enter Websocket path. Leave blank to disable Websocket support",
26
28
  "cert": "path to certificate (PEM format)",
27
29
  "key": "path to private key (PEM format)",
30
+ "ca": "path to CA certificate (PEM format)",
28
31
  "dburl": "mongodb://localhost:27017/mqtt",
29
32
  "username": "leave blank to disable authentication",
30
33
  "password": "leave blank to disable authentication"
31
34
  },
32
35
  "tabs-label": {
33
36
  "connection": "Connection",
37
+ "persistence": "Persistence",
34
38
  "security": "Security"
35
39
  }
36
40
  }
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "node-red-contrib-aedes",
3
- "version": "0.14.0",
3
+ "version": "0.15.0",
4
4
  "description": "Node Red MQTT broker node based on aedes.js",
5
5
  "dependencies": {
6
6
  "aedes": "^0.51.3",
@@ -1,5 +1,5 @@
1
1
  /* eslint-env mocha */
2
- /* eslint no-console: ["error", { allow: ["warn", "error"] }] */
2
+ /* eslint no-console: ["error", { allow: ["log", "warn", "error"] }] */
3
3
  const helper = require('node-red-node-test-helper');
4
4
  const aedesNode = require('../aedes.js');
5
5
  const mqtt = require('mqtt');
@@ -27,11 +27,29 @@ describe('Aedes Broker Last Will tests', function () {
27
27
  [], []
28
28
  ]
29
29
  }];
30
- helper.load(aedesNode, flow, function () {
31
- const n1 = helper.getNode('n1');
32
- n1.should.have.property('name', 'Aedes 1883');
30
+ try {
31
+ helper.load(aedesNode, flow, function () {
32
+ const n1 = helper.getNode('n1');
33
+ n1.should.have.property('name', 'Aedes 1883');
34
+ done();
35
+ });
36
+ } catch (n) {
37
+ // Check if AggregateError
38
+ console.log(
39
+ n instanceof AggregateError
40
+ );
41
+
42
+ // Print the message of the error
43
+ console.log(n.message);
44
+
45
+ // Print the name of the error
46
+ console.log(n.name);
47
+
48
+ // Print all the errors that this
49
+ // error comprises
50
+ console.log(n.errors);
33
51
  done();
34
- });
52
+ }
35
53
  });
36
54
 
37
55
  it('a subscriber should receive a last will message on publisher disconnect', function (done) {
@@ -56,8 +74,20 @@ describe('Aedes Broker Last Will tests', function () {
56
74
  clientId: 'client1',
57
75
  will: { topic: 'testLastWill', payload: 'last will' }
58
76
  });
59
- client1.on('error', function (err) {
60
- console.error('Error: ', err.toString());
77
+ client1.on('error', function (n) {
78
+ console.log(
79
+ n instanceof AggregateError
80
+ );
81
+
82
+ // Print the message of the error
83
+ console.log(n.message);
84
+
85
+ // Print the name of the error
86
+ console.log(n.name);
87
+
88
+ // Print all the errors that this
89
+ // error comprises
90
+ console.log(n.errors);
61
91
  });
62
92
  client1.on('connect', function () {
63
93
  // console.log('External client1 connected');