@iobroker/socket-classes 0.1.2

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.
@@ -0,0 +1,1150 @@
1
+ const SocketCommands = require('./socketCommands');
2
+ const axios = require('axios');
3
+
4
+ class SocketCommandsAdmin extends SocketCommands {
5
+ static ALLOW_CACHE = [
6
+ 'getRepository',
7
+ 'getInstalled',
8
+ 'getInstalledAdapter',
9
+ 'getVersion',
10
+ 'getDiagData',
11
+ 'getLocationOnDisk',
12
+ 'getDevList',
13
+ 'getLogs',
14
+ 'getHostInfo',
15
+ ];
16
+
17
+ constructor(adapter, updateSession, objects, states) {
18
+ super(adapter, updateSession);
19
+
20
+ this.objects = objects;
21
+ this.states = states;
22
+
23
+ this.thersholdInterval = null;
24
+ this.cmdSessions = {};
25
+
26
+ this.eventsThreshold = {
27
+ count: 0,
28
+ timeActivated: 0,
29
+ active: false,
30
+ accidents: 0,
31
+ repeatSeconds: 3, // how many seconds continuously must be number of events > value
32
+ value: parseInt(adapter.config.thresholdValue, 10) || 200, // how many events allowed in one check interval
33
+ checkInterval: 1000 // duration of one check interval
34
+ };
35
+
36
+
37
+ this.cache = {};
38
+ this.cacheGB = null; // cache garbage collector
39
+ // do not send too many state updates
40
+ }
41
+
42
+ start(onThresholdChanged) {
43
+ // detect event bursts
44
+ this.thersholdInterval = setInterval(() => {
45
+ if (!this.eventsThreshold.active) {
46
+ if (this.eventsThreshold.count > this.eventsThreshold.value) {
47
+ this.eventsThreshold.accidents++;
48
+
49
+ if (this.eventsThreshold.accidents >= this.eventsThreshold.repeatSeconds) {
50
+ this._enableEventThreshold();
51
+ }
52
+ } else {
53
+ this.eventsThreshold.accidents = 0;
54
+ }
55
+ this.eventsThreshold.count = 0;
56
+ } else if (Date.now() - this.eventsThreshold.timeActivated > 60000) {
57
+ this.disableEventThreshold();
58
+ }
59
+ }, this.eventsThreshold.checkInterval);
60
+
61
+ this.onThresholdChanged = onThresholdChanged;
62
+ }
63
+
64
+ _fixAdminUI(obj) {
65
+ if (obj && obj.common && !obj.common.adminUI) {
66
+ if (obj.common.noConfig) {
67
+ obj.common.adminUI = obj.common.adminUI || {};
68
+ obj.common.adminUI.config = 'none';
69
+ } else if (obj.common.jsonConfig) {
70
+ obj.common.adminUI = obj.common.adminUI || {};
71
+ obj.common.adminUI.config = 'json';
72
+ } else if (obj.common.materialize) {
73
+ obj.common.adminUI = obj.common.adminUI || {};
74
+ obj.common.adminUI.config = 'materialize';
75
+ } else {
76
+ obj.common.adminUI = obj.common.adminUI || {};
77
+ obj.common.adminUI.config = 'html';
78
+ }
79
+
80
+ if (obj.common.jsonCustom) {
81
+ obj.common.adminUI = obj.common.adminUI || {};
82
+ obj.common.adminUI.custom = 'json';
83
+ } else if (obj.common.supportCustoms) {
84
+ obj.common.adminUI = obj.common.adminUI || {};
85
+ obj.common.adminUI.custom = 'json';
86
+ }
87
+
88
+ if (obj.common.materializeTab && obj.common.adminTab) {
89
+ obj.common.adminUI = obj.common.adminUI || {};
90
+ obj.common.adminUI.tab = 'materialize';
91
+ } else if (obj.common.adminTab) {
92
+ obj.common.adminUI = obj.common.adminUI || {};
93
+ obj.common.adminUI.tab = 'html';
94
+ }
95
+
96
+ obj.common.adminUI && this.adapter.log.debug(`Please add to "${obj._id.replace(/\.\d+$/, '')}" common.adminUI=${JSON.stringify(obj.common.adminUI)}`);
97
+ }
98
+ }
99
+
100
+ _readInstanceConfig(id, user, isTab, configs) {
101
+ return new Promise(resolve =>
102
+ this.adapter.getForeignObject('system.adapter.' + id, {user}, (err, obj) => {
103
+ if (obj && obj.common) {
104
+ const instance = id.split('.').pop();
105
+ const config = {
106
+ id,
107
+ title: obj.common.titleLang || obj.common.title,
108
+ desc: obj.common.desc,
109
+ color: obj.common.color,
110
+ url: `/adapter/${obj.common.name}/${isTab ? 'tab' : 'index'}${!isTab && obj.common.materialize ? '_m' : ''}.html${instance ? '?' + instance : ''}`,
111
+ icon: obj.common.icon,
112
+ materialize: obj.common.materialize,
113
+ jsonConfig: obj.common.jsonConfig,
114
+ };
115
+ if (isTab) {
116
+ config.tab = true;
117
+ } else {
118
+ config.config = true;
119
+ }
120
+ /*if (typeof config.title === 'object') {
121
+ config.title = config.title[adapter.systemConfig.language] || config.title.en;
122
+ }
123
+ if (typeof config.desc === 'object') {
124
+ config.desc = config.desc[adapter.systemConfig.language] || config.desc.en;
125
+ }*/
126
+ configs.push(config);
127
+ }
128
+ resolve();
129
+ }));
130
+ }
131
+
132
+ _sendToHost(host, command, message, callback) {
133
+ const hash = `${host}_${command}`;
134
+ if (!message && SocketCommandsAdmin.ALLOW_CACHE.includes(command) && this.cache[hash]) {
135
+ if (Date.now() - this.cache[hash].ts < 500) {
136
+ return typeof callback === 'function' && setImmediate(data => callback(data), JSON.parse(this.cache[hash].res));
137
+ } else {
138
+ delete this.cache[hash];
139
+ }
140
+ }
141
+ this.adapter.sendToHost(host, command, message, res => {
142
+ if (!message && SocketCommandsAdmin.ALLOW_CACHE.includes(command)) {
143
+ this.cache[hash] = {ts: Date.now(), res: JSON.stringify(res)};
144
+
145
+ this.cacheGB = this.cacheGB || setInterval(() => {
146
+ const commands = Object.keys(this.cache);
147
+ commands.forEach(cmd => {
148
+ if (Date.now() - this.cache[cmd].ts > 500) {
149
+ delete this.cache[cmd];
150
+ }
151
+ });
152
+ if (!commands.length) {
153
+ clearInterval(this.cacheGB);
154
+ this.cacheGB = null;
155
+ }
156
+ }, 2000);
157
+ }
158
+ typeof callback === 'function' && setImmediate(() => callback(res));
159
+ });
160
+ }
161
+
162
+ // remove this function when js.controller 4.x will be mainstream
163
+ _readLicenses(login, password) {
164
+ const config = {
165
+ headers: { Authorization: `Basic ${Buffer.from(login + ':' + password).toString('base64')}` },
166
+ timeout: 4000,
167
+ validateStatus: status => status < 400
168
+ };
169
+
170
+ return axios.get(`https://iobroker.net:3001/api/v1/licenses`, config)
171
+ .then(response => {
172
+ if (response.data && response.data.length) {
173
+ const now = Date.now();
174
+ response.data = response.data.filter(license => !license.validTill || license.validTill === '0000-00-00 00:00:00' || new Date(license.validTill).getTime() > now);
175
+ }
176
+ return response.data;
177
+ })
178
+ .catch(err => {
179
+ if (err.response) {
180
+ throw new Error((err.response.data && err.response.data.error) || err.response.data || err.response.status);
181
+ } else if (err.request) {
182
+ throw new Error('no response');
183
+ } else {
184
+ throw err;
185
+ }
186
+ });
187
+ }
188
+
189
+ // remove this function when js.controller 4.x will be mainstream
190
+ _updateLicenses(login, password) {
191
+ // if login and password provided in the message, just try to read without saving it in system.licenses
192
+ if (login && password) {
193
+ return this._readLicenses(login, password);
194
+ } else {
195
+ // get actual object
196
+ return this.adapter.getForeignObjectAsync('system.licenses')
197
+ .then(systemLicenses => {
198
+ // If password and login exist
199
+ if (systemLicenses && systemLicenses.native && systemLicenses.native.password && systemLicenses.native.login) {
200
+ // get the secret to decode the password
201
+ return this.adapter.getForeignObjectAsync('system.config')
202
+ .then(systemConfig => {
203
+ // decode the password
204
+ let password;
205
+ try {
206
+ password = this.adapter.decrypt(systemConfig.native.secret, systemLicenses.native.password);
207
+ } catch {
208
+ throw new Error('Cannot decode password');
209
+ }
210
+
211
+ // read licenses from iobroker.net
212
+ return this._readLicenses(systemLicenses.native.login, password);
213
+ })
214
+ .then(licenses => {
215
+ // save licenses to system.licenses and remember the time
216
+ // merge the information together
217
+ const oldLicenses = systemLicenses.native.licenses || [];
218
+ systemLicenses.native.licenses = licenses;
219
+ oldLicenses.forEach(oldLicense => {
220
+ if (oldLicense.usedBy) {
221
+ const newLicense = licenses.find(item => item.json === oldLicense.json);
222
+ if (newLicense) {
223
+ newLicense.usedBy = oldLicense.usedBy;
224
+ }
225
+ }
226
+ });
227
+
228
+ systemLicenses.native.readTime = new Date().toISOString();
229
+
230
+ // save only if object changed
231
+ return this.adapter.setForeignObjectAsync('system.licenses', systemLicenses)
232
+ .then(() => licenses);
233
+ })
234
+ .catch(err => {
235
+ // if password is invalid
236
+ if (err.message.includes('Authentication required') || err.message.includes('Cannot decode password')) {
237
+ // clear existing licenses if exist
238
+ if (systemLicenses && systemLicenses.native && systemLicenses.native.licenses && systemLicenses.native.licenses.length) {
239
+ systemLicenses.native.licenses = [];
240
+ systemLicenses.native.readTime = new Date().toISOString();
241
+ return this.adapter.setForeignObjectAsync('system.licenses', systemLicenses)
242
+ .then(() => {
243
+ throw err;
244
+ });
245
+ } else {
246
+ throw err;
247
+ }
248
+ } else {
249
+ throw err;
250
+ }
251
+ });
252
+ } else {
253
+ // if password or login are empty => clear existing licenses if exist
254
+ if (systemLicenses && systemLicenses.native && systemLicenses.native.licenses && systemLicenses.native.licenses.length) {
255
+ systemLicenses.native.licenses = [];
256
+ systemLicenses.native.readTime = new Date().toISOString();
257
+ return this.adapter.setForeignObjectAsync('system.licenses', systemLicenses)
258
+ .then(() => {
259
+ throw new Error('No password or login');
260
+ });
261
+ } else {
262
+ throw new Error('No password or login');
263
+ }
264
+ }
265
+ });
266
+ }
267
+ }
268
+
269
+ disableEventThreshold(readAll) {
270
+ if (this.eventsThreshold.active) {
271
+ this.eventsThreshold.accidents = 0;
272
+ this.eventsThreshold.count = 0;
273
+ this.eventsThreshold.active = false;
274
+ this.eventsThreshold.timeActivated = 0;
275
+ this.adapter.log.info('Subscribe on all states again');
276
+
277
+ setTimeout(() => {
278
+ /*
279
+ if (readAll) {
280
+ this.adapter.getForeignStates('*', (err, res) => {
281
+ this.adapter.log.info('received all states');
282
+ for (const id in res) {
283
+ if (res.hasOwnProperty(id) && JSON.stringify(states[id]) !== JSON.stringify(res[id])) {
284
+ this.server.sockets.emit('stateChange', id, res[id]);
285
+ states[id] = res[id];
286
+ }
287
+ }
288
+ });
289
+ }
290
+ */
291
+
292
+ this.onThresholdChanged && this.onThresholdChanged(false);
293
+ this.adapter.unsubscribeForeignStates('system.adapter.*');
294
+
295
+ Object.keys(this.subscribes.stateChange).forEach(pattern =>
296
+ this.adapter.subscribeForeignStates(pattern));
297
+ }, 50);
298
+ }
299
+ }
300
+
301
+ _enableEventThreshold() {
302
+ if (!this.eventsThreshold.active) {
303
+ this.eventsThreshold.active = true;
304
+
305
+ setTimeout(() => {
306
+ this.adapter.log.info(`Unsubscribe from all states, except system's, because over ${this.eventsThreshold.repeatSeconds} seconds the number of events is over ${this.eventsThreshold.value} (in last second ${this.eventsThreshold.count})`);
307
+ this.eventsThreshold.timeActivated = Date.now();
308
+
309
+ this.onThresholdChanged && this.onThresholdChanged(true);
310
+ //this.server && this.server.sockets && this.server.sockets.emit('eventsThreshold', true);
311
+
312
+ Object.keys(this.subscribes.stateChange).forEach(pattern =>
313
+ this.adapter.unsubscribeForeignStates(pattern));
314
+
315
+ this.adapter.subscribeForeignStates('system.adapter.*');
316
+ }, 100);
317
+ }
318
+ }
319
+
320
+ _addUser(user, pw, options, callback) {
321
+ if (typeof options === 'function') {
322
+ callback = options;
323
+ options = null;
324
+ }
325
+
326
+ if (!user.match(/^[-.A-Za-züäößÖÄÜа-яА-Я@+$§0-9=?!&# ]+$/)) {
327
+ if (typeof callback === 'function') {
328
+ callback('Invalid characters in the name. Only following special characters are allowed: -@+$§=?!&# and letters');
329
+ }
330
+ return;
331
+ }
332
+
333
+ this.adapter.getForeignObject('system.user.' + user, options, (err, obj) => {
334
+ if (obj) {
335
+ if (typeof callback === 'function') {
336
+ callback('User yet exists');
337
+ }
338
+ } else {
339
+ this.adapter.setForeignObject('system.user.' + user, {
340
+ type: 'user',
341
+ common: {
342
+ name: user,
343
+ enabled: true,
344
+ groups: []
345
+ }
346
+ }, options, () => {
347
+ this.adapter.setPassword(user, pw, options, callback);
348
+ });
349
+ }
350
+ });
351
+ }
352
+
353
+ _delUser(user, options, callback) {
354
+ this.adapter.getForeignObject('system.user.' + user, options, (err, obj) => {
355
+ if (err || !obj) {
356
+ if (typeof callback === 'function') {
357
+ callback('User does not exist');
358
+ }
359
+ } else {
360
+ if (obj.common.dontDelete) {
361
+ if (typeof callback === 'function') {
362
+ callback('Cannot delete user, while is system user');
363
+ }
364
+ } else {
365
+ this.adapter.delForeignObject('system.user.' + user, options, err =>
366
+ // Remove this user from all groups in web client
367
+ typeof callback === 'function' && callback(err));
368
+ }
369
+ }
370
+ });
371
+ }
372
+
373
+ _addGroup(group, desc, acl, options, callback) {
374
+ let name = group;
375
+ if (typeof acl === 'function') {
376
+ callback = acl;
377
+ acl = null;
378
+ }
379
+ if (typeof desc === 'function') {
380
+ callback = desc;
381
+ desc = null;
382
+ }
383
+ if (typeof options === 'function') {
384
+ callback = options;
385
+ options = null;
386
+ }
387
+ if (name && name.substring(0, 1) !== name.substring(0, 1).toUpperCase()) {
388
+ name = name.substring(0, 1).toUpperCase() + name.substring(1);
389
+ }
390
+ group = group.substring(0, 1).toLowerCase() + group.substring(1);
391
+
392
+ if (!group.match(/^[-.A-Za-züäößÖÄÜа-яА-Я@+$§0-9=?!&#_ ]+$/)) {
393
+ return typeof callback === 'function' && callback('Invalid characters in the group name. Only following special characters are allowed: -@+$§=?!&# and letters');
394
+ }
395
+
396
+ this.adapter.getForeignObject('system.group.' + group, options, (err, obj) => {
397
+ if (obj) {
398
+ typeof callback === 'function' && callback('Group yet exists');
399
+ } else {
400
+ obj = {
401
+ _id: 'system.group.' + group,
402
+ type: 'group',
403
+ common: {
404
+ name,
405
+ desc,
406
+ members: [],
407
+ acl: acl
408
+ }
409
+ };
410
+ this.adapter.setForeignObject('system.group.' + group, obj, options, err =>
411
+ typeof callback === 'function' && callback(err, obj));
412
+ }
413
+ });
414
+ }
415
+
416
+ _delGroup(group, options, callback) {
417
+ this.adapter.getForeignObject('system.group.' + group, options, (err, obj) => {
418
+ if (err || !obj) {
419
+ typeof callback === 'function' && callback('Group does not exist');
420
+ } else {
421
+ if (obj.common.dontDelete) {
422
+ typeof callback === 'function' && callback('Cannot delete group, while is system group');
423
+ } else {
424
+ this.adapter.delForeignObject('system.group.' + group, options, err => {
425
+ // Remove this group from all users in web client
426
+ typeof callback === 'function' && callback(err);
427
+ });
428
+ }
429
+ }
430
+ });
431
+ }
432
+
433
+ static _checkObject(obj, options, flag) {
434
+ // read rights of object
435
+ if (!obj || !obj.common || !obj.acl || flag === 'list') {
436
+ return true;
437
+ }
438
+
439
+ if (options.user !== 'system.user.admin' && !options.groups.includes('system.group.administrator')) {
440
+ if (obj.acl.owner !== options.user) {
441
+ // Check if the user is in the group
442
+ if (options.groups.includes(obj.acl.ownerGroup)) {
443
+ // Check group rights
444
+ if (!(obj.acl.object & (flag << 4))) {
445
+ return false;
446
+ }
447
+ } else {
448
+ // everybody
449
+ if (!(obj.acl.object & flag)) {
450
+ return false;
451
+ }
452
+ }
453
+ } else {
454
+ // Check group rights
455
+ if (!(obj.acl.object & (flag << 8))) {
456
+ return false;
457
+ }
458
+ }
459
+ }
460
+ return true;
461
+ }
462
+
463
+ _getAllObjects(socket, callback) {
464
+ if (typeof callback !== 'function') {
465
+ return this.adapter.log.warn('[_getAllObjects] Invalid callback');
466
+ }
467
+
468
+ if (this._checkPermissions(socket, 'getObjects', callback)) {
469
+ if (this.objects) {
470
+ if (socket._acl &&
471
+ socket._acl.user !== 'system.user.admin' &&
472
+ !socket._acl.groups.includes('system.group.administrator')) {
473
+ const result = {};
474
+ for (const id in this.objects) {
475
+ if (this.objects.hasOwnProperty(id) && SocketCommandsAdmin._checkObject(this.objects[id], socket._acl, 4 /* 'read' */)) {
476
+ result[id] = this.objects[id];
477
+ }
478
+ }
479
+ callback(null, result);
480
+ } else {
481
+ callback(null, this.objects);
482
+ }
483
+ } else {
484
+ this.adapter.getObjectList({include_docs: true}, (err, res) => {
485
+ this.adapter.log.info('received all objects');
486
+ res = res.rows;
487
+ const objects = {};
488
+
489
+ if (socket._acl &&
490
+ socket._acl.user !== 'system.user.admin' &&
491
+ !socket._acl.groups.includes('system.group.administrator')) {
492
+ for (let i = 0; i < res.length; i++) {
493
+ if (SocketCommandsAdmin._checkObject(res[i].doc, socket._acl, 4 /* 'read' */)) {
494
+ objects[res[i].doc._id] = res[i].doc;
495
+ }
496
+ }
497
+ callback(null, objects);
498
+ } else {
499
+ for (let j = 0; j < res.length; j++) {
500
+ objects[res[j].doc._id] = res[j].doc;
501
+ }
502
+ callback(null, objects);
503
+ }
504
+ });
505
+ }
506
+ }
507
+ }
508
+
509
+ _initCommandsUser() {
510
+ this.commands['addUser'] = (socket, user, pass, callback) => {
511
+ if (this._checkPermissions(socket, 'addUser', callback, user)) {
512
+ this._addUser(user, pass, {user: socket._acl.user}, (err, ...args) =>
513
+ SocketCommands._fixCallback(callback, err, ...args));
514
+ }
515
+ };
516
+
517
+ this.commands['delUser'] = (socket, user, callback) => {
518
+ if (this._checkPermissions(socket, 'delUser', callback, user)) {
519
+ this._delUser(user, {user: socket._acl.user}, (err, ...args) =>
520
+ SocketCommands._fixCallback(callback, err, ...args));
521
+ }
522
+ };
523
+
524
+ this.commands['addGroup'] = (socket, group, desc, acl, callback) => {
525
+ if (this._checkPermissions(socket, 'addGroup', callback, group)) {
526
+ this._addGroup(group, desc, acl, {user: socket._acl.user}, (err, ...args) =>
527
+ SocketCommands._fixCallback(callback, err, ...args));
528
+ }
529
+ };
530
+
531
+ this.commands['delGroup'] = (socket, group, callback) => {
532
+ if (this._checkPermissions(socket, 'delGroup', callback, group)) {
533
+ this._delGroup(group, {user: socket._acl.user}, (err, ...args) =>
534
+ SocketCommands._fixCallback(callback, err, ...args));
535
+ }
536
+ };
537
+ this.commands['changePassword'] = (socket, user, pass, callback) => {
538
+ if (this.__updateSession(socket)) {
539
+ if (user === socket._acl.user || this._checkPermissions(socket, 'changePassword', callback, user)) {
540
+ this.adapter.setPassword(user, pass, {user: socket._acl.user}, (err, ...args) =>
541
+ SocketCommands._fixCallback(callback, err, ...args));
542
+ }
543
+ }
544
+ };
545
+ }
546
+
547
+ _initCommandsAdmin() {
548
+ this.commands['getHostByIp'] = (socket, ip, callback) => {
549
+ if (typeof callback !== 'function') {
550
+ return this.adapter.log.warn('[getHostByIp] Invalid callback');
551
+ }
552
+ if (this._checkPermissions(socket, 'getHostByIp', ip)) {
553
+ this.adapter.getObjectView('system', 'host', {}, {user: socket._acl.user}, (err, data) => {
554
+ if (data && data.rows && data.rows.length) {
555
+ for (let i = 0; i < data.rows.length; i++) {
556
+ const obj = data.rows[i].value;
557
+ // if we requested specific name
558
+ if (obj.common.hostname === ip) {
559
+ return callback(ip, obj);
560
+ } else
561
+ // try to find this IP in the list
562
+ if (obj.native.hardware && obj.native.hardware.networkInterfaces) {
563
+ const net = obj.native.hardware.networkInterfaces;
564
+ for (const eth in net) {
565
+ if (!Object.prototype.hasOwnProperty.call(net, eth)) {
566
+ continue;
567
+ }
568
+ for (let j = 0; j < net[eth].length; j++) {
569
+ if (net[eth][j].address === ip) {
570
+ return callback(ip, obj);
571
+ }
572
+ }
573
+ }
574
+ }
575
+ }
576
+ }
577
+
578
+ callback(ip, null);
579
+ });
580
+ }
581
+ };
582
+
583
+ this.commands['requireLog'] = (socket, isEnabled, callback) => {
584
+ if (this._checkPermissions(socket, 'setObject', callback)) {
585
+ if (isEnabled) {
586
+ this.subscribe(socket, 'log', 'dummy');
587
+ } else {
588
+ this.unsubscribe(socket, 'log', 'dummy');
589
+ }
590
+
591
+ this.adapter.log.level === 'debug' && this._showSubscribes(socket, 'log');
592
+
593
+ typeof callback === 'function' && setImmediate(callback, null);
594
+ }
595
+ };
596
+
597
+ this.commands['readLogs'] = (socket, host, callback) => {
598
+ if (this._checkPermissions(socket, 'readLogs', callback)) {
599
+ let timeout = setTimeout(() => {
600
+ if (timeout) {
601
+ let result = {list: []};
602
+
603
+ // deliver file list
604
+ try {
605
+ const config = this.adapter.systemConfig;
606
+ // detect file log
607
+ if (config && config.log && config.log.transport) {
608
+ for (const transport in config.log.transport) {
609
+ if (Object.prototype.hasOwnProperty.call(config.log.transport, transport) && config.log.transport[transport].type === 'file') {
610
+ let filename = config.log.transport[transport].filename || 'log/';
611
+ const parts = filename.replace(/\\/g, '/').split('/');
612
+ parts.pop();
613
+ filename = parts.join('/');
614
+ if (filename[0] !== '/' && !filename.match(/^\W:/)) {
615
+ const _filename = path.normalize(__dirname + '/../../../') + filename;
616
+ if (!fs.existsSync(_filename)) {
617
+ filename = path.normalize(__dirname + '/../../') + filename;
618
+ } else {
619
+ filename = _filename;
620
+ }
621
+ }
622
+ if (fs.existsSync(filename)) {
623
+ const files = fs.readdirSync(filename);
624
+
625
+ for (let f = 0; f < files.length; f++) {
626
+ try {
627
+ if (!files[f].endsWith('-audit.json')) {
628
+ const stat = fs.lstatSync(filename + '/' + files[f]);
629
+ if (!stat.isDirectory()) {
630
+ result.list.push({
631
+ fileName: 'log/' + transport + '/' + files[f],
632
+ size: stat.size
633
+ });
634
+ }
635
+ }
636
+ } catch (e) {
637
+ // push unchecked
638
+ // result.list.push('log/' + transport + '/' + files[f]);
639
+ this.adapter.log.error(`Cannot check file: ${filename}/${files[f]}`);
640
+ }
641
+ }
642
+ }
643
+ }
644
+ }
645
+ } else {
646
+ result = {error: 'no file loggers'};
647
+ }
648
+ } catch (e) {
649
+ this.adapter.log.error(e);
650
+ result = {error: e};
651
+ }
652
+ typeof callback === 'function' && callback(result.error, result.list);
653
+ }
654
+ }, 500);
655
+
656
+ this._sendToHost(host, 'getLogFiles', null, result => {
657
+ clearTimeout(timeout);
658
+ timeout = null;
659
+ typeof callback === 'function' && callback(result.error, result.list);
660
+ });
661
+ }
662
+ };
663
+
664
+ this.commands['delState'] = (socket, id, callback) => {
665
+ if (this._checkPermissions(socket, 'delState', callback, id)) {
666
+ // clear cache
667
+ if (this.states && this.states[id]) {
668
+ delete this.states[id];
669
+ }
670
+ this.adapter.delForeignState(id, {user: socket._acl.user}, (err, ...args) =>
671
+ SocketCommands._fixCallback(callback, err, ...args));
672
+ }
673
+ };
674
+
675
+ // commands will be executed on host/controller
676
+ // following response commands are expected: cmdStdout, cmdStderr, cmdExit
677
+ this.commands['cmdExec'] = (socket, host, id, cmd, callback) => {
678
+ if (this._checkPermissions(socket, 'cmdExec', callback, cmd)) {
679
+ this.adapter.log.debug(`cmdExec on ${host}(${id}): ${cmd}`);
680
+ // remember socket for this ID.
681
+ this.cmdSessions[id] = {socket};
682
+ this.adapter.sendToHost(host, 'cmdExec', {data: cmd, id: id});
683
+ }
684
+ };
685
+
686
+ this.commands['eventsThreshold'] = (socket, isActive) => {
687
+ if (!isActive) {
688
+ this.disableEventThreshold(true);
689
+ } else {
690
+ this._enableEventThreshold();
691
+ }
692
+ };
693
+
694
+ this.commands['getRatings'] = (socket, update, callback) => {
695
+ if (update) {
696
+ this.adapter._updateRatings()
697
+ .then(() => typeof callback === 'function' && callback(null, this.adapter._ratings));
698
+ } else {
699
+ typeof callback === 'function' && callback(null, this.adapter._ratings);
700
+ }
701
+ };
702
+
703
+ this.commands['getCurrentInstance'] = (socket, callback) => {
704
+ typeof callback === 'function' && callback(null, this.adapter.namespace);
705
+ };
706
+
707
+ this.commands['checkFeatureSupported'] = (socket, feature, callback) => {
708
+ typeof callback === 'function' && callback(null, this.adapter.supportsFeature && this.adapter.supportsFeature(feature));
709
+ };
710
+
711
+ this.commands['decrypt'] = (socket, encryptedText, callback) => {
712
+ if (this.secret) {
713
+ typeof callback === 'function' && callback(null, this.adapter.decrypt(this.secret, encryptedText));
714
+ } else {
715
+ this.adapter.getForeignObject('system.config', (err, obj) => {
716
+ if (obj && obj.native && obj.native.secret) {
717
+ this.secret = obj.native.secret;
718
+ typeof callback === 'function' && callback(null, this.adapter.decrypt(this.secret, encryptedText));
719
+ } else {
720
+ this.adapter.log.error(`No system.config found: ${err}`);
721
+ SocketCommands._fixCallback(callback, err);
722
+ }
723
+ });
724
+ }
725
+ };
726
+
727
+ this.commands['encrypt'] = (socket, plainText, callback) => {
728
+ if (this.secret) {
729
+ typeof callback === 'function' && callback(null, this.adapter.encrypt(this.secret, plainText));
730
+ } else {
731
+ this.adapter.getForeignObject('system.config', (err, obj) => {
732
+ if (obj && obj.native && obj.native.secret) {
733
+ this.secret = obj.native.secret;
734
+ typeof callback === 'function' && callback(null, this.adapter.encrypt(this.secret, plainText));
735
+ } else {
736
+ this.adapter.log.error(`No system.config found: ${err}`);
737
+ SocketCommands._fixCallback(callback, err);
738
+ }
739
+ });
740
+ }
741
+ };
742
+
743
+ this.commands['getIsEasyModeStrict'] = (socket, callback) => {
744
+ typeof callback === 'function' && callback(null, this.adapter.config.accessLimit);
745
+ };
746
+
747
+ this.commands['getEasyMode'] = (socket, callback) => {
748
+ if (this._checkPermissions(socket, 'getObject', callback)) {
749
+ let user;
750
+ if (this.adapter.config.auth) {
751
+ user = socket._acl.user;
752
+ if (!user.startsWith('system.user.')) {
753
+ user = 'system.user.' + user;
754
+ }
755
+ } else {
756
+ user = this.adapter.config.defaultUser;
757
+ }
758
+
759
+ if (this.adapter.config.accessLimit) {
760
+ const configs = [];
761
+ const promises = [];
762
+ this.adapter.config.accessAllowedConfigs.forEach(id => promises.push(this._readInstanceConfig(id, user, false, configs)));
763
+ this.adapter.config.accessAllowedTabs.forEach(id => promises.push(this._readInstanceConfig(id, user, true, configs)));
764
+
765
+ Promise.all(promises)
766
+ .then(() => {
767
+ callback(null, {
768
+ strict: true,
769
+ configs
770
+ });
771
+ });
772
+ } else {
773
+ this.adapter.getObjectView('system', 'instance', {startkey: 'system.adapter.', endkey: 'system.adapter.\u9999'}, {user}, (err, doc) => {
774
+ const promises = [];
775
+ const configs = [];
776
+ if (!err && doc.rows.length) {
777
+ for (let i = 0; i < doc.rows.length; i++) {
778
+ const obj = doc.rows[i].value;
779
+ if (obj.common.noConfig && !obj.common.adminTab) {
780
+ continue;
781
+ }
782
+ if (!obj.common.enabled) {
783
+ continue;
784
+ }
785
+ if (!obj.common.noConfig) {
786
+ promises.push(this._readInstanceConfig(obj._id.substring('system.adapter.'.length), user, false, configs));
787
+ }
788
+ }
789
+ }
790
+ Promise.all(promises)
791
+ .then(() =>
792
+ callback(null, {
793
+ strict: false,
794
+ configs
795
+ })
796
+ );
797
+ });
798
+ }
799
+ }
800
+ };
801
+
802
+ this.commands['getAdapterInstances'] = (socket, adapterName, callback) => {
803
+ if (typeof callback === 'function') {
804
+ if (this._checkPermissions(socket, 'getObject', callback)) {
805
+ let _adapterName = adapterName || this.adapterName || '';
806
+ if (_adapterName) {
807
+ _adapterName += '.'
808
+ }
809
+ this.adapter.getObjectView('system', 'instance',
810
+ {startkey: `system.adapter.${_adapterName}`, endkey: `system.adapter.${_adapterName}\u9999`},
811
+ (err, doc) => {
812
+ if (err) {
813
+ callback(err);
814
+ } else {
815
+ callback(null, doc.rows
816
+ .map(item => {
817
+ const obj = item.value;
818
+ if (obj.common) {
819
+ delete obj.common.news;
820
+ }
821
+ this._fixAdminUI(obj);
822
+ return obj;
823
+ })
824
+ .filter(obj => obj && (!adapterName || (obj.common && obj.common.name === adapterName))));
825
+ }
826
+ });
827
+ }
828
+ }
829
+ };
830
+
831
+ this.commands['getAdapters'] = (socket, adapterName, callback) => {
832
+ if (typeof callback === 'function' && this._checkPermissions(socket, 'getObject', callback)) {
833
+ this.adapter.getObjectView('system', 'adapter',
834
+ {startkey: `system.adapter.${adapterName || ''}`, endkey: `system.adapter.${adapterName || '\u9999'}`},
835
+ (err, doc) =>
836
+ {
837
+ if (err) {
838
+ callback(err);
839
+ } else {
840
+ callback(null, doc.rows
841
+ .filter(obj => obj && (!adapterName || (obj.common && obj.common.name === this.adapterName)))
842
+ .map(item => {
843
+ const obj = item.value;
844
+ if (obj.common) {
845
+ delete obj.common.news;
846
+ delete obj.native;
847
+ }
848
+ this._fixAdminUI(obj);
849
+ return obj;
850
+ }));
851
+ }
852
+ });
853
+ }
854
+ };
855
+
856
+ this.commands['updateLicenses'] = (socket, login, password, callback) => {
857
+ if (this._checkPermissions(socket, 'setObject', callback, login, password)) {
858
+ if (this.adapter.supportsFeature('CONTROLLER_LICENSE_MANAGER')) {
859
+ let timeout = setTimeout(() => {
860
+ if (timeout) {
861
+ timeout = null;
862
+ typeof callback === 'function' && callback('updateLicenses timeout');
863
+ }
864
+ }, 7000);
865
+
866
+ this._sendToHost(this.adapter.common.host, 'updateLicenses', {login, password}, result => {
867
+ if (timeout) {
868
+ clearTimeout(timeout);
869
+ timeout = null;
870
+ typeof callback === 'function' && callback(result.error, result && result.result);
871
+ }
872
+ });
873
+ } else {
874
+ // remove this branch when js-controller 4.x will be mainstream
875
+ this._updateLicenses(login, password)
876
+ .then(licenses => typeof callback === 'function' && callback(null, licenses))
877
+ .catch(err => typeof callback === 'function' && callback(err));
878
+ }
879
+ }
880
+ };
881
+
882
+ this.commands['getCompactInstances'] = (socket, callback) => {
883
+ if (typeof callback === 'function') {
884
+ if (this._checkPermissions(socket, 'getObject', callback)) {
885
+ this.adapter.getObjectView('system', 'instance',
886
+ {startkey: `system.adapter.`, endkey: `system.adapter.\u9999`},
887
+ (err, doc) => {
888
+ if (err) {
889
+ callback(err);
890
+ } else {
891
+ // calculate
892
+ const result = {};
893
+
894
+ doc.rows.forEach(item => {
895
+ const obj = item.value;
896
+ result[item.id] = {
897
+ adminTab: obj.common.adminTab,
898
+ name: obj.common.name,
899
+ icon: obj.common.icon,
900
+ enabled: obj.common.enabled
901
+ };
902
+ });
903
+
904
+ callback(null, result);
905
+ }
906
+ });
907
+ }
908
+ }
909
+ };
910
+
911
+ this.commands['getCompactAdapters'] = (socket, callback) => {
912
+ if (typeof callback === 'function') {
913
+ if (this._checkPermissions(socket, 'getObject', callback)) {
914
+ this.adapter.getObjectView('system', 'adapter',
915
+ {startkey: `system.adapter.`, endkey: `system.adapter.\u9999`},
916
+ (err, doc) => {
917
+ if (err) {
918
+ callback(err);
919
+ } else {
920
+ // calculate
921
+ const result = {};
922
+
923
+ doc.rows.forEach(item => {
924
+ const obj = item.value;
925
+ if (obj && obj.common && obj.common.name) {
926
+ result[obj.common.name] = {icon: obj.common.icon, v: obj.common.version};
927
+ if (obj.common.ignoreVersion) {
928
+ result[obj.common.name].iv = obj.common.ignoreVersion;
929
+ }
930
+ }
931
+ });
932
+
933
+ callback(null, result);
934
+ }
935
+ });
936
+ }
937
+ }
938
+ };
939
+
940
+ this.commands['getCompactInstalled'] = (socket, host, callback) => {
941
+ if (typeof callback === 'function') {
942
+ if (this._checkPermissions(socket, 'sendToHost', callback)) {
943
+ this._sendToHost(host, 'getInstalled', null, data => {
944
+ const result = {};
945
+ Object.keys(data).forEach(name => result[name] = {version: data[name].version});
946
+ callback(result);
947
+ });
948
+ }
949
+ }
950
+ };
951
+
952
+ this.commands['getCompactSystemConfig'] = (socket, callback) => {
953
+ if (this._checkPermissions(socket, 'getObject', callback)) {
954
+ this.adapter.getForeignObject('system.config', (err, obj) => {
955
+ obj = obj || {};
956
+ const secret = obj.native && obj.native.secret;
957
+ delete obj.native;
958
+ if (secret) {
959
+ obj.native = {secret};
960
+ }
961
+ callback(err, obj);
962
+ });
963
+ }
964
+ };
965
+
966
+ this.commands['getCompactRepository'] = (socket, host, callback) => {
967
+ if (this._checkPermissions(socket, 'sendToHost', callback)) {
968
+ this._sendToHost(host, 'getRepository', null, data => {
969
+ // Extract only version and icon
970
+ const result = {};
971
+ data && Object.keys(data).forEach(name => result[name] = {
972
+ version: data[name].version,
973
+ icon: data[name].extIcon
974
+ });
975
+ callback(result);
976
+ });
977
+ }
978
+ };
979
+
980
+ this.commands['getCompactHosts'] = (socket, callback) => {
981
+ if (this._checkPermissions(socket, 'getObject', callback)) {
982
+ this.adapter.getObjectView('system', 'host',
983
+ {startkey: 'system.host.', endkey: 'system.host.\u9999'}, (err, doc) => {
984
+ if (err) {
985
+ callback(err);
986
+ } else {
987
+ const result = [];
988
+ doc.rows.map(item => {
989
+ const host = item.value;
990
+ if (host) {
991
+ host.common = host.common || {};
992
+ result.push({
993
+ _id: host._id,
994
+ common: {
995
+ name: host.common.name,
996
+ icon: host.common.icon,
997
+ color: host.common.color,
998
+ installedVersion: host.common.installedVersion
999
+ },
1000
+ native: {
1001
+ hardware: {
1002
+ networkInterfaces: (host.native && host.native.hardware && host.native.hardware.networkInterfaces) || undefined
1003
+ }
1004
+ }
1005
+ });
1006
+ }
1007
+ })
1008
+ callback(null, result);
1009
+ }
1010
+ });
1011
+ }
1012
+ };
1013
+ }
1014
+
1015
+ __initCommandsCommon() {
1016
+ super.__initCommandsCommon();
1017
+
1018
+ this._initCommandsAdmin();
1019
+ this._initCommandsUser();
1020
+ }
1021
+
1022
+ __initCommandsStates() {
1023
+ super.__initCommandsStates();
1024
+
1025
+ this.commands['getForeignStates'] = (socket, pattern, callback) => {
1026
+ if (this._checkPermissions(socket, 'getStates', callback)) {
1027
+ if (typeof callback === 'function') {
1028
+ this.adapter.getForeignStates(pattern, (err, ...args) =>
1029
+ SocketCommands._fixCallback(callback, err, ...args));
1030
+ } else {
1031
+ this.adapter.log.warn('[getForeignStates] Invalid callback')
1032
+ }
1033
+ }
1034
+ };
1035
+
1036
+ this.commands['delObjects'] = (socket, id, options, callback) => {
1037
+ if (this._checkPermissions(socket, 'delObject', callback, id)) {
1038
+ if (!options || typeof options !== 'object') {
1039
+ options = {};
1040
+ }
1041
+ options.user = socket._acl.user;
1042
+ options.recursive = true;
1043
+ this.adapter.delForeignObject(id, options, (err, ...args) =>
1044
+ SocketCommands._fixCallback(callback, err, ...args));
1045
+ }
1046
+ };
1047
+ }
1048
+
1049
+ __initCommandsFiles() {
1050
+ super.__initCommandsFiles();
1051
+
1052
+ this.commands['writeFile'] = (socket, _adapter, fileName, data64, options, callback) => {
1053
+ if (this._checkPermissions(socket, 'writeFile', callback, fileName)) {
1054
+ if (typeof options === 'function') {
1055
+ callback = options;
1056
+ options = {user: socket._acl.user};
1057
+ }
1058
+ options = options || {};
1059
+ options.user = socket._acl.user;
1060
+ const buffer = Buffer.from(data64, 'base64');
1061
+ this.adapter.writeFile(_adapter, fileName, buffer, {user: socket._acl.user}, (err, ...args) =>
1062
+ SocketCommands._fixCallback(callback, err, ...args));
1063
+ }
1064
+ };
1065
+ }
1066
+
1067
+ __initCommandsObjects() {
1068
+ super.__initCommandsObjects();
1069
+
1070
+ this.commands['getAllObjects'] = (socket, callback) => {
1071
+ return this._getAllObjects(socket, callback);
1072
+ };
1073
+
1074
+ this.commands['extendObject'] = (socket, id, obj, callback) => {
1075
+ if (this._checkPermissions(socket, 'extendObject', callback, id)) {
1076
+ this.adapter.extendForeignObject(id, obj, {user: socket._acl.user}, (err, ...args) =>
1077
+ SocketCommands._fixCallback(callback, err, ...args));
1078
+ }
1079
+ };
1080
+
1081
+ this.commands['getForeignObjects'] = (socket, pattern, type, callback) => {
1082
+ if (this._checkPermissions(socket, 'getObjects', callback)) {
1083
+ if (typeof type === 'function') {
1084
+ callback = type;
1085
+ type = undefined;
1086
+ }
1087
+
1088
+ this.adapter.getForeignObjects(pattern, type, {user: socket._acl.user}, (err, ...args) => {
1089
+ if (typeof callback === 'function') {
1090
+ SocketCommands._fixCallback(callback, err, ...args);
1091
+ } else {
1092
+ this.adapter.log.warn('[getObjects] Invalid callback');
1093
+ }
1094
+ });
1095
+ }
1096
+ };
1097
+
1098
+ this.commands['delObject'] = (socket, id, options, callback) => {
1099
+ if (this._checkPermissions(socket, 'delObject', callback, id)) {
1100
+ if (typeof options === 'function') {
1101
+ callback = options;
1102
+ options = null;
1103
+ }
1104
+ if (!options || typeof options !== 'object') {
1105
+ options = {};
1106
+ }
1107
+ options.user = socket._acl.user;
1108
+ this.adapter.delForeignObject(id, options, (err, ...args) =>
1109
+ SocketCommands._fixCallback(callback, err, ...args));
1110
+ }
1111
+ };
1112
+ }
1113
+
1114
+ stateChange(id, state) {
1115
+ if (this.states) {
1116
+ if (!state) {
1117
+ if (this.states[id]) {
1118
+ delete this.states[id];
1119
+ }
1120
+ } else {
1121
+ this.states[id] = state;
1122
+ }
1123
+ }
1124
+
1125
+ if (!this.eventsThreshold.active) {
1126
+ this.eventsThreshold.count++;
1127
+ }
1128
+ }
1129
+
1130
+ sendCommand(obj) {
1131
+ if (this.cmdSessions[obj.message.id]) {
1132
+ if (obj.command === 'cmdExit') {
1133
+ delete this.cmdSessions[obj.message.id];
1134
+ }
1135
+ return true;
1136
+ }
1137
+ }
1138
+
1139
+ destroy() {
1140
+ this.thersholdInterval && clearInterval(this.thersholdInterval);
1141
+ this.thersholdInterval = null;
1142
+
1143
+ this.cacheGB && clearInterval(this.cacheGB);
1144
+ this.cacheGB = null;
1145
+
1146
+ super.destroy();
1147
+ }
1148
+ }
1149
+
1150
+ module.exports = SocketCommandsAdmin;