@soundworks/core 3.5.1 → 3.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/client/AbstractPlugin.js +1 -1
- package/client/Client.js +16 -13
- package/client/PluginManager.js +39 -27
- package/client/Socket.js +17 -9
- package/common/ParameterBag.js +11 -13
- package/common/SharedState.js +48 -19
- package/common/SharedStateManagerServer.js +2 -2
- package/common/SharedStatePrivate.js +0 -2
- package/{server/utils → common}/logger.js +34 -6
- package/common/shared-state-utils.js +0 -1
- package/package.json +14 -3
- package/server/AbstractExperience.js +1 -1
- package/server/AbstractPlugin.js +7 -4
- package/server/Client.js +4 -4
- package/server/PluginManager.js +136 -40
- package/server/Server.js +293 -217
- package/server/Socket.js +9 -10
- package/server/Sockets.js +0 -1
package/server/Server.js
CHANGED
|
@@ -5,28 +5,32 @@ Object.defineProperty(exports, "__esModule", {
|
|
|
5
5
|
});
|
|
6
6
|
exports.default = void 0;
|
|
7
7
|
|
|
8
|
-
var _events = _interopRequireDefault(require("events"));
|
|
9
|
-
|
|
10
8
|
var _fs = _interopRequireDefault(require("fs"));
|
|
11
9
|
|
|
12
10
|
var _http = _interopRequireDefault(require("http"));
|
|
13
11
|
|
|
14
12
|
var _https = _interopRequireDefault(require("https"));
|
|
15
13
|
|
|
16
|
-
var _lodash = _interopRequireDefault(require("lodash.merge"));
|
|
17
|
-
|
|
18
14
|
var _path = _interopRequireDefault(require("path"));
|
|
19
15
|
|
|
20
|
-
var _pem = _interopRequireDefault(require("pem"));
|
|
21
|
-
|
|
22
16
|
var _os = _interopRequireDefault(require("os"));
|
|
23
17
|
|
|
24
|
-
var
|
|
18
|
+
var _crypto = require("crypto");
|
|
25
19
|
|
|
26
|
-
var
|
|
20
|
+
var _chalk = _interopRequireDefault(require("chalk"));
|
|
27
21
|
|
|
28
22
|
var _compression = _interopRequireDefault(require("compression"));
|
|
29
23
|
|
|
24
|
+
var _keyv = _interopRequireDefault(require("keyv"));
|
|
25
|
+
|
|
26
|
+
var _keyvFile = _interopRequireDefault(require("keyv-file"));
|
|
27
|
+
|
|
28
|
+
var _lodash = _interopRequireDefault(require("lodash.merge"));
|
|
29
|
+
|
|
30
|
+
var _pem = _interopRequireDefault(require("pem"));
|
|
31
|
+
|
|
32
|
+
var _polka = _interopRequireDefault(require("polka"));
|
|
33
|
+
|
|
30
34
|
var _Client = _interopRequireDefault(require("./Client.js"));
|
|
31
35
|
|
|
32
36
|
var _PluginManager = _interopRequireDefault(require("./PluginManager.js"));
|
|
@@ -35,17 +39,11 @@ var _Sockets = _interopRequireDefault(require("./Sockets.js"));
|
|
|
35
39
|
|
|
36
40
|
var _SharedStateManagerServer = _interopRequireDefault(require("../common/SharedStateManagerServer.js"));
|
|
37
41
|
|
|
38
|
-
var _logger = _interopRequireDefault(require("
|
|
39
|
-
|
|
40
|
-
var _keyv = _interopRequireDefault(require("keyv"));
|
|
41
|
-
|
|
42
|
-
var _keyvFile = _interopRequireDefault(require("keyv-file"));
|
|
42
|
+
var _logger = _interopRequireDefault(require("../common/logger.js"));
|
|
43
43
|
|
|
44
44
|
function _interopRequireDefault(obj) { return obj && obj.__esModule ? obj : { default: obj }; }
|
|
45
45
|
|
|
46
46
|
let _dbNamespaces = new Set();
|
|
47
|
-
|
|
48
|
-
let _dbStore = null;
|
|
49
47
|
/**
|
|
50
48
|
* Server side entry point for a `soundworks` application.
|
|
51
49
|
*
|
|
@@ -64,6 +62,7 @@ let _dbStore = null;
|
|
|
64
62
|
* soundworks.server.start();
|
|
65
63
|
*/
|
|
66
64
|
|
|
65
|
+
|
|
67
66
|
class Server {
|
|
68
67
|
constructor() {
|
|
69
68
|
/**
|
|
@@ -141,8 +140,13 @@ class Server {
|
|
|
141
140
|
* @param {String}
|
|
142
141
|
*/
|
|
143
142
|
|
|
144
|
-
this.templateDirectory = null;
|
|
143
|
+
this.templateDirectory = null;
|
|
144
|
+
/**
|
|
145
|
+
* If https is required, will contain informations about the certificates
|
|
146
|
+
* (self-signed, validity dates, etc.)
|
|
147
|
+
*/
|
|
145
148
|
|
|
149
|
+
this.httpsInfos = null;
|
|
146
150
|
/**
|
|
147
151
|
* Required activities that must be started. Only used in Experience
|
|
148
152
|
* and Plugin - do not expose.
|
|
@@ -150,13 +154,6 @@ class Server {
|
|
|
150
154
|
*/
|
|
151
155
|
|
|
152
156
|
this.activities = new Set();
|
|
153
|
-
/**
|
|
154
|
-
* Key and certificates (may be generated and self-signed) for https server.
|
|
155
|
-
* @todo - put in config...
|
|
156
|
-
* @private
|
|
157
|
-
*/
|
|
158
|
-
|
|
159
|
-
this._httpsInfos = null;
|
|
160
157
|
/**
|
|
161
158
|
* Mapping between a `clientType` and its related activities.
|
|
162
159
|
* @private
|
|
@@ -170,14 +167,15 @@ class Server {
|
|
|
170
167
|
*/
|
|
171
168
|
|
|
172
169
|
this._routes = {};
|
|
173
|
-
/**
|
|
174
|
-
* @private
|
|
175
|
-
*/
|
|
170
|
+
/** @private */
|
|
176
171
|
|
|
177
172
|
this._htmlTemplateConfig = {
|
|
178
173
|
engine: null,
|
|
179
174
|
directory: null
|
|
180
175
|
};
|
|
176
|
+
/** @private */
|
|
177
|
+
|
|
178
|
+
this._listeners = new Map();
|
|
181
179
|
}
|
|
182
180
|
/**
|
|
183
181
|
*
|
|
@@ -225,7 +223,7 @@ class Server {
|
|
|
225
223
|
*/
|
|
226
224
|
|
|
227
225
|
|
|
228
|
-
async init(config, clientConfigFunction = (clientType,
|
|
226
|
+
async init(config, clientConfigFunction = (clientType, _serverConfig, _httpRequest) => ({
|
|
229
227
|
clientType
|
|
230
228
|
})) {
|
|
231
229
|
const defaultConfig = {
|
|
@@ -300,6 +298,7 @@ class Server {
|
|
|
300
298
|
});
|
|
301
299
|
}
|
|
302
300
|
|
|
301
|
+
await this._emit('inited');
|
|
303
302
|
return Promise.resolve();
|
|
304
303
|
}
|
|
305
304
|
/**
|
|
@@ -310,222 +309,255 @@ class Server {
|
|
|
310
309
|
|
|
311
310
|
|
|
312
311
|
async start() {
|
|
313
|
-
|
|
314
|
-
|
|
315
|
-
|
|
316
|
-
|
|
317
|
-
|
|
318
|
-
|
|
319
|
-
|
|
320
|
-
|
|
321
|
-
|
|
322
|
-
|
|
323
|
-
}
|
|
312
|
+
// ------------------------------------------------------------
|
|
313
|
+
// init acitvities
|
|
314
|
+
// ------------------------------------------------------------
|
|
315
|
+
// we should at least have one activity registered for each client here
|
|
316
|
+
// i.e. the Experience so it's ok
|
|
317
|
+
this.activities.forEach(activity => {
|
|
318
|
+
activity.clientTypes.forEach(clientType => {
|
|
319
|
+
if (!(clientType in this.config.app.clients)) {
|
|
320
|
+
throw new Error(`[soundworks:core] no config found for client type ${clientType}`);
|
|
321
|
+
}
|
|
324
322
|
|
|
325
|
-
|
|
326
|
-
|
|
327
|
-
|
|
323
|
+
if (!this._clientTypeActivitiesMap[clientType]) {
|
|
324
|
+
this._clientTypeActivitiesMap[clientType] = new Set();
|
|
325
|
+
}
|
|
328
326
|
|
|
329
|
-
|
|
330
|
-
|
|
331
|
-
|
|
332
|
-
|
|
333
|
-
|
|
334
|
-
|
|
335
|
-
|
|
336
|
-
|
|
327
|
+
this._clientTypeActivitiesMap[clientType].add(activity);
|
|
328
|
+
});
|
|
329
|
+
}); // start http server
|
|
330
|
+
|
|
331
|
+
const useHttps = this.config.env.useHttps || false;
|
|
332
|
+
return Promise.resolve() // ------------------------------------------------------------
|
|
333
|
+
// create HTTP(S) SERVER
|
|
334
|
+
// ------------------------------------------------------------
|
|
335
|
+
.then(async () => {
|
|
336
|
+
// create http server
|
|
337
|
+
if (!useHttps) {
|
|
338
|
+
const httpServer = _http.default.createServer();
|
|
339
|
+
|
|
340
|
+
return Promise.resolve(httpServer);
|
|
341
|
+
} else {
|
|
342
|
+
const httpsInfos = this.config.env.httpsInfos;
|
|
337
343
|
|
|
338
|
-
|
|
339
|
-
|
|
340
|
-
|
|
344
|
+
if (httpsInfos.key && httpsInfos.cert) {
|
|
345
|
+
// use given certificate
|
|
346
|
+
try {
|
|
347
|
+
const key = _fs.default.readFileSync(httpsInfos.key);
|
|
341
348
|
|
|
342
|
-
|
|
343
|
-
// use given certificate
|
|
344
|
-
try {
|
|
345
|
-
const key = _fs.default.readFileSync(httpsInfos.key);
|
|
349
|
+
const cert = _fs.default.readFileSync(httpsInfos.cert); // this fails with self-signed certificates for whatever reason...
|
|
346
350
|
|
|
347
|
-
const cert = _fs.default.readFileSync(httpsInfos.cert);
|
|
348
351
|
|
|
349
|
-
|
|
350
|
-
|
|
351
|
-
|
|
352
|
-
|
|
352
|
+
const x509 = new _crypto.X509Certificate(cert);
|
|
353
|
+
this.httpsInfos = {
|
|
354
|
+
selfSigned: false,
|
|
355
|
+
CN: x509.subject.split('=')[1],
|
|
356
|
+
altNames: x509.subjectAltName.split(',').map(e => e.trim().split(':')[1]),
|
|
357
|
+
validFrom: x509.validFrom,
|
|
358
|
+
validTo: x509.validTo
|
|
359
|
+
};
|
|
353
360
|
|
|
354
|
-
|
|
361
|
+
const httpsServer = _https.default.createServer({
|
|
362
|
+
key,
|
|
363
|
+
cert
|
|
364
|
+
});
|
|
355
365
|
|
|
356
|
-
|
|
357
|
-
|
|
358
|
-
|
|
366
|
+
return Promise.resolve(httpsServer);
|
|
367
|
+
} catch (err) {
|
|
368
|
+
_logger.default.error(`
|
|
369
|
+
Invalid certificate files, please check your:
|
|
359
370
|
- key file: ${httpsInfos.key}
|
|
360
371
|
- cert file: ${httpsInfos.cert}
|
|
361
|
-
|
|
362
|
-
throw err;
|
|
363
|
-
}
|
|
364
|
-
} else {
|
|
365
|
-
return new Promise(async (resolve, reject) => {
|
|
366
|
-
const key = await this.db.get('httpsKey');
|
|
367
|
-
const cert = await this.db.get('httpsCert');
|
|
372
|
+
`);
|
|
368
373
|
|
|
369
|
-
|
|
370
|
-
|
|
371
|
-
|
|
372
|
-
|
|
373
|
-
|
|
374
|
+
throw err;
|
|
375
|
+
}
|
|
376
|
+
} else {
|
|
377
|
+
const key = await this.db.get('httpsKey');
|
|
378
|
+
const cert = await this.db.get('httpsCert');
|
|
374
379
|
|
|
375
|
-
|
|
380
|
+
if (key && cert) {
|
|
381
|
+
this.httpsInfos = {
|
|
382
|
+
selfSigned: true
|
|
383
|
+
};
|
|
376
384
|
|
|
377
|
-
|
|
378
|
-
|
|
379
|
-
|
|
380
|
-
|
|
381
|
-
days: 1,
|
|
382
|
-
selfSigned: true
|
|
383
|
-
}, async (err, keys) => {
|
|
384
|
-
if (err) {
|
|
385
|
-
return console.error(err.stack);
|
|
386
|
-
}
|
|
385
|
+
const httpsServer = _https.default.createServer({
|
|
386
|
+
key,
|
|
387
|
+
cert
|
|
388
|
+
});
|
|
387
389
|
|
|
388
|
-
|
|
389
|
-
|
|
390
|
-
|
|
391
|
-
|
|
392
|
-
|
|
393
|
-
|
|
390
|
+
return Promise.resolve(httpsServer);
|
|
391
|
+
} else {
|
|
392
|
+
return new Promise(resolve => {
|
|
393
|
+
// generate certificate on the fly (for development purposes)
|
|
394
|
+
_pem.default.createCertificate({
|
|
395
|
+
days: 1,
|
|
396
|
+
selfSigned: true
|
|
397
|
+
}, async (err, keys) => {
|
|
398
|
+
if (err) {
|
|
399
|
+
_logger.default.error(err.stack);
|
|
400
|
+
|
|
401
|
+
return;
|
|
402
|
+
}
|
|
394
403
|
|
|
395
|
-
|
|
404
|
+
const key = keys.serviceKey;
|
|
405
|
+
const cert = keys.certificate;
|
|
406
|
+
this.httpsInfos = {
|
|
407
|
+
selfSigned: true
|
|
408
|
+
}; // we store the generated cert so that we don't have to re-accept
|
|
409
|
+
// the cert each time the server restarts
|
|
396
410
|
|
|
397
|
-
|
|
411
|
+
await this.db.set('httpsKey', key);
|
|
412
|
+
await this.db.set('httpsCert', cert);
|
|
413
|
+
|
|
414
|
+
const httpsServer = _https.default.createServer({
|
|
415
|
+
key,
|
|
416
|
+
cert
|
|
398
417
|
});
|
|
399
|
-
|
|
418
|
+
|
|
419
|
+
resolve(httpsServer);
|
|
420
|
+
});
|
|
400
421
|
});
|
|
401
422
|
}
|
|
402
423
|
}
|
|
403
|
-
}
|
|
404
|
-
|
|
405
|
-
|
|
406
|
-
|
|
407
|
-
|
|
408
|
-
|
|
409
|
-
|
|
410
|
-
|
|
411
|
-
|
|
412
|
-
|
|
413
|
-
|
|
414
|
-
|
|
424
|
+
}
|
|
425
|
+
}).then(httpServer => {
|
|
426
|
+
this.httpServer = httpServer;
|
|
427
|
+
this.router.server = httpServer;
|
|
428
|
+
return Promise.resolve();
|
|
429
|
+
}).then(() => {
|
|
430
|
+
let nodeOnly = true; // do not throw if no browser clients are defined, very usefull for
|
|
431
|
+
// cleaning tests in particular
|
|
432
|
+
|
|
433
|
+
for (let clientType in this.config.app.clients) {
|
|
434
|
+
if (this.config.app.clients[clientType].target === 'browser') {
|
|
435
|
+
nodeOnly = false;
|
|
415
436
|
}
|
|
437
|
+
}
|
|
416
438
|
|
|
417
|
-
|
|
418
|
-
|
|
419
|
-
|
|
420
|
-
|
|
439
|
+
if (!nodeOnly) {
|
|
440
|
+
if (this.templateEngine === null) {
|
|
441
|
+
throw new Error('Undefined "server.templateEngine": please provide a valid template engine');
|
|
442
|
+
}
|
|
421
443
|
|
|
422
|
-
|
|
423
|
-
|
|
424
|
-
|
|
425
|
-
|
|
426
|
-
|
|
427
|
-
|
|
444
|
+
if (this.templateDirectory === null) {
|
|
445
|
+
throw new Error('Undefined "server.templateDirectory": please provide a valid template directory');
|
|
446
|
+
}
|
|
447
|
+
} // ------------------------------------------------------------
|
|
448
|
+
// INIT ROUTING
|
|
449
|
+
// ------------------------------------------------------------
|
|
428
450
|
|
|
429
451
|
|
|
430
|
-
|
|
452
|
+
_logger.default.title(`configured clients and routing`);
|
|
431
453
|
|
|
432
|
-
|
|
433
|
-
|
|
454
|
+
const routes = [];
|
|
455
|
+
let defaultClientType = null;
|
|
434
456
|
|
|
435
|
-
|
|
436
|
-
|
|
437
|
-
|
|
438
|
-
|
|
439
|
-
|
|
457
|
+
for (let clientType in this.config.app.clients) {
|
|
458
|
+
if (this.config.app.clients[clientType].default === true) {
|
|
459
|
+
defaultClientType = clientType;
|
|
460
|
+
}
|
|
461
|
+
} // we must open default route last
|
|
440
462
|
|
|
441
463
|
|
|
442
|
-
|
|
443
|
-
|
|
444
|
-
|
|
445
|
-
|
|
446
|
-
|
|
464
|
+
for (let clientType in this._clientTypeActivitiesMap) {
|
|
465
|
+
// do nothing if client type is not registered in config
|
|
466
|
+
if (!(clientType in this.config.app.clients)) {
|
|
467
|
+
continue;
|
|
468
|
+
}
|
|
447
469
|
|
|
448
|
-
|
|
449
|
-
|
|
470
|
+
if (clientType !== defaultClientType) {
|
|
471
|
+
const clientTarget = this.config.app.clients[clientType].target;
|
|
450
472
|
|
|
451
|
-
|
|
473
|
+
const path = this._openClientRoute(clientType, clientTarget, this.router);
|
|
452
474
|
|
|
453
|
-
|
|
454
|
-
|
|
455
|
-
|
|
456
|
-
|
|
457
|
-
|
|
458
|
-
|
|
475
|
+
routes.push({
|
|
476
|
+
clientType,
|
|
477
|
+
path
|
|
478
|
+
});
|
|
479
|
+
}
|
|
480
|
+
} // open default route last
|
|
459
481
|
|
|
460
482
|
|
|
461
|
-
|
|
462
|
-
|
|
463
|
-
|
|
464
|
-
|
|
465
|
-
|
|
483
|
+
for (let clientType in this._clientTypeActivitiesMap) {
|
|
484
|
+
// do nothing if client type is not registered in config
|
|
485
|
+
if (!(clientType in this.config.app.clients)) {
|
|
486
|
+
continue;
|
|
487
|
+
}
|
|
466
488
|
|
|
467
|
-
|
|
468
|
-
|
|
489
|
+
if (clientType === defaultClientType) {
|
|
490
|
+
const clientTarget = this.config.app.clients[clientType].target;
|
|
469
491
|
|
|
470
|
-
|
|
492
|
+
const path = this._openClientRoute(clientType, clientTarget, this.router, true);
|
|
471
493
|
|
|
472
|
-
|
|
473
|
-
|
|
474
|
-
|
|
475
|
-
|
|
476
|
-
}
|
|
494
|
+
routes.unshift({
|
|
495
|
+
clientType,
|
|
496
|
+
path
|
|
497
|
+
});
|
|
477
498
|
}
|
|
499
|
+
}
|
|
478
500
|
|
|
479
|
-
|
|
480
|
-
|
|
481
|
-
|
|
482
|
-
|
|
483
|
-
|
|
484
|
-
|
|
485
|
-
|
|
486
|
-
|
|
487
|
-
|
|
488
|
-
|
|
489
|
-
|
|
490
|
-
|
|
491
|
-
|
|
492
|
-
|
|
493
|
-
|
|
494
|
-
|
|
495
|
-
|
|
496
|
-
|
|
497
|
-
|
|
498
|
-
|
|
499
|
-
|
|
500
|
-
|
|
501
|
-
|
|
502
|
-
|
|
503
|
-
|
|
504
|
-
|
|
505
|
-
|
|
506
|
-
|
|
507
|
-
|
|
508
|
-
|
|
509
|
-
|
|
510
|
-
|
|
511
|
-
|
|
512
|
-
});
|
|
501
|
+
_logger.default.clientConfigAndRouting(routes, this.config);
|
|
502
|
+
|
|
503
|
+
return Promise.resolve();
|
|
504
|
+
}).then(() => {
|
|
505
|
+
// ------------------------------------------------------------
|
|
506
|
+
// START SOCKET SERVER
|
|
507
|
+
// ------------------------------------------------------------
|
|
508
|
+
this.sockets.start(this.httpServer, this.config.env.websockets, (clientType, socket) => this._onSocketConnection(clientType, socket));
|
|
509
|
+
return Promise.resolve();
|
|
510
|
+
}).then(async () => {
|
|
511
|
+
// ------------------------------------------------------------
|
|
512
|
+
// START SERVICE MANAGER
|
|
513
|
+
// ------------------------------------------------------------
|
|
514
|
+
return this.pluginManager.start();
|
|
515
|
+
}).then(() => {
|
|
516
|
+
// ------------------------------------------------------------
|
|
517
|
+
// START HTTP SERVER
|
|
518
|
+
// ------------------------------------------------------------
|
|
519
|
+
return new Promise(resolve => {
|
|
520
|
+
const port = this.config.env.port;
|
|
521
|
+
const useHttps = this.config.env.useHttps || false;
|
|
522
|
+
const protocol = useHttps ? 'https' : 'http';
|
|
523
|
+
|
|
524
|
+
const ifaces = _os.default.networkInterfaces();
|
|
525
|
+
|
|
526
|
+
this.router.listen(port, async () => {
|
|
527
|
+
_logger.default.title(`${protocol} server listening on`);
|
|
528
|
+
|
|
529
|
+
Object.keys(ifaces).forEach(dev => {
|
|
530
|
+
ifaces[dev].forEach(details => {
|
|
531
|
+
if (details.family === 'IPv4') {
|
|
532
|
+
_logger.default.ip(protocol, details.address, port);
|
|
533
|
+
}
|
|
513
534
|
});
|
|
514
|
-
resolve();
|
|
515
535
|
});
|
|
536
|
+
|
|
537
|
+
if (this.httpsInfos !== null) {
|
|
538
|
+
_logger.default.title(`https certificates infos`);
|
|
539
|
+
|
|
540
|
+
if (this.httpsInfos.selfSigned) {
|
|
541
|
+
_logger.default.log(` self-signed: ${this.httpsInfos.selfSigned ? 'true' : 'false'}`);
|
|
542
|
+
} else {
|
|
543
|
+
_logger.default.log(` valid from: ${this.httpsInfos.validFrom}`);
|
|
544
|
+
|
|
545
|
+
_logger.default.log(` valid to: ${this.httpsInfos.validTo}`);
|
|
546
|
+
}
|
|
547
|
+
}
|
|
548
|
+
|
|
549
|
+
await this._emit('started');
|
|
550
|
+
resolve();
|
|
516
551
|
});
|
|
517
552
|
});
|
|
518
|
-
|
|
519
|
-
return Promise.resolve();
|
|
520
|
-
} catch (err) {
|
|
521
|
-
console.error(err);
|
|
522
|
-
}
|
|
553
|
+
});
|
|
523
554
|
} // @todo - handle gracefull close of the server
|
|
524
555
|
|
|
525
556
|
|
|
526
557
|
async stop() {
|
|
527
558
|
this.sockets.terminate();
|
|
528
559
|
this.httpServer.close();
|
|
560
|
+
await this._emit('stopped');
|
|
529
561
|
return Promise.resolve();
|
|
530
562
|
}
|
|
531
563
|
/**
|
|
@@ -618,39 +650,47 @@ class Server {
|
|
|
618
650
|
const activities = this._clientTypeActivitiesMap[clientType];
|
|
619
651
|
|
|
620
652
|
if (activities == undefined) {
|
|
621
|
-
|
|
653
|
+
_logger.default.error(`[soundworks:core] No activity registered for "${clientType}", make sure an Experience has been created for this client`);
|
|
622
654
|
}
|
|
623
655
|
|
|
624
656
|
socket.addListener('close', () => {
|
|
625
657
|
// clean sockets
|
|
626
658
|
socket.terminate(); // remove client from activities
|
|
627
659
|
|
|
628
|
-
|
|
660
|
+
if (activities) {
|
|
661
|
+
activities.forEach(activity => activity.disconnect(client));
|
|
662
|
+
} // destroy client
|
|
663
|
+
|
|
629
664
|
|
|
630
665
|
client.destroy();
|
|
631
666
|
});
|
|
632
667
|
socket.addListener('s:client:handshake', data => {
|
|
633
|
-
//
|
|
634
|
-
|
|
635
|
-
|
|
636
|
-
|
|
637
|
-
|
|
638
|
-
|
|
639
|
-
|
|
640
|
-
|
|
641
|
-
|
|
642
|
-
|
|
643
|
-
|
|
644
|
-
|
|
645
|
-
|
|
646
|
-
|
|
647
|
-
|
|
648
|
-
|
|
649
|
-
data: missingPlugins
|
|
650
|
-
};
|
|
651
|
-
socket.send('s:client:error', err);
|
|
652
|
-
return;
|
|
668
|
+
// client should throw error if no activity registered server-side (cf. #16)
|
|
669
|
+
if (activities === undefined) {
|
|
670
|
+
const err = {
|
|
671
|
+
type: 'no-activity'
|
|
672
|
+
};
|
|
673
|
+
socket.send('s:client:error', err);
|
|
674
|
+
return;
|
|
675
|
+
} // check coherence between client-side and server-side plugin requirements
|
|
676
|
+
|
|
677
|
+
|
|
678
|
+
const clientRequiredPlugins = data.requiredPlugins || [];
|
|
679
|
+
const serverRequiredPlugins = this.pluginManager.getRequiredPlugins(clientType);
|
|
680
|
+
const missingPlugins = [];
|
|
681
|
+
clientRequiredPlugins.forEach(pluginId => {
|
|
682
|
+
if (serverRequiredPlugins.indexOf(pluginId) === -1) {
|
|
683
|
+
missingPlugins.push(pluginId);
|
|
653
684
|
}
|
|
685
|
+
});
|
|
686
|
+
|
|
687
|
+
if (missingPlugins.length > 0) {
|
|
688
|
+
const err = {
|
|
689
|
+
type: 'plugins',
|
|
690
|
+
data: missingPlugins
|
|
691
|
+
};
|
|
692
|
+
socket.send('s:client:error', err);
|
|
693
|
+
return;
|
|
654
694
|
}
|
|
655
695
|
|
|
656
696
|
activities.forEach(activity => activity.connect(client));
|
|
@@ -699,10 +739,46 @@ class Server {
|
|
|
699
739
|
namespace,
|
|
700
740
|
store
|
|
701
741
|
});
|
|
702
|
-
db.on('error', err =>
|
|
742
|
+
db.on('error', err => _logger.default.error('[soundworks:core] db ${namespace} error: ${err}'));
|
|
703
743
|
return db;
|
|
704
744
|
}
|
|
705
745
|
|
|
746
|
+
addListener(channel, callback) {
|
|
747
|
+
if (!this._listeners.has(channel)) {
|
|
748
|
+
const listeners = new Set();
|
|
749
|
+
|
|
750
|
+
this._listeners.set(channel, listeners);
|
|
751
|
+
}
|
|
752
|
+
|
|
753
|
+
const listeners = this._listeners.get(channel);
|
|
754
|
+
|
|
755
|
+
listeners.add(callback);
|
|
756
|
+
}
|
|
757
|
+
|
|
758
|
+
removeListener(channel, callback) {
|
|
759
|
+
if (this._listeners.has(channel)) {
|
|
760
|
+
const listeners = this._listeners.get(channel);
|
|
761
|
+
|
|
762
|
+
listeners.delete(callback);
|
|
763
|
+
|
|
764
|
+
if (listeners.size === 0) {
|
|
765
|
+
this._listeners.delete(channel);
|
|
766
|
+
}
|
|
767
|
+
}
|
|
768
|
+
}
|
|
769
|
+
/** @private */
|
|
770
|
+
|
|
771
|
+
|
|
772
|
+
async _emit(channel) {
|
|
773
|
+
if (this._listeners.has(channel)) {
|
|
774
|
+
const listeners = this._listeners.get(channel);
|
|
775
|
+
|
|
776
|
+
for (let callback of listeners) {
|
|
777
|
+
await callback();
|
|
778
|
+
}
|
|
779
|
+
}
|
|
780
|
+
}
|
|
781
|
+
|
|
706
782
|
}
|
|
707
783
|
|
|
708
784
|
var _default = Server;
|