@iobroker/js-controller-common 4.0.21 → 4.1.0-alpha.12-20220314-28d0dece
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/build/index.d.ts +5 -0
- package/build/index.js +30 -0
- package/build/lib/common/exitCodes.d.ts +58 -0
- package/build/lib/common/exitCodes.d.ts.map +1 -0
- package/build/lib/common/exitCodes.js +62 -0
- package/build/lib/common/exitCodes.js.map +1 -0
- package/build/lib/common/logger.d.ts +14 -0
- package/build/lib/common/logger.d.ts.map +1 -0
- package/{lib → build/lib}/common/logger.js +187 -193
- package/build/lib/common/logger.js.map +1 -0
- package/build/lib/common/password.d.ts +37 -0
- package/build/lib/common/password.d.ts.map +1 -0
- package/{lib → build/lib}/common/password.js +26 -46
- package/build/lib/common/password.js.map +1 -0
- package/build/lib/common/tools.d.ts +568 -0
- package/build/lib/common/tools.d.ts.map +1 -0
- package/{lib → build/lib}/common/tools.js +1110 -1475
- package/build/lib/common/tools.js.map +1 -0
- package/package.json +13 -9
- package/index.js +0 -6
- package/lib/common/exitCodes.js +0 -65
|
@@ -1,31 +1,43 @@
|
|
|
1
|
-
|
|
2
|
-
|
|
3
|
-
|
|
4
|
-
|
|
5
|
-
|
|
6
|
-
|
|
7
|
-
|
|
8
|
-
const
|
|
9
|
-
const
|
|
10
|
-
const
|
|
11
|
-
const
|
|
12
|
-
const
|
|
13
|
-
const
|
|
14
|
-
const
|
|
15
|
-
const
|
|
16
|
-
|
|
17
|
-
|
|
18
|
-
require(
|
|
19
|
-
|
|
20
|
-
|
|
21
|
-
|
|
22
|
-
|
|
1
|
+
"use strict";
|
|
2
|
+
var __importDefault = (this && this.__importDefault) || function (mod) {
|
|
3
|
+
return (mod && mod.__esModule) ? mod : { "default": mod };
|
|
4
|
+
};
|
|
5
|
+
Object.defineProperty(exports, "__esModule", { value: true });
|
|
6
|
+
exports.maybeCallback = exports.getInstances = exports.getAllEnums = exports.getAllInstances = exports.validateGeneralObjectProperties = exports.parseDependencies = exports.removeIdFromAllEnums = exports.formatAliasValue = exports.measureEventLoopLag = exports.isArray = exports.isObject = exports.decrypt = exports.encrypt = exports.appendStackTrace = exports.pattern2RegEx = exports.setQualityForInstance = exports.promiseSequence = exports.promisifyNoError = exports.promisify = exports.getConfigFileName = exports.getDefaultDataDir = exports.getControllerDir = exports.getHostInfo = exports.generateDefaultCertificates = exports.getCertificateInfo = exports.getDiskInfo = exports.rebuildNodeModules = exports.uninstallNodeModule = exports.installNodeModule = exports.getHostName = exports.getAdapterDir = exports.sendDiagInfo = exports.getRepositoryFileAsync = exports.getRepositoryFile = exports.getInstalledInfo = exports.getJsonAsync = exports.getJson = exports.getFile = exports.createUuid = exports.isDocker = exports.findIPs = exports.rmdirRecursiveSync = exports.appName = exports.isHostRunning = exports.isSingleHost = exports.decryptPhrase = exports.upToDate = exports.checkNonEditable = exports.FORBIDDEN_CHARS = exports.ERRORS = void 0;
|
|
7
|
+
exports.compressFileGZip = exports.updateLicenses = exports.setExecutableCapabilities = exports.getInstancesOrderedByStartPrio = exports.getLogger = exports.getInstanceIndicatorObjects = exports.removePreservedProperties = exports.parseGithubPathname = exports.isGithubPathname = exports.parseShortGithubUrl = exports.isShortGithubUrl = exports.getDefaultNodeArgs = exports.resolveAdapterMainFile = exports.pipeLinewise = exports.execAsync = exports.maybeCallbackWithError = void 0;
|
|
8
|
+
const fs_extra_1 = __importDefault(require("fs-extra"));
|
|
9
|
+
const path_1 = __importDefault(require("path"));
|
|
10
|
+
const semver_1 = __importDefault(require("semver"));
|
|
11
|
+
const os_1 = __importDefault(require("os"));
|
|
12
|
+
const node_forge_1 = __importDefault(require("node-forge"));
|
|
13
|
+
const deep_clone_1 = __importDefault(require("deep-clone"));
|
|
14
|
+
const promisify_child_process_1 = __importDefault(require("promisify-child-process"));
|
|
15
|
+
const readline_1 = require("readline");
|
|
16
|
+
const stream_1 = require("stream");
|
|
17
|
+
const pak_1 = require("@alcalzone/pak");
|
|
18
|
+
const exitCodes_1 = require("./exitCodes");
|
|
19
|
+
const zlib_1 = __importDefault(require("zlib"));
|
|
20
|
+
const password_1 = require("./password");
|
|
21
|
+
const jsonwebtoken_1 = __importDefault(require("jsonwebtoken"));
|
|
22
|
+
const axios_1 = __importDefault(require("axios"));
|
|
23
|
+
const crypto_1 = __importDefault(require("crypto"));
|
|
24
|
+
const child_process_1 = require("child_process");
|
|
25
|
+
const url_1 = require("url");
|
|
26
|
+
const events_1 = __importDefault(require("events"));
|
|
27
|
+
// eslint-disable-next-line @typescript-eslint/no-var-requires
|
|
28
|
+
const extend = require('node.extend');
|
|
29
|
+
var ERRORS;
|
|
30
|
+
(function (ERRORS) {
|
|
31
|
+
ERRORS["ERROR_NOT_FOUND"] = "Not exists";
|
|
32
|
+
ERRORS["ERROR_EMPTY_OBJECT"] = "null object";
|
|
33
|
+
ERRORS["ERROR_NO_OBJECT"] = "no object";
|
|
34
|
+
ERRORS["ERROR_DB_CLOSED"] = "DB closed";
|
|
35
|
+
})(ERRORS = exports.ERRORS || (exports.ERRORS = {}));
|
|
36
|
+
events_1.default.EventEmitter.prototype.setMaxListeners(100);
|
|
23
37
|
let npmVersion;
|
|
24
|
-
let crypto;
|
|
25
38
|
let diskusage;
|
|
26
39
|
const randomID = Math.round(Math.random() * 10000000000000); // Used for creation of User-Agent
|
|
27
40
|
const VENDOR_FILE = '/etc/iob-vendor.json';
|
|
28
|
-
|
|
29
41
|
let lastCalculationOfIps;
|
|
30
42
|
let ownIpArr = [];
|
|
31
43
|
// Here we define all characters that are forbidden in IDs. Since we want to allow multiple
|
|
@@ -41,59 +53,54 @@ let ownIpArr = [];
|
|
|
41
53
|
// · " :!#$%&()+=@^{}|~" (for legacy reasons)
|
|
42
54
|
//
|
|
43
55
|
/** All characters that may not appear in an object ID. */
|
|
44
|
-
|
|
45
|
-
|
|
56
|
+
exports.FORBIDDEN_CHARS = /[^._\-/ :!#$%&()+=@^{}|~\p{Ll}\p{Lu}\p{Nd}]+/gu;
|
|
46
57
|
/**
|
|
47
58
|
* recursively copy values from old object to new one
|
|
48
59
|
*
|
|
49
60
|
* @alias copyAttributes
|
|
50
61
|
* @memberof tools
|
|
51
|
-
* @param
|
|
52
|
-
* @param
|
|
53
|
-
* @param
|
|
54
|
-
* @param
|
|
62
|
+
* @param oldObj source object
|
|
63
|
+
* @param newObj destination object
|
|
64
|
+
* @param originalObj optional object for read __no_change__ values
|
|
65
|
+
* @param isNonEdit optional indicator if copy is in nonEdit part
|
|
55
66
|
*
|
|
56
67
|
*/
|
|
57
68
|
function copyAttributes(oldObj, newObj, originalObj, isNonEdit) {
|
|
58
69
|
for (const attr of Object.keys(oldObj)) {
|
|
59
|
-
if (
|
|
60
|
-
oldObj[attr] === undefined ||
|
|
70
|
+
if (oldObj[attr] === undefined ||
|
|
61
71
|
oldObj[attr] === null ||
|
|
62
72
|
typeof oldObj[attr] !== 'object' ||
|
|
63
|
-
oldObj[attr] instanceof Array
|
|
64
|
-
) {
|
|
73
|
+
oldObj[attr] instanceof Array) {
|
|
65
74
|
if (oldObj[attr] === '__no_change__' && originalObj && !isNonEdit) {
|
|
66
75
|
if (originalObj[attr] !== undefined) {
|
|
67
|
-
newObj[attr] =
|
|
68
|
-
}
|
|
76
|
+
newObj[attr] = (0, deep_clone_1.default)(originalObj[attr]);
|
|
77
|
+
}
|
|
78
|
+
else {
|
|
69
79
|
console.log(`Attribute ${attr} ignored by copying`);
|
|
70
80
|
}
|
|
71
|
-
}
|
|
81
|
+
}
|
|
82
|
+
else if (oldObj[attr] === '__delete__' && !isNonEdit) {
|
|
72
83
|
if (newObj[attr] !== undefined) {
|
|
73
84
|
delete newObj[attr];
|
|
74
85
|
}
|
|
75
|
-
}
|
|
86
|
+
}
|
|
87
|
+
else {
|
|
76
88
|
newObj[attr] = oldObj[attr];
|
|
77
89
|
}
|
|
78
|
-
}
|
|
90
|
+
}
|
|
91
|
+
else {
|
|
79
92
|
newObj[attr] = newObj[attr] || {};
|
|
80
|
-
copyAttributes(
|
|
81
|
-
oldObj[attr],
|
|
82
|
-
newObj[attr],
|
|
83
|
-
originalObj && originalObj[attr],
|
|
84
|
-
isNonEdit || attr === 'nonEdit'
|
|
85
|
-
);
|
|
93
|
+
copyAttributes(oldObj[attr], newObj[attr], originalObj && originalObj[attr], isNonEdit || attr === 'nonEdit');
|
|
86
94
|
}
|
|
87
95
|
}
|
|
88
96
|
}
|
|
89
|
-
|
|
90
97
|
/**
|
|
91
98
|
* Checks the flag nonEdit and restores non-changeable values if required
|
|
92
99
|
*
|
|
93
100
|
* @alias checkNonEditable
|
|
94
101
|
* @memberof tools
|
|
95
|
-
* @param
|
|
96
|
-
* @param
|
|
102
|
+
* @param oldObject source object
|
|
103
|
+
* @param newObject destination object
|
|
97
104
|
*
|
|
98
105
|
*/
|
|
99
106
|
function checkNonEditable(oldObject, newObject) {
|
|
@@ -103,51 +110,47 @@ function checkNonEditable(oldObject, newObject) {
|
|
|
103
110
|
if (!oldObject.nonEdit && !newObject.nonEdit) {
|
|
104
111
|
return true;
|
|
105
112
|
}
|
|
106
|
-
|
|
107
113
|
// if nonEdit is protected with password
|
|
108
114
|
if (oldObject.nonEdit && oldObject.nonEdit.passHash) {
|
|
109
115
|
// If new Object wants to update the nonEdit information
|
|
110
116
|
if (newObject.nonEdit && newObject.nonEdit.password) {
|
|
111
|
-
|
|
112
|
-
const hash = crypto.createHash('sha256').update(newObject.nonEdit.password.toString()).digest('base64');
|
|
117
|
+
const hash = crypto_1.default.createHash('sha256').update(newObject.nonEdit.password.toString()).digest('base64');
|
|
113
118
|
if (oldObject.nonEdit.passHash !== hash) {
|
|
114
119
|
delete newObject.nonEdit;
|
|
115
120
|
return false;
|
|
116
|
-
}
|
|
117
|
-
|
|
121
|
+
}
|
|
122
|
+
else {
|
|
123
|
+
oldObject.nonEdit = (0, deep_clone_1.default)(newObject.nonEdit);
|
|
118
124
|
delete oldObject.nonEdit.password;
|
|
119
125
|
delete newObject.nonEdit.password;
|
|
120
126
|
oldObject.nonEdit.passHash = hash;
|
|
121
127
|
newObject.nonEdit.passHash = hash;
|
|
122
128
|
}
|
|
123
129
|
copyAttributes(newObject.nonEdit, newObject, newObject);
|
|
124
|
-
|
|
125
130
|
if (newObject.passHash) {
|
|
126
131
|
delete newObject.passHash;
|
|
127
132
|
}
|
|
128
133
|
if (newObject.nonEdit && newObject.nonEdit.password) {
|
|
129
134
|
delete newObject.nonEdit.password;
|
|
130
135
|
}
|
|
131
|
-
|
|
132
136
|
return true;
|
|
133
|
-
}
|
|
137
|
+
}
|
|
138
|
+
else {
|
|
134
139
|
newObject.nonEdit = oldObject.nonEdit;
|
|
135
140
|
}
|
|
136
|
-
}
|
|
137
|
-
|
|
141
|
+
}
|
|
142
|
+
else if (newObject.nonEdit) {
|
|
143
|
+
oldObject.nonEdit = (0, deep_clone_1.default)(newObject.nonEdit);
|
|
138
144
|
if (newObject.nonEdit.password) {
|
|
139
|
-
|
|
140
|
-
const hash = crypto.createHash('sha256').update(newObject.nonEdit.password.toString()).digest('base64');
|
|
145
|
+
const hash = crypto_1.default.createHash('sha256').update(newObject.nonEdit.password.toString()).digest('base64');
|
|
141
146
|
delete oldObject.nonEdit.password;
|
|
142
147
|
delete newObject.nonEdit.password;
|
|
143
148
|
oldObject.nonEdit.passHash = hash;
|
|
144
149
|
newObject.nonEdit.passHash = hash;
|
|
145
150
|
}
|
|
146
151
|
}
|
|
147
|
-
|
|
148
152
|
// restore settings
|
|
149
153
|
copyAttributes(oldObject.nonEdit, newObject, oldObject);
|
|
150
|
-
|
|
151
154
|
if (newObject.passHash) {
|
|
152
155
|
delete newObject.passHash;
|
|
153
156
|
}
|
|
@@ -156,22 +159,20 @@ function checkNonEditable(oldObject, newObject) {
|
|
|
156
159
|
}
|
|
157
160
|
return true;
|
|
158
161
|
}
|
|
159
|
-
|
|
162
|
+
exports.checkNonEditable = checkNonEditable;
|
|
160
163
|
/**
|
|
161
|
-
* @param
|
|
162
|
-
* @param
|
|
164
|
+
* @param repoVersion
|
|
165
|
+
* @param installedVersion
|
|
163
166
|
* @throws {Error} if version is invalid
|
|
164
167
|
*/
|
|
165
168
|
function upToDate(repoVersion, installedVersion) {
|
|
166
169
|
// Check if the installed version is at least the repo version
|
|
167
|
-
return
|
|
170
|
+
return semver_1.default.gte(installedVersion, repoVersion);
|
|
168
171
|
}
|
|
169
|
-
|
|
172
|
+
exports.upToDate = upToDate;
|
|
170
173
|
// TODO: this is only here for backward compatibility, if MULTIHOST password was still setup with old decryption
|
|
171
174
|
function decryptPhrase(password, data, callback) {
|
|
172
|
-
|
|
173
|
-
const decipher = crypto.createDecipher('aes192', password);
|
|
174
|
-
|
|
175
|
+
const decipher = crypto_1.default.createDecipher('aes192', password);
|
|
175
176
|
try {
|
|
176
177
|
let decrypted = '';
|
|
177
178
|
decipher.on('readable', () => {
|
|
@@ -184,43 +185,46 @@ function decryptPhrase(password, data, callback) {
|
|
|
184
185
|
console.error('Cannot decode secret: ' + error);
|
|
185
186
|
callback(null);
|
|
186
187
|
});
|
|
187
|
-
|
|
188
188
|
decipher.on('end', () => callback(decrypted));
|
|
189
|
-
|
|
190
189
|
decipher.write(data, 'hex');
|
|
191
190
|
decipher.end();
|
|
192
|
-
}
|
|
191
|
+
}
|
|
192
|
+
catch (e) {
|
|
193
193
|
console.error(`Cannot decode secret: ${e.message}`);
|
|
194
194
|
callback(null);
|
|
195
195
|
}
|
|
196
196
|
}
|
|
197
|
-
|
|
197
|
+
exports.decryptPhrase = decryptPhrase;
|
|
198
198
|
/**
|
|
199
199
|
* Checks if multiple host objects exists, without using object views
|
|
200
200
|
*
|
|
201
|
-
* @param
|
|
202
|
-
* @return
|
|
201
|
+
* @param objects the objects db
|
|
202
|
+
* @return true if only one host object exists
|
|
203
203
|
*/
|
|
204
204
|
async function isSingleHost(objects) {
|
|
205
|
-
const res = await objects.getObjectList({
|
|
205
|
+
const res = await objects.getObjectList({
|
|
206
|
+
startkey: 'system.host.',
|
|
207
|
+
endkey: 'system.host.\u9999'
|
|
208
|
+
});
|
|
206
209
|
const hostObjs = res.rows.filter(obj => obj.value && obj.value.type === 'host');
|
|
207
210
|
return hostObjs.length <= 1; // on setup no host object is there yet
|
|
208
211
|
}
|
|
209
|
-
|
|
212
|
+
exports.isSingleHost = isSingleHost;
|
|
210
213
|
/**
|
|
211
214
|
* Checks if at least one host is running in a MH environment
|
|
212
215
|
*
|
|
213
|
-
* @param
|
|
214
|
-
* @param
|
|
215
|
-
* @return
|
|
216
|
+
* @param objects the objects db
|
|
217
|
+
* @param states the states db
|
|
218
|
+
* @return true if one or more hosts running else false
|
|
216
219
|
*/
|
|
217
220
|
async function isHostRunning(objects, states) {
|
|
218
221
|
// do it without object view for now, can be reverted if no one downgrades to < 4 (redis-sets)
|
|
219
222
|
// const res = await objects.getObjectViewAsync('system', 'host', { startkey: '', endkey: '\u9999' });
|
|
220
|
-
|
|
221
|
-
|
|
223
|
+
const res = await objects.getObjectList({
|
|
224
|
+
startkey: 'system.host.',
|
|
225
|
+
endkey: 'system.host.\u9999'
|
|
226
|
+
});
|
|
222
227
|
res.rows = res.rows.filter(obj => obj.value && obj.value.type === 'host');
|
|
223
|
-
|
|
224
228
|
for (const hostObj of res.rows) {
|
|
225
229
|
const state = await states.getState(`${hostObj.id}.alive`);
|
|
226
230
|
if (state && state.val) {
|
|
@@ -229,102 +233,111 @@ async function isHostRunning(objects, states) {
|
|
|
229
233
|
}
|
|
230
234
|
return false;
|
|
231
235
|
}
|
|
232
|
-
|
|
236
|
+
exports.isHostRunning = isHostRunning;
|
|
237
|
+
/**
|
|
238
|
+
* Checks if ioBroker is installed in a dev environment
|
|
239
|
+
* @private
|
|
240
|
+
* @return {boolean}
|
|
241
|
+
*/
|
|
242
|
+
function _isDevInstallation() {
|
|
243
|
+
return fs_extra_1.default.pathExistsSync(`${__dirname}/../../../../../packages/controller`);
|
|
244
|
+
}
|
|
233
245
|
function getAppName() {
|
|
234
|
-
if (
|
|
246
|
+
if (_isDevInstallation()) {
|
|
235
247
|
// dev install - GitHub folder is uppercase
|
|
236
248
|
return 'ioBroker';
|
|
237
249
|
}
|
|
238
|
-
|
|
239
250
|
return 'iobroker';
|
|
240
251
|
}
|
|
241
|
-
|
|
252
|
+
exports.appName = getAppName();
|
|
242
253
|
function rmdirRecursiveSync(path) {
|
|
243
|
-
if (
|
|
244
|
-
|
|
254
|
+
if (fs_extra_1.default.existsSync(path)) {
|
|
255
|
+
fs_extra_1.default.readdirSync(path).forEach(file => {
|
|
245
256
|
const curPath = `${path}/${file}`;
|
|
246
257
|
try {
|
|
247
|
-
if (
|
|
258
|
+
if (fs_extra_1.default.statSync(curPath).isDirectory()) {
|
|
248
259
|
// recurse
|
|
249
260
|
rmdirRecursiveSync(curPath);
|
|
250
|
-
}
|
|
261
|
+
}
|
|
262
|
+
else {
|
|
251
263
|
// delete file
|
|
252
|
-
|
|
264
|
+
fs_extra_1.default.unlinkSync(curPath);
|
|
253
265
|
}
|
|
254
|
-
}
|
|
266
|
+
}
|
|
267
|
+
catch (e) {
|
|
255
268
|
if (e.code !== 'ENOENT') {
|
|
256
269
|
console.log(`Cannot delete file ${path}: ${e.message}`);
|
|
257
|
-
}
|
|
270
|
+
}
|
|
271
|
+
else {
|
|
258
272
|
throw e;
|
|
259
273
|
}
|
|
260
274
|
}
|
|
261
275
|
});
|
|
262
276
|
// delete (hopefully) empty folder
|
|
263
277
|
try {
|
|
264
|
-
|
|
265
|
-
}
|
|
278
|
+
fs_extra_1.default.rmdirSync(path);
|
|
279
|
+
}
|
|
280
|
+
catch (e) {
|
|
266
281
|
if (e.code !== 'ENOENT') {
|
|
267
282
|
console.log(`Cannot delete directory ${path}: ${e.message}`);
|
|
268
|
-
}
|
|
283
|
+
}
|
|
284
|
+
else {
|
|
269
285
|
throw e;
|
|
270
286
|
}
|
|
271
287
|
}
|
|
272
288
|
}
|
|
273
289
|
}
|
|
274
|
-
|
|
290
|
+
exports.rmdirRecursiveSync = rmdirRecursiveSync;
|
|
275
291
|
function findIPs() {
|
|
276
292
|
if (!lastCalculationOfIps || Date.now() - lastCalculationOfIps > 10000) {
|
|
277
293
|
lastCalculationOfIps = Date.now();
|
|
278
294
|
ownIpArr = [];
|
|
279
295
|
try {
|
|
280
|
-
const ifaces =
|
|
281
|
-
Object.
|
|
282
|
-
|
|
283
|
-
|
|
284
|
-
|
|
285
|
-
|
|
286
|
-
)
|
|
287
|
-
);
|
|
288
|
-
} catch (e) {
|
|
296
|
+
const ifaces = os_1.default.networkInterfaces();
|
|
297
|
+
for (const iface of Object.values(ifaces)) {
|
|
298
|
+
iface === null || iface === void 0 ? void 0 : iface.forEach(details => !details.internal && ownIpArr.push(details.address));
|
|
299
|
+
}
|
|
300
|
+
}
|
|
301
|
+
catch (e) {
|
|
289
302
|
console.error(`Can not find local IPs: ${e.message}`);
|
|
290
303
|
}
|
|
291
304
|
}
|
|
292
|
-
|
|
293
305
|
return ownIpArr;
|
|
294
306
|
}
|
|
295
|
-
|
|
307
|
+
exports.findIPs = findIPs;
|
|
296
308
|
function findPath(path, url) {
|
|
297
309
|
if (!url) {
|
|
298
310
|
return '';
|
|
299
311
|
}
|
|
300
|
-
if (url.
|
|
312
|
+
if (url.startsWith('http://') || url.startsWith('https://')) {
|
|
301
313
|
return url;
|
|
302
|
-
}
|
|
303
|
-
|
|
314
|
+
}
|
|
315
|
+
else {
|
|
316
|
+
if (path.startsWith('http://') || path.startsWith('https://')) {
|
|
304
317
|
return (path + url).replace(/\/\//g, '/').replace('http:/', 'http://').replace('https:/', 'https://');
|
|
305
|
-
}
|
|
318
|
+
}
|
|
319
|
+
else {
|
|
306
320
|
if (url[0] === '/') {
|
|
307
|
-
return __dirname
|
|
308
|
-
}
|
|
309
|
-
|
|
321
|
+
return `${__dirname}/..${url}`;
|
|
322
|
+
}
|
|
323
|
+
else {
|
|
324
|
+
return `${__dirname}/../${path}${url}`;
|
|
310
325
|
}
|
|
311
326
|
}
|
|
312
327
|
}
|
|
313
328
|
}
|
|
314
|
-
|
|
315
329
|
function getMac(callback) {
|
|
316
330
|
const macRegex = /(?:[a-z0-9]{2}[:-]){5}[a-z0-9]{2}/gi;
|
|
317
331
|
const zeroRegex = /(?:[0]{2}[:-]){5}[0]{2}/;
|
|
318
332
|
const command = process.platform.indexOf('win') === 0 ? 'getmac' : 'ifconfig || ip link';
|
|
319
|
-
|
|
320
|
-
require('child_process').exec(command, { windowsHide: true }, (err, stdout, _stderr) => {
|
|
333
|
+
(0, child_process_1.exec)(command, { windowsHide: true }, (err, stdout, _stderr) => {
|
|
321
334
|
if (err) {
|
|
322
335
|
callback(err);
|
|
323
|
-
}
|
|
336
|
+
}
|
|
337
|
+
else {
|
|
324
338
|
let macAddress;
|
|
325
339
|
let match;
|
|
326
340
|
let result = null;
|
|
327
|
-
|
|
328
341
|
while (true) {
|
|
329
342
|
match = macRegex.exec(stdout);
|
|
330
343
|
if (!match) {
|
|
@@ -335,92 +348,86 @@ function getMac(callback) {
|
|
|
335
348
|
result = macAddress;
|
|
336
349
|
}
|
|
337
350
|
}
|
|
338
|
-
|
|
339
351
|
if (result === null) {
|
|
340
352
|
callback(new Error('could not determine the mac address from:\n' + stdout));
|
|
341
|
-
}
|
|
353
|
+
}
|
|
354
|
+
else {
|
|
342
355
|
callback(null, result.replace(/-/g, ':').toLowerCase());
|
|
343
356
|
}
|
|
344
357
|
}
|
|
345
358
|
});
|
|
346
359
|
}
|
|
347
|
-
|
|
348
360
|
/**
|
|
349
361
|
* Checks if we are running inside a docker container
|
|
350
|
-
* @returns {boolean}
|
|
351
362
|
*/
|
|
352
363
|
function isDocker() {
|
|
353
364
|
try {
|
|
354
|
-
|
|
365
|
+
fs_extra_1.default.statSync('/.dockerenv');
|
|
355
366
|
return true;
|
|
356
|
-
}
|
|
367
|
+
}
|
|
368
|
+
catch {
|
|
357
369
|
// ignore error
|
|
358
370
|
}
|
|
359
|
-
|
|
360
371
|
try {
|
|
361
372
|
// check docker group
|
|
362
|
-
return
|
|
363
|
-
}
|
|
373
|
+
return fs_extra_1.default.readFileSync('/proc/self/cgroup', 'utf8').includes('docker');
|
|
374
|
+
}
|
|
375
|
+
catch {
|
|
364
376
|
return false;
|
|
365
377
|
}
|
|
366
378
|
}
|
|
367
|
-
|
|
379
|
+
exports.isDocker = isDocker;
|
|
368
380
|
// Build unique uuid based on MAC address if possible
|
|
369
381
|
function uuid(givenMac, callback) {
|
|
370
382
|
if (typeof givenMac === 'function') {
|
|
371
383
|
callback = givenMac;
|
|
372
384
|
givenMac = '';
|
|
373
385
|
}
|
|
374
|
-
|
|
375
386
|
const _isDocker = isDocker();
|
|
376
|
-
|
|
377
387
|
// return constant UUID for all CI environments to keep the statistics clean
|
|
388
|
+
// eslint-disable-next-line @typescript-eslint/no-var-requires
|
|
378
389
|
if (require('ci-info').isCI) {
|
|
379
390
|
return callback('55travis-pipe-line-cior-githubaction');
|
|
380
391
|
}
|
|
381
|
-
|
|
382
392
|
let mac = givenMac !== null ? givenMac || '' : null;
|
|
383
393
|
let u;
|
|
384
|
-
|
|
385
394
|
if (!_isDocker && mac === '') {
|
|
386
|
-
const ifaces =
|
|
387
|
-
|
|
395
|
+
const ifaces = os_1.default.networkInterfaces();
|
|
388
396
|
// Find first not empty MAC
|
|
389
|
-
for (const
|
|
390
|
-
|
|
391
|
-
|
|
392
|
-
mac
|
|
393
|
-
|
|
397
|
+
for (const iface of Object.values(ifaces)) {
|
|
398
|
+
if (iface) {
|
|
399
|
+
for (const entry of iface) {
|
|
400
|
+
if (entry.mac !== '00:00:00:00:00:00') {
|
|
401
|
+
mac = entry.mac;
|
|
402
|
+
break;
|
|
403
|
+
}
|
|
394
404
|
}
|
|
395
405
|
}
|
|
396
|
-
|
|
397
406
|
if (mac) {
|
|
398
407
|
break;
|
|
399
408
|
}
|
|
400
409
|
}
|
|
401
410
|
}
|
|
402
|
-
|
|
403
411
|
if (!_isDocker && mac === '') {
|
|
404
412
|
return getMac((_err, mac) => uuid(mac || null, callback));
|
|
405
413
|
}
|
|
406
|
-
|
|
407
414
|
if (!_isDocker && mac) {
|
|
408
|
-
const md5sum =
|
|
415
|
+
const md5sum = crypto_1.default.createHash('md5');
|
|
409
416
|
md5sum.update(mac);
|
|
410
417
|
mac = md5sum.digest('hex');
|
|
411
418
|
u =
|
|
412
419
|
mac.substring(0, 8) +
|
|
413
|
-
|
|
414
|
-
|
|
415
|
-
|
|
416
|
-
|
|
417
|
-
|
|
418
|
-
|
|
419
|
-
|
|
420
|
-
|
|
421
|
-
}
|
|
420
|
+
'-' +
|
|
421
|
+
mac.substring(8, 12) +
|
|
422
|
+
'-' +
|
|
423
|
+
mac.substring(12, 16) +
|
|
424
|
+
'-' +
|
|
425
|
+
mac.substring(16, 20) +
|
|
426
|
+
'-' +
|
|
427
|
+
mac.substring(20);
|
|
428
|
+
}
|
|
429
|
+
else {
|
|
422
430
|
// Returns a RFC4122 compliant v4 UUID https://gist.github.com/LeverOne/1308368 (DO WTF YOU WANT TO PUBLIC LICENSE)
|
|
423
|
-
/** @type {any} */
|
|
424
431
|
let a;
|
|
425
432
|
let b;
|
|
426
433
|
b = a = '';
|
|
@@ -429,269 +436,244 @@ function uuid(givenMac, callback) {
|
|
|
429
436
|
}
|
|
430
437
|
u = b;
|
|
431
438
|
}
|
|
432
|
-
|
|
433
439
|
callback(u);
|
|
434
440
|
}
|
|
435
|
-
|
|
436
441
|
function updateUuid(newUuid, _objects, callback) {
|
|
437
442
|
uuid('', _uuid => {
|
|
438
443
|
_uuid = newUuid || _uuid;
|
|
439
444
|
// Add vendor prefix to UUID
|
|
440
|
-
if (
|
|
445
|
+
if (fs_extra_1.default.existsSync(VENDOR_FILE)) {
|
|
441
446
|
try {
|
|
447
|
+
// eslint-disable-next-line @typescript-eslint/no-var-requires
|
|
442
448
|
const vendor = require(VENDOR_FILE);
|
|
443
449
|
if (vendor.vendor && vendor.vendor.uuidPrefix && vendor.vendor.uuidPrefix.length === 2) {
|
|
444
450
|
_uuid = vendor.vendor.uuidPrefix + _uuid;
|
|
445
451
|
}
|
|
446
|
-
}
|
|
452
|
+
}
|
|
453
|
+
catch {
|
|
447
454
|
console.error(`Cannot parse ${VENDOR_FILE}`);
|
|
448
455
|
}
|
|
449
456
|
}
|
|
450
|
-
|
|
451
|
-
|
|
452
|
-
|
|
453
|
-
|
|
454
|
-
type: '
|
|
455
|
-
common: {
|
|
456
|
-
name: 'uuid',
|
|
457
|
-
type: 'uuid'
|
|
458
|
-
},
|
|
459
|
-
ts: new Date().getTime(),
|
|
460
|
-
from: 'system.host.' + getHostName() + '.tools',
|
|
461
|
-
native: {
|
|
462
|
-
uuid: _uuid
|
|
463
|
-
}
|
|
457
|
+
_objects.setObject('system.meta.uuid', {
|
|
458
|
+
type: 'meta',
|
|
459
|
+
common: {
|
|
460
|
+
name: 'uuid',
|
|
461
|
+
type: 'uuid'
|
|
464
462
|
},
|
|
465
|
-
|
|
466
|
-
|
|
467
|
-
|
|
468
|
-
|
|
469
|
-
} else {
|
|
470
|
-
_objects.getObject('system.meta.uuid', (err, obj) => {
|
|
471
|
-
if (obj.native.uuid !== _uuid) {
|
|
472
|
-
console.error('object system.meta.uuid cannot be updated: write protected');
|
|
473
|
-
} else {
|
|
474
|
-
console.log('object system.meta.uuid created: ' + _uuid);
|
|
475
|
-
}
|
|
476
|
-
callback(_uuid);
|
|
477
|
-
});
|
|
478
|
-
}
|
|
463
|
+
ts: new Date().getTime(),
|
|
464
|
+
from: `system.host.${getHostName()}.tools`,
|
|
465
|
+
native: {
|
|
466
|
+
uuid: _uuid
|
|
479
467
|
}
|
|
480
|
-
)
|
|
468
|
+
}, (err) => {
|
|
469
|
+
if (err) {
|
|
470
|
+
console.error(`object system.meta.uuid cannot be updated: ${err.message}`);
|
|
471
|
+
callback();
|
|
472
|
+
}
|
|
473
|
+
else {
|
|
474
|
+
_objects.getObject('system.meta.uuid', (err, obj) => {
|
|
475
|
+
if (obj.native.uuid !== _uuid) {
|
|
476
|
+
console.error('object system.meta.uuid cannot be updated: write protected');
|
|
477
|
+
}
|
|
478
|
+
else {
|
|
479
|
+
console.log(`object system.meta.uuid created: ${_uuid}`);
|
|
480
|
+
}
|
|
481
|
+
callback(_uuid);
|
|
482
|
+
});
|
|
483
|
+
}
|
|
484
|
+
});
|
|
481
485
|
});
|
|
482
486
|
}
|
|
483
|
-
|
|
484
487
|
/**
|
|
485
488
|
* Generates a new uuid if non existing
|
|
486
489
|
*
|
|
487
|
-
* @param
|
|
488
|
-
* @return
|
|
490
|
+
* @param objects - objects DB
|
|
491
|
+
* @return uuid if successfully created/updated
|
|
489
492
|
*/
|
|
490
493
|
async function createUuid(objects) {
|
|
491
|
-
const promiseCheckPassword = new Promise(resolve =>
|
|
492
|
-
|
|
493
|
-
|
|
494
|
-
|
|
495
|
-
|
|
496
|
-
//
|
|
497
|
-
|
|
498
|
-
|
|
499
|
-
|
|
500
|
-
|
|
501
|
-
|
|
502
|
-
|
|
503
|
-
|
|
504
|
-
|
|
505
|
-
|
|
506
|
-
|
|
507
|
-
|
|
508
|
-
|
|
509
|
-
|
|
510
|
-
|
|
511
|
-
ts: new Date().getTime(),
|
|
512
|
-
from: `system.host.${getHostName()}.tools`,
|
|
513
|
-
native: {}
|
|
514
|
-
},
|
|
515
|
-
() => {
|
|
516
|
-
console.log('object system.user.admin created');
|
|
517
|
-
resolve();
|
|
518
|
-
}
|
|
519
|
-
);
|
|
494
|
+
const promiseCheckPassword = new Promise(resolve => objects.getObject('system.user.admin', (err, obj) => {
|
|
495
|
+
if (err || !obj) {
|
|
496
|
+
// Default Password for user 'admin' is application name in lower case
|
|
497
|
+
(0, password_1.password)(exports.appName).hash(null, null, (err, res) => {
|
|
498
|
+
err && console.error(err);
|
|
499
|
+
// Create user here and not in io-package.js because of hash password
|
|
500
|
+
objects.setObject('system.user.admin', {
|
|
501
|
+
type: 'user',
|
|
502
|
+
common: {
|
|
503
|
+
name: 'admin',
|
|
504
|
+
password: res,
|
|
505
|
+
dontDelete: true,
|
|
506
|
+
enabled: true
|
|
507
|
+
},
|
|
508
|
+
ts: new Date().getTime(),
|
|
509
|
+
from: `system.host.${getHostName()}.tools`,
|
|
510
|
+
native: {}
|
|
511
|
+
}, () => {
|
|
512
|
+
console.log('object system.user.admin created');
|
|
513
|
+
resolve();
|
|
520
514
|
});
|
|
521
|
-
}
|
|
522
|
-
|
|
523
|
-
|
|
524
|
-
|
|
525
|
-
|
|
526
|
-
|
|
527
|
-
|
|
528
|
-
|
|
529
|
-
|
|
530
|
-
|
|
531
|
-
|
|
532
|
-
|
|
533
|
-
|
|
534
|
-
|
|
535
|
-
|
|
536
|
-
|
|
537
|
-
|
|
538
|
-
|
|
539
|
-
|
|
540
|
-
|
|
541
|
-
|
|
542
|
-
|
|
515
|
+
});
|
|
516
|
+
}
|
|
517
|
+
else {
|
|
518
|
+
resolve();
|
|
519
|
+
}
|
|
520
|
+
}));
|
|
521
|
+
const promiseCheckUuid = new Promise(resolve => objects.getObject('system.meta.uuid', (err, obj) => {
|
|
522
|
+
if (!err && obj && obj.native && obj.native.uuid) {
|
|
523
|
+
const PROBLEM_UUIDS = [
|
|
524
|
+
'ab265f4a-67f9-a46a-c0b2-61e4b95cefe5',
|
|
525
|
+
'7abd3182-d399-f7bd-da19-9550d8babede',
|
|
526
|
+
'deb6f2a8-fe69-5491-0a50-a9f9b8f3419c',
|
|
527
|
+
'ec66c85e-fc36-f6f9-f1c9-f5a2882d23c7',
|
|
528
|
+
'e6203b03-f5f4-253a-e4f6-b295fc543ab7',
|
|
529
|
+
'd659fa3d-7ef9-202a-ea23-acd0aff67b24'
|
|
530
|
+
];
|
|
531
|
+
// if COMMON invalid docker uuid
|
|
532
|
+
if (PROBLEM_UUIDS.includes(obj.native.uuid)) {
|
|
533
|
+
// Read vis license
|
|
534
|
+
objects.getObject('system.adapter.vis.0', (err, licObj) => {
|
|
535
|
+
if (!licObj || !licObj.native || !licObj.native.license) {
|
|
536
|
+
// generate new UUID
|
|
537
|
+
updateUuid('', objects, _uuid => resolve(_uuid));
|
|
538
|
+
}
|
|
539
|
+
else {
|
|
540
|
+
// decode obj.native.license
|
|
541
|
+
let data;
|
|
542
|
+
try {
|
|
543
|
+
data = jsonwebtoken_1.default.decode(licObj.native.license);
|
|
544
|
+
}
|
|
545
|
+
catch {
|
|
546
|
+
data = null;
|
|
547
|
+
}
|
|
548
|
+
if (!data || typeof data === 'string' || !data.uuid) {
|
|
543
549
|
// generate new UUID
|
|
544
|
-
updateUuid('', objects,
|
|
545
|
-
}
|
|
546
|
-
|
|
547
|
-
|
|
548
|
-
|
|
549
|
-
data = jwt.decode(licObj.native.license);
|
|
550
|
-
} catch {
|
|
551
|
-
data = null;
|
|
550
|
+
updateUuid('', objects, __uuid => resolve(__uuid));
|
|
551
|
+
}
|
|
552
|
+
else {
|
|
553
|
+
if (data.uuid !== obj.native.uuid) {
|
|
554
|
+
updateUuid(data.correct ? data.uuid : '', objects, _uuid => resolve(_uuid));
|
|
552
555
|
}
|
|
553
|
-
|
|
554
|
-
|
|
555
|
-
|
|
556
|
-
|
|
557
|
-
|
|
558
|
-
if (data.uuid !== obj.native.uuid) {
|
|
559
|
-
updateUuid(data.correct ? data.uuid : '', objects, _uuid => resolve(_uuid));
|
|
560
|
-
} else {
|
|
561
|
-
// Show error
|
|
562
|
-
console.warn(
|
|
563
|
-
`Your iobroker.vis license must be updated. Please contact info@iobroker.net to get a new license!`
|
|
564
|
-
);
|
|
565
|
-
console.warn(
|
|
566
|
-
`Provide following information in email: ${data.email}, invoice: ${data.invoice}`
|
|
567
|
-
);
|
|
568
|
-
resolve();
|
|
569
|
-
}
|
|
556
|
+
else {
|
|
557
|
+
// Show error
|
|
558
|
+
console.warn(`Your iobroker.vis license must be updated. Please contact info@iobroker.net to get a new license!`);
|
|
559
|
+
console.warn(`Provide following information in email: ${data.email}, invoice: ${data.invoice}`);
|
|
560
|
+
resolve();
|
|
570
561
|
}
|
|
571
562
|
}
|
|
572
|
-
}
|
|
573
|
-
}
|
|
574
|
-
resolve();
|
|
575
|
-
}
|
|
576
|
-
} else {
|
|
577
|
-
// generate new UUID
|
|
578
|
-
updateUuid('', objects, _uuid => resolve(_uuid));
|
|
563
|
+
}
|
|
564
|
+
});
|
|
579
565
|
}
|
|
580
|
-
|
|
581
|
-
|
|
582
|
-
|
|
566
|
+
else {
|
|
567
|
+
resolve();
|
|
568
|
+
}
|
|
569
|
+
}
|
|
570
|
+
else {
|
|
571
|
+
// generate new UUID
|
|
572
|
+
updateUuid('', objects, _uuid => resolve(_uuid));
|
|
573
|
+
}
|
|
574
|
+
}));
|
|
583
575
|
const result = await Promise.all([promiseCheckPassword, promiseCheckUuid]);
|
|
584
576
|
return result[1];
|
|
585
577
|
}
|
|
586
|
-
|
|
578
|
+
exports.createUuid = createUuid;
|
|
587
579
|
// Download file to tmp or return file name directly
|
|
588
|
-
function getFile(urlOrPath, fileName, callback) {
|
|
589
|
-
request = request || require('request');
|
|
590
|
-
|
|
580
|
+
async function getFile(urlOrPath, fileName, callback) {
|
|
591
581
|
// If object was read
|
|
592
|
-
if (
|
|
593
|
-
urlOrPath.substring(0, '
|
|
594
|
-
urlOrPath.substring(0, 'https://'.length) === 'https://'
|
|
595
|
-
) {
|
|
582
|
+
if (urlOrPath.substring(0, 'http://'.length) === 'http://' ||
|
|
583
|
+
urlOrPath.substring(0, 'https://'.length) === 'https://') {
|
|
596
584
|
const tmpFile = `${__dirname}/../tmp/${fileName || Math.floor(Math.random() * 0xffffffe) + '.zip'}`;
|
|
597
|
-
|
|
598
|
-
|
|
599
|
-
|
|
600
|
-
|
|
601
|
-
|
|
602
|
-
|
|
603
|
-
|
|
604
|
-
|
|
605
|
-
|
|
606
|
-
|
|
607
|
-
|
|
608
|
-
.on('close', () => {
|
|
609
|
-
console.log('downloaded ' + tmpFile);
|
|
585
|
+
try {
|
|
586
|
+
// Add some information to user-agent, like chrome, IE and Firefox do
|
|
587
|
+
const res = await axios_1.default.get(urlOrPath, {
|
|
588
|
+
responseType: 'stream',
|
|
589
|
+
headers: {
|
|
590
|
+
'User-Agent': `${exports.appName}, RND: ${randomID}, N: ${process.version}`,
|
|
591
|
+
'Accept-Encoding': 'gzip'
|
|
592
|
+
}
|
|
593
|
+
});
|
|
594
|
+
res.data.pipe(fs_extra_1.default.createWriteStream(tmpFile)).on('close', () => {
|
|
595
|
+
console.log(`downloaded ${tmpFile}`);
|
|
610
596
|
callback && callback(tmpFile);
|
|
611
597
|
});
|
|
612
|
-
|
|
598
|
+
}
|
|
599
|
+
catch (e) {
|
|
600
|
+
console.log(`Cannot download "${tmpFile}": ${e.message}`);
|
|
601
|
+
callback && callback(tmpFile);
|
|
602
|
+
}
|
|
603
|
+
}
|
|
604
|
+
else {
|
|
613
605
|
try {
|
|
614
|
-
if (
|
|
606
|
+
if (fs_extra_1.default.existsSync(urlOrPath)) {
|
|
615
607
|
callback && callback(urlOrPath);
|
|
616
|
-
}
|
|
608
|
+
}
|
|
609
|
+
else if (fs_extra_1.default.existsSync(`${__dirname}/../${urlOrPath}`)) {
|
|
617
610
|
callback && callback(`${__dirname}/../${urlOrPath}`);
|
|
618
|
-
}
|
|
611
|
+
}
|
|
612
|
+
else if (fs_extra_1.default.existsSync(`${__dirname}/../tmp/${urlOrPath}`)) {
|
|
619
613
|
callback && callback(`${__dirname}/../tmp/${urlOrPath}`);
|
|
620
|
-
}
|
|
614
|
+
}
|
|
615
|
+
else {
|
|
621
616
|
console.log('File not found: ' + urlOrPath);
|
|
622
|
-
process.exit(EXIT_CODES.FILE_NOT_FOUND);
|
|
617
|
+
process.exit(exitCodes_1.EXIT_CODES.FILE_NOT_FOUND);
|
|
623
618
|
}
|
|
624
|
-
}
|
|
619
|
+
}
|
|
620
|
+
catch (err) {
|
|
625
621
|
console.log(`File "${urlOrPath}" could no be read: ${err.message}`);
|
|
626
|
-
process.exit(EXIT_CODES.FILE_NOT_FOUND);
|
|
622
|
+
process.exit(exitCodes_1.EXIT_CODES.FILE_NOT_FOUND);
|
|
627
623
|
}
|
|
628
624
|
}
|
|
629
625
|
}
|
|
630
|
-
|
|
626
|
+
exports.getFile = getFile;
|
|
631
627
|
// Return content of the json file. Download it or read directly
|
|
632
|
-
function getJson(urlOrPath, agent, callback) {
|
|
628
|
+
async function getJson(urlOrPath, agent, callback) {
|
|
633
629
|
if (typeof agent === 'function') {
|
|
634
630
|
callback = agent;
|
|
635
631
|
agent = '';
|
|
636
632
|
}
|
|
637
633
|
agent = agent || '';
|
|
638
|
-
|
|
639
|
-
request = request || require('request');
|
|
640
634
|
let sources = {};
|
|
641
635
|
// If object was read
|
|
642
636
|
if (urlOrPath && typeof urlOrPath === 'object') {
|
|
643
637
|
if (callback) {
|
|
644
638
|
callback(urlOrPath);
|
|
645
639
|
}
|
|
646
|
-
}
|
|
640
|
+
}
|
|
641
|
+
else if (!urlOrPath) {
|
|
647
642
|
console.log('Empty url!');
|
|
648
643
|
if (callback) {
|
|
649
644
|
callback(null);
|
|
650
645
|
}
|
|
651
|
-
}
|
|
652
|
-
|
|
653
|
-
|
|
654
|
-
urlOrPath.substring(0, 'https://'.length) === 'https://'
|
|
655
|
-
|
|
656
|
-
|
|
657
|
-
|
|
658
|
-
|
|
659
|
-
|
|
660
|
-
|
|
661
|
-
headers: { 'User-Agent': agent }
|
|
662
|
-
},
|
|
663
|
-
(error, response, body) => {
|
|
664
|
-
if (error || !body || response.statusCode !== 200) {
|
|
665
|
-
console.warn(`Cannot download json from ${urlOrPath}. Error: ${error || body}`);
|
|
666
|
-
if (callback) {
|
|
667
|
-
callback(null, urlOrPath);
|
|
668
|
-
}
|
|
669
|
-
return;
|
|
670
|
-
}
|
|
671
|
-
try {
|
|
672
|
-
sources = JSON.parse(body);
|
|
673
|
-
} catch {
|
|
674
|
-
console.error('Json file is invalid on ' + urlOrPath);
|
|
675
|
-
if (callback) {
|
|
676
|
-
callback(null, urlOrPath);
|
|
677
|
-
}
|
|
678
|
-
return;
|
|
679
|
-
}
|
|
680
|
-
|
|
681
|
-
if (callback) {
|
|
682
|
-
callback(sources, urlOrPath);
|
|
683
|
-
}
|
|
646
|
+
}
|
|
647
|
+
else {
|
|
648
|
+
if (urlOrPath.substring(0, 'http://'.length) === 'http://' ||
|
|
649
|
+
urlOrPath.substring(0, 'https://'.length) === 'https://') {
|
|
650
|
+
try {
|
|
651
|
+
const res = await axios_1.default.get(urlOrPath, {
|
|
652
|
+
headers: { 'Accept-Encoding': 'gzip', timeout: 10000, 'User-Agent': agent }
|
|
653
|
+
});
|
|
654
|
+
if (res.status !== 200 || !res.data) {
|
|
655
|
+
throw new Error(`Invalid response, body: ${res.data}, status code: ${res.status}`);
|
|
684
656
|
}
|
|
685
|
-
|
|
686
|
-
|
|
687
|
-
|
|
688
|
-
|
|
689
|
-
|
|
690
|
-
|
|
657
|
+
sources = res.data;
|
|
658
|
+
if (callback) {
|
|
659
|
+
callback(sources, urlOrPath);
|
|
660
|
+
}
|
|
661
|
+
}
|
|
662
|
+
catch (e) {
|
|
663
|
+
console.warn(`Cannot download json from ${urlOrPath}. Error: ${e.message}`);
|
|
664
|
+
if (callback) {
|
|
665
|
+
callback(null, urlOrPath);
|
|
666
|
+
}
|
|
667
|
+
return;
|
|
668
|
+
}
|
|
669
|
+
}
|
|
670
|
+
else {
|
|
671
|
+
if (fs_extra_1.default.existsSync(urlOrPath)) {
|
|
691
672
|
try {
|
|
692
|
-
sources =
|
|
693
|
-
}
|
|
694
|
-
|
|
673
|
+
sources = fs_extra_1.default.readJSONSync(urlOrPath);
|
|
674
|
+
}
|
|
675
|
+
catch (e) {
|
|
676
|
+
console.log(`Cannot parse json file from ${urlOrPath}. Error: ${e.message}`);
|
|
695
677
|
if (callback) {
|
|
696
678
|
callback(null, urlOrPath);
|
|
697
679
|
}
|
|
@@ -700,13 +682,13 @@ function getJson(urlOrPath, agent, callback) {
|
|
|
700
682
|
if (callback) {
|
|
701
683
|
callback(sources, urlOrPath);
|
|
702
684
|
}
|
|
703
|
-
}
|
|
685
|
+
}
|
|
686
|
+
else if (fs_extra_1.default.existsSync(`${__dirname}/../${urlOrPath}`)) {
|
|
704
687
|
try {
|
|
705
|
-
sources =
|
|
706
|
-
}
|
|
707
|
-
|
|
708
|
-
|
|
709
|
-
);
|
|
688
|
+
sources = fs_extra_1.default.readJSONSync(`${__dirname}/../${urlOrPath}`);
|
|
689
|
+
}
|
|
690
|
+
catch (e) {
|
|
691
|
+
console.log(`Cannot parse json file from ${__dirname}/../${urlOrPath}. Error: ${e.message}`);
|
|
710
692
|
if (callback) {
|
|
711
693
|
callback(null, urlOrPath);
|
|
712
694
|
}
|
|
@@ -715,13 +697,13 @@ function getJson(urlOrPath, agent, callback) {
|
|
|
715
697
|
if (callback) {
|
|
716
698
|
callback(sources, urlOrPath);
|
|
717
699
|
}
|
|
718
|
-
}
|
|
700
|
+
}
|
|
701
|
+
else if (fs_extra_1.default.existsSync(`${__dirname}/../tmp/${urlOrPath}`)) {
|
|
719
702
|
try {
|
|
720
|
-
sources =
|
|
721
|
-
}
|
|
722
|
-
|
|
723
|
-
|
|
724
|
-
);
|
|
703
|
+
sources = fs_extra_1.default.readJSONSync(`${__dirname}/../tmp/${urlOrPath}`);
|
|
704
|
+
}
|
|
705
|
+
catch (e) {
|
|
706
|
+
console.log(`Cannot parse json file from ${__dirname}/../tmp/${urlOrPath}. Error: ${e.message}`);
|
|
725
707
|
if (callback) {
|
|
726
708
|
callback(null, urlOrPath);
|
|
727
709
|
}
|
|
@@ -730,7 +712,8 @@ function getJson(urlOrPath, agent, callback) {
|
|
|
730
712
|
if (callback) {
|
|
731
713
|
callback(sources, urlOrPath);
|
|
732
714
|
}
|
|
733
|
-
}
|
|
715
|
+
}
|
|
716
|
+
else {
|
|
734
717
|
//if (urlOrPath.indexOf('/example/') === -1) console.log('Json file not found: ' + urlOrPath);
|
|
735
718
|
if (callback) {
|
|
736
719
|
callback(null, urlOrPath);
|
|
@@ -739,97 +722,105 @@ function getJson(urlOrPath, agent, callback) {
|
|
|
739
722
|
}
|
|
740
723
|
}
|
|
741
724
|
}
|
|
742
|
-
|
|
725
|
+
exports.getJson = getJson;
|
|
743
726
|
/**
|
|
744
727
|
* Return content of the json file. Download it or read directly
|
|
745
|
-
* @param
|
|
746
|
-
* @param
|
|
747
|
-
* @returns
|
|
728
|
+
* @param urlOrPath URL where the json file could be found
|
|
729
|
+
* @param agent optional agent identifier like "Windows Chrome 12.56"
|
|
730
|
+
* @returns json object
|
|
748
731
|
*/
|
|
749
732
|
async function getJsonAsync(urlOrPath, agent) {
|
|
750
733
|
agent = agent || '';
|
|
751
|
-
|
|
752
|
-
axios = axios || require('axios');
|
|
753
734
|
let sources = {};
|
|
754
735
|
// If object was read
|
|
755
736
|
if (urlOrPath && typeof urlOrPath === 'object') {
|
|
756
737
|
return urlOrPath;
|
|
757
|
-
}
|
|
738
|
+
}
|
|
739
|
+
else if (!urlOrPath) {
|
|
758
740
|
console.log('Empty url!');
|
|
759
741
|
return null;
|
|
760
|
-
}
|
|
742
|
+
}
|
|
743
|
+
else {
|
|
761
744
|
if (urlOrPath.startsWith('http://') || urlOrPath.startsWith('https://')) {
|
|
762
745
|
try {
|
|
763
|
-
const result = await
|
|
746
|
+
const result = await (0, axios_1.default)(urlOrPath, {
|
|
764
747
|
timeout: 10000,
|
|
765
748
|
headers: { 'User-Agent': agent },
|
|
766
749
|
validateStatus: status => status !== 200
|
|
767
750
|
});
|
|
768
751
|
return result.data;
|
|
769
|
-
}
|
|
770
|
-
|
|
752
|
+
}
|
|
753
|
+
catch (e) {
|
|
754
|
+
console.warn(`Cannot download json from ${urlOrPath}. Error: ${e.message}`);
|
|
771
755
|
return null;
|
|
772
756
|
}
|
|
773
|
-
}
|
|
774
|
-
|
|
757
|
+
}
|
|
758
|
+
else {
|
|
759
|
+
if (fs_extra_1.default.existsSync(urlOrPath)) {
|
|
775
760
|
try {
|
|
776
|
-
sources =
|
|
777
|
-
}
|
|
761
|
+
sources = fs_extra_1.default.readJSONSync(urlOrPath);
|
|
762
|
+
}
|
|
763
|
+
catch (e) {
|
|
778
764
|
console.warn(`Cannot parse json file from ${urlOrPath}. Error: ${e.message}`);
|
|
779
765
|
return null;
|
|
780
766
|
}
|
|
781
767
|
return sources;
|
|
782
|
-
}
|
|
768
|
+
}
|
|
769
|
+
else if (fs_extra_1.default.existsSync(__dirname + '/../' + urlOrPath)) {
|
|
783
770
|
try {
|
|
784
|
-
sources =
|
|
785
|
-
}
|
|
771
|
+
sources = fs_extra_1.default.readJSONSync(`${__dirname}/../${urlOrPath}`);
|
|
772
|
+
}
|
|
773
|
+
catch (e) {
|
|
786
774
|
console.warn(`Cannot parse json file from ${__dirname}/../${urlOrPath}. Error: ${e.message}`);
|
|
787
775
|
return null;
|
|
788
776
|
}
|
|
789
777
|
return sources;
|
|
790
|
-
}
|
|
778
|
+
}
|
|
779
|
+
else if (fs_extra_1.default.existsSync(`${__dirname}/../tmp/${urlOrPath}`)) {
|
|
791
780
|
try {
|
|
792
|
-
sources =
|
|
793
|
-
}
|
|
781
|
+
sources = fs_extra_1.default.readJSONSync(`${__dirname}/../tmp/${urlOrPath}`);
|
|
782
|
+
}
|
|
783
|
+
catch (e) {
|
|
794
784
|
console.log(`Cannot parse json file from ${__dirname}/../tmp/${urlOrPath}. Error: ${e.message}`);
|
|
795
785
|
return null;
|
|
796
786
|
}
|
|
797
787
|
return sources;
|
|
798
|
-
}
|
|
788
|
+
}
|
|
789
|
+
else {
|
|
799
790
|
//if (urlOrPath.indexOf('/example/') === -1) console.log('Json file not found: ' + urlOrPath);
|
|
800
791
|
return null;
|
|
801
792
|
}
|
|
802
793
|
}
|
|
803
794
|
}
|
|
804
795
|
}
|
|
805
|
-
|
|
796
|
+
exports.getJsonAsync = getJsonAsync;
|
|
806
797
|
function scanDirectory(dirName, list, regExp) {
|
|
807
|
-
if (
|
|
798
|
+
if (fs_extra_1.default.existsSync(dirName)) {
|
|
808
799
|
let dirs;
|
|
809
800
|
try {
|
|
810
|
-
dirs =
|
|
811
|
-
}
|
|
801
|
+
dirs = fs_extra_1.default.readdirSync(dirName);
|
|
802
|
+
}
|
|
803
|
+
catch (e) {
|
|
812
804
|
console.log(`Cannot read or parse ${dirName}: ${e.message}`);
|
|
813
805
|
return;
|
|
814
806
|
}
|
|
815
807
|
for (let i = 0; i < dirs.length; i++) {
|
|
816
808
|
try {
|
|
817
|
-
const fullPath =
|
|
818
|
-
const fileIoName =
|
|
819
|
-
const fileName =
|
|
820
|
-
if (regExp.test(dirs[i]) &&
|
|
821
|
-
const ioPackage =
|
|
822
|
-
const package_ =
|
|
809
|
+
const fullPath = path_1.default.join(dirName, dirs[i]);
|
|
810
|
+
const fileIoName = path_1.default.join(fullPath, 'io-package.json');
|
|
811
|
+
const fileName = path_1.default.join(fullPath, 'package.json');
|
|
812
|
+
if (regExp.test(dirs[i]) && fs_extra_1.default.existsSync(fileIoName)) {
|
|
813
|
+
const ioPackage = fs_extra_1.default.readJSONSync(fileIoName);
|
|
814
|
+
const package_ = fs_extra_1.default.existsSync(fileName) ? fs_extra_1.default.readJSONSync(fileName) : {};
|
|
823
815
|
const localIcon = ioPackage.common.icon
|
|
824
|
-
? `/adapter/${dirs[i].substring(
|
|
816
|
+
? `/adapter/${dirs[i].substring(exports.appName.length + 1)}/${ioPackage.common.icon}`
|
|
825
817
|
: '';
|
|
826
|
-
//noinspection JSUnresolvedVariable
|
|
827
818
|
list[ioPackage.common.name] = {
|
|
828
819
|
controller: ioPackage.common.controller || false,
|
|
829
820
|
version: ioPackage.common.version,
|
|
830
821
|
icon: ioPackage.common.extIcon || localIcon,
|
|
831
822
|
localIcon,
|
|
832
|
-
title: ioPackage.common.title,
|
|
823
|
+
title: ioPackage.common.title,
|
|
833
824
|
titleLang: ioPackage.common.titleLang,
|
|
834
825
|
desc: ioPackage.common.desc,
|
|
835
826
|
platform: ioPackage.common.platform,
|
|
@@ -839,47 +830,48 @@ function scanDirectory(dirName, list, regExp) {
|
|
|
839
830
|
license: ioPackage.common.license
|
|
840
831
|
? ioPackage.common.license
|
|
841
832
|
: package_.licenses && package_.licenses.length
|
|
842
|
-
|
|
843
|
-
|
|
833
|
+
? package_.licenses[0].type
|
|
834
|
+
: '',
|
|
844
835
|
licenseUrl: package_.licenses && package_.licenses.length ? package_.licenses[0].url : ''
|
|
845
836
|
};
|
|
846
837
|
}
|
|
847
|
-
}
|
|
848
|
-
|
|
849
|
-
|
|
850
|
-
);
|
|
838
|
+
}
|
|
839
|
+
catch (e) {
|
|
840
|
+
console.log(`Cannot read or parse ${__dirname}/../node_modules/${dirs[i]}/io-package.json: ${e.message}`);
|
|
851
841
|
}
|
|
852
842
|
}
|
|
853
843
|
}
|
|
854
844
|
}
|
|
855
|
-
|
|
856
845
|
/**
|
|
857
846
|
* Get list of all installed adapters and controller version on this host
|
|
858
|
-
* @param
|
|
859
|
-
* @returns
|
|
847
|
+
* @param hostRunningVersion Version of the running js-controller, will be included in the returned information if provided
|
|
848
|
+
* @returns object containing information about installed host
|
|
860
849
|
*/
|
|
861
850
|
function getInstalledInfo(hostRunningVersion) {
|
|
862
851
|
const result = {};
|
|
863
852
|
const fullPath = getControllerDir();
|
|
864
|
-
|
|
853
|
+
if (!fullPath) {
|
|
854
|
+
console.error('Could not determine controller directory on getInstalledInfo.');
|
|
855
|
+
return result;
|
|
856
|
+
}
|
|
865
857
|
// Get info about host
|
|
866
858
|
let ioPackage;
|
|
867
859
|
try {
|
|
868
|
-
ioPackage =
|
|
869
|
-
}
|
|
860
|
+
ioPackage = fs_extra_1.default.readJSONSync(path_1.default.join(fullPath, 'io-package.json'));
|
|
861
|
+
}
|
|
862
|
+
catch (e) {
|
|
870
863
|
console.error(`Cannot get installed host information: ${e.message}`);
|
|
871
864
|
}
|
|
872
|
-
const package_ =
|
|
873
|
-
?
|
|
865
|
+
const package_ = fs_extra_1.default.existsSync(path_1.default.join(fullPath, 'package.json'))
|
|
866
|
+
? fs_extra_1.default.readJSONSync(path_1.default.join(fullPath, 'package.json'))
|
|
874
867
|
: {};
|
|
875
|
-
const regExp = new RegExp(`^${
|
|
876
|
-
|
|
868
|
+
const regExp = new RegExp(`^${exports.appName}\\.`, 'i');
|
|
877
869
|
if (ioPackage) {
|
|
878
870
|
result[ioPackage.common.name] = {
|
|
879
871
|
controller: true,
|
|
880
872
|
version: ioPackage.common.version,
|
|
881
873
|
icon: ioPackage.common.extIcon || ioPackage.common.icon,
|
|
882
|
-
title: ioPackage.common.title,
|
|
874
|
+
title: ioPackage.common.title,
|
|
883
875
|
titleLang: ioPackage.common.titleLang,
|
|
884
876
|
desc: ioPackage.common.desc,
|
|
885
877
|
platform: ioPackage.common.platform,
|
|
@@ -889,32 +881,27 @@ function getInstalledInfo(hostRunningVersion) {
|
|
|
889
881
|
license: ioPackage.common.license
|
|
890
882
|
? ioPackage.common.license
|
|
891
883
|
: package_.licenses && package_.licenses.length
|
|
892
|
-
|
|
893
|
-
|
|
884
|
+
? package_.licenses[0].type
|
|
885
|
+
: '',
|
|
894
886
|
licenseUrl: package_.licenses && package_.licenses.length ? package_.licenses[0].url : ''
|
|
895
887
|
};
|
|
896
888
|
}
|
|
897
|
-
|
|
898
889
|
// we scan the sub node modules of controller and same hierarchy as controller
|
|
899
|
-
scanDirectory(
|
|
900
|
-
scanDirectory(
|
|
901
|
-
|
|
890
|
+
scanDirectory(path_1.default.join(fullPath, 'node_modules'), result, regExp);
|
|
891
|
+
scanDirectory(path_1.default.join(fullPath, '..'), result, regExp);
|
|
902
892
|
return result;
|
|
903
893
|
}
|
|
904
|
-
|
|
894
|
+
exports.getInstalledInfo = getInstalledInfo;
|
|
905
895
|
/**
|
|
906
896
|
* Reads an adapter's npm version
|
|
907
|
-
* @param
|
|
908
|
-
* @param
|
|
897
|
+
* @param adapter The adapter to read the npm version from. Null for the root ioBroker packet
|
|
898
|
+
* @param callback
|
|
909
899
|
*/
|
|
910
900
|
function getNpmVersion(adapter, callback) {
|
|
911
|
-
adapter = adapter ?
|
|
901
|
+
adapter = adapter ? `${exports.appName}.${adapter}` : exports.appName;
|
|
912
902
|
adapter = adapter.toLowerCase();
|
|
913
|
-
|
|
914
903
|
const cliCommand = `npm view ${adapter}@latest version`;
|
|
915
|
-
|
|
916
|
-
const exec = require('child_process').exec;
|
|
917
|
-
exec(cliCommand, { timeout: 2000, windowsHide: true }, (error, stdout, _stderr) => {
|
|
904
|
+
(0, child_process_1.exec)(cliCommand, { timeout: 2000, windowsHide: true }, (error, stdout, _stderr) => {
|
|
918
905
|
let version;
|
|
919
906
|
if (error) {
|
|
920
907
|
// command failed
|
|
@@ -922,17 +909,17 @@ function getNpmVersion(adapter, callback) {
|
|
|
922
909
|
callback(error);
|
|
923
910
|
return;
|
|
924
911
|
}
|
|
925
|
-
}
|
|
926
|
-
|
|
912
|
+
}
|
|
913
|
+
else if (stdout) {
|
|
914
|
+
version = semver_1.default.valid(stdout.trim());
|
|
927
915
|
}
|
|
928
916
|
if (typeof callback === 'function') {
|
|
929
917
|
callback(null, version);
|
|
930
918
|
}
|
|
931
919
|
});
|
|
932
920
|
}
|
|
933
|
-
|
|
934
921
|
function getIoPack(sources, name, callback) {
|
|
935
|
-
getJson(sources[name].meta, ioPack => {
|
|
922
|
+
getJson(sources[name].meta, '', ioPack => {
|
|
936
923
|
const packUrl = sources[name].meta.replace('io-package.json', 'package.json');
|
|
937
924
|
if (!ioPack) {
|
|
938
925
|
if (sources._helper) {
|
|
@@ -941,9 +928,10 @@ function getIoPack(sources, name, callback) {
|
|
|
941
928
|
if (callback) {
|
|
942
929
|
callback(sources, name);
|
|
943
930
|
}
|
|
944
|
-
}
|
|
931
|
+
}
|
|
932
|
+
else {
|
|
945
933
|
setImmediate(() => {
|
|
946
|
-
getJson(packUrl, pack => {
|
|
934
|
+
getJson(packUrl, '', pack => {
|
|
947
935
|
const version = sources[name].version;
|
|
948
936
|
const type = sources[name].type;
|
|
949
937
|
// If installed from git or something else
|
|
@@ -951,7 +939,6 @@ function getIoPack(sources, name, callback) {
|
|
|
951
939
|
if (sources[name].url && name !== 'js-controller') {
|
|
952
940
|
if (ioPack && ioPack.common) {
|
|
953
941
|
sources[name] = extend(true, sources[name], ioPack.common);
|
|
954
|
-
|
|
955
942
|
// overwrite type of adapter from repository
|
|
956
943
|
if (type) {
|
|
957
944
|
sources[name].type = type;
|
|
@@ -965,11 +952,11 @@ function getIoPack(sources, name, callback) {
|
|
|
965
952
|
}
|
|
966
953
|
}
|
|
967
954
|
}
|
|
968
|
-
|
|
969
955
|
if (callback) {
|
|
970
956
|
callback(sources, name);
|
|
971
957
|
}
|
|
972
|
-
}
|
|
958
|
+
}
|
|
959
|
+
else {
|
|
973
960
|
if (ioPack && ioPack.common) {
|
|
974
961
|
sources[name] = extend(true, sources[name], ioPack.common);
|
|
975
962
|
if (pack && pack.licenses && pack.licenses.length) {
|
|
@@ -981,34 +968,33 @@ function getIoPack(sources, name, callback) {
|
|
|
981
968
|
}
|
|
982
969
|
}
|
|
983
970
|
}
|
|
984
|
-
|
|
985
971
|
// overwrite type of adapter from repository
|
|
986
972
|
if (type) {
|
|
987
973
|
sources[name].type = type;
|
|
988
974
|
}
|
|
989
|
-
|
|
990
975
|
if (version) {
|
|
991
976
|
sources[name].version = version;
|
|
992
977
|
if (callback) {
|
|
993
978
|
callback(sources, name);
|
|
994
979
|
}
|
|
995
|
-
}
|
|
996
|
-
|
|
997
|
-
|
|
998
|
-
sources[name].meta.substring(0, 'https://'.length) === 'https://'
|
|
999
|
-
) {
|
|
980
|
+
}
|
|
981
|
+
else {
|
|
982
|
+
if (sources[name].meta.substring(0, 'http://'.length) === 'http://' ||
|
|
983
|
+
sources[name].meta.substring(0, 'https://'.length) === 'https://') {
|
|
1000
984
|
//installed from npm
|
|
1001
985
|
getNpmVersion(name, (_err, version) => {
|
|
1002
986
|
if (version) {
|
|
1003
987
|
sources[name].version = version;
|
|
1004
|
-
}
|
|
988
|
+
}
|
|
989
|
+
else {
|
|
1005
990
|
sources[name].version = 'npm error';
|
|
1006
991
|
}
|
|
1007
992
|
if (callback) {
|
|
1008
993
|
callback(sources, name);
|
|
1009
994
|
}
|
|
1010
995
|
});
|
|
1011
|
-
}
|
|
996
|
+
}
|
|
997
|
+
else {
|
|
1012
998
|
if (callback) {
|
|
1013
999
|
callback(sources, name);
|
|
1014
1000
|
}
|
|
@@ -1020,7 +1006,6 @@ function getIoPack(sources, name, callback) {
|
|
|
1020
1006
|
}
|
|
1021
1007
|
});
|
|
1022
1008
|
}
|
|
1023
|
-
|
|
1024
1009
|
function _getRepositoryFile(sources, path, callback) {
|
|
1025
1010
|
if (!sources._helper) {
|
|
1026
1011
|
let count = 0;
|
|
@@ -1031,7 +1016,6 @@ function _getRepositoryFile(sources, path, callback) {
|
|
|
1031
1016
|
count++;
|
|
1032
1017
|
}
|
|
1033
1018
|
sources._helper = { failCounter: [] };
|
|
1034
|
-
|
|
1035
1019
|
sources._helper.timeout = setTimeout(() => {
|
|
1036
1020
|
if (sources._helper) {
|
|
1037
1021
|
delete sources._helper;
|
|
@@ -1041,18 +1025,16 @@ function _getRepositoryFile(sources, path, callback) {
|
|
|
1041
1025
|
}
|
|
1042
1026
|
}
|
|
1043
1027
|
if (callback) {
|
|
1044
|
-
callback(`Timeout by read all package.json (${count}) seconds
|
|
1028
|
+
callback(new Error(`Timeout by read all package.json (${count}) seconds`), sources);
|
|
1045
1029
|
}
|
|
1046
|
-
callback =
|
|
1030
|
+
callback = undefined;
|
|
1047
1031
|
}
|
|
1048
1032
|
}, count * 1000);
|
|
1049
1033
|
}
|
|
1050
|
-
|
|
1051
1034
|
for (const name of Object.keys(sources)) {
|
|
1052
1035
|
if (sources[name].processed || name === '_helper') {
|
|
1053
1036
|
continue;
|
|
1054
1037
|
}
|
|
1055
|
-
|
|
1056
1038
|
sources[name].processed = true;
|
|
1057
1039
|
if (sources[name].url) {
|
|
1058
1040
|
sources[name].url = findPath(path, sources[name].url);
|
|
@@ -1063,7 +1045,6 @@ function _getRepositoryFile(sources, path, callback) {
|
|
|
1063
1045
|
if (sources[name].icon) {
|
|
1064
1046
|
sources[name].icon = findPath(path, sources[name].icon);
|
|
1065
1047
|
}
|
|
1066
|
-
|
|
1067
1048
|
if (!sources[name].name && sources[name].meta) {
|
|
1068
1049
|
getIoPack(sources, name, _ignore => {
|
|
1069
1050
|
if (sources._helper) {
|
|
@@ -1076,10 +1057,11 @@ function _getRepositoryFile(sources, path, callback) {
|
|
|
1076
1057
|
}
|
|
1077
1058
|
}
|
|
1078
1059
|
if (callback) {
|
|
1079
|
-
callback('Looks like there is no internet.', sources);
|
|
1060
|
+
callback(new Error('Looks like there is no internet.'), sources);
|
|
1080
1061
|
}
|
|
1081
|
-
callback =
|
|
1082
|
-
}
|
|
1062
|
+
callback = undefined;
|
|
1063
|
+
}
|
|
1064
|
+
else {
|
|
1083
1065
|
// process next
|
|
1084
1066
|
setImmediate(() => _getRepositoryFile(sources, path, callback));
|
|
1085
1067
|
}
|
|
@@ -1088,12 +1070,11 @@ function _getRepositoryFile(sources, path, callback) {
|
|
|
1088
1070
|
return;
|
|
1089
1071
|
}
|
|
1090
1072
|
}
|
|
1091
|
-
|
|
1092
1073
|
// all packages are processed
|
|
1093
1074
|
if (sources._helper) {
|
|
1094
1075
|
let err;
|
|
1095
1076
|
if (sources._helper.failCounter.length) {
|
|
1096
|
-
err =
|
|
1077
|
+
err = new Error(`Following packages cannot be read: ${sources._helper.failCounter.join(', ')}`);
|
|
1097
1078
|
}
|
|
1098
1079
|
clearTimeout(sources._helper.timeout);
|
|
1099
1080
|
delete sources._helper;
|
|
@@ -1105,129 +1086,125 @@ function _getRepositoryFile(sources, path, callback) {
|
|
|
1105
1086
|
if (callback) {
|
|
1106
1087
|
callback(err, sources);
|
|
1107
1088
|
}
|
|
1108
|
-
callback =
|
|
1089
|
+
callback = undefined;
|
|
1109
1090
|
}
|
|
1110
1091
|
}
|
|
1111
|
-
|
|
1112
|
-
function _checkRepositoryFileHash(urlOrPath, additionalInfo, callback) {
|
|
1113
|
-
request = request || require('request');
|
|
1114
|
-
|
|
1092
|
+
async function _checkRepositoryFileHash(urlOrPath, additionalInfo, callback) {
|
|
1115
1093
|
// read hash of file
|
|
1116
1094
|
if (urlOrPath.startsWith('http://') || urlOrPath.startsWith('https://')) {
|
|
1117
1095
|
urlOrPath = urlOrPath.replace(/\.json$/, '-hash.json');
|
|
1118
1096
|
let json = null;
|
|
1119
|
-
|
|
1120
|
-
|
|
1121
|
-
|
|
1122
|
-
|
|
1123
|
-
try {
|
|
1124
|
-
json = JSON.parse(body);
|
|
1125
|
-
} catch {
|
|
1126
|
-
console.error('Json file is invalid on ' + urlOrPath);
|
|
1127
|
-
}
|
|
1097
|
+
try {
|
|
1098
|
+
const res = await axios_1.default.get(urlOrPath, { headers: { 'Accept-Encoding': 'gzip', timeout: 10000 } });
|
|
1099
|
+
if (!res.data || res.status !== 200) {
|
|
1100
|
+
throw new Error(`Invalid response, body: ${res.data}, status code: ${res.status}`);
|
|
1128
1101
|
}
|
|
1129
|
-
|
|
1130
|
-
|
|
1131
|
-
|
|
1132
|
-
|
|
1133
|
-
|
|
1134
|
-
|
|
1135
|
-
|
|
1136
|
-
|
|
1137
|
-
|
|
1138
|
-
|
|
1139
|
-
|
|
1140
|
-
|
|
1141
|
-
|
|
1142
|
-
//
|
|
1143
|
-
|
|
1144
|
-
|
|
1145
|
-
|
|
1146
|
-
|
|
1147
|
-
|
|
1148
|
-
|
|
1149
|
-
|
|
1150
|
-
|
|
1102
|
+
json = res.data;
|
|
1103
|
+
}
|
|
1104
|
+
catch (e) {
|
|
1105
|
+
console.warn(`Cannot download json from ${urlOrPath}. Error: ${e.message}`);
|
|
1106
|
+
}
|
|
1107
|
+
if (json && json.hash) {
|
|
1108
|
+
// The hash download was successful
|
|
1109
|
+
if (additionalInfo && additionalInfo.sources && json.hash === additionalInfo.hash) {
|
|
1110
|
+
// The hash is the same as for the cached sources
|
|
1111
|
+
console.log('hash unchanged, use cached sources');
|
|
1112
|
+
callback(null, additionalInfo.sources, json.hash);
|
|
1113
|
+
}
|
|
1114
|
+
else {
|
|
1115
|
+
// Either we have no sources cached or the hash changed
|
|
1116
|
+
// => force download of new sources
|
|
1117
|
+
console.log('hash changed or no sources cached => force download of new sources');
|
|
1118
|
+
callback(null, null, json.hash);
|
|
1119
|
+
}
|
|
1120
|
+
}
|
|
1121
|
+
else {
|
|
1122
|
+
// Could not download new sources, use the old ones
|
|
1123
|
+
console.log('failed to download new sources, use cached sources');
|
|
1124
|
+
callback(null, additionalInfo.sources, '');
|
|
1125
|
+
}
|
|
1126
|
+
}
|
|
1127
|
+
else {
|
|
1151
1128
|
// it is a file and file has not hash
|
|
1152
1129
|
callback(null, null, 0);
|
|
1153
1130
|
}
|
|
1154
1131
|
}
|
|
1155
|
-
|
|
1156
1132
|
/**
|
|
1157
1133
|
* Get list of all adapters and controller in some repository file or in /conf/source-dist.json
|
|
1158
1134
|
*
|
|
1159
1135
|
* @alias getRepositoryFile
|
|
1160
1136
|
* @memberof tools
|
|
1161
|
-
* @param
|
|
1162
|
-
* @param
|
|
1163
|
-
* @param
|
|
1137
|
+
* @param urlOrPath URL starting with http:// or https:// or local file link
|
|
1138
|
+
* @param additionalInfo destination object
|
|
1139
|
+
* @param callback function (err, sources, actualHash) { }
|
|
1164
1140
|
*
|
|
1165
1141
|
*/
|
|
1166
1142
|
function getRepositoryFile(urlOrPath, additionalInfo, callback) {
|
|
1167
1143
|
let sources = {};
|
|
1168
|
-
let
|
|
1169
|
-
|
|
1144
|
+
let _path = '';
|
|
1170
1145
|
if (typeof additionalInfo === 'function') {
|
|
1146
|
+
// @ts-expect-error: fix all fun calls then remove
|
|
1171
1147
|
callback = additionalInfo;
|
|
1172
1148
|
additionalInfo = {};
|
|
1173
1149
|
}
|
|
1174
|
-
|
|
1175
1150
|
additionalInfo = additionalInfo || {};
|
|
1176
|
-
|
|
1177
|
-
extend = extend || require('node.extend');
|
|
1178
|
-
|
|
1179
1151
|
if (urlOrPath) {
|
|
1180
1152
|
const parts = urlOrPath.split('/');
|
|
1181
|
-
|
|
1153
|
+
_path = parts.splice(0, parts.length - 1).join('/') + '/';
|
|
1182
1154
|
}
|
|
1183
|
-
|
|
1184
1155
|
// If object was read
|
|
1185
1156
|
if (urlOrPath && typeof urlOrPath === 'object') {
|
|
1186
1157
|
if (typeof callback === 'function') {
|
|
1187
1158
|
callback(null, urlOrPath);
|
|
1188
1159
|
}
|
|
1189
|
-
}
|
|
1160
|
+
}
|
|
1161
|
+
else if (!urlOrPath) {
|
|
1190
1162
|
try {
|
|
1191
|
-
|
|
1192
|
-
|
|
1163
|
+
const controllerDir = getControllerDir();
|
|
1164
|
+
if (controllerDir) {
|
|
1165
|
+
sources = fs_extra_1.default.readJSONSync(path_1.default.join(controllerDir, getDefaultDataDir(), 'sources.json'));
|
|
1166
|
+
}
|
|
1167
|
+
}
|
|
1168
|
+
catch {
|
|
1193
1169
|
sources = {};
|
|
1194
1170
|
}
|
|
1195
1171
|
try {
|
|
1196
|
-
const
|
|
1197
|
-
|
|
1198
|
-
|
|
1172
|
+
const controllerDir = getControllerDir();
|
|
1173
|
+
if (controllerDir) {
|
|
1174
|
+
const sourcesDist = fs_extra_1.default.readJSONSync(path_1.default.join(controllerDir, 'conf', 'sources-dist.json'));
|
|
1175
|
+
sources = extend(true, sourcesDist, sources);
|
|
1176
|
+
}
|
|
1177
|
+
}
|
|
1178
|
+
catch {
|
|
1199
1179
|
// continue regardless of error
|
|
1200
1180
|
}
|
|
1201
|
-
|
|
1202
1181
|
for (const s of Object.keys(sources)) {
|
|
1203
1182
|
if (additionalInfo[s] && additionalInfo[s].published) {
|
|
1204
1183
|
sources[s].published = additionalInfo[s].published;
|
|
1205
1184
|
}
|
|
1206
1185
|
}
|
|
1207
|
-
|
|
1208
|
-
_getRepositoryFile(sources, path, err => {
|
|
1186
|
+
_getRepositoryFile(sources, _path, err => {
|
|
1209
1187
|
if (err) {
|
|
1210
|
-
console.error(`[${new Date()}] ${err}`);
|
|
1188
|
+
console.error(`[${new Date().toString()}] ${err.message}`);
|
|
1211
1189
|
}
|
|
1212
1190
|
if (typeof callback === 'function') {
|
|
1213
1191
|
callback(err, sources);
|
|
1214
1192
|
}
|
|
1215
1193
|
});
|
|
1216
|
-
}
|
|
1194
|
+
}
|
|
1195
|
+
else {
|
|
1217
1196
|
let agent = '';
|
|
1218
1197
|
if (additionalInfo) {
|
|
1219
1198
|
// Add some information to user-agent, like chrome, IE and Firefox do
|
|
1220
|
-
agent = `${additionalInfo.name}, RND: ${additionalInfo.randomID || randomID}, Node:${
|
|
1221
|
-
additionalInfo.node
|
|
1222
|
-
}, V:${additionalInfo.controller}`;
|
|
1199
|
+
agent = `${additionalInfo.name}, RND: ${additionalInfo.randomID || randomID}, Node:${additionalInfo.node}, V:${additionalInfo.controller}`;
|
|
1223
1200
|
}
|
|
1224
|
-
|
|
1225
1201
|
// load hash of file first to not load the whole 1MB of sources
|
|
1226
1202
|
_checkRepositoryFileHash(urlOrPath, additionalInfo, (err, sources, actualSourcesHash) => {
|
|
1227
1203
|
if (!err && sources) {
|
|
1228
1204
|
// Source file was not changed
|
|
1229
1205
|
typeof callback === 'function' && callback(err, sources, actualSourcesHash);
|
|
1230
|
-
}
|
|
1206
|
+
}
|
|
1207
|
+
else {
|
|
1231
1208
|
getJson(urlOrPath, agent, sources => {
|
|
1232
1209
|
if (sources) {
|
|
1233
1210
|
for (const s of Object.keys(sources)) {
|
|
@@ -1235,147 +1212,135 @@ function getRepositoryFile(urlOrPath, additionalInfo, callback) {
|
|
|
1235
1212
|
sources[s].published = additionalInfo[s].published;
|
|
1236
1213
|
}
|
|
1237
1214
|
}
|
|
1238
|
-
setImmediate(() =>
|
|
1239
|
-
|
|
1240
|
-
|
|
1241
|
-
|
|
1242
|
-
|
|
1243
|
-
|
|
1244
|
-
} else {
|
|
1215
|
+
setImmediate(() => _getRepositoryFile(sources, _path, err => {
|
|
1216
|
+
err && console.error(`[${new Date().toString()}] ${err.message}`);
|
|
1217
|
+
typeof callback === 'function' && callback(err, sources, actualSourcesHash);
|
|
1218
|
+
}));
|
|
1219
|
+
}
|
|
1220
|
+
else {
|
|
1245
1221
|
// return cached sources, because no sources found
|
|
1246
|
-
console.log(
|
|
1247
|
-
|
|
1248
|
-
additionalInfo.sources ? 'use cached sources' : 'no cached sources available'
|
|
1249
|
-
}`
|
|
1250
|
-
);
|
|
1251
|
-
return maybeCallbackWithError(
|
|
1252
|
-
callback,
|
|
1253
|
-
`Cannot read "${urlOrPath}"`,
|
|
1254
|
-
additionalInfo.sources,
|
|
1255
|
-
''
|
|
1256
|
-
);
|
|
1222
|
+
console.log(`failed to download new sources, ${additionalInfo.sources ? 'use cached sources' : 'no cached sources available'}`);
|
|
1223
|
+
return maybeCallbackWithError(callback, `Cannot read "${urlOrPath}"`, additionalInfo.sources, '');
|
|
1257
1224
|
}
|
|
1258
1225
|
});
|
|
1259
1226
|
}
|
|
1260
1227
|
});
|
|
1261
1228
|
}
|
|
1262
1229
|
}
|
|
1263
|
-
|
|
1230
|
+
exports.getRepositoryFile = getRepositoryFile;
|
|
1264
1231
|
/**
|
|
1265
1232
|
* Read on repository
|
|
1266
1233
|
*
|
|
1267
1234
|
* @alias getRepositoryFileAsync
|
|
1268
1235
|
* @memberof tools
|
|
1269
|
-
* @param
|
|
1270
|
-
* @param
|
|
1271
|
-
* @param
|
|
1272
|
-
* @param
|
|
1236
|
+
* @param url URL starting with http:// or https:// or local file link
|
|
1237
|
+
* @param hash actual hash
|
|
1238
|
+
* @param force Force repository update despite on hash
|
|
1239
|
+
* @param _actualRepo Actual repository
|
|
1273
1240
|
*
|
|
1274
1241
|
*/
|
|
1275
1242
|
async function getRepositoryFileAsync(url, hash, force, _actualRepo) {
|
|
1276
1243
|
let _hash;
|
|
1277
1244
|
let data;
|
|
1278
|
-
|
|
1279
1245
|
if (url.startsWith('http://') || url.startsWith('https://')) {
|
|
1280
|
-
axios = axios || require('axios');
|
|
1281
1246
|
try {
|
|
1282
|
-
_hash = await
|
|
1283
|
-
}
|
|
1247
|
+
_hash = await (0, axios_1.default)({ url: url.replace(/\.json$/, '-hash.json'), timeout: 10000 });
|
|
1248
|
+
}
|
|
1249
|
+
catch {
|
|
1284
1250
|
// ignore missing hash file
|
|
1285
1251
|
}
|
|
1286
|
-
|
|
1287
1252
|
if (_actualRepo && !force && hash && _hash && _hash.data && _hash.data.hash === hash) {
|
|
1288
1253
|
data = _actualRepo;
|
|
1289
|
-
}
|
|
1290
|
-
|
|
1291
|
-
|
|
1292
|
-
|
|
1254
|
+
}
|
|
1255
|
+
else {
|
|
1256
|
+
const agent = `${exports.appName}, RND: ${randomID}, Node:${process.version}, V:${
|
|
1257
|
+
// eslint-disable-next-line @typescript-eslint/no-var-requires
|
|
1258
|
+
require('@iobroker/js-controller-common/package.json').version}`;
|
|
1293
1259
|
try {
|
|
1294
|
-
data = await
|
|
1260
|
+
data = await (0, axios_1.default)({
|
|
1295
1261
|
url,
|
|
1296
1262
|
timeout: 10000,
|
|
1297
1263
|
headers: { 'User-Agent': agent }
|
|
1298
1264
|
});
|
|
1299
1265
|
data = data.data;
|
|
1300
|
-
}
|
|
1266
|
+
}
|
|
1267
|
+
catch (e) {
|
|
1301
1268
|
throw new Error(`Cannot download repository file from "${url}": ${e.message}`);
|
|
1302
1269
|
}
|
|
1303
1270
|
}
|
|
1304
|
-
}
|
|
1305
|
-
|
|
1271
|
+
}
|
|
1272
|
+
else {
|
|
1273
|
+
if (fs_extra_1.default.existsSync(url)) {
|
|
1306
1274
|
try {
|
|
1307
|
-
data = JSON.parse(
|
|
1308
|
-
}
|
|
1275
|
+
data = JSON.parse(fs_extra_1.default.readFileSync(url).toString('utf8'));
|
|
1276
|
+
}
|
|
1277
|
+
catch (e) {
|
|
1309
1278
|
throw new Error(`Error: Cannot read or parse file "${url}": ${e}`);
|
|
1310
1279
|
}
|
|
1311
|
-
}
|
|
1280
|
+
}
|
|
1281
|
+
else {
|
|
1312
1282
|
throw new Error(`Error: Cannot find file "${url}"`);
|
|
1313
1283
|
}
|
|
1314
1284
|
}
|
|
1315
|
-
|
|
1316
1285
|
return {
|
|
1317
1286
|
json: data,
|
|
1318
1287
|
changed: _hash && _hash.data ? hash !== _hash.data.hash : true,
|
|
1319
1288
|
hash: _hash && _hash.data ? _hash.data.hash : ''
|
|
1320
1289
|
};
|
|
1321
1290
|
}
|
|
1322
|
-
|
|
1291
|
+
exports.getRepositoryFileAsync = getRepositoryFileAsync;
|
|
1323
1292
|
/**
|
|
1324
1293
|
* Sends the given object to the diagnosis server
|
|
1325
1294
|
*
|
|
1326
|
-
* @param
|
|
1327
|
-
* @return {Promise<void>}
|
|
1295
|
+
* @param obj - diagnosis object
|
|
1328
1296
|
*/
|
|
1329
1297
|
async function sendDiagInfo(obj) {
|
|
1330
1298
|
const objStr = JSON.stringify(obj);
|
|
1331
1299
|
console.log(`Send diag info: ${objStr}`);
|
|
1332
|
-
|
|
1333
|
-
const params = new URLSearchParams();
|
|
1300
|
+
const params = new url_1.URLSearchParams();
|
|
1334
1301
|
params.append('data', objStr);
|
|
1335
1302
|
const config = {
|
|
1336
1303
|
headers: { 'Content-Type': 'application/x-www-form-urlencoded' },
|
|
1337
1304
|
timeout: 4000
|
|
1338
1305
|
};
|
|
1339
|
-
|
|
1340
1306
|
try {
|
|
1341
|
-
await
|
|
1342
|
-
}
|
|
1307
|
+
await axios_1.default.post(`http://download.${exports.appName}.net/diag.php`, params, config);
|
|
1308
|
+
}
|
|
1309
|
+
catch (e) {
|
|
1343
1310
|
console.log(`Cannot send diag info: ${e.message}`);
|
|
1344
1311
|
}
|
|
1345
1312
|
}
|
|
1346
|
-
|
|
1313
|
+
exports.sendDiagInfo = sendDiagInfo;
|
|
1347
1314
|
/**
|
|
1348
1315
|
* Finds the adapter directory of a given adapter
|
|
1349
1316
|
*
|
|
1350
1317
|
* @alias getAdapterDir
|
|
1351
1318
|
* @memberof tools
|
|
1352
|
-
* @param
|
|
1353
|
-
* @returns
|
|
1319
|
+
* @param adapter name of the adapter, e.g. hm-rpc
|
|
1320
|
+
* @returns path to adapter directory or null if no directory found
|
|
1354
1321
|
*/
|
|
1355
1322
|
function getAdapterDir(adapter) {
|
|
1356
|
-
const appName = module.exports.appName;
|
|
1357
|
-
|
|
1358
1323
|
// snip off 'iobroker.'
|
|
1359
|
-
if (adapter.startsWith(appName + '.')) {
|
|
1360
|
-
adapter = adapter.substring(appName.length + 1);
|
|
1324
|
+
if (adapter.startsWith(exports.appName + '.')) {
|
|
1325
|
+
adapter = adapter.substring(exports.appName.length + 1);
|
|
1361
1326
|
}
|
|
1362
1327
|
// snip off instance id
|
|
1363
1328
|
if (/\.\d+$/.test(adapter)) {
|
|
1364
1329
|
adapter = adapter.substr(0, adapter.lastIndexOf('.'));
|
|
1365
1330
|
}
|
|
1366
|
-
|
|
1367
|
-
const possibilities = [`${appName.toLowerCase()}.${adapter}/package.json`, `${appName}.${adapter}/package.json`];
|
|
1368
|
-
|
|
1331
|
+
const possibilities = [`${exports.appName.toLowerCase()}.${adapter}/package.json`, `${exports.appName}.${adapter}/package.json`];
|
|
1369
1332
|
/** @type {string} */
|
|
1370
1333
|
let adapterPath;
|
|
1371
1334
|
for (const possibility of possibilities) {
|
|
1372
1335
|
// special case to not read adapters from js-controller/node_module/adapter and check first in parent directory
|
|
1373
|
-
if (
|
|
1336
|
+
if (fs_extra_1.default.existsSync(`${__dirname}/../../${possibility}`)) {
|
|
1374
1337
|
adapterPath = `${__dirname}/../../${possibility}`;
|
|
1375
|
-
}
|
|
1338
|
+
}
|
|
1339
|
+
else {
|
|
1376
1340
|
try {
|
|
1377
1341
|
adapterPath = require.resolve(possibility);
|
|
1378
|
-
}
|
|
1342
|
+
}
|
|
1343
|
+
catch {
|
|
1379
1344
|
// not found
|
|
1380
1345
|
}
|
|
1381
1346
|
}
|
|
@@ -1383,41 +1348,42 @@ function getAdapterDir(adapter) {
|
|
|
1383
1348
|
break;
|
|
1384
1349
|
}
|
|
1385
1350
|
}
|
|
1386
|
-
|
|
1387
1351
|
if (!adapterPath) {
|
|
1388
1352
|
return null; // inactive
|
|
1389
|
-
}
|
|
1390
|
-
|
|
1353
|
+
}
|
|
1354
|
+
else {
|
|
1355
|
+
const parts = path_1.default.normalize(adapterPath).split(/[\\/]/g);
|
|
1391
1356
|
parts.pop();
|
|
1392
1357
|
return parts.join('/');
|
|
1393
1358
|
}
|
|
1394
1359
|
}
|
|
1395
|
-
|
|
1360
|
+
exports.getAdapterDir = getAdapterDir;
|
|
1396
1361
|
/**
|
|
1397
1362
|
* Returns the hostname of this host
|
|
1398
1363
|
* @alias getHostName
|
|
1399
|
-
* @returns {string}
|
|
1400
1364
|
*/
|
|
1401
1365
|
function getHostName() {
|
|
1366
|
+
var _a;
|
|
1402
1367
|
// for tests purposes
|
|
1403
1368
|
if (process.env.IOB_HOSTNAME) {
|
|
1404
1369
|
return process.env.IOB_HOSTNAME;
|
|
1405
1370
|
}
|
|
1406
1371
|
try {
|
|
1407
1372
|
const configName = getConfigFileName();
|
|
1408
|
-
const config =
|
|
1409
|
-
return
|
|
1410
|
-
}
|
|
1411
|
-
|
|
1373
|
+
const config = fs_extra_1.default.readJSONSync(configName);
|
|
1374
|
+
return ((_a = config.system) === null || _a === void 0 ? void 0 : _a.hostname) || os_1.default.hostname();
|
|
1375
|
+
}
|
|
1376
|
+
catch {
|
|
1377
|
+
return os_1.default.hostname();
|
|
1412
1378
|
}
|
|
1413
1379
|
}
|
|
1414
|
-
|
|
1380
|
+
exports.getHostName = getHostName;
|
|
1415
1381
|
/**
|
|
1416
1382
|
* Read version of system npm
|
|
1417
1383
|
*
|
|
1418
1384
|
* @alias getSystemNpmVersion
|
|
1419
1385
|
* @memberof Tools
|
|
1420
|
-
* @param
|
|
1386
|
+
* @param callback return result
|
|
1421
1387
|
* <pre><code>
|
|
1422
1388
|
* function (err, version) {
|
|
1423
1389
|
* adapter.log.debug('NPM version is: ' + version);
|
|
@@ -1425,27 +1391,27 @@ function getHostName() {
|
|
|
1425
1391
|
* </code></pre>
|
|
1426
1392
|
*/
|
|
1427
1393
|
function getSystemNpmVersion(callback) {
|
|
1428
|
-
|
|
1429
|
-
|
|
1394
|
+
// eslint-disable-next-line @typescript-eslint/no-var-requires
|
|
1395
|
+
const { exec } = require('child_process');
|
|
1430
1396
|
// remove local node_modules\.bin dir from path
|
|
1431
1397
|
// or we potentially get a wrong npm version
|
|
1432
1398
|
const newEnv = Object.assign({}, process.env);
|
|
1399
|
+
// @ts-expect-error TODO
|
|
1433
1400
|
newEnv.PATH = (newEnv.PATH || newEnv.Path || newEnv.path)
|
|
1434
|
-
.split(
|
|
1401
|
+
.split(path_1.default.delimiter)
|
|
1435
1402
|
.filter(dir => {
|
|
1436
|
-
|
|
1437
|
-
|
|
1438
|
-
|
|
1439
|
-
.join(
|
|
1403
|
+
dir = dir.toLowerCase();
|
|
1404
|
+
return !dir.includes('iobroker') || !dir.includes(path_1.default.join('node_modules', '.bin'));
|
|
1405
|
+
})
|
|
1406
|
+
.join(path_1.default.delimiter);
|
|
1440
1407
|
try {
|
|
1441
1408
|
let timeout = setTimeout(() => {
|
|
1442
1409
|
timeout = null;
|
|
1443
1410
|
if (callback) {
|
|
1444
|
-
callback('timeout');
|
|
1445
|
-
callback =
|
|
1411
|
+
callback(new Error('timeout'));
|
|
1412
|
+
callback = undefined;
|
|
1446
1413
|
}
|
|
1447
1414
|
}, 10000);
|
|
1448
|
-
|
|
1449
1415
|
exec('npm -v', { encoding: 'utf8', env: newEnv, windowsHide: true }, (error, stdout) => {
|
|
1450
1416
|
//, stderr) {
|
|
1451
1417
|
if (timeout) {
|
|
@@ -1453,67 +1419,54 @@ function getSystemNpmVersion(callback) {
|
|
|
1453
1419
|
timeout = null;
|
|
1454
1420
|
}
|
|
1455
1421
|
if (stdout) {
|
|
1456
|
-
stdout =
|
|
1422
|
+
stdout = semver_1.default.valid(stdout.trim());
|
|
1457
1423
|
}
|
|
1458
1424
|
if (callback) {
|
|
1459
1425
|
callback(error, stdout);
|
|
1460
|
-
callback =
|
|
1426
|
+
callback = undefined;
|
|
1461
1427
|
}
|
|
1462
1428
|
});
|
|
1463
|
-
}
|
|
1429
|
+
}
|
|
1430
|
+
catch (e) {
|
|
1464
1431
|
if (callback) {
|
|
1465
1432
|
callback(e);
|
|
1466
|
-
callback =
|
|
1433
|
+
callback = undefined;
|
|
1467
1434
|
}
|
|
1468
1435
|
}
|
|
1469
1436
|
}
|
|
1470
|
-
|
|
1471
1437
|
const getSystemNpmVersionAsync = promisify(getSystemNpmVersion);
|
|
1472
|
-
|
|
1473
1438
|
/**
|
|
1474
1439
|
* @private
|
|
1475
1440
|
* Figure out which package manager is in charge, but with a fallback to npm.
|
|
1476
|
-
* @param
|
|
1477
|
-
* @returns {Promise<PackageManager>}
|
|
1441
|
+
* @param cwd Which directory to work in. If none is given, this defaults to ioBroker's root directory.
|
|
1478
1442
|
*/
|
|
1479
1443
|
async function detectPackageManagerWithFallback(cwd) {
|
|
1480
1444
|
try {
|
|
1481
1445
|
// For the first attempt, use pak's default of requiring a lockfile. This makes sure we find ioBroker's root dir
|
|
1482
|
-
return await detectPackageManager(
|
|
1483
|
-
|
|
1484
|
-
|
|
1485
|
-
|
|
1486
|
-
|
|
1487
|
-
|
|
1488
|
-
|
|
1489
|
-
|
|
1490
|
-
|
|
1491
|
-
|
|
1492
|
-
} catch {
|
|
1446
|
+
return await (0, pak_1.detectPackageManager)(typeof cwd === 'string'
|
|
1447
|
+
? // If a cwd was provided, use it
|
|
1448
|
+
{ cwd }
|
|
1449
|
+
: // Otherwise try to find the ioBroker root dir
|
|
1450
|
+
{
|
|
1451
|
+
cwd: __dirname,
|
|
1452
|
+
setCwdToPackageRoot: true
|
|
1453
|
+
});
|
|
1454
|
+
}
|
|
1455
|
+
catch {
|
|
1493
1456
|
// Lockfile not found, use default to avoid picking up a wrong package manager
|
|
1494
1457
|
// like a globally installed yarn
|
|
1495
1458
|
}
|
|
1496
|
-
|
|
1497
1459
|
// Since we have no lockfile to rely on, assume the root dir is 2 levels above js-controller
|
|
1498
|
-
const ioBrokerRootDir =
|
|
1460
|
+
const ioBrokerRootDir = path_1.default.join(getControllerDir(), '..', '..');
|
|
1499
1461
|
// And fallback to npm
|
|
1500
|
-
const pak = new packageManagers.npm();
|
|
1462
|
+
const pak = new pak_1.packageManagers.npm();
|
|
1501
1463
|
pak.cwd = cwd || ioBrokerRootDir;
|
|
1502
1464
|
return pak;
|
|
1503
1465
|
}
|
|
1504
|
-
|
|
1505
|
-
/**
|
|
1506
|
-
* @typedef {object} InstallNodeModuleOptions
|
|
1507
|
-
* @property {boolean} [unsafePerm] Whether the `--unsafe-perm` flag should be used
|
|
1508
|
-
* @property {boolean} [debug] Whether to include `stderr` in the output and increase the loglevel to include more than errors
|
|
1509
|
-
* @property {string} [cwd] Which directory to work in. If none is given, this defaults to ioBroker's root directory.
|
|
1510
|
-
*/
|
|
1511
|
-
|
|
1512
1466
|
/**
|
|
1513
1467
|
* Installs a node module using npm or a similar package manager
|
|
1514
|
-
* @param
|
|
1515
|
-
* @param
|
|
1516
|
-
* @returns {Promise<import('@alcalzone/pak').CommandResult>}
|
|
1468
|
+
* @param npmUrl Which node module to install
|
|
1469
|
+
* @param options Options for the installation
|
|
1517
1470
|
*/
|
|
1518
1471
|
async function installNodeModule(npmUrl, options = {}) {
|
|
1519
1472
|
// Figure out which package manager is in charge (probably npm at this point)
|
|
@@ -1522,38 +1475,29 @@ async function installNodeModule(npmUrl, options = {}) {
|
|
|
1522
1475
|
if (!options.debug) {
|
|
1523
1476
|
pak.loglevel = 'error';
|
|
1524
1477
|
}
|
|
1525
|
-
|
|
1526
1478
|
// Set up streams to pass the command output through
|
|
1527
1479
|
if (options.debug) {
|
|
1528
|
-
const stdall = new PassThrough();
|
|
1480
|
+
const stdall = new stream_1.PassThrough();
|
|
1529
1481
|
pak.stdall = stdall;
|
|
1530
1482
|
pipeLinewise(stdall, process.stdout);
|
|
1531
|
-
}
|
|
1532
|
-
|
|
1483
|
+
}
|
|
1484
|
+
else {
|
|
1485
|
+
const stdout = new stream_1.PassThrough();
|
|
1533
1486
|
pak.stdout = stdout;
|
|
1534
1487
|
pipeLinewise(stdout, process.stdout);
|
|
1535
1488
|
}
|
|
1536
|
-
|
|
1537
1489
|
// And install the module
|
|
1538
|
-
/** @type {import('@alcalzone/pak').InstallOptions} */
|
|
1539
1490
|
const installOpts = {};
|
|
1540
1491
|
if (options.unsafePerm) {
|
|
1541
1492
|
installOpts.additionalArgs = ['--unsafe-perm'];
|
|
1542
1493
|
}
|
|
1543
1494
|
return pak.install([npmUrl], installOpts);
|
|
1544
1495
|
}
|
|
1545
|
-
|
|
1546
|
-
/**
|
|
1547
|
-
* @typedef {object} UninstallNodeModuleOptions
|
|
1548
|
-
* @property {boolean} [debug] Whether to include `stderr` in the output and increase the loglevel to include more than errors
|
|
1549
|
-
* @property {string} [cwd] Which directory to work in. If none is given, this defaults to ioBroker's root directory.
|
|
1550
|
-
*/
|
|
1551
|
-
|
|
1496
|
+
exports.installNodeModule = installNodeModule;
|
|
1552
1497
|
/**
|
|
1553
1498
|
* Uninstalls a node module using npm or a similar package manager
|
|
1554
|
-
* @param
|
|
1555
|
-
* @param
|
|
1556
|
-
* @returns {Promise<import('@alcalzone/pak').CommandResult>}
|
|
1499
|
+
* @param packageName Which node module to uninstall
|
|
1500
|
+
* @param options Options for the installation
|
|
1557
1501
|
*/
|
|
1558
1502
|
async function uninstallNodeModule(packageName, options = {}) {
|
|
1559
1503
|
// Figure out which package manager is in charge (probably npm at this point)
|
|
@@ -1562,33 +1506,24 @@ async function uninstallNodeModule(packageName, options = {}) {
|
|
|
1562
1506
|
if (!options.debug) {
|
|
1563
1507
|
pak.loglevel = 'error';
|
|
1564
1508
|
}
|
|
1565
|
-
|
|
1566
1509
|
// Set up streams to pass the command output through
|
|
1567
1510
|
if (options.debug) {
|
|
1568
|
-
const stdall = new PassThrough();
|
|
1511
|
+
const stdall = new stream_1.PassThrough();
|
|
1569
1512
|
pak.stdall = stdall;
|
|
1570
1513
|
pipeLinewise(stdall, process.stdout);
|
|
1571
|
-
}
|
|
1572
|
-
|
|
1514
|
+
}
|
|
1515
|
+
else {
|
|
1516
|
+
const stdout = new stream_1.PassThrough();
|
|
1573
1517
|
pak.stdout = stdout;
|
|
1574
1518
|
pipeLinewise(stdout, process.stdout);
|
|
1575
1519
|
}
|
|
1576
|
-
|
|
1577
1520
|
return pak.uninstall([packageName]);
|
|
1578
1521
|
}
|
|
1579
|
-
|
|
1580
|
-
/**
|
|
1581
|
-
* @typedef {object} RebuildNodeModulesOptions
|
|
1582
|
-
* @property {boolean} [debug] Whether to include `stderr` in the output and increase the loglevel to include more than errors
|
|
1583
|
-
* @property {string} [cwd] Which directory to work in. If none is given, this defaults to ioBroker's root directory.
|
|
1584
|
-
* @property {string} [module] single module which needs rebuild, can contain version too like module@version
|
|
1585
|
-
*/
|
|
1586
|
-
|
|
1522
|
+
exports.uninstallNodeModule = uninstallNodeModule;
|
|
1587
1523
|
/**
|
|
1588
1524
|
* Rebuilds all native node_modules that are dependencies of the project in the current working directory / project root.
|
|
1589
1525
|
* If `options.cwd` is given, the directory must contain a lockfile.
|
|
1590
|
-
* @param
|
|
1591
|
-
* @returns {Promise<import('@alcalzone/pak').CommandResult>}
|
|
1526
|
+
* @param options Options for the rebuild
|
|
1592
1527
|
*/
|
|
1593
1528
|
async function rebuildNodeModules(options = {}) {
|
|
1594
1529
|
// Figure out which package manager is in charge (probably npm at this point)
|
|
@@ -1597,28 +1532,27 @@ async function rebuildNodeModules(options = {}) {
|
|
|
1597
1532
|
if (!options.debug) {
|
|
1598
1533
|
pak.loglevel = 'error';
|
|
1599
1534
|
}
|
|
1600
|
-
|
|
1601
1535
|
// Set up streams to pass the command output through
|
|
1602
1536
|
if (options.debug) {
|
|
1603
|
-
const stdall = new PassThrough();
|
|
1537
|
+
const stdall = new stream_1.PassThrough();
|
|
1604
1538
|
pak.stdall = stdall;
|
|
1605
1539
|
pipeLinewise(stdall, process.stdout);
|
|
1606
|
-
}
|
|
1607
|
-
|
|
1540
|
+
}
|
|
1541
|
+
else {
|
|
1542
|
+
const stdout = new stream_1.PassThrough();
|
|
1608
1543
|
pak.stdout = stdout;
|
|
1609
1544
|
pipeLinewise(stdout, process.stdout);
|
|
1610
1545
|
}
|
|
1611
|
-
|
|
1612
1546
|
return pak.rebuild(options.module ? [options.module] : undefined);
|
|
1613
1547
|
}
|
|
1614
|
-
|
|
1548
|
+
exports.rebuildNodeModules = rebuildNodeModules;
|
|
1615
1549
|
/**
|
|
1616
1550
|
* Read disk free space
|
|
1617
1551
|
*
|
|
1618
1552
|
* @alias getDiskInfo
|
|
1619
1553
|
* @memberof Tools
|
|
1620
|
-
* @param
|
|
1621
|
-
* @param
|
|
1554
|
+
* @param platform result of os.platform() (win32 => Windows, darwin => OSX)
|
|
1555
|
+
* @param callback return result
|
|
1622
1556
|
* <pre><code>
|
|
1623
1557
|
* function (err, infos) {
|
|
1624
1558
|
* adapter.log.debug('Disks sizes is: ' + info['Disk size'] + ' - ' + info['Disk free']);
|
|
@@ -1626,19 +1560,20 @@ async function rebuildNodeModules(options = {}) {
|
|
|
1626
1560
|
* </code></pre>
|
|
1627
1561
|
*/
|
|
1628
1562
|
function getDiskInfo(platform, callback) {
|
|
1629
|
-
platform = platform ||
|
|
1563
|
+
platform = platform || os_1.default.platform();
|
|
1630
1564
|
if (diskusage) {
|
|
1631
1565
|
try {
|
|
1632
1566
|
const path = platform === 'win32' ? __dirname.substring(0, 2) : '/';
|
|
1633
1567
|
const info = diskusage.checkSync(path);
|
|
1634
1568
|
return callback && callback(null, { 'Disk size': info.total, 'Disk free': info.free });
|
|
1635
|
-
}
|
|
1569
|
+
}
|
|
1570
|
+
catch (err) {
|
|
1636
1571
|
console.log(err);
|
|
1637
1572
|
}
|
|
1638
|
-
}
|
|
1639
|
-
|
|
1573
|
+
}
|
|
1574
|
+
else {
|
|
1640
1575
|
try {
|
|
1641
|
-
if (platform === '
|
|
1576
|
+
if (platform === 'win32') {
|
|
1642
1577
|
// Caption FreeSpace Size
|
|
1643
1578
|
// A:
|
|
1644
1579
|
// C: 66993807360 214640357376
|
|
@@ -1646,65 +1581,58 @@ function getDiskInfo(platform, callback) {
|
|
|
1646
1581
|
// Y: 116649795584 148368257024
|
|
1647
1582
|
// Z: 116649795584 148368257024
|
|
1648
1583
|
const disk = __dirname.substring(0, 2).toUpperCase();
|
|
1649
|
-
|
|
1650
|
-
|
|
1651
|
-
|
|
1652
|
-
|
|
1653
|
-
|
|
1654
|
-
|
|
1655
|
-
|
|
1656
|
-
|
|
1657
|
-
|
|
1658
|
-
|
|
1659
|
-
|
|
1660
|
-
|
|
1661
|
-
|
|
1662
|
-
|
|
1663
|
-
|
|
1664
|
-
|
|
1665
|
-
|
|
1666
|
-
|
|
1667
|
-
callback &&
|
|
1668
|
-
callback(error, {
|
|
1669
|
-
'Disk size': parseInt(parts[2]),
|
|
1670
|
-
'Disk free': parseInt(parts[1])
|
|
1671
|
-
})
|
|
1672
|
-
);
|
|
1673
|
-
}
|
|
1584
|
+
(0, child_process_1.exec)('wmic logicaldisk get size,freespace,caption', {
|
|
1585
|
+
encoding: 'utf8',
|
|
1586
|
+
windowsHide: true
|
|
1587
|
+
}, (error, stdout) => {
|
|
1588
|
+
//, stderr) {
|
|
1589
|
+
if (stdout) {
|
|
1590
|
+
const lines = stdout.split('\n');
|
|
1591
|
+
const line = lines.find(line => {
|
|
1592
|
+
const parts = line.split(/\s+/);
|
|
1593
|
+
return parts[0].toUpperCase() === disk;
|
|
1594
|
+
});
|
|
1595
|
+
if (line) {
|
|
1596
|
+
const parts = line.split(/\s+/);
|
|
1597
|
+
return (callback &&
|
|
1598
|
+
callback(error, {
|
|
1599
|
+
'Disk size': parseInt(parts[2]),
|
|
1600
|
+
'Disk free': parseInt(parts[1])
|
|
1601
|
+
}));
|
|
1674
1602
|
}
|
|
1675
|
-
callback && callback(error, null);
|
|
1676
1603
|
}
|
|
1677
|
-
|
|
1678
|
-
|
|
1679
|
-
|
|
1604
|
+
callback && callback(error, null);
|
|
1605
|
+
});
|
|
1606
|
+
}
|
|
1607
|
+
else {
|
|
1608
|
+
(0, child_process_1.exec)('df -k /', { encoding: 'utf8', windowsHide: true }, (error, stdout) => {
|
|
1680
1609
|
//, stderr) {
|
|
1681
1610
|
// Filesystem 1K-blocks Used Available Use% Mounted on
|
|
1682
1611
|
// /dev/mapper/vg00-lv01 162544556 9966192 145767152 7% /
|
|
1683
1612
|
try {
|
|
1684
1613
|
if (stdout) {
|
|
1685
1614
|
const parts = stdout.split('\n')[1].split(/\s+/);
|
|
1686
|
-
return (
|
|
1687
|
-
callback &&
|
|
1615
|
+
return (callback &&
|
|
1688
1616
|
callback(error, {
|
|
1689
1617
|
'Disk size': parseInt(parts[1]) * 1024,
|
|
1690
1618
|
'Disk free': parseInt(parts[3]) * 1024
|
|
1691
|
-
})
|
|
1692
|
-
);
|
|
1619
|
+
}));
|
|
1693
1620
|
}
|
|
1694
|
-
}
|
|
1621
|
+
}
|
|
1622
|
+
catch {
|
|
1695
1623
|
// continue regardless of error
|
|
1696
1624
|
}
|
|
1697
1625
|
callback && callback(error, null);
|
|
1698
1626
|
});
|
|
1699
1627
|
}
|
|
1700
|
-
}
|
|
1628
|
+
}
|
|
1629
|
+
catch (e) {
|
|
1701
1630
|
callback && callback(e, null);
|
|
1702
1631
|
}
|
|
1703
1632
|
}
|
|
1704
1633
|
}
|
|
1705
|
-
|
|
1634
|
+
exports.getDiskInfo = getDiskInfo;
|
|
1706
1635
|
const getDiskInfoAsync = promisify(getDiskInfo);
|
|
1707
|
-
|
|
1708
1636
|
/**
|
|
1709
1637
|
* Returns information about a certificate
|
|
1710
1638
|
*
|
|
@@ -1724,28 +1652,23 @@ const getDiskInfoAsync = promisify(getDiskInfo);
|
|
|
1724
1652
|
*
|
|
1725
1653
|
* @alias getCertificateInfo
|
|
1726
1654
|
* @memberof Tools
|
|
1727
|
-
* @param
|
|
1655
|
+
* @param cert
|
|
1728
1656
|
* @return certificate information object
|
|
1729
1657
|
*/
|
|
1730
1658
|
function getCertificateInfo(cert) {
|
|
1731
1659
|
let info = null;
|
|
1732
|
-
|
|
1733
1660
|
if (!cert) {
|
|
1734
1661
|
return null;
|
|
1735
1662
|
}
|
|
1736
|
-
|
|
1737
|
-
forge.options.usePureJavaScript = false;
|
|
1738
|
-
const pki = forge.pki;
|
|
1739
|
-
|
|
1663
|
+
const pki = node_forge_1.default.pki;
|
|
1740
1664
|
let certFile = null;
|
|
1741
1665
|
try {
|
|
1742
|
-
if (typeof cert === 'string' && cert.length < 1024 &&
|
|
1666
|
+
if (typeof cert === 'string' && cert.length < 1024 && fs_extra_1.default.existsSync(cert)) {
|
|
1743
1667
|
certFile = cert;
|
|
1744
|
-
cert =
|
|
1668
|
+
cert = fs_extra_1.default.readFileSync(cert, 'utf8');
|
|
1745
1669
|
}
|
|
1746
|
-
|
|
1670
|
+
// cast to any we use some undocumented? properties below
|
|
1747
1671
|
const crt = pki.certificateFromPem(cert);
|
|
1748
|
-
|
|
1749
1672
|
info = {
|
|
1750
1673
|
certificateFilename: certFile,
|
|
1751
1674
|
certificate: cert,
|
|
@@ -1760,16 +1683,16 @@ function getCertificateInfo(cert) {
|
|
|
1760
1683
|
validityNotBefore: crt.validity.notBefore,
|
|
1761
1684
|
validityNotAfter: crt.validity.notAfter
|
|
1762
1685
|
};
|
|
1763
|
-
|
|
1764
1686
|
// do not return info about values
|
|
1765
1687
|
delete info.keyUsage.value;
|
|
1766
1688
|
delete info.extKeyUsage.value;
|
|
1767
1689
|
return info;
|
|
1768
|
-
}
|
|
1690
|
+
}
|
|
1691
|
+
catch {
|
|
1769
1692
|
return null;
|
|
1770
1693
|
}
|
|
1771
1694
|
}
|
|
1772
|
-
|
|
1695
|
+
exports.getCertificateInfo = getCertificateInfo;
|
|
1773
1696
|
/**
|
|
1774
1697
|
* Returns default SSL certificates (private and public)
|
|
1775
1698
|
*
|
|
@@ -1780,44 +1703,31 @@ function getCertificateInfo(cert) {
|
|
|
1780
1703
|
*
|
|
1781
1704
|
* @alias generateDefaultCertificates
|
|
1782
1705
|
* @memberof Tools
|
|
1783
|
-
* @returns {{ defaultPrivate: any[];defaultPublic: any[] }}
|
|
1784
1706
|
* <pre><code>
|
|
1785
1707
|
* const certificates = tools.generateDefaultCertificates();
|
|
1786
1708
|
* </code></pre>
|
|
1787
1709
|
*/
|
|
1788
1710
|
function generateDefaultCertificates() {
|
|
1789
|
-
|
|
1790
|
-
// like its secure random number generator, you may set the forge.options.usePureJavaScript flag to true. It
|
|
1791
|
-
// is not recommended that you set this flag as native code is typically more performant and may have stronger
|
|
1792
|
-
// security properties. It may be useful to set this flag to test certain features that you plan to run in
|
|
1793
|
-
// environments that are different from your testing environment.
|
|
1794
|
-
// https://github.com/digitalbazaar/forge
|
|
1795
|
-
forge.options.usePureJavaScript = false;
|
|
1796
|
-
const pki = forge.pki;
|
|
1711
|
+
const pki = node_forge_1.default.pki;
|
|
1797
1712
|
const keys = pki.rsa.generateKeyPair({ bits: 2048, e: 0x10001 });
|
|
1798
1713
|
const cert = pki.createCertificate();
|
|
1799
|
-
|
|
1800
1714
|
cert.publicKey = keys.publicKey;
|
|
1801
|
-
cert.serialNumber =
|
|
1715
|
+
cert.serialNumber = `0${makeid(17)}`;
|
|
1802
1716
|
cert.validity.notBefore = new Date();
|
|
1803
1717
|
cert.validity.notAfter = new Date();
|
|
1804
1718
|
cert.validity.notAfter.setFullYear(cert.validity.notBefore.getFullYear() + 1);
|
|
1805
|
-
|
|
1806
1719
|
const subAttrs = [
|
|
1807
1720
|
{ name: 'commonName', value: getHostName() },
|
|
1808
1721
|
{ name: 'organizationName', value: 'ioBroker GmbH' },
|
|
1809
1722
|
{ shortName: 'OU', value: 'iobroker' }
|
|
1810
1723
|
];
|
|
1811
|
-
|
|
1812
1724
|
const issAttrs = [
|
|
1813
1725
|
{ name: 'commonName', value: 'iobroker' },
|
|
1814
1726
|
{ name: 'organizationName', value: 'ioBroker GmbH' },
|
|
1815
1727
|
{ shortName: 'OU', value: 'iobroker' }
|
|
1816
1728
|
];
|
|
1817
|
-
|
|
1818
1729
|
cert.setSubject(subAttrs);
|
|
1819
1730
|
cert.setIssuer(issAttrs);
|
|
1820
|
-
|
|
1821
1731
|
cert.setExtensions([
|
|
1822
1732
|
{
|
|
1823
1733
|
name: 'basicConstraints',
|
|
@@ -1842,7 +1752,7 @@ function generateDefaultCertificates() {
|
|
|
1842
1752
|
altNames: [
|
|
1843
1753
|
{
|
|
1844
1754
|
type: 2,
|
|
1845
|
-
value:
|
|
1755
|
+
value: os_1.default.hostname()
|
|
1846
1756
|
}
|
|
1847
1757
|
]
|
|
1848
1758
|
},
|
|
@@ -1861,21 +1771,15 @@ function generateDefaultCertificates() {
|
|
|
1861
1771
|
name: 'authorityKeyIdentifier'
|
|
1862
1772
|
}
|
|
1863
1773
|
]);
|
|
1864
|
-
|
|
1865
|
-
cert.sign(keys.privateKey, forge.md.sha256.create());
|
|
1866
|
-
|
|
1774
|
+
cert.sign(keys.privateKey, node_forge_1.default.md.sha256.create());
|
|
1867
1775
|
const pem_pkey = pki.privateKeyToPem(keys.privateKey);
|
|
1868
1776
|
const pem_cert = pki.certificateToPem(cert);
|
|
1869
|
-
|
|
1870
|
-
//console.log(pem_pkey);
|
|
1871
|
-
//console.log(pem_cert);
|
|
1872
|
-
|
|
1873
1777
|
return {
|
|
1874
1778
|
defaultPrivate: pem_pkey,
|
|
1875
1779
|
defaultPublic: pem_cert
|
|
1876
1780
|
};
|
|
1877
1781
|
}
|
|
1878
|
-
|
|
1782
|
+
exports.generateDefaultCertificates = generateDefaultCertificates;
|
|
1879
1783
|
function makeid(length) {
|
|
1880
1784
|
let result = '';
|
|
1881
1785
|
const characters = 'abcdef0123456789';
|
|
@@ -1885,7 +1789,6 @@ function makeid(length) {
|
|
|
1885
1789
|
}
|
|
1886
1790
|
return result;
|
|
1887
1791
|
}
|
|
1888
|
-
|
|
1889
1792
|
/**
|
|
1890
1793
|
* Collects information about host and available adapters
|
|
1891
1794
|
*
|
|
@@ -1896,50 +1799,46 @@ function makeid(length) {
|
|
|
1896
1799
|
*
|
|
1897
1800
|
* @alias getHostInfo
|
|
1898
1801
|
* @memberof Tools
|
|
1899
|
-
* @param
|
|
1802
|
+
* @param objects db
|
|
1803
|
+
* @param callback
|
|
1900
1804
|
* <pre><code>
|
|
1901
|
-
* function (
|
|
1805
|
+
* function (result) {
|
|
1902
1806
|
* adapter.log.debug('Info about host: ' + JSON.stringify(result, null, 2);
|
|
1903
1807
|
* }
|
|
1904
1808
|
* </code></pre>
|
|
1905
1809
|
*/
|
|
1906
1810
|
async function getHostInfo(objects, callback) {
|
|
1907
|
-
|
|
1908
|
-
|
|
1909
|
-
if (diskusage !== false) {
|
|
1811
|
+
if (diskusage) {
|
|
1910
1812
|
try {
|
|
1911
1813
|
diskusage = diskusage || require('diskusage');
|
|
1912
|
-
}
|
|
1913
|
-
|
|
1814
|
+
}
|
|
1815
|
+
catch {
|
|
1816
|
+
// ignore
|
|
1914
1817
|
}
|
|
1915
1818
|
}
|
|
1916
|
-
|
|
1917
|
-
const cpus = os.cpus();
|
|
1819
|
+
const cpus = os_1.default.cpus();
|
|
1918
1820
|
const dateObj = new Date();
|
|
1919
|
-
|
|
1920
1821
|
const data = {
|
|
1921
|
-
Platform: isDocker() ? 'docker' :
|
|
1822
|
+
Platform: isDocker() ? 'docker' : os_1.default.platform(),
|
|
1922
1823
|
os: process.platform,
|
|
1923
|
-
Architecture:
|
|
1824
|
+
Architecture: os_1.default.arch(),
|
|
1924
1825
|
CPUs: cpus && Array.isArray(cpus) ? cpus.length : null,
|
|
1925
1826
|
Speed: cpus && Array.isArray(cpus) ? cpus[0].speed : null,
|
|
1926
1827
|
Model: cpus && Array.isArray(cpus) ? cpus[0].model : null,
|
|
1927
|
-
RAM:
|
|
1928
|
-
'System uptime': Math.round(
|
|
1828
|
+
RAM: os_1.default.totalmem(),
|
|
1829
|
+
'System uptime': Math.round(os_1.default.uptime()),
|
|
1929
1830
|
'Node.js': process.version,
|
|
1930
|
-
time: dateObj.getTime(),
|
|
1831
|
+
time: dateObj.getTime(),
|
|
1931
1832
|
timeOffset: dateObj.getTimezoneOffset()
|
|
1932
1833
|
};
|
|
1933
|
-
|
|
1934
1834
|
if (data.Platform === 'win32') {
|
|
1935
1835
|
data.Platform = 'Windows';
|
|
1936
|
-
}
|
|
1836
|
+
}
|
|
1837
|
+
else if (data.Platform === 'darwin') {
|
|
1937
1838
|
data.Platform = 'OSX';
|
|
1938
1839
|
}
|
|
1939
|
-
|
|
1940
1840
|
const systemConfig = await objects.getObjectAsync('system.config');
|
|
1941
1841
|
const systemRepos = await objects.getObjectAsync('system.repositories');
|
|
1942
|
-
|
|
1943
1842
|
// Check if repositories exists
|
|
1944
1843
|
const allRepos = {};
|
|
1945
1844
|
if (systemRepos && systemRepos.native && systemRepos.native.repositories) {
|
|
@@ -1949,37 +1848,37 @@ async function getHostInfo(objects, callback) {
|
|
|
1949
1848
|
repos
|
|
1950
1849
|
.filter(repo => systemRepos.native.repositories[repo] && systemRepos.native.repositories[repo].json)
|
|
1951
1850
|
.forEach(repo => Object.assign(allRepos, systemRepos.native.repositories[repo].json));
|
|
1952
|
-
|
|
1953
1851
|
data['adapters count'] = Object.keys(allRepos).length;
|
|
1954
1852
|
}
|
|
1955
|
-
|
|
1956
1853
|
if (!npmVersion) {
|
|
1957
1854
|
try {
|
|
1958
1855
|
const version = await getSystemNpmVersionAsync();
|
|
1959
|
-
data
|
|
1856
|
+
data.NPM = 'v' + (version || ' ---');
|
|
1960
1857
|
npmVersion = version;
|
|
1961
|
-
} catch (e) {
|
|
1962
|
-
console.error('Cannot get NPM version: ' + e);
|
|
1963
1858
|
}
|
|
1964
|
-
|
|
1965
|
-
|
|
1859
|
+
catch (e) {
|
|
1860
|
+
console.error(`Cannot get NPM version: ${e.message}`);
|
|
1861
|
+
}
|
|
1862
|
+
}
|
|
1863
|
+
else {
|
|
1864
|
+
data.NPM = npmVersion;
|
|
1966
1865
|
}
|
|
1967
1866
|
try {
|
|
1968
1867
|
const info = await getDiskInfoAsync(data.Platform);
|
|
1969
1868
|
if (info) {
|
|
1970
1869
|
Object.assign(data, info);
|
|
1971
1870
|
}
|
|
1972
|
-
}
|
|
1973
|
-
|
|
1871
|
+
}
|
|
1872
|
+
catch (e) {
|
|
1873
|
+
console.error(`Cannot get disk information: ${e.message}`);
|
|
1974
1874
|
}
|
|
1975
1875
|
callback && callback(data);
|
|
1976
|
-
|
|
1977
1876
|
return data;
|
|
1978
1877
|
}
|
|
1979
|
-
|
|
1878
|
+
exports.getHostInfo = getHostInfo;
|
|
1980
1879
|
/**
|
|
1981
1880
|
* Finds the controller root directory
|
|
1982
|
-
* @returns
|
|
1881
|
+
* @returns absolute path to controller dir
|
|
1983
1882
|
*/
|
|
1984
1883
|
function getControllerDir() {
|
|
1985
1884
|
const possibilities = ['iobroker.js-controller', 'ioBroker.js-controller'];
|
|
@@ -1988,106 +1887,100 @@ function getControllerDir() {
|
|
|
1988
1887
|
// package.json is guaranteed to be in the module root folder
|
|
1989
1888
|
// so once that is resolved, take the dirname and we're done
|
|
1990
1889
|
const possiblePath = require.resolve(`${pkg}/package.json`);
|
|
1991
|
-
if (
|
|
1992
|
-
return
|
|
1890
|
+
if (fs_extra_1.default.existsSync(possiblePath)) {
|
|
1891
|
+
return path_1.default.dirname(possiblePath);
|
|
1993
1892
|
}
|
|
1994
|
-
}
|
|
1893
|
+
}
|
|
1894
|
+
catch {
|
|
1995
1895
|
/* not found */
|
|
1996
1896
|
}
|
|
1997
1897
|
}
|
|
1998
|
-
|
|
1999
1898
|
// Apparently, checking vs null/undefined may miss the odd case of controllerPath being ""
|
|
2000
1899
|
// Thus we check for falsyness, which includes failing on an empty path
|
|
2001
|
-
let checkPath =
|
|
1900
|
+
let checkPath = path_1.default.join(__dirname, '../..');
|
|
2002
1901
|
// Also check in the current check dir (along with iobroker.js-controller subdirs)
|
|
2003
1902
|
possibilities.unshift('');
|
|
2004
1903
|
while (true) {
|
|
2005
1904
|
for (const pkg of possibilities) {
|
|
2006
1905
|
try {
|
|
2007
|
-
const possiblePath =
|
|
2008
|
-
if (
|
|
1906
|
+
const possiblePath = path_1.default.join(checkPath, pkg);
|
|
1907
|
+
if (fs_extra_1.default.existsSync(path_1.default.join(possiblePath, 'iob.bat'))) {
|
|
2009
1908
|
return possiblePath;
|
|
2010
1909
|
}
|
|
2011
|
-
}
|
|
1910
|
+
}
|
|
1911
|
+
catch {
|
|
2012
1912
|
// not found, continue with next possiblity
|
|
2013
1913
|
}
|
|
2014
1914
|
}
|
|
2015
|
-
|
|
2016
1915
|
// Controller not found here - go to the parent dir
|
|
2017
|
-
const newPath =
|
|
1916
|
+
const newPath = path_1.default.dirname(checkPath);
|
|
2018
1917
|
if (newPath === checkPath) {
|
|
2019
1918
|
// We already reached the root dir, abort
|
|
2020
1919
|
break;
|
|
2021
1920
|
}
|
|
2022
1921
|
checkPath = newPath;
|
|
2023
1922
|
}
|
|
1923
|
+
throw new Error('Could not determine controller directory');
|
|
2024
1924
|
}
|
|
2025
|
-
|
|
2026
|
-
// All paths are returned always relative to /node_modules/' +
|
|
1925
|
+
exports.getControllerDir = getControllerDir;
|
|
1926
|
+
// All paths are returned always relative to /node_modules/' + appName + '.js-controller
|
|
2027
1927
|
// the result has always "/" as last symbol
|
|
2028
1928
|
function getDefaultDataDir() {
|
|
2029
|
-
if (
|
|
1929
|
+
if (_isDevInstallation()) {
|
|
2030
1930
|
// dev install
|
|
2031
1931
|
return './data/';
|
|
2032
1932
|
}
|
|
2033
|
-
|
|
2034
|
-
const appName = module.exports.appName.toLowerCase();
|
|
2035
|
-
|
|
1933
|
+
const _appName = exports.appName.toLowerCase();
|
|
2036
1934
|
// if debugging with npm5
|
|
2037
|
-
if (
|
|
2038
|
-
return
|
|
2039
|
-
}
|
|
1935
|
+
if (fs_extra_1.default.existsSync(`${__dirname}/../../../node_modules/${_appName}.js-controller`)) {
|
|
1936
|
+
return `../${_appName}-data/`;
|
|
1937
|
+
}
|
|
1938
|
+
else {
|
|
2040
1939
|
// If installed with npm
|
|
2041
|
-
return
|
|
1940
|
+
return `../../${_appName}-data/`;
|
|
2042
1941
|
}
|
|
2043
1942
|
}
|
|
2044
|
-
|
|
1943
|
+
exports.getDefaultDataDir = getDefaultDataDir;
|
|
2045
1944
|
/**
|
|
2046
1945
|
* Returns the path of the config file
|
|
2047
|
-
*
|
|
2048
|
-
* @returns {string}
|
|
2049
1946
|
*/
|
|
2050
1947
|
function getConfigFileName() {
|
|
2051
|
-
/** @type {string|string[]} */
|
|
2052
1948
|
let configDir = __dirname.replace(/\\/g, '/');
|
|
2053
1949
|
configDir = configDir.split('/');
|
|
2054
|
-
const
|
|
2055
|
-
|
|
2056
|
-
if (fs.existsSync(__dirname + '/../../../../packages/controller')) {
|
|
1950
|
+
const _appName = exports.appName.toLowerCase();
|
|
1951
|
+
if (_isDevInstallation()) {
|
|
2057
1952
|
// dev install -> Remove /lib
|
|
2058
|
-
configDir.splice(configDir.length -
|
|
1953
|
+
configDir.splice(configDir.length - 4, 4);
|
|
2059
1954
|
configDir = configDir.join('/');
|
|
2060
1955
|
configDir += '/controller'; // go inside controller dir
|
|
2061
|
-
if (
|
|
2062
|
-
return `${configDir}/conf/${
|
|
2063
|
-
}
|
|
2064
|
-
|
|
1956
|
+
if (fs_extra_1.default.existsSync(`${configDir}/conf/${_appName}.json`)) {
|
|
1957
|
+
return `${configDir}/conf/${_appName}.json`;
|
|
1958
|
+
}
|
|
1959
|
+
else {
|
|
1960
|
+
return `${configDir}/data/${_appName}.json`;
|
|
2065
1961
|
}
|
|
2066
1962
|
}
|
|
2067
|
-
|
|
2068
1963
|
// if debugging with npm5 -> node_modules on e.g. /opt/node_modules
|
|
2069
|
-
if (
|
|
2070
|
-
|
|
2071
|
-
fs.existsSync(`${__dirname}/../../../../../../../node_modules/${appName}.js-controller`)
|
|
2072
|
-
) {
|
|
1964
|
+
if (fs_extra_1.default.existsSync(`${__dirname}/../../../../../../../../node_modules/${_appName.toLowerCase()}.js-controller`) ||
|
|
1965
|
+
fs_extra_1.default.existsSync(`${__dirname}/../../../../../../../../node_modules/${_appName}.js-controller`)) {
|
|
2073
1966
|
// remove /node_modules/' + appName + '.js-controller/lib
|
|
2074
|
-
configDir.splice(configDir.length -
|
|
1967
|
+
configDir.splice(configDir.length - 8, 8);
|
|
2075
1968
|
configDir = configDir.join('/');
|
|
2076
|
-
}
|
|
2077
|
-
|
|
2078
|
-
|
|
1969
|
+
}
|
|
1970
|
+
else {
|
|
1971
|
+
// If installed with npm -> remove node_modules/@iobroker/js-controller-common/src/lib/common
|
|
1972
|
+
configDir.splice(configDir.length - 6, 6);
|
|
2079
1973
|
configDir = configDir.join('/');
|
|
2080
1974
|
}
|
|
2081
|
-
|
|
2082
|
-
return `${configDir}/${appName}-data/${appName}.json`;
|
|
1975
|
+
return `${configDir}/${_appName}-data/${_appName}.json`;
|
|
2083
1976
|
}
|
|
2084
|
-
|
|
1977
|
+
exports.getConfigFileName = getConfigFileName;
|
|
2085
1978
|
/**
|
|
2086
1979
|
* Puts all values from an `arguments` object into an array, starting at the given index
|
|
2087
|
-
* @param
|
|
2088
|
-
* @param
|
|
1980
|
+
* @param argsObj An `arguments` object as passed to a function
|
|
1981
|
+
* @param startIndex The optional index to start taking the arguments from
|
|
2089
1982
|
*/
|
|
2090
|
-
function sliceArgs(argsObj, startIndex) {
|
|
1983
|
+
function sliceArgs(argsObj, startIndex = 0) {
|
|
2091
1984
|
if (startIndex === null || startIndex === undefined) {
|
|
2092
1985
|
startIndex = 0;
|
|
2093
1986
|
}
|
|
@@ -2097,200 +1990,183 @@ function sliceArgs(argsObj, startIndex) {
|
|
|
2097
1990
|
}
|
|
2098
1991
|
return ret;
|
|
2099
1992
|
}
|
|
2100
|
-
|
|
2101
1993
|
/**
|
|
2102
1994
|
* Promisifies a function which returns an error as the first argument in its callback
|
|
2103
|
-
* @param
|
|
2104
|
-
* @param
|
|
2105
|
-
* @param
|
|
1995
|
+
* @param fn The function to promisify
|
|
1996
|
+
* @param context (optional) The context (value of `this` to bind the function to)
|
|
1997
|
+
* @param returnArgNames (optional) If the callback contains multiple arguments,
|
|
2106
1998
|
* you can combine them into one object by passing the names as an array.
|
|
2107
1999
|
* Otherwise the Promise will resolve with an array
|
|
2108
|
-
* @returns {(...args: any[]) => Promise<any>}
|
|
2109
2000
|
*/
|
|
2110
2001
|
function promisify(fn, context, returnArgNames) {
|
|
2111
2002
|
return function () {
|
|
2003
|
+
// eslint-disable-next-line prefer-rest-params
|
|
2112
2004
|
const args = sliceArgs(arguments);
|
|
2113
|
-
// @ts-
|
|
2005
|
+
// @ts-expect-error we cannot know the type of `this`
|
|
2114
2006
|
context = context || this;
|
|
2115
2007
|
return new Promise((resolve, reject) => {
|
|
2116
|
-
fn.apply(
|
|
2117
|
-
|
|
2118
|
-
|
|
2119
|
-
|
|
2120
|
-
|
|
2121
|
-
|
|
2122
|
-
|
|
2123
|
-
|
|
2124
|
-
|
|
2125
|
-
|
|
2126
|
-
|
|
2127
|
-
|
|
2128
|
-
|
|
2129
|
-
|
|
2130
|
-
|
|
2131
|
-
|
|
2132
|
-
|
|
2133
|
-
|
|
2134
|
-
|
|
2135
|
-
|
|
2136
|
-
|
|
2137
|
-
|
|
2138
|
-
ret[returnArgNames[i]] = extraArgs[i];
|
|
2139
|
-
}
|
|
2140
|
-
} else {
|
|
2141
|
-
// we return the raw array
|
|
2142
|
-
ret = extraArgs;
|
|
2008
|
+
fn.apply(context, args.concat([
|
|
2009
|
+
function (error, result) {
|
|
2010
|
+
if (error) {
|
|
2011
|
+
return reject(error instanceof Error ? error : new Error(error));
|
|
2012
|
+
}
|
|
2013
|
+
else {
|
|
2014
|
+
// decide on how we want to return the callback arguments
|
|
2015
|
+
switch (arguments.length) {
|
|
2016
|
+
case 1: // only an error was given
|
|
2017
|
+
return resolve(); // Promise<void>
|
|
2018
|
+
case 2: // a single value (result) was returned
|
|
2019
|
+
return resolve(result);
|
|
2020
|
+
default: {
|
|
2021
|
+
// multiple values should be returned
|
|
2022
|
+
let ret;
|
|
2023
|
+
// eslint-disable-next-line prefer-rest-params
|
|
2024
|
+
const extraArgs = sliceArgs(arguments, 1);
|
|
2025
|
+
if (returnArgNames && returnArgNames.length === extraArgs.length) {
|
|
2026
|
+
// we can build an object
|
|
2027
|
+
ret = {};
|
|
2028
|
+
for (let i = 0; i < returnArgNames.length; i++) {
|
|
2029
|
+
ret[returnArgNames[i]] = extraArgs[i];
|
|
2143
2030
|
}
|
|
2144
|
-
return resolve(ret);
|
|
2145
2031
|
}
|
|
2032
|
+
else {
|
|
2033
|
+
// we return the raw array
|
|
2034
|
+
ret = extraArgs;
|
|
2035
|
+
}
|
|
2036
|
+
return resolve(ret);
|
|
2146
2037
|
}
|
|
2147
2038
|
}
|
|
2148
2039
|
}
|
|
2149
|
-
|
|
2150
|
-
);
|
|
2040
|
+
}
|
|
2041
|
+
]));
|
|
2151
2042
|
});
|
|
2152
2043
|
};
|
|
2153
2044
|
}
|
|
2154
|
-
|
|
2045
|
+
exports.promisify = promisify;
|
|
2155
2046
|
/**
|
|
2156
2047
|
* Promisifies a function which does not provide an error as the first argument in its callback
|
|
2157
|
-
* @param
|
|
2158
|
-
* @param
|
|
2159
|
-
* @param
|
|
2048
|
+
* @param fn The function to promisify
|
|
2049
|
+
* @param context (optional) The context (value of `this` to bind the function to)
|
|
2050
|
+
* @param returnArgNames (optional) If the callback contains multiple arguments,
|
|
2160
2051
|
* you can combine them into one object by passing the names as an array.
|
|
2161
2052
|
* Otherwise the Promise will resolve with an array
|
|
2162
|
-
* @returns {(...args: any[]) => Promise<any>}
|
|
2163
2053
|
*/
|
|
2164
2054
|
function promisifyNoError(fn, context, returnArgNames) {
|
|
2165
2055
|
return function () {
|
|
2056
|
+
// eslint-disable-next-line prefer-rest-params
|
|
2166
2057
|
const args = sliceArgs(arguments);
|
|
2167
|
-
// @ts-
|
|
2058
|
+
// @ts-expect-error we cannot know the type of `this`
|
|
2168
2059
|
context = context || this;
|
|
2169
2060
|
return new Promise((resolve, _reject) => {
|
|
2170
|
-
fn.apply(
|
|
2171
|
-
|
|
2172
|
-
|
|
2173
|
-
|
|
2174
|
-
|
|
2175
|
-
|
|
2176
|
-
|
|
2177
|
-
|
|
2178
|
-
|
|
2179
|
-
|
|
2180
|
-
|
|
2181
|
-
|
|
2182
|
-
|
|
2183
|
-
|
|
2184
|
-
|
|
2185
|
-
|
|
2186
|
-
|
|
2187
|
-
ret =
|
|
2188
|
-
for (let i = 0; i < returnArgNames.length; i++) {
|
|
2189
|
-
ret[returnArgNames[i]] = extraArgs[i];
|
|
2190
|
-
}
|
|
2191
|
-
} else {
|
|
2192
|
-
// we return the raw array
|
|
2193
|
-
ret = extraArgs;
|
|
2061
|
+
fn.apply(context, args.concat([
|
|
2062
|
+
function (result) {
|
|
2063
|
+
// decide on how we want to return the callback arguments
|
|
2064
|
+
switch (arguments.length) {
|
|
2065
|
+
case 0: // no arguments were given
|
|
2066
|
+
return resolve(); // Promise<void>
|
|
2067
|
+
case 1: // a single value (result) was returned
|
|
2068
|
+
return resolve(result);
|
|
2069
|
+
default: {
|
|
2070
|
+
// multiple values should be returned
|
|
2071
|
+
let ret;
|
|
2072
|
+
// eslint-disable-next-line prefer-rest-params
|
|
2073
|
+
const extraArgs = sliceArgs(arguments, 0);
|
|
2074
|
+
if (returnArgNames && returnArgNames.length === extraArgs.length) {
|
|
2075
|
+
// we can build an object
|
|
2076
|
+
ret = {};
|
|
2077
|
+
for (let i = 0; i < returnArgNames.length; i++) {
|
|
2078
|
+
ret[returnArgNames[i]] = extraArgs[i];
|
|
2194
2079
|
}
|
|
2195
|
-
return resolve(ret);
|
|
2196
2080
|
}
|
|
2081
|
+
else {
|
|
2082
|
+
// we return the raw array
|
|
2083
|
+
ret = extraArgs;
|
|
2084
|
+
}
|
|
2085
|
+
return resolve(ret);
|
|
2197
2086
|
}
|
|
2198
2087
|
}
|
|
2199
|
-
|
|
2200
|
-
);
|
|
2088
|
+
}
|
|
2089
|
+
]));
|
|
2201
2090
|
});
|
|
2202
2091
|
};
|
|
2203
2092
|
}
|
|
2204
|
-
|
|
2093
|
+
exports.promisifyNoError = promisifyNoError;
|
|
2205
2094
|
/**
|
|
2206
2095
|
* Creates and executes an array of promises in sequence
|
|
2207
|
-
* @param
|
|
2096
|
+
* @param promiseFactories An array of promise-returning functions
|
|
2208
2097
|
*/
|
|
2209
2098
|
function promiseSequence(promiseFactories) {
|
|
2099
|
+
/** @ts-expect-error don't want to touch now */
|
|
2210
2100
|
return promiseFactories.reduce((promise, factory) => {
|
|
2211
2101
|
return promise.then(result => factory().then(Array.prototype.concat.bind(result)));
|
|
2212
2102
|
}, Promise.resolve([]));
|
|
2213
2103
|
}
|
|
2214
|
-
|
|
2215
|
-
function _setQualityForStates(states, keys, quality
|
|
2216
|
-
|
|
2217
|
-
|
|
2218
|
-
|
|
2219
|
-
|
|
2220
|
-
|
|
2221
|
-
|
|
2222
|
-
|
|
2223
|
-
q: quality
|
|
2224
|
-
},
|
|
2225
|
-
() => setImmediate(_setQualityForStates, states, keys, quality, cb)
|
|
2226
|
-
);
|
|
2104
|
+
exports.promiseSequence = promiseSequence;
|
|
2105
|
+
async function _setQualityForStates(states, keys, quality) {
|
|
2106
|
+
for (const key of keys) {
|
|
2107
|
+
try {
|
|
2108
|
+
await states.setState(key, { ack: null, q: quality });
|
|
2109
|
+
}
|
|
2110
|
+
catch {
|
|
2111
|
+
// ignore
|
|
2112
|
+
}
|
|
2227
2113
|
}
|
|
2228
2114
|
}
|
|
2229
|
-
|
|
2230
2115
|
function setQualityForInstance(objects, states, namespace, q) {
|
|
2231
2116
|
return new Promise((resolve, reject) => {
|
|
2232
|
-
objects.getObjectView(
|
|
2233
|
-
|
|
2234
|
-
|
|
2235
|
-
|
|
2236
|
-
|
|
2237
|
-
|
|
2238
|
-
|
|
2239
|
-
}
|
|
2240
|
-
|
|
2241
|
-
|
|
2242
|
-
|
|
2243
|
-
|
|
2244
|
-
|
|
2245
|
-
|
|
2246
|
-
|
|
2247
|
-
|
|
2248
|
-
// if instance still active, but device is offline
|
|
2249
|
-
if (!(q & 0x10) && id.match(/\.info\.connection$/)) {
|
|
2250
|
-
continue;
|
|
2251
|
-
}
|
|
2252
|
-
keys.push(id);
|
|
2117
|
+
objects.getObjectView('system', 'state', {
|
|
2118
|
+
startkey: `${namespace}.`,
|
|
2119
|
+
endkey: `${namespace}.\u9999`,
|
|
2120
|
+
include_docs: false
|
|
2121
|
+
}, (err, _states) => {
|
|
2122
|
+
if (err) {
|
|
2123
|
+
reject(err);
|
|
2124
|
+
}
|
|
2125
|
+
else {
|
|
2126
|
+
let keys = [];
|
|
2127
|
+
if (_states && _states.rows) {
|
|
2128
|
+
for (let s = 0; s < _states.rows.length; s++) {
|
|
2129
|
+
const id = _states.rows[s].id;
|
|
2130
|
+
// if instance still active, but device is offline
|
|
2131
|
+
if (!(q & 0x10) && id.match(/\.info\.connection$/)) {
|
|
2132
|
+
continue;
|
|
2253
2133
|
}
|
|
2134
|
+
keys.push(id);
|
|
2254
2135
|
}
|
|
2255
|
-
// read all values for IDs
|
|
2256
|
-
states.getStates(keys, (_err, values) => {
|
|
2257
|
-
// Get only states, that have ack = true
|
|
2258
|
-
keys = keys.filter((_id, i) => values[i] && values[i].ack);
|
|
2259
|
-
// update quality code of the states to new one
|
|
2260
|
-
_setQualityForStates(states, keys, q, err => (err ? reject(err) : resolve()));
|
|
2261
|
-
});
|
|
2262
2136
|
}
|
|
2137
|
+
// read all values for IDs
|
|
2138
|
+
states.getStates(keys, async (_err, values) => {
|
|
2139
|
+
// Get only states, that have ack = true
|
|
2140
|
+
keys = keys.filter((_id, i) => values[i] && values[i].ack);
|
|
2141
|
+
// update quality code of the states to new one
|
|
2142
|
+
await _setQualityForStates(states, keys, q);
|
|
2143
|
+
resolve();
|
|
2144
|
+
});
|
|
2263
2145
|
}
|
|
2264
|
-
);
|
|
2146
|
+
});
|
|
2265
2147
|
});
|
|
2266
2148
|
}
|
|
2267
|
-
|
|
2149
|
+
exports.setQualityForInstance = setQualityForInstance;
|
|
2268
2150
|
/**
|
|
2269
2151
|
* Converts ioB pattern into regex.
|
|
2270
|
-
* @param
|
|
2271
|
-
* @returns {string}
|
|
2152
|
+
* @param pattern - Regex string to use it in new RegExp(pattern)
|
|
2272
2153
|
*/
|
|
2273
2154
|
function pattern2RegEx(pattern) {
|
|
2274
2155
|
pattern = (pattern || '').toString();
|
|
2275
|
-
|
|
2276
2156
|
const startsWithWildcard = pattern[0] === '*';
|
|
2277
2157
|
const endsWithWildcard = pattern[pattern.length - 1] === '*';
|
|
2278
|
-
|
|
2279
2158
|
pattern = pattern.replace(/[-/\\^$+?.()|[\]{}]/g, '\\$&').replace(/\*/g, '.*');
|
|
2280
|
-
|
|
2281
2159
|
return (startsWithWildcard ? '' : '^') + pattern + (endsWithWildcard ? '' : '$');
|
|
2282
2160
|
}
|
|
2283
|
-
|
|
2161
|
+
exports.pattern2RegEx = pattern2RegEx;
|
|
2284
2162
|
/**
|
|
2285
2163
|
* Generates a stack trace that can be added to log outputs to trace their source
|
|
2286
|
-
* @param
|
|
2287
|
-
* @returns {string}
|
|
2164
|
+
* @param [wrapperName = 'captureStackTrace'] The wrapper function after which the stack trace should begin
|
|
2288
2165
|
*/
|
|
2289
2166
|
function captureStackTrace(wrapperName) {
|
|
2290
2167
|
if (typeof wrapperName !== 'string') {
|
|
2291
2168
|
wrapperName = 'captureStackTrace';
|
|
2292
2169
|
}
|
|
2293
|
-
|
|
2294
2170
|
const ret = new Error();
|
|
2295
2171
|
if (ret.stack) {
|
|
2296
2172
|
let foundSelf = false;
|
|
@@ -2308,28 +2184,25 @@ function captureStackTrace(wrapperName) {
|
|
|
2308
2184
|
}
|
|
2309
2185
|
return '';
|
|
2310
2186
|
}
|
|
2311
|
-
|
|
2312
2187
|
/**
|
|
2313
2188
|
* Appends the stack trace generated by `captureStackTrace` to the given string
|
|
2314
|
-
* @param
|
|
2315
|
-
* @returns {string}
|
|
2189
|
+
* @param str - The string to append the stack trace to
|
|
2316
2190
|
*/
|
|
2317
2191
|
function appendStackTrace(str) {
|
|
2318
2192
|
// Convert anything that isn't a string into a string
|
|
2319
2193
|
if (typeof str !== 'string') {
|
|
2320
2194
|
str = String(str);
|
|
2321
2195
|
}
|
|
2322
|
-
if (str.
|
|
2196
|
+
if (!str.endsWith('\n')) {
|
|
2323
2197
|
str += '\n';
|
|
2324
2198
|
}
|
|
2325
2199
|
return str + captureStackTrace('appendStackTrace');
|
|
2326
2200
|
}
|
|
2327
|
-
|
|
2201
|
+
exports.appendStackTrace = appendStackTrace;
|
|
2328
2202
|
/**
|
|
2329
2203
|
* Encrypt the password/value with given key
|
|
2330
|
-
* @param
|
|
2331
|
-
* @param
|
|
2332
|
-
* @returns {string}
|
|
2204
|
+
* @param key - Secret key
|
|
2205
|
+
* @param value - value to encrypt
|
|
2333
2206
|
*/
|
|
2334
2207
|
function encryptLegacy(key, value) {
|
|
2335
2208
|
let result = '';
|
|
@@ -2338,12 +2211,10 @@ function encryptLegacy(key, value) {
|
|
|
2338
2211
|
}
|
|
2339
2212
|
return result;
|
|
2340
2213
|
}
|
|
2341
|
-
|
|
2342
2214
|
/**
|
|
2343
2215
|
* Decrypt the password/value with given key
|
|
2344
|
-
* @param
|
|
2345
|
-
* @param
|
|
2346
|
-
* @returns {string}
|
|
2216
|
+
* @param key - Secret key
|
|
2217
|
+
* @param value - value to decrypt
|
|
2347
2218
|
*/
|
|
2348
2219
|
function decryptLegacy(key, value) {
|
|
2349
2220
|
let result = '';
|
|
@@ -2352,59 +2223,46 @@ function decryptLegacy(key, value) {
|
|
|
2352
2223
|
}
|
|
2353
2224
|
return result;
|
|
2354
2225
|
}
|
|
2355
|
-
|
|
2356
2226
|
/**
|
|
2357
2227
|
* encrypts a value by a given key via AES-192-CBC
|
|
2358
2228
|
*
|
|
2359
|
-
* @param
|
|
2360
|
-
* @param
|
|
2361
|
-
* @returns {string}
|
|
2229
|
+
* @param key - Secret key
|
|
2230
|
+
* @param value - value to decrypt
|
|
2362
2231
|
*/
|
|
2363
2232
|
function encrypt(key, value) {
|
|
2364
2233
|
if (!/^[0-9a-f]{48}$/.test(key)) {
|
|
2365
2234
|
// key length is not matching for AES-192-CBC or key is no valid hex - fallback to old encryption
|
|
2366
2235
|
return encryptLegacy(key, value);
|
|
2367
2236
|
}
|
|
2368
|
-
|
|
2369
|
-
|
|
2370
|
-
|
|
2371
|
-
const iv = crypto.randomBytes(16);
|
|
2372
|
-
const cipher = crypto.createCipheriv('aes-192-cbc', Buffer.from(key, 'hex'), iv);
|
|
2373
|
-
|
|
2237
|
+
const iv = crypto_1.default.randomBytes(16);
|
|
2238
|
+
const cipher = crypto_1.default.createCipheriv('aes-192-cbc', Buffer.from(key, 'hex'), iv);
|
|
2374
2239
|
const encrypted = Buffer.concat([cipher.update(value), cipher.final()]);
|
|
2375
|
-
|
|
2376
2240
|
return `$/aes-192-cbc:${iv.toString('hex')}:${encrypted.toString('hex')}`;
|
|
2377
2241
|
}
|
|
2378
|
-
|
|
2242
|
+
exports.encrypt = encrypt;
|
|
2379
2243
|
/**
|
|
2380
2244
|
* encrypts a value by a given key via AES-192-CBC
|
|
2381
2245
|
*
|
|
2382
|
-
* @param
|
|
2383
|
-
* @param
|
|
2384
|
-
* @returns {string}
|
|
2246
|
+
* @param key - Secret key
|
|
2247
|
+
* @param value - value to decrypt
|
|
2385
2248
|
*/
|
|
2386
2249
|
function decrypt(key, value) {
|
|
2387
2250
|
// if not encrypted as aes-192 or key not a valid 48 digit hex -> fallback
|
|
2388
2251
|
if (!value.startsWith(`$/aes-192-cbc:`) || !/^[0-9a-f]{48}$/.test(key)) {
|
|
2389
2252
|
return decryptLegacy(key, value);
|
|
2390
2253
|
}
|
|
2391
|
-
|
|
2392
|
-
crypto = crypto || require('crypto');
|
|
2393
|
-
|
|
2394
|
-
const textParts = value.split(':');
|
|
2254
|
+
const textParts = value.split(':', 3);
|
|
2395
2255
|
const iv = Buffer.from(textParts[1], 'hex');
|
|
2396
|
-
const encryptedText = Buffer.from(textParts
|
|
2397
|
-
const decipher =
|
|
2398
|
-
|
|
2256
|
+
const encryptedText = Buffer.from(textParts[2], 'hex');
|
|
2257
|
+
const decipher = crypto_1.default.createDecipheriv('aes-192-cbc', Buffer.from(key, 'hex'), iv);
|
|
2399
2258
|
const decrypted = Buffer.concat([decipher.update(encryptedText), decipher.final()]);
|
|
2400
|
-
|
|
2401
2259
|
return decrypted.toString();
|
|
2402
2260
|
}
|
|
2403
|
-
|
|
2261
|
+
exports.decrypt = decrypt;
|
|
2404
2262
|
/**
|
|
2405
2263
|
* Tests whether the given variable is a real object and not an Array
|
|
2406
|
-
* @param
|
|
2407
|
-
* @returns
|
|
2264
|
+
* @param it The variable to test
|
|
2265
|
+
* @returns true if it is Record<string, any>
|
|
2408
2266
|
*/
|
|
2409
2267
|
function isObject(it) {
|
|
2410
2268
|
// This is necessary because:
|
|
@@ -2414,200 +2272,149 @@ function isObject(it) {
|
|
|
2414
2272
|
return Object.prototype.toString.call(it) === '[object Object]'; // this code is 25% faster then below one
|
|
2415
2273
|
// return it && typeof it === 'object' && !(it instanceof Array);
|
|
2416
2274
|
}
|
|
2417
|
-
|
|
2275
|
+
exports.isObject = isObject;
|
|
2418
2276
|
/**
|
|
2419
2277
|
* Tests whether the given variable is really an Array
|
|
2420
|
-
* @param
|
|
2278
|
+
* @param it The variable to test
|
|
2421
2279
|
*/
|
|
2422
2280
|
function isArray(it) {
|
|
2423
2281
|
return Array.isArray(it); // from node 0.1 is a part of engine
|
|
2424
2282
|
}
|
|
2425
|
-
|
|
2283
|
+
exports.isArray = isArray;
|
|
2426
2284
|
/**
|
|
2427
2285
|
* Measure the Node.js event loop lag and repeatedly call the provided callback function with the updated results
|
|
2428
|
-
* @param
|
|
2429
|
-
* @param
|
|
2286
|
+
* @param ms The number of milliseconds for monitoring
|
|
2287
|
+
* @param cb Callback function to call for each new value
|
|
2430
2288
|
*/
|
|
2431
2289
|
function measureEventLoopLag(ms, cb) {
|
|
2432
2290
|
let start = hrtime();
|
|
2433
|
-
|
|
2434
2291
|
let timeout = setTimeout(check, ms);
|
|
2435
2292
|
timeout.unref();
|
|
2436
|
-
|
|
2437
2293
|
function check() {
|
|
2438
2294
|
// workaround for https://github.com/joyent/node/issues/8364
|
|
2439
2295
|
clearTimeout(timeout);
|
|
2440
|
-
|
|
2441
2296
|
// how much time has actually elapsed in the loop beyond what
|
|
2442
2297
|
// setTimeout says is supposed to happen. we use setTimeout to
|
|
2443
2298
|
// cover multiple iterations of the event loop, getting a larger
|
|
2444
2299
|
// sample of what the process is working on.
|
|
2445
2300
|
const t = hrtime();
|
|
2446
|
-
|
|
2447
2301
|
// we use Math.max to handle case where timers are running efficiently
|
|
2448
2302
|
// and our callback executes earlier than `ms` due to how timers are
|
|
2449
2303
|
// implemented. this is ok. it means we're healthy.
|
|
2450
2304
|
cb && cb(Math.max(0, t - start - ms));
|
|
2451
2305
|
start = t;
|
|
2452
|
-
|
|
2453
2306
|
timeout = setTimeout(check, ms);
|
|
2454
2307
|
timeout.unref();
|
|
2455
2308
|
}
|
|
2456
|
-
|
|
2457
2309
|
function hrtime() {
|
|
2458
2310
|
const t = process.hrtime();
|
|
2459
2311
|
return t[0] * 1e3 + t[1] / 1e6;
|
|
2460
2312
|
}
|
|
2461
2313
|
}
|
|
2462
|
-
|
|
2314
|
+
exports.measureEventLoopLag = measureEventLoopLag;
|
|
2463
2315
|
/**
|
|
2464
2316
|
* This function convert state values by read and write of aliases. Function is synchron.
|
|
2465
2317
|
*
|
|
2466
|
-
* @param
|
|
2467
|
-
* @param
|
|
2468
|
-
* @param
|
|
2469
|
-
* @param
|
|
2470
|
-
* @param
|
|
2318
|
+
* @param sourceObj
|
|
2319
|
+
* @param targetObj
|
|
2320
|
+
* @param state Object with val, ack and so on
|
|
2321
|
+
* @param logger Logging object
|
|
2322
|
+
* @param logNamespace optional Logging namespace
|
|
2471
2323
|
*/
|
|
2472
2324
|
function formatAliasValue(sourceObj, targetObj, state, logger, logNamespace) {
|
|
2473
|
-
logNamespace = logNamespace ? logNamespace
|
|
2474
|
-
|
|
2325
|
+
logNamespace = logNamespace ? `${logNamespace} ` : '';
|
|
2475
2326
|
if (!state) {
|
|
2476
|
-
return;
|
|
2327
|
+
return null;
|
|
2477
2328
|
}
|
|
2478
2329
|
if (state.val === undefined) {
|
|
2479
2330
|
state.val = null;
|
|
2480
2331
|
return state;
|
|
2481
2332
|
}
|
|
2482
|
-
|
|
2483
2333
|
if (targetObj && targetObj.alias && targetObj.alias.read) {
|
|
2484
2334
|
try {
|
|
2485
2335
|
// process the value here
|
|
2486
|
-
const func = new Function(
|
|
2487
|
-
|
|
2488
|
-
|
|
2489
|
-
|
|
2490
|
-
|
|
2491
|
-
'sType',
|
|
2492
|
-
'sMin',
|
|
2493
|
-
'sMax',
|
|
2494
|
-
'return ' + targetObj.alias.read
|
|
2495
|
-
);
|
|
2496
|
-
state.val = func(
|
|
2497
|
-
state.val,
|
|
2498
|
-
targetObj.type,
|
|
2499
|
-
targetObj.min,
|
|
2500
|
-
targetObj.max,
|
|
2501
|
-
sourceObj.type,
|
|
2502
|
-
sourceObj.min,
|
|
2503
|
-
sourceObj.max
|
|
2504
|
-
);
|
|
2505
|
-
} catch (e) {
|
|
2506
|
-
logger.error(
|
|
2507
|
-
`${logNamespace} Invalid read function for ${targetObj._id}: ${targetObj.alias.read} => ${e.message}`
|
|
2508
|
-
);
|
|
2336
|
+
const func = new Function('val', 'type', 'min', 'max', 'sType', 'sMin', 'sMax', `return ${targetObj.alias.read}`);
|
|
2337
|
+
state.val = func(state.val, targetObj.type, targetObj.min, targetObj.max, sourceObj.type, sourceObj.min, sourceObj.max);
|
|
2338
|
+
}
|
|
2339
|
+
catch (e) {
|
|
2340
|
+
logger.error(`${logNamespace} Invalid read function for ${targetObj._id}: ${targetObj.alias.read} => ${e.message}`);
|
|
2509
2341
|
return null;
|
|
2510
2342
|
}
|
|
2511
2343
|
}
|
|
2512
|
-
|
|
2513
2344
|
if (sourceObj && sourceObj.alias && sourceObj.alias.write) {
|
|
2514
2345
|
try {
|
|
2515
2346
|
// process the value here
|
|
2516
|
-
const func = new Function(
|
|
2517
|
-
|
|
2518
|
-
|
|
2519
|
-
|
|
2520
|
-
|
|
2521
|
-
'tType',
|
|
2522
|
-
'tMin',
|
|
2523
|
-
'tMax',
|
|
2524
|
-
'return ' + sourceObj.alias.write
|
|
2525
|
-
);
|
|
2526
|
-
state.val = func(
|
|
2527
|
-
state.val,
|
|
2528
|
-
sourceObj.type,
|
|
2529
|
-
sourceObj.min,
|
|
2530
|
-
sourceObj.max,
|
|
2531
|
-
targetObj.type,
|
|
2532
|
-
targetObj.min,
|
|
2533
|
-
targetObj.max
|
|
2534
|
-
);
|
|
2535
|
-
} catch (e) {
|
|
2536
|
-
logger.error(
|
|
2537
|
-
`${logNamespace} Invalid write function for ${sourceObj._id}: ${sourceObj.alias.write} => ${e.message}`
|
|
2538
|
-
);
|
|
2347
|
+
const func = new Function('val', 'type', 'min', 'max', 'tType', 'tMin', 'tMax', `return ${sourceObj.alias.write}`);
|
|
2348
|
+
state.val = func(state.val, sourceObj.type, sourceObj.min, sourceObj.max, targetObj.type, targetObj.min, targetObj.max);
|
|
2349
|
+
}
|
|
2350
|
+
catch (e) {
|
|
2351
|
+
logger.error(`${logNamespace} Invalid write function for ${sourceObj._id}: ${sourceObj.alias.write} => ${e.message}`);
|
|
2539
2352
|
return null;
|
|
2540
2353
|
}
|
|
2541
2354
|
}
|
|
2542
|
-
|
|
2543
2355
|
if (targetObj && typeof state.val !== targetObj.type && state.val !== null) {
|
|
2544
2356
|
if (targetObj.type === 'boolean') {
|
|
2545
2357
|
const lowerVal = typeof state.val === 'string' ? state.val.toLowerCase() : state.val;
|
|
2546
2358
|
if (lowerVal === 'off' || lowerVal === 'aus' || state.val === '0') {
|
|
2547
2359
|
state.val = false;
|
|
2548
|
-
}
|
|
2360
|
+
}
|
|
2361
|
+
else {
|
|
2549
2362
|
// this also handles strings like "EIN" or such that will be true
|
|
2550
2363
|
state.val = !!state.val;
|
|
2551
2364
|
}
|
|
2552
|
-
}
|
|
2365
|
+
}
|
|
2366
|
+
else if (targetObj.type === 'number') {
|
|
2553
2367
|
state.val = parseFloat(state.val);
|
|
2554
|
-
}
|
|
2368
|
+
}
|
|
2369
|
+
else if (targetObj.type === 'string') {
|
|
2555
2370
|
state.val = state.val.toString();
|
|
2556
2371
|
}
|
|
2557
2372
|
}
|
|
2558
|
-
|
|
2559
2373
|
// auto-scaling, only if val not null and unit for target (x)or source is %
|
|
2560
|
-
if (
|
|
2561
|
-
(
|
|
2562
|
-
|
|
2563
|
-
|
|
2564
|
-
) {
|
|
2565
|
-
if (
|
|
2566
|
-
targetObj &&
|
|
2374
|
+
if (((targetObj && targetObj.alias && !targetObj.alias.read) ||
|
|
2375
|
+
(sourceObj && sourceObj.alias && !sourceObj.alias.write)) &&
|
|
2376
|
+
state.val !== null) {
|
|
2377
|
+
if (targetObj &&
|
|
2567
2378
|
targetObj.type === 'number' &&
|
|
2568
2379
|
targetObj.unit === '%' &&
|
|
2569
2380
|
sourceObj &&
|
|
2570
2381
|
sourceObj.type === 'number' &&
|
|
2571
2382
|
sourceObj.unit !== '%' &&
|
|
2572
2383
|
sourceObj.min !== undefined &&
|
|
2573
|
-
sourceObj.max !== undefined
|
|
2574
|
-
) {
|
|
2384
|
+
sourceObj.max !== undefined) {
|
|
2575
2385
|
// scale target between 0 and 100 % based on sources min/max
|
|
2576
2386
|
state.val = ((state.val - sourceObj.min) / (sourceObj.max - sourceObj.min)) * 100;
|
|
2577
|
-
}
|
|
2578
|
-
|
|
2387
|
+
}
|
|
2388
|
+
else if (sourceObj &&
|
|
2579
2389
|
sourceObj.type === 'number' &&
|
|
2580
2390
|
sourceObj.unit === '%' &&
|
|
2581
2391
|
targetObj &&
|
|
2582
2392
|
targetObj.unit !== '%' &&
|
|
2583
2393
|
targetObj.type === 'number' &&
|
|
2584
2394
|
targetObj.min !== undefined &&
|
|
2585
|
-
targetObj.max !== undefined
|
|
2586
|
-
) {
|
|
2395
|
+
targetObj.max !== undefined) {
|
|
2587
2396
|
// scale target based on its min/max by its source (assuming source is meant to be 0 - 100 %)
|
|
2588
2397
|
state.val = ((targetObj.max - targetObj.min) * state.val) / 100 + targetObj.min;
|
|
2589
2398
|
}
|
|
2590
2399
|
}
|
|
2591
|
-
|
|
2592
2400
|
return state;
|
|
2593
2401
|
}
|
|
2594
|
-
|
|
2402
|
+
exports.formatAliasValue = formatAliasValue;
|
|
2595
2403
|
/**
|
|
2596
2404
|
* remove given id from all enums
|
|
2597
2405
|
*
|
|
2598
2406
|
* @alias removeIdFromAllEnums
|
|
2599
2407
|
* @memberof tools
|
|
2600
|
-
* @param
|
|
2601
|
-
* @param
|
|
2602
|
-
* @param
|
|
2603
|
-
* @returns
|
|
2408
|
+
* @param objects object to access objects db
|
|
2409
|
+
* @param id the object id which will be deleted from enums
|
|
2410
|
+
* @param allEnums objects with all enums to use - if not provided all enums will be queried
|
|
2411
|
+
* @returns Promise All objects are tried to be updated - reject will happen as soon as one fails with the error of the first fail
|
|
2604
2412
|
*
|
|
2605
2413
|
*/
|
|
2606
2414
|
async function removeIdFromAllEnums(objects, id, allEnums) {
|
|
2607
2415
|
if (!allEnums) {
|
|
2608
|
-
allEnums = await
|
|
2416
|
+
allEnums = await getAllEnums(objects);
|
|
2609
2417
|
}
|
|
2610
|
-
|
|
2611
2418
|
let error = null;
|
|
2612
2419
|
for (const [enumId, enumObj] of Object.entries(allEnums)) {
|
|
2613
2420
|
const idx = enumObj.common.members ? enumObj.common.members.indexOf(id) : -1;
|
|
@@ -2618,7 +2425,8 @@ async function removeIdFromAllEnums(objects, id, allEnums) {
|
|
|
2618
2425
|
await objects.setObjectAsync(enumId, enumObj);
|
|
2619
2426
|
// update cache directly to prevent race conditions when sending many delete in a short time
|
|
2620
2427
|
allEnums[enumId] = enumObj;
|
|
2621
|
-
}
|
|
2428
|
+
}
|
|
2429
|
+
catch (err) {
|
|
2622
2430
|
if (!error) {
|
|
2623
2431
|
error = err;
|
|
2624
2432
|
}
|
|
@@ -2629,14 +2437,14 @@ async function removeIdFromAllEnums(objects, id, allEnums) {
|
|
|
2629
2437
|
throw error;
|
|
2630
2438
|
}
|
|
2631
2439
|
}
|
|
2632
|
-
|
|
2440
|
+
exports.removeIdFromAllEnums = removeIdFromAllEnums;
|
|
2633
2441
|
/**
|
|
2634
2442
|
* Parses dependencies to standardized object of form
|
|
2635
2443
|
*
|
|
2636
2444
|
* @alias parseDependencies
|
|
2637
2445
|
* @memberof tools
|
|
2638
|
-
* @param
|
|
2639
|
-
* @returns
|
|
2446
|
+
* @param dependencies dependencies array or single dependency
|
|
2447
|
+
* @returns parsedDeps parsed dependencies
|
|
2640
2448
|
*/
|
|
2641
2449
|
function parseDependencies(dependencies) {
|
|
2642
2450
|
let adapters = {};
|
|
@@ -2645,29 +2453,32 @@ function parseDependencies(dependencies) {
|
|
|
2645
2453
|
if (typeof rule === 'string') {
|
|
2646
2454
|
// No version given, all are okay
|
|
2647
2455
|
adapters[rule] = '*';
|
|
2648
|
-
}
|
|
2456
|
+
}
|
|
2457
|
+
else if (isObject(rule)) {
|
|
2649
2458
|
// can be object containing single adapter or multiple
|
|
2650
2459
|
Object.keys(rule)
|
|
2651
2460
|
.filter(adapter => !adapters[adapter])
|
|
2652
2461
|
.forEach(adapter => (adapters[adapter] = rule[adapter]));
|
|
2653
2462
|
}
|
|
2654
2463
|
});
|
|
2655
|
-
}
|
|
2464
|
+
}
|
|
2465
|
+
else if (typeof dependencies === 'string') {
|
|
2656
2466
|
// its a single string without version requirement
|
|
2657
2467
|
adapters[dependencies] = '*';
|
|
2658
|
-
}
|
|
2468
|
+
}
|
|
2469
|
+
else if (isObject(dependencies)) {
|
|
2659
2470
|
// if dependencies is already an object, just use it
|
|
2660
2471
|
adapters = dependencies;
|
|
2661
2472
|
}
|
|
2662
2473
|
return adapters;
|
|
2663
2474
|
}
|
|
2664
|
-
|
|
2475
|
+
exports.parseDependencies = parseDependencies;
|
|
2665
2476
|
/**
|
|
2666
2477
|
* Validates types of obj.common properties and object.type, if invalid types are used, an error is thrown.
|
|
2667
2478
|
* If attributes of obj.common are not provided, no error is thrown. obj.type has to be there and has to be valid.
|
|
2668
2479
|
*
|
|
2669
|
-
* @param
|
|
2670
|
-
* @param
|
|
2480
|
+
* @param obj an object which will be validated
|
|
2481
|
+
* @param extend (optional) if true checks will allow more optional cases for extendObject calls
|
|
2671
2482
|
* @throws Error if a property has the wrong type or obj.type is non existing
|
|
2672
2483
|
*/
|
|
2673
2484
|
function validateGeneralObjectProperties(obj, extend) {
|
|
@@ -2675,15 +2486,12 @@ function validateGeneralObjectProperties(obj, extend) {
|
|
|
2675
2486
|
if (obj && obj.type === undefined && obj.views !== undefined) {
|
|
2676
2487
|
return;
|
|
2677
2488
|
}
|
|
2678
|
-
|
|
2679
2489
|
if (!obj || (obj.type === undefined && !extend)) {
|
|
2680
2490
|
throw new Error(`obj.type has to exist`);
|
|
2681
2491
|
}
|
|
2682
|
-
|
|
2683
2492
|
if (obj.type !== undefined && typeof obj.type !== 'string') {
|
|
2684
2493
|
throw new Error(`obj.type has an invalid type! Expected "string", received "${typeof obj.type}"`);
|
|
2685
2494
|
}
|
|
2686
|
-
|
|
2687
2495
|
const allowedObjectTypes = [
|
|
2688
2496
|
'state',
|
|
2689
2497
|
'channel',
|
|
@@ -2701,175 +2509,115 @@ function validateGeneralObjectProperties(obj, extend) {
|
|
|
2701
2509
|
'folder'
|
|
2702
2510
|
];
|
|
2703
2511
|
if (obj.type !== undefined && !allowedObjectTypes.includes(obj.type)) {
|
|
2704
|
-
throw new Error(
|
|
2705
|
-
`obj.type has an invalid value (${obj.type}) but has to be one of ${allowedObjectTypes.join(', ')}`
|
|
2706
|
-
);
|
|
2512
|
+
throw new Error(`obj.type has an invalid value (${obj.type}) but has to be one of ${allowedObjectTypes.join(', ')}`);
|
|
2707
2513
|
}
|
|
2708
|
-
|
|
2709
2514
|
// obj.common is optional in general check
|
|
2710
2515
|
if (!obj.common) {
|
|
2711
2516
|
return;
|
|
2712
2517
|
}
|
|
2713
|
-
|
|
2714
2518
|
if (obj.common.name !== undefined && typeof obj.common.name !== 'string' && typeof obj.common.name !== 'object') {
|
|
2715
|
-
throw new Error(
|
|
2716
|
-
|
|
2717
|
-
|
|
2718
|
-
} else if (['adapter'].includes(obj.type) && typeof obj.common.name !== 'string') {
|
|
2519
|
+
throw new Error(`obj.common.name has an invalid type! Expected "string" or "object", received "${typeof obj.common.name}"`);
|
|
2520
|
+
}
|
|
2521
|
+
else if (['adapter'].includes(obj.type) && typeof obj.common.name !== 'string') {
|
|
2719
2522
|
// TODO: we need this for group/user too, but have to solve problems described in #1266
|
|
2720
2523
|
// for some types, name needs to be a unique string
|
|
2721
2524
|
throw new Error(`obj.common.name has an invalid type! Expected "string", received "${typeof obj.common.name}"`);
|
|
2722
2525
|
}
|
|
2723
|
-
|
|
2724
2526
|
if (obj.common.type !== undefined) {
|
|
2725
2527
|
if (typeof obj.common.type !== 'string') {
|
|
2726
|
-
throw new Error(
|
|
2727
|
-
`obj.common.type has an invalid type! Expected "string", received "${typeof obj.common.type}"`
|
|
2728
|
-
);
|
|
2528
|
+
throw new Error(`obj.common.type has an invalid type! Expected "string", received "${typeof obj.common.type}"`);
|
|
2729
2529
|
}
|
|
2730
|
-
|
|
2731
2530
|
if (obj.type === 'state') {
|
|
2732
2531
|
// if object type indicates a state, check that common.type matches
|
|
2733
2532
|
const allowedStateTypes = ['number', 'string', 'boolean', 'array', 'object', 'mixed', 'file', 'json'];
|
|
2734
2533
|
if (!allowedStateTypes.includes(obj.common.type)) {
|
|
2735
|
-
throw new Error(
|
|
2736
|
-
`obj.common.type has an invalid value (${
|
|
2737
|
-
obj.common.type
|
|
2738
|
-
}) but has to be one of ${allowedStateTypes.join(', ')}`
|
|
2739
|
-
);
|
|
2534
|
+
throw new Error(`obj.common.type has an invalid value (${obj.common.type}) but has to be one of ${allowedStateTypes.join(', ')}`);
|
|
2740
2535
|
}
|
|
2741
|
-
|
|
2742
2536
|
// ensure that min max only exists for common.type number and is number itself
|
|
2743
2537
|
if (obj.common.min !== undefined) {
|
|
2744
2538
|
if (typeof obj.common.min !== 'number') {
|
|
2745
|
-
throw new Error(
|
|
2746
|
-
`obj.common.min has an invalid type! Expected "number", received "${typeof obj.common.min}"`
|
|
2747
|
-
);
|
|
2539
|
+
throw new Error(`obj.common.min has an invalid type! Expected "number", received "${typeof obj.common.min}"`);
|
|
2748
2540
|
}
|
|
2749
|
-
|
|
2750
2541
|
if (obj.common.type !== 'number' && obj.common.type !== 'mixed') {
|
|
2751
|
-
throw new Error(
|
|
2752
|
-
`obj.common.min is only allowed on obj.common.type "number" or "mixed", received "${obj.common.type}"`
|
|
2753
|
-
);
|
|
2542
|
+
throw new Error(`obj.common.min is only allowed on obj.common.type "number" or "mixed", received "${obj.common.type}"`);
|
|
2754
2543
|
}
|
|
2755
2544
|
}
|
|
2756
|
-
|
|
2757
2545
|
if (obj.common.max !== undefined) {
|
|
2758
2546
|
if (typeof obj.common.max !== 'number') {
|
|
2759
|
-
throw new Error(
|
|
2760
|
-
`obj.common.max has an invalid type! Expected "number", received "${typeof obj.common.max}"`
|
|
2761
|
-
);
|
|
2547
|
+
throw new Error(`obj.common.max has an invalid type! Expected "number", received "${typeof obj.common.max}"`);
|
|
2762
2548
|
}
|
|
2763
|
-
|
|
2764
2549
|
if (obj.common.type !== 'number' && obj.common.type !== 'mixed') {
|
|
2765
|
-
throw new Error(
|
|
2766
|
-
`obj.common.max is only allowed on obj.common.type "number" or "mixed", received "${obj.common.type}"`
|
|
2767
|
-
);
|
|
2550
|
+
throw new Error(`obj.common.max is only allowed on obj.common.type "number" or "mixed", received "${obj.common.type}"`);
|
|
2768
2551
|
}
|
|
2769
|
-
|
|
2770
2552
|
if (obj.common.min !== undefined && obj.common.min > obj.common.max) {
|
|
2771
|
-
throw new Error(
|
|
2772
|
-
`obj.common.min (${obj.common.min}) needs to be less than or equal to obj.common.max (${obj.common.max})`
|
|
2773
|
-
);
|
|
2553
|
+
throw new Error(`obj.common.min (${obj.common.min}) needs to be less than or equal to obj.common.max (${obj.common.max})`);
|
|
2774
2554
|
}
|
|
2775
2555
|
}
|
|
2776
|
-
|
|
2777
2556
|
// ensure, that default value has correct type
|
|
2778
2557
|
if (obj.common.def !== undefined && obj.common.def !== null) {
|
|
2779
2558
|
if (obj.common.type === 'file') {
|
|
2780
2559
|
// defaults are set via setState but would need setBinaryState
|
|
2781
2560
|
throw new Error('Default value is not supported for type "file"');
|
|
2782
2561
|
}
|
|
2783
|
-
|
|
2784
2562
|
// else do what strictObjectChecks does for val
|
|
2785
|
-
if (
|
|
2786
|
-
|
|
2787
|
-
|
|
2788
|
-
|
|
2789
|
-
|
|
2790
|
-
|
|
2791
|
-
(obj.common.type === 'file' && typeof obj.common.def === 'string') ||
|
|
2792
|
-
(obj.common.type === 'object' && typeof obj.common.def === 'string')
|
|
2793
|
-
)
|
|
2794
|
-
) {
|
|
2563
|
+
if (!((obj.common.type === 'mixed' && typeof obj.common.def !== 'object') ||
|
|
2564
|
+
(obj.common.type !== 'object' && obj.common.type === typeof obj.common.def) ||
|
|
2565
|
+
(obj.common.type === 'array' && typeof obj.common.def === 'string') ||
|
|
2566
|
+
(obj.common.type === 'json' && typeof obj.common.def === 'string') ||
|
|
2567
|
+
(obj.common.type === 'file' && typeof obj.common.def === 'string') ||
|
|
2568
|
+
(obj.common.type === 'object' && typeof obj.common.def === 'string'))) {
|
|
2795
2569
|
// types can be 'number', 'string', 'boolean', 'array', 'object', 'mixed', 'file', 'json'
|
|
2796
2570
|
// array, object, json need to be string
|
|
2797
2571
|
if (['object', 'json', 'file', 'array'].includes(obj.common.type)) {
|
|
2798
|
-
throw new Error(
|
|
2799
|
-
|
|
2800
|
-
|
|
2801
|
-
|
|
2802
|
-
|
|
2803
|
-
`
|
|
2804
|
-
obj.common.type === 'mixed'
|
|
2805
|
-
? `one of type "string", "number", "boolean"`
|
|
2806
|
-
: `type "${obj.common.type}"`
|
|
2807
|
-
} but received type "${typeof obj.common.def}"`
|
|
2808
|
-
);
|
|
2572
|
+
throw new Error(`Default value has to be stringified but received type "${typeof obj.common.def}"`);
|
|
2573
|
+
}
|
|
2574
|
+
else {
|
|
2575
|
+
throw new Error(`Default value has to be ${obj.common.type === 'mixed'
|
|
2576
|
+
? `one of type "string", "number", "boolean"`
|
|
2577
|
+
: `type "${obj.common.type}"`} but received type "${typeof obj.common.def}"`);
|
|
2809
2578
|
}
|
|
2810
2579
|
}
|
|
2811
2580
|
}
|
|
2812
2581
|
}
|
|
2813
2582
|
}
|
|
2814
|
-
|
|
2815
2583
|
if (obj.common.read !== undefined && typeof obj.common.read !== 'boolean') {
|
|
2816
|
-
throw new Error(
|
|
2817
|
-
`obj.common.read has an invalid type! Expected "boolean", received "${typeof obj.common.read}"`
|
|
2818
|
-
);
|
|
2584
|
+
throw new Error(`obj.common.read has an invalid type! Expected "boolean", received "${typeof obj.common.read}"`);
|
|
2819
2585
|
}
|
|
2820
|
-
|
|
2821
2586
|
if (obj.common.write !== undefined && typeof obj.common.write !== 'boolean') {
|
|
2822
|
-
throw new Error(
|
|
2823
|
-
`obj.common.write has an invalid type! Expected "boolean", received "${typeof obj.common.write}"`
|
|
2824
|
-
);
|
|
2587
|
+
throw new Error(`obj.common.write has an invalid type! Expected "boolean", received "${typeof obj.common.write}"`);
|
|
2825
2588
|
}
|
|
2826
|
-
|
|
2827
2589
|
if (obj.common.role !== undefined && typeof obj.common.role !== 'string') {
|
|
2828
2590
|
throw new Error(`obj.common.role has an invalid type! Expected "string", received "${typeof obj.common.role}"`);
|
|
2829
2591
|
}
|
|
2830
|
-
|
|
2831
2592
|
if (obj.common.desc !== undefined && typeof obj.common.desc !== 'string' && typeof obj.common.desc !== 'object') {
|
|
2832
|
-
throw new Error(
|
|
2833
|
-
`obj.common.desc has an invalid type! Expected "string" or "object", received "${typeof obj.common.desc}"`
|
|
2834
|
-
);
|
|
2593
|
+
throw new Error(`obj.common.desc has an invalid type! Expected "string" or "object", received "${typeof obj.common.desc}"`);
|
|
2835
2594
|
}
|
|
2836
|
-
|
|
2837
|
-
if (
|
|
2838
|
-
obj.type === 'state' &&
|
|
2595
|
+
if (obj.type === 'state' &&
|
|
2839
2596
|
obj.common.custom !== undefined &&
|
|
2840
2597
|
obj.common.custom !== null &&
|
|
2841
|
-
!isObject(obj.common.custom)
|
|
2842
|
-
|
|
2843
|
-
throw new Error(
|
|
2844
|
-
`obj.common.custom has an invalid type! Expected "object", received "${typeof obj.common.custom}"`
|
|
2845
|
-
);
|
|
2598
|
+
!isObject(obj.common.custom)) {
|
|
2599
|
+
throw new Error(`obj.common.custom has an invalid type! Expected "object", received "${typeof obj.common.custom}"`);
|
|
2846
2600
|
}
|
|
2847
|
-
|
|
2848
2601
|
// common.states needs to be a real object or an array
|
|
2849
|
-
if (
|
|
2850
|
-
obj.common.states !== null && // we allow null for deletion TODO: implement https://github.com/ioBroker/ioBroker.js-controller/issues/1735
|
|
2602
|
+
if (obj.common.states !== null && // we allow null for deletion TODO: implement https://github.com/ioBroker/ioBroker.js-controller/issues/1735
|
|
2851
2603
|
obj.common.states !== undefined &&
|
|
2852
2604
|
!isObject(obj.common.states) &&
|
|
2853
|
-
!Array.isArray(obj.common.states)
|
|
2854
|
-
|
|
2855
|
-
throw new Error(
|
|
2856
|
-
`obj.common.states has an invalid type! Expected "object", received "${typeof obj.common.states}"`
|
|
2857
|
-
);
|
|
2605
|
+
!Array.isArray(obj.common.states)) {
|
|
2606
|
+
throw new Error(`obj.common.states has an invalid type! Expected "object", received "${typeof obj.common.states}"`);
|
|
2858
2607
|
}
|
|
2859
2608
|
}
|
|
2860
|
-
|
|
2609
|
+
exports.validateGeneralObjectProperties = validateGeneralObjectProperties;
|
|
2861
2610
|
/**
|
|
2862
2611
|
* get all instances of all adapters in the list
|
|
2863
2612
|
*
|
|
2864
2613
|
* @alias getAllInstances
|
|
2865
2614
|
* @memberof tools
|
|
2866
|
-
* @param
|
|
2867
|
-
* @param
|
|
2868
|
-
* @returns
|
|
2615
|
+
* @param adapters list of adapter names to get instances for
|
|
2616
|
+
* @param objects class redis objects
|
|
2617
|
+
* @returns array of IDs
|
|
2869
2618
|
*/
|
|
2870
2619
|
async function getAllInstances(adapters, objects) {
|
|
2871
2620
|
const instances = [];
|
|
2872
|
-
|
|
2873
2621
|
for (let i = 0; i < adapters.length; i++) {
|
|
2874
2622
|
if (!adapters[i]) {
|
|
2875
2623
|
continue;
|
|
@@ -2881,21 +2629,21 @@ async function getAllInstances(adapters, objects) {
|
|
|
2881
2629
|
instances.push(inst[j]);
|
|
2882
2630
|
}
|
|
2883
2631
|
}
|
|
2884
|
-
}
|
|
2632
|
+
}
|
|
2633
|
+
else {
|
|
2885
2634
|
if (!instances.includes(adapters[i])) {
|
|
2886
2635
|
instances.push(adapters[i]);
|
|
2887
2636
|
}
|
|
2888
2637
|
}
|
|
2889
2638
|
}
|
|
2890
|
-
|
|
2891
2639
|
return instances;
|
|
2892
2640
|
}
|
|
2893
|
-
|
|
2641
|
+
exports.getAllInstances = getAllInstances;
|
|
2894
2642
|
/**
|
|
2895
2643
|
* Get all existing enums
|
|
2896
2644
|
*
|
|
2897
|
-
* @param
|
|
2898
|
-
* @returns
|
|
2645
|
+
* @param objects - objects db
|
|
2646
|
+
* @returns Promise
|
|
2899
2647
|
*/
|
|
2900
2648
|
async function getAllEnums(objects) {
|
|
2901
2649
|
const allEnums = {};
|
|
@@ -2908,22 +2656,21 @@ async function getAllEnums(objects) {
|
|
|
2908
2656
|
allEnums[row.id] = row.value;
|
|
2909
2657
|
}
|
|
2910
2658
|
}
|
|
2911
|
-
|
|
2912
2659
|
return allEnums;
|
|
2913
2660
|
}
|
|
2914
|
-
|
|
2661
|
+
exports.getAllEnums = getAllEnums;
|
|
2915
2662
|
/**
|
|
2916
2663
|
* get async all instances of one adapter
|
|
2917
2664
|
*
|
|
2918
2665
|
* @alias getInstances
|
|
2919
|
-
* @param
|
|
2920
|
-
* @param
|
|
2921
|
-
* @param
|
|
2666
|
+
* @param adapter name of the adapter
|
|
2667
|
+
* @param objects objects DB
|
|
2668
|
+
* @param withObjects return objects instead of only ids
|
|
2922
2669
|
*/
|
|
2923
2670
|
async function getInstances(adapter, objects, withObjects) {
|
|
2924
2671
|
const arr = await objects.getObjectListAsync({
|
|
2925
|
-
startkey:
|
|
2926
|
-
endkey:
|
|
2672
|
+
startkey: `system.adapter.${adapter}.`,
|
|
2673
|
+
endkey: `system.adapter.${adapter}.\u9999`
|
|
2927
2674
|
});
|
|
2928
2675
|
const instances = [];
|
|
2929
2676
|
if (arr && arr.rows) {
|
|
@@ -2933,62 +2680,64 @@ async function getInstances(adapter, objects, withObjects) {
|
|
|
2933
2680
|
}
|
|
2934
2681
|
if (withObjects) {
|
|
2935
2682
|
instances.push(arr.rows[i].value);
|
|
2936
|
-
}
|
|
2683
|
+
}
|
|
2684
|
+
else {
|
|
2937
2685
|
instances.push(arr.rows[i].value._id);
|
|
2938
2686
|
}
|
|
2939
2687
|
}
|
|
2940
2688
|
}
|
|
2941
|
-
|
|
2942
2689
|
return instances;
|
|
2943
2690
|
}
|
|
2944
|
-
|
|
2691
|
+
exports.getInstances = getInstances;
|
|
2945
2692
|
/**
|
|
2946
2693
|
* Checks if the given callback is a function and if so calls it with the given parameter immediately, else a resolved Promise is returned
|
|
2947
2694
|
*
|
|
2948
|
-
* @param
|
|
2949
|
-
* @param
|
|
2950
|
-
* @returns
|
|
2695
|
+
* @param callback - callback function to be executed
|
|
2696
|
+
* @param args - as many arguments as needed, which will be returned by the callback function or by the Promise
|
|
2697
|
+
* @returns if Promise is resolved with multiple arguments, an array is returned
|
|
2951
2698
|
*/
|
|
2952
2699
|
function maybeCallback(callback, ...args) {
|
|
2953
2700
|
if (typeof callback === 'function') {
|
|
2954
2701
|
// if function we call it with given param
|
|
2955
2702
|
setImmediate(callback, ...args);
|
|
2956
|
-
}
|
|
2703
|
+
}
|
|
2704
|
+
else {
|
|
2957
2705
|
return Promise.resolve(args.length > 1 ? args : args[0]);
|
|
2958
2706
|
}
|
|
2959
2707
|
}
|
|
2960
|
-
|
|
2708
|
+
exports.maybeCallback = maybeCallback;
|
|
2961
2709
|
/**
|
|
2962
2710
|
* Checks if the given callback is a function and if so calls it with the given error and parameter immediately, else a resolved or rejected Promise is returned. Error ERROR_DB_CLOSED are not rejecting the promise
|
|
2963
2711
|
*
|
|
2964
|
-
* @param
|
|
2965
|
-
* @param
|
|
2712
|
+
* @param callback - callback function to be executed
|
|
2713
|
+
* @param error - error which will be used by the callback function. If callback is not a function and
|
|
2966
2714
|
* 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.
|
|
2967
|
-
* @param
|
|
2968
|
-
* @returns
|
|
2715
|
+
* @param args - as many arguments as needed, which will be returned by the callback function or by the Promise
|
|
2716
|
+
* @returns if Promise is resolved with multiple arguments, an array is returned
|
|
2969
2717
|
*/
|
|
2970
2718
|
function maybeCallbackWithError(callback, error, ...args) {
|
|
2971
2719
|
if (error !== undefined && error !== null && !(error instanceof Error)) {
|
|
2972
2720
|
// if it's not a real Error, we convert it into one
|
|
2973
2721
|
error = new Error(error);
|
|
2974
2722
|
}
|
|
2975
|
-
const isDbError = error ? error.message === ERROR_DB_CLOSED : false;
|
|
2976
|
-
|
|
2723
|
+
const isDbError = error ? error.message === ERRORS.ERROR_DB_CLOSED : false;
|
|
2977
2724
|
if (typeof callback === 'function') {
|
|
2978
2725
|
setImmediate(callback, error, ...args);
|
|
2979
|
-
}
|
|
2726
|
+
}
|
|
2727
|
+
else if (error && !isDbError) {
|
|
2980
2728
|
return Promise.reject(error);
|
|
2981
|
-
}
|
|
2729
|
+
}
|
|
2730
|
+
else {
|
|
2982
2731
|
return Promise.resolve(args.length > 1 ? args : args[0]);
|
|
2983
2732
|
}
|
|
2984
2733
|
}
|
|
2985
|
-
|
|
2734
|
+
exports.maybeCallbackWithError = maybeCallbackWithError;
|
|
2986
2735
|
/**
|
|
2987
2736
|
* Executes a command asynchronously. On success, the promise resolves with stdout and stderr.
|
|
2988
2737
|
* On error, the promise rejects with the exit code or signal, as well as stdout and stderr.
|
|
2989
|
-
* @param
|
|
2990
|
-
* @param
|
|
2991
|
-
* @returns
|
|
2738
|
+
* @param command The command to execute
|
|
2739
|
+
* @param execOptions The options for child_process.exec
|
|
2740
|
+
* @returns child process promise
|
|
2992
2741
|
*/
|
|
2993
2742
|
function execAsync(command, execOptions) {
|
|
2994
2743
|
const defaultOptions = {
|
|
@@ -2997,80 +2746,79 @@ function execAsync(command, execOptions) {
|
|
|
2997
2746
|
// And we want to capture stdout/stderr
|
|
2998
2747
|
encoding: 'utf8'
|
|
2999
2748
|
};
|
|
3000
|
-
|
|
3001
|
-
return cpPromise.exec(command, { ...defaultOptions, ...execOptions });
|
|
2749
|
+
return promisify_child_process_1.default.exec(command, { ...defaultOptions, ...execOptions });
|
|
3002
2750
|
}
|
|
3003
|
-
|
|
2751
|
+
exports.execAsync = execAsync;
|
|
3004
2752
|
/**
|
|
3005
2753
|
* Takes input from one stream and writes it to another as soon as a complete line was read.
|
|
3006
|
-
* @param
|
|
3007
|
-
* @param
|
|
2754
|
+
* @param input The stream to read from
|
|
2755
|
+
* @param output The stream to write into
|
|
3008
2756
|
*/
|
|
3009
2757
|
function pipeLinewise(input, output) {
|
|
3010
|
-
const rl = createInterface({
|
|
2758
|
+
const rl = (0, readline_1.createInterface)({
|
|
3011
2759
|
input,
|
|
3012
2760
|
crlfDelay: Infinity
|
|
3013
2761
|
});
|
|
3014
2762
|
rl.on('line', line => {
|
|
3015
2763
|
try {
|
|
3016
|
-
output.write(line +
|
|
3017
|
-
}
|
|
2764
|
+
output.write(line + os_1.default.EOL);
|
|
2765
|
+
}
|
|
2766
|
+
catch {
|
|
3018
2767
|
// ignore
|
|
3019
2768
|
}
|
|
3020
2769
|
});
|
|
3021
|
-
rl.on('error', () => {
|
|
2770
|
+
rl.on('error', () => {
|
|
2771
|
+
/** Ignore Errors */
|
|
2772
|
+
});
|
|
3022
2773
|
}
|
|
3023
|
-
|
|
2774
|
+
exports.pipeLinewise = pipeLinewise;
|
|
3024
2775
|
/**
|
|
3025
2776
|
* Find the adapter main file as full path
|
|
3026
2777
|
*
|
|
3027
2778
|
* @memberof tools
|
|
3028
|
-
* @param
|
|
3029
|
-
* @returns
|
|
2779
|
+
* @param adapter - adapter name of the adapter, e.g. hm-rpc
|
|
2780
|
+
* @returns full file name
|
|
3030
2781
|
*/
|
|
3031
2782
|
async function resolveAdapterMainFile(adapter) {
|
|
3032
2783
|
const adapterDir = getAdapterDir(adapter);
|
|
3033
2784
|
if (!adapterDir) {
|
|
3034
2785
|
throw new Error(`Could not find adapter dir of ${adapter}`);
|
|
3035
2786
|
}
|
|
3036
|
-
|
|
3037
2787
|
const possibleMainFiles = ['main.js', `${adapter}.js`];
|
|
3038
|
-
|
|
3039
2788
|
// Add package.json -> main as the 2nd choice
|
|
3040
2789
|
try {
|
|
3041
|
-
const pack = JSON.parse(await
|
|
2790
|
+
const pack = JSON.parse(await fs_extra_1.default.readFile(path_1.default.join(adapterDir, 'package.json'), 'utf8'));
|
|
3042
2791
|
if (pack && typeof pack.main === 'string') {
|
|
3043
2792
|
possibleMainFiles.unshift(pack.main);
|
|
3044
2793
|
}
|
|
3045
|
-
}
|
|
2794
|
+
}
|
|
2795
|
+
catch {
|
|
3046
2796
|
// Ignore, we have fallback solutions
|
|
3047
2797
|
}
|
|
3048
|
-
|
|
3049
2798
|
// Add io-package.json -> common.main as the preferred choice
|
|
3050
2799
|
try {
|
|
3051
|
-
const ioPack = JSON.parse(await
|
|
2800
|
+
const ioPack = JSON.parse(await fs_extra_1.default.readFile(path_1.default.join(adapterDir, 'io-package.json'), 'utf8'));
|
|
3052
2801
|
if (ioPack && ioPack.common && typeof ioPack.common.main === 'string') {
|
|
3053
2802
|
possibleMainFiles.unshift(ioPack.common.main);
|
|
3054
2803
|
}
|
|
3055
|
-
}
|
|
2804
|
+
}
|
|
2805
|
+
catch {
|
|
3056
2806
|
// Ignore, we have fallback solutions
|
|
3057
2807
|
}
|
|
3058
|
-
|
|
3059
2808
|
// Try all possible main files
|
|
3060
2809
|
for (const mainFile of possibleMainFiles) {
|
|
3061
|
-
const fullFileName =
|
|
3062
|
-
if (await
|
|
2810
|
+
const fullFileName = path_1.default.join(adapterDir, mainFile);
|
|
2811
|
+
if (await fs_extra_1.default.pathExists(fullFileName)) {
|
|
3063
2812
|
return fullFileName;
|
|
3064
2813
|
}
|
|
3065
2814
|
}
|
|
3066
|
-
|
|
3067
2815
|
throw new Error(`Could not find main file of ${adapter}`);
|
|
3068
2816
|
}
|
|
3069
|
-
|
|
2817
|
+
exports.resolveAdapterMainFile = resolveAdapterMainFile;
|
|
3070
2818
|
/**
|
|
3071
2819
|
* Returns the default nodeArgs required to execute the main file, e.g. transpile hooks for TypeScript
|
|
3072
|
-
* @param
|
|
3073
|
-
* @returns
|
|
2820
|
+
* @param mainFile
|
|
2821
|
+
* @returns default node args for cli
|
|
3074
2822
|
*/
|
|
3075
2823
|
function getDefaultNodeArgs(mainFile) {
|
|
3076
2824
|
if (mainFile.endsWith('.ts')) {
|
|
@@ -3078,22 +2826,20 @@ function getDefaultNodeArgs(mainFile) {
|
|
|
3078
2826
|
}
|
|
3079
2827
|
return [];
|
|
3080
2828
|
}
|
|
3081
|
-
|
|
2829
|
+
exports.getDefaultNodeArgs = getDefaultNodeArgs;
|
|
3082
2830
|
/** This is used for the short github URL format that NPM accepts (<githubname>/<githubrepo>[#<commit-ish>]) */
|
|
3083
2831
|
const shortGithubUrlRegex = /^(?<user>[^/]+)\/(?<repo>[^#]+)(?:#(?<commit>.+))?$/;
|
|
3084
|
-
|
|
3085
2832
|
/**
|
|
3086
2833
|
* Tests if the given URL matches the format <githubname>/<githubrepo>[#<commit-ish>]
|
|
3087
|
-
* @param
|
|
2834
|
+
* @param url The URL to parse
|
|
3088
2835
|
*/
|
|
3089
2836
|
function isShortGithubUrl(url) {
|
|
3090
2837
|
return shortGithubUrlRegex.test(url);
|
|
3091
2838
|
}
|
|
3092
|
-
|
|
2839
|
+
exports.isShortGithubUrl = isShortGithubUrl;
|
|
3093
2840
|
/**
|
|
3094
2841
|
* Tries to parse an URL in the format <githubname>/<githubrepo>[#<commit-ish>] into its separate parts
|
|
3095
|
-
* @param
|
|
3096
|
-
* @returns {{user: string; repo: string; commit?: string} | null}
|
|
2842
|
+
* @param url The URL to parse
|
|
3097
2843
|
*/
|
|
3098
2844
|
function parseShortGithubUrl(url) {
|
|
3099
2845
|
const match = shortGithubUrlRegex.exec(url);
|
|
@@ -3106,23 +2852,20 @@ function parseShortGithubUrl(url) {
|
|
|
3106
2852
|
commit: match.groups.commit
|
|
3107
2853
|
};
|
|
3108
2854
|
}
|
|
3109
|
-
|
|
2855
|
+
exports.parseShortGithubUrl = parseShortGithubUrl;
|
|
3110
2856
|
/** This is used to parse the pathname of a github URL */
|
|
3111
|
-
const githubPathnameRegex =
|
|
3112
|
-
/^\/(?<user>[^/]+)\/(?<repo>[^/]*?)(?:\.git)?(?:\/(?:tree|tarball|archive)\/(?<commit>.*?)(?:\.(?:zip|gz|tar\.gz))?)?$/;
|
|
3113
|
-
|
|
2857
|
+
const githubPathnameRegex = /^\/(?<user>[^/]+)\/(?<repo>[^/]*?)(?:\.git)?(?:\/(?:tree|tarball|archive)\/(?<commit>.*?)(?:\.(?:zip|gz|tar\.gz))?)?$/;
|
|
3114
2858
|
/**
|
|
3115
2859
|
* Tests if the given pathname matches the format /<githubname>/<githubrepo>[.git][/<tarball|tree|archive>/<commit-ish>[.zip|.gz]]
|
|
3116
|
-
* @param
|
|
2860
|
+
* @param pathname The pathname part of a Github URL
|
|
3117
2861
|
*/
|
|
3118
2862
|
function isGithubPathname(pathname) {
|
|
3119
2863
|
return githubPathnameRegex.test(pathname);
|
|
3120
2864
|
}
|
|
3121
|
-
|
|
2865
|
+
exports.isGithubPathname = isGithubPathname;
|
|
3122
2866
|
/**
|
|
3123
2867
|
* Tries to a github pathname format /<githubname>/<githubrepo>[.git][/<tarball|tree|archive>/<commit-ish>[.zip|.gz|.tar.gz]] into its separate parts
|
|
3124
|
-
* @param
|
|
3125
|
-
* @returns {{user: string; repo: string; commit: string} | null}
|
|
2868
|
+
* @param pathname The pathname part of a Github URL
|
|
3126
2869
|
*/
|
|
3127
2870
|
function parseGithubPathname(pathname) {
|
|
3128
2871
|
const match = githubPathnameRegex.exec(pathname);
|
|
@@ -3135,23 +2878,25 @@ function parseGithubPathname(pathname) {
|
|
|
3135
2878
|
commit: match.groups.commit
|
|
3136
2879
|
};
|
|
3137
2880
|
}
|
|
3138
|
-
|
|
2881
|
+
exports.parseGithubPathname = parseGithubPathname;
|
|
3139
2882
|
/**
|
|
3140
2883
|
* Removes properties which are given by preserve
|
|
3141
|
-
* @param
|
|
3142
|
-
* @param
|
|
3143
|
-
* @param
|
|
2884
|
+
* @param preserve - object which has true entries (or array of selected attributes) for all attributes which should be removed from currObj
|
|
2885
|
+
* @param oldObj - old object
|
|
2886
|
+
* @param newObj - new object
|
|
3144
2887
|
*/
|
|
3145
2888
|
function removePreservedProperties(preserve, oldObj, newObj) {
|
|
3146
2889
|
for (const prop of Object.keys(preserve)) {
|
|
3147
2890
|
if (isObject(preserve[prop]) && isObject(newObj[prop])) {
|
|
3148
2891
|
// we have to go one step deeper
|
|
3149
2892
|
removePreservedProperties(preserve[prop], oldObj[prop], newObj[prop]);
|
|
3150
|
-
}
|
|
2893
|
+
}
|
|
2894
|
+
else if (newObj && newObj[prop] !== undefined && oldObj && oldObj[prop] !== undefined) {
|
|
3151
2895
|
// we only need to remove something if its in the old object and in the new one
|
|
3152
2896
|
if (typeof preserve[prop] === 'boolean') {
|
|
3153
2897
|
delete newObj[prop];
|
|
3154
|
-
}
|
|
2898
|
+
}
|
|
2899
|
+
else if (Array.isArray(preserve[prop])) {
|
|
3155
2900
|
// array, only rm selected subattributes instead of whole attribute
|
|
3156
2901
|
for (const rmProp of preserve[prop]) {
|
|
3157
2902
|
if (oldObj[prop][rmProp] !== undefined && newObj[prop][rmProp] !== undefined) {
|
|
@@ -3163,13 +2908,12 @@ function removePreservedProperties(preserve, oldObj, newObj) {
|
|
|
3163
2908
|
}
|
|
3164
2909
|
}
|
|
3165
2910
|
}
|
|
3166
|
-
|
|
2911
|
+
exports.removePreservedProperties = removePreservedProperties;
|
|
3167
2912
|
/**
|
|
3168
2913
|
* Returns the array of system.adapter.<namespace>.* objects which are created for every instance
|
|
3169
2914
|
*
|
|
3170
|
-
* @param
|
|
3171
|
-
* @param
|
|
3172
|
-
* @returns {object[]}
|
|
2915
|
+
* @param namespace - adapter namespace + id, e.g. hm-rpc.0
|
|
2916
|
+
* @param createWakeup - indicator to create wakeup object too
|
|
3173
2917
|
*/
|
|
3174
2918
|
function getInstanceIndicatorObjects(namespace, createWakeup) {
|
|
3175
2919
|
const id = `system.adapter.${namespace}`;
|
|
@@ -3358,7 +3102,6 @@ function getInstanceIndicatorObjects(namespace, createWakeup) {
|
|
|
3358
3102
|
native: {}
|
|
3359
3103
|
}
|
|
3360
3104
|
];
|
|
3361
|
-
|
|
3362
3105
|
if (createWakeup) {
|
|
3363
3106
|
objs.push({
|
|
3364
3107
|
_id: `${id}.wakeup`,
|
|
@@ -3373,10 +3116,9 @@ function getInstanceIndicatorObjects(namespace, createWakeup) {
|
|
|
3373
3116
|
native: {}
|
|
3374
3117
|
});
|
|
3375
3118
|
}
|
|
3376
|
-
|
|
3377
3119
|
return objs;
|
|
3378
3120
|
}
|
|
3379
|
-
|
|
3121
|
+
exports.getInstanceIndicatorObjects = getInstanceIndicatorObjects;
|
|
3380
3122
|
function getLogger(log) {
|
|
3381
3123
|
if (!log) {
|
|
3382
3124
|
log = {
|
|
@@ -3396,92 +3138,89 @@ function getLogger(log) {
|
|
|
3396
3138
|
console.log(msg);
|
|
3397
3139
|
}
|
|
3398
3140
|
};
|
|
3399
|
-
}
|
|
3141
|
+
}
|
|
3142
|
+
else if (!log.silly) {
|
|
3400
3143
|
log.silly = log.debug;
|
|
3401
3144
|
}
|
|
3402
3145
|
return log;
|
|
3403
3146
|
}
|
|
3404
|
-
|
|
3147
|
+
exports.getLogger = getLogger;
|
|
3405
3148
|
/**
|
|
3406
3149
|
* Get ordered instances according to tier level
|
|
3407
3150
|
*
|
|
3408
|
-
* @param
|
|
3409
|
-
* @param
|
|
3410
|
-
* @param
|
|
3411
|
-
* @return {Promise<object[]>}
|
|
3151
|
+
* @param objects - Objects DB
|
|
3152
|
+
* @param logger - logger object
|
|
3153
|
+
* @param logPrefix prefix for logging
|
|
3412
3154
|
*/
|
|
3413
3155
|
async function getInstancesOrderedByStartPrio(objects, logger, logPrefix = '') {
|
|
3414
3156
|
const instances = { 1: [], 2: [], 3: [], admin: [] };
|
|
3415
3157
|
const allowedTiers = [1, 2, 3];
|
|
3416
|
-
|
|
3417
3158
|
if (logPrefix) {
|
|
3418
3159
|
// append space if we have a prefix
|
|
3419
3160
|
logPrefix += ' ';
|
|
3420
3161
|
}
|
|
3421
|
-
|
|
3422
|
-
let doc = {};
|
|
3162
|
+
let doc = { rows: [] };
|
|
3423
3163
|
try {
|
|
3424
3164
|
doc = await objects.getObjectViewAsync('system', 'instance', {
|
|
3425
3165
|
startkey: 'system.adapter.',
|
|
3426
3166
|
endkey: 'system.adapter.\u9999'
|
|
3427
3167
|
});
|
|
3428
|
-
}
|
|
3168
|
+
}
|
|
3169
|
+
catch (e) {
|
|
3429
3170
|
if (e.message && e.message.startsWith('Cannot find ')) {
|
|
3430
|
-
logger.error(`${logPrefix} _design/system missing - call node ${
|
|
3431
|
-
}
|
|
3171
|
+
logger.error(`${logPrefix} _design/system missing - call node ${exports.appName}.js setup`);
|
|
3172
|
+
}
|
|
3173
|
+
else {
|
|
3432
3174
|
logger.error(`${logPrefix} Can not get instances: ${e.message}`);
|
|
3433
3175
|
}
|
|
3434
3176
|
}
|
|
3435
|
-
|
|
3436
3177
|
if (!doc.rows || doc.rows.length === 0) {
|
|
3437
3178
|
logger.info(`${logPrefix} no instances found`);
|
|
3438
|
-
}
|
|
3179
|
+
}
|
|
3180
|
+
else {
|
|
3439
3181
|
for (const row of doc.rows) {
|
|
3440
3182
|
if (row && row.value) {
|
|
3441
3183
|
if (row.value._id.startsWith('system.adapter.admin')) {
|
|
3442
3184
|
instances.admin.push(row.value);
|
|
3443
|
-
|
|
3185
|
+
/** @ts-expect-error https://github.com/ioBroker/adapter-core/issues/427 */
|
|
3186
|
+
}
|
|
3187
|
+
else if (row.value.common && allowedTiers.includes(parseInt(row.value.common.tier))) {
|
|
3188
|
+
/** @ts-expect-error we have checked that it is in allowedTiers, thus it is valid */
|
|
3444
3189
|
instances[row.value.common.tier].push(row.value);
|
|
3445
|
-
}
|
|
3190
|
+
}
|
|
3191
|
+
else {
|
|
3446
3192
|
// no valid tier so put it in the last one
|
|
3447
3193
|
instances['3'].push(row.value);
|
|
3448
3194
|
}
|
|
3449
3195
|
}
|
|
3450
3196
|
}
|
|
3451
3197
|
}
|
|
3452
|
-
|
|
3453
3198
|
return [...instances.admin, ...instances['1'], ...instances['2'], ...instances['3']];
|
|
3454
3199
|
}
|
|
3455
|
-
|
|
3200
|
+
exports.getInstancesOrderedByStartPrio = getInstancesOrderedByStartPrio;
|
|
3456
3201
|
/**
|
|
3457
3202
|
* Set capabilities of the given executable on Linux systems
|
|
3458
|
-
* @param
|
|
3459
|
-
* @param
|
|
3460
|
-
* @param
|
|
3461
|
-
* @param
|
|
3462
|
-
* @param
|
|
3463
|
-
* @returns {Promise<void>}
|
|
3203
|
+
* @param execPath - path to the executable for node you can determine it via process.execPath
|
|
3204
|
+
* @param capabilities - capabilities to set, e.g. ['cap_net_admin', 'cap_net_bind_service']
|
|
3205
|
+
* @param modeEffective - add effective mode
|
|
3206
|
+
* @param modePermitted - add permitted mode
|
|
3207
|
+
* @param modeInherited - add inherited mode
|
|
3464
3208
|
*/
|
|
3465
3209
|
async function setExecutableCapabilities(execPath, capabilities, modeEffective, modePermitted, modeInherited) {
|
|
3466
|
-
if (!Array.isArray(capabilities) || !capabilities.length) {
|
|
3467
|
-
throw new Error('No capabilities array provided');
|
|
3468
|
-
}
|
|
3469
|
-
|
|
3470
3210
|
// if not linux do nothing and silently exit
|
|
3471
|
-
if (
|
|
3211
|
+
if (os_1.default.platform() !== 'linux') {
|
|
3472
3212
|
return;
|
|
3473
3213
|
}
|
|
3474
|
-
|
|
3475
3214
|
// if Docker and Admin Capabilities should be set check if we are allowed to do that
|
|
3476
3215
|
if (isDocker() && capabilities.includes('cap_net_admin')) {
|
|
3477
3216
|
try {
|
|
3478
|
-
const systemCaps =
|
|
3217
|
+
const systemCaps = fs_extra_1.default.readFileSync(`/proc/${process.pid}/status`, 'utf-8');
|
|
3479
3218
|
const capBnd = systemCaps.match(/^CapBnd:\s(.+)$/m);
|
|
3480
3219
|
// We found a value in CapBnd line
|
|
3481
3220
|
if (capBnd && capBnd[1]) {
|
|
3482
3221
|
const { stdout } = await execAsync(`capsh --decode=${capBnd[1]}`);
|
|
3483
3222
|
// Stdout looks like "0x00000000a80425fb=cap_chown,cap_dac_override,..."
|
|
3484
|
-
if (stdout && stdout.startsWith(`0x${capBnd[1]}=`)) {
|
|
3223
|
+
if (typeof stdout === 'string' && stdout.startsWith(`0x${capBnd[1]}=`)) {
|
|
3485
3224
|
const capBndArr = stdout.substring(capBnd[1].length + 3).split(',');
|
|
3486
3225
|
// if Admin Capability is not included in System Capabilities we remove it from array
|
|
3487
3226
|
if (!capBndArr.includes('cap_net_admin')) {
|
|
@@ -3489,87 +3228,79 @@ async function setExecutableCapabilities(execPath, capabilities, modeEffective,
|
|
|
3489
3228
|
}
|
|
3490
3229
|
}
|
|
3491
3230
|
}
|
|
3492
|
-
}
|
|
3231
|
+
}
|
|
3232
|
+
catch {
|
|
3493
3233
|
// Ok we could not find it out, so update Caps but better without Admin Capability
|
|
3494
3234
|
capabilities = capabilities.filter(c => c !== 'cap_net_admin');
|
|
3495
3235
|
}
|
|
3496
3236
|
}
|
|
3497
|
-
|
|
3498
3237
|
if (capabilities.length) {
|
|
3499
3238
|
let modes = '';
|
|
3500
3239
|
const capabilitiesStr = capabilities.join(',');
|
|
3501
|
-
|
|
3502
3240
|
if (modeEffective) {
|
|
3503
3241
|
modes += 'e';
|
|
3504
3242
|
}
|
|
3505
|
-
|
|
3506
3243
|
if (modePermitted) {
|
|
3507
3244
|
modes += 'p';
|
|
3508
3245
|
}
|
|
3509
|
-
|
|
3510
3246
|
if (modeInherited) {
|
|
3511
3247
|
modes += 'i';
|
|
3512
3248
|
}
|
|
3513
|
-
|
|
3514
3249
|
if (modes.length) {
|
|
3515
3250
|
modes = `+${modes}`;
|
|
3516
3251
|
}
|
|
3517
|
-
|
|
3518
3252
|
// if this throws it needs to be caught outside
|
|
3519
|
-
await
|
|
3253
|
+
await promisify_child_process_1.default.exec(`sudo setcap ${capabilitiesStr}${modes} ${execPath}`);
|
|
3520
3254
|
}
|
|
3521
3255
|
}
|
|
3522
|
-
|
|
3256
|
+
exports.setExecutableCapabilities = setExecutableCapabilities;
|
|
3523
3257
|
/**
|
|
3524
3258
|
* Requests the licenses from ioBroker.net
|
|
3525
|
-
* @param
|
|
3526
|
-
* @param
|
|
3527
|
-
* @returns
|
|
3259
|
+
* @param login Login for ioBroker.net
|
|
3260
|
+
* @param password Decoded password for ioBroker.net
|
|
3261
|
+
* @returns array of all licenses stored on iobroker.net
|
|
3528
3262
|
*/
|
|
3529
3263
|
async function _readLicenses(login, password) {
|
|
3530
|
-
axios = axios || require('axios');
|
|
3531
3264
|
const config = {
|
|
3532
|
-
headers: { Authorization: `Basic ${Buffer.from(login
|
|
3265
|
+
headers: { Authorization: `Basic ${Buffer.from(`${login}:${password}`).toString('base64')}` },
|
|
3533
3266
|
timeout: 4000
|
|
3534
3267
|
};
|
|
3535
|
-
|
|
3536
3268
|
try {
|
|
3537
|
-
const response = await
|
|
3269
|
+
const response = await axios_1.default.get(`https://iobroker.net:3001/api/v1/licenses`, config);
|
|
3538
3270
|
if (response.data && response.data.length) {
|
|
3539
3271
|
const now = Date.now();
|
|
3540
|
-
response.data = response.data.filter(
|
|
3541
|
-
license
|
|
3542
|
-
|
|
3543
|
-
|
|
3544
|
-
new Date(license.validTill).getTime() > now
|
|
3545
|
-
);
|
|
3546
|
-
}
|
|
3547
|
-
|
|
3272
|
+
response.data = response.data.filter((license) => !license.validTill ||
|
|
3273
|
+
license.validTill === '0000-00-00 00:00:00' ||
|
|
3274
|
+
new Date(license.validTill).getTime() > now);
|
|
3275
|
+
}
|
|
3548
3276
|
return response.data;
|
|
3549
|
-
}
|
|
3277
|
+
}
|
|
3278
|
+
catch (err) {
|
|
3550
3279
|
if (err.response) {
|
|
3551
3280
|
throw new Error((err.response.data && err.response.data.error) || err.response.data || err.response.status);
|
|
3552
|
-
}
|
|
3281
|
+
}
|
|
3282
|
+
else if (err.request) {
|
|
3553
3283
|
throw new Error('no response');
|
|
3554
|
-
}
|
|
3284
|
+
}
|
|
3285
|
+
else {
|
|
3555
3286
|
throw err;
|
|
3556
3287
|
}
|
|
3557
3288
|
}
|
|
3558
3289
|
}
|
|
3559
|
-
|
|
3560
3290
|
/**
|
|
3561
3291
|
* Reads the licenses from iobroker.net
|
|
3562
3292
|
* Reads the licenses from iobroker.net and if no login/password provided stores it in system.licenses
|
|
3563
|
-
* @param
|
|
3564
|
-
* @param
|
|
3565
|
-
* @param
|
|
3566
|
-
* @returns
|
|
3293
|
+
* @param objects Object store instance
|
|
3294
|
+
* @param login Login for ioBroker.net
|
|
3295
|
+
* @param password Decoded password for ioBroker.net
|
|
3296
|
+
* @returns array of all licenses stored on iobroker.net
|
|
3567
3297
|
*/
|
|
3568
3298
|
async function updateLicenses(objects, login, password) {
|
|
3569
3299
|
// if login and password provided in the message, just try to read without saving it in system.licenses
|
|
3570
3300
|
if (login && password) {
|
|
3571
3301
|
return _readLicenses(login, password);
|
|
3572
|
-
}
|
|
3302
|
+
}
|
|
3303
|
+
else {
|
|
3573
3304
|
// get actual object
|
|
3574
3305
|
const systemLicenses = await objects.getObjectAsync('system.licenses');
|
|
3575
3306
|
// If password and login exist
|
|
@@ -3577,15 +3308,14 @@ async function updateLicenses(objects, login, password) {
|
|
|
3577
3308
|
try {
|
|
3578
3309
|
// get the secret to decode the password
|
|
3579
3310
|
const systemConfig = await objects.getObjectAsync('system.config');
|
|
3580
|
-
|
|
3581
3311
|
// decode the password
|
|
3582
3312
|
let password;
|
|
3583
3313
|
try {
|
|
3584
3314
|
password = decrypt(systemConfig.native.secret, systemLicenses.native.password);
|
|
3585
|
-
}
|
|
3315
|
+
}
|
|
3316
|
+
catch (err) {
|
|
3586
3317
|
throw new Error(`Cannot decode password: ${err.message}`);
|
|
3587
3318
|
}
|
|
3588
|
-
|
|
3589
3319
|
// read licenses from iobroker.net
|
|
3590
3320
|
const licenses = await _readLicenses(systemLicenses.native.login, password);
|
|
3591
3321
|
// save licenses to system.licenses and remember the time
|
|
@@ -3600,41 +3330,34 @@ async function updateLicenses(objects, login, password) {
|
|
|
3600
3330
|
}
|
|
3601
3331
|
}
|
|
3602
3332
|
});
|
|
3603
|
-
|
|
3604
3333
|
systemLicenses.native.readTime = new Date().toISOString();
|
|
3605
|
-
|
|
3606
3334
|
// update read time
|
|
3607
3335
|
await objects.setObjectAsync('system.licenses', systemLicenses);
|
|
3608
3336
|
return licenses;
|
|
3609
|
-
}
|
|
3337
|
+
}
|
|
3338
|
+
catch (err) {
|
|
3610
3339
|
// if password is invalid
|
|
3611
|
-
if (
|
|
3612
|
-
err.message.includes('
|
|
3613
|
-
err.message.includes('Cannot decode password:')
|
|
3614
|
-
) {
|
|
3340
|
+
if (err.message.includes('Authentication required') ||
|
|
3341
|
+
err.message.includes('Cannot decode password:')) {
|
|
3615
3342
|
// clear existing licenses if exist
|
|
3616
|
-
if (
|
|
3617
|
-
systemLicenses &&
|
|
3343
|
+
if (systemLicenses &&
|
|
3618
3344
|
systemLicenses.native &&
|
|
3619
3345
|
systemLicenses.native.licenses &&
|
|
3620
|
-
systemLicenses.native.licenses.length
|
|
3621
|
-
) {
|
|
3346
|
+
systemLicenses.native.licenses.length) {
|
|
3622
3347
|
systemLicenses.native.licenses = [];
|
|
3623
3348
|
systemLicenses.native.readTime = new Date().toISOString();
|
|
3624
3349
|
await objects.setObjectAsync('system.licenses', systemLicenses);
|
|
3625
3350
|
}
|
|
3626
3351
|
}
|
|
3627
|
-
|
|
3628
3352
|
throw err;
|
|
3629
3353
|
}
|
|
3630
|
-
}
|
|
3354
|
+
}
|
|
3355
|
+
else {
|
|
3631
3356
|
// if password or login are empty => clear existing licenses if exist
|
|
3632
|
-
if (
|
|
3633
|
-
systemLicenses &&
|
|
3357
|
+
if (systemLicenses &&
|
|
3634
3358
|
systemLicenses.native &&
|
|
3635
3359
|
systemLicenses.native.licenses &&
|
|
3636
|
-
systemLicenses.native.licenses.length
|
|
3637
|
-
) {
|
|
3360
|
+
systemLicenses.native.licenses.length) {
|
|
3638
3361
|
systemLicenses.native.licenses = [];
|
|
3639
3362
|
systemLicenses.native.readTime = new Date().toISOString();
|
|
3640
3363
|
await objects.setObjectAsync('system.licenses', systemLicenses);
|
|
@@ -3643,26 +3366,19 @@ async function updateLicenses(objects, login, password) {
|
|
|
3643
3366
|
}
|
|
3644
3367
|
}
|
|
3645
3368
|
}
|
|
3646
|
-
|
|
3647
|
-
/**
|
|
3648
|
-
* @typedef {object} GZipFileOptions
|
|
3649
|
-
* @property {boolean} [deleteInput] Delete the input file after compression. Default: false.
|
|
3650
|
-
*/
|
|
3651
|
-
|
|
3369
|
+
exports.updateLicenses = updateLicenses;
|
|
3652
3370
|
/**
|
|
3653
3371
|
* Compresses an input file using GZip and writes it somewhere else
|
|
3654
|
-
* @param
|
|
3655
|
-
* @param
|
|
3656
|
-
* @param
|
|
3657
|
-
* @returns {Promise<void>}
|
|
3372
|
+
* @param inputFilename The filename of the input file that should be gzipped
|
|
3373
|
+
* @param outputFilename The filename of the output file where the gzipped content should be written to
|
|
3374
|
+
* @param options Options for the compression
|
|
3658
3375
|
*/
|
|
3659
3376
|
function compressFileGZip(inputFilename, outputFilename, options = {}) {
|
|
3660
3377
|
const { deleteInput = false } = options;
|
|
3661
|
-
|
|
3662
3378
|
return new Promise((resolve, reject) => {
|
|
3663
|
-
const input =
|
|
3664
|
-
const output =
|
|
3665
|
-
const compress =
|
|
3379
|
+
const input = fs_extra_1.default.createReadStream(inputFilename);
|
|
3380
|
+
const output = fs_extra_1.default.createWriteStream(outputFilename);
|
|
3381
|
+
const compress = zlib_1.default.createGzip();
|
|
3666
3382
|
input.on('error', err => {
|
|
3667
3383
|
reject(err);
|
|
3668
3384
|
});
|
|
@@ -3675,97 +3391,16 @@ function compressFileGZip(inputFilename, outputFilename, options = {}) {
|
|
|
3675
3391
|
output.on('close', () => {
|
|
3676
3392
|
if (deleteInput) {
|
|
3677
3393
|
try {
|
|
3678
|
-
|
|
3679
|
-
}
|
|
3394
|
+
fs_extra_1.default.unlinkSync(inputFilename);
|
|
3395
|
+
}
|
|
3396
|
+
catch {
|
|
3680
3397
|
// Ignore
|
|
3681
3398
|
}
|
|
3682
3399
|
}
|
|
3683
3400
|
resolve();
|
|
3684
3401
|
});
|
|
3685
|
-
|
|
3686
3402
|
input.pipe(compress).pipe(output);
|
|
3687
3403
|
});
|
|
3688
3404
|
}
|
|
3689
|
-
|
|
3690
|
-
|
|
3691
|
-
const ERROR_EMPTY_OBJECT = 'null object';
|
|
3692
|
-
const ERROR_NO_OBJECT = 'no object';
|
|
3693
|
-
const ERROR_DB_CLOSED = 'DB closed';
|
|
3694
|
-
|
|
3695
|
-
module.exports = {
|
|
3696
|
-
appName: getAppName(),
|
|
3697
|
-
createUuid,
|
|
3698
|
-
decryptPhrase,
|
|
3699
|
-
execAsync,
|
|
3700
|
-
findIPs,
|
|
3701
|
-
generateDefaultCertificates,
|
|
3702
|
-
getAdapterDir,
|
|
3703
|
-
getInstances,
|
|
3704
|
-
getAllInstances,
|
|
3705
|
-
getCertificateInfo,
|
|
3706
|
-
getConfigFileName,
|
|
3707
|
-
getDefaultDataDir,
|
|
3708
|
-
getDefaultNodeArgs,
|
|
3709
|
-
getFile,
|
|
3710
|
-
getHostInfo,
|
|
3711
|
-
getHostName,
|
|
3712
|
-
getInstalledInfo,
|
|
3713
|
-
getInstanceIndicatorObjects,
|
|
3714
|
-
getIoPack,
|
|
3715
|
-
getJson,
|
|
3716
|
-
getJsonAsync,
|
|
3717
|
-
getInstancesOrderedByStartPrio,
|
|
3718
|
-
getRepositoryFile,
|
|
3719
|
-
getRepositoryFileAsync,
|
|
3720
|
-
getSystemNpmVersion,
|
|
3721
|
-
installNodeModule,
|
|
3722
|
-
uninstallNodeModule,
|
|
3723
|
-
rebuildNodeModules,
|
|
3724
|
-
isObject,
|
|
3725
|
-
isArray,
|
|
3726
|
-
maybeCallback,
|
|
3727
|
-
maybeCallbackWithError,
|
|
3728
|
-
promisify,
|
|
3729
|
-
promisifyNoError,
|
|
3730
|
-
promiseSequence,
|
|
3731
|
-
removeIdFromAllEnums,
|
|
3732
|
-
resolveAdapterMainFile,
|
|
3733
|
-
rmdirRecursiveSync,
|
|
3734
|
-
parseDependencies,
|
|
3735
|
-
sendDiagInfo,
|
|
3736
|
-
upToDate,
|
|
3737
|
-
validateGeneralObjectProperties,
|
|
3738
|
-
checkNonEditable,
|
|
3739
|
-
copyAttributes,
|
|
3740
|
-
getDiskInfo,
|
|
3741
|
-
setQualityForInstance,
|
|
3742
|
-
appendStackTrace,
|
|
3743
|
-
captureStackTrace,
|
|
3744
|
-
pattern2RegEx,
|
|
3745
|
-
encrypt,
|
|
3746
|
-
decrypt,
|
|
3747
|
-
measureEventLoopLag,
|
|
3748
|
-
formatAliasValue,
|
|
3749
|
-
pipeLinewise,
|
|
3750
|
-
isShortGithubUrl,
|
|
3751
|
-
parseShortGithubUrl,
|
|
3752
|
-
setExecutableCapabilities,
|
|
3753
|
-
isGithubPathname,
|
|
3754
|
-
isSingleHost,
|
|
3755
|
-
isHostRunning,
|
|
3756
|
-
parseGithubPathname,
|
|
3757
|
-
removePreservedProperties,
|
|
3758
|
-
FORBIDDEN_CHARS,
|
|
3759
|
-
getControllerDir,
|
|
3760
|
-
getLogger,
|
|
3761
|
-
getAllEnums,
|
|
3762
|
-
updateLicenses,
|
|
3763
|
-
compressFileGZip,
|
|
3764
|
-
isDocker,
|
|
3765
|
-
ERRORS: {
|
|
3766
|
-
ERROR_NOT_FOUND: ERROR_NOT_FOUND,
|
|
3767
|
-
ERROR_EMPTY_OBJECT: ERROR_EMPTY_OBJECT,
|
|
3768
|
-
ERROR_NO_OBJECT: ERROR_NO_OBJECT,
|
|
3769
|
-
ERROR_DB_CLOSED: ERROR_DB_CLOSED
|
|
3770
|
-
}
|
|
3771
|
-
};
|
|
3405
|
+
exports.compressFileGZip = compressFileGZip;
|
|
3406
|
+
//# sourceMappingURL=tools.js.map
|