smart-home-engine 1.1.4 → 1.1.6
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-DmNh4UvK.js → index-DRKYxwDj.js} +3 -3
- package/dist/web/assets/{index-DKIgEFlE.css → index-iTrR9H4f.css} +1 -1
- package/dist/web/assets/{tsMode-C5Yyw0IM.js → tsMode-CY_XZkxX.js} +1 -1
- package/dist/web/index.html +2 -2
- package/package.json +1 -1
- package/src/index.js +1 -0
- package/src/lib/dynsec.js +15 -0
- package/src/web/broker-api.js +51 -5
|
@@ -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-DRKYxwDj.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-DRKYxwDj.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-iTrR9H4f.css">
|
|
179
179
|
</head>
|
|
180
180
|
<body>
|
|
181
181
|
<div id="app"></div>
|
package/package.json
CHANGED
package/src/index.js
CHANGED
package/src/lib/dynsec.js
CHANGED
|
@@ -39,9 +39,14 @@ function _drain() {
|
|
|
39
39
|
const { command, payload, resolve, reject } = _queue.shift();
|
|
40
40
|
_inflight = true;
|
|
41
41
|
|
|
42
|
+
const safePayload = { ...payload };
|
|
43
|
+
if ('password' in safePayload) safePayload.password = '***';
|
|
44
|
+
if (_log) _log.debug(`dynsec: → sending "${command}"`, JSON.stringify(safePayload));
|
|
45
|
+
|
|
42
46
|
const timer = setTimeout(() => {
|
|
43
47
|
_inflight = false;
|
|
44
48
|
_inflightResolve = null;
|
|
49
|
+
if (_log) _log.debug(`dynsec: timeout waiting for response to "${command}" (${_timeout}ms)`);
|
|
45
50
|
reject(new Error(`dynsec timeout waiting for response to "${command}"`));
|
|
46
51
|
_drain();
|
|
47
52
|
}, _timeout);
|
|
@@ -52,8 +57,10 @@ function _drain() {
|
|
|
52
57
|
_inflightResolve = null;
|
|
53
58
|
const r = responses.find((resp) => resp.command === command);
|
|
54
59
|
if (r && r.error) {
|
|
60
|
+
if (_log) _log.debug(`dynsec: ✕ "${command}" error: ${r.error}`);
|
|
55
61
|
reject(new Error(r.error));
|
|
56
62
|
} else {
|
|
63
|
+
if (_log) _log.debug(`dynsec: ✓ "${command}" ok`);
|
|
57
64
|
resolve(r || {});
|
|
58
65
|
}
|
|
59
66
|
_drain();
|
|
@@ -67,8 +74,12 @@ function _request(command, payload = {}) {
|
|
|
67
74
|
return Promise.reject(new Error('she.broker: dynsec not configured — set broker.dynsec in config.json'));
|
|
68
75
|
}
|
|
69
76
|
if (!_connected) {
|
|
77
|
+
if (_log) _log.debug(`dynsec: request "${command}" rejected — not connected (queue length: ${_queue.length})`);
|
|
70
78
|
return Promise.reject(new Error('she.broker: dynsec not connected'));
|
|
71
79
|
}
|
|
80
|
+
const safePayload = { ...payload };
|
|
81
|
+
if ('password' in safePayload) safePayload.password = '***';
|
|
82
|
+
if (_log) _log.debug(`dynsec: queuing "${command}" (queue length: ${_queue.length}, inflight: ${_inflight})`, JSON.stringify(safePayload));
|
|
72
83
|
return new Promise((resolve, reject) => {
|
|
73
84
|
_queue.push({ command, payload, resolve, reject });
|
|
74
85
|
_drain();
|
|
@@ -139,8 +150,12 @@ function init(config, log) {
|
|
|
139
150
|
_log.error('dynsec: invalid JSON on response topic');
|
|
140
151
|
return;
|
|
141
152
|
}
|
|
153
|
+
const cmds = Array.isArray(msg.responses) ? msg.responses.map((r) => r.command).join(', ') : 'none';
|
|
154
|
+
if (_log) _log.debug(`dynsec: ← response received, commands: [${cmds}]`);
|
|
142
155
|
if (_inflightResolve && Array.isArray(msg.responses)) {
|
|
143
156
|
_inflightResolve(msg.responses);
|
|
157
|
+
} else if (!_inflightResolve) {
|
|
158
|
+
if (_log) _log.debug(`dynsec: unexpected response (no inflight request), commands: [${cmds}]`);
|
|
144
159
|
}
|
|
145
160
|
});
|
|
146
161
|
}
|
package/src/web/broker-api.js
CHANGED
|
@@ -27,6 +27,13 @@ const DEFAULT_SSH_KEY = path.join(sheConfig['data-dir'], 'ssh', 'broker_id_ed255
|
|
|
27
27
|
|
|
28
28
|
const router = express.Router();
|
|
29
29
|
|
|
30
|
+
let _log = null;
|
|
31
|
+
|
|
32
|
+
/** Must be called once from index.js so broker-api can emit debug-level log lines. */
|
|
33
|
+
function setLogger(log) {
|
|
34
|
+
_log = log;
|
|
35
|
+
}
|
|
36
|
+
|
|
30
37
|
// ── Helpers ────────────────────────────────────────────────────────────────────
|
|
31
38
|
|
|
32
39
|
/** Get broker config from live config.json */
|
|
@@ -191,12 +198,18 @@ router.post('/reload', async (req, res) => {
|
|
|
191
198
|
try {
|
|
192
199
|
const bc = getBrokerConfig(req);
|
|
193
200
|
if (bc.ssh && bc.ssh.host) {
|
|
201
|
+
const cmd = bc.reloadCmd || 'sudo systemctl reload mosquitto';
|
|
202
|
+
_log?.debug(`broker: remote reload on ${bc.ssh.host}: ${cmd}`);
|
|
194
203
|
const result = await sshDeploy.runCommand(bc.ssh, cmd);
|
|
204
|
+
_log?.debug(`broker: remote reload stdout=${result.stdout} stderr=${result.stderr}`);
|
|
195
205
|
return res.json({ ok: true, ...result });
|
|
196
206
|
}
|
|
207
|
+
_log?.debug('broker: local reload mosquitto');
|
|
197
208
|
const result = await mosquittoConf.reload(bc);
|
|
209
|
+
_log?.debug(`broker: local reload stdout=${result.stdout} stderr=${result.stderr}`);
|
|
198
210
|
res.json({ ok: true, stdout: result.stdout, stderr: result.stderr });
|
|
199
211
|
} catch (err) {
|
|
212
|
+
_log?.debug(`broker: reload error: ${err.message}`);
|
|
200
213
|
handleError(res, err);
|
|
201
214
|
}
|
|
202
215
|
});
|
|
@@ -210,12 +223,18 @@ router.post('/restart', async (req, res) => {
|
|
|
210
223
|
try {
|
|
211
224
|
const bc = getBrokerConfig(req);
|
|
212
225
|
if (bc.ssh && bc.ssh.host) {
|
|
226
|
+
const cmd = bc.restartCmd || 'sudo systemctl restart mosquitto';
|
|
227
|
+
_log?.debug(`broker: remote restart on ${bc.ssh.host}: ${cmd}`);
|
|
213
228
|
const result = await sshDeploy.runCommand(bc.ssh, cmd);
|
|
229
|
+
_log?.debug(`broker: remote restart stdout=${result.stdout} stderr=${result.stderr}`);
|
|
214
230
|
return res.json({ ok: true, ...result });
|
|
215
231
|
}
|
|
232
|
+
_log?.debug('broker: local restart mosquitto');
|
|
216
233
|
const result = await mosquittoConf.restart(bc);
|
|
234
|
+
_log?.debug(`broker: local restart stdout=${result.stdout} stderr=${result.stderr}`);
|
|
217
235
|
res.json({ ok: true, stdout: result.stdout, stderr: result.stderr });
|
|
218
236
|
} catch (err) {
|
|
237
|
+
_log?.debug(`broker: restart error: ${err.message}`);
|
|
219
238
|
handleError(res, err);
|
|
220
239
|
}
|
|
221
240
|
});
|
|
@@ -654,7 +673,7 @@ router.delete('/ca/trusted/:fingerprint', async (req, res) => {
|
|
|
654
673
|
}
|
|
655
674
|
});
|
|
656
675
|
|
|
657
|
-
module.exports = { router };
|
|
676
|
+
module.exports = { router, setLogger };
|
|
658
677
|
|
|
659
678
|
// ── SSH routes ─────────────────────────────────────────────────────────────────
|
|
660
679
|
// Note: these routes are mounted on the same router but defined after module.exports
|
|
@@ -679,9 +698,12 @@ router.post('/ssh/keygen', async (req, res) => {
|
|
|
679
698
|
try {
|
|
680
699
|
const bc = getBrokerConfig(req);
|
|
681
700
|
const identityFile = (bc.ssh && bc.ssh.identityFile) || DEFAULT_SSH_KEY;
|
|
701
|
+
_log?.debug(`broker: generating SSH keypair at ${identityFile}`);
|
|
682
702
|
const publicKey = await sshDeploy.generateKeypair(identityFile);
|
|
703
|
+
_log?.debug('broker: SSH keypair generated ok');
|
|
683
704
|
res.json({ ok: true, publicKey });
|
|
684
705
|
} catch (err) {
|
|
706
|
+
_log?.debug(`broker: SSH keygen error: ${err.message}`);
|
|
685
707
|
handleError(res, err);
|
|
686
708
|
}
|
|
687
709
|
});
|
|
@@ -691,9 +713,14 @@ router.post('/ssh/test', async (req, res) => {
|
|
|
691
713
|
try {
|
|
692
714
|
const bc = getBrokerConfig(req);
|
|
693
715
|
if (!bc.ssh || !bc.ssh.host) return res.status(400).json({ error: 'broker.ssh.host not configured' });
|
|
716
|
+
const user = (bc.ssh && bc.ssh.user) || require('os').userInfo().username;
|
|
717
|
+
const key = sshDeploy.expandHome((bc.ssh && bc.ssh.identityFile) || DEFAULT_SSH_KEY);
|
|
718
|
+
_log?.debug(`broker: testing SSH to ${user}@${bc.ssh.host}:${bc.ssh.port || 22} key=${key}`);
|
|
694
719
|
await sshDeploy.testConnection(bc.ssh);
|
|
720
|
+
_log?.debug(`broker: SSH connection to ${bc.ssh.host} ok`);
|
|
695
721
|
res.json({ ok: true });
|
|
696
722
|
} catch (err) {
|
|
723
|
+
_log?.debug(`broker: SSH test to ${bc.ssh && bc.ssh.host} failed: ${err.message}`);
|
|
697
724
|
res.json({ ok: false, error: err.message });
|
|
698
725
|
}
|
|
699
726
|
});
|
|
@@ -740,11 +767,17 @@ router.post('/wizard/bootstrap', async (req, res) => {
|
|
|
740
767
|
const dynSecPath = `${configDir}/dynamic-security.json`;
|
|
741
768
|
const confFilePath = `${configDir}/mosquitto.conf`;
|
|
742
769
|
|
|
770
|
+
_log?.debug(`broker: wizard bootstrap mode=${isRemote ? 'remote' : 'local'} configDir=${configDir} adminUser=${username}`);
|
|
771
|
+
|
|
743
772
|
if (isRemote) {
|
|
744
773
|
// mosquitto_ctrl must run on the broker host — invoke it via SSH.
|
|
774
|
+
const ctrlCmd = `mosquitto_ctrl dynsec init "${dynSecPath}" "${username}" "${password}"`;
|
|
775
|
+
_log?.debug(`broker: SSH mosquitto_ctrl on ${bc.ssh.host}: mosquitto_ctrl dynsec init "${dynSecPath}" "${username}" ***`);
|
|
745
776
|
try {
|
|
746
|
-
await sshDeploy.runCommand(bc.ssh,
|
|
777
|
+
const r = await sshDeploy.runCommand(bc.ssh, ctrlCmd);
|
|
778
|
+
_log?.debug(`broker: mosquitto_ctrl ok stdout=${r.stdout} stderr=${r.stderr}`);
|
|
747
779
|
} catch (err) {
|
|
780
|
+
_log?.debug(`broker: mosquitto_ctrl SSH failed: ${err.message}`);
|
|
748
781
|
return res.status(500).json({
|
|
749
782
|
error: `mosquitto_ctrl failed on remote host: ${err.message}. Ensure mosquitto is installed on the remote broker host.`,
|
|
750
783
|
});
|
|
@@ -753,23 +786,32 @@ router.post('/wizard/bootstrap', async (req, res) => {
|
|
|
753
786
|
// Read the remote mosquitto.conf, parse, and add the plugin line if missing.
|
|
754
787
|
let remoteConfRaw = '';
|
|
755
788
|
try {
|
|
789
|
+
_log?.debug(`broker: reading remote conf ${bc.ssh.host}:${confFilePath}`);
|
|
756
790
|
remoteConfRaw = await sshDeploy.readRemoteFile(bc.ssh, confFilePath);
|
|
757
|
-
|
|
758
|
-
|
|
791
|
+
_log?.debug(`broker: remote conf read ok (${remoteConfRaw.length} bytes)`);
|
|
792
|
+
} catch (e) {
|
|
793
|
+
_log?.debug(`broker: remote conf read failed (${e.message}), starting from empty config`);
|
|
759
794
|
}
|
|
760
795
|
const parsed = mosquittoConf.parseText(remoteConfRaw);
|
|
761
796
|
if (!parsed.managed.plugin || !String(parsed.managed.plugin).includes('mosquitto_dynamic_security')) {
|
|
762
797
|
parsed.managed.plugin = 'mosquitto_dynamic_security.so';
|
|
763
798
|
parsed.managed.plugin_opt_dynsec_config_file = dynSecPath;
|
|
764
799
|
const content = mosquittoConf.serialise(parsed);
|
|
800
|
+
_log?.debug(`broker: uploading updated conf to ${bc.ssh.host}:${confFilePath}`);
|
|
765
801
|
await sshDeploy.uploadContent(bc.ssh, content, confFilePath);
|
|
802
|
+
_log?.debug('broker: conf upload ok');
|
|
803
|
+
} else {
|
|
804
|
+
_log?.debug('broker: plugin line already present in remote conf, skipping upload');
|
|
766
805
|
}
|
|
767
806
|
} else {
|
|
768
807
|
// Local mode: run mosquitto_ctrl on this host.
|
|
769
808
|
fs.mkdirSync(configDir, { recursive: true });
|
|
809
|
+
_log?.debug(`broker: local mosquitto_ctrl dynsec init ${dynSecPath} ${username} ***`);
|
|
770
810
|
try {
|
|
771
|
-
await execFileAsync('mosquitto_ctrl', ['dynsec', 'init', dynSecPath, username, password], { timeout: 10000 });
|
|
811
|
+
const r = await execFileAsync('mosquitto_ctrl', ['dynsec', 'init', dynSecPath, username, password], { timeout: 10000 });
|
|
812
|
+
_log?.debug(`broker: mosquitto_ctrl ok stdout=${r.stdout} stderr=${r.stderr}`);
|
|
772
813
|
} catch (err) {
|
|
814
|
+
_log?.debug(`broker: local mosquitto_ctrl failed: ${err.message}`);
|
|
773
815
|
return res.status(500).json({
|
|
774
816
|
error: `mosquitto_ctrl failed: ${err.message}. Ensure mosquitto is installed on this host.`,
|
|
775
817
|
});
|
|
@@ -781,7 +823,11 @@ router.post('/wizard/bootstrap', async (req, res) => {
|
|
|
781
823
|
parsed.managed.plugin = 'mosquitto_dynamic_security.so';
|
|
782
824
|
parsed.managed.plugin_opt_dynsec_config_file = dynSecPath;
|
|
783
825
|
const content = mosquittoConf.serialise(parsed);
|
|
826
|
+
_log?.debug(`broker: writing updated local conf to ${confFilePath}`);
|
|
784
827
|
mosquittoConf.write(confFilePath, content);
|
|
828
|
+
_log?.debug('broker: local conf write ok');
|
|
829
|
+
} else {
|
|
830
|
+
_log?.debug('broker: plugin line already present in local conf, skipping write');
|
|
785
831
|
}
|
|
786
832
|
}
|
|
787
833
|
|