@iobroker/js-controller-common 4.0.0-alpha.9-20211115-5dac659e → 4.0.3
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/LICENSE +1 -1
- package/README.md +1 -1
- package/index.js +1 -1
- package/lib/common/exitCodes.js +5 -1
- package/lib/common/logger.js +152 -126
- package/lib/common/password.js +7 -9
- package/lib/common/tools.js +1029 -483
- package/package.json +7 -6
package/lib/common/tools.js
CHANGED
|
@@ -8,9 +8,11 @@ const forge = require('node-forge');
|
|
|
8
8
|
const deepClone = require('deep-clone');
|
|
9
9
|
const cpPromise = require('promisify-child-process');
|
|
10
10
|
const jwt = require('jsonwebtoken');
|
|
11
|
-
const {createInterface} = require('readline');
|
|
12
|
-
const {PassThrough} = require('stream');
|
|
13
|
-
const {detectPackageManager} = require('@alcalzone/pak');
|
|
11
|
+
const { createInterface } = require('readline');
|
|
12
|
+
const { PassThrough } = require('stream');
|
|
13
|
+
const { detectPackageManager } = require('@alcalzone/pak');
|
|
14
|
+
const EXIT_CODES = require('./exitCodes');
|
|
15
|
+
const zlib = require('zlib');
|
|
14
16
|
|
|
15
17
|
// @ts-ignore
|
|
16
18
|
require('events').EventEmitter.prototype._maxListeners = 100;
|
|
@@ -21,7 +23,7 @@ let password;
|
|
|
21
23
|
let npmVersion;
|
|
22
24
|
let crypto;
|
|
23
25
|
let diskusage;
|
|
24
|
-
const randomID = Math.round(Math.random() * 10000000000000);
|
|
26
|
+
const randomID = Math.round(Math.random() * 10000000000000); // Used for creation of User-Agent
|
|
25
27
|
const VENDOR_FILE = '/etc/iob-vendor.json';
|
|
26
28
|
|
|
27
29
|
let lastCalculationOfIps;
|
|
@@ -54,7 +56,12 @@ const FORBIDDEN_CHARS = /[^._\-/ :!#$%&()+=@^{}|~\p{Ll}\p{Lu}\p{Nd}]+/gu;
|
|
|
54
56
|
*/
|
|
55
57
|
function copyAttributes(oldObj, newObj, originalObj, isNonEdit) {
|
|
56
58
|
for (const attr of Object.keys(oldObj)) {
|
|
57
|
-
if (
|
|
59
|
+
if (
|
|
60
|
+
oldObj[attr] === undefined ||
|
|
61
|
+
oldObj[attr] === null ||
|
|
62
|
+
typeof oldObj[attr] !== 'object' ||
|
|
63
|
+
oldObj[attr] instanceof Array
|
|
64
|
+
) {
|
|
58
65
|
if (oldObj[attr] === '__no_change__' && originalObj && !isNonEdit) {
|
|
59
66
|
if (originalObj[attr] !== undefined) {
|
|
60
67
|
newObj[attr] = deepClone(originalObj[attr]);
|
|
@@ -70,7 +77,12 @@ function copyAttributes(oldObj, newObj, originalObj, isNonEdit) {
|
|
|
70
77
|
}
|
|
71
78
|
} else {
|
|
72
79
|
newObj[attr] = newObj[attr] || {};
|
|
73
|
-
copyAttributes(
|
|
80
|
+
copyAttributes(
|
|
81
|
+
oldObj[attr],
|
|
82
|
+
newObj[attr],
|
|
83
|
+
originalObj && originalObj[attr],
|
|
84
|
+
isNonEdit || attr === 'nonEdit'
|
|
85
|
+
);
|
|
74
86
|
}
|
|
75
87
|
}
|
|
76
88
|
}
|
|
@@ -121,7 +133,6 @@ function checkNonEditable(oldObject, newObject) {
|
|
|
121
133
|
} else {
|
|
122
134
|
newObject.nonEdit = oldObject.nonEdit;
|
|
123
135
|
}
|
|
124
|
-
|
|
125
136
|
} else if (newObject.nonEdit) {
|
|
126
137
|
oldObject.nonEdit = deepClone(newObject.nonEdit);
|
|
127
138
|
if (newObject.nonEdit.password) {
|
|
@@ -149,6 +160,7 @@ function checkNonEditable(oldObject, newObject) {
|
|
|
149
160
|
/**
|
|
150
161
|
* @param {string} repoVersion
|
|
151
162
|
* @param {string} installedVersion
|
|
163
|
+
* @throws {Error} if version is invalid
|
|
152
164
|
*/
|
|
153
165
|
function upToDate(repoVersion, installedVersion) {
|
|
154
166
|
// Check if the installed version is at least the repo version
|
|
@@ -178,22 +190,49 @@ function decryptPhrase(password, data, callback) {
|
|
|
178
190
|
decipher.write(data, 'hex');
|
|
179
191
|
decipher.end();
|
|
180
192
|
} catch (e) {
|
|
181
|
-
console.error(
|
|
193
|
+
console.error(`Cannot decode secret: ${e.message}`);
|
|
182
194
|
callback(null);
|
|
183
195
|
}
|
|
184
196
|
}
|
|
185
197
|
|
|
186
|
-
|
|
187
|
-
|
|
198
|
+
/**
|
|
199
|
+
* Checks if multiple host objects exists, without using object views
|
|
200
|
+
*
|
|
201
|
+
* @param {object} objects the objects db
|
|
202
|
+
* @return {Promise<boolean>} true if only one host object exists
|
|
203
|
+
*/
|
|
204
|
+
async function isSingleHost(objects) {
|
|
205
|
+
const res = await objects.getObjectList({ startkey: 'system.host.', endkey: 'system.host.\u9999' });
|
|
206
|
+
const hostObjs = res.rows.filter(obj => obj.value && obj.value.type === 'host');
|
|
207
|
+
return hostObjs.length <= 1; // on setup no host object is there yet
|
|
208
|
+
}
|
|
188
209
|
|
|
189
|
-
|
|
190
|
-
|
|
191
|
-
|
|
210
|
+
/**
|
|
211
|
+
* Checks if at least one host is running in a MH environment
|
|
212
|
+
*
|
|
213
|
+
* @param {object} objects the objects db
|
|
214
|
+
* @param {object} states the states db
|
|
215
|
+
* @return Promise<boolean> true if one or more hosts running else false
|
|
216
|
+
*/
|
|
217
|
+
async function isHostRunning(objects, states) {
|
|
218
|
+
const res = await objects.getObjectViewAsync('system', 'host', { startkey: '', endkey: '\u9999' });
|
|
219
|
+
|
|
220
|
+
for (const hostObj of res.rows) {
|
|
221
|
+
const state = await states.getState(`${hostObj.id}.alive`);
|
|
222
|
+
if (state.val) {
|
|
223
|
+
return true;
|
|
224
|
+
}
|
|
192
225
|
}
|
|
226
|
+
return false;
|
|
227
|
+
}
|
|
193
228
|
|
|
194
|
-
|
|
195
|
-
|
|
229
|
+
function getAppName() {
|
|
230
|
+
if (fs.existsSync(__dirname + '/../../../../packages/controller')) {
|
|
231
|
+
// dev install - GitHub folder is uppercase
|
|
232
|
+
return 'ioBroker';
|
|
233
|
+
}
|
|
196
234
|
|
|
235
|
+
return 'iobroker';
|
|
197
236
|
}
|
|
198
237
|
|
|
199
238
|
function rmdirRecursiveSync(path) {
|
|
@@ -224,9 +263,12 @@ function findIPs() {
|
|
|
224
263
|
try {
|
|
225
264
|
const ifaces = require('os').networkInterfaces();
|
|
226
265
|
Object.keys(ifaces).forEach(dev =>
|
|
227
|
-
ifaces[dev].forEach(
|
|
228
|
-
|
|
229
|
-
|
|
266
|
+
ifaces[dev].forEach(
|
|
267
|
+
details =>
|
|
268
|
+
// noinspection JSUnresolvedVariable
|
|
269
|
+
!details.internal && ownIpArr.push(details.address)
|
|
270
|
+
)
|
|
271
|
+
);
|
|
230
272
|
} catch (e) {
|
|
231
273
|
console.error(`Can not find local IPs: ${e.message}`);
|
|
232
274
|
}
|
|
@@ -239,12 +281,10 @@ function findPath(path, url) {
|
|
|
239
281
|
if (!url) {
|
|
240
282
|
return '';
|
|
241
283
|
}
|
|
242
|
-
if (url.substring(0, 'http://'.length) === 'http://' ||
|
|
243
|
-
url.substring(0, 'https://'.length) === 'https://') {
|
|
284
|
+
if (url.substring(0, 'http://'.length) === 'http://' || url.substring(0, 'https://'.length) === 'https://') {
|
|
244
285
|
return url;
|
|
245
286
|
} else {
|
|
246
|
-
if (path.substring(0, 'http://'.length) === 'http://' ||
|
|
247
|
-
path.substring(0, 'https://'.length) === 'https://') {
|
|
287
|
+
if (path.substring(0, 'http://'.length) === 'http://' || path.substring(0, 'https://'.length) === 'https://') {
|
|
248
288
|
return (path + url).replace(/\/\//g, '/').replace('http:/', 'http://').replace('https:/', 'https://');
|
|
249
289
|
} else {
|
|
250
290
|
if (url[0] === '/') {
|
|
@@ -257,11 +297,11 @@ function findPath(path, url) {
|
|
|
257
297
|
}
|
|
258
298
|
|
|
259
299
|
function getMac(callback) {
|
|
260
|
-
const macRegex = /(?:[a-z0-9]{2}[:-]){5}[a-z0-9]{2}/
|
|
300
|
+
const macRegex = /(?:[a-z0-9]{2}[:-]){5}[a-z0-9]{2}/gi;
|
|
261
301
|
const zeroRegex = /(?:[0]{2}[:-]){5}[0]{2}/;
|
|
262
|
-
const command =
|
|
302
|
+
const command = process.platform.indexOf('win') === 0 ? 'getmac' : 'ifconfig || ip link';
|
|
263
303
|
|
|
264
|
-
require('child_process').exec(command, {windowsHide: true}, (err, stdout, _stderr) => {
|
|
304
|
+
require('child_process').exec(command, { windowsHide: true }, (err, stdout, _stderr) => {
|
|
265
305
|
if (err) {
|
|
266
306
|
callback(err);
|
|
267
307
|
} else {
|
|
@@ -323,7 +363,7 @@ function uuid(givenMac, callback) {
|
|
|
323
363
|
return callback('55travis-pipe-line-cior-githubaction');
|
|
324
364
|
}
|
|
325
365
|
|
|
326
|
-
let mac = givenMac !== null ?
|
|
366
|
+
let mac = givenMac !== null ? givenMac || '' : null;
|
|
327
367
|
let u;
|
|
328
368
|
|
|
329
369
|
if (!_isDocker && mac === '') {
|
|
@@ -352,7 +392,16 @@ function uuid(givenMac, callback) {
|
|
|
352
392
|
const md5sum = require('crypto').createHash('md5');
|
|
353
393
|
md5sum.update(mac);
|
|
354
394
|
mac = md5sum.digest('hex');
|
|
355
|
-
u =
|
|
395
|
+
u =
|
|
396
|
+
mac.substring(0, 8) +
|
|
397
|
+
'-' +
|
|
398
|
+
mac.substring(8, 12) +
|
|
399
|
+
'-' +
|
|
400
|
+
mac.substring(12, 16) +
|
|
401
|
+
'-' +
|
|
402
|
+
mac.substring(16, 20) +
|
|
403
|
+
'-' +
|
|
404
|
+
mac.substring(20);
|
|
356
405
|
} else {
|
|
357
406
|
// Returns a RFC4122 compliant v4 UUID https://gist.github.com/LeverOne/1308368 (DO WTF YOU WANT TO PUBLIC LICENSE)
|
|
358
407
|
/** @type {any} */
|
|
@@ -360,7 +409,7 @@ function uuid(givenMac, callback) {
|
|
|
360
409
|
let b;
|
|
361
410
|
b = a = '';
|
|
362
411
|
while (a++ < 36) {
|
|
363
|
-
b += (
|
|
412
|
+
b += (a * 51) & 52 ? (a ^ 15 ? 8 ^ (Math.random() * (a ^ 20 ? 16 : 4)) : 4).toString(16) : '-';
|
|
364
413
|
}
|
|
365
414
|
u = b;
|
|
366
415
|
}
|
|
@@ -383,62 +432,75 @@ function updateUuid(newUuid, _objects, callback) {
|
|
|
383
432
|
}
|
|
384
433
|
}
|
|
385
434
|
|
|
386
|
-
_objects.setObject(
|
|
387
|
-
|
|
388
|
-
|
|
389
|
-
|
|
390
|
-
|
|
435
|
+
_objects.setObject(
|
|
436
|
+
'system.meta.uuid',
|
|
437
|
+
{
|
|
438
|
+
type: 'meta',
|
|
439
|
+
common: {
|
|
440
|
+
name: 'uuid',
|
|
441
|
+
type: 'uuid'
|
|
442
|
+
},
|
|
443
|
+
ts: new Date().getTime(),
|
|
444
|
+
from: 'system.host.' + getHostName() + '.tools',
|
|
445
|
+
native: {
|
|
446
|
+
uuid: _uuid
|
|
447
|
+
}
|
|
391
448
|
},
|
|
392
|
-
|
|
393
|
-
|
|
394
|
-
|
|
395
|
-
|
|
396
|
-
|
|
397
|
-
|
|
398
|
-
|
|
399
|
-
|
|
400
|
-
|
|
401
|
-
|
|
402
|
-
|
|
403
|
-
|
|
404
|
-
|
|
405
|
-
|
|
406
|
-
console.log('object system.meta.uuid created: ' + _uuid);
|
|
407
|
-
}
|
|
408
|
-
|
|
409
|
-
callback(_uuid);
|
|
410
|
-
});
|
|
449
|
+
err => {
|
|
450
|
+
if (err) {
|
|
451
|
+
console.error('object system.meta.uuid cannot be updated: ' + err);
|
|
452
|
+
callback();
|
|
453
|
+
} else {
|
|
454
|
+
_objects.getObject('system.meta.uuid', (err, obj) => {
|
|
455
|
+
if (obj.native.uuid !== _uuid) {
|
|
456
|
+
console.error('object system.meta.uuid cannot be updated: write protected');
|
|
457
|
+
} else {
|
|
458
|
+
console.log('object system.meta.uuid created: ' + _uuid);
|
|
459
|
+
}
|
|
460
|
+
callback(_uuid);
|
|
461
|
+
});
|
|
462
|
+
}
|
|
411
463
|
}
|
|
412
|
-
|
|
464
|
+
);
|
|
413
465
|
});
|
|
414
466
|
}
|
|
415
467
|
|
|
416
|
-
|
|
468
|
+
/**
|
|
469
|
+
* Generates a new uuid if non existing
|
|
470
|
+
*
|
|
471
|
+
* @param {object} objects - objects DB
|
|
472
|
+
* @return {Promise<void|string>} uuid if successfully created/updated
|
|
473
|
+
*/
|
|
474
|
+
async function createUuid(objects) {
|
|
417
475
|
const promiseCheckPassword = new Promise(resolve =>
|
|
418
|
-
|
|
476
|
+
objects.getObject('system.user.admin', (err, obj) => {
|
|
419
477
|
if (err || !obj) {
|
|
420
478
|
password = password || require('./password');
|
|
421
479
|
|
|
422
480
|
// Default Password for user 'admin' is application name in lower case
|
|
423
|
-
password(
|
|
481
|
+
password(module.exports.appName).hash(null, null, (err, res) => {
|
|
424
482
|
err && console.error(err);
|
|
425
483
|
|
|
426
484
|
// Create user here and not in io-package.js because of hash password
|
|
427
|
-
|
|
428
|
-
|
|
429
|
-
|
|
430
|
-
|
|
431
|
-
|
|
432
|
-
|
|
433
|
-
|
|
485
|
+
objects.setObject(
|
|
486
|
+
'system.user.admin',
|
|
487
|
+
{
|
|
488
|
+
type: 'user',
|
|
489
|
+
common: {
|
|
490
|
+
name: 'admin',
|
|
491
|
+
password: res,
|
|
492
|
+
dontDelete: true,
|
|
493
|
+
enabled: true
|
|
494
|
+
},
|
|
495
|
+
ts: new Date().getTime(),
|
|
496
|
+
from: `system.host.${getHostName()}.tools`,
|
|
497
|
+
native: {}
|
|
434
498
|
},
|
|
435
|
-
|
|
436
|
-
|
|
437
|
-
|
|
438
|
-
|
|
439
|
-
|
|
440
|
-
resolve();
|
|
441
|
-
});
|
|
499
|
+
() => {
|
|
500
|
+
console.log('object system.user.admin created');
|
|
501
|
+
resolve();
|
|
502
|
+
}
|
|
503
|
+
);
|
|
442
504
|
});
|
|
443
505
|
} else {
|
|
444
506
|
resolve();
|
|
@@ -446,7 +508,7 @@ function createUuid(_objects, callback) {
|
|
|
446
508
|
})
|
|
447
509
|
);
|
|
448
510
|
const promiseCheckUuid = new Promise(resolve =>
|
|
449
|
-
|
|
511
|
+
objects.getObject('system.meta.uuid', (err, obj) => {
|
|
450
512
|
if (!err && obj && obj.native && obj.native.uuid) {
|
|
451
513
|
const PROBLEM_UUIDS = [
|
|
452
514
|
'ab265f4a-67f9-a46a-c0b2-61e4b95cefe5',
|
|
@@ -460,10 +522,10 @@ function createUuid(_objects, callback) {
|
|
|
460
522
|
// if COMMON invalid docker uuid
|
|
461
523
|
if (PROBLEM_UUIDS.includes(obj.native.uuid)) {
|
|
462
524
|
// Read vis license
|
|
463
|
-
|
|
525
|
+
objects.getObject('system.adapter.vis.0', (err, licObj) => {
|
|
464
526
|
if (!licObj || !licObj.native || !licObj.native.license) {
|
|
465
527
|
// generate new UUID
|
|
466
|
-
updateUuid('',
|
|
528
|
+
updateUuid('', objects, _uuid => resolve(_uuid));
|
|
467
529
|
} else {
|
|
468
530
|
// decode obj.native.license
|
|
469
531
|
let data;
|
|
@@ -475,14 +537,18 @@ function createUuid(_objects, callback) {
|
|
|
475
537
|
|
|
476
538
|
if (!data || !data.uuid) {
|
|
477
539
|
// generate new UUID
|
|
478
|
-
updateUuid('',
|
|
540
|
+
updateUuid('', objects, __uuid => resolve(__uuid));
|
|
479
541
|
} else {
|
|
480
542
|
if (data.uuid !== obj.native.uuid) {
|
|
481
|
-
updateUuid(data.correct ? data.uuid : '',
|
|
543
|
+
updateUuid(data.correct ? data.uuid : '', objects, _uuid => resolve(_uuid));
|
|
482
544
|
} else {
|
|
483
545
|
// Show error
|
|
484
|
-
console.warn(
|
|
485
|
-
|
|
546
|
+
console.warn(
|
|
547
|
+
`Your iobroker.vis license must be updated. Please contact info@iobroker.net to get a new license!`
|
|
548
|
+
);
|
|
549
|
+
console.warn(
|
|
550
|
+
`Provide following information in email: ${data.email}, invoice: ${data.invoice}`
|
|
551
|
+
);
|
|
486
552
|
resolve();
|
|
487
553
|
}
|
|
488
554
|
}
|
|
@@ -493,13 +559,13 @@ function createUuid(_objects, callback) {
|
|
|
493
559
|
}
|
|
494
560
|
} else {
|
|
495
561
|
// generate new UUID
|
|
496
|
-
updateUuid('',
|
|
562
|
+
updateUuid('', objects, _uuid => resolve(_uuid));
|
|
497
563
|
}
|
|
498
564
|
})
|
|
499
565
|
);
|
|
500
566
|
|
|
501
|
-
|
|
502
|
-
|
|
567
|
+
const result = await Promise.all([promiseCheckPassword, promiseCheckUuid]);
|
|
568
|
+
return result[1];
|
|
503
569
|
}
|
|
504
570
|
|
|
505
571
|
// Download file to tmp or return file name directly
|
|
@@ -507,41 +573,41 @@ function getFile(urlOrPath, fileName, callback) {
|
|
|
507
573
|
request = request || require('request');
|
|
508
574
|
|
|
509
575
|
// If object was read
|
|
510
|
-
if (
|
|
511
|
-
urlOrPath.substring(0, '
|
|
512
|
-
|
|
576
|
+
if (
|
|
577
|
+
urlOrPath.substring(0, 'http://'.length) === 'http://' ||
|
|
578
|
+
urlOrPath.substring(0, 'https://'.length) === 'https://'
|
|
579
|
+
) {
|
|
580
|
+
const tmpFile = `${__dirname}/../tmp/${fileName || Math.floor(Math.random() * 0xffffffe) + '.zip'}`;
|
|
513
581
|
// Add some information to user-agent, like chrome, IE and Firefox do
|
|
514
582
|
request({
|
|
515
583
|
url: urlOrPath,
|
|
516
584
|
gzip: true,
|
|
517
|
-
headers: {'User-Agent': `${module.exports.appName}, RND: ${randomID}, N: ${process.version}`}
|
|
518
|
-
})
|
|
519
|
-
|
|
520
|
-
|
|
521
|
-
callback(tmpFile);
|
|
522
|
-
}
|
|
523
|
-
|
|
524
|
-
|
|
525
|
-
|
|
526
|
-
callback(tmpFile);
|
|
527
|
-
}
|
|
528
|
-
});
|
|
585
|
+
headers: { 'User-Agent': `${module.exports.appName}, RND: ${randomID}, N: ${process.version}` }
|
|
586
|
+
})
|
|
587
|
+
.on('error', error => {
|
|
588
|
+
console.log(`Cannot download "${tmpFile}": ${error}`);
|
|
589
|
+
callback && callback(tmpFile);
|
|
590
|
+
})
|
|
591
|
+
.pipe(fs.createWriteStream(tmpFile))
|
|
592
|
+
.on('close', () => {
|
|
593
|
+
console.log('downloaded ' + tmpFile);
|
|
594
|
+
callback && callback(tmpFile);
|
|
595
|
+
});
|
|
529
596
|
} else {
|
|
530
|
-
|
|
531
|
-
if (
|
|
532
|
-
callback(urlOrPath);
|
|
533
|
-
}
|
|
534
|
-
|
|
535
|
-
if (
|
|
536
|
-
callback(__dirname
|
|
537
|
-
}
|
|
538
|
-
|
|
539
|
-
|
|
540
|
-
callback(__dirname + '/../tmp/' + urlOrPath);
|
|
597
|
+
try {
|
|
598
|
+
if (fs.existsSync(urlOrPath)) {
|
|
599
|
+
callback && callback(urlOrPath);
|
|
600
|
+
} else if (fs.existsSync(`${__dirname}/../${urlOrPath}`)) {
|
|
601
|
+
callback && callback(`${__dirname}/../${urlOrPath}`);
|
|
602
|
+
} else if (fs.existsSync(`${__dirname}/../tmp/${urlOrPath}`)) {
|
|
603
|
+
callback && callback(`${__dirname}/../tmp/${urlOrPath}`);
|
|
604
|
+
} else {
|
|
605
|
+
console.log('File not found: ' + urlOrPath);
|
|
606
|
+
process.exit(EXIT_CODES.FILE_NOT_FOUND);
|
|
541
607
|
}
|
|
542
|
-
}
|
|
543
|
-
console.log(
|
|
544
|
-
process.exit(
|
|
608
|
+
} catch (err) {
|
|
609
|
+
console.log(`File "${urlOrPath}" could no be read: ${err.message}`);
|
|
610
|
+
process.exit(EXIT_CODES.FILE_NOT_FOUND);
|
|
545
611
|
}
|
|
546
612
|
}
|
|
547
613
|
}
|
|
@@ -567,35 +633,40 @@ function getJson(urlOrPath, agent, callback) {
|
|
|
567
633
|
callback(null);
|
|
568
634
|
}
|
|
569
635
|
} else {
|
|
570
|
-
if (
|
|
571
|
-
urlOrPath.substring(0, '
|
|
572
|
-
|
|
573
|
-
|
|
574
|
-
|
|
575
|
-
|
|
576
|
-
|
|
577
|
-
|
|
578
|
-
|
|
579
|
-
|
|
580
|
-
|
|
581
|
-
|
|
636
|
+
if (
|
|
637
|
+
urlOrPath.substring(0, 'http://'.length) === 'http://' ||
|
|
638
|
+
urlOrPath.substring(0, 'https://'.length) === 'https://'
|
|
639
|
+
) {
|
|
640
|
+
request(
|
|
641
|
+
{
|
|
642
|
+
url: urlOrPath,
|
|
643
|
+
timeout: 10000,
|
|
644
|
+
gzip: true,
|
|
645
|
+
headers: { 'User-Agent': agent }
|
|
646
|
+
},
|
|
647
|
+
(error, response, body) => {
|
|
648
|
+
if (error || !body || response.statusCode !== 200) {
|
|
649
|
+
console.warn(`Cannot download json from ${urlOrPath}. Error: ${error || body}`);
|
|
650
|
+
if (callback) {
|
|
651
|
+
callback(null, urlOrPath);
|
|
652
|
+
}
|
|
653
|
+
return;
|
|
582
654
|
}
|
|
583
|
-
|
|
584
|
-
|
|
585
|
-
|
|
586
|
-
|
|
587
|
-
|
|
588
|
-
|
|
589
|
-
|
|
590
|
-
|
|
655
|
+
try {
|
|
656
|
+
sources = JSON.parse(body);
|
|
657
|
+
} catch {
|
|
658
|
+
console.error('Json file is invalid on ' + urlOrPath);
|
|
659
|
+
if (callback) {
|
|
660
|
+
callback(null, urlOrPath);
|
|
661
|
+
}
|
|
662
|
+
return;
|
|
591
663
|
}
|
|
592
|
-
return;
|
|
593
|
-
}
|
|
594
664
|
|
|
595
|
-
|
|
596
|
-
|
|
665
|
+
if (callback) {
|
|
666
|
+
callback(sources, urlOrPath);
|
|
667
|
+
}
|
|
597
668
|
}
|
|
598
|
-
|
|
669
|
+
).on('error', _error => {
|
|
599
670
|
//console.log('Cannot download json from ' + urlOrPath + '. Error: ' + error);
|
|
600
671
|
//if (callback) callback(null, urlOrPath);
|
|
601
672
|
});
|
|
@@ -617,7 +688,9 @@ function getJson(urlOrPath, agent, callback) {
|
|
|
617
688
|
try {
|
|
618
689
|
sources = fs.readJSONSync(__dirname + '/../' + urlOrPath);
|
|
619
690
|
} catch (e) {
|
|
620
|
-
console.log(
|
|
691
|
+
console.log(
|
|
692
|
+
'Cannot parse json file from ' + __dirname + '/../' + urlOrPath + '. Error: ' + e.message
|
|
693
|
+
);
|
|
621
694
|
if (callback) {
|
|
622
695
|
callback(null, urlOrPath);
|
|
623
696
|
}
|
|
@@ -630,7 +703,9 @@ function getJson(urlOrPath, agent, callback) {
|
|
|
630
703
|
try {
|
|
631
704
|
sources = fs.readJSONSync(__dirname + '/../tmp/' + urlOrPath);
|
|
632
705
|
} catch (e) {
|
|
633
|
-
console.log(
|
|
706
|
+
console.log(
|
|
707
|
+
'Cannot parse json file from ' + __dirname + '/../tmp/' + urlOrPath + '. Error: ' + e.message
|
|
708
|
+
);
|
|
634
709
|
if (callback) {
|
|
635
710
|
callback(null, urlOrPath);
|
|
636
711
|
}
|
|
@@ -649,6 +724,69 @@ function getJson(urlOrPath, agent, callback) {
|
|
|
649
724
|
}
|
|
650
725
|
}
|
|
651
726
|
|
|
727
|
+
/**
|
|
728
|
+
* Return content of the json file. Download it or read directly
|
|
729
|
+
* @param {string|object} urlOrPath URL where the json file could be found
|
|
730
|
+
* @param {string} agent optional agent identifier like "Windows Chrome 12.56"
|
|
731
|
+
* @returns {object} json object
|
|
732
|
+
*/
|
|
733
|
+
async function getJsonAsync(urlOrPath, agent) {
|
|
734
|
+
agent = agent || '';
|
|
735
|
+
|
|
736
|
+
axios = axios || require('axios');
|
|
737
|
+
let sources = {};
|
|
738
|
+
// If object was read
|
|
739
|
+
if (urlOrPath && typeof urlOrPath === 'object') {
|
|
740
|
+
return urlOrPath;
|
|
741
|
+
} else if (!urlOrPath) {
|
|
742
|
+
console.log('Empty url!');
|
|
743
|
+
return null;
|
|
744
|
+
} else {
|
|
745
|
+
if (urlOrPath.startsWith('http://') || urlOrPath.startsWith('https://')) {
|
|
746
|
+
try {
|
|
747
|
+
const result = await axios(urlOrPath, {
|
|
748
|
+
timeout: 10000,
|
|
749
|
+
headers: { 'User-Agent': agent },
|
|
750
|
+
validateStatus: status => status !== 200
|
|
751
|
+
});
|
|
752
|
+
return result.data;
|
|
753
|
+
} catch (error) {
|
|
754
|
+
console.warn(`Cannot download json from ${urlOrPath}. Error: ${error}`);
|
|
755
|
+
return null;
|
|
756
|
+
}
|
|
757
|
+
} else {
|
|
758
|
+
if (fs.existsSync(urlOrPath)) {
|
|
759
|
+
try {
|
|
760
|
+
sources = fs.readJSONSync(urlOrPath);
|
|
761
|
+
} catch (e) {
|
|
762
|
+
console.warn(`Cannot parse json file from ${urlOrPath}. Error: ${e.message}`);
|
|
763
|
+
return null;
|
|
764
|
+
}
|
|
765
|
+
return sources;
|
|
766
|
+
} else if (fs.existsSync(__dirname + '/../' + urlOrPath)) {
|
|
767
|
+
try {
|
|
768
|
+
sources = fs.readJSONSync(`${__dirname}/../${urlOrPath}`);
|
|
769
|
+
} catch (e) {
|
|
770
|
+
console.warn(`Cannot parse json file from ${__dirname}/../${urlOrPath}. Error: ${e.message}`);
|
|
771
|
+
return null;
|
|
772
|
+
}
|
|
773
|
+
return sources;
|
|
774
|
+
} else if (fs.existsSync(`${__dirname}/../tmp/${urlOrPath}`)) {
|
|
775
|
+
try {
|
|
776
|
+
sources = fs.readJSONSync(`${__dirname}/../tmp/${urlOrPath}`);
|
|
777
|
+
} catch (e) {
|
|
778
|
+
console.log(`Cannot parse json file from ${__dirname}/../tmp/${urlOrPath}. Error: ${e.message}`);
|
|
779
|
+
return null;
|
|
780
|
+
}
|
|
781
|
+
return sources;
|
|
782
|
+
} else {
|
|
783
|
+
//if (urlOrPath.indexOf('/example/') === -1) console.log('Json file not found: ' + urlOrPath);
|
|
784
|
+
return null;
|
|
785
|
+
}
|
|
786
|
+
}
|
|
787
|
+
}
|
|
788
|
+
}
|
|
789
|
+
|
|
652
790
|
function scanDirectory(dirName, list, regExp) {
|
|
653
791
|
if (fs.existsSync(dirName)) {
|
|
654
792
|
let dirs;
|
|
@@ -665,10 +803,10 @@ function scanDirectory(dirName, list, regExp) {
|
|
|
665
803
|
const fileName = path.join(fullPath, 'package.json');
|
|
666
804
|
if (regExp.test(dirs[i]) && fs.existsSync(fileIoName)) {
|
|
667
805
|
const ioPackage = fs.readJSONSync(fileIoName);
|
|
668
|
-
const package_ = fs.existsSync(fileName)
|
|
669
|
-
|
|
670
|
-
|
|
671
|
-
|
|
806
|
+
const package_ = fs.existsSync(fileName) ? fs.readJSONSync(fileName) : {};
|
|
807
|
+
const localIcon = ioPackage.common.icon
|
|
808
|
+
? `/adapter/${dirs[i].substring(module.exports.appName.length + 1)}/${ioPackage.common.icon}`
|
|
809
|
+
: '';
|
|
672
810
|
//noinspection JSUnresolvedVariable
|
|
673
811
|
list[ioPackage.common.name] = {
|
|
674
812
|
controller: ioPackage.common.controller || false,
|
|
@@ -682,12 +820,18 @@ function scanDirectory(dirName, list, regExp) {
|
|
|
682
820
|
keywords: ioPackage.common.keywords,
|
|
683
821
|
readme: ioPackage.common.readme,
|
|
684
822
|
type: ioPackage.common.type,
|
|
685
|
-
license: ioPackage.common.license
|
|
686
|
-
|
|
823
|
+
license: ioPackage.common.license
|
|
824
|
+
? ioPackage.common.license
|
|
825
|
+
: package_.licenses && package_.licenses.length
|
|
826
|
+
? package_.licenses[0].type
|
|
827
|
+
: '',
|
|
828
|
+
licenseUrl: package_.licenses && package_.licenses.length ? package_.licenses[0].url : ''
|
|
687
829
|
};
|
|
688
830
|
}
|
|
689
831
|
} catch (e) {
|
|
690
|
-
console.log(
|
|
832
|
+
console.log(
|
|
833
|
+
`Cannot read or parse ${__dirname}/../node_modules/${dirs[i]}/io-package.json: ${e.message}`
|
|
834
|
+
);
|
|
691
835
|
}
|
|
692
836
|
}
|
|
693
837
|
}
|
|
@@ -709,7 +853,9 @@ function getInstalledInfo(hostRunningVersion) {
|
|
|
709
853
|
} catch (e) {
|
|
710
854
|
console.error(`Cannot get installed host information: ${e.message}`);
|
|
711
855
|
}
|
|
712
|
-
const package_ = fs.existsSync(path.join(fullPath, 'package.json'))
|
|
856
|
+
const package_ = fs.existsSync(path.join(fullPath, 'package.json'))
|
|
857
|
+
? fs.readJSONSync(path.join(fullPath, 'package.json'))
|
|
858
|
+
: {};
|
|
713
859
|
const regExp = new RegExp(`^${module.exports.appName}\\.`, 'i');
|
|
714
860
|
|
|
715
861
|
if (ioPackage) {
|
|
@@ -724,20 +870,19 @@ function getInstalledInfo(hostRunningVersion) {
|
|
|
724
870
|
keywords: ioPackage.common.keywords,
|
|
725
871
|
readme: ioPackage.common.readme,
|
|
726
872
|
runningVersion: hostRunningVersion,
|
|
727
|
-
license: ioPackage.common.license
|
|
728
|
-
|
|
873
|
+
license: ioPackage.common.license
|
|
874
|
+
? ioPackage.common.license
|
|
875
|
+
: package_.licenses && package_.licenses.length
|
|
876
|
+
? package_.licenses[0].type
|
|
877
|
+
: '',
|
|
878
|
+
licenseUrl: package_.licenses && package_.licenses.length ? package_.licenses[0].url : ''
|
|
729
879
|
};
|
|
730
880
|
}
|
|
731
881
|
|
|
732
|
-
|
|
733
|
-
scanDirectory(path.join(
|
|
882
|
+
// we scan the sub node modules of controller and same hierarchy as controller
|
|
883
|
+
scanDirectory(path.join(fullPath, 'node_modules'), result, regExp);
|
|
884
|
+
scanDirectory(path.join(fullPath, '..'), result, regExp);
|
|
734
885
|
|
|
735
|
-
if (
|
|
736
|
-
fs.existsSync(path.join(__dirname, `../../../node_modules/${module.exports.appName.toLowerCase()}.js-controller`)) ||
|
|
737
|
-
fs.existsSync(path.join(__dirname, `../../../node_modules/${module.exports.appName}.js-controller`))
|
|
738
|
-
) {
|
|
739
|
-
scanDirectory(path.join(__dirname, '../..'), result, regExp);
|
|
740
|
-
}
|
|
741
886
|
return result;
|
|
742
887
|
}
|
|
743
888
|
|
|
@@ -753,7 +898,7 @@ function getNpmVersion(adapter, callback) {
|
|
|
753
898
|
const cliCommand = `npm view ${adapter}@latest version`;
|
|
754
899
|
|
|
755
900
|
const exec = require('child_process').exec;
|
|
756
|
-
exec(cliCommand, {timeout: 2000, windowsHide: true}, (error, stdout, _stderr) => {
|
|
901
|
+
exec(cliCommand, { timeout: 2000, windowsHide: true }, (error, stdout, _stderr) => {
|
|
757
902
|
let version;
|
|
758
903
|
if (error) {
|
|
759
904
|
// command failed
|
|
@@ -832,8 +977,10 @@ function getIoPack(sources, name, callback) {
|
|
|
832
977
|
callback(sources, name);
|
|
833
978
|
}
|
|
834
979
|
} else {
|
|
835
|
-
if (
|
|
836
|
-
sources[name].meta.substring(0, '
|
|
980
|
+
if (
|
|
981
|
+
sources[name].meta.substring(0, 'http://'.length) === 'http://' ||
|
|
982
|
+
sources[name].meta.substring(0, 'https://'.length) === 'https://'
|
|
983
|
+
) {
|
|
837
984
|
//installed from npm
|
|
838
985
|
getNpmVersion(name, (_err, version) => {
|
|
839
986
|
if (version) {
|
|
@@ -867,7 +1014,7 @@ function _getRepositoryFile(sources, path, callback) {
|
|
|
867
1014
|
}
|
|
868
1015
|
count++;
|
|
869
1016
|
}
|
|
870
|
-
sources._helper = {failCounter: []};
|
|
1017
|
+
sources._helper = { failCounter: [] };
|
|
871
1018
|
|
|
872
1019
|
sources._helper.timeout = setTimeout(() => {
|
|
873
1020
|
if (sources._helper) {
|
|
@@ -918,8 +1065,7 @@ function _getRepositoryFile(sources, path, callback) {
|
|
|
918
1065
|
callback = null;
|
|
919
1066
|
} else {
|
|
920
1067
|
// process next
|
|
921
|
-
setImmediate(() =>
|
|
922
|
-
_getRepositoryFile(sources, path, callback));
|
|
1068
|
+
setImmediate(() => _getRepositoryFile(sources, path, callback));
|
|
923
1069
|
}
|
|
924
1070
|
}
|
|
925
1071
|
});
|
|
@@ -954,7 +1100,7 @@ function _checkRepositoryFileHash(urlOrPath, additionalInfo, callback) {
|
|
|
954
1100
|
if (urlOrPath.startsWith('http://') || urlOrPath.startsWith('https://')) {
|
|
955
1101
|
urlOrPath = urlOrPath.replace(/\.json$/, '-hash.json');
|
|
956
1102
|
let json = null;
|
|
957
|
-
request({url: urlOrPath, timeout: 10000, gzip: true}, (error, response, body) => {
|
|
1103
|
+
request({ url: urlOrPath, timeout: 10000, gzip: true }, (error, response, body) => {
|
|
958
1104
|
if (error || !body || response.statusCode !== 200) {
|
|
959
1105
|
console.warn(`Cannot download json from ${urlOrPath}. Error: ${error || body}`);
|
|
960
1106
|
} else {
|
|
@@ -1055,7 +1201,9 @@ function getRepositoryFile(urlOrPath, additionalInfo, callback) {
|
|
|
1055
1201
|
let agent = '';
|
|
1056
1202
|
if (additionalInfo) {
|
|
1057
1203
|
// Add some information to user-agent, like chrome, IE and Firefox do
|
|
1058
|
-
agent = `${additionalInfo.name}, RND: ${additionalInfo.randomID || randomID}, Node:${
|
|
1204
|
+
agent = `${additionalInfo.name}, RND: ${additionalInfo.randomID || randomID}, Node:${
|
|
1205
|
+
additionalInfo.node
|
|
1206
|
+
}, V:${additionalInfo.controller}`;
|
|
1059
1207
|
}
|
|
1060
1208
|
|
|
1061
1209
|
// load hash of file first to not load the whole 1MB of sources
|
|
@@ -1075,11 +1223,21 @@ function getRepositoryFile(urlOrPath, additionalInfo, callback) {
|
|
|
1075
1223
|
_getRepositoryFile(sources, path, err => {
|
|
1076
1224
|
err && console.error(`[${new Date()}] ${err}`);
|
|
1077
1225
|
typeof callback === 'function' && callback(err, sources, actualSourcesHash);
|
|
1078
|
-
})
|
|
1226
|
+
})
|
|
1227
|
+
);
|
|
1079
1228
|
} else {
|
|
1080
1229
|
// return cached sources, because no sources found
|
|
1081
|
-
console.log(
|
|
1082
|
-
|
|
1230
|
+
console.log(
|
|
1231
|
+
`failed to download new sources, ${
|
|
1232
|
+
additionalInfo.sources ? 'use cached sources' : 'no cached sources available'
|
|
1233
|
+
}`
|
|
1234
|
+
);
|
|
1235
|
+
return maybeCallbackWithError(
|
|
1236
|
+
callback,
|
|
1237
|
+
`Cannot read "${urlOrPath}"`,
|
|
1238
|
+
additionalInfo.sources,
|
|
1239
|
+
''
|
|
1240
|
+
);
|
|
1083
1241
|
}
|
|
1084
1242
|
});
|
|
1085
1243
|
}
|
|
@@ -1102,7 +1260,7 @@ async function getRepositoryFileAsync(url, hash, force, _actualRepo) {
|
|
|
1102
1260
|
let _hash;
|
|
1103
1261
|
if (_actualRepo && !force && hash && (url.startsWith('http://') || url.startsWith('https://'))) {
|
|
1104
1262
|
axios = axios || require('axios');
|
|
1105
|
-
_hash = await axios({url: url.replace(/\.json$/, '-hash.json'), timeout: 10000});
|
|
1263
|
+
_hash = await axios({ url: url.replace(/\.json$/, '-hash.json'), timeout: 10000 });
|
|
1106
1264
|
if (_hash && _hash.data && hash === _hash.data.hash) {
|
|
1107
1265
|
return _actualRepo;
|
|
1108
1266
|
}
|
|
@@ -1113,13 +1271,15 @@ async function getRepositoryFileAsync(url, hash, force, _actualRepo) {
|
|
|
1113
1271
|
if (url.startsWith('http://') || url.startsWith('https://')) {
|
|
1114
1272
|
axios = axios || require('axios');
|
|
1115
1273
|
if (!_hash) {
|
|
1116
|
-
_hash = await axios({url: url.replace(/\.json$/, '-hash.json'), timeout: 10000});
|
|
1274
|
+
_hash = await axios({ url: url.replace(/\.json$/, '-hash.json'), timeout: 10000 });
|
|
1117
1275
|
}
|
|
1118
1276
|
|
|
1119
1277
|
if (_actualRepo && hash && _hash && _hash.data && _hash.data.hash === hash) {
|
|
1120
1278
|
data = _actualRepo;
|
|
1121
1279
|
} else {
|
|
1122
|
-
const agent = `${module.exports.appName}, RND: ${randomID}, Node:${process.version}, V:${
|
|
1280
|
+
const agent = `${module.exports.appName}, RND: ${randomID}, Node:${process.version}, V:${
|
|
1281
|
+
require('../../package.json').version
|
|
1282
|
+
}`;
|
|
1123
1283
|
data = await axios({
|
|
1124
1284
|
url,
|
|
1125
1285
|
timeout: 10000,
|
|
@@ -1139,27 +1299,35 @@ async function getRepositoryFileAsync(url, hash, force, _actualRepo) {
|
|
|
1139
1299
|
}
|
|
1140
1300
|
}
|
|
1141
1301
|
|
|
1142
|
-
return {
|
|
1302
|
+
return {
|
|
1303
|
+
json: data,
|
|
1304
|
+
changed: _hash && _hash.data ? hash !== _hash.data.hash : true,
|
|
1305
|
+
hash: _hash && _hash.data ? _hash.data.hash : ''
|
|
1306
|
+
};
|
|
1143
1307
|
}
|
|
1144
1308
|
|
|
1145
|
-
|
|
1146
|
-
|
|
1147
|
-
|
|
1148
|
-
|
|
1309
|
+
/**
|
|
1310
|
+
* Sends the given object to the diagnosis server
|
|
1311
|
+
*
|
|
1312
|
+
* @param {object} obj - diagnosis object
|
|
1313
|
+
* @return {Promise<void>}
|
|
1314
|
+
*/
|
|
1315
|
+
async function sendDiagInfo(obj) {
|
|
1316
|
+
const objStr = JSON.stringify(obj);
|
|
1317
|
+
console.log(`Send diag info: ${objStr}`);
|
|
1149
1318
|
axios = axios || require('axios');
|
|
1150
1319
|
const params = new URLSearchParams();
|
|
1151
|
-
params.append('data',
|
|
1320
|
+
params.append('data', objStr);
|
|
1152
1321
|
const config = {
|
|
1153
|
-
headers: {'Content-Type': 'application/x-www-form-urlencoded'},
|
|
1322
|
+
headers: { 'Content-Type': 'application/x-www-form-urlencoded' },
|
|
1154
1323
|
timeout: 4000
|
|
1155
1324
|
};
|
|
1156
1325
|
|
|
1157
|
-
|
|
1158
|
-
.
|
|
1159
|
-
|
|
1160
|
-
|
|
1161
|
-
|
|
1162
|
-
});
|
|
1326
|
+
try {
|
|
1327
|
+
await axios.post(`http://download.${module.exports.appName}.net/diag.php`, params, config);
|
|
1328
|
+
} catch (e) {
|
|
1329
|
+
console.log(`Cannot send diag info: ${e.message}`);
|
|
1330
|
+
}
|
|
1163
1331
|
}
|
|
1164
1332
|
|
|
1165
1333
|
/**
|
|
@@ -1182,10 +1350,7 @@ function getAdapterDir(adapter) {
|
|
|
1182
1350
|
adapter = adapter.substr(0, adapter.lastIndexOf('.'));
|
|
1183
1351
|
}
|
|
1184
1352
|
|
|
1185
|
-
const possibilities = [
|
|
1186
|
-
`${appName.toLowerCase()}.${adapter}/package.json`,
|
|
1187
|
-
`${appName}.${adapter}/package.json`
|
|
1188
|
-
];
|
|
1353
|
+
const possibilities = [`${appName.toLowerCase()}.${adapter}/package.json`, `${appName}.${adapter}/package.json`];
|
|
1189
1354
|
|
|
1190
1355
|
/** @type {string} */
|
|
1191
1356
|
let adapterPath;
|
|
@@ -1267,7 +1432,8 @@ function getSystemNpmVersion(callback) {
|
|
|
1267
1432
|
}
|
|
1268
1433
|
}, 10000);
|
|
1269
1434
|
|
|
1270
|
-
exec('npm -v', {encoding: 'utf8', env: newEnv, windowsHide: true}, (error, stdout) => {
|
|
1435
|
+
exec('npm -v', { encoding: 'utf8', env: newEnv, windowsHide: true }, (error, stdout) => {
|
|
1436
|
+
//, stderr) {
|
|
1271
1437
|
if (timeout) {
|
|
1272
1438
|
clearTimeout(timeout);
|
|
1273
1439
|
timeout = null;
|
|
@@ -1301,19 +1467,19 @@ const getSystemNpmVersionAsync = promisify(getSystemNpmVersion);
|
|
|
1301
1467
|
* Installs a node module using npm or a similar package manager
|
|
1302
1468
|
* @param {string} npmUrl Which node module to install
|
|
1303
1469
|
* @param {InstallNodeModuleOptions} options Options for the installation
|
|
1304
|
-
* @returns {Promise<import(
|
|
1470
|
+
* @returns {Promise<import('@alcalzone/pak').CommandResult>}
|
|
1305
1471
|
*/
|
|
1306
1472
|
async function installNodeModule(npmUrl, options = {}) {
|
|
1307
1473
|
// Figure out which package manager is in charge (probably npm at this point)
|
|
1308
1474
|
const pak = await detectPackageManager(
|
|
1309
1475
|
typeof options.cwd === 'string'
|
|
1310
|
-
// If a cwd was provided, use it
|
|
1311
|
-
|
|
1312
|
-
// Otherwise find the ioBroker root dir
|
|
1313
|
-
|
|
1314
|
-
|
|
1315
|
-
|
|
1316
|
-
|
|
1476
|
+
? // If a cwd was provided, use it
|
|
1477
|
+
{ cwd: options.cwd }
|
|
1478
|
+
: // Otherwise find the ioBroker root dir
|
|
1479
|
+
{
|
|
1480
|
+
cwd: __dirname,
|
|
1481
|
+
setCwdToPackageRoot: true
|
|
1482
|
+
}
|
|
1317
1483
|
);
|
|
1318
1484
|
// By default, don't print all the stuff the package manager spits out
|
|
1319
1485
|
if (!options.debug) {
|
|
@@ -1332,7 +1498,7 @@ async function installNodeModule(npmUrl, options = {}) {
|
|
|
1332
1498
|
}
|
|
1333
1499
|
|
|
1334
1500
|
// And install the module
|
|
1335
|
-
/** @type {import(
|
|
1501
|
+
/** @type {import('@alcalzone/pak').InstallOptions} */
|
|
1336
1502
|
const installOpts = {};
|
|
1337
1503
|
if (options.unsafePerm) {
|
|
1338
1504
|
installOpts.additionalArgs = ['--unsafe-perm'];
|
|
@@ -1350,19 +1516,19 @@ async function installNodeModule(npmUrl, options = {}) {
|
|
|
1350
1516
|
* Uninstalls a node module using npm or a similar package manager
|
|
1351
1517
|
* @param {string} packageName Which node module to uninstall
|
|
1352
1518
|
* @param {UninstallNodeModuleOptions} options Options for the installation
|
|
1353
|
-
* @returns {Promise<import(
|
|
1519
|
+
* @returns {Promise<import('@alcalzone/pak').CommandResult>}
|
|
1354
1520
|
*/
|
|
1355
1521
|
async function uninstallNodeModule(packageName, options = {}) {
|
|
1356
1522
|
// Figure out which package manager is in charge (probably npm at this point)
|
|
1357
1523
|
const pak = await detectPackageManager(
|
|
1358
1524
|
typeof options.cwd === 'string'
|
|
1359
|
-
// If a cwd was provided, use it
|
|
1360
|
-
|
|
1361
|
-
// Otherwise find the ioBroker root dir
|
|
1362
|
-
|
|
1363
|
-
|
|
1364
|
-
|
|
1365
|
-
|
|
1525
|
+
? // If a cwd was provided, use it
|
|
1526
|
+
{ cwd: options.cwd }
|
|
1527
|
+
: // Otherwise find the ioBroker root dir
|
|
1528
|
+
{
|
|
1529
|
+
cwd: __dirname,
|
|
1530
|
+
setCwdToPackageRoot: true
|
|
1531
|
+
}
|
|
1366
1532
|
);
|
|
1367
1533
|
// By default, don't print all the stuff the package manager spits out
|
|
1368
1534
|
if (!options.debug) {
|
|
@@ -1393,19 +1559,19 @@ async function uninstallNodeModule(packageName, options = {}) {
|
|
|
1393
1559
|
* Rebuilds all native node_modules that are dependencies of the project in the current working directory / project root.
|
|
1394
1560
|
* If `options.cwd` is given, the directory must contain a lockfile.
|
|
1395
1561
|
* @param {RebuildNodeModulesOptions} options Options for the rebuild
|
|
1396
|
-
* @returns {Promise<import(
|
|
1562
|
+
* @returns {Promise<import('@alcalzone/pak').CommandResult>}
|
|
1397
1563
|
*/
|
|
1398
1564
|
async function rebuildNodeModules(options = {}) {
|
|
1399
1565
|
// Figure out which package manager is in charge (probably npm at this point)
|
|
1400
1566
|
const pak = await detectPackageManager(
|
|
1401
1567
|
typeof options.cwd === 'string'
|
|
1402
|
-
// If a cwd was provided, use it
|
|
1403
|
-
|
|
1404
|
-
// Otherwise find the ioBroker root dir
|
|
1405
|
-
|
|
1406
|
-
|
|
1407
|
-
|
|
1408
|
-
|
|
1568
|
+
? // If a cwd was provided, use it
|
|
1569
|
+
{ cwd: options.cwd }
|
|
1570
|
+
: // Otherwise find the ioBroker root dir
|
|
1571
|
+
{
|
|
1572
|
+
cwd: __dirname,
|
|
1573
|
+
setCwdToPackageRoot: true
|
|
1574
|
+
}
|
|
1409
1575
|
);
|
|
1410
1576
|
// By default, don't print all the stuff the package manager spits out
|
|
1411
1577
|
if (!options.debug) {
|
|
@@ -1445,7 +1611,7 @@ function getDiskInfo(platform, callback) {
|
|
|
1445
1611
|
try {
|
|
1446
1612
|
const path = platform === 'win32' ? __dirname.substring(0, 2) : '/';
|
|
1447
1613
|
const info = diskusage.checkSync(path);
|
|
1448
|
-
return callback && callback(null, {'Disk size': info.total, 'Disk free': info.free});
|
|
1614
|
+
return callback && callback(null, { 'Disk size': info.total, 'Disk free': info.free });
|
|
1449
1615
|
} catch (err) {
|
|
1450
1616
|
console.log(err);
|
|
1451
1617
|
}
|
|
@@ -1461,37 +1627,49 @@ function getDiskInfo(platform, callback) {
|
|
|
1461
1627
|
// Z: 116649795584 148368257024
|
|
1462
1628
|
const disk = __dirname.substring(0, 2).toUpperCase();
|
|
1463
1629
|
|
|
1464
|
-
exec(
|
|
1465
|
-
|
|
1466
|
-
|
|
1467
|
-
|
|
1468
|
-
|
|
1469
|
-
|
|
1470
|
-
|
|
1471
|
-
|
|
1472
|
-
|
|
1473
|
-
|
|
1474
|
-
|
|
1475
|
-
|
|
1476
|
-
|
|
1477
|
-
'Disk size': parseInt(parts[2]),
|
|
1478
|
-
'Disk free': parseInt(parts[1])
|
|
1630
|
+
exec(
|
|
1631
|
+
'wmic logicaldisk get size,freespace,caption',
|
|
1632
|
+
{
|
|
1633
|
+
encoding: 'utf8',
|
|
1634
|
+
windowsHide: true
|
|
1635
|
+
},
|
|
1636
|
+
(error, stdout) => {
|
|
1637
|
+
//, stderr) {
|
|
1638
|
+
if (stdout) {
|
|
1639
|
+
const lines = stdout.split('\n');
|
|
1640
|
+
const line = lines.find(line => {
|
|
1641
|
+
const parts = line.split(/\s+/);
|
|
1642
|
+
return parts[0].toUpperCase() === disk;
|
|
1479
1643
|
});
|
|
1644
|
+
if (line) {
|
|
1645
|
+
const parts = line.split(/\s+/);
|
|
1646
|
+
return (
|
|
1647
|
+
callback &&
|
|
1648
|
+
callback(error, {
|
|
1649
|
+
'Disk size': parseInt(parts[2]),
|
|
1650
|
+
'Disk free': parseInt(parts[1])
|
|
1651
|
+
})
|
|
1652
|
+
);
|
|
1653
|
+
}
|
|
1480
1654
|
}
|
|
1655
|
+
callback && callback(error, null);
|
|
1481
1656
|
}
|
|
1482
|
-
|
|
1483
|
-
});
|
|
1657
|
+
);
|
|
1484
1658
|
} else {
|
|
1485
|
-
exec('df -k /', {encoding: 'utf8', windowsHide: true}, (error, stdout) => {
|
|
1659
|
+
exec('df -k /', { encoding: 'utf8', windowsHide: true }, (error, stdout) => {
|
|
1660
|
+
//, stderr) {
|
|
1486
1661
|
// Filesystem 1K-blocks Used Available Use% Mounted on
|
|
1487
1662
|
// /dev/mapper/vg00-lv01 162544556 9966192 145767152 7% /
|
|
1488
1663
|
try {
|
|
1489
1664
|
if (stdout) {
|
|
1490
1665
|
const parts = stdout.split('\n')[1].split(/\s+/);
|
|
1491
|
-
return
|
|
1492
|
-
|
|
1493
|
-
|
|
1494
|
-
|
|
1666
|
+
return (
|
|
1667
|
+
callback &&
|
|
1668
|
+
callback(error, {
|
|
1669
|
+
'Disk size': parseInt(parts[1]) * 1024,
|
|
1670
|
+
'Disk free': parseInt(parts[3]) * 1024
|
|
1671
|
+
})
|
|
1672
|
+
);
|
|
1495
1673
|
}
|
|
1496
1674
|
} catch {
|
|
1497
1675
|
// continue regardless of error
|
|
@@ -1596,7 +1774,7 @@ function generateDefaultCertificates() {
|
|
|
1596
1774
|
// https://github.com/digitalbazaar/forge
|
|
1597
1775
|
forge.options.usePureJavaScript = false;
|
|
1598
1776
|
const pki = forge.pki;
|
|
1599
|
-
const keys = pki.rsa.generateKeyPair({bits: 2048, e: 0x10001});
|
|
1777
|
+
const keys = pki.rsa.generateKeyPair({ bits: 2048, e: 0x10001 });
|
|
1600
1778
|
const cert = pki.createCertificate();
|
|
1601
1779
|
|
|
1602
1780
|
cert.publicKey = keys.publicKey;
|
|
@@ -1606,15 +1784,15 @@ function generateDefaultCertificates() {
|
|
|
1606
1784
|
cert.validity.notAfter.setFullYear(cert.validity.notBefore.getFullYear() + 1);
|
|
1607
1785
|
|
|
1608
1786
|
const subAttrs = [
|
|
1609
|
-
{name: 'commonName', value: getHostName()},
|
|
1610
|
-
{name: 'organizationName', value: 'ioBroker GmbH'},
|
|
1611
|
-
{shortName: 'OU', value: 'iobroker'}
|
|
1787
|
+
{ name: 'commonName', value: getHostName() },
|
|
1788
|
+
{ name: 'organizationName', value: 'ioBroker GmbH' },
|
|
1789
|
+
{ shortName: 'OU', value: 'iobroker' }
|
|
1612
1790
|
];
|
|
1613
1791
|
|
|
1614
1792
|
const issAttrs = [
|
|
1615
|
-
{name: 'commonName', value: 'iobroker'},
|
|
1616
|
-
{name: 'organizationName', value: 'ioBroker GmbH'},
|
|
1617
|
-
{shortName: 'OU', value: 'iobroker'}
|
|
1793
|
+
{ name: 'commonName', value: 'iobroker' },
|
|
1794
|
+
{ name: 'organizationName', value: 'ioBroker GmbH' },
|
|
1795
|
+
{ shortName: 'OU', value: 'iobroker' }
|
|
1618
1796
|
];
|
|
1619
1797
|
|
|
1620
1798
|
cert.setSubject(subAttrs);
|
|
@@ -1641,10 +1819,12 @@ function generateDefaultCertificates() {
|
|
|
1641
1819
|
},
|
|
1642
1820
|
{
|
|
1643
1821
|
name: 'subjectAltName',
|
|
1644
|
-
altNames: [
|
|
1645
|
-
|
|
1646
|
-
|
|
1647
|
-
|
|
1822
|
+
altNames: [
|
|
1823
|
+
{
|
|
1824
|
+
type: 2,
|
|
1825
|
+
value: os.hostname()
|
|
1826
|
+
}
|
|
1827
|
+
]
|
|
1648
1828
|
},
|
|
1649
1829
|
{
|
|
1650
1830
|
name: 'subjectKeyIdentifier'
|
|
@@ -1715,8 +1895,10 @@ async function getHostInfo(objects, callback) {
|
|
|
1715
1895
|
}
|
|
1716
1896
|
|
|
1717
1897
|
const cpus = os.cpus();
|
|
1898
|
+
const dateObj = new Date();
|
|
1899
|
+
|
|
1718
1900
|
const data = {
|
|
1719
|
-
Platform: os.platform(),
|
|
1901
|
+
Platform: isDocker() ? 'docker' : os.platform(),
|
|
1720
1902
|
os: process.platform,
|
|
1721
1903
|
Architecture: os.arch(),
|
|
1722
1904
|
CPUs: cpus && Array.isArray(cpus) ? cpus.length : null,
|
|
@@ -1724,7 +1906,9 @@ async function getHostInfo(objects, callback) {
|
|
|
1724
1906
|
Model: cpus && Array.isArray(cpus) ? cpus[0].model : null,
|
|
1725
1907
|
RAM: os.totalmem(),
|
|
1726
1908
|
'System uptime': Math.round(os.uptime()),
|
|
1727
|
-
'Node.js': process.version
|
|
1909
|
+
'Node.js': process.version,
|
|
1910
|
+
time: dateObj.getTime(), // give infos to compare the local times
|
|
1911
|
+
timeOffset: dateObj.getTimezoneOffset()
|
|
1728
1912
|
};
|
|
1729
1913
|
|
|
1730
1914
|
if (data.Platform === 'win32') {
|
|
@@ -1739,7 +1923,9 @@ async function getHostInfo(objects, callback) {
|
|
|
1739
1923
|
// Check if repositories exists
|
|
1740
1924
|
const allRepos = {};
|
|
1741
1925
|
if (systemRepos && systemRepos.native && systemRepos.native.repositories) {
|
|
1742
|
-
const repos = Array.isArray(systemConfig.common.activeRepo)
|
|
1926
|
+
const repos = Array.isArray(systemConfig.common.activeRepo)
|
|
1927
|
+
? systemConfig.common.activeRepo
|
|
1928
|
+
: [systemConfig.common.activeRepo];
|
|
1743
1929
|
repos
|
|
1744
1930
|
.filter(repo => systemRepos.native.repositories[repo] && systemRepos.native.repositories[repo].json)
|
|
1745
1931
|
.forEach(repo => Object.assign(allRepos, systemRepos.native.repositories[repo].json));
|
|
@@ -1860,8 +2046,10 @@ function getConfigFileName() {
|
|
|
1860
2046
|
}
|
|
1861
2047
|
|
|
1862
2048
|
// if debugging with npm5 -> node_modules on e.g. /opt/node_modules
|
|
1863
|
-
if (
|
|
1864
|
-
fs.existsSync(`${__dirname}/../../../../../../../node_modules/${appName}.js-controller`)
|
|
2049
|
+
if (
|
|
2050
|
+
fs.existsSync(`${__dirname}/../../../../../../../node_modules/${appName.toLowerCase()}.js-controller`) ||
|
|
2051
|
+
fs.existsSync(`${__dirname}/../../../../../../../node_modules/${appName}.js-controller`)
|
|
2052
|
+
) {
|
|
1865
2053
|
// remove /node_modules/' + appName + '.js-controller/lib
|
|
1866
2054
|
configDir.splice(configDir.length - 7, 7);
|
|
1867
2055
|
configDir = configDir.join('/');
|
|
@@ -1905,37 +2093,41 @@ function promisify(fn, context, returnArgNames) {
|
|
|
1905
2093
|
// @ts-ignore we cannot know the type of `this`
|
|
1906
2094
|
context = context || this;
|
|
1907
2095
|
return new Promise((resolve, reject) => {
|
|
1908
|
-
fn.apply(
|
|
1909
|
-
|
|
1910
|
-
|
|
1911
|
-
|
|
1912
|
-
|
|
1913
|
-
|
|
1914
|
-
|
|
1915
|
-
|
|
1916
|
-
|
|
1917
|
-
|
|
1918
|
-
|
|
1919
|
-
|
|
1920
|
-
|
|
1921
|
-
|
|
1922
|
-
|
|
1923
|
-
|
|
1924
|
-
|
|
1925
|
-
|
|
1926
|
-
|
|
1927
|
-
|
|
2096
|
+
fn.apply(
|
|
2097
|
+
context,
|
|
2098
|
+
args.concat([
|
|
2099
|
+
function (error, result) {
|
|
2100
|
+
if (error) {
|
|
2101
|
+
return reject(error instanceof Error ? error : new Error(error));
|
|
2102
|
+
} else {
|
|
2103
|
+
// decide on how we want to return the callback arguments
|
|
2104
|
+
switch (arguments.length) {
|
|
2105
|
+
case 1: // only an error was given
|
|
2106
|
+
return resolve(); // Promise<void>
|
|
2107
|
+
case 2: // a single value (result) was returned
|
|
2108
|
+
return resolve(result);
|
|
2109
|
+
default: {
|
|
2110
|
+
// multiple values should be returned
|
|
2111
|
+
/** @type {{} | any[]} */
|
|
2112
|
+
let ret;
|
|
2113
|
+
const extraArgs = sliceArgs(arguments, 1);
|
|
2114
|
+
if (returnArgNames && returnArgNames.length === extraArgs.length) {
|
|
2115
|
+
// we can build an object
|
|
2116
|
+
ret = {};
|
|
2117
|
+
for (let i = 0; i < returnArgNames.length; i++) {
|
|
2118
|
+
ret[returnArgNames[i]] = extraArgs[i];
|
|
2119
|
+
}
|
|
2120
|
+
} else {
|
|
2121
|
+
// we return the raw array
|
|
2122
|
+
ret = extraArgs;
|
|
1928
2123
|
}
|
|
1929
|
-
|
|
1930
|
-
// we return the raw array
|
|
1931
|
-
ret = extraArgs;
|
|
2124
|
+
return resolve(ret);
|
|
1932
2125
|
}
|
|
1933
|
-
return resolve(ret);
|
|
1934
2126
|
}
|
|
1935
2127
|
}
|
|
1936
2128
|
}
|
|
1937
|
-
|
|
1938
|
-
|
|
2129
|
+
])
|
|
2130
|
+
);
|
|
1939
2131
|
});
|
|
1940
2132
|
};
|
|
1941
2133
|
}
|
|
@@ -1955,33 +2147,37 @@ function promisifyNoError(fn, context, returnArgNames) {
|
|
|
1955
2147
|
// @ts-ignore we cannot know the type of `this`
|
|
1956
2148
|
context = context || this;
|
|
1957
2149
|
return new Promise((resolve, _reject) => {
|
|
1958
|
-
fn.apply(
|
|
1959
|
-
|
|
1960
|
-
|
|
1961
|
-
|
|
1962
|
-
|
|
1963
|
-
|
|
1964
|
-
|
|
1965
|
-
|
|
1966
|
-
|
|
1967
|
-
|
|
1968
|
-
|
|
1969
|
-
|
|
1970
|
-
|
|
1971
|
-
|
|
1972
|
-
|
|
1973
|
-
|
|
1974
|
-
|
|
2150
|
+
fn.apply(
|
|
2151
|
+
context,
|
|
2152
|
+
args.concat([
|
|
2153
|
+
function (result) {
|
|
2154
|
+
// decide on how we want to return the callback arguments
|
|
2155
|
+
switch (arguments.length) {
|
|
2156
|
+
case 0: // no arguments were given
|
|
2157
|
+
return resolve(); // Promise<void>
|
|
2158
|
+
case 1: // a single value (result) was returned
|
|
2159
|
+
return resolve(result);
|
|
2160
|
+
default: {
|
|
2161
|
+
// multiple values should be returned
|
|
2162
|
+
/** @type {{} | any[]} */
|
|
2163
|
+
let ret;
|
|
2164
|
+
const extraArgs = sliceArgs(arguments, 0);
|
|
2165
|
+
if (returnArgNames && returnArgNames.length === extraArgs.length) {
|
|
2166
|
+
// we can build an object
|
|
2167
|
+
ret = {};
|
|
2168
|
+
for (let i = 0; i < returnArgNames.length; i++) {
|
|
2169
|
+
ret[returnArgNames[i]] = extraArgs[i];
|
|
2170
|
+
}
|
|
2171
|
+
} else {
|
|
2172
|
+
// we return the raw array
|
|
2173
|
+
ret = extraArgs;
|
|
1975
2174
|
}
|
|
1976
|
-
|
|
1977
|
-
// we return the raw array
|
|
1978
|
-
ret = extraArgs;
|
|
2175
|
+
return resolve(ret);
|
|
1979
2176
|
}
|
|
1980
|
-
return resolve(ret);
|
|
1981
2177
|
}
|
|
1982
2178
|
}
|
|
1983
|
-
|
|
1984
|
-
|
|
2179
|
+
])
|
|
2180
|
+
);
|
|
1985
2181
|
});
|
|
1986
2182
|
};
|
|
1987
2183
|
}
|
|
@@ -2000,43 +2196,52 @@ function _setQualityForStates(states, keys, quality, cb) {
|
|
|
2000
2196
|
if (!keys || !states || !keys.length) {
|
|
2001
2197
|
cb();
|
|
2002
2198
|
} else {
|
|
2003
|
-
states.setState(
|
|
2004
|
-
|
|
2005
|
-
|
|
2006
|
-
|
|
2199
|
+
states.setState(
|
|
2200
|
+
keys.shift(),
|
|
2201
|
+
{
|
|
2202
|
+
ack: null,
|
|
2203
|
+
q: quality
|
|
2204
|
+
},
|
|
2205
|
+
() => setImmediate(_setQualityForStates, states, keys, quality, cb)
|
|
2206
|
+
);
|
|
2007
2207
|
}
|
|
2008
2208
|
}
|
|
2009
2209
|
|
|
2010
2210
|
function setQualityForInstance(objects, states, namespace, q) {
|
|
2011
2211
|
return new Promise((resolve, reject) => {
|
|
2012
|
-
objects.getObjectView(
|
|
2013
|
-
|
|
2014
|
-
|
|
2015
|
-
|
|
2016
|
-
|
|
2017
|
-
|
|
2018
|
-
|
|
2019
|
-
}
|
|
2020
|
-
|
|
2021
|
-
if (
|
|
2022
|
-
|
|
2023
|
-
|
|
2024
|
-
|
|
2025
|
-
|
|
2026
|
-
|
|
2212
|
+
objects.getObjectView(
|
|
2213
|
+
'system',
|
|
2214
|
+
'state',
|
|
2215
|
+
{
|
|
2216
|
+
startkey: namespace + '.',
|
|
2217
|
+
endkey: namespace + '.\u9999',
|
|
2218
|
+
include_docs: false
|
|
2219
|
+
},
|
|
2220
|
+
(err, _states) => {
|
|
2221
|
+
if (err) {
|
|
2222
|
+
reject(err);
|
|
2223
|
+
} else {
|
|
2224
|
+
let keys = [];
|
|
2225
|
+
if (_states && _states.rows) {
|
|
2226
|
+
for (let s = 0; s < _states.rows.length; s++) {
|
|
2227
|
+
const id = _states.rows[s].id;
|
|
2228
|
+
// if instance still active, but device is offline
|
|
2229
|
+
if (!(q & 0x10) && id.match(/\.info\.connection$/)) {
|
|
2230
|
+
continue;
|
|
2231
|
+
}
|
|
2232
|
+
keys.push(id);
|
|
2027
2233
|
}
|
|
2028
|
-
keys.push(id);
|
|
2029
2234
|
}
|
|
2235
|
+
// read all values for IDs
|
|
2236
|
+
states.getStates(keys, (_err, values) => {
|
|
2237
|
+
// Get only states, that have ack = true
|
|
2238
|
+
keys = keys.filter((_id, i) => values[i] && values[i].ack);
|
|
2239
|
+
// update quality code of the states to new one
|
|
2240
|
+
_setQualityForStates(states, keys, q, err => (err ? reject(err) : resolve()));
|
|
2241
|
+
});
|
|
2030
2242
|
}
|
|
2031
|
-
// read all values for IDs
|
|
2032
|
-
states.getStates(keys, (_err, values) => {
|
|
2033
|
-
// Get only states, that have ack = true
|
|
2034
|
-
keys = keys.filter((_id, i) => values[i] && values[i].ack);
|
|
2035
|
-
// update quality code of the states to new one
|
|
2036
|
-
_setQualityForStates(states, keys, q, err => err ? reject(err) : resolve());
|
|
2037
|
-
});
|
|
2038
2243
|
}
|
|
2039
|
-
|
|
2244
|
+
);
|
|
2040
2245
|
});
|
|
2041
2246
|
}
|
|
2042
2247
|
|
|
@@ -2069,17 +2274,16 @@ function captureStackTrace(wrapperName) {
|
|
|
2069
2274
|
const ret = new Error();
|
|
2070
2275
|
if (ret.stack) {
|
|
2071
2276
|
let foundSelf = false;
|
|
2072
|
-
const lines = ret.stack.split('\n')
|
|
2073
|
-
|
|
2074
|
-
|
|
2075
|
-
|
|
2076
|
-
|
|
2077
|
-
|
|
2078
|
-
|
|
2079
|
-
|
|
2080
|
-
|
|
2081
|
-
|
|
2082
|
-
});
|
|
2277
|
+
const lines = ret.stack.split('\n').filter(line => {
|
|
2278
|
+
// keep all lines after this function's
|
|
2279
|
+
if (foundSelf) {
|
|
2280
|
+
return true;
|
|
2281
|
+
}
|
|
2282
|
+
if (line.includes(wrapperName)) {
|
|
2283
|
+
foundSelf = true;
|
|
2284
|
+
}
|
|
2285
|
+
return false;
|
|
2286
|
+
});
|
|
2083
2287
|
return lines.join('\n');
|
|
2084
2288
|
}
|
|
2085
2289
|
return '';
|
|
@@ -2232,7 +2436,7 @@ function measureEventLoopLag(ms, cb) {
|
|
|
2232
2436
|
|
|
2233
2437
|
function hrtime() {
|
|
2234
2438
|
const t = process.hrtime();
|
|
2235
|
-
return
|
|
2439
|
+
return t[0] * 1e3 + t[1] / 1e6;
|
|
2236
2440
|
}
|
|
2237
2441
|
}
|
|
2238
2442
|
|
|
@@ -2259,10 +2463,29 @@ function formatAliasValue(sourceObj, targetObj, state, logger, logNamespace) {
|
|
|
2259
2463
|
if (targetObj && targetObj.alias && targetObj.alias.read) {
|
|
2260
2464
|
try {
|
|
2261
2465
|
// process the value here
|
|
2262
|
-
const func = new Function(
|
|
2263
|
-
|
|
2466
|
+
const func = new Function(
|
|
2467
|
+
'val',
|
|
2468
|
+
'type',
|
|
2469
|
+
'min',
|
|
2470
|
+
'max',
|
|
2471
|
+
'sType',
|
|
2472
|
+
'sMin',
|
|
2473
|
+
'sMax',
|
|
2474
|
+
'return ' + targetObj.alias.read
|
|
2475
|
+
);
|
|
2476
|
+
state.val = func(
|
|
2477
|
+
state.val,
|
|
2478
|
+
targetObj.type,
|
|
2479
|
+
targetObj.min,
|
|
2480
|
+
targetObj.max,
|
|
2481
|
+
sourceObj.type,
|
|
2482
|
+
sourceObj.min,
|
|
2483
|
+
sourceObj.max
|
|
2484
|
+
);
|
|
2264
2485
|
} catch (e) {
|
|
2265
|
-
logger.error(
|
|
2486
|
+
logger.error(
|
|
2487
|
+
`${logNamespace} Invalid read function for ${targetObj._id}: ${targetObj.alias.read} => ${e.message}`
|
|
2488
|
+
);
|
|
2266
2489
|
return null;
|
|
2267
2490
|
}
|
|
2268
2491
|
}
|
|
@@ -2270,10 +2493,29 @@ function formatAliasValue(sourceObj, targetObj, state, logger, logNamespace) {
|
|
|
2270
2493
|
if (sourceObj && sourceObj.alias && sourceObj.alias.write) {
|
|
2271
2494
|
try {
|
|
2272
2495
|
// process the value here
|
|
2273
|
-
const func = new Function(
|
|
2274
|
-
|
|
2496
|
+
const func = new Function(
|
|
2497
|
+
'val',
|
|
2498
|
+
'type',
|
|
2499
|
+
'min',
|
|
2500
|
+
'max',
|
|
2501
|
+
'tType',
|
|
2502
|
+
'tMin',
|
|
2503
|
+
'tMax',
|
|
2504
|
+
'return ' + sourceObj.alias.write
|
|
2505
|
+
);
|
|
2506
|
+
state.val = func(
|
|
2507
|
+
state.val,
|
|
2508
|
+
sourceObj.type,
|
|
2509
|
+
sourceObj.min,
|
|
2510
|
+
sourceObj.max,
|
|
2511
|
+
targetObj.type,
|
|
2512
|
+
targetObj.min,
|
|
2513
|
+
targetObj.max
|
|
2514
|
+
);
|
|
2275
2515
|
} catch (e) {
|
|
2276
|
-
logger.error(
|
|
2516
|
+
logger.error(
|
|
2517
|
+
`${logNamespace} Invalid write function for ${sourceObj._id}: ${sourceObj.alias.write} => ${e.message}`
|
|
2518
|
+
);
|
|
2277
2519
|
return null;
|
|
2278
2520
|
}
|
|
2279
2521
|
}
|
|
@@ -2295,15 +2537,35 @@ function formatAliasValue(sourceObj, targetObj, state, logger, logNamespace) {
|
|
|
2295
2537
|
}
|
|
2296
2538
|
|
|
2297
2539
|
// auto-scaling, only if val not null and unit for target (x)or source is %
|
|
2298
|
-
if (
|
|
2299
|
-
|
|
2300
|
-
sourceObj
|
|
2540
|
+
if (
|
|
2541
|
+
((targetObj && targetObj.alias && !targetObj.alias.read) ||
|
|
2542
|
+
(sourceObj && sourceObj.alias && !sourceObj.alias.write)) &&
|
|
2543
|
+
state.val !== null
|
|
2544
|
+
) {
|
|
2545
|
+
if (
|
|
2546
|
+
targetObj &&
|
|
2547
|
+
targetObj.type === 'number' &&
|
|
2548
|
+
targetObj.unit === '%' &&
|
|
2549
|
+
sourceObj &&
|
|
2550
|
+
sourceObj.type === 'number' &&
|
|
2551
|
+
sourceObj.unit !== '%' &&
|
|
2552
|
+
sourceObj.min !== undefined &&
|
|
2553
|
+
sourceObj.max !== undefined
|
|
2554
|
+
) {
|
|
2301
2555
|
// scale target between 0 and 100 % based on sources min/max
|
|
2302
|
-
state.val = (state.val - sourceObj.min) / (sourceObj.max - sourceObj.min) * 100;
|
|
2303
|
-
} else if (
|
|
2304
|
-
|
|
2556
|
+
state.val = ((state.val - sourceObj.min) / (sourceObj.max - sourceObj.min)) * 100;
|
|
2557
|
+
} else if (
|
|
2558
|
+
sourceObj &&
|
|
2559
|
+
sourceObj.type === 'number' &&
|
|
2560
|
+
sourceObj.unit === '%' &&
|
|
2561
|
+
targetObj &&
|
|
2562
|
+
targetObj.unit !== '%' &&
|
|
2563
|
+
targetObj.type === 'number' &&
|
|
2564
|
+
targetObj.min !== undefined &&
|
|
2565
|
+
targetObj.max !== undefined
|
|
2566
|
+
) {
|
|
2305
2567
|
// scale target based on its min/max by its source (assuming source is meant to be 0 - 100 %)
|
|
2306
|
-
state.val = ((targetObj.max - targetObj.min) * state.val / 100
|
|
2568
|
+
state.val = ((targetObj.max - targetObj.min) * state.val) / 100 + targetObj.min;
|
|
2307
2569
|
}
|
|
2308
2570
|
}
|
|
2309
2571
|
|
|
@@ -2317,32 +2579,35 @@ function formatAliasValue(sourceObj, targetObj, state, logger, logNamespace) {
|
|
|
2317
2579
|
* @memberof tools
|
|
2318
2580
|
* @param {object} objects object to access objects db
|
|
2319
2581
|
* @param {string} id the object id which will be deleted from enums
|
|
2320
|
-
* @
|
|
2582
|
+
* @param {object} [allEnums] objects with all enums to use - if not provided all enums will be queried
|
|
2583
|
+
* @returns {Promise} All objects are tried to be updated - reject will happen as soon as one fails with the error of the first fail
|
|
2321
2584
|
*
|
|
2322
2585
|
*/
|
|
2323
|
-
function removeIdFromAllEnums(objects, id) {
|
|
2324
|
-
|
|
2325
|
-
|
|
2326
|
-
|
|
2327
|
-
|
|
2328
|
-
|
|
2329
|
-
|
|
2330
|
-
|
|
2331
|
-
|
|
2332
|
-
|
|
2333
|
-
|
|
2334
|
-
|
|
2335
|
-
|
|
2336
|
-
|
|
2337
|
-
|
|
2338
|
-
|
|
2339
|
-
|
|
2340
|
-
|
|
2341
|
-
}
|
|
2342
|
-
|
|
2343
|
-
|
|
2344
|
-
|
|
2345
|
-
|
|
2586
|
+
async function removeIdFromAllEnums(objects, id, allEnums) {
|
|
2587
|
+
if (!allEnums) {
|
|
2588
|
+
allEnums = await this.getAllEnums(objects);
|
|
2589
|
+
}
|
|
2590
|
+
|
|
2591
|
+
let error = null;
|
|
2592
|
+
for (const [enumId, enumObj] of Object.entries(allEnums)) {
|
|
2593
|
+
const idx = enumObj.common.members ? enumObj.common.members.indexOf(id) : -1;
|
|
2594
|
+
if (idx !== -1) {
|
|
2595
|
+
// the id is in the enum now we have to remove it
|
|
2596
|
+
enumObj.common.members.splice(idx, 1);
|
|
2597
|
+
try {
|
|
2598
|
+
await objects.setObjectAsync(enumId, enumObj);
|
|
2599
|
+
// update cache directly to prevent race conditions when sending many delete in a short time
|
|
2600
|
+
allEnums[enumId] = enumObj;
|
|
2601
|
+
} catch (err) {
|
|
2602
|
+
if (!error) {
|
|
2603
|
+
error = err;
|
|
2604
|
+
}
|
|
2605
|
+
}
|
|
2606
|
+
}
|
|
2607
|
+
}
|
|
2608
|
+
if (error) {
|
|
2609
|
+
throw error;
|
|
2610
|
+
}
|
|
2346
2611
|
}
|
|
2347
2612
|
|
|
2348
2613
|
/**
|
|
@@ -2350,8 +2615,8 @@ function removeIdFromAllEnums(objects, id) {
|
|
|
2350
2615
|
*
|
|
2351
2616
|
* @alias parseDependencies
|
|
2352
2617
|
* @memberof tools
|
|
2353
|
-
* @param {string[]|
|
|
2354
|
-
* @returns {
|
|
2618
|
+
* @param {string[]|Record<string, string>[]|string|Record<string, string>} dependencies dependencies array or single dependency
|
|
2619
|
+
* @returns {Record<string, string>} parsed dependencies
|
|
2355
2620
|
*/
|
|
2356
2621
|
function parseDependencies(dependencies) {
|
|
2357
2622
|
let adapters = {};
|
|
@@ -2362,7 +2627,9 @@ function parseDependencies(dependencies) {
|
|
|
2362
2627
|
adapters[rule] = '*';
|
|
2363
2628
|
} else if (isObject(rule)) {
|
|
2364
2629
|
// can be object containing single adapter or multiple
|
|
2365
|
-
Object.keys(rule)
|
|
2630
|
+
Object.keys(rule)
|
|
2631
|
+
.filter(adapter => !adapters[adapter])
|
|
2632
|
+
.forEach(adapter => (adapters[adapter] = rule[adapter]));
|
|
2366
2633
|
}
|
|
2367
2634
|
});
|
|
2368
2635
|
} else if (typeof dependencies === 'string') {
|
|
@@ -2394,12 +2661,29 @@ function validateGeneralObjectProperties(obj, extend) {
|
|
|
2394
2661
|
}
|
|
2395
2662
|
|
|
2396
2663
|
if (obj.type !== undefined && typeof obj.type !== 'string') {
|
|
2397
|
-
throw new Error(`obj.type has an invalid type! Expected "string", received
|
|
2398
|
-
}
|
|
2399
|
-
|
|
2400
|
-
const allowedObjectTypes = [
|
|
2664
|
+
throw new Error(`obj.type has an invalid type! Expected "string", received "${typeof obj.type}"`);
|
|
2665
|
+
}
|
|
2666
|
+
|
|
2667
|
+
const allowedObjectTypes = [
|
|
2668
|
+
'state',
|
|
2669
|
+
'channel',
|
|
2670
|
+
'device',
|
|
2671
|
+
'enum',
|
|
2672
|
+
'host',
|
|
2673
|
+
'adapter',
|
|
2674
|
+
'instance',
|
|
2675
|
+
'meta',
|
|
2676
|
+
'config',
|
|
2677
|
+
'script',
|
|
2678
|
+
'user',
|
|
2679
|
+
'group',
|
|
2680
|
+
'chart',
|
|
2681
|
+
'folder'
|
|
2682
|
+
];
|
|
2401
2683
|
if (obj.type !== undefined && !allowedObjectTypes.includes(obj.type)) {
|
|
2402
|
-
throw new Error(
|
|
2684
|
+
throw new Error(
|
|
2685
|
+
`obj.type has an invalid value (${obj.type}) but has to be one of ${allowedObjectTypes.join(', ')}`
|
|
2686
|
+
);
|
|
2403
2687
|
}
|
|
2404
2688
|
|
|
2405
2689
|
// obj.common is optional in general check
|
|
@@ -2408,39 +2692,152 @@ function validateGeneralObjectProperties(obj, extend) {
|
|
|
2408
2692
|
}
|
|
2409
2693
|
|
|
2410
2694
|
if (obj.common.name !== undefined && typeof obj.common.name !== 'string' && typeof obj.common.name !== 'object') {
|
|
2411
|
-
throw new Error(
|
|
2695
|
+
throw new Error(
|
|
2696
|
+
`obj.common.name has an invalid type! Expected "string" or "object", received "${typeof obj.common.name}"`
|
|
2697
|
+
);
|
|
2698
|
+
} else if (['adapter'].includes(obj.type) && typeof obj.common.name !== 'string') {
|
|
2699
|
+
// TODO: we need this for group/user too, but have to solve problems described in #1266
|
|
2700
|
+
// for some types, name needs to be a unique string
|
|
2701
|
+
throw new Error(`obj.common.name has an invalid type! Expected "string", received "${typeof obj.common.name}"`);
|
|
2412
2702
|
}
|
|
2413
2703
|
|
|
2414
2704
|
if (obj.common.type !== undefined) {
|
|
2415
2705
|
if (typeof obj.common.type !== 'string') {
|
|
2416
|
-
throw new Error(
|
|
2417
|
-
|
|
2706
|
+
throw new Error(
|
|
2707
|
+
`obj.common.type has an invalid type! Expected "string", received "${typeof obj.common.type}"`
|
|
2708
|
+
);
|
|
2709
|
+
}
|
|
2710
|
+
|
|
2711
|
+
if (obj.type === 'state') {
|
|
2712
|
+
// if object type indicates a state, check that common.type matches
|
|
2713
|
+
const allowedStateTypes = ['number', 'string', 'boolean', 'array', 'object', 'mixed', 'file', 'json'];
|
|
2714
|
+
if (!allowedStateTypes.includes(obj.common.type)) {
|
|
2715
|
+
throw new Error(
|
|
2716
|
+
`obj.common.type has an invalid value (${
|
|
2717
|
+
obj.common.type
|
|
2718
|
+
}) but has to be one of ${allowedStateTypes.join(', ')}`
|
|
2719
|
+
);
|
|
2720
|
+
}
|
|
2721
|
+
|
|
2722
|
+
// ensure that min max only exists for common.type number and is number itself
|
|
2723
|
+
if (obj.common.min !== undefined) {
|
|
2724
|
+
if (typeof obj.common.min !== 'number') {
|
|
2725
|
+
throw new Error(
|
|
2726
|
+
`obj.common.min has an invalid type! Expected "number", received "${typeof obj.common.min}"`
|
|
2727
|
+
);
|
|
2728
|
+
}
|
|
2729
|
+
|
|
2730
|
+
if (obj.common.type !== 'number' && obj.common.type !== 'mixed') {
|
|
2731
|
+
throw new Error(
|
|
2732
|
+
`obj.common.min is only allowed on obj.common.type "number" or "mixed", received "${obj.common.type}"`
|
|
2733
|
+
);
|
|
2734
|
+
}
|
|
2735
|
+
}
|
|
2736
|
+
|
|
2737
|
+
if (obj.common.max !== undefined) {
|
|
2738
|
+
if (typeof obj.common.max !== 'number') {
|
|
2739
|
+
throw new Error(
|
|
2740
|
+
`obj.common.max has an invalid type! Expected "number", received "${typeof obj.common.max}"`
|
|
2741
|
+
);
|
|
2742
|
+
}
|
|
2743
|
+
|
|
2744
|
+
if (obj.common.type !== 'number' && obj.common.type !== 'mixed') {
|
|
2745
|
+
throw new Error(
|
|
2746
|
+
`obj.common.max is only allowed on obj.common.type "number" or "mixed", received "${obj.common.type}"`
|
|
2747
|
+
);
|
|
2748
|
+
}
|
|
2418
2749
|
|
|
2419
|
-
|
|
2420
|
-
|
|
2421
|
-
|
|
2422
|
-
|
|
2750
|
+
if (obj.common.min !== undefined && obj.common.min > obj.common.max) {
|
|
2751
|
+
throw new Error(
|
|
2752
|
+
`obj.common.min (${obj.common.min}) needs to be less than or equal to obj.common.max (${obj.common.max})`
|
|
2753
|
+
);
|
|
2754
|
+
}
|
|
2755
|
+
}
|
|
2756
|
+
|
|
2757
|
+
// ensure, that default value has correct type
|
|
2758
|
+
if (obj.common.def !== undefined && obj.common.def !== null) {
|
|
2759
|
+
if (obj.common.type === 'file') {
|
|
2760
|
+
// defaults are set via setState but would need setBinaryState
|
|
2761
|
+
throw new Error('Default value is not supported for type "file"');
|
|
2762
|
+
}
|
|
2763
|
+
|
|
2764
|
+
// else do what strictObjectChecks does for val
|
|
2765
|
+
if (
|
|
2766
|
+
!(
|
|
2767
|
+
(obj.common.type === 'mixed' && typeof obj.common.def !== 'object') ||
|
|
2768
|
+
(obj.common.type !== 'object' && obj.common.type === typeof obj.common.def) ||
|
|
2769
|
+
(obj.common.type === 'array' && typeof obj.common.def === 'string') ||
|
|
2770
|
+
(obj.common.type === 'json' && typeof obj.common.def === 'string') ||
|
|
2771
|
+
(obj.common.type === 'file' && typeof obj.common.def === 'string') ||
|
|
2772
|
+
(obj.common.type === 'object' && typeof obj.common.def === 'string')
|
|
2773
|
+
)
|
|
2774
|
+
) {
|
|
2775
|
+
// types can be 'number', 'string', 'boolean', 'array', 'object', 'mixed', 'file', 'json'
|
|
2776
|
+
// array, object, json need to be string
|
|
2777
|
+
if (['object', 'json', 'file', 'array'].includes(obj.common.type)) {
|
|
2778
|
+
throw new Error(
|
|
2779
|
+
`Default value has to be stringified but received type "${typeof obj.common.def}"`
|
|
2780
|
+
);
|
|
2781
|
+
} else {
|
|
2782
|
+
throw new Error(
|
|
2783
|
+
`Default value has to be ${
|
|
2784
|
+
obj.common.type === 'mixed'
|
|
2785
|
+
? `one of type "string", "number", "boolean"`
|
|
2786
|
+
: `type "${obj.common.type}"`
|
|
2787
|
+
} but received type "${typeof obj.common.def}"`
|
|
2788
|
+
);
|
|
2789
|
+
}
|
|
2790
|
+
}
|
|
2791
|
+
}
|
|
2423
2792
|
}
|
|
2424
2793
|
}
|
|
2425
2794
|
|
|
2426
2795
|
if (obj.common.read !== undefined && typeof obj.common.read !== 'boolean') {
|
|
2427
|
-
throw new Error(
|
|
2796
|
+
throw new Error(
|
|
2797
|
+
`obj.common.read has an invalid type! Expected "boolean", received "${typeof obj.common.read}"`
|
|
2798
|
+
);
|
|
2428
2799
|
}
|
|
2429
2800
|
|
|
2430
2801
|
if (obj.common.write !== undefined && typeof obj.common.write !== 'boolean') {
|
|
2431
|
-
throw new Error(
|
|
2802
|
+
throw new Error(
|
|
2803
|
+
`obj.common.write has an invalid type! Expected "boolean", received "${typeof obj.common.write}"`
|
|
2804
|
+
);
|
|
2432
2805
|
}
|
|
2433
2806
|
|
|
2434
2807
|
if (obj.common.role !== undefined && typeof obj.common.role !== 'string') {
|
|
2435
|
-
throw new Error(`obj.common.role has an invalid type! Expected "string", received
|
|
2808
|
+
throw new Error(`obj.common.role has an invalid type! Expected "string", received "${typeof obj.common.role}"`);
|
|
2436
2809
|
}
|
|
2437
2810
|
|
|
2438
2811
|
if (obj.common.desc !== undefined && typeof obj.common.desc !== 'string' && typeof obj.common.desc !== 'object') {
|
|
2439
|
-
throw new Error(
|
|
2812
|
+
throw new Error(
|
|
2813
|
+
`obj.common.desc has an invalid type! Expected "string" or "object", received "${typeof obj.common.desc}"`
|
|
2814
|
+
);
|
|
2815
|
+
}
|
|
2816
|
+
|
|
2817
|
+
if (
|
|
2818
|
+
obj.type === 'state' &&
|
|
2819
|
+
obj.common.custom !== undefined &&
|
|
2820
|
+
obj.common.custom !== null &&
|
|
2821
|
+
!isObject(obj.common.custom)
|
|
2822
|
+
) {
|
|
2823
|
+
throw new Error(
|
|
2824
|
+
`obj.common.custom has an invalid type! Expected "object", received "${typeof obj.common.custom}"`
|
|
2825
|
+
);
|
|
2440
2826
|
}
|
|
2441
2827
|
|
|
2442
|
-
|
|
2443
|
-
|
|
2828
|
+
// common.states needs to be a real object or an array
|
|
2829
|
+
if (
|
|
2830
|
+
obj.common.states !== null &&
|
|
2831
|
+
obj.common.states !== undefined &&
|
|
2832
|
+
!isObject(obj.common.states) &&
|
|
2833
|
+
!Array.isArray(obj.common.states)
|
|
2834
|
+
) {
|
|
2835
|
+
throw new Error(
|
|
2836
|
+
`obj.common.states has an invalid type! Expected "object", received "${typeof obj.common.states}"`
|
|
2837
|
+
);
|
|
2838
|
+
} else if (obj.common.states === null && !extend) {
|
|
2839
|
+
// extend only allowed on extend
|
|
2840
|
+
throw new Error(`obj.common.states has an invalid type! Expected non-null "object", received "null"`);
|
|
2444
2841
|
}
|
|
2445
2842
|
}
|
|
2446
2843
|
|
|
@@ -2450,81 +2847,63 @@ function validateGeneralObjectProperties(obj, extend) {
|
|
|
2450
2847
|
* @alias getAllInstances
|
|
2451
2848
|
* @memberof tools
|
|
2452
2849
|
* @param {string[]} adapters list of adapter names to get instances for
|
|
2453
|
-
* @param {
|
|
2850
|
+
* @param {object} objects class redis objects
|
|
2851
|
+
* @returns {Promise<string[]>} - array of IDs
|
|
2454
2852
|
*/
|
|
2455
|
-
function getAllInstances(adapters, objects
|
|
2853
|
+
async function getAllInstances(adapters, objects) {
|
|
2456
2854
|
const instances = [];
|
|
2457
|
-
|
|
2458
|
-
for (let k = 0; k < adapters.length; k++) {
|
|
2459
|
-
if (!adapters[k]) {
|
|
2460
|
-
continue;
|
|
2461
|
-
}
|
|
2462
|
-
if (adapters[k].indexOf('.') === -1) {
|
|
2463
|
-
count++;
|
|
2464
|
-
}
|
|
2465
|
-
}
|
|
2855
|
+
|
|
2466
2856
|
for (let i = 0; i < adapters.length; i++) {
|
|
2467
2857
|
if (!adapters[i]) {
|
|
2468
2858
|
continue;
|
|
2469
2859
|
}
|
|
2470
|
-
if (adapters[i].
|
|
2471
|
-
getInstances(adapters[i], objects, false
|
|
2472
|
-
|
|
2473
|
-
|
|
2474
|
-
|
|
2475
|
-
}
|
|
2476
|
-
}
|
|
2477
|
-
if (!--count && callback) {
|
|
2478
|
-
callback(null, instances);
|
|
2479
|
-
callback = null;
|
|
2860
|
+
if (!adapters[i].includes('.')) {
|
|
2861
|
+
const inst = await getInstances(adapters[i], objects, false);
|
|
2862
|
+
for (let j = 0; j < inst.length; j++) {
|
|
2863
|
+
if (!instances.includes(inst[j])) {
|
|
2864
|
+
instances.push(inst[j]);
|
|
2480
2865
|
}
|
|
2481
|
-
}
|
|
2866
|
+
}
|
|
2482
2867
|
} else {
|
|
2483
|
-
if (instances.
|
|
2868
|
+
if (!instances.includes(adapters[i])) {
|
|
2484
2869
|
instances.push(adapters[i]);
|
|
2485
2870
|
}
|
|
2486
2871
|
}
|
|
2487
2872
|
}
|
|
2488
|
-
if (!count && callback) {
|
|
2489
|
-
callback(null, instances);
|
|
2490
|
-
callback = null;
|
|
2491
|
-
}
|
|
2492
|
-
}
|
|
2493
2873
|
|
|
2494
|
-
|
|
2495
|
-
|
|
2496
|
-
*/
|
|
2497
|
-
const getAllInstancesAsync = promisify(getAllInstances);
|
|
2874
|
+
return instances;
|
|
2875
|
+
}
|
|
2498
2876
|
|
|
2499
2877
|
/**
|
|
2500
|
-
*
|
|
2878
|
+
* Get all existing enums
|
|
2501
2879
|
*
|
|
2502
|
-
* @
|
|
2503
|
-
* @
|
|
2504
|
-
* @param {object }objects objects DB
|
|
2505
|
-
* @param {boolean} withObjects return objects instead of only ids
|
|
2506
|
-
* @param {function} callback callback to be executed
|
|
2880
|
+
* @param {object} objects - objects db
|
|
2881
|
+
* @returns {Promise<{}>}
|
|
2507
2882
|
*/
|
|
2508
|
-
function
|
|
2509
|
-
|
|
2510
|
-
|
|
2511
|
-
|
|
2883
|
+
async function getAllEnums(objects) {
|
|
2884
|
+
const allEnums = {};
|
|
2885
|
+
const res = await objects.getObjectViewAsync('system', 'enum', {
|
|
2886
|
+
startkey: 'enum.',
|
|
2887
|
+
endkey: 'enum.\u9999'
|
|
2888
|
+
});
|
|
2889
|
+
if (res && res.rows) {
|
|
2890
|
+
for (const row of res.rows) {
|
|
2891
|
+
allEnums[row.id] = row.value;
|
|
2892
|
+
}
|
|
2512
2893
|
}
|
|
2513
2894
|
|
|
2514
|
-
return
|
|
2515
|
-
.then(instances => callback(null, instances))
|
|
2516
|
-
.catch(error => callback(error));
|
|
2895
|
+
return allEnums;
|
|
2517
2896
|
}
|
|
2518
2897
|
|
|
2519
2898
|
/**
|
|
2520
2899
|
* get async all instances of one adapter
|
|
2521
2900
|
*
|
|
2522
|
-
* @alias
|
|
2523
|
-
* @param {string
|
|
2524
|
-
* @param {object
|
|
2901
|
+
* @alias getInstances
|
|
2902
|
+
* @param {string} adapter name of the adapter
|
|
2903
|
+
* @param {object}objects objects DB
|
|
2525
2904
|
* @param {boolean} withObjects return objects instead of only ids
|
|
2526
2905
|
*/
|
|
2527
|
-
async function
|
|
2906
|
+
async function getInstances(adapter, objects, withObjects) {
|
|
2528
2907
|
const arr = await objects.getObjectListAsync({
|
|
2529
2908
|
startkey: 'system.adapter.' + adapter + '.',
|
|
2530
2909
|
endkey: 'system.adapter.' + adapter + '.\u9999'
|
|
@@ -2550,7 +2929,7 @@ async function getInstancesAsync(adapter, objects, withObjects) {
|
|
|
2550
2929
|
* Checks if the given callback is a function and if so calls it with the given parameter immediately, else a resolved Promise is returned
|
|
2551
2930
|
*
|
|
2552
2931
|
* @param {(...args: any[]) => void | null | undefined} callback - callback function to be executed
|
|
2553
|
-
* @param {any
|
|
2932
|
+
* @param {...any} args - as many arguments as needed, which will be returned by the callback function or by the Promise
|
|
2554
2933
|
* @returns {Promise<any>} - if Promise is resolved with multiple arguments, an array is returned
|
|
2555
2934
|
*/
|
|
2556
2935
|
function maybeCallback(callback, ...args) {
|
|
@@ -2568,7 +2947,7 @@ function maybeCallback(callback, ...args) {
|
|
|
2568
2947
|
* @param {((error: Error | null | undefined, ...args: any[]) => void) | null | undefined} callback - callback function to be executed
|
|
2569
2948
|
* @param {Error | string | null | undefined} error - error which will be used by the callback function. If callback is not a function and
|
|
2570
2949
|
* error is given, a rejected Promise is returned. If error is given but it is not an instance of Error, it is converted into one.
|
|
2571
|
-
* @param {any
|
|
2950
|
+
* @param {...any} args - as many arguments as needed, which will be returned by the callback function or by the Promise
|
|
2572
2951
|
* @returns {Promise<any>} - if Promise is resolved with multiple arguments, an array is returned
|
|
2573
2952
|
*/
|
|
2574
2953
|
function maybeCallbackWithError(callback, error, ...args) {
|
|
@@ -2591,8 +2970,8 @@ function maybeCallbackWithError(callback, error, ...args) {
|
|
|
2591
2970
|
* Executes a command asynchronously. On success, the promise resolves with stdout and stderr.
|
|
2592
2971
|
* On error, the promise rejects with the exit code or signal, as well as stdout and stderr.
|
|
2593
2972
|
* @param {string} command The command to execute
|
|
2594
|
-
* @param {import(
|
|
2595
|
-
* @returns {import(
|
|
2973
|
+
* @param {import('child_process').ExecOptions} [execOptions] The options for child_process.exec
|
|
2974
|
+
* @returns {import('child_process').ChildProcess & Promise<{stdout?: string; stderr?: string}>}
|
|
2596
2975
|
*/
|
|
2597
2976
|
function execAsync(command, execOptions) {
|
|
2598
2977
|
const defaultOptions = {
|
|
@@ -2602,7 +2981,7 @@ function execAsync(command, execOptions) {
|
|
|
2602
2981
|
encoding: 'utf8'
|
|
2603
2982
|
};
|
|
2604
2983
|
// @ts-ignore We set the encoding, so stdout/stdrr must be a string
|
|
2605
|
-
return cpPromise.exec(command, {...defaultOptions, ...execOptions});
|
|
2984
|
+
return cpPromise.exec(command, { ...defaultOptions, ...execOptions });
|
|
2606
2985
|
}
|
|
2607
2986
|
|
|
2608
2987
|
/**
|
|
@@ -2631,19 +3010,11 @@ async function resolveAdapterMainFile(adapter) {
|
|
|
2631
3010
|
throw new Error(`Could not find adapter dir of ${adapter}`);
|
|
2632
3011
|
}
|
|
2633
3012
|
|
|
2634
|
-
const possibleMainFiles = [
|
|
2635
|
-
'main.js',
|
|
2636
|
-
`${adapter}.js`
|
|
2637
|
-
];
|
|
3013
|
+
const possibleMainFiles = ['main.js', `${adapter}.js`];
|
|
2638
3014
|
|
|
2639
3015
|
// Add package.json -> main as the 2nd choice
|
|
2640
3016
|
try {
|
|
2641
|
-
const pack = JSON.parse(
|
|
2642
|
-
await fs.readFile(
|
|
2643
|
-
path.join(adapterDir, 'package.json'),
|
|
2644
|
-
'utf8'
|
|
2645
|
-
)
|
|
2646
|
-
);
|
|
3017
|
+
const pack = JSON.parse(await fs.readFile(path.join(adapterDir, 'package.json'), 'utf8'));
|
|
2647
3018
|
if (pack && typeof pack.main === 'string') {
|
|
2648
3019
|
possibleMainFiles.unshift(pack.main);
|
|
2649
3020
|
}
|
|
@@ -2653,12 +3024,7 @@ async function resolveAdapterMainFile(adapter) {
|
|
|
2653
3024
|
|
|
2654
3025
|
// Add io-package.json -> common.main as the preferred choice
|
|
2655
3026
|
try {
|
|
2656
|
-
const ioPack = JSON.parse(
|
|
2657
|
-
await fs.readFile(
|
|
2658
|
-
path.join(adapterDir, 'io-package.json'),
|
|
2659
|
-
'utf8'
|
|
2660
|
-
)
|
|
2661
|
-
);
|
|
3027
|
+
const ioPack = JSON.parse(await fs.readFile(path.join(adapterDir, 'io-package.json'), 'utf8'));
|
|
2662
3028
|
if (ioPack && ioPack.common && typeof ioPack.common.main === 'string') {
|
|
2663
3029
|
possibleMainFiles.unshift(ioPack.common.main);
|
|
2664
3030
|
}
|
|
@@ -2718,7 +3084,8 @@ function parseShortGithubUrl(url) {
|
|
|
2718
3084
|
}
|
|
2719
3085
|
|
|
2720
3086
|
/** This is used to parse the pathname of a github URL */
|
|
2721
|
-
const githubPathnameRegex =
|
|
3087
|
+
const githubPathnameRegex =
|
|
3088
|
+
/^\/(?<user>[^/]+)\/(?<repo>[^/]*?)(?:\.git)?(?:\/(?:tree|tarball|archive)\/(?<commit>.*?)(?:\.(?:zip|gz|tar\.gz))?)?$/;
|
|
2722
3089
|
|
|
2723
3090
|
/**
|
|
2724
3091
|
* Tests if the given pathname matches the format /<githubname>/<githubrepo>[.git][/<tarball|tree|archive>/<commit-ish>[.zip|.gz]]
|
|
@@ -2756,7 +3123,7 @@ function removePreservedProperties(preserve, oldObj, newObj) {
|
|
|
2756
3123
|
if (isObject(preserve[prop]) && isObject(newObj[prop])) {
|
|
2757
3124
|
// we have to go one step deeper
|
|
2758
3125
|
removePreservedProperties(preserve[prop], oldObj[prop], newObj[prop]);
|
|
2759
|
-
} else if (newObj && newObj[prop] !== undefined &&
|
|
3126
|
+
} else if (newObj && newObj[prop] !== undefined && oldObj && oldObj[prop] !== undefined) {
|
|
2760
3127
|
// we only need to remove something if its in the old object and in the new one
|
|
2761
3128
|
if (typeof preserve[prop] === 'boolean') {
|
|
2762
3129
|
delete newObj[prop];
|
|
@@ -2831,7 +3198,8 @@ function getInstanceIndicatorObjects(namespace, createWakeup) {
|
|
|
2831
3198
|
unit: '% of one core'
|
|
2832
3199
|
},
|
|
2833
3200
|
native: {}
|
|
2834
|
-
},
|
|
3201
|
+
},
|
|
3202
|
+
{
|
|
2835
3203
|
_id: `${id}.cputime`,
|
|
2836
3204
|
type: 'state',
|
|
2837
3205
|
common: {
|
|
@@ -2902,7 +3270,7 @@ function getInstanceIndicatorObjects(namespace, createWakeup) {
|
|
|
2902
3270
|
type: 'state',
|
|
2903
3271
|
common: {
|
|
2904
3272
|
name: `${namespace} events input counter`,
|
|
2905
|
-
desc:
|
|
3273
|
+
desc: "State's inputs in 15 seconds",
|
|
2906
3274
|
type: 'number',
|
|
2907
3275
|
read: true,
|
|
2908
3276
|
write: false,
|
|
@@ -2916,7 +3284,7 @@ function getInstanceIndicatorObjects(namespace, createWakeup) {
|
|
|
2916
3284
|
type: 'state',
|
|
2917
3285
|
common: {
|
|
2918
3286
|
name: `${namespace} events output counter`,
|
|
2919
|
-
desc:
|
|
3287
|
+
desc: "State's outputs in 15 seconds",
|
|
2920
3288
|
type: 'number',
|
|
2921
3289
|
read: true,
|
|
2922
3290
|
write: false,
|
|
@@ -2988,11 +3356,14 @@ function getInstanceIndicatorObjects(namespace, createWakeup) {
|
|
|
2988
3356
|
function getLogger(log) {
|
|
2989
3357
|
if (!log) {
|
|
2990
3358
|
log = {
|
|
2991
|
-
silly: function (_msg) {
|
|
3359
|
+
silly: function (_msg) {
|
|
3360
|
+
/*console.log(msg);*/
|
|
2992
3361
|
},
|
|
2993
|
-
debug: function (_msg) {
|
|
3362
|
+
debug: function (_msg) {
|
|
3363
|
+
/*console.log(msg);*/
|
|
2994
3364
|
},
|
|
2995
|
-
info: function (_msg) {
|
|
3365
|
+
info: function (_msg) {
|
|
3366
|
+
/*console.log(msg);*/
|
|
2996
3367
|
},
|
|
2997
3368
|
warn: function (msg) {
|
|
2998
3369
|
console.log(msg);
|
|
@@ -3016,7 +3387,7 @@ function getLogger(log) {
|
|
|
3016
3387
|
* @return {Promise<object[]>}
|
|
3017
3388
|
*/
|
|
3018
3389
|
async function getInstancesOrderedByStartPrio(objects, logger, logPrefix = '') {
|
|
3019
|
-
const instances = {
|
|
3390
|
+
const instances = { 1: [], 2: [], 3: [], admin: [] };
|
|
3020
3391
|
const allowedTiers = [1, 2, 3];
|
|
3021
3392
|
|
|
3022
3393
|
if (logPrefix) {
|
|
@@ -3026,17 +3397,20 @@ async function getInstancesOrderedByStartPrio(objects, logger, logPrefix = '') {
|
|
|
3026
3397
|
|
|
3027
3398
|
let doc = {};
|
|
3028
3399
|
try {
|
|
3029
|
-
doc = await objects.getObjectViewAsync('system', 'instance'
|
|
3400
|
+
doc = await objects.getObjectViewAsync('system', 'instance', {
|
|
3401
|
+
startkey: 'system.adapter.',
|
|
3402
|
+
endkey: 'system.adapter.\u9999'
|
|
3403
|
+
});
|
|
3030
3404
|
} catch (e) {
|
|
3031
|
-
if (e.message.startsWith('Cannot find ')) {
|
|
3032
|
-
logger.error(`${logPrefix}_design/system missing - call node ${
|
|
3405
|
+
if (e.message && e.message.startsWith('Cannot find ')) {
|
|
3406
|
+
logger.error(`${logPrefix} _design/system missing - call node ${module.exports.appName}.js setup`);
|
|
3033
3407
|
} else {
|
|
3034
|
-
logger.error(`${logPrefix}Can not get instances: ${e.message}`);
|
|
3408
|
+
logger.error(`${logPrefix} Can not get instances: ${e.message}`);
|
|
3035
3409
|
}
|
|
3036
3410
|
}
|
|
3037
3411
|
|
|
3038
3412
|
if (!doc.rows || doc.rows.length === 0) {
|
|
3039
|
-
logger.info(`${logPrefix}no instances found`);
|
|
3413
|
+
logger.info(`${logPrefix} no instances found`);
|
|
3040
3414
|
} else {
|
|
3041
3415
|
for (const row of doc.rows) {
|
|
3042
3416
|
if (row && row.value) {
|
|
@@ -3095,6 +3469,173 @@ async function setExecutableCapabilities(execPath, capabilities, modeEffective,
|
|
|
3095
3469
|
}
|
|
3096
3470
|
}
|
|
3097
3471
|
|
|
3472
|
+
/**
|
|
3473
|
+
* Requests the licenses from ioBroker.net
|
|
3474
|
+
* @param {string} login Login for ioBroker.net
|
|
3475
|
+
* @param {string} password Decoded password for ioBroker.net
|
|
3476
|
+
* @returns {Promise<object[]>} array of all licenses stored on iobroker.net
|
|
3477
|
+
*/
|
|
3478
|
+
async function _readLicenses(login, password) {
|
|
3479
|
+
axios = axios || require('axios');
|
|
3480
|
+
const config = {
|
|
3481
|
+
headers: { Authorization: `Basic ${Buffer.from(login + ':' + password).toString('base64')}` },
|
|
3482
|
+
timeout: 4000
|
|
3483
|
+
};
|
|
3484
|
+
|
|
3485
|
+
try {
|
|
3486
|
+
const response = await axios.get(`https://iobroker.net:3001/api/v1/licenses`, config);
|
|
3487
|
+
if (response.data && response.data.length) {
|
|
3488
|
+
const now = Date.now();
|
|
3489
|
+
response.data = response.data.filter(
|
|
3490
|
+
license =>
|
|
3491
|
+
!license.validTill ||
|
|
3492
|
+
license.validTill === '0000-00-00 00:00:00' ||
|
|
3493
|
+
new Date(license.validTill).getTime() > now
|
|
3494
|
+
);
|
|
3495
|
+
}
|
|
3496
|
+
|
|
3497
|
+
return response.data;
|
|
3498
|
+
} catch (err) {
|
|
3499
|
+
if (err.response) {
|
|
3500
|
+
throw new Error((err.response.data && err.response.data.error) || err.response.data || err.response.status);
|
|
3501
|
+
} else if (err.request) {
|
|
3502
|
+
throw new Error('no response');
|
|
3503
|
+
} else {
|
|
3504
|
+
throw err;
|
|
3505
|
+
}
|
|
3506
|
+
}
|
|
3507
|
+
}
|
|
3508
|
+
|
|
3509
|
+
/**
|
|
3510
|
+
* Reads the licenses from iobroker.net
|
|
3511
|
+
* Reads the licenses from iobroker.net and if no login/password provided stores it in system.licenses
|
|
3512
|
+
* @param {object} objects Object store instance
|
|
3513
|
+
* @param {string} login Login for ioBroker.net
|
|
3514
|
+
* @param {string} password Decoded password for ioBroker.net
|
|
3515
|
+
* @returns {Promise<object[]>} array of all licenses stored on iobroker.net
|
|
3516
|
+
*/
|
|
3517
|
+
async function updateLicenses(objects, login, password) {
|
|
3518
|
+
// if login and password provided in the message, just try to read without saving it in system.licenses
|
|
3519
|
+
if (login && password) {
|
|
3520
|
+
return _readLicenses(login, password);
|
|
3521
|
+
} else {
|
|
3522
|
+
// get actual object
|
|
3523
|
+
const systemLicenses = await objects.getObjectAsync('system.licenses');
|
|
3524
|
+
// If password and login exist
|
|
3525
|
+
if (systemLicenses && systemLicenses.native && systemLicenses.native.password && systemLicenses.native.login) {
|
|
3526
|
+
try {
|
|
3527
|
+
// get the secret to decode the password
|
|
3528
|
+
const systemConfig = await objects.getObjectAsync('system.config');
|
|
3529
|
+
|
|
3530
|
+
// decode the password
|
|
3531
|
+
let password;
|
|
3532
|
+
try {
|
|
3533
|
+
password = decrypt(systemConfig.native.secret, systemLicenses.native.password);
|
|
3534
|
+
} catch (err) {
|
|
3535
|
+
throw new Error(`Cannot decode password: ${err.message}`);
|
|
3536
|
+
}
|
|
3537
|
+
|
|
3538
|
+
// read licenses from iobroker.net
|
|
3539
|
+
const licenses = await _readLicenses(systemLicenses.native.login, password);
|
|
3540
|
+
// save licenses to system.licenses and remember the time
|
|
3541
|
+
// merge the information together
|
|
3542
|
+
const oldLicenses = systemLicenses.native.licenses || [];
|
|
3543
|
+
systemLicenses.native.licenses = licenses;
|
|
3544
|
+
oldLicenses.forEach(oldLicense => {
|
|
3545
|
+
if (oldLicense.usedBy) {
|
|
3546
|
+
const newLicense = licenses.find(item => item.json === oldLicense.json);
|
|
3547
|
+
if (newLicense) {
|
|
3548
|
+
newLicense.usedBy = oldLicense.usedBy;
|
|
3549
|
+
}
|
|
3550
|
+
}
|
|
3551
|
+
});
|
|
3552
|
+
|
|
3553
|
+
systemLicenses.native.readTime = new Date().toISOString();
|
|
3554
|
+
|
|
3555
|
+
// update read time
|
|
3556
|
+
await objects.setObjectAsync('system.licenses', systemLicenses);
|
|
3557
|
+
return licenses;
|
|
3558
|
+
} catch (err) {
|
|
3559
|
+
// if password is invalid
|
|
3560
|
+
if (
|
|
3561
|
+
err.message.includes('Authentication required') ||
|
|
3562
|
+
err.message.includes('Cannot decode password:')
|
|
3563
|
+
) {
|
|
3564
|
+
// clear existing licenses if exist
|
|
3565
|
+
if (
|
|
3566
|
+
systemLicenses &&
|
|
3567
|
+
systemLicenses.native &&
|
|
3568
|
+
systemLicenses.native.licenses &&
|
|
3569
|
+
systemLicenses.native.licenses.length
|
|
3570
|
+
) {
|
|
3571
|
+
systemLicenses.native.licenses = [];
|
|
3572
|
+
systemLicenses.native.readTime = new Date().toISOString();
|
|
3573
|
+
await objects.setObjectAsync('system.licenses', systemLicenses);
|
|
3574
|
+
}
|
|
3575
|
+
}
|
|
3576
|
+
|
|
3577
|
+
throw err;
|
|
3578
|
+
}
|
|
3579
|
+
} else {
|
|
3580
|
+
// if password or login are empty => clear existing licenses if exist
|
|
3581
|
+
if (
|
|
3582
|
+
systemLicenses &&
|
|
3583
|
+
systemLicenses.native &&
|
|
3584
|
+
systemLicenses.native.licenses &&
|
|
3585
|
+
systemLicenses.native.licenses.length
|
|
3586
|
+
) {
|
|
3587
|
+
systemLicenses.native.licenses = [];
|
|
3588
|
+
systemLicenses.native.readTime = new Date().toISOString();
|
|
3589
|
+
await objects.setObjectAsync('system.licenses', systemLicenses);
|
|
3590
|
+
}
|
|
3591
|
+
throw new Error('No password or login');
|
|
3592
|
+
}
|
|
3593
|
+
}
|
|
3594
|
+
}
|
|
3595
|
+
|
|
3596
|
+
/**
|
|
3597
|
+
* @typedef {object} GZipFileOptions
|
|
3598
|
+
* @property {boolean} [deleteInput] Delete the input file after compression. Default: false.
|
|
3599
|
+
*/
|
|
3600
|
+
|
|
3601
|
+
/**
|
|
3602
|
+
* Compresses an input file using GZip and writes it somewhere else
|
|
3603
|
+
* @param {string} inputFilename The filename of the input file that should be gzipped
|
|
3604
|
+
* @param {string} outputFilename The filename of the output file where the gzipped content should be written to
|
|
3605
|
+
* @param {GZipFileOptions} [options] Options for the compression
|
|
3606
|
+
* @returns {Promise<void>}
|
|
3607
|
+
*/
|
|
3608
|
+
function compressFileGZip(inputFilename, outputFilename, options = {}) {
|
|
3609
|
+
const { deleteInput = false } = options;
|
|
3610
|
+
|
|
3611
|
+
return new Promise((resolve, reject) => {
|
|
3612
|
+
const input = fs.createReadStream(inputFilename);
|
|
3613
|
+
const output = fs.createWriteStream(outputFilename);
|
|
3614
|
+
const compress = zlib.createGzip();
|
|
3615
|
+
input.on('error', err => {
|
|
3616
|
+
reject(err);
|
|
3617
|
+
});
|
|
3618
|
+
output.on('error', err => {
|
|
3619
|
+
reject(err);
|
|
3620
|
+
});
|
|
3621
|
+
compress.on('error', err => {
|
|
3622
|
+
reject(err);
|
|
3623
|
+
});
|
|
3624
|
+
output.on('close', () => {
|
|
3625
|
+
if (deleteInput) {
|
|
3626
|
+
try {
|
|
3627
|
+
fs.unlinkSync(inputFilename);
|
|
3628
|
+
} catch {
|
|
3629
|
+
// Ignore
|
|
3630
|
+
}
|
|
3631
|
+
}
|
|
3632
|
+
resolve();
|
|
3633
|
+
});
|
|
3634
|
+
|
|
3635
|
+
input.pipe(compress).pipe(output);
|
|
3636
|
+
});
|
|
3637
|
+
}
|
|
3638
|
+
|
|
3098
3639
|
const ERROR_NOT_FOUND = 'Not exists';
|
|
3099
3640
|
const ERROR_EMPTY_OBJECT = 'null object';
|
|
3100
3641
|
const ERROR_NO_OBJECT = 'no object';
|
|
@@ -3109,9 +3650,7 @@ module.exports = {
|
|
|
3109
3650
|
generateDefaultCertificates,
|
|
3110
3651
|
getAdapterDir,
|
|
3111
3652
|
getInstances,
|
|
3112
|
-
getInstancesAsync,
|
|
3113
3653
|
getAllInstances,
|
|
3114
|
-
getAllInstancesAsync,
|
|
3115
3654
|
getCertificateInfo,
|
|
3116
3655
|
getConfigFileName,
|
|
3117
3656
|
getDefaultDataDir,
|
|
@@ -3123,6 +3662,7 @@ module.exports = {
|
|
|
3123
3662
|
getInstanceIndicatorObjects,
|
|
3124
3663
|
getIoPack,
|
|
3125
3664
|
getJson,
|
|
3665
|
+
getJsonAsync,
|
|
3126
3666
|
getInstancesOrderedByStartPrio,
|
|
3127
3667
|
getRepositoryFile,
|
|
3128
3668
|
getRepositoryFileAsync,
|
|
@@ -3160,11 +3700,17 @@ module.exports = {
|
|
|
3160
3700
|
parseShortGithubUrl,
|
|
3161
3701
|
setExecutableCapabilities,
|
|
3162
3702
|
isGithubPathname,
|
|
3703
|
+
isSingleHost,
|
|
3704
|
+
isHostRunning,
|
|
3163
3705
|
parseGithubPathname,
|
|
3164
3706
|
removePreservedProperties,
|
|
3165
3707
|
FORBIDDEN_CHARS,
|
|
3166
3708
|
getControllerDir,
|
|
3167
3709
|
getLogger,
|
|
3710
|
+
getAllEnums,
|
|
3711
|
+
updateLicenses,
|
|
3712
|
+
compressFileGZip,
|
|
3713
|
+
isDocker,
|
|
3168
3714
|
ERRORS: {
|
|
3169
3715
|
ERROR_NOT_FOUND: ERROR_NOT_FOUND,
|
|
3170
3716
|
ERROR_EMPTY_OBJECT: ERROR_EMPTY_OBJECT,
|