smart-home-engine 1.1.5 → 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.
|
@@ -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,7 +172,7 @@
|
|
|
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
178
|
<link rel="stylesheet" crossorigin href="/assets/index-iTrR9H4f.css">
|
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
|
});
|
|
@@ -211,12 +224,17 @@ router.post('/restart', async (req, res) => {
|
|
|
211
224
|
const bc = getBrokerConfig(req);
|
|
212
225
|
if (bc.ssh && bc.ssh.host) {
|
|
213
226
|
const cmd = bc.restartCmd || 'sudo systemctl restart mosquitto';
|
|
227
|
+
_log?.debug(`broker: remote restart on ${bc.ssh.host}: ${cmd}`);
|
|
214
228
|
const result = await sshDeploy.runCommand(bc.ssh, cmd);
|
|
229
|
+
_log?.debug(`broker: remote restart stdout=${result.stdout} stderr=${result.stderr}`);
|
|
215
230
|
return res.json({ ok: true, ...result });
|
|
216
231
|
}
|
|
232
|
+
_log?.debug('broker: local restart mosquitto');
|
|
217
233
|
const result = await mosquittoConf.restart(bc);
|
|
234
|
+
_log?.debug(`broker: local restart stdout=${result.stdout} stderr=${result.stderr}`);
|
|
218
235
|
res.json({ ok: true, stdout: result.stdout, stderr: result.stderr });
|
|
219
236
|
} catch (err) {
|
|
237
|
+
_log?.debug(`broker: restart error: ${err.message}`);
|
|
220
238
|
handleError(res, err);
|
|
221
239
|
}
|
|
222
240
|
});
|
|
@@ -655,7 +673,7 @@ router.delete('/ca/trusted/:fingerprint', async (req, res) => {
|
|
|
655
673
|
}
|
|
656
674
|
});
|
|
657
675
|
|
|
658
|
-
module.exports = { router };
|
|
676
|
+
module.exports = { router, setLogger };
|
|
659
677
|
|
|
660
678
|
// ── SSH routes ─────────────────────────────────────────────────────────────────
|
|
661
679
|
// Note: these routes are mounted on the same router but defined after module.exports
|
|
@@ -680,9 +698,12 @@ router.post('/ssh/keygen', async (req, res) => {
|
|
|
680
698
|
try {
|
|
681
699
|
const bc = getBrokerConfig(req);
|
|
682
700
|
const identityFile = (bc.ssh && bc.ssh.identityFile) || DEFAULT_SSH_KEY;
|
|
701
|
+
_log?.debug(`broker: generating SSH keypair at ${identityFile}`);
|
|
683
702
|
const publicKey = await sshDeploy.generateKeypair(identityFile);
|
|
703
|
+
_log?.debug('broker: SSH keypair generated ok');
|
|
684
704
|
res.json({ ok: true, publicKey });
|
|
685
705
|
} catch (err) {
|
|
706
|
+
_log?.debug(`broker: SSH keygen error: ${err.message}`);
|
|
686
707
|
handleError(res, err);
|
|
687
708
|
}
|
|
688
709
|
});
|
|
@@ -692,9 +713,14 @@ router.post('/ssh/test', async (req, res) => {
|
|
|
692
713
|
try {
|
|
693
714
|
const bc = getBrokerConfig(req);
|
|
694
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}`);
|
|
695
719
|
await sshDeploy.testConnection(bc.ssh);
|
|
720
|
+
_log?.debug(`broker: SSH connection to ${bc.ssh.host} ok`);
|
|
696
721
|
res.json({ ok: true });
|
|
697
722
|
} catch (err) {
|
|
723
|
+
_log?.debug(`broker: SSH test to ${bc.ssh && bc.ssh.host} failed: ${err.message}`);
|
|
698
724
|
res.json({ ok: false, error: err.message });
|
|
699
725
|
}
|
|
700
726
|
});
|
|
@@ -741,11 +767,17 @@ router.post('/wizard/bootstrap', async (req, res) => {
|
|
|
741
767
|
const dynSecPath = `${configDir}/dynamic-security.json`;
|
|
742
768
|
const confFilePath = `${configDir}/mosquitto.conf`;
|
|
743
769
|
|
|
770
|
+
_log?.debug(`broker: wizard bootstrap mode=${isRemote ? 'remote' : 'local'} configDir=${configDir} adminUser=${username}`);
|
|
771
|
+
|
|
744
772
|
if (isRemote) {
|
|
745
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}" ***`);
|
|
746
776
|
try {
|
|
747
|
-
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}`);
|
|
748
779
|
} catch (err) {
|
|
780
|
+
_log?.debug(`broker: mosquitto_ctrl SSH failed: ${err.message}`);
|
|
749
781
|
return res.status(500).json({
|
|
750
782
|
error: `mosquitto_ctrl failed on remote host: ${err.message}. Ensure mosquitto is installed on the remote broker host.`,
|
|
751
783
|
});
|
|
@@ -754,23 +786,32 @@ router.post('/wizard/bootstrap', async (req, res) => {
|
|
|
754
786
|
// Read the remote mosquitto.conf, parse, and add the plugin line if missing.
|
|
755
787
|
let remoteConfRaw = '';
|
|
756
788
|
try {
|
|
789
|
+
_log?.debug(`broker: reading remote conf ${bc.ssh.host}:${confFilePath}`);
|
|
757
790
|
remoteConfRaw = await sshDeploy.readRemoteFile(bc.ssh, confFilePath);
|
|
758
|
-
|
|
759
|
-
|
|
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`);
|
|
760
794
|
}
|
|
761
795
|
const parsed = mosquittoConf.parseText(remoteConfRaw);
|
|
762
796
|
if (!parsed.managed.plugin || !String(parsed.managed.plugin).includes('mosquitto_dynamic_security')) {
|
|
763
797
|
parsed.managed.plugin = 'mosquitto_dynamic_security.so';
|
|
764
798
|
parsed.managed.plugin_opt_dynsec_config_file = dynSecPath;
|
|
765
799
|
const content = mosquittoConf.serialise(parsed);
|
|
800
|
+
_log?.debug(`broker: uploading updated conf to ${bc.ssh.host}:${confFilePath}`);
|
|
766
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');
|
|
767
805
|
}
|
|
768
806
|
} else {
|
|
769
807
|
// Local mode: run mosquitto_ctrl on this host.
|
|
770
808
|
fs.mkdirSync(configDir, { recursive: true });
|
|
809
|
+
_log?.debug(`broker: local mosquitto_ctrl dynsec init ${dynSecPath} ${username} ***`);
|
|
771
810
|
try {
|
|
772
|
-
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}`);
|
|
773
813
|
} catch (err) {
|
|
814
|
+
_log?.debug(`broker: local mosquitto_ctrl failed: ${err.message}`);
|
|
774
815
|
return res.status(500).json({
|
|
775
816
|
error: `mosquitto_ctrl failed: ${err.message}. Ensure mosquitto is installed on this host.`,
|
|
776
817
|
});
|
|
@@ -782,7 +823,11 @@ router.post('/wizard/bootstrap', async (req, res) => {
|
|
|
782
823
|
parsed.managed.plugin = 'mosquitto_dynamic_security.so';
|
|
783
824
|
parsed.managed.plugin_opt_dynsec_config_file = dynSecPath;
|
|
784
825
|
const content = mosquittoConf.serialise(parsed);
|
|
826
|
+
_log?.debug(`broker: writing updated local conf to ${confFilePath}`);
|
|
785
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');
|
|
786
831
|
}
|
|
787
832
|
}
|
|
788
833
|
|