node-red-contrib-aedes 0.15.1 → 1.1.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/.github/workflows/nodejs.yml +1 -1
- package/CHANGELOG.md +10 -0
- package/README.md +9 -2
- package/aedes.html +29 -19
- package/aedes.js +181 -172
- package/locales/de/aedes.html +110 -0
- package/locales/de/aedes.json +50 -0
- package/locales/en-US/aedes.html +103 -12
- package/locales/en-US/aedes.json +10 -1
- package/package.json +6 -4
- package/test/aedes_last_will_spec.js +25 -48
- package/test/aedes_qos_spec.js +83 -77
- package/test/aedes_retain_spec.js +84 -78
- package/test/aedes_spec.js +260 -67
- package/test/aedes_ws_spec.js +112 -38
- package/test/test-utils.js +17 -0
- package/docs/DEV-SETUP.md +0 -86
- package/docs/MIGRATION-PLAN-0.51-TO-1.0.md +0 -349
- package/docs/MIGRATION-PLAN-NODE-RED-4.md +0 -107
package/CHANGELOG.md
CHANGED
|
@@ -1,5 +1,15 @@
|
|
|
1
1
|
# node-red-contrib-aedes Changelog
|
|
2
2
|
|
|
3
|
+
## Feb 15, 2026, Version 1.1.0
|
|
4
|
+
### Notable changes
|
|
5
|
+
- Improved help documentation
|
|
6
|
+
- Improved UI
|
|
7
|
+
- Added German locale (de)
|
|
8
|
+
|
|
9
|
+
## Feb 14, 2026, Version 1.0.0
|
|
10
|
+
### Notable changes
|
|
11
|
+
- Update aedes to version 1.0.0
|
|
12
|
+
|
|
3
13
|
## Jul 22, 2025, Version 0.15.0
|
|
4
14
|
### Notable changes
|
|
5
15
|
- TLS configuration with local filess
|
package/README.md
CHANGED
|
@@ -30,7 +30,7 @@ Just put this node on Node-RED and hit the deploy button. The MQTT Broker will r
|
|
|
30
30
|
- Standard TCP Support
|
|
31
31
|
- WebSocket Support via port or path
|
|
32
32
|
- SSL / TLS
|
|
33
|
-
- Message Persistence (In-memory
|
|
33
|
+
- Message Persistence (In-memory or MongoDB)
|
|
34
34
|
|
|
35
35
|
For more information see [Aedes](https://github.com/moscajs/aedes/blob/master/README.md#features).
|
|
36
36
|
|
|
@@ -43,7 +43,14 @@ You can also bind the WebSocket to the root `"/"` path and having `wss://yourser
|
|
|
43
43
|
|
|
44
44
|
## Version Compatibility
|
|
45
45
|
|
|
46
|
-
The current version
|
|
46
|
+
The current version is based on **Aedes version 1.0**, which introduces several breaking changes. If your environment requires Aedes version 0.51, you can switch to version 0.15.x of this package.
|
|
47
|
+
|
|
48
|
+
To install the compatible version using the `version-11` dist-tag:
|
|
49
|
+
```sh
|
|
50
|
+
npm install node-red-contrib-aedes@version-15
|
|
51
|
+
```
|
|
52
|
+
|
|
53
|
+
The current version and version 0.15.x require **Node.js >= 20** and **Node-RED >= 3.0**.
|
|
47
54
|
|
|
48
55
|
If you are running an older Node.js version (e.g. on locked-down or legacy hardware), please use version **0.11.x** of this package.
|
|
49
56
|
|
package/aedes.html
CHANGED
|
@@ -25,27 +25,28 @@
|
|
|
25
25
|
<div id="node-config-aedes-broker-tabs-content" style="min-height:150px;">
|
|
26
26
|
<div id="aedes-broker-tab-connection" style="display:none">
|
|
27
27
|
<div class="form-row">
|
|
28
|
-
<label for="node-input-mqtt_port"><i class="fa fa-
|
|
28
|
+
<label for="node-input-mqtt_port"><i class="fa fa-plug"></i> <span
|
|
29
29
|
data-i18n="aedes-mqtt-broker.label.mqtt_port"></span></label>
|
|
30
30
|
<input type="text" id="node-input-mqtt_port"
|
|
31
31
|
data-i18n="[placeholder]aedes-mqtt-broker.placeholder.mqtt_port">
|
|
32
32
|
</div>
|
|
33
33
|
<div class="form-row">
|
|
34
|
-
<label for="node-input-mqtt_ws_bind"><i class="fa fa-
|
|
35
|
-
|
|
34
|
+
<label for="node-input-mqtt_ws_bind"><i class="fa fa-exchange"></i> <span
|
|
35
|
+
data-i18n="aedes-mqtt-broker.label.mqtt_ws_bind"></span></label>
|
|
36
|
+
<select id="node-input-mqtt_ws_bind" type="text" style="width:30%; text-align:center;">
|
|
36
37
|
<option value="path" default>path</option>
|
|
37
38
|
<option value="port">port</option>
|
|
38
39
|
</select>
|
|
39
40
|
</div>
|
|
40
41
|
<div class="form-row" id="node-input-mqtt_ws_path-label">
|
|
41
|
-
<label for="node-input-mqtt_ws_path"><i class="fa fa-
|
|
42
|
-
data-i18n="aedes-mqtt-broker.label.mqtt_ws_path"></label>
|
|
42
|
+
<label for="node-input-mqtt_ws_path"><i class="fa fa-link"></i> <span
|
|
43
|
+
data-i18n="aedes-mqtt-broker.label.mqtt_ws_path"></span></label>
|
|
43
44
|
<input type="text" id="node-input-mqtt_ws_path"
|
|
44
45
|
data-i18n="[placeholder]aedes-mqtt-broker.placeholder.mqtt_ws_path">
|
|
45
46
|
</div>
|
|
46
47
|
<div class="form-row" id="node-input-mqtt_ws_port-label">
|
|
47
|
-
<label for="node-input-mqtt_ws_port"><i class="fa fa-
|
|
48
|
-
data-i18n="aedes-mqtt-broker.label.mqtt_ws_port"></label>
|
|
48
|
+
<label for="node-input-mqtt_ws_port"><i class="fa fa-plug"></i> <span
|
|
49
|
+
data-i18n="aedes-mqtt-broker.label.mqtt_ws_port"></span></label>
|
|
49
50
|
<input type="text" id="node-input-mqtt_ws_port"
|
|
50
51
|
data-i18n="[placeholder]aedes-mqtt-broker.placeholder.mqtt_ws_port">
|
|
51
52
|
</div>
|
|
@@ -58,7 +59,7 @@
|
|
|
58
59
|
<input type="checkbox" id="node-input-uselocalfiles"
|
|
59
60
|
style="display: inline-block; width: auto; vertical-align: top;">
|
|
60
61
|
<label for="node-input-uselocalfiles" style="width: 70%;"><span
|
|
61
|
-
data-i18n="aedes-mqtt-broker.label.use-local-files"></label>
|
|
62
|
+
data-i18n="aedes-mqtt-broker.label.use-local-files"></span></label>
|
|
62
63
|
</div>
|
|
63
64
|
|
|
64
65
|
<div class="form-row">
|
|
@@ -122,17 +123,17 @@
|
|
|
122
123
|
</div>
|
|
123
124
|
<div id="aedes-broker-tab-persistence" style="display:none">
|
|
124
125
|
<div class="form-row">
|
|
125
|
-
<label for="node-input-persistence_bind"><i class="fa fa-
|
|
126
|
-
data-i18n="aedes-mqtt-broker.label.persistence_bind"></label>
|
|
127
|
-
<select id="node-input-persistence_bind" type="text" style="width:30
|
|
126
|
+
<label for="node-input-persistence_bind"><i class="fa fa-hdd-o"></i> <span
|
|
127
|
+
data-i18n="aedes-mqtt-broker.label.persistence_bind"></span></label>
|
|
128
|
+
<select id="node-input-persistence_bind" type="text" style="width:30%; text-align:center;">
|
|
128
129
|
<option value="memory" data-i18n="aedes-mqtt-broker.label.persistence_memory" default></option>
|
|
129
130
|
<option value="mongodb" data-i18n="aedes-mqtt-broker.label.persistence_mongodb"></option>
|
|
130
131
|
<!-- <option value="level" data-i18n="aedes-mqtt-broker.label.persistence_level"></option> -->
|
|
131
132
|
</select>
|
|
132
133
|
</div>
|
|
133
|
-
<div class="form-row" id="node-row-dburl"
|
|
134
|
+
<div class="form-row hide" id="node-row-dburl">
|
|
134
135
|
<label for="node-input-dburl"><i class="fa fa-database"></i> <span
|
|
135
|
-
data-i18n="aedes-mqtt-broker.label.dburl"></label>
|
|
136
|
+
data-i18n="aedes-mqtt-broker.label.dburl"></span></label>
|
|
136
137
|
<input type="text" id="node-input-dburl" data-i18n="[placeholder]aedes-mqtt-broker.placeholder.dburl">
|
|
137
138
|
</div>
|
|
138
139
|
</div>
|
|
@@ -157,9 +158,16 @@
|
|
|
157
158
|
category: 'network',
|
|
158
159
|
defaults: {
|
|
159
160
|
name: { value: '' },
|
|
160
|
-
mqtt_port: { value: 1883, validate:
|
|
161
|
+
mqtt_port: { value: 1883, validate: function (v) {
|
|
162
|
+
var n = parseInt(v, 10);
|
|
163
|
+
return !isNaN(n) && n >= 1 && n <= 65535;
|
|
164
|
+
}},
|
|
161
165
|
mqtt_ws_bind: { value: 'port', required: true },
|
|
162
|
-
mqtt_ws_port: { value: null, required: false, validate:
|
|
166
|
+
mqtt_ws_port: { value: null, required: false, validate: function (v) {
|
|
167
|
+
if (v === '' || v === null || v === undefined) return true;
|
|
168
|
+
var n = parseInt(v, 10);
|
|
169
|
+
return !isNaN(n) && n >= 1 && n <= 65535;
|
|
170
|
+
}},
|
|
163
171
|
mqtt_ws_path: { value: '', required: false },
|
|
164
172
|
cert: {
|
|
165
173
|
value: '',
|
|
@@ -331,7 +339,7 @@
|
|
|
331
339
|
$('#tls-button-key-clear').on('click', function () {
|
|
332
340
|
clearNameData('key');
|
|
333
341
|
});
|
|
334
|
-
$("#tls-
|
|
342
|
+
$("#tls-button-ca-clear").on("click", function() {
|
|
335
343
|
clearNameData("ca");
|
|
336
344
|
});
|
|
337
345
|
|
|
@@ -360,9 +368,11 @@
|
|
|
360
368
|
$('#node-input-tls').val('');
|
|
361
369
|
}
|
|
362
370
|
if ($("#node-input-uselocalfiles").is(':checked')) {
|
|
363
|
-
|
|
364
|
-
|
|
365
|
-
|
|
371
|
+
['ca', 'cert', 'key'].forEach(function (prop) {
|
|
372
|
+
$('#tls-' + prop + 'name').text('');
|
|
373
|
+
$('#node-input-' + prop + 'data').val('');
|
|
374
|
+
$('#node-input-' + prop + 'name').val('');
|
|
375
|
+
});
|
|
366
376
|
} else {
|
|
367
377
|
$("#node-input-ca").val("");
|
|
368
378
|
$("#node-input-cert").val("");
|
package/aedes.js
CHANGED
|
@@ -19,13 +19,13 @@ module.exports = function (RED) {
|
|
|
19
19
|
const MongoPersistence = require('aedes-persistence-mongodb');
|
|
20
20
|
// const { Level } = require('level');
|
|
21
21
|
// const LevelPersistence = require('aedes-persistence-level');
|
|
22
|
-
|
|
22
|
+
// aedes is ESM-only in v1 -- dynamically imported in initializeBroker()
|
|
23
23
|
const fs = require('fs');
|
|
24
24
|
const net = require('net');
|
|
25
25
|
const tls = require('tls');
|
|
26
26
|
const http = require('http');
|
|
27
27
|
const https = require('https');
|
|
28
|
-
const
|
|
28
|
+
const { WebSocketServer, createWebSocketStream } = require('ws');
|
|
29
29
|
|
|
30
30
|
let serverUpgradeAdded = false;
|
|
31
31
|
const listenerNodes = {};
|
|
@@ -51,131 +51,53 @@ module.exports = function (RED) {
|
|
|
51
51
|
}
|
|
52
52
|
}
|
|
53
53
|
|
|
54
|
-
function
|
|
55
|
-
|
|
56
|
-
|
|
57
|
-
|
|
58
|
-
|
|
59
|
-
this.mqtt_ws_bind = config.mqtt_ws_bind;
|
|
60
|
-
this.usetls = config.usetls;
|
|
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
|
-
|
|
69
|
-
if (this.mqtt_ws_bind === 'path') {
|
|
70
|
-
this.mqtt_ws_port = 0;
|
|
71
|
-
} else {
|
|
72
|
-
this.mqtt_ws_path = '';
|
|
73
|
-
}
|
|
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
|
-
}
|
|
103
|
-
if (this.credentials) {
|
|
104
|
-
this.username = this.credentials.username;
|
|
105
|
-
this.password = this.credentials.password;
|
|
106
|
-
}
|
|
107
|
-
|
|
108
|
-
if (typeof this.usetls === 'undefined') {
|
|
109
|
-
this.usetls = false;
|
|
110
|
-
}
|
|
111
|
-
|
|
112
|
-
const node = this;
|
|
113
|
-
|
|
114
|
-
const aedesSettings = {};
|
|
115
|
-
const serverOptions = {};
|
|
116
|
-
|
|
117
|
-
if (config.persistence_bind === 'mongodb' && config.dburl) {
|
|
118
|
-
aedesSettings.persistence = MongoPersistence({
|
|
119
|
-
url: config.dburl
|
|
120
|
-
});
|
|
121
|
-
node.log('Start persistence to MongeDB');
|
|
122
|
-
/*
|
|
123
|
-
} else if (config.persistence_bind === 'level') {
|
|
124
|
-
aedesSettings.persistence = LevelPersistence(new Level('leveldb'));
|
|
125
|
-
node.log('Start persistence to LevelDB');
|
|
126
|
-
*/
|
|
127
|
-
}
|
|
128
|
-
|
|
129
|
-
if (this.cert && this.key && this.usetls) {
|
|
130
|
-
serverOptions.cert = this.cert;
|
|
131
|
-
serverOptions.key = this.key;
|
|
132
|
-
serverOptions.ca = this.ca;
|
|
133
|
-
}
|
|
54
|
+
async function initializeBroker (node, config, aedesSettings, serverOptions) {
|
|
55
|
+
const { Aedes } = await import('aedes');
|
|
56
|
+
const broker = await Aedes.createBroker(aedesSettings);
|
|
57
|
+
if (node._closing) { broker.close(); return; }
|
|
58
|
+
node._broker = broker;
|
|
134
59
|
|
|
135
|
-
const broker = aedes.createBroker(aedesSettings);
|
|
136
60
|
let server;
|
|
137
|
-
if (
|
|
61
|
+
if (node.usetls) {
|
|
138
62
|
server = tls.createServer(serverOptions, broker.handle);
|
|
139
63
|
} else {
|
|
140
64
|
server = net.createServer(broker.handle);
|
|
141
65
|
}
|
|
66
|
+
node._server = server;
|
|
142
67
|
|
|
143
|
-
|
|
144
|
-
let httpServer = null;
|
|
145
|
-
|
|
146
|
-
if (this.mqtt_ws_port) {
|
|
68
|
+
if (node.mqtt_ws_port) {
|
|
147
69
|
// Awkward check since http or ws do not fire an error event in case the port is in use
|
|
148
70
|
const testServer = net.createServer();
|
|
149
71
|
testServer.once('error', function (err) {
|
|
150
72
|
if (err.code === 'EADDRINUSE') {
|
|
151
73
|
node.error(
|
|
152
|
-
'
|
|
74
|
+
RED._('aedes-mqtt-broker.error.port-in-use', { port: config.mqtt_ws_port })
|
|
153
75
|
);
|
|
154
76
|
} else {
|
|
155
77
|
node.error(
|
|
156
|
-
'
|
|
157
|
-
config.mqtt_ws_port +
|
|
158
|
-
', ' +
|
|
159
|
-
err.toString()
|
|
78
|
+
RED._('aedes-mqtt-broker.error.server-error', { port: config.mqtt_ws_port, error: err.toString() })
|
|
160
79
|
);
|
|
161
80
|
}
|
|
81
|
+
node.status({ fill: 'red', shape: 'ring', text: 'aedes-mqtt-broker.status.error' });
|
|
162
82
|
});
|
|
163
83
|
testServer.once('listening', function () {
|
|
164
84
|
testServer.close();
|
|
165
85
|
});
|
|
166
86
|
|
|
167
87
|
testServer.once('close', function () {
|
|
88
|
+
let httpServer;
|
|
168
89
|
if (node.usetls) {
|
|
169
90
|
httpServer = https.createServer(serverOptions);
|
|
170
91
|
} else {
|
|
171
92
|
httpServer = http.createServer();
|
|
172
93
|
}
|
|
173
|
-
|
|
174
|
-
|
|
175
|
-
|
|
176
|
-
|
|
177
|
-
broker.handle
|
|
178
|
-
);
|
|
94
|
+
node._httpServer = httpServer;
|
|
95
|
+
const wss = new WebSocketServer({ server: httpServer });
|
|
96
|
+
wss.on('connection', function (websocket, req) {
|
|
97
|
+
const stream = createWebSocketStream(websocket);
|
|
98
|
+
broker.handle(stream, req);
|
|
99
|
+
});
|
|
100
|
+
node._wss = wss;
|
|
179
101
|
httpServer.listen(config.mqtt_ws_port, function () {
|
|
180
102
|
node.log(
|
|
181
103
|
'Binding aedes mqtt server on ws port: ' + config.mqtt_ws_port
|
|
@@ -187,7 +109,7 @@ module.exports = function (RED) {
|
|
|
187
109
|
});
|
|
188
110
|
}
|
|
189
111
|
|
|
190
|
-
if (
|
|
112
|
+
if (node.mqtt_ws_path !== '') {
|
|
191
113
|
if (!serverUpgradeAdded) {
|
|
192
114
|
RED.server.on('upgrade', handleServerUpgrade);
|
|
193
115
|
serverUpgradeAdded = true;
|
|
@@ -206,46 +128,44 @@ module.exports = function (RED) {
|
|
|
206
128
|
node.error(
|
|
207
129
|
RED._('websocket.errors.duplicate-path', { path: node.mqtt_ws_path })
|
|
208
130
|
);
|
|
209
|
-
|
|
210
|
-
|
|
211
|
-
|
|
212
|
-
const serverOptions_ = {
|
|
213
|
-
noServer: true
|
|
214
|
-
};
|
|
215
|
-
if (RED.settings.webSocketNodeVerifyClient) {
|
|
216
|
-
serverOptions_.verifyClient = RED.settings.webSocketNodeVerifyClient;
|
|
217
|
-
}
|
|
218
|
-
|
|
219
|
-
node.server = ws.createServer(
|
|
220
|
-
{
|
|
131
|
+
} else {
|
|
132
|
+
listenerNodes[node.fullPath] = node;
|
|
133
|
+
const serverOptions_ = {
|
|
221
134
|
noServer: true
|
|
222
|
-
}
|
|
223
|
-
|
|
224
|
-
|
|
135
|
+
};
|
|
136
|
+
if (RED.settings.webSocketNodeVerifyClient) {
|
|
137
|
+
serverOptions_.verifyClient = RED.settings.webSocketNodeVerifyClient;
|
|
138
|
+
}
|
|
225
139
|
|
|
226
|
-
|
|
140
|
+
node.server = new WebSocketServer(serverOptions_);
|
|
141
|
+
node.server.on('connection', function (websocket, req) {
|
|
142
|
+
const stream = createWebSocketStream(websocket);
|
|
143
|
+
broker.handle(stream, req);
|
|
144
|
+
});
|
|
145
|
+
|
|
146
|
+
node.log('Binding aedes mqtt server on ws path: ' + node.fullPath);
|
|
147
|
+
}
|
|
227
148
|
}
|
|
228
149
|
|
|
229
150
|
server.once('error', function (err) {
|
|
230
151
|
if (err.code === 'EADDRINUSE') {
|
|
231
|
-
node.error(
|
|
232
|
-
|
|
233
|
-
|
|
234
|
-
shape: 'ring',
|
|
235
|
-
text: 'node-red:common.status.disconnected'
|
|
236
|
-
});
|
|
152
|
+
node.error(
|
|
153
|
+
RED._('aedes-mqtt-broker.error.port-in-use', { port: config.mqtt_port })
|
|
154
|
+
);
|
|
237
155
|
} else {
|
|
238
|
-
node.error(
|
|
239
|
-
|
|
240
|
-
|
|
241
|
-
shape: 'ring',
|
|
242
|
-
text: 'node-red:common.status.disconnected'
|
|
243
|
-
});
|
|
156
|
+
node.error(
|
|
157
|
+
RED._('aedes-mqtt-broker.error.server-error', { port: config.mqtt_port, error: err.toString() })
|
|
158
|
+
);
|
|
244
159
|
}
|
|
160
|
+
node.status({
|
|
161
|
+
fill: 'red',
|
|
162
|
+
shape: 'ring',
|
|
163
|
+
text: 'aedes-mqtt-broker.status.error'
|
|
164
|
+
});
|
|
245
165
|
});
|
|
246
166
|
|
|
247
|
-
if (
|
|
248
|
-
server.listen(
|
|
167
|
+
if (node.mqtt_port) {
|
|
168
|
+
server.listen(node.mqtt_port, function () {
|
|
249
169
|
node.log('Binding aedes mqtt server on port: ' + config.mqtt_port);
|
|
250
170
|
node.status({
|
|
251
171
|
fill: 'green',
|
|
@@ -255,7 +175,7 @@ module.exports = function (RED) {
|
|
|
255
175
|
});
|
|
256
176
|
}
|
|
257
177
|
|
|
258
|
-
if (
|
|
178
|
+
if (node.credentials && node.username && node.password) {
|
|
259
179
|
broker.authenticate = function (client, username, password, callback) {
|
|
260
180
|
const authorized =
|
|
261
181
|
username === node.username &&
|
|
@@ -383,7 +303,7 @@ module.exports = function (RED) {
|
|
|
383
303
|
}
|
|
384
304
|
});
|
|
385
305
|
|
|
386
|
-
if (
|
|
306
|
+
if (node.wires && node.wires[1] && node.wires[1].length > 0) {
|
|
387
307
|
node.log('Publish output wired. Enable broker publish event messages.');
|
|
388
308
|
broker.on('publish', function (packet, client) {
|
|
389
309
|
const msg = {
|
|
@@ -400,54 +320,143 @@ module.exports = function (RED) {
|
|
|
400
320
|
broker.on('closed', function () {
|
|
401
321
|
node.debug('Closed event');
|
|
402
322
|
});
|
|
323
|
+
}
|
|
324
|
+
|
|
325
|
+
function AedesBrokerNode (config) {
|
|
326
|
+
RED.nodes.createNode(this, config);
|
|
327
|
+
this.mqtt_port = parseInt(config.mqtt_port, 10);
|
|
328
|
+
this.mqtt_ws_port = parseInt(config.mqtt_ws_port, 10);
|
|
329
|
+
this.mqtt_ws_path = '' + config.mqtt_ws_path;
|
|
330
|
+
this.mqtt_ws_bind = config.mqtt_ws_bind;
|
|
331
|
+
this.usetls = config.usetls;
|
|
332
|
+
|
|
333
|
+
const certPath = config.cert ? config.cert.trim() : '';
|
|
334
|
+
const keyPath = config.key ? config.key.trim() : '';
|
|
335
|
+
const caPath = config.ca ? config.ca.trim() : '';
|
|
336
|
+
|
|
337
|
+
this.uselocalfiles = config.uselocalfiles;
|
|
338
|
+
this.dburl = config.dburl;
|
|
339
|
+
|
|
340
|
+
if (this.mqtt_ws_bind === 'path') {
|
|
341
|
+
this.mqtt_ws_port = 0;
|
|
342
|
+
} else {
|
|
343
|
+
this.mqtt_ws_path = '';
|
|
344
|
+
}
|
|
403
345
|
|
|
404
|
-
|
|
346
|
+
if (certPath.length > 0 || keyPath.length > 0 || caPath.length > 0) {
|
|
347
|
+
if ((certPath.length > 0) !== (keyPath.length > 0)) {
|
|
348
|
+
this.valid = false;
|
|
349
|
+
this.error(RED._('tls.error.missing-file'));
|
|
350
|
+
return;
|
|
351
|
+
}
|
|
352
|
+
try {
|
|
353
|
+
if (certPath) {
|
|
354
|
+
this.cert = fs.readFileSync(certPath);
|
|
355
|
+
}
|
|
356
|
+
if (keyPath) {
|
|
357
|
+
this.key = fs.readFileSync(keyPath);
|
|
358
|
+
}
|
|
359
|
+
if (caPath) {
|
|
360
|
+
this.ca = fs.readFileSync(caPath);
|
|
361
|
+
}
|
|
362
|
+
} catch (err) {
|
|
363
|
+
this.valid = false;
|
|
364
|
+
this.error(err.toString());
|
|
365
|
+
return;
|
|
366
|
+
}
|
|
367
|
+
} else {
|
|
368
|
+
if (this.credentials) {
|
|
369
|
+
this.cert = this.credentials.certdata || '';
|
|
370
|
+
this.key = this.credentials.keydata || '';
|
|
371
|
+
this.ca = this.credentials.cadata || '';
|
|
372
|
+
}
|
|
373
|
+
}
|
|
374
|
+
if (this.credentials) {
|
|
375
|
+
this.username = this.credentials.username;
|
|
376
|
+
this.password = this.credentials.password;
|
|
377
|
+
}
|
|
378
|
+
|
|
379
|
+
if (typeof this.usetls === 'undefined') {
|
|
380
|
+
this.usetls = false;
|
|
381
|
+
}
|
|
382
|
+
|
|
383
|
+
const node = this;
|
|
384
|
+
|
|
385
|
+
const aedesSettings = {};
|
|
386
|
+
const serverOptions = {};
|
|
387
|
+
|
|
388
|
+
if (config.persistence_bind === 'mongodb' && config.dburl) {
|
|
389
|
+
aedesSettings.persistence = MongoPersistence({
|
|
390
|
+
url: config.dburl
|
|
391
|
+
});
|
|
392
|
+
node.log('Start persistence to MongoDB');
|
|
393
|
+
/*
|
|
394
|
+
} else if (config.persistence_bind === 'level') {
|
|
395
|
+
aedesSettings.persistence = LevelPersistence(new Level('leveldb'));
|
|
396
|
+
node.log('Start persistence to LevelDB');
|
|
397
|
+
*/
|
|
398
|
+
}
|
|
399
|
+
|
|
400
|
+
if (this.cert && this.key && this.usetls) {
|
|
401
|
+
serverOptions.cert = this.cert;
|
|
402
|
+
serverOptions.key = this.key;
|
|
403
|
+
serverOptions.ca = this.ca;
|
|
404
|
+
}
|
|
405
|
+
|
|
406
|
+
node._closing = false;
|
|
407
|
+
node._broker = null;
|
|
408
|
+
node._server = null;
|
|
409
|
+
node._wss = null;
|
|
410
|
+
node._httpServer = null;
|
|
411
|
+
|
|
412
|
+
node._initPromise = initializeBroker(node, config, aedesSettings, serverOptions);
|
|
413
|
+
node._initPromise.catch(function (err) {
|
|
414
|
+
node.error(RED._('aedes-mqtt-broker.error.init-failed', { error: err.toString() }));
|
|
415
|
+
node.status({ fill: 'red', shape: 'ring', text: 'aedes-mqtt-broker.status.init-failed' });
|
|
416
|
+
});
|
|
417
|
+
|
|
418
|
+
this.on('close', async function (removed, done) {
|
|
419
|
+
node._closing = true;
|
|
405
420
|
if (removed) {
|
|
406
421
|
node.debug('Node removed or disabled');
|
|
407
422
|
} else {
|
|
408
423
|
node.debug('Node restarting');
|
|
409
424
|
}
|
|
410
|
-
|
|
411
|
-
|
|
412
|
-
|
|
413
|
-
|
|
414
|
-
|
|
415
|
-
|
|
416
|
-
|
|
417
|
-
|
|
418
|
-
httpServer.close(function () {
|
|
419
|
-
node.debug('after httpServer.close(): ');
|
|
420
|
-
done();
|
|
421
|
-
});
|
|
422
|
-
});
|
|
423
|
-
} else {
|
|
424
|
-
done();
|
|
425
|
-
}
|
|
426
|
-
}
|
|
425
|
+
try {
|
|
426
|
+
await node._initPromise;
|
|
427
|
+
closeBroker(node, done);
|
|
428
|
+
} catch (e) {
|
|
429
|
+
done();
|
|
430
|
+
}
|
|
431
|
+
});
|
|
432
|
+
}
|
|
427
433
|
|
|
428
|
-
|
|
429
|
-
|
|
430
|
-
|
|
431
|
-
|
|
432
|
-
|
|
433
|
-
|
|
434
|
-
node.
|
|
435
|
-
|
|
436
|
-
node.log(
|
|
437
|
-
'Unbinding aedes mqtt server from ws path: ' + node.fullPath
|
|
438
|
-
);
|
|
439
|
-
delete listenerNodes[node.fullPath];
|
|
440
|
-
node.server.close(function () {
|
|
441
|
-
wsClose();
|
|
442
|
-
});
|
|
443
|
-
} else {
|
|
444
|
-
wsClose();
|
|
445
|
-
}
|
|
446
|
-
});
|
|
434
|
+
function closeBroker (node, done) {
|
|
435
|
+
process.nextTick(function () {
|
|
436
|
+
function wsClose () {
|
|
437
|
+
if (node._wss) {
|
|
438
|
+
node._wss.close(function () {
|
|
439
|
+
if (node._httpServer) {
|
|
440
|
+
node._httpServer.close(function () { done(); });
|
|
441
|
+
} else { done(); }
|
|
447
442
|
});
|
|
448
|
-
}
|
|
449
|
-
|
|
450
|
-
|
|
443
|
+
} else { done(); }
|
|
444
|
+
}
|
|
445
|
+
function serverClose () {
|
|
446
|
+
if (node._server) {
|
|
447
|
+
node._server.close(function () {
|
|
448
|
+
if (node.mqtt_ws_path !== '' && node.fullPath) {
|
|
449
|
+
delete listenerNodes[node.fullPath];
|
|
450
|
+
if (node.server) {
|
|
451
|
+
node.server.close(function () { wsClose(); });
|
|
452
|
+
} else { wsClose(); }
|
|
453
|
+
} else { wsClose(); }
|
|
454
|
+
});
|
|
455
|
+
} else { wsClose(); }
|
|
456
|
+
}
|
|
457
|
+
if (node._broker) {
|
|
458
|
+
node._broker.close(function () { serverClose(); });
|
|
459
|
+
} else { serverClose(); }
|
|
451
460
|
});
|
|
452
461
|
}
|
|
453
462
|
|