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