@matterbridge/core 3.10.13-dev-20261005-49e0dbd → 3.10.13-dev-20261007-320f739
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/cli.js +5 -0
- package/dist/deviceManager.d.ts +2 -1
- package/dist/deviceManager.js +29 -0
- package/dist/frontend.d.ts +1 -5
- package/dist/frontend.js +19 -300
- package/dist/matterNode.d.ts +3 -8
- package/dist/matterNode.js +25 -115
- package/dist/matterNodeHelpers.d.ts +15 -0
- package/dist/matterNodeHelpers.js +393 -0
- package/dist/matterbridge.d.ts +1 -6
- package/dist/matterbridge.js +48 -116
- package/dist/pluginManager.js +2 -1
- package/package.json +5 -5
- package/dist/backend.d.ts +0 -37
- package/dist/backend.js +0 -308
- package/dist/backendExpress.d.ts +0 -20
- package/dist/backendExpress.js +0 -453
- package/dist/backendWsServer.d.ts +0 -38
- package/dist/backendWsServer.js +0 -326
package/dist/backend.js
DELETED
|
@@ -1,308 +0,0 @@
|
|
|
1
|
-
import EventEmitter from 'node:events';
|
|
2
|
-
import path from 'node:path';
|
|
3
|
-
import { BroadcastServer } from '@matterbridge/thread';
|
|
4
|
-
import { getParameter, hasParameter } from '@matterbridge/utils/cli';
|
|
5
|
-
import { getErrorMessage, inspectError, logError } from '@matterbridge/utils/error';
|
|
6
|
-
import { logModuleLoaded } from '@matterbridge/utils/loader';
|
|
7
|
-
import { AnsiLogger, rs, UNDERLINE, UNDERLINEOFF } from 'node-ansi-logger';
|
|
8
|
-
logModuleLoaded('Backend');
|
|
9
|
-
export class Backend extends EventEmitter {
|
|
10
|
-
debug;
|
|
11
|
-
verbose;
|
|
12
|
-
log;
|
|
13
|
-
matterbridge;
|
|
14
|
-
server;
|
|
15
|
-
port = 8283;
|
|
16
|
-
listening = false;
|
|
17
|
-
httpServer;
|
|
18
|
-
httpsServer;
|
|
19
|
-
backendExpress;
|
|
20
|
-
backendWsServer;
|
|
21
|
-
storedPassword = undefined;
|
|
22
|
-
authClients = new Set();
|
|
23
|
-
authClientsTimeout = undefined;
|
|
24
|
-
constructor(matterbridge) {
|
|
25
|
-
super();
|
|
26
|
-
this.debug = hasParameter('debug') || hasParameter('verbose') || hasParameter('debug-frontend') || hasParameter('verbose-frontend');
|
|
27
|
-
this.verbose = hasParameter('verbose') || hasParameter('verbose-frontend');
|
|
28
|
-
this.matterbridge = matterbridge;
|
|
29
|
-
this.log = new AnsiLogger({
|
|
30
|
-
logName: 'Backend',
|
|
31
|
-
logNameColor: '\x1b[38;5;97m',
|
|
32
|
-
logTimestampFormat: 4,
|
|
33
|
-
logLevel: this.debug ? "debug" : "info",
|
|
34
|
-
});
|
|
35
|
-
this.server = new BroadcastServer('frontend', this.log);
|
|
36
|
-
this.server.on('broadcast_message', this.broadcastMsgHandler.bind(this));
|
|
37
|
-
}
|
|
38
|
-
destroy() {
|
|
39
|
-
this.server.close();
|
|
40
|
-
}
|
|
41
|
-
async broadcastMsgHandler(msg) {
|
|
42
|
-
if (this.server.isWorkerRequest(msg)) {
|
|
43
|
-
switch (msg.type) {
|
|
44
|
-
case 'get_log_level':
|
|
45
|
-
this.server.respond({ ...msg, result: { logLevel: this.log.logLevel } });
|
|
46
|
-
break;
|
|
47
|
-
case 'set_log_level':
|
|
48
|
-
this.log.logLevel = msg.params.logLevel;
|
|
49
|
-
this.server.respond({ ...msg, result: { logLevel: this.log.logLevel } });
|
|
50
|
-
break;
|
|
51
|
-
}
|
|
52
|
-
}
|
|
53
|
-
}
|
|
54
|
-
async start(port = 8283) {
|
|
55
|
-
this.log.debug('Starting backend...');
|
|
56
|
-
this.port = port;
|
|
57
|
-
const { BackendExpress } = await import('./backendExpress.js');
|
|
58
|
-
const { BackendWsServer } = await import('./backendWsServer.js');
|
|
59
|
-
this.backendExpress = new BackendExpress(this.matterbridge, this);
|
|
60
|
-
this.backendWsServer = new BackendWsServer(this.matterbridge, this);
|
|
61
|
-
if (!hasParameter('ssl')) {
|
|
62
|
-
const http = await import('node:http');
|
|
63
|
-
await this.backendExpress.start();
|
|
64
|
-
try {
|
|
65
|
-
this.log.debug(`Creating HTTP server...`);
|
|
66
|
-
this.httpServer = http.createServer(this.backendExpress.expressApp);
|
|
67
|
-
}
|
|
68
|
-
catch (error) {
|
|
69
|
-
logError(this.log, `Failed to create HTTP server`, error);
|
|
70
|
-
this.emit('server_error', error);
|
|
71
|
-
await this.backendExpress.stop();
|
|
72
|
-
return;
|
|
73
|
-
}
|
|
74
|
-
await this.backendWsServer.start();
|
|
75
|
-
this.httpServer.listen(this.port, getParameter('bind'), () => {
|
|
76
|
-
const addr = this.httpServer?.address();
|
|
77
|
-
if (addr && typeof addr !== 'string') {
|
|
78
|
-
this.log.info(`The frontend http server is bound to ${addr.family} ${addr.address}:${addr.port}`);
|
|
79
|
-
}
|
|
80
|
-
if (this.matterbridge.systemInformation.ipv4Address !== '' && !getParameter('bind'))
|
|
81
|
-
this.log.info(`The frontend http server is listening on ${UNDERLINE}http://${this.matterbridge.systemInformation.ipv4Address}:${this.port}${UNDERLINEOFF}${rs}`);
|
|
82
|
-
if (this.matterbridge.systemInformation.ipv6Address !== '' && !getParameter('bind'))
|
|
83
|
-
this.log.info(`The frontend http server is listening on ${UNDERLINE}http://[${this.matterbridge.systemInformation.ipv6Address}]:${this.port}${UNDERLINEOFF}${rs}`);
|
|
84
|
-
this.listening = true;
|
|
85
|
-
this.emit('server_listening', 'http', this.port);
|
|
86
|
-
});
|
|
87
|
-
this.httpServer.on('upgrade', (req, socket, head) => {
|
|
88
|
-
try {
|
|
89
|
-
if ((req.headers.upgrade || '').toLowerCase() !== 'websocket') {
|
|
90
|
-
this.log.error(`WebSocket upgrade error: Invalid upgrade header ${req.headers.upgrade}`);
|
|
91
|
-
socket.write('HTTP/1.1 400 Bad Request\r\nConnection: close\r\n\r\n');
|
|
92
|
-
return socket.destroy();
|
|
93
|
-
}
|
|
94
|
-
const url = new URL(req.url ?? '/', `http://${req.headers.host || 'localhost'}`);
|
|
95
|
-
const password = url.searchParams.get('password') ?? '';
|
|
96
|
-
if (password !== this.storedPassword) {
|
|
97
|
-
this.log.error(`WebSocket upgrade error: Invalid password ${password ? '[redacted]' : '(empty)'}`);
|
|
98
|
-
socket.write('HTTP/1.1 401 Unauthorized\r\nConnection: close\r\n\r\n');
|
|
99
|
-
return socket.destroy();
|
|
100
|
-
}
|
|
101
|
-
this.log.debug(`WebSocket upgrade success host ${url.host} password ${password ? '[redacted]' : '(empty)'}`);
|
|
102
|
-
if (req.socket.remoteAddress)
|
|
103
|
-
this.authClients.add(req.socket.remoteAddress);
|
|
104
|
-
this.backendWsServer?.webSocketServer?.handleUpgrade(req, socket, head, (ws) => {
|
|
105
|
-
this.backendWsServer?.webSocketServer?.emit('connection', ws, req);
|
|
106
|
-
});
|
|
107
|
-
}
|
|
108
|
-
catch (err) {
|
|
109
|
-
{
|
|
110
|
-
inspectError(this.log, 'WebSocket upgrade error:', err);
|
|
111
|
-
socket.write('HTTP/1.1 500 Internal Server Error\r\nConnection: close\r\n\r\n');
|
|
112
|
-
socket.destroy();
|
|
113
|
-
}
|
|
114
|
-
}
|
|
115
|
-
return undefined;
|
|
116
|
-
});
|
|
117
|
-
this.httpServer.on('error', (error) => {
|
|
118
|
-
this.log.error(`Frontend http server error listening on ${this.port}`);
|
|
119
|
-
switch (error.code) {
|
|
120
|
-
case 'EACCES':
|
|
121
|
-
this.log.error(`Port ${this.port} requires elevated privileges`);
|
|
122
|
-
break;
|
|
123
|
-
case 'EADDRINUSE':
|
|
124
|
-
this.log.error(`Port ${this.port} is already in use`);
|
|
125
|
-
break;
|
|
126
|
-
}
|
|
127
|
-
this.emit('server_error', error);
|
|
128
|
-
return undefined;
|
|
129
|
-
});
|
|
130
|
-
}
|
|
131
|
-
else {
|
|
132
|
-
let cert;
|
|
133
|
-
let key;
|
|
134
|
-
let ca;
|
|
135
|
-
let fullChain;
|
|
136
|
-
let pfx;
|
|
137
|
-
let passphrase;
|
|
138
|
-
let httpsServerOptions;
|
|
139
|
-
const fs = await import('node:fs');
|
|
140
|
-
if (fs.existsSync(path.join(this.matterbridge.matterbridgeDirectory, 'certs', 'cert.p12'))) {
|
|
141
|
-
try {
|
|
142
|
-
pfx = await fs.promises.readFile(path.join(this.matterbridge.matterbridgeDirectory, 'certs', 'cert.p12'));
|
|
143
|
-
this.log.info(`Loaded p12 certificate file ${path.join(this.matterbridge.matterbridgeDirectory, 'certs', 'cert.p12')}`);
|
|
144
|
-
}
|
|
145
|
-
catch (error) {
|
|
146
|
-
logError(this.log, `Error reading p12 certificate file ${path.join(this.matterbridge.matterbridgeDirectory, 'certs', 'cert.p12')}`, error);
|
|
147
|
-
this.emit('server_error', error);
|
|
148
|
-
return;
|
|
149
|
-
}
|
|
150
|
-
try {
|
|
151
|
-
passphrase = await fs.promises.readFile(path.join(this.matterbridge.matterbridgeDirectory, 'certs', 'cert.pass'), 'utf8');
|
|
152
|
-
passphrase = passphrase.trim();
|
|
153
|
-
this.log.info(`Loaded p12 passphrase file ${path.join(this.matterbridge.matterbridgeDirectory, 'certs', 'cert.pass')}`);
|
|
154
|
-
}
|
|
155
|
-
catch (error) {
|
|
156
|
-
logError(this.log, `Error reading p12 passphrase file ${path.join(this.matterbridge.matterbridgeDirectory, 'certs', 'cert.pass')}`, error);
|
|
157
|
-
this.emit('server_error', error);
|
|
158
|
-
return;
|
|
159
|
-
}
|
|
160
|
-
httpsServerOptions = { pfx, passphrase };
|
|
161
|
-
}
|
|
162
|
-
else {
|
|
163
|
-
try {
|
|
164
|
-
cert = await fs.promises.readFile(path.join(this.matterbridge.matterbridgeDirectory, 'certs', 'cert.pem'), 'utf8');
|
|
165
|
-
this.log.info(`Loaded certificate file ${path.join(this.matterbridge.matterbridgeDirectory, 'certs', 'cert.pem')}`);
|
|
166
|
-
}
|
|
167
|
-
catch (error) {
|
|
168
|
-
logError(this.log, `Error reading certificate file ${path.join(this.matterbridge.matterbridgeDirectory, 'certs', 'cert.pem')}`, error);
|
|
169
|
-
this.emit('server_error', error);
|
|
170
|
-
return;
|
|
171
|
-
}
|
|
172
|
-
try {
|
|
173
|
-
key = await fs.promises.readFile(path.join(this.matterbridge.matterbridgeDirectory, 'certs', 'key.pem'), 'utf8');
|
|
174
|
-
this.log.info(`Loaded key file ${path.join(this.matterbridge.matterbridgeDirectory, 'certs', 'key.pem')}`);
|
|
175
|
-
}
|
|
176
|
-
catch (error) {
|
|
177
|
-
logError(this.log, `Error reading key file ${path.join(this.matterbridge.matterbridgeDirectory, 'certs', 'key.pem')}`, error);
|
|
178
|
-
this.emit('server_error', error);
|
|
179
|
-
return;
|
|
180
|
-
}
|
|
181
|
-
try {
|
|
182
|
-
ca = await fs.promises.readFile(path.join(this.matterbridge.matterbridgeDirectory, 'certs', 'ca.pem'), 'utf8');
|
|
183
|
-
fullChain = `${cert}\n${ca}`;
|
|
184
|
-
this.log.info(`Loaded CA certificate file ${path.join(this.matterbridge.matterbridgeDirectory, 'certs', 'ca.pem')}`);
|
|
185
|
-
}
|
|
186
|
-
catch (error) {
|
|
187
|
-
this.log.info(`CA certificate file ${path.join(this.matterbridge.matterbridgeDirectory, 'certs', 'ca.pem')} not loaded: ${getErrorMessage(error)}`);
|
|
188
|
-
}
|
|
189
|
-
httpsServerOptions = { cert: fullChain ?? cert, key, ca };
|
|
190
|
-
}
|
|
191
|
-
if (hasParameter('mtls')) {
|
|
192
|
-
httpsServerOptions.requestCert = true;
|
|
193
|
-
httpsServerOptions.rejectUnauthorized = true;
|
|
194
|
-
}
|
|
195
|
-
const https = await import('node:https');
|
|
196
|
-
await this.backendExpress.start();
|
|
197
|
-
try {
|
|
198
|
-
this.log.debug(`Creating HTTPS server...`);
|
|
199
|
-
this.httpsServer = https.createServer(httpsServerOptions, this.backendExpress.expressApp);
|
|
200
|
-
}
|
|
201
|
-
catch (error) {
|
|
202
|
-
logError(this.log, `Failed to create HTTPS server`, error);
|
|
203
|
-
this.emit('server_error', error);
|
|
204
|
-
await this.backendExpress.stop();
|
|
205
|
-
return;
|
|
206
|
-
}
|
|
207
|
-
await this.backendWsServer.start();
|
|
208
|
-
this.httpsServer.listen(this.port, getParameter('bind'), () => {
|
|
209
|
-
const addr = this.httpsServer?.address();
|
|
210
|
-
if (addr && typeof addr !== 'string') {
|
|
211
|
-
this.log.info(`The frontend https server is bound to ${addr.family} ${addr.address}:${addr.port}`);
|
|
212
|
-
}
|
|
213
|
-
if (this.matterbridge.systemInformation.ipv4Address !== '' && !getParameter('bind'))
|
|
214
|
-
this.log.info(`The frontend https server is listening on ${UNDERLINE}https://${this.matterbridge.systemInformation.ipv4Address}:${this.port}${UNDERLINEOFF}${rs}`);
|
|
215
|
-
if (this.matterbridge.systemInformation.ipv6Address !== '' && !getParameter('bind'))
|
|
216
|
-
this.log.info(`The frontend https server is listening on ${UNDERLINE}https://[${this.matterbridge.systemInformation.ipv6Address}]:${this.port}${UNDERLINEOFF}${rs}`);
|
|
217
|
-
this.listening = true;
|
|
218
|
-
this.emit('server_listening', 'https', this.port);
|
|
219
|
-
});
|
|
220
|
-
this.httpsServer.on('upgrade', (req, socket, head) => {
|
|
221
|
-
try {
|
|
222
|
-
if ((req.headers.upgrade || '').toLowerCase() !== 'websocket') {
|
|
223
|
-
socket.write('HTTP/1.1 400 Bad Request\r\nConnection: close\r\n\r\n');
|
|
224
|
-
return socket.destroy();
|
|
225
|
-
}
|
|
226
|
-
const url = new URL(req.url ?? '/', `https://${req.headers.host || 'localhost'}`);
|
|
227
|
-
const password = url.searchParams.get('password') ?? '';
|
|
228
|
-
if (password !== this.storedPassword) {
|
|
229
|
-
this.log.error(`WebSocket upgrade error: Invalid password ${password ? '[redacted]' : '(empty)'}`);
|
|
230
|
-
socket.write('HTTP/1.1 401 Unauthorized\r\nConnection: close\r\n\r\n');
|
|
231
|
-
return socket.destroy();
|
|
232
|
-
}
|
|
233
|
-
this.log.debug(`WebSocket upgrade success host ${url.host} password ${password ? '[redacted]' : '(empty)'}`);
|
|
234
|
-
if (req.socket.remoteAddress)
|
|
235
|
-
this.authClients.add(req.socket.remoteAddress);
|
|
236
|
-
this.backendWsServer?.webSocketServer?.handleUpgrade(req, socket, head, (ws) => {
|
|
237
|
-
this.backendWsServer?.webSocketServer?.emit('connection', ws, req);
|
|
238
|
-
});
|
|
239
|
-
}
|
|
240
|
-
catch (err) {
|
|
241
|
-
{
|
|
242
|
-
inspectError(this.log, 'WebSocket upgrade error:', err);
|
|
243
|
-
socket.write('HTTP/1.1 500 Internal Server Error\r\nConnection: close\r\n\r\n');
|
|
244
|
-
socket.destroy();
|
|
245
|
-
}
|
|
246
|
-
}
|
|
247
|
-
return undefined;
|
|
248
|
-
});
|
|
249
|
-
this.httpsServer.on('error', (error) => {
|
|
250
|
-
this.log.error(`Frontend https server error listening on ${this.port}`);
|
|
251
|
-
switch (error.code) {
|
|
252
|
-
case 'EACCES':
|
|
253
|
-
this.log.error(`Port ${this.port} requires elevated privileges`);
|
|
254
|
-
break;
|
|
255
|
-
case 'EADDRINUSE':
|
|
256
|
-
this.log.error(`Port ${this.port} is already in use`);
|
|
257
|
-
break;
|
|
258
|
-
}
|
|
259
|
-
this.emit('server_error', error);
|
|
260
|
-
return;
|
|
261
|
-
});
|
|
262
|
-
}
|
|
263
|
-
this.log.debug('Backend started');
|
|
264
|
-
}
|
|
265
|
-
async stop() {
|
|
266
|
-
this.log.debug('Stopping backend...');
|
|
267
|
-
await this.backendWsServer?.stop();
|
|
268
|
-
if (this.httpServer) {
|
|
269
|
-
this.log.debug('Closing http server...');
|
|
270
|
-
this.httpServer.close();
|
|
271
|
-
this.log.debug('Http server closed successfully');
|
|
272
|
-
this.listening = false;
|
|
273
|
-
this.emit('server_stopped');
|
|
274
|
-
this.httpServer.removeAllListeners();
|
|
275
|
-
this.httpServer = undefined;
|
|
276
|
-
this.log.debug('Backend http server closed successfully');
|
|
277
|
-
}
|
|
278
|
-
if (this.httpsServer) {
|
|
279
|
-
this.log.debug('Closing https server...');
|
|
280
|
-
this.httpsServer.close();
|
|
281
|
-
this.log.debug('Https server closed successfully');
|
|
282
|
-
this.listening = false;
|
|
283
|
-
this.emit('server_stopped');
|
|
284
|
-
this.httpsServer.removeAllListeners();
|
|
285
|
-
this.httpsServer = undefined;
|
|
286
|
-
this.log.debug('Backend https server closed successfully');
|
|
287
|
-
}
|
|
288
|
-
await this.backendExpress?.stop();
|
|
289
|
-
this.backendWsServer?.destroy();
|
|
290
|
-
this.backendExpress?.destroy();
|
|
291
|
-
this.backendWsServer = undefined;
|
|
292
|
-
this.backendExpress = undefined;
|
|
293
|
-
this.log.debug('Backend stopped');
|
|
294
|
-
}
|
|
295
|
-
async getApiSettings() {
|
|
296
|
-
const response = await this.server.fetch({ type: 'matterbridge_apisettings', src: 'frontend', dst: 'matterbridge', params: undefined });
|
|
297
|
-
return response.result.data;
|
|
298
|
-
}
|
|
299
|
-
async getApiPlugins() {
|
|
300
|
-
const response = await this.server.fetch({ type: 'plugins_apipluginarray', src: 'frontend', dst: 'plugins', params: undefined });
|
|
301
|
-
return response.result.plugins;
|
|
302
|
-
}
|
|
303
|
-
async getApiDevices(pluginName) {
|
|
304
|
-
const _response = await this.server.fetch({ type: 'devices_basearray', src: 'frontend', dst: 'devices', params: { pluginName } });
|
|
305
|
-
return [];
|
|
306
|
-
}
|
|
307
|
-
async generateDiagnostic() { }
|
|
308
|
-
}
|
package/dist/backendExpress.d.ts
DELETED
|
@@ -1,20 +0,0 @@
|
|
|
1
|
-
import { type SharedMatterbridge } from '@matterbridge/types';
|
|
2
|
-
import express from 'express';
|
|
3
|
-
import type { Backend } from './backend.js';
|
|
4
|
-
export declare class BackendExpress {
|
|
5
|
-
private debug;
|
|
6
|
-
private verbose;
|
|
7
|
-
private log;
|
|
8
|
-
private backend;
|
|
9
|
-
private matterbridge;
|
|
10
|
-
private readonly server;
|
|
11
|
-
private fileLimiter;
|
|
12
|
-
expressApp: express.Application | undefined;
|
|
13
|
-
serverFetchTimeout: number;
|
|
14
|
-
constructor(matterbridge: SharedMatterbridge, backend: Backend);
|
|
15
|
-
destroy(): void;
|
|
16
|
-
private broadcastMsgHandler;
|
|
17
|
-
private validateReq;
|
|
18
|
-
start(): Promise<express.Application | undefined>;
|
|
19
|
-
stop(): Promise<express.Application | undefined>;
|
|
20
|
-
}
|