smart-home-engine 1.6.1 → 1.7.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/dist/web/assets/{index-DBAKaUFL.js → index-Ba2WAZRy.js} +69 -69
- package/dist/web/assets/{index-D-Rifjr_.css → index-CloKwceI.css} +1 -1
- package/dist/web/assets/{tsMode-C5T2U6K3.js → tsMode-BXBhvzFd.js} +1 -1
- package/dist/web/index.html +2 -2
- package/package.json +1 -1
- package/src/lib/mosquitto-conf.js +45 -2
- package/src/web/broker-api.js +37 -1
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
import{m as O}from"./monaco-langs-BW2J83t5.js";import{t as I}from"./index-
|
|
1
|
+
import{m as O}from"./monaco-langs-BW2J83t5.js";import{t as I}from"./index-Ba2WAZRy.js";/*!-----------------------------------------------------------------------------
|
|
2
2
|
* Copyright (c) Microsoft Corporation. All rights reserved.
|
|
3
3
|
* Version: 0.52.2(404545bded1df6ffa41ea0af4e8ddb219018c6c1)
|
|
4
4
|
* Released under the MIT license
|
package/dist/web/index.html
CHANGED
|
@@ -172,10 +172,10 @@
|
|
|
172
172
|
}
|
|
173
173
|
})();
|
|
174
174
|
</script>
|
|
175
|
-
<script type="module" crossorigin src="/assets/index-
|
|
175
|
+
<script type="module" crossorigin src="/assets/index-Ba2WAZRy.js"></script>
|
|
176
176
|
<link rel="modulepreload" crossorigin href="/assets/monaco-langs-BW2J83t5.js">
|
|
177
177
|
<link rel="stylesheet" crossorigin href="/assets/monaco-langs-DyX1CsEw.css">
|
|
178
|
-
<link rel="stylesheet" crossorigin href="/assets/index-
|
|
178
|
+
<link rel="stylesheet" crossorigin href="/assets/index-CloKwceI.css">
|
|
179
179
|
</head>
|
|
180
180
|
<body>
|
|
181
181
|
<div id="app"></div>
|
package/package.json
CHANGED
|
@@ -29,7 +29,19 @@ const execFileAsync = promisify(execFile);
|
|
|
29
29
|
// NOTE: 'plugin_opt_dynsec_config_file' is kept here only for migration —
|
|
30
30
|
// when read from an old conf it is normalised to 'plugin_opt_config_file' on
|
|
31
31
|
// the same line that recognises it (see parseText below).
|
|
32
|
-
const MANAGED_SINGLE_KEYS = new Set([
|
|
32
|
+
const MANAGED_SINGLE_KEYS = new Set([
|
|
33
|
+
'per_listener_settings', 'allow_anonymous',
|
|
34
|
+
'persistence', 'persistence_location',
|
|
35
|
+
'log_dest', 'log_type',
|
|
36
|
+
'plugin', 'plugin_opt_config_file', 'plugin_opt_dynsec_config_file',
|
|
37
|
+
// Connection limits
|
|
38
|
+
'max_connections', 'max_inflight_messages', 'max_queued_messages',
|
|
39
|
+
'max_packet_size', 'message_size_limit', 'max_keepalive',
|
|
40
|
+
// Sessions
|
|
41
|
+
'persistent_client_expiration', 'retain_available',
|
|
42
|
+
// Performance / misc
|
|
43
|
+
'set_tcp_nodelay', 'connection_messages',
|
|
44
|
+
]);
|
|
33
45
|
|
|
34
46
|
/**
|
|
35
47
|
* Parse mosquitto.conf text into a structured object.
|
|
@@ -112,6 +124,9 @@ function isListenerSubkey(key) {
|
|
|
112
124
|
return [
|
|
113
125
|
'protocol',
|
|
114
126
|
'socket_domain',
|
|
127
|
+
'mount_point',
|
|
128
|
+
'max_connections',
|
|
129
|
+
'max_qos',
|
|
115
130
|
'certfile',
|
|
116
131
|
'keyfile',
|
|
117
132
|
'cafile',
|
|
@@ -119,6 +134,7 @@ function isListenerSubkey(key) {
|
|
|
119
134
|
'crlfile',
|
|
120
135
|
'require_certificate',
|
|
121
136
|
'use_identity_as_username',
|
|
137
|
+
'use_subject_as_username',
|
|
122
138
|
'tls_version',
|
|
123
139
|
'websockets_log_level',
|
|
124
140
|
'allow_anonymous',
|
|
@@ -130,6 +146,15 @@ function applyListenerKey(listener, key, value) {
|
|
|
130
146
|
case 'protocol':
|
|
131
147
|
listener.protocol = value;
|
|
132
148
|
break;
|
|
149
|
+
case 'mount_point':
|
|
150
|
+
listener.mount_point = value;
|
|
151
|
+
break;
|
|
152
|
+
case 'max_connections':
|
|
153
|
+
listener.max_connections = parseInt(value, 10);
|
|
154
|
+
break;
|
|
155
|
+
case 'max_qos':
|
|
156
|
+
listener.max_qos = parseInt(value, 10);
|
|
157
|
+
break;
|
|
133
158
|
case 'certfile':
|
|
134
159
|
case 'keyfile':
|
|
135
160
|
case 'cafile':
|
|
@@ -144,6 +169,9 @@ function applyListenerKey(listener, key, value) {
|
|
|
144
169
|
case 'use_identity_as_username':
|
|
145
170
|
listener.tls.use_identity_as_username = value === 'true';
|
|
146
171
|
break;
|
|
172
|
+
case 'use_subject_as_username':
|
|
173
|
+
listener.tls.use_subject_as_username = value === 'true';
|
|
174
|
+
break;
|
|
147
175
|
case 'allow_anonymous':
|
|
148
176
|
listener.allow_anonymous = value === 'true';
|
|
149
177
|
break;
|
|
@@ -164,7 +192,16 @@ function serialise(conf) {
|
|
|
164
192
|
// Managed single-key entries first
|
|
165
193
|
const { managed = {}, listeners = [], passthrough = [] } = conf;
|
|
166
194
|
|
|
167
|
-
const keyOrder = [
|
|
195
|
+
const keyOrder = [
|
|
196
|
+
'per_listener_settings', 'allow_anonymous',
|
|
197
|
+
'persistence', 'persistence_location',
|
|
198
|
+
'max_connections', 'max_inflight_messages', 'max_queued_messages',
|
|
199
|
+
'max_packet_size', 'message_size_limit', 'max_keepalive',
|
|
200
|
+
'persistent_client_expiration', 'retain_available',
|
|
201
|
+
'set_tcp_nodelay', 'connection_messages',
|
|
202
|
+
'log_dest', 'log_type',
|
|
203
|
+
'plugin', 'plugin_opt_config_file',
|
|
204
|
+
];
|
|
168
205
|
for (const key of keyOrder) {
|
|
169
206
|
if (managed[key] === undefined) continue;
|
|
170
207
|
const val = managed[key];
|
|
@@ -182,6 +219,9 @@ function serialise(conf) {
|
|
|
182
219
|
const addr = l.bindAddress ? ` ${l.bindAddress}` : '';
|
|
183
220
|
lines.push(`listener ${l.port}${addr}`);
|
|
184
221
|
if (l.protocol && l.protocol !== 'mqtt') lines.push(`protocol ${l.protocol}`);
|
|
222
|
+
if (l.mount_point) lines.push(`mount_point ${l.mount_point}`);
|
|
223
|
+
if (l.max_connections !== undefined && l.max_connections !== null) lines.push(`max_connections ${l.max_connections}`);
|
|
224
|
+
if (l.max_qos !== undefined && l.max_qos !== null) lines.push(`max_qos ${l.max_qos}`);
|
|
185
225
|
const tls = l.tls || {};
|
|
186
226
|
for (const tlsKey of ['certfile', 'keyfile', 'cafile', 'capath', 'crlfile', 'tls_version']) {
|
|
187
227
|
if (tls[tlsKey]) lines.push(`${tlsKey} ${tls[tlsKey]}`);
|
|
@@ -192,6 +232,9 @@ function serialise(conf) {
|
|
|
192
232
|
if (tls.use_identity_as_username !== undefined) {
|
|
193
233
|
lines.push(`use_identity_as_username ${tls.use_identity_as_username ? 'true' : 'false'}`);
|
|
194
234
|
}
|
|
235
|
+
if (tls.use_subject_as_username !== undefined) {
|
|
236
|
+
lines.push(`use_subject_as_username ${tls.use_subject_as_username ? 'true' : 'false'}`);
|
|
237
|
+
}
|
|
195
238
|
if (l.allow_anonymous !== undefined) {
|
|
196
239
|
lines.push(`allow_anonymous ${l.allow_anonymous ? 'true' : 'false'}`);
|
|
197
240
|
}
|
package/src/web/broker-api.js
CHANGED
|
@@ -17,6 +17,9 @@ const express = require('express');
|
|
|
17
17
|
const path = require('path');
|
|
18
18
|
const fs = require('fs');
|
|
19
19
|
const crypto = require('crypto');
|
|
20
|
+
const { execFile, spawn } = require('child_process');
|
|
21
|
+
const { promisify } = require('util');
|
|
22
|
+
const execFileAsync = promisify(execFile);
|
|
20
23
|
const dynsec = require('../lib/dynsec');
|
|
21
24
|
const mosquittoConf = require('../lib/mosquitto-conf');
|
|
22
25
|
const ca = require('../lib/ca');
|
|
@@ -947,7 +950,7 @@ module.exports = { router, setLogger, setStore };
|
|
|
947
950
|
router.get('/local/check', async (req, res) => {
|
|
948
951
|
function probe(cmd) {
|
|
949
952
|
return new Promise((resolve) => {
|
|
950
|
-
const cp =
|
|
953
|
+
const cp = spawn(cmd, ['--help'], { stdio: 'ignore' });
|
|
951
954
|
cp.on('error', (e) => resolve(e.code !== 'ENOENT'));
|
|
952
955
|
cp.on('close', () => resolve(true));
|
|
953
956
|
});
|
|
@@ -960,6 +963,39 @@ router.get('/local/check', async (req, res) => {
|
|
|
960
963
|
}
|
|
961
964
|
});
|
|
962
965
|
|
|
966
|
+
// ── IP address listing ────────────────────────────────────────────────────────
|
|
967
|
+
|
|
968
|
+
/**
|
|
969
|
+
* GET /she/broker/ip-addresses
|
|
970
|
+
* List host IP addresses for bind-address autocomplete.
|
|
971
|
+
* Runs `ip a` locally or via SSH depending on broker.ssh configuration.
|
|
972
|
+
*/
|
|
973
|
+
router.get('/ip-addresses', async (req, res) => {
|
|
974
|
+
const bc = getBrokerConfig(req);
|
|
975
|
+
try {
|
|
976
|
+
let stdout = '';
|
|
977
|
+
if (bc.ssh && bc.ssh.host) {
|
|
978
|
+
const result = await sshDeploy.runCommand(bc.ssh, 'ip a 2>/dev/null || ip addr 2>/dev/null');
|
|
979
|
+
stdout = result.stdout;
|
|
980
|
+
} else {
|
|
981
|
+
const result = await execFileAsync('ip', ['a'], { timeout: 5000 }).catch(() =>
|
|
982
|
+
execFileAsync('ip', ['addr'], { timeout: 5000 }),
|
|
983
|
+
);
|
|
984
|
+
stdout = result.stdout;
|
|
985
|
+
}
|
|
986
|
+
// Extract IPv4 and IPv6 addresses; skip loopback
|
|
987
|
+
const addresses = [];
|
|
988
|
+
for (const m of stdout.matchAll(/inet6?\s+([\da-f.:]+)(?:\/\d+)?/gi)) {
|
|
989
|
+
const addr = m[1];
|
|
990
|
+
if (addr === '127.0.0.1' || addr === '::1') continue;
|
|
991
|
+
addresses.push(addr);
|
|
992
|
+
}
|
|
993
|
+
res.json({ addresses: [...new Set(addresses)] });
|
|
994
|
+
} catch {
|
|
995
|
+
res.json({ addresses: [] });
|
|
996
|
+
}
|
|
997
|
+
});
|
|
998
|
+
|
|
963
999
|
// ── SSH routes ─────────────────────────────────────────────────────────────────
|
|
964
1000
|
// Note: these routes are mounted on the same router but defined after module.exports
|
|
965
1001
|
// because they add to `router` (which is already exported by reference).
|