@iobroker/js-controller-cli 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 +89 -0
- package/{index.js → build/index.js} +7 -1
- package/build/lib/cli/cliCert.d.ts +16 -0
- package/build/lib/cli/cliCert.d.ts.map +1 -0
- package/{lib → build/lib}/cli/cliCert.js +5 -9
- package/build/lib/cli/cliCert.js.map +1 -0
- package/build/lib/cli/cliCommand.d.ts +24 -0
- package/build/lib/cli/cliCommand.d.ts.map +1 -0
- package/{lib → build/lib}/cli/cliCommand.js +2 -5
- package/build/lib/cli/cliCommand.js.map +1 -0
- package/build/lib/cli/cliCompact.d.ts +26 -0
- package/build/lib/cli/cliCompact.d.ts.map +1 -0
- package/{lib → build/lib}/cli/cliCompact.js +23 -43
- package/build/lib/cli/cliCompact.js.map +1 -0
- package/build/lib/cli/cliDebug.d.ts +19 -0
- package/build/lib/cli/cliDebug.d.ts.map +1 -0
- package/{lib → build/lib}/cli/cliDebug.js +6 -13
- package/build/lib/cli/cliDebug.js.map +1 -0
- package/build/lib/cli/cliHost.d.ts +36 -0
- package/build/lib/cli/cliHost.d.ts.map +1 -0
- package/{lib → build/lib}/cli/cliHost.js +22 -47
- package/build/lib/cli/cliHost.js.map +1 -0
- package/build/lib/cli/cliLogs.d.ts +58 -0
- package/build/lib/cli/cliLogs.d.ts.map +1 -0
- package/{lib → build/lib}/cli/cliLogs.js +16 -19
- package/build/lib/cli/cliLogs.js.map +1 -0
- package/build/lib/cli/cliMessage.d.ts +10 -0
- package/build/lib/cli/cliMessage.d.ts.map +1 -0
- package/{lib → build/lib}/cli/cliMessage.js +5 -7
- package/build/lib/cli/cliMessage.js.map +1 -0
- package/build/lib/cli/cliObjects.d.ts +105 -0
- package/build/lib/cli/cliObjects.d.ts.map +1 -0
- package/{lib → build/lib}/cli/cliObjects.js +69 -110
- package/build/lib/cli/cliObjects.js.map +1 -0
- package/build/lib/cli/cliPlugin.d.ts +23 -0
- package/build/lib/cli/cliPlugin.d.ts.map +1 -0
- package/{lib → build/lib}/cli/cliPlugin.js +24 -50
- package/build/lib/cli/cliPlugin.js.map +1 -0
- package/build/lib/cli/cliProcess.d.ts +50 -0
- package/build/lib/cli/cliProcess.d.ts.map +1 -0
- package/{lib → build/lib}/cli/cliProcess.js +50 -62
- package/build/lib/cli/cliProcess.js.map +1 -0
- package/build/lib/cli/cliStates.d.ts +51 -0
- package/build/lib/cli/cliStates.d.ts.map +1 -0
- package/{lib → build/lib}/cli/cliStates.js +64 -86
- package/build/lib/cli/cliStates.js.map +1 -0
- package/build/lib/cli/cliTools.d.ts +64 -0
- package/build/lib/cli/cliTools.d.ts.map +1 -0
- package/{lib → build/lib}/cli/cliTools.js +1 -12
- package/build/lib/cli/cliTools.js.map +1 -0
- package/build/lib/cli/messages.d.ts +53 -0
- package/build/lib/cli/messages.d.ts.map +1 -0
- package/{lib → build/lib}/cli/messages.js +27 -66
- package/build/lib/cli/messages.js.map +1 -0
- package/build/lib/setup/setupBackup.d.ts +126 -0
- package/build/lib/setup/setupBackup.d.ts.map +1 -0
- package/build/lib/setup/setupBackup.js +1031 -0
- package/build/lib/setup/setupBackup.js.map +1 -0
- package/build/lib/setup/setupInstall.d.ts +23 -0
- package/build/lib/setup/setupInstall.d.ts.map +1 -0
- package/build/lib/setup/setupInstall.js +1620 -0
- package/build/lib/setup/setupInstall.js.map +1 -0
- package/build/lib/setup/setupList.d.ts +49 -0
- package/build/lib/setup/setupList.d.ts.map +1 -0
- package/build/lib/setup/setupList.js +722 -0
- package/build/lib/setup/setupList.js.map +1 -0
- package/build/lib/setup/setupPacketManager.d.ts +65 -0
- package/build/lib/setup/setupPacketManager.d.ts.map +1 -0
- package/build/lib/setup/setupPacketManager.js +216 -0
- package/build/lib/setup/setupPacketManager.js.map +1 -0
- package/build/lib/setup/setupUpload.d.ts +72 -0
- package/build/lib/setup/setupUpload.d.ts.map +1 -0
- package/build/lib/setup/setupUpload.js +784 -0
- package/build/lib/setup/setupUpload.js.map +1 -0
- package/package.json +18 -11
- package/lib/setup/setupList.js +0 -840
|
@@ -0,0 +1,1620 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
/**
|
|
3
|
+
* Install adapter
|
|
4
|
+
*
|
|
5
|
+
* Copyright 2013-2022 bluefox <dogafox@gmail.com>
|
|
6
|
+
*
|
|
7
|
+
* MIT License
|
|
8
|
+
*
|
|
9
|
+
*/
|
|
10
|
+
var __importDefault = (this && this.__importDefault) || function (mod) {
|
|
11
|
+
return (mod && mod.__esModule) ? mod : { "default": mod };
|
|
12
|
+
};
|
|
13
|
+
Object.defineProperty(exports, "__esModule", { value: true });
|
|
14
|
+
const js_controller_common_1 = require("@iobroker/js-controller-common");
|
|
15
|
+
const fs_extra_1 = __importDefault(require("fs-extra"));
|
|
16
|
+
const path_1 = __importDefault(require("path"));
|
|
17
|
+
const semver_1 = __importDefault(require("semver"));
|
|
18
|
+
const child_process_1 = __importDefault(require("child_process"));
|
|
19
|
+
const axios_1 = __importDefault(require("axios"));
|
|
20
|
+
const os_1 = require("os");
|
|
21
|
+
const deep_clone_1 = __importDefault(require("deep-clone"));
|
|
22
|
+
const url_1 = require("url");
|
|
23
|
+
const setupUpload_1 = require("./setupUpload");
|
|
24
|
+
const setupPacketManager_1 = require("./setupPacketManager");
|
|
25
|
+
const hostname = js_controller_common_1.tools.getHostName();
|
|
26
|
+
const osPlatform = (0, os_1.platform)();
|
|
27
|
+
/** Note: this is duplicated in preinstallCheck */
|
|
28
|
+
const RECOMMENDED_NPM_VERSION = 8;
|
|
29
|
+
class Install {
|
|
30
|
+
constructor(options) {
|
|
31
|
+
// todo solve it somehow
|
|
32
|
+
this.unsafePermAlways = [
|
|
33
|
+
`${js_controller_common_1.tools.appName.toLowerCase()}.zwave`,
|
|
34
|
+
`${js_controller_common_1.tools.appName.toLowerCase()}.amazon-dash`,
|
|
35
|
+
`${js_controller_common_1.tools.appName.toLowerCase()}.xbox`
|
|
36
|
+
];
|
|
37
|
+
this.isRootOnUnix = typeof process.getuid === 'function' && process.getuid() === 0;
|
|
38
|
+
options = options || {};
|
|
39
|
+
if (!options.states) {
|
|
40
|
+
throw new Error('Invalid arguments: states is missing');
|
|
41
|
+
}
|
|
42
|
+
if (!options.objects) {
|
|
43
|
+
throw new Error('Invalid arguments: objects is missing');
|
|
44
|
+
}
|
|
45
|
+
if (!options.processExit) {
|
|
46
|
+
throw new Error('Invalid arguments: processExit is missing');
|
|
47
|
+
}
|
|
48
|
+
if (!options.getRepository) {
|
|
49
|
+
throw new Error('Invalid arguments: getRepository is missing');
|
|
50
|
+
}
|
|
51
|
+
this.objects = options.objects;
|
|
52
|
+
this.states = options.states;
|
|
53
|
+
this.processExit = options.processExit;
|
|
54
|
+
this.getRepository = options.getRepository;
|
|
55
|
+
this.params = options.params || {};
|
|
56
|
+
this.tarballRegex = /\/tarball\/[^/]+$/;
|
|
57
|
+
this.upload = new setupUpload_1.Upload(options);
|
|
58
|
+
}
|
|
59
|
+
/**
|
|
60
|
+
* Enables or disables given instances
|
|
61
|
+
*/
|
|
62
|
+
async enableInstances(instances, enabled) {
|
|
63
|
+
if (instances && instances.length) {
|
|
64
|
+
const ts = Date.now();
|
|
65
|
+
for (const instance of instances) {
|
|
66
|
+
const updatedObj = {
|
|
67
|
+
common: {
|
|
68
|
+
enabled
|
|
69
|
+
},
|
|
70
|
+
from: `system.host.${js_controller_common_1.tools.getHostName()}.cli`,
|
|
71
|
+
ts
|
|
72
|
+
};
|
|
73
|
+
console.log(`host.${hostname} Adapter "${instance._id}" is ${enabled ? 'started' : 'stopped.'}`);
|
|
74
|
+
// @ts-expect-error should be fixed with #1917
|
|
75
|
+
await this.objects.extendObjectAsync(instance._id, updatedObj);
|
|
76
|
+
}
|
|
77
|
+
}
|
|
78
|
+
}
|
|
79
|
+
/**
|
|
80
|
+
* Download given packet
|
|
81
|
+
*
|
|
82
|
+
* @param repoUrl
|
|
83
|
+
* @param packetName
|
|
84
|
+
* @param options, { stopDb: true } - will stop the db before upgrade ONLY use it for controller upgrade -
|
|
85
|
+
* db is gone afterwards, does not work with stoppedList
|
|
86
|
+
* @param stoppedList
|
|
87
|
+
*/
|
|
88
|
+
async downloadPacket(repoUrl, packetName, options, stoppedList) {
|
|
89
|
+
let url;
|
|
90
|
+
if (!options || typeof options !== 'object') {
|
|
91
|
+
options = {};
|
|
92
|
+
}
|
|
93
|
+
stoppedList = stoppedList || [];
|
|
94
|
+
let sources;
|
|
95
|
+
if (!repoUrl || typeof repoUrl !== 'object') {
|
|
96
|
+
try {
|
|
97
|
+
sources = await this.getRepository(repoUrl, this.params);
|
|
98
|
+
}
|
|
99
|
+
catch (err) {
|
|
100
|
+
return this.processExit(err);
|
|
101
|
+
}
|
|
102
|
+
}
|
|
103
|
+
else {
|
|
104
|
+
sources = repoUrl;
|
|
105
|
+
}
|
|
106
|
+
if (options.stopDb && stoppedList.length) {
|
|
107
|
+
console.warn('[downloadPacket] stoppedList cannot be used if stopping of databases is requested');
|
|
108
|
+
stoppedList = [];
|
|
109
|
+
}
|
|
110
|
+
const debug = process.argv.includes('--debug');
|
|
111
|
+
let version;
|
|
112
|
+
// check if the adapter has format adapter@1.0.0
|
|
113
|
+
if (packetName.includes('@')) {
|
|
114
|
+
const parts = packetName.split('@');
|
|
115
|
+
packetName = parts[0];
|
|
116
|
+
version = parts[1];
|
|
117
|
+
}
|
|
118
|
+
else {
|
|
119
|
+
// always take version from repository
|
|
120
|
+
if (sources[packetName] && sources[packetName].version) {
|
|
121
|
+
version = sources[packetName].version;
|
|
122
|
+
}
|
|
123
|
+
else {
|
|
124
|
+
version = '';
|
|
125
|
+
}
|
|
126
|
+
}
|
|
127
|
+
options.packetName = packetName;
|
|
128
|
+
options.unsafePerm = sources[packetName] && sources[packetName].unsafePerm;
|
|
129
|
+
// Check if flag stopBeforeUpdate is true or on windows we stop because of issue #1436
|
|
130
|
+
if (((sources[packetName] && sources[packetName].stopBeforeUpdate) || process.platform === 'win32') &&
|
|
131
|
+
!stoppedList.length) {
|
|
132
|
+
stoppedList = await this._getInstancesOfAdapter(packetName);
|
|
133
|
+
await this.enableInstances(stoppedList, false);
|
|
134
|
+
}
|
|
135
|
+
// try to extract the information from local sources-dist.json
|
|
136
|
+
if (!sources[packetName]) {
|
|
137
|
+
try {
|
|
138
|
+
const sourcesDist = fs_extra_1.default.readJsonSync(`${js_controller_common_1.tools.getControllerDir()}/conf/sources-dist.json`);
|
|
139
|
+
sources[packetName] = sourcesDist[packetName];
|
|
140
|
+
}
|
|
141
|
+
catch {
|
|
142
|
+
// OK
|
|
143
|
+
}
|
|
144
|
+
}
|
|
145
|
+
if (sources[packetName]) {
|
|
146
|
+
url = sources[packetName].url;
|
|
147
|
+
if (url &&
|
|
148
|
+
packetName === 'js-controller' &&
|
|
149
|
+
fs_extra_1.default.pathExistsSync(`${js_controller_common_1.tools.getControllerDir()}/../../node_modules/${js_controller_common_1.tools.appName}.js-controller`)) {
|
|
150
|
+
url = null;
|
|
151
|
+
}
|
|
152
|
+
if (!url && packetName !== 'example') {
|
|
153
|
+
if (options.stopDb) {
|
|
154
|
+
if (this.objects.destroy) {
|
|
155
|
+
await this.objects.destroy();
|
|
156
|
+
console.log('Stopped Objects DB');
|
|
157
|
+
}
|
|
158
|
+
if (this.states.destroy) {
|
|
159
|
+
await this.states.destroy();
|
|
160
|
+
console.log('Stopped States DB');
|
|
161
|
+
}
|
|
162
|
+
}
|
|
163
|
+
// Install node modules
|
|
164
|
+
await this._npmInstallWithCheck(`${js_controller_common_1.tools.appName.toLowerCase()}.${packetName}${version ? `@${version}` : ''}`, options, debug);
|
|
165
|
+
return { packetName, stoppedList };
|
|
166
|
+
}
|
|
167
|
+
else if (url && url.match(this.tarballRegex)) {
|
|
168
|
+
if (options.stopDb) {
|
|
169
|
+
if (this.objects.destroy) {
|
|
170
|
+
await this.objects.destroy();
|
|
171
|
+
console.log('Stopped Objects DB');
|
|
172
|
+
}
|
|
173
|
+
if (this.states.destroy) {
|
|
174
|
+
await this.states.destroy();
|
|
175
|
+
console.log('Stopped States DB');
|
|
176
|
+
}
|
|
177
|
+
}
|
|
178
|
+
// Install node modules
|
|
179
|
+
await this._npmInstallWithCheck(url, options, debug);
|
|
180
|
+
return { packetName, stoppedList };
|
|
181
|
+
}
|
|
182
|
+
else if (!url) {
|
|
183
|
+
// Adapter
|
|
184
|
+
console.warn(`host.${hostname} Adapter "${packetName}" can be updated only together with ${js_controller_common_1.tools.appName}.js-controller`);
|
|
185
|
+
return { packetName, stoppedList };
|
|
186
|
+
}
|
|
187
|
+
}
|
|
188
|
+
console.error(`host.${hostname} Unknown packetName ${packetName}. Please install packages from outside the repository using npm!`);
|
|
189
|
+
return this.processExit(js_controller_common_1.EXIT_CODES.UNKNOWN_PACKET_NAME);
|
|
190
|
+
}
|
|
191
|
+
/**
|
|
192
|
+
* Install npm module from url
|
|
193
|
+
*/
|
|
194
|
+
async _npmInstallWithCheck(npmUrl, options, debug) {
|
|
195
|
+
// Get npm version
|
|
196
|
+
try {
|
|
197
|
+
let npmVersion;
|
|
198
|
+
try {
|
|
199
|
+
npmVersion = child_process_1.default.execSync('npm -v', { encoding: 'utf8' });
|
|
200
|
+
if (npmVersion) {
|
|
201
|
+
npmVersion = semver_1.default.valid(npmVersion.trim());
|
|
202
|
+
}
|
|
203
|
+
console.log(`NPM version: ${npmVersion}`);
|
|
204
|
+
}
|
|
205
|
+
catch (err) {
|
|
206
|
+
console.error(`Error trying to check npm version: ${err.message}`);
|
|
207
|
+
}
|
|
208
|
+
if (!npmVersion) {
|
|
209
|
+
console.error('!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!');
|
|
210
|
+
console.error('Aborting install because the npm version could not be checked!');
|
|
211
|
+
console.error('Please check that npm is installed correctly.');
|
|
212
|
+
console.error(`Use "npm install -g npm@${RECOMMENDED_NPM_VERSION}" or "npm install -g npm@latest" to install a supported version.`);
|
|
213
|
+
console.error('You need to make sure to repeat this step after installing an update to NodeJS and/or npm');
|
|
214
|
+
console.error('!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!');
|
|
215
|
+
return this.processExit(js_controller_common_1.EXIT_CODES.INVALID_NPM_VERSION);
|
|
216
|
+
}
|
|
217
|
+
else if (semver_1.default.gte(npmVersion, '5.0.0') && semver_1.default.lt(npmVersion, '5.7.1')) {
|
|
218
|
+
console.error('!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!');
|
|
219
|
+
console.error('NPM 5 is only supported starting with version 5.7.1!');
|
|
220
|
+
console.error(`Please use "npm install -g npm@${RECOMMENDED_NPM_VERSION}" to upgrade npm to ${RECOMMENDED_NPM_VERSION}.x or `);
|
|
221
|
+
console.error('use "npm install -g npm@latest" to install a supported version of npm!');
|
|
222
|
+
console.error('You need to make sure to repeat this step after installing an update to NodeJS and/or npm');
|
|
223
|
+
console.error('!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!');
|
|
224
|
+
return this.processExit(js_controller_common_1.EXIT_CODES.INVALID_NPM_VERSION);
|
|
225
|
+
}
|
|
226
|
+
}
|
|
227
|
+
catch (err) {
|
|
228
|
+
console.error(`Could not check npm version: ${err.message}`);
|
|
229
|
+
console.error('Assuming that correct version is installed.');
|
|
230
|
+
}
|
|
231
|
+
try {
|
|
232
|
+
return await this._npmInstall(npmUrl, options, debug);
|
|
233
|
+
}
|
|
234
|
+
catch (err) {
|
|
235
|
+
console.error(`Could not install ${npmUrl}: ${err.message}`);
|
|
236
|
+
}
|
|
237
|
+
}
|
|
238
|
+
async _npmInstall(npmUrl, options, debug) {
|
|
239
|
+
if (typeof options !== 'object') {
|
|
240
|
+
options = {};
|
|
241
|
+
}
|
|
242
|
+
// zwave for example requires always unsafe-perm option
|
|
243
|
+
if (this.unsafePermAlways.some(adapter => npmUrl.includes(adapter))) {
|
|
244
|
+
options.unsafePerm = true;
|
|
245
|
+
}
|
|
246
|
+
else if (this.isRootOnUnix) {
|
|
247
|
+
// If ioBroker or the CLI is executed as root on unix platforms,
|
|
248
|
+
// not providing the --unsafe-perm options means that every pre/postinstall
|
|
249
|
+
// script fails when it uses npm commands.
|
|
250
|
+
options.unsafePerm = true;
|
|
251
|
+
}
|
|
252
|
+
console.log(` Installing ${npmUrl}... (System call)`);
|
|
253
|
+
const result = await js_controller_common_1.tools.installNodeModule(npmUrl, {
|
|
254
|
+
debug: !!debug,
|
|
255
|
+
unsafePerm: !!options.unsafePerm
|
|
256
|
+
});
|
|
257
|
+
if (result.success || result.exitCode === 1) {
|
|
258
|
+
// code 1 is strange error that cannot be explained. Everything is installed but error :(
|
|
259
|
+
// Determine where the packet would be installed if npm succeeds
|
|
260
|
+
// TODO: There's probably a better way to figure this out
|
|
261
|
+
let packetDirName;
|
|
262
|
+
if (options.packetName) {
|
|
263
|
+
packetDirName = js_controller_common_1.tools.appName.toLowerCase() + '.' + options.packetName;
|
|
264
|
+
}
|
|
265
|
+
else {
|
|
266
|
+
packetDirName = npmUrl.toLowerCase();
|
|
267
|
+
// If the user installed a git commit-ish, the url contains stuff that doesn't belong in a folder name
|
|
268
|
+
// e.g. iobroker/iobroker.javascript#branch-name
|
|
269
|
+
if (packetDirName.includes('#')) {
|
|
270
|
+
packetDirName = packetDirName.substr(0, packetDirName.indexOf('#'));
|
|
271
|
+
}
|
|
272
|
+
if (packetDirName.includes('/') && !packetDirName.startsWith('@')) {
|
|
273
|
+
// only scoped packages (e.g. @types/node ) may have a slash in their path
|
|
274
|
+
packetDirName = packetDirName.substring(packetDirName.lastIndexOf('/') + 1);
|
|
275
|
+
}
|
|
276
|
+
}
|
|
277
|
+
const installDir = js_controller_common_1.tools.getAdapterDir(packetDirName);
|
|
278
|
+
// inject the installedFrom information in io-package
|
|
279
|
+
if (installDir && fs_extra_1.default.existsSync(installDir)) {
|
|
280
|
+
const ioPackPath = path_1.default.join(installDir, 'io-package.json');
|
|
281
|
+
let iopack;
|
|
282
|
+
try {
|
|
283
|
+
iopack = fs_extra_1.default.readJSONSync(ioPackPath);
|
|
284
|
+
}
|
|
285
|
+
catch {
|
|
286
|
+
iopack = null;
|
|
287
|
+
}
|
|
288
|
+
if (iopack) {
|
|
289
|
+
iopack.common = iopack.common || {};
|
|
290
|
+
iopack.common.installedFrom = npmUrl;
|
|
291
|
+
try {
|
|
292
|
+
fs_extra_1.default.writeFileSync(ioPackPath, JSON.stringify(iopack, null, 2), 'utf8');
|
|
293
|
+
}
|
|
294
|
+
catch {
|
|
295
|
+
// OK
|
|
296
|
+
}
|
|
297
|
+
}
|
|
298
|
+
}
|
|
299
|
+
else {
|
|
300
|
+
// TODO: revisit this - this should not happen
|
|
301
|
+
console.error(`host.${hostname} Cannot install ${npmUrl}: ${result.exitCode}`);
|
|
302
|
+
return this.processExit(js_controller_common_1.EXIT_CODES.CANNOT_INSTALL_NPM_PACKET);
|
|
303
|
+
}
|
|
304
|
+
// create file that indicates, that npm was called there
|
|
305
|
+
fs_extra_1.default.writeFileSync(path_1.default.join(installDir, 'iob_npm.done'), ' ');
|
|
306
|
+
// command succeeded
|
|
307
|
+
return { _url: npmUrl, installDir: path_1.default.dirname(installDir) };
|
|
308
|
+
}
|
|
309
|
+
else {
|
|
310
|
+
console.error(`host.${hostname} Cannot install ${npmUrl}: ${result.exitCode}`);
|
|
311
|
+
return this.processExit(js_controller_common_1.EXIT_CODES.CANNOT_INSTALL_NPM_PACKET);
|
|
312
|
+
}
|
|
313
|
+
}
|
|
314
|
+
async _npmUninstall(packageName, debug) {
|
|
315
|
+
const result = await js_controller_common_1.tools.uninstallNodeModule(packageName, { debug: !!debug });
|
|
316
|
+
if (!result.success) {
|
|
317
|
+
throw new Error(`host.${hostname}: Cannot uninstall ${packageName}: ${result.exitCode}`);
|
|
318
|
+
}
|
|
319
|
+
}
|
|
320
|
+
// this command is executed always on THIS host
|
|
321
|
+
async _checkDependencies(adapter, deps, globalDeps, _options) {
|
|
322
|
+
if (!deps && !globalDeps) {
|
|
323
|
+
return adapter;
|
|
324
|
+
}
|
|
325
|
+
deps = js_controller_common_1.tools.parseDependencies(deps);
|
|
326
|
+
globalDeps = js_controller_common_1.tools.parseDependencies(globalDeps);
|
|
327
|
+
// combine both dependencies
|
|
328
|
+
const allDeps = { ...deps, ...globalDeps };
|
|
329
|
+
// Get all installed adapters
|
|
330
|
+
// @ts-expect-error #1917 should fix it
|
|
331
|
+
const objs = await this.objects.getObjectViewAsync('system', 'instance', {
|
|
332
|
+
startkey: 'system.adapter.',
|
|
333
|
+
endkey: 'system.adapter.\u9999'
|
|
334
|
+
});
|
|
335
|
+
if (objs && objs.rows && objs.rows.length) {
|
|
336
|
+
for (const dName in allDeps) {
|
|
337
|
+
let isFound = false;
|
|
338
|
+
if (dName === 'js-controller') {
|
|
339
|
+
const version = allDeps[dName];
|
|
340
|
+
// Check only if version not *, else we dont have to read io-pack unnecessarily
|
|
341
|
+
if (version !== '*') {
|
|
342
|
+
const packJson = fs_extra_1.default.readJSONSync(`${js_controller_common_1.tools.getControllerDir()}/package.json`);
|
|
343
|
+
if (!semver_1.default.satisfies(packJson.version, version, { includePrerelease: true })) {
|
|
344
|
+
console.error(`host.${hostname} Invalid version of "${dName}". Installed "${packJson.version}", required "${version}"`);
|
|
345
|
+
return this.processExit(js_controller_common_1.EXIT_CODES.INVALID_DEPENDENCY_VERSION);
|
|
346
|
+
}
|
|
347
|
+
else {
|
|
348
|
+
isFound = true;
|
|
349
|
+
}
|
|
350
|
+
}
|
|
351
|
+
else {
|
|
352
|
+
isFound = true;
|
|
353
|
+
}
|
|
354
|
+
}
|
|
355
|
+
if (!isFound) {
|
|
356
|
+
let gInstances = [];
|
|
357
|
+
let locInstances = [];
|
|
358
|
+
// if global dep get all instances of adapter
|
|
359
|
+
if (globalDeps[dName] !== undefined) {
|
|
360
|
+
gInstances = objs.rows.filter(obj => obj && obj.value && obj.value.common && obj.value.common.name === dName);
|
|
361
|
+
}
|
|
362
|
+
if (deps[dName] !== undefined) {
|
|
363
|
+
// local dep get all instances on same host
|
|
364
|
+
locInstances = objs.rows.filter(obj => obj &&
|
|
365
|
+
obj.value &&
|
|
366
|
+
obj.value.common &&
|
|
367
|
+
obj.value.common.name === dName &&
|
|
368
|
+
obj.value.common.host === hostname);
|
|
369
|
+
if (locInstances.length === 0) {
|
|
370
|
+
console.error(`host.${hostname} Required dependency "${dName}" not found on this host.`);
|
|
371
|
+
}
|
|
372
|
+
}
|
|
373
|
+
// we check, that all existing instances match - respect different versions for local and global deps
|
|
374
|
+
for (const instance of locInstances) {
|
|
375
|
+
if (
|
|
376
|
+
// @ts-expect-error InstaceCommon has version: TODO fix tpes
|
|
377
|
+
!semver_1.default.satisfies(instance.value.common.version, deps[dName], {
|
|
378
|
+
includePrerelease: true
|
|
379
|
+
})) {
|
|
380
|
+
console.error(`host.${hostname} Invalid version of "${dName}". Installed "${
|
|
381
|
+
// @ts-expect-error InstaceCommon has version: TODO fix tpes
|
|
382
|
+
instance.value.common.version}", required "${deps[dName]}"`);
|
|
383
|
+
return this.processExit(js_controller_common_1.EXIT_CODES.INVALID_DEPENDENCY_VERSION);
|
|
384
|
+
}
|
|
385
|
+
else {
|
|
386
|
+
isFound = true;
|
|
387
|
+
}
|
|
388
|
+
}
|
|
389
|
+
for (const instance of gInstances) {
|
|
390
|
+
if (
|
|
391
|
+
// @ts-expect-error InstaceCommon has version: TODO fix tpes
|
|
392
|
+
!semver_1.default.satisfies(instance.value.common.version, globalDeps[dName], {
|
|
393
|
+
includePrerelease: true
|
|
394
|
+
})) {
|
|
395
|
+
console.error(`host.${hostname} Invalid version of "${dName}". Installed "${
|
|
396
|
+
// @ts-expect-error InstaceCommon has version: TODO fix tpes
|
|
397
|
+
instance.value.common.version}", required "${globalDeps[dName]}"`);
|
|
398
|
+
return this.processExit(js_controller_common_1.EXIT_CODES.INVALID_DEPENDENCY_VERSION);
|
|
399
|
+
}
|
|
400
|
+
else {
|
|
401
|
+
isFound = true;
|
|
402
|
+
}
|
|
403
|
+
}
|
|
404
|
+
}
|
|
405
|
+
// if required dependency not found => install it
|
|
406
|
+
if (!isFound) {
|
|
407
|
+
await this.createInstance(dName, _options);
|
|
408
|
+
}
|
|
409
|
+
}
|
|
410
|
+
}
|
|
411
|
+
}
|
|
412
|
+
async _uploadStaticObjects(adapter, _adapterConf) {
|
|
413
|
+
let adapterConf;
|
|
414
|
+
if (!_adapterConf) {
|
|
415
|
+
const adapterDir = js_controller_common_1.tools.getAdapterDir(adapter);
|
|
416
|
+
if (!adapterDir || !fs_extra_1.default.existsSync(`${adapterDir}/io-package.json`)) {
|
|
417
|
+
const message = `Adapter directory "${adapterDir}" does not exists`;
|
|
418
|
+
console.error(`host.${hostname} ${message}`);
|
|
419
|
+
throw new Error(message);
|
|
420
|
+
}
|
|
421
|
+
try {
|
|
422
|
+
adapterConf = await fs_extra_1.default.readJSON(path_1.default.join(adapterDir, 'io-package.json'));
|
|
423
|
+
}
|
|
424
|
+
catch (err) {
|
|
425
|
+
const message = `error reading io-package.json: ${err.message}`;
|
|
426
|
+
console.error(`host.${hostname} ${message}`, adapter);
|
|
427
|
+
throw new Error(message);
|
|
428
|
+
}
|
|
429
|
+
}
|
|
430
|
+
else {
|
|
431
|
+
adapterConf = _adapterConf;
|
|
432
|
+
}
|
|
433
|
+
let objs;
|
|
434
|
+
if (adapterConf.objects && adapterConf.objects.length > 0) {
|
|
435
|
+
objs = adapterConf.objects;
|
|
436
|
+
}
|
|
437
|
+
else {
|
|
438
|
+
objs = [];
|
|
439
|
+
}
|
|
440
|
+
// check if some dependencies are missing and install them if some found
|
|
441
|
+
await this._checkDependencies(adapter, adapterConf.common.dependencies, adapterConf.common.globalDependencies, this.params);
|
|
442
|
+
adapterConf.common.installedVersion = adapterConf.common.version;
|
|
443
|
+
if (adapterConf.common.news) {
|
|
444
|
+
delete adapterConf.common.news; // remove this information as it will be taken from repo
|
|
445
|
+
}
|
|
446
|
+
objs.push({
|
|
447
|
+
_id: `system.adapter.${adapterConf.common.name}`,
|
|
448
|
+
type: 'adapter',
|
|
449
|
+
common: adapterConf.common,
|
|
450
|
+
native: adapterConf.native
|
|
451
|
+
});
|
|
452
|
+
if (objs && objs.length) {
|
|
453
|
+
for (let i = 0; i < objs.length; i++) {
|
|
454
|
+
const obj = objs[i];
|
|
455
|
+
obj.from = `system.host.${js_controller_common_1.tools.getHostName()}.cli`;
|
|
456
|
+
obj.ts = Date.now();
|
|
457
|
+
try {
|
|
458
|
+
// @ts-expect-error #1917
|
|
459
|
+
await this.objects.extendObjectAsync(obj._id, obj);
|
|
460
|
+
}
|
|
461
|
+
catch (err) {
|
|
462
|
+
console.error(`host.${hostname} error setObject ${obj._id} ${err.message}`);
|
|
463
|
+
return js_controller_common_1.EXIT_CODES.CANNOT_SET_OBJECT;
|
|
464
|
+
}
|
|
465
|
+
console.log(`host.${hostname} object ${obj._id} created/updated`);
|
|
466
|
+
}
|
|
467
|
+
}
|
|
468
|
+
}
|
|
469
|
+
/**
|
|
470
|
+
* Installs given adapter
|
|
471
|
+
*/
|
|
472
|
+
async installAdapter(adapter, repoUrl, _installCount) {
|
|
473
|
+
_installCount = _installCount || 0;
|
|
474
|
+
const fullName = adapter;
|
|
475
|
+
if (adapter.includes('@')) {
|
|
476
|
+
adapter = adapter.split('@')[0];
|
|
477
|
+
}
|
|
478
|
+
const adapterDir = js_controller_common_1.tools.getAdapterDir(adapter);
|
|
479
|
+
console.log(`host.${hostname} install adapter ${fullName}`);
|
|
480
|
+
if (!fs_extra_1.default.existsSync(adapterDir + '/io-package.json')) {
|
|
481
|
+
if (_installCount === 2) {
|
|
482
|
+
console.error(`host.${hostname} Cannot install ${adapter}`);
|
|
483
|
+
return this.processExit(js_controller_common_1.EXIT_CODES.CANNOT_INSTALL_NPM_PACKET);
|
|
484
|
+
}
|
|
485
|
+
_installCount++;
|
|
486
|
+
// @ts-expect-error TODO needs adaption
|
|
487
|
+
const { stoppedList } = await this.downloadPacket(repoUrl, fullName);
|
|
488
|
+
await this.installAdapter(adapter, repoUrl, _installCount);
|
|
489
|
+
await this.enableInstances(stoppedList, true); // even if unlikely make sure to reenable disabled instances
|
|
490
|
+
return adapter;
|
|
491
|
+
}
|
|
492
|
+
let adapterConf;
|
|
493
|
+
try {
|
|
494
|
+
adapterConf = fs_extra_1.default.readJSONSync(`${adapterDir}/io-package.json`);
|
|
495
|
+
}
|
|
496
|
+
catch (err) {
|
|
497
|
+
console.error(`host.${hostname} error: reading io-package.json ${err.message}`);
|
|
498
|
+
return this.processExit(js_controller_common_1.EXIT_CODES.INVALID_IO_PACKAGE_JSON);
|
|
499
|
+
}
|
|
500
|
+
// Check if the operation system is ok
|
|
501
|
+
if (adapterConf.common && adapterConf.common.os) {
|
|
502
|
+
if (typeof adapterConf.common.os === 'string' && adapterConf.common.os !== osPlatform) {
|
|
503
|
+
console.error(`host.${hostname} Adapter does not support current os. Required ${adapterConf.common.os}. Actual platform: ${osPlatform}`);
|
|
504
|
+
return this.processExit(js_controller_common_1.EXIT_CODES.INVALID_OS);
|
|
505
|
+
}
|
|
506
|
+
else {
|
|
507
|
+
if (!adapterConf.common.os.includes(osPlatform)) {
|
|
508
|
+
console.error(`host.${hostname} Adapter does not support current os. Required one of ${adapterConf.common.os.join(', ')}. Actual platform: ${osPlatform}`);
|
|
509
|
+
return this.processExit(js_controller_common_1.EXIT_CODES.INVALID_OS);
|
|
510
|
+
}
|
|
511
|
+
}
|
|
512
|
+
}
|
|
513
|
+
let engineVersion;
|
|
514
|
+
try {
|
|
515
|
+
// read directly from disk and not via require to allow "on the fly" updates of adapters.
|
|
516
|
+
const p = JSON.parse(fs_extra_1.default.readFileSync(adapterDir + '/package.json', 'utf8'));
|
|
517
|
+
engineVersion = p && p.engines && p.engines.node;
|
|
518
|
+
}
|
|
519
|
+
catch {
|
|
520
|
+
console.error(`host.${hostname}: Cannot read and parse "${adapterDir}/package.json"`);
|
|
521
|
+
}
|
|
522
|
+
// check node.js version if defined in package.json
|
|
523
|
+
if (engineVersion) {
|
|
524
|
+
if (!semver_1.default.satisfies(process.version.replace(/^v/, ''), engineVersion)) {
|
|
525
|
+
console.error(`host.${hostname} Adapter does not support current nodejs version. Required ${engineVersion}. Actual version: ${process.version}`);
|
|
526
|
+
return this.processExit(js_controller_common_1.EXIT_CODES.INVALID_NODE_VERSION);
|
|
527
|
+
}
|
|
528
|
+
}
|
|
529
|
+
if (adapterConf.common.osDependencies && adapterConf.common.osDependencies[process.platform]) {
|
|
530
|
+
// install linux/osx libraries
|
|
531
|
+
try {
|
|
532
|
+
this.packetManager = this.packetManager || new setupPacketManager_1.PacketManager();
|
|
533
|
+
await this.packetManager.install(adapterConf.common.osDependencies[process.platform]);
|
|
534
|
+
}
|
|
535
|
+
catch (err) {
|
|
536
|
+
console.error(`host.${hostname} Could not install required OS packages: ${err.message}`);
|
|
537
|
+
}
|
|
538
|
+
}
|
|
539
|
+
await this.upload.uploadAdapter(adapter, true, true);
|
|
540
|
+
await this.upload.uploadAdapter(adapter, false, true);
|
|
541
|
+
await this.callInstallOfAdapter(adapter, adapterConf);
|
|
542
|
+
await this._uploadStaticObjects(adapter);
|
|
543
|
+
await this.upload.upgradeAdapterObjects(adapter);
|
|
544
|
+
return adapter;
|
|
545
|
+
}
|
|
546
|
+
async callInstallOfAdapter(adapter, config) {
|
|
547
|
+
if (config.common.install) {
|
|
548
|
+
// Install node modules
|
|
549
|
+
let cmd = 'node ';
|
|
550
|
+
let fileFullName;
|
|
551
|
+
try {
|
|
552
|
+
fileFullName = await js_controller_common_1.tools.resolveAdapterMainFile(adapter);
|
|
553
|
+
}
|
|
554
|
+
catch {
|
|
555
|
+
return;
|
|
556
|
+
}
|
|
557
|
+
return new Promise(resolve => {
|
|
558
|
+
cmd += `"${fileFullName}" --install`;
|
|
559
|
+
console.log(`host.${hostname} command: ${cmd}`);
|
|
560
|
+
const child = child_process_1.default.exec(cmd, { windowsHide: true });
|
|
561
|
+
if (child.stderr) {
|
|
562
|
+
js_controller_common_1.tools.pipeLinewise(child.stderr, process.stdout);
|
|
563
|
+
}
|
|
564
|
+
child.on('exit', () => resolve(adapter));
|
|
565
|
+
});
|
|
566
|
+
}
|
|
567
|
+
}
|
|
568
|
+
/**
|
|
569
|
+
* Create adapter instance
|
|
570
|
+
*/
|
|
571
|
+
async createInstance(adapter, options) {
|
|
572
|
+
let ignoreIfExists = false;
|
|
573
|
+
options = options || {};
|
|
574
|
+
options.host = options.host || js_controller_common_1.tools.getHostName();
|
|
575
|
+
if (options.enabled === 'true') {
|
|
576
|
+
options.enabled = true;
|
|
577
|
+
}
|
|
578
|
+
if (options.enabled === 'false') {
|
|
579
|
+
options.enabled = false;
|
|
580
|
+
}
|
|
581
|
+
if (options.ignoreIfExists !== undefined) {
|
|
582
|
+
ignoreIfExists = !!options.ignoreIfExists;
|
|
583
|
+
delete options.ignoreIfExists;
|
|
584
|
+
}
|
|
585
|
+
let doc;
|
|
586
|
+
let err;
|
|
587
|
+
try {
|
|
588
|
+
doc = await this.objects.getObjectAsync(`system.adapter.${adapter}`);
|
|
589
|
+
}
|
|
590
|
+
catch (_err) {
|
|
591
|
+
err = _err;
|
|
592
|
+
}
|
|
593
|
+
// Adapter is not installed - install it now
|
|
594
|
+
if (err || !doc || !doc.common.installedVersion) {
|
|
595
|
+
await this.installAdapter(adapter);
|
|
596
|
+
doc = await this.objects.getObjectAsync(`system.adapter.${adapter}`);
|
|
597
|
+
}
|
|
598
|
+
if (!doc) {
|
|
599
|
+
console.error('Adapter object not found, cannot create instance');
|
|
600
|
+
return void this.processExit(js_controller_common_1.EXIT_CODES.ADAPTER_NOT_FOUND);
|
|
601
|
+
}
|
|
602
|
+
// Check if some web pages should be uploaded
|
|
603
|
+
await this.upload.uploadAdapter(adapter, true, false);
|
|
604
|
+
await this.upload.uploadAdapter(adapter, false, false);
|
|
605
|
+
// @ts-expect-error #1917
|
|
606
|
+
const res = await this.objects.getObjectViewAsync('system', 'instance', {
|
|
607
|
+
startkey: `system.adapter.${adapter}.`,
|
|
608
|
+
endkey: `system.adapter.${adapter}.\u9999`
|
|
609
|
+
});
|
|
610
|
+
const systemConfig = await this.objects.getObjectAsync('system.config');
|
|
611
|
+
const defaultLogLevel = systemConfig && systemConfig.common && systemConfig.common.defaultLogLevel;
|
|
612
|
+
if (!res) {
|
|
613
|
+
console.error(`host.${hostname} error: view instanceStats`);
|
|
614
|
+
return this.processExit(js_controller_common_1.EXIT_CODES.CANNOT_READ_INSTANCES);
|
|
615
|
+
}
|
|
616
|
+
// Count started instances
|
|
617
|
+
if (doc.common.singleton && res.rows.length) {
|
|
618
|
+
if (ignoreIfExists) {
|
|
619
|
+
return;
|
|
620
|
+
}
|
|
621
|
+
console.error(`host.${hostname} error: this adapter does not allow multiple instances`);
|
|
622
|
+
return this.processExit(js_controller_common_1.EXIT_CODES.NO_MULTIPLE_INSTANCES_ALLOWED);
|
|
623
|
+
}
|
|
624
|
+
// check singletonHost one on host
|
|
625
|
+
if (doc.common.singletonHost) {
|
|
626
|
+
for (const row of res.rows) {
|
|
627
|
+
if (row.value.common.host === hostname) {
|
|
628
|
+
if (ignoreIfExists) {
|
|
629
|
+
return;
|
|
630
|
+
}
|
|
631
|
+
console.error(`host.${hostname} error: this adapter does not allow multiple instances on one host`);
|
|
632
|
+
return this.processExit(js_controller_common_1.EXIT_CODES.NO_MULTIPLE_INSTANCES_ALLOWED_ON_HOST);
|
|
633
|
+
}
|
|
634
|
+
}
|
|
635
|
+
}
|
|
636
|
+
let instance = null;
|
|
637
|
+
if (options.instance !== undefined) {
|
|
638
|
+
instance = options.instance;
|
|
639
|
+
// find max instance
|
|
640
|
+
if (res.rows.find(obj => parseInt(obj.id.split('.').pop(), 10) === instance)) {
|
|
641
|
+
console.error(`host.${hostname} error: instance yet exists`);
|
|
642
|
+
return this.processExit(js_controller_common_1.EXIT_CODES.INSTANCE_ALREADY_EXISTS);
|
|
643
|
+
}
|
|
644
|
+
}
|
|
645
|
+
else {
|
|
646
|
+
// find max instance
|
|
647
|
+
for (const row of res.rows) {
|
|
648
|
+
const iInstance = parseInt(row.id.split('.').pop(), 10);
|
|
649
|
+
if (instance === null || iInstance > instance) {
|
|
650
|
+
instance = iInstance;
|
|
651
|
+
}
|
|
652
|
+
}
|
|
653
|
+
if (instance === null) {
|
|
654
|
+
instance = 0;
|
|
655
|
+
}
|
|
656
|
+
else {
|
|
657
|
+
instance++;
|
|
658
|
+
}
|
|
659
|
+
}
|
|
660
|
+
const instanceObj = (0, deep_clone_1.default)(doc);
|
|
661
|
+
instanceObj._id = `system.adapter.${adapter}.${instance}`;
|
|
662
|
+
// @ts-expect-error we now convert the adapter object to an instance object
|
|
663
|
+
instanceObj.type = 'instance';
|
|
664
|
+
// @ts-expect-error types needed TODO
|
|
665
|
+
if (instanceObj.common.news) {
|
|
666
|
+
// @ts-expect-error types needed TODO
|
|
667
|
+
delete instanceObj.common.news; // remove this information as it could be big, but it will be taken from repo
|
|
668
|
+
}
|
|
669
|
+
instanceObj.common.enabled =
|
|
670
|
+
options.enabled === true || options.enabled === false
|
|
671
|
+
? options.enabled
|
|
672
|
+
: instanceObj.common.enabled === true || instanceObj.common.enabled === false
|
|
673
|
+
? instanceObj.common.enabled
|
|
674
|
+
: false;
|
|
675
|
+
// @ts-expect-error we now convert the adapter object to an instance object
|
|
676
|
+
instanceObj.common.host = options.host;
|
|
677
|
+
if (options.port) {
|
|
678
|
+
instanceObj.native = instanceObj.native || {};
|
|
679
|
+
instanceObj.native.port = options.port;
|
|
680
|
+
}
|
|
681
|
+
if (instanceObj.common.dataFolder && instanceObj.common.dataFolder.includes('%INSTANCE%')) {
|
|
682
|
+
instanceObj.common.dataFolder = instanceObj.common.dataFolder.replace(/%INSTANCE%/g, instance.toString());
|
|
683
|
+
}
|
|
684
|
+
if (defaultLogLevel) {
|
|
685
|
+
// @ts-expect-error TODO should it be loglevel or logLevel
|
|
686
|
+
instanceObj.common.loglevel = defaultLogLevel;
|
|
687
|
+
// @ts-expect-error TODO should it be loglevel or logLevel
|
|
688
|
+
}
|
|
689
|
+
else if (!instanceObj.common.loglevel) {
|
|
690
|
+
// @ts-expect-error TODO should it be loglevel or logLevel looked into my objects -> loglevel
|
|
691
|
+
instanceObj.common.loglevel = 'info';
|
|
692
|
+
}
|
|
693
|
+
console.log(`host.${hostname} create instance ${adapter}`);
|
|
694
|
+
let objs;
|
|
695
|
+
if (!instanceObj.common.onlyWWW && instanceObj.common.mode !== 'once') {
|
|
696
|
+
objs = js_controller_common_1.tools.getInstanceIndicatorObjects(`${adapter}.${instance}`, !!instanceObj.common.wakeup);
|
|
697
|
+
}
|
|
698
|
+
else {
|
|
699
|
+
objs = [];
|
|
700
|
+
}
|
|
701
|
+
const adapterDir = js_controller_common_1.tools.getAdapterDir(adapter);
|
|
702
|
+
if (adapterDir && fs_extra_1.default.existsSync(path_1.default.join(adapterDir, 'www'))) {
|
|
703
|
+
objs.push({
|
|
704
|
+
_id: `system.adapter.${adapter}.upload`,
|
|
705
|
+
type: 'state',
|
|
706
|
+
common: {
|
|
707
|
+
name: adapter + '.upload',
|
|
708
|
+
type: 'number',
|
|
709
|
+
read: true,
|
|
710
|
+
write: false,
|
|
711
|
+
role: 'indicator.state',
|
|
712
|
+
unit: '%',
|
|
713
|
+
def: 0,
|
|
714
|
+
desc: 'Upload process indicator'
|
|
715
|
+
},
|
|
716
|
+
native: {}
|
|
717
|
+
});
|
|
718
|
+
}
|
|
719
|
+
let adapterConf;
|
|
720
|
+
try {
|
|
721
|
+
adapterConf = fs_extra_1.default.readJSONSync(`${adapterDir}/io-package.json`);
|
|
722
|
+
}
|
|
723
|
+
catch (err) {
|
|
724
|
+
console.error(`host.${hostname} error: reading io-package.json ${err.message}`);
|
|
725
|
+
return this.processExit(js_controller_common_1.EXIT_CODES.INVALID_IO_PACKAGE_JSON);
|
|
726
|
+
}
|
|
727
|
+
adapterConf.instanceObjects = adapterConf.instanceObjects || [];
|
|
728
|
+
adapterConf.objects = adapterConf.objects || [];
|
|
729
|
+
const defStates = new Map();
|
|
730
|
+
// Create only for this instance the predefined in io-package.json objects
|
|
731
|
+
// It is not necessary to write "system.adapter.name.N." in the object '_id'
|
|
732
|
+
for (const instanceObject of adapterConf.instanceObjects) {
|
|
733
|
+
instanceObject._id = `${adapter}.${instance}${instanceObject._id ? `.${instanceObject._id}` : ''}`;
|
|
734
|
+
if (instanceObject.common) {
|
|
735
|
+
if (instanceObject.common.name) {
|
|
736
|
+
// if name has many languages
|
|
737
|
+
if (typeof instanceObject.common.name === 'object') {
|
|
738
|
+
Object.keys(instanceObject.common.name).forEach(lang => (instanceObject.common.name[lang] = instanceObject.common.name[lang].replace('%INSTANCE%', instance)));
|
|
739
|
+
}
|
|
740
|
+
else {
|
|
741
|
+
instanceObject.common.name = instanceObject.common.name.replace('%INSTANCE%', instance);
|
|
742
|
+
}
|
|
743
|
+
}
|
|
744
|
+
if (instanceObject.common.desc) {
|
|
745
|
+
// if name has many languages
|
|
746
|
+
if (typeof instanceObject.common.desc === 'object') {
|
|
747
|
+
Object.keys(instanceObject.common.desc).forEach(lang => (instanceObject.common.desc[lang] = instanceObject.common.desc[lang].replace('%INSTANCE%', instance)));
|
|
748
|
+
}
|
|
749
|
+
else {
|
|
750
|
+
instanceObject.common.desc = instanceObject.common.desc.replace('%INSTANCE%', instance);
|
|
751
|
+
}
|
|
752
|
+
}
|
|
753
|
+
}
|
|
754
|
+
objs.push(instanceObject);
|
|
755
|
+
if (instanceObject.common && instanceObject.common.def !== undefined) {
|
|
756
|
+
defStates.set(instanceObject._id, {
|
|
757
|
+
val: instanceObject.common.def
|
|
758
|
+
});
|
|
759
|
+
}
|
|
760
|
+
}
|
|
761
|
+
// upload all objects
|
|
762
|
+
for (const obj of objs) {
|
|
763
|
+
try {
|
|
764
|
+
js_controller_common_1.tools.validateGeneralObjectProperties(obj);
|
|
765
|
+
}
|
|
766
|
+
catch (err) {
|
|
767
|
+
// todo: in the future we will not create this object
|
|
768
|
+
console.warn(`host.${hostname} Object ${obj._id} is invalid: ${err.message}`);
|
|
769
|
+
console.warn(`host.${hostname} This object will not be created in future versions. Please report this to the developer.`);
|
|
770
|
+
}
|
|
771
|
+
obj.from = `system.host.${js_controller_common_1.tools.getHostName()}.cli`;
|
|
772
|
+
obj.ts = Date.now();
|
|
773
|
+
try {
|
|
774
|
+
await this.objects.setObjectAsync(obj._id, obj);
|
|
775
|
+
console.log(`host.${hostname} object ${obj._id} created`);
|
|
776
|
+
}
|
|
777
|
+
catch (err) {
|
|
778
|
+
console.error(`host.${hostname} error: ${err.message}`);
|
|
779
|
+
}
|
|
780
|
+
}
|
|
781
|
+
// sets the default states if any given
|
|
782
|
+
for (const [id, defState] of defStates) {
|
|
783
|
+
defState.ack = true;
|
|
784
|
+
defState.from = `system.host.${js_controller_common_1.tools.getHostName()}.cli`;
|
|
785
|
+
try {
|
|
786
|
+
await this.states.setStateAsync(id, defState);
|
|
787
|
+
console.log(`host.${hostname} Set default value of ${id}: ${defState.val}`);
|
|
788
|
+
}
|
|
789
|
+
catch (err) {
|
|
790
|
+
console.error(`host.${hostname} error: ${err.message}`);
|
|
791
|
+
}
|
|
792
|
+
}
|
|
793
|
+
instanceObj.from = `system.host.${js_controller_common_1.tools.getHostName()}.cli`;
|
|
794
|
+
instanceObj.ts = Date.now();
|
|
795
|
+
try {
|
|
796
|
+
await this.objects.setObjectAsync(instanceObj._id, instanceObj);
|
|
797
|
+
console.log(`host.${hostname} object ${instanceObj._id} created`);
|
|
798
|
+
}
|
|
799
|
+
catch (err) {
|
|
800
|
+
console.error(`host.${hostname} error: ${err.message}`);
|
|
801
|
+
}
|
|
802
|
+
}
|
|
803
|
+
/**
|
|
804
|
+
* Enumerate all instances of an adapter
|
|
805
|
+
*/
|
|
806
|
+
async _enumerateAdapterInstances(knownObjIDs, notDeleted, adapter, instance) {
|
|
807
|
+
if (!notDeleted) {
|
|
808
|
+
notDeleted = [];
|
|
809
|
+
}
|
|
810
|
+
// We need to filter the instances using RegExp, because the naive approach with startkey/endkey
|
|
811
|
+
// startkey: system.adapter.mqtt
|
|
812
|
+
// endkey: system.adapter.mqtt.\u9999
|
|
813
|
+
// matches system.adapter.mqtt AND system.adapter.mqtt-client
|
|
814
|
+
const instanceRegex = instance !== undefined
|
|
815
|
+
? new RegExp(`^system\\.adapter\\.${adapter}\\.${instance}$`)
|
|
816
|
+
: new RegExp(`^system\\.adapter\\.${adapter}\\.\\d+$`);
|
|
817
|
+
try {
|
|
818
|
+
const doc = await this.objects.getObjectView('system', 'instance', {
|
|
819
|
+
startkey: `system.adapter.${adapter}${instance !== undefined ? `.${instance}` : ''}`,
|
|
820
|
+
endkey: `system.adapter.${adapter}${instance !== undefined ? `.${instance}` : ''}\u9999`
|
|
821
|
+
});
|
|
822
|
+
// add non-duplicates to the list (if instance not given -> only for this host)
|
|
823
|
+
const newObjIDs = doc.rows
|
|
824
|
+
// only the ones with an ID ...
|
|
825
|
+
.filter((row) => { var _a; return !!((_a = row === null || row === void 0 ? void 0 : row.value) === null || _a === void 0 ? void 0 : _a._id); })
|
|
826
|
+
// ... that matches the pattern
|
|
827
|
+
.filter(row => instanceRegex.test(row.value._id))
|
|
828
|
+
// if instance given also delete from foreign host else only instance on this host
|
|
829
|
+
.filter(row => {
|
|
830
|
+
var _a, _b;
|
|
831
|
+
if (instance !== undefined || !((_a = row.value.common) === null || _a === void 0 ? void 0 : _a.host) || ((_b = row.value.common) === null || _b === void 0 ? void 0 : _b.host) === hostname) {
|
|
832
|
+
return true;
|
|
833
|
+
}
|
|
834
|
+
else {
|
|
835
|
+
if (!notDeleted.includes(row.value._id)) {
|
|
836
|
+
notDeleted.push(row.value._id);
|
|
837
|
+
}
|
|
838
|
+
return false;
|
|
839
|
+
}
|
|
840
|
+
})
|
|
841
|
+
.map(row => row.value._id)
|
|
842
|
+
.filter(id => !knownObjIDs.includes(id));
|
|
843
|
+
knownObjIDs.push(...newObjIDs);
|
|
844
|
+
if (newObjIDs.length > 0) {
|
|
845
|
+
console.log(`host.${hostname} Counted ${newObjIDs.length} instances of ${adapter}${instance !== undefined ? `.${instance}` : ''}`);
|
|
846
|
+
}
|
|
847
|
+
}
|
|
848
|
+
catch (err) {
|
|
849
|
+
err !== js_controller_common_1.tools.ERRORS.ERROR_NOT_FOUND &&
|
|
850
|
+
err.message !== js_controller_common_1.tools.ERRORS.ERROR_NOT_FOUND &&
|
|
851
|
+
console.error(`host.${hostname} error: ${err.message}`);
|
|
852
|
+
}
|
|
853
|
+
}
|
|
854
|
+
/**
|
|
855
|
+
* Enumerate all meta objects of an adapter
|
|
856
|
+
*/
|
|
857
|
+
async _enumerateAdapterMeta(knownObjIDs, adapter, metaFilesToDelete) {
|
|
858
|
+
try {
|
|
859
|
+
// @ts-expect-error #1917
|
|
860
|
+
const doc = await this.objects.getObjectViewAsync('system', 'meta', {
|
|
861
|
+
startkey: `${adapter}.`,
|
|
862
|
+
endkey: `${adapter}.\u9999`
|
|
863
|
+
});
|
|
864
|
+
if (doc && doc.rows.length !== 0) {
|
|
865
|
+
const adapterRegex = new RegExp(`^${adapter}\\.`);
|
|
866
|
+
// add non-duplicates to the list
|
|
867
|
+
const newObjs = doc.rows
|
|
868
|
+
.filter(row => row && row.value && row.value._id)
|
|
869
|
+
.map(row => row.value._id)
|
|
870
|
+
.filter(id => adapterRegex.test(id))
|
|
871
|
+
.filter(id => knownObjIDs.indexOf(id) === -1);
|
|
872
|
+
knownObjIDs.push(...newObjs);
|
|
873
|
+
// meta ids can also be present as files
|
|
874
|
+
metaFilesToDelete.push(...newObjs);
|
|
875
|
+
if (newObjs.length) {
|
|
876
|
+
console.log(`host.${hostname} Counted ${newObjs.length} meta of ${adapter}`);
|
|
877
|
+
}
|
|
878
|
+
}
|
|
879
|
+
}
|
|
880
|
+
catch (err) {
|
|
881
|
+
err !== js_controller_common_1.tools.ERRORS.ERROR_NOT_FOUND &&
|
|
882
|
+
err.message !== js_controller_common_1.tools.ERRORS.ERROR_NOT_FOUND &&
|
|
883
|
+
console.error(`host.${hostname} error: ${err.message}`);
|
|
884
|
+
}
|
|
885
|
+
}
|
|
886
|
+
async _enumerateAdapters(knownObjIDs, adapter) {
|
|
887
|
+
// This does not really enumerate the adapters, but finds the adapter object
|
|
888
|
+
// if it exists and adds it to the list
|
|
889
|
+
try {
|
|
890
|
+
const obj = await this.objects.getObjectAsync(`system.adapter.${adapter}`);
|
|
891
|
+
if (obj) {
|
|
892
|
+
if (obj.common && obj.common.nondeletable) {
|
|
893
|
+
// If the adapter is non-deletable, mark it as not installed
|
|
894
|
+
console.log(`host.${hostname} Adapter ${adapter} cannot be deleted completely, because it is marked non-deletable.`);
|
|
895
|
+
obj.common.installedVersion = '';
|
|
896
|
+
obj.from = `system.host.${js_controller_common_1.tools.getHostName()}.cli`;
|
|
897
|
+
obj.ts = Date.now();
|
|
898
|
+
await this.objects.setObjectAsync(obj._id, obj);
|
|
899
|
+
return js_controller_common_1.EXIT_CODES.CANNOT_DELETE_NON_DELETABLE;
|
|
900
|
+
}
|
|
901
|
+
else {
|
|
902
|
+
// The adapter is deletable, remember it for deletion
|
|
903
|
+
knownObjIDs.push(obj._id);
|
|
904
|
+
console.log(`host.${hostname} Counted 1 adapter for ${adapter}`);
|
|
905
|
+
return js_controller_common_1.EXIT_CODES.NO_ERROR;
|
|
906
|
+
}
|
|
907
|
+
}
|
|
908
|
+
}
|
|
909
|
+
catch (err) {
|
|
910
|
+
console.error(`host.${hostname} Cannot enumerate adapters: ${err.message}`);
|
|
911
|
+
}
|
|
912
|
+
}
|
|
913
|
+
/**
|
|
914
|
+
* Enumerates the devices of an adapter (or instance)
|
|
915
|
+
*
|
|
916
|
+
* @param knownObjIDs The already known object ids
|
|
917
|
+
* @param adapter The adapter to enumerate the devices for
|
|
918
|
+
* @param instance The instance to enumerate the devices for (optional)
|
|
919
|
+
*/
|
|
920
|
+
async _enumerateAdapterDevices(knownObjIDs, adapter, instance) {
|
|
921
|
+
const adapterRegex = new RegExp(`^${adapter}${instance ? `\\.${instance}` : ''}\\.`);
|
|
922
|
+
try {
|
|
923
|
+
// @ts-expect-error #1917
|
|
924
|
+
const doc = await this.objects.getObjectViewAsync('system', 'device', {
|
|
925
|
+
startkey: `${adapter}${instance !== undefined ? `.${instance}` : ''}`,
|
|
926
|
+
endkey: `${adapter}${instance !== undefined ? `.${instance}` : ''}\u9999`
|
|
927
|
+
});
|
|
928
|
+
if (doc && doc.rows && doc.rows.length) {
|
|
929
|
+
// add non-duplicates to the list
|
|
930
|
+
const newObjs = doc.rows
|
|
931
|
+
.filter(row => row && row.value && row.value._id)
|
|
932
|
+
.map(row => row.value._id)
|
|
933
|
+
.filter(id => adapterRegex.test(id))
|
|
934
|
+
.filter(id => !knownObjIDs.includes(id));
|
|
935
|
+
knownObjIDs.push(...newObjs);
|
|
936
|
+
if (newObjs.length > 0) {
|
|
937
|
+
console.log(`host.${hostname} Counted ${newObjs.length} devices of ${adapter}${instance !== undefined ? `.${instance}` : ''}`);
|
|
938
|
+
}
|
|
939
|
+
}
|
|
940
|
+
}
|
|
941
|
+
catch (err) {
|
|
942
|
+
err !== js_controller_common_1.tools.ERRORS.ERROR_NOT_FOUND &&
|
|
943
|
+
err.message !== js_controller_common_1.tools.ERRORS.ERROR_NOT_FOUND &&
|
|
944
|
+
console.error(`host.${hostname} error: ${err.message}`);
|
|
945
|
+
}
|
|
946
|
+
}
|
|
947
|
+
/**
|
|
948
|
+
* Enumerates the channels of an adapter (or instance)
|
|
949
|
+
* @param knownObjIDs The already known object ids
|
|
950
|
+
* @param adapter The adapter to enumerate the channels for
|
|
951
|
+
* @param instance The instance to enumerate the channels for (optional)
|
|
952
|
+
*/
|
|
953
|
+
async _enumerateAdapterChannels(knownObjIDs, adapter, instance) {
|
|
954
|
+
const adapterRegex = new RegExp(`^${adapter}${instance ? `\\.${instance}` : ''}\\.`);
|
|
955
|
+
try {
|
|
956
|
+
// @ts-expect-error #1917
|
|
957
|
+
const doc = await this.objects.getObjectViewAsync('system', 'channel', {
|
|
958
|
+
startkey: `${adapter}${instance !== undefined ? `.${instance}` : ''}`,
|
|
959
|
+
endkey: `${adapter}${instance !== undefined ? `.${instance}` : ''}\u9999`
|
|
960
|
+
});
|
|
961
|
+
if (doc && doc.rows && doc.rows.length) {
|
|
962
|
+
// add non-duplicates to the list
|
|
963
|
+
const newObjs = doc.rows
|
|
964
|
+
.filter(row => row && row.value && row.value._id)
|
|
965
|
+
.map(row => row.value._id)
|
|
966
|
+
.filter(id => adapterRegex.test(id))
|
|
967
|
+
.filter(id => !knownObjIDs.includes(id));
|
|
968
|
+
knownObjIDs.push(...newObjs);
|
|
969
|
+
if (newObjs.length > 0) {
|
|
970
|
+
console.log(`host.${hostname} Counted ${newObjs.length} channels of ${adapter}${instance ? `.${instance}` : ''}`);
|
|
971
|
+
}
|
|
972
|
+
}
|
|
973
|
+
}
|
|
974
|
+
catch (err) {
|
|
975
|
+
err !== js_controller_common_1.tools.ERRORS.ERROR_NOT_FOUND &&
|
|
976
|
+
err.message !== js_controller_common_1.tools.ERRORS.ERROR_NOT_FOUND &&
|
|
977
|
+
console.error(`host.${hostname} error: ${err.message}`);
|
|
978
|
+
}
|
|
979
|
+
}
|
|
980
|
+
/**
|
|
981
|
+
* Enumerates the states of an adapter (or instance)
|
|
982
|
+
* @param knownObjIDs The already known object ids
|
|
983
|
+
* @param adapter The adapter to enumerate the states for
|
|
984
|
+
* @param instance The instance to enumerate the states for (optional)
|
|
985
|
+
*/
|
|
986
|
+
async _enumerateAdapterStateObjects(knownObjIDs, adapter, instance) {
|
|
987
|
+
const adapterRegex = new RegExp(`^${adapter}${instance ? `\\.${instance}` : ''}\\.`);
|
|
988
|
+
const sysAdapterRegex = new RegExp(`^system\\.adapter\\.${adapter}${instance ? `\\.${instance}` : ''}\\.`);
|
|
989
|
+
try {
|
|
990
|
+
// @ts-expect-error #1917
|
|
991
|
+
let doc = await this.objects.getObjectViewAsync('system', 'state', {
|
|
992
|
+
startkey: `${adapter}${instance !== undefined ? `.${instance}` : ''}`,
|
|
993
|
+
endkey: `${adapter}${instance !== undefined ? `.${instance}` : ''}\u9999`
|
|
994
|
+
});
|
|
995
|
+
if (doc && doc.rows && doc.rows.length) {
|
|
996
|
+
// add non-duplicates to the list
|
|
997
|
+
const newObjs = doc.rows
|
|
998
|
+
.filter(row => row && row.value && row.value._id)
|
|
999
|
+
.map(row => row.value._id)
|
|
1000
|
+
.filter(id => adapterRegex.test(id))
|
|
1001
|
+
.filter(id => !knownObjIDs.includes(id));
|
|
1002
|
+
knownObjIDs.push(...newObjs);
|
|
1003
|
+
if (newObjs.length > 0) {
|
|
1004
|
+
console.log(`host.${hostname} Counted ${newObjs.length} states of ${adapter}${instance ? `.${instance}` : ''}`);
|
|
1005
|
+
}
|
|
1006
|
+
}
|
|
1007
|
+
// @ts-expect-error #1917
|
|
1008
|
+
doc = await this.objects.getObjectViewAsync('system', 'state', {
|
|
1009
|
+
startkey: `system.adapter.${adapter}${instance !== undefined ? `.${instance}` : ''}`,
|
|
1010
|
+
endkey: `system.adapter.${adapter}${instance !== undefined ? `.${instance}` : ''}\u9999`
|
|
1011
|
+
});
|
|
1012
|
+
if (doc && doc.rows && doc.rows.length) {
|
|
1013
|
+
// add non-duplicates to the list
|
|
1014
|
+
const newObjs = doc.rows
|
|
1015
|
+
.filter(row => row && row.value && row.value._id)
|
|
1016
|
+
.map(row => row.value._id)
|
|
1017
|
+
.filter(id => sysAdapterRegex.test(id))
|
|
1018
|
+
.filter(id => !knownObjIDs.includes(id));
|
|
1019
|
+
knownObjIDs.push(...newObjs);
|
|
1020
|
+
if (newObjs.length > 0) {
|
|
1021
|
+
console.log(`host.${hostname} Counted ${newObjs.length} states of system.adapter.${adapter}${instance ? `.${instance}` : ''}`);
|
|
1022
|
+
}
|
|
1023
|
+
}
|
|
1024
|
+
}
|
|
1025
|
+
catch (err) {
|
|
1026
|
+
err !== js_controller_common_1.tools.ERRORS.ERROR_NOT_FOUND &&
|
|
1027
|
+
err.message !== js_controller_common_1.tools.ERRORS.ERROR_NOT_FOUND &&
|
|
1028
|
+
console.error(`host.${hostname} error: ${err.message}`);
|
|
1029
|
+
}
|
|
1030
|
+
}
|
|
1031
|
+
/**
|
|
1032
|
+
* Enumerates the docs of an adapter (or instance)
|
|
1033
|
+
* @param knownObjIDs The already known object ids
|
|
1034
|
+
* @param adapter The adapter to enumerate the states for
|
|
1035
|
+
* @param instance The instance to enumerate the states for (optional)
|
|
1036
|
+
*/
|
|
1037
|
+
async _enumerateAdapterDocs(knownObjIDs, adapter, instance) {
|
|
1038
|
+
const adapterRegex = new RegExp(`^${adapter}${instance ? `\\.${instance}` : ''}\\.`);
|
|
1039
|
+
const sysAdapterRegex = new RegExp(`^system\\.adapter\\.${adapter}${instance ? `\\.${instance}` : ''}\\.`);
|
|
1040
|
+
try {
|
|
1041
|
+
// @ts-expect-error #1917
|
|
1042
|
+
const doc = await this.objects.getObjectListAsync({ include_docs: true });
|
|
1043
|
+
if (doc && doc.rows && doc.rows.length) {
|
|
1044
|
+
// add non-duplicates to the list
|
|
1045
|
+
const newObjs = doc.rows
|
|
1046
|
+
.filter(row => row && row.value && row.value._id)
|
|
1047
|
+
.map(row => row.value._id)
|
|
1048
|
+
.filter(id => adapterRegex.test(id) || sysAdapterRegex.test(id))
|
|
1049
|
+
.filter(id => !knownObjIDs.includes(id));
|
|
1050
|
+
knownObjIDs.push(...newObjs);
|
|
1051
|
+
if (newObjs.length > 0) {
|
|
1052
|
+
console.log(`host.${hostname} Counted ${newObjs.length} objects of ${adapter}${instance ? `.${instance}` : ''}`);
|
|
1053
|
+
}
|
|
1054
|
+
}
|
|
1055
|
+
}
|
|
1056
|
+
catch (err) {
|
|
1057
|
+
err !== js_controller_common_1.tools.ERRORS.ERROR_NOT_FOUND &&
|
|
1058
|
+
err.message !== js_controller_common_1.tools.ERRORS.ERROR_NOT_FOUND &&
|
|
1059
|
+
console.error(`host.${hostname} error: ${err.message}`);
|
|
1060
|
+
}
|
|
1061
|
+
}
|
|
1062
|
+
/**
|
|
1063
|
+
* Enumerate all state IDs of an adapter (or instance)
|
|
1064
|
+
*/
|
|
1065
|
+
async _enumerateAdapterStates(knownStateIDs, adapter, instance) {
|
|
1066
|
+
for (const pattern of [
|
|
1067
|
+
`io.${adapter}.${instance ? instance + '.' : ''}*`,
|
|
1068
|
+
`messagebox.${adapter}.${instance ? instance + '.' : ''}*`,
|
|
1069
|
+
`log.${adapter}.${instance ? instance + '.' : ''}*`,
|
|
1070
|
+
`${adapter}.${instance ? instance + '.' : ''}*`,
|
|
1071
|
+
`system.adapter.${adapter}.${instance ? instance + '.' : ''}*`
|
|
1072
|
+
]) {
|
|
1073
|
+
try {
|
|
1074
|
+
// @ts-expect-error #1917
|
|
1075
|
+
const ids = await this.states.getKeys(pattern);
|
|
1076
|
+
if (ids && ids.length) {
|
|
1077
|
+
// add non-duplicates to the list
|
|
1078
|
+
const newStates = ids.filter(id => !knownStateIDs.includes(id));
|
|
1079
|
+
knownStateIDs.push(...newStates);
|
|
1080
|
+
if (newStates.length) {
|
|
1081
|
+
console.log(`host.${hostname} Counted ${newStates.length} states (${pattern}) from states`);
|
|
1082
|
+
}
|
|
1083
|
+
}
|
|
1084
|
+
}
|
|
1085
|
+
catch (err) {
|
|
1086
|
+
console.error(`host.${hostname} Cannot get keys async: ${err.message}`);
|
|
1087
|
+
}
|
|
1088
|
+
}
|
|
1089
|
+
}
|
|
1090
|
+
/**
|
|
1091
|
+
* delete WWW pages, objects and meta files
|
|
1092
|
+
*/
|
|
1093
|
+
async _deleteAdapterFiles(adapter, metaFilesToDelete) {
|
|
1094
|
+
// special files, which are not meta (vis widgets), combined with meta object ids
|
|
1095
|
+
const filesToDelete = [
|
|
1096
|
+
{ id: `vis`, name: `widgets/${adapter}` },
|
|
1097
|
+
{ id: `vis`, name: `widgets/${adapter}.html` },
|
|
1098
|
+
{ id: adapter },
|
|
1099
|
+
{ id: `${adapter}.admin` },
|
|
1100
|
+
...metaFilesToDelete.map(id => ({ id }))
|
|
1101
|
+
];
|
|
1102
|
+
for (const file of filesToDelete) {
|
|
1103
|
+
const id = typeof file === 'object' ? file.id : file;
|
|
1104
|
+
try {
|
|
1105
|
+
// @ts-expect-error #1917
|
|
1106
|
+
await this.objects.unlinkAsync(id, file.name || '');
|
|
1107
|
+
console.log(`host.${hostname} file ${id + (file.name ? `/${file.name}` : '')} deleted`);
|
|
1108
|
+
}
|
|
1109
|
+
catch (err) {
|
|
1110
|
+
err !== js_controller_common_1.tools.ERRORS.ERROR_NOT_FOUND &&
|
|
1111
|
+
err.message !== js_controller_common_1.tools.ERRORS.ERROR_NOT_FOUND &&
|
|
1112
|
+
console.error(`host.${hostname} Cannot delete ${id} files folder: ${err.message}`);
|
|
1113
|
+
}
|
|
1114
|
+
}
|
|
1115
|
+
for (const objId of [adapter, `${adapter}.admin`]) {
|
|
1116
|
+
try {
|
|
1117
|
+
await this.objects.delObjectAsync(objId);
|
|
1118
|
+
console.log(`host.${hostname} object ${objId} deleted`);
|
|
1119
|
+
}
|
|
1120
|
+
catch (err) {
|
|
1121
|
+
err !== js_controller_common_1.tools.ERRORS.ERROR_NOT_FOUND &&
|
|
1122
|
+
err.message !== js_controller_common_1.tools.ERRORS.ERROR_NOT_FOUND &&
|
|
1123
|
+
console.error(`host.${hostname} cannot delete objects: ${err.message}`);
|
|
1124
|
+
}
|
|
1125
|
+
}
|
|
1126
|
+
}
|
|
1127
|
+
async _deleteAdapterStates(stateIDs) {
|
|
1128
|
+
if (stateIDs.length > 1000) {
|
|
1129
|
+
console.log(`host.${hostname} Deleting ${stateIDs.length} state(s). Be patient...`);
|
|
1130
|
+
}
|
|
1131
|
+
else if (stateIDs.length) {
|
|
1132
|
+
console.log(`host.${hostname} Deleting ${stateIDs.length} state(s).`);
|
|
1133
|
+
}
|
|
1134
|
+
while (stateIDs.length > 0) {
|
|
1135
|
+
if (stateIDs.length % 200 === 0) {
|
|
1136
|
+
// write progress report
|
|
1137
|
+
console.log(`host.${hostname}: Only ${stateIDs.length} states left to be deleted.`);
|
|
1138
|
+
}
|
|
1139
|
+
// try to delete the current state
|
|
1140
|
+
try {
|
|
1141
|
+
// @ts-expect-error #1917
|
|
1142
|
+
await this.states.delState(stateIDs.pop());
|
|
1143
|
+
}
|
|
1144
|
+
catch (err) {
|
|
1145
|
+
// yep that works!
|
|
1146
|
+
err !== js_controller_common_1.tools.ERRORS.ERROR_NOT_FOUND &&
|
|
1147
|
+
err.message !== js_controller_common_1.tools.ERRORS.ERROR_NOT_FOUND &&
|
|
1148
|
+
console.error(`host.${hostname} Cannot delete states: ${err.message}`);
|
|
1149
|
+
}
|
|
1150
|
+
}
|
|
1151
|
+
}
|
|
1152
|
+
async _deleteAdapterObjects(objIDs) {
|
|
1153
|
+
if (objIDs.length > 1000) {
|
|
1154
|
+
console.log(`host.${hostname} Deleting ${objIDs.length} object(s). Be patient...`);
|
|
1155
|
+
}
|
|
1156
|
+
else if (objIDs.length) {
|
|
1157
|
+
console.log(`host.${hostname} Deleting ${objIDs.length} object(s).`);
|
|
1158
|
+
}
|
|
1159
|
+
let allEnums;
|
|
1160
|
+
if (objIDs.length > 1) {
|
|
1161
|
+
try {
|
|
1162
|
+
// cache all enums, else it will be slow to delete many objects
|
|
1163
|
+
allEnums = await js_controller_common_1.tools.getAllEnums(this.objects);
|
|
1164
|
+
}
|
|
1165
|
+
catch (e) {
|
|
1166
|
+
console.error(`host.${hostname}: Could not retrieve all enums: ${e.message}`);
|
|
1167
|
+
}
|
|
1168
|
+
}
|
|
1169
|
+
while (objIDs.length > 0) {
|
|
1170
|
+
if (objIDs.length % 200 === 0) {
|
|
1171
|
+
// write progress report
|
|
1172
|
+
console.log(`host.${hostname}: Only ${objIDs.length} objects left to be deleted.`);
|
|
1173
|
+
}
|
|
1174
|
+
// try to delete the current object
|
|
1175
|
+
try {
|
|
1176
|
+
const id = objIDs.pop();
|
|
1177
|
+
await this.objects.delObjectAsync(id);
|
|
1178
|
+
await js_controller_common_1.tools.removeIdFromAllEnums(this.objects, id, allEnums);
|
|
1179
|
+
}
|
|
1180
|
+
catch (e) {
|
|
1181
|
+
if (e !== js_controller_common_1.tools.ERRORS.ERROR_NOT_FOUND && e.message !== js_controller_common_1.tools.ERRORS.ERROR_NOT_FOUND) {
|
|
1182
|
+
console.error(`host.${hostname} cannot delete objects: ${e.message}`);
|
|
1183
|
+
}
|
|
1184
|
+
}
|
|
1185
|
+
}
|
|
1186
|
+
}
|
|
1187
|
+
/**
|
|
1188
|
+
* Deltes given adapter from filesystem and removes all instances
|
|
1189
|
+
*/
|
|
1190
|
+
async deleteAdapter(adapter) {
|
|
1191
|
+
const knownObjectIDs = [];
|
|
1192
|
+
const metaFilesToDelete = [];
|
|
1193
|
+
const notDeletedObjectIDs = [];
|
|
1194
|
+
const knownStateIDs = [];
|
|
1195
|
+
let resultCode = js_controller_common_1.EXIT_CODES.NO_ERROR;
|
|
1196
|
+
const _uninstallNpm = async () => {
|
|
1197
|
+
try {
|
|
1198
|
+
// find the adapter's io-package.json
|
|
1199
|
+
const adapterNpm = `${js_controller_common_1.tools.appName}.${adapter}`;
|
|
1200
|
+
// eslint-disable-next-line @typescript-eslint/no-var-requires
|
|
1201
|
+
const ioPack = require(`${adapterNpm}/io-package.json`); // yep, it's that easy
|
|
1202
|
+
if (!ioPack.common || !ioPack.common.nondeletable) {
|
|
1203
|
+
await this._npmUninstall(adapterNpm, false);
|
|
1204
|
+
// after uninstalling we have to restart the defined adapters
|
|
1205
|
+
if (ioPack.common.restartAdapters) {
|
|
1206
|
+
if (!Array.isArray(ioPack.common.restartAdapters)) {
|
|
1207
|
+
// its not an array, now it can only be a single adapter as string
|
|
1208
|
+
if (typeof ioPack.common.restartAdapters !== 'string') {
|
|
1209
|
+
return;
|
|
1210
|
+
}
|
|
1211
|
+
ioPack.common.restartAdapters = [ioPack.common.restartAdapters];
|
|
1212
|
+
}
|
|
1213
|
+
if (ioPack.common.restartAdapters.length && ioPack.common.restartAdapters[0]) {
|
|
1214
|
+
const instances = await js_controller_common_1.tools.getAllInstances(ioPack.common.restartAdapters, this.objects);
|
|
1215
|
+
if (instances && instances.length) {
|
|
1216
|
+
for (const instance of instances) {
|
|
1217
|
+
const obj = await this.objects.getObjectAsync(instance);
|
|
1218
|
+
// if instance is enabled
|
|
1219
|
+
if (obj && obj.common && obj.common.enabled) {
|
|
1220
|
+
try {
|
|
1221
|
+
obj.common.enabled = false; // disable instance
|
|
1222
|
+
obj.from = `system.host.${js_controller_common_1.tools.getHostName()}.cli`;
|
|
1223
|
+
obj.ts = Date.now();
|
|
1224
|
+
await this.objects.setObjectAsync(obj._id, obj);
|
|
1225
|
+
obj.common.enabled = true; // enable instance
|
|
1226
|
+
obj.from = `system.host.${js_controller_common_1.tools.getHostName()}.cli`;
|
|
1227
|
+
obj.ts = Date.now();
|
|
1228
|
+
await this.objects.setObjectAsync(obj._id, obj);
|
|
1229
|
+
console.log(`Adapter "${obj._id}" restarted.`);
|
|
1230
|
+
}
|
|
1231
|
+
catch (err) {
|
|
1232
|
+
console.error(`Cannot restart adapter "${obj._id}": ${err.message}`);
|
|
1233
|
+
}
|
|
1234
|
+
}
|
|
1235
|
+
}
|
|
1236
|
+
}
|
|
1237
|
+
}
|
|
1238
|
+
}
|
|
1239
|
+
}
|
|
1240
|
+
}
|
|
1241
|
+
catch (err) {
|
|
1242
|
+
console.error(`Error deleting adapter ${adapter} from disk: ${err.message}`);
|
|
1243
|
+
console.error(`You might have to delete it yourself!`);
|
|
1244
|
+
}
|
|
1245
|
+
};
|
|
1246
|
+
try {
|
|
1247
|
+
// detect if all instances on this host, if not so the www and admin must not be deleted
|
|
1248
|
+
await this._enumerateAdapterInstances(knownObjectIDs, notDeletedObjectIDs, adapter);
|
|
1249
|
+
if (notDeletedObjectIDs.length) {
|
|
1250
|
+
// just delete all instances on this host and then delete npm
|
|
1251
|
+
for (const knownObjectID of knownObjectIDs) {
|
|
1252
|
+
await this.deleteInstance(adapter, parseInt(knownObjectID.split('.').pop()));
|
|
1253
|
+
}
|
|
1254
|
+
// remove adapter from custom
|
|
1255
|
+
await this._removeCustomFromObjects([adapter]);
|
|
1256
|
+
await _uninstallNpm();
|
|
1257
|
+
}
|
|
1258
|
+
else {
|
|
1259
|
+
// we are not allowed to delete last instance if another instance depends on us
|
|
1260
|
+
const dependentInstance = await this._hasDependentInstances(adapter);
|
|
1261
|
+
if (dependentInstance) {
|
|
1262
|
+
console.log(`Cannot remove adapter "${adapter}", because instance "${dependentInstance}" depends on it!`);
|
|
1263
|
+
return js_controller_common_1.EXIT_CODES.CANNOT_DELETE_DEPENDENCY;
|
|
1264
|
+
}
|
|
1265
|
+
const instances = knownObjectIDs.map(id => `${adapter}.${id.split('.').pop()}`);
|
|
1266
|
+
await this._enumerateAdapterMeta(knownObjectIDs, adapter, metaFilesToDelete);
|
|
1267
|
+
resultCode = (await this._enumerateAdapters(knownObjectIDs, adapter)) || resultCode;
|
|
1268
|
+
await this._enumerateAdapterDevices(knownObjectIDs, adapter);
|
|
1269
|
+
await this._enumerateAdapterChannels(knownObjectIDs, adapter);
|
|
1270
|
+
await this._enumerateAdapterStateObjects(knownObjectIDs, adapter);
|
|
1271
|
+
await this._enumerateAdapterStates(knownStateIDs, adapter);
|
|
1272
|
+
await this._enumerateAdapterDocs(knownObjectIDs, adapter);
|
|
1273
|
+
await this._deleteAdapterFiles(adapter, metaFilesToDelete);
|
|
1274
|
+
await this._deleteAdapterObjects(knownObjectIDs);
|
|
1275
|
+
await this._deleteAdapterStates(knownStateIDs);
|
|
1276
|
+
if (this.params.custom) {
|
|
1277
|
+
// remove adapter from custom
|
|
1278
|
+
await this._removeCustomFromObjects([...instances, adapter]);
|
|
1279
|
+
}
|
|
1280
|
+
await _uninstallNpm();
|
|
1281
|
+
}
|
|
1282
|
+
}
|
|
1283
|
+
catch (err) {
|
|
1284
|
+
console.error(`There was an error uninstalling ${adapter} on ${hostname}: ${err.message}`);
|
|
1285
|
+
}
|
|
1286
|
+
return resultCode;
|
|
1287
|
+
}
|
|
1288
|
+
/**
|
|
1289
|
+
* Deletes given instance of an adapter
|
|
1290
|
+
*
|
|
1291
|
+
* @param adapter adapter name like hm-rpc
|
|
1292
|
+
* @param instance e.g. 1
|
|
1293
|
+
*/
|
|
1294
|
+
async deleteInstance(adapter, instance) {
|
|
1295
|
+
const knownObjectIDs = [];
|
|
1296
|
+
const knownStateIDs = [];
|
|
1297
|
+
// we are not allowed to delete last instance if another instance depends on us
|
|
1298
|
+
const dependentInstance = await this._hasDependentInstances(adapter, instance);
|
|
1299
|
+
if (dependentInstance) {
|
|
1300
|
+
console.log(`Cannot remove instance "${adapter}.${instance}", because instance "${dependentInstance}" depends on it!`);
|
|
1301
|
+
return js_controller_common_1.EXIT_CODES.CANNOT_DELETE_DEPENDENCY;
|
|
1302
|
+
}
|
|
1303
|
+
await this._enumerateAdapterInstances(knownObjectIDs, [], adapter, instance);
|
|
1304
|
+
await this._enumerateAdapterDevices(knownObjectIDs, adapter, instance);
|
|
1305
|
+
await this._enumerateAdapterChannels(knownObjectIDs, adapter, instance);
|
|
1306
|
+
await this._enumerateAdapterStateObjects(knownObjectIDs, adapter, instance);
|
|
1307
|
+
await this._enumerateAdapterStates(knownStateIDs, adapter, instance);
|
|
1308
|
+
await this._enumerateAdapterDocs(knownObjectIDs, adapter, instance);
|
|
1309
|
+
await this._deleteAdapterObjects(knownObjectIDs);
|
|
1310
|
+
await this._deleteAdapterStates(knownStateIDs);
|
|
1311
|
+
if (this.params.custom) {
|
|
1312
|
+
// delete instance from custom
|
|
1313
|
+
await this._removeCustomFromObjects([`${adapter}.${instance}`]);
|
|
1314
|
+
}
|
|
1315
|
+
// TODO delete meta objects - I think a recursive deletion of all child object would be less effort.
|
|
1316
|
+
}
|
|
1317
|
+
/**
|
|
1318
|
+
* Removes the custom attribute of the provided adapter/instance
|
|
1319
|
+
*
|
|
1320
|
+
* @param ids - id of the adapter/instance to check for
|
|
1321
|
+
*/
|
|
1322
|
+
async _removeCustomFromObjects(ids) {
|
|
1323
|
+
var _a;
|
|
1324
|
+
// get all objects which have a custom attribute
|
|
1325
|
+
// @ts-expect-error # 1917
|
|
1326
|
+
const res = await this.objects.getObjectViewAsync('system', 'custom', {
|
|
1327
|
+
startkey: '',
|
|
1328
|
+
endkey: '\u9999'
|
|
1329
|
+
});
|
|
1330
|
+
if (res && res.rows) {
|
|
1331
|
+
for (const row of res.rows) {
|
|
1332
|
+
let obj;
|
|
1333
|
+
for (const id of ids) {
|
|
1334
|
+
if (Object.prototype.hasOwnProperty.call(row.value, id)) {
|
|
1335
|
+
if (!obj) {
|
|
1336
|
+
obj = await this.objects.getObjectAsync(row.id);
|
|
1337
|
+
}
|
|
1338
|
+
if ((_a = obj === null || obj === void 0 ? void 0 : obj.common) === null || _a === void 0 ? void 0 : _a.custom) {
|
|
1339
|
+
delete obj.common.custom[id];
|
|
1340
|
+
}
|
|
1341
|
+
}
|
|
1342
|
+
}
|
|
1343
|
+
if (obj) {
|
|
1344
|
+
// if we have removed a custom attribute, set it to db
|
|
1345
|
+
await this.objects.setObjectAsync(row.id, obj);
|
|
1346
|
+
}
|
|
1347
|
+
}
|
|
1348
|
+
}
|
|
1349
|
+
}
|
|
1350
|
+
/**
|
|
1351
|
+
* Installs an adapter from given url
|
|
1352
|
+
*/
|
|
1353
|
+
async installAdapterFromUrl(url, name) {
|
|
1354
|
+
// If the user provided an URL, try to parse it into known ways to represent a Github URL
|
|
1355
|
+
let parsedUrl;
|
|
1356
|
+
try {
|
|
1357
|
+
parsedUrl = new url_1.URL(url);
|
|
1358
|
+
}
|
|
1359
|
+
catch {
|
|
1360
|
+
/* ignore, not a valid URL */
|
|
1361
|
+
}
|
|
1362
|
+
const debug = process.argv.includes('--debug');
|
|
1363
|
+
if (parsedUrl && parsedUrl.hostname === 'github.com') {
|
|
1364
|
+
if (!js_controller_common_1.tools.isGithubPathname(parsedUrl.pathname)) {
|
|
1365
|
+
return console.error(`Cannot install from GitHub. Invalid URL ${url}`);
|
|
1366
|
+
}
|
|
1367
|
+
// This is a URL we can parse
|
|
1368
|
+
// @ts-expect-error check if type check above is enough
|
|
1369
|
+
const { repo, user, commit } = js_controller_common_1.tools.parseGithubPathname(parsedUrl.pathname);
|
|
1370
|
+
if (!commit) {
|
|
1371
|
+
// No commit given, try to get it from the API
|
|
1372
|
+
try {
|
|
1373
|
+
const result = await (0, axios_1.default)(`http://api.github.com/repos/${user}/${repo}/commits`, {
|
|
1374
|
+
headers: {
|
|
1375
|
+
'User-Agent': 'ioBroker Adapter install',
|
|
1376
|
+
// @ts-expect-error should be okay..
|
|
1377
|
+
validateStatus: status => status === 200
|
|
1378
|
+
}
|
|
1379
|
+
});
|
|
1380
|
+
if (result.data && Array.isArray(result.data) && result.data.length >= 1 && result.data[0].sha) {
|
|
1381
|
+
url = `${user}/${repo}#${result.data[0].sha}`;
|
|
1382
|
+
}
|
|
1383
|
+
else {
|
|
1384
|
+
console.log(`Info: Can not get current GitHub commit, only remember that we installed from GitHub.`);
|
|
1385
|
+
url = `${user}/${repo}`;
|
|
1386
|
+
}
|
|
1387
|
+
}
|
|
1388
|
+
catch (err) {
|
|
1389
|
+
console.log(`Info: Can not get current GitHub commit, only remember that we installed from GitHub: ${err.message}`);
|
|
1390
|
+
// Install using the npm Github URL syntax `npm i user/repo_name`:
|
|
1391
|
+
url = `${user}/${repo}`;
|
|
1392
|
+
}
|
|
1393
|
+
}
|
|
1394
|
+
else {
|
|
1395
|
+
// We've extracted all we need from the URL
|
|
1396
|
+
url = `${user}/${repo}#${commit}`;
|
|
1397
|
+
}
|
|
1398
|
+
}
|
|
1399
|
+
console.log(`install ${url}`);
|
|
1400
|
+
// Try to extract name from URL
|
|
1401
|
+
if (!name) {
|
|
1402
|
+
const reNpmPacket = new RegExp('^' + js_controller_common_1.tools.appName + '\\.([-_\\w\\d]+)(@.*)?$', 'i');
|
|
1403
|
+
const match = reNpmPacket.exec(url); // we have iobroker.adaptername@1.2.3
|
|
1404
|
+
if (match) {
|
|
1405
|
+
name = match[1];
|
|
1406
|
+
}
|
|
1407
|
+
else if (url.match(/\.(tgz|gz|zip|tar\.gz)$/)) {
|
|
1408
|
+
const parts = url.split('/');
|
|
1409
|
+
const last = parts.pop();
|
|
1410
|
+
const mm = last.match(/\.([-_\w\d]+)-[.\d]+/);
|
|
1411
|
+
if (mm) {
|
|
1412
|
+
name = mm[1];
|
|
1413
|
+
}
|
|
1414
|
+
}
|
|
1415
|
+
else {
|
|
1416
|
+
const githubUrlParts = js_controller_common_1.tools.parseShortGithubUrl(url);
|
|
1417
|
+
// Try to extract the adapter name from the github url if possible
|
|
1418
|
+
// Otherwise fall back to the complete URL
|
|
1419
|
+
if (githubUrlParts) {
|
|
1420
|
+
name = githubUrlParts.repo;
|
|
1421
|
+
}
|
|
1422
|
+
else {
|
|
1423
|
+
name = url;
|
|
1424
|
+
}
|
|
1425
|
+
// Remove the leading `iobroker.` from the name
|
|
1426
|
+
const reG = new RegExp(js_controller_common_1.tools.appName + '\\.([-_\\w\\d]+)$', 'i');
|
|
1427
|
+
const match = reG.exec(name);
|
|
1428
|
+
if (match) {
|
|
1429
|
+
name = match[1];
|
|
1430
|
+
}
|
|
1431
|
+
}
|
|
1432
|
+
}
|
|
1433
|
+
if (name === 'js-controller') {
|
|
1434
|
+
console.error(`Cannot install "js-controller" from url, use "${js_controller_common_1.tools.appName.toLowerCase()} upgrade self"`);
|
|
1435
|
+
return;
|
|
1436
|
+
}
|
|
1437
|
+
const options = {
|
|
1438
|
+
packetName: name
|
|
1439
|
+
};
|
|
1440
|
+
/** list of stopped instances for windows */
|
|
1441
|
+
let stoppedList = [];
|
|
1442
|
+
if (process.platform === 'win32') {
|
|
1443
|
+
stoppedList = await this._getInstancesOfAdapter(name);
|
|
1444
|
+
await this.enableInstances(stoppedList, false);
|
|
1445
|
+
}
|
|
1446
|
+
const res = await this._npmInstallWithCheck(url, options, debug);
|
|
1447
|
+
// if we have no installDir, the method has called processExit itself
|
|
1448
|
+
if (!res || !res.installDir) {
|
|
1449
|
+
return;
|
|
1450
|
+
}
|
|
1451
|
+
const { installDir } = res;
|
|
1452
|
+
if (name) {
|
|
1453
|
+
await this.upload.uploadAdapter(name, true, true);
|
|
1454
|
+
await this.upload.uploadAdapter(name, false, true);
|
|
1455
|
+
await this.upload.upgradeAdapterObjects(name);
|
|
1456
|
+
}
|
|
1457
|
+
else {
|
|
1458
|
+
// Try to find io-package.json with newest date
|
|
1459
|
+
const dirs = fs_extra_1.default.readdirSync(installDir);
|
|
1460
|
+
let date = null;
|
|
1461
|
+
let dir = null;
|
|
1462
|
+
for (const _dir of dirs) {
|
|
1463
|
+
if (fs_extra_1.default.existsSync(`${installDir}/${_dir}/io-package.json`)) {
|
|
1464
|
+
const stat = fs_extra_1.default.statSync(`${installDir}/${_dir}/io-package.json`);
|
|
1465
|
+
if (!date || stat.mtime.getTime() > date.getTime()) {
|
|
1466
|
+
dir = _dir;
|
|
1467
|
+
date = stat.mtime;
|
|
1468
|
+
}
|
|
1469
|
+
}
|
|
1470
|
+
}
|
|
1471
|
+
// if modify time is not older than one hour
|
|
1472
|
+
if (dir && date && Date.now() - date.getTime() < 3600000) {
|
|
1473
|
+
name = dir.substring(js_controller_common_1.tools.appName.length + 1);
|
|
1474
|
+
await this.upload.uploadAdapter(name, true, true);
|
|
1475
|
+
await this.upload.uploadAdapter(name, false, true);
|
|
1476
|
+
await this.upload.upgradeAdapterObjects(name);
|
|
1477
|
+
}
|
|
1478
|
+
}
|
|
1479
|
+
// re-enable stopped instances
|
|
1480
|
+
await this.enableInstances(stoppedList, true);
|
|
1481
|
+
}
|
|
1482
|
+
/**
|
|
1483
|
+
* Checks if other adapters depend on this adapter
|
|
1484
|
+
*
|
|
1485
|
+
* @param adapter adapter name
|
|
1486
|
+
* @param instance instance, like 1
|
|
1487
|
+
* @return if dependent exists returns adapter name
|
|
1488
|
+
*/
|
|
1489
|
+
async _hasDependentInstances(adapter, instance) {
|
|
1490
|
+
try {
|
|
1491
|
+
// lets get all instances
|
|
1492
|
+
// @ts-expect-error #1917
|
|
1493
|
+
const doc = await this.objects.getObjectViewAsync('system', 'instance', {
|
|
1494
|
+
startkey: 'system.adapter.',
|
|
1495
|
+
endkey: 'system.adapter.\u9999'
|
|
1496
|
+
});
|
|
1497
|
+
if (!doc) {
|
|
1498
|
+
console.error(`Could not check dependent instances for "${adapter}", because instances could not be read`);
|
|
1499
|
+
return;
|
|
1500
|
+
}
|
|
1501
|
+
let scopedHostname;
|
|
1502
|
+
if (instance) {
|
|
1503
|
+
// we need to respect host relative to the instance
|
|
1504
|
+
[scopedHostname] = doc.rows
|
|
1505
|
+
.filter(row => row.id === `system.adapter.${adapter}.${instance}`)
|
|
1506
|
+
.map(row => row.value.common.host);
|
|
1507
|
+
}
|
|
1508
|
+
// fallback is this host
|
|
1509
|
+
scopedHostname = scopedHostname || hostname;
|
|
1510
|
+
for (const row of doc.rows) {
|
|
1511
|
+
if (!row.value.common) {
|
|
1512
|
+
// this object seems to be corrupted so it will not need our adapter
|
|
1513
|
+
continue;
|
|
1514
|
+
}
|
|
1515
|
+
const localDeps = js_controller_common_1.tools.parseDependencies(row.value.common.dependencies);
|
|
1516
|
+
for (const localDep of Object.keys(localDeps)) {
|
|
1517
|
+
if (row.value.common.host === scopedHostname && localDep === adapter) {
|
|
1518
|
+
if (!instance) {
|
|
1519
|
+
// this adapter needs us locally and all instances should be deleted
|
|
1520
|
+
return `${row.value.common.name}.${row.id.split('.').pop()}`;
|
|
1521
|
+
}
|
|
1522
|
+
else {
|
|
1523
|
+
// check if other instance of us exists on this host
|
|
1524
|
+
if (this._checkDependencyFulfilledThisHost(adapter, instance, doc.rows, scopedHostname)) {
|
|
1525
|
+
// there are other instances of our adapter - ok
|
|
1526
|
+
break;
|
|
1527
|
+
}
|
|
1528
|
+
else {
|
|
1529
|
+
return `${row.value.common.name}.${row.id.split('.').pop()}`;
|
|
1530
|
+
}
|
|
1531
|
+
}
|
|
1532
|
+
}
|
|
1533
|
+
}
|
|
1534
|
+
const globalDeps = js_controller_common_1.tools.parseDependencies(row.value.common.globalDependencies);
|
|
1535
|
+
for (const globalDep of Object.keys(globalDeps)) {
|
|
1536
|
+
if (globalDep === adapter) {
|
|
1537
|
+
if (!instance) {
|
|
1538
|
+
// all instances on this host should be removed so check if there are some on other hosts
|
|
1539
|
+
if (this._checkDependencyFulfilledForeignHosts(adapter, doc.rows, scopedHostname)) {
|
|
1540
|
+
break;
|
|
1541
|
+
}
|
|
1542
|
+
else {
|
|
1543
|
+
return row.value.common.name;
|
|
1544
|
+
}
|
|
1545
|
+
}
|
|
1546
|
+
else if (this._checkDependencyFulfilledForeignHosts(adapter, doc.rows, scopedHostname) ||
|
|
1547
|
+
this._checkDependencyFulfilledThisHost(adapter, instance, doc.rows, scopedHostname)) {
|
|
1548
|
+
// another instance of our adapter is on another host or on ours, no need to search further
|
|
1549
|
+
break;
|
|
1550
|
+
}
|
|
1551
|
+
else {
|
|
1552
|
+
return row.value.common.name;
|
|
1553
|
+
}
|
|
1554
|
+
}
|
|
1555
|
+
}
|
|
1556
|
+
}
|
|
1557
|
+
}
|
|
1558
|
+
catch (e) {
|
|
1559
|
+
console.error(`Could not check dependent instances for "${adapter}": ${e.message}`);
|
|
1560
|
+
}
|
|
1561
|
+
}
|
|
1562
|
+
/**
|
|
1563
|
+
* Checks if adapter can also be found on another host than this
|
|
1564
|
+
*
|
|
1565
|
+
* @param adapter adapter name
|
|
1566
|
+
* @param instancesRows all instances objects view rows
|
|
1567
|
+
* @param scopedHostname hostname which should be assumed as local
|
|
1568
|
+
* @return true if an instance is present on other host
|
|
1569
|
+
*/
|
|
1570
|
+
_checkDependencyFulfilledForeignHosts(adapter, instancesRows, scopedHostname) {
|
|
1571
|
+
for (const row of instancesRows) {
|
|
1572
|
+
if (row.value && row.value.common.name === adapter && row.value.common.host !== scopedHostname) {
|
|
1573
|
+
return true;
|
|
1574
|
+
}
|
|
1575
|
+
}
|
|
1576
|
+
return false;
|
|
1577
|
+
}
|
|
1578
|
+
/**
|
|
1579
|
+
* Checks if another instance then the given is present on this host
|
|
1580
|
+
*
|
|
1581
|
+
* @param adapter adapter name
|
|
1582
|
+
* @param instance instance number like 1
|
|
1583
|
+
* @param instancesRows all instances objects view rows
|
|
1584
|
+
* @param scopedHostname hostname which should be assumed as local
|
|
1585
|
+
* @return true if another instance is present on this host
|
|
1586
|
+
*/
|
|
1587
|
+
_checkDependencyFulfilledThisHost(adapter, instance, instancesRows, scopedHostname) {
|
|
1588
|
+
for (const row of instancesRows) {
|
|
1589
|
+
if (row.value &&
|
|
1590
|
+
row.value.common.name === adapter &&
|
|
1591
|
+
row.value.common.host === scopedHostname &&
|
|
1592
|
+
parseInt(row.value._id.split('.').pop()) !== instance) {
|
|
1593
|
+
return true;
|
|
1594
|
+
}
|
|
1595
|
+
}
|
|
1596
|
+
return false;
|
|
1597
|
+
}
|
|
1598
|
+
/**
|
|
1599
|
+
* Get all instances of an adapter which are on the current host
|
|
1600
|
+
*/
|
|
1601
|
+
async _getInstancesOfAdapter(adapter) {
|
|
1602
|
+
const instances = [];
|
|
1603
|
+
// @ts-expect-error fixed with #1917
|
|
1604
|
+
const doc = await this.objects.getObjectListAsync({
|
|
1605
|
+
startkey: `system.adapter.${adapter}.`,
|
|
1606
|
+
endkey: `system.adapter.${adapter}.\u9999`
|
|
1607
|
+
});
|
|
1608
|
+
if (doc) {
|
|
1609
|
+
for (const row of doc.rows) {
|
|
1610
|
+
// stop only started instances on this host
|
|
1611
|
+
if (row.value.common.enabled && hostname === row.value.common.host) {
|
|
1612
|
+
instances.push(row.value);
|
|
1613
|
+
}
|
|
1614
|
+
}
|
|
1615
|
+
}
|
|
1616
|
+
return instances;
|
|
1617
|
+
}
|
|
1618
|
+
}
|
|
1619
|
+
module.exports = { Install };
|
|
1620
|
+
//# sourceMappingURL=setupInstall.js.map
|