@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.
Files changed (76) hide show
  1. package/build/index.d.ts +89 -0
  2. package/{index.js → build/index.js} +7 -1
  3. package/build/lib/cli/cliCert.d.ts +16 -0
  4. package/build/lib/cli/cliCert.d.ts.map +1 -0
  5. package/{lib → build/lib}/cli/cliCert.js +5 -9
  6. package/build/lib/cli/cliCert.js.map +1 -0
  7. package/build/lib/cli/cliCommand.d.ts +24 -0
  8. package/build/lib/cli/cliCommand.d.ts.map +1 -0
  9. package/{lib → build/lib}/cli/cliCommand.js +2 -5
  10. package/build/lib/cli/cliCommand.js.map +1 -0
  11. package/build/lib/cli/cliCompact.d.ts +26 -0
  12. package/build/lib/cli/cliCompact.d.ts.map +1 -0
  13. package/{lib → build/lib}/cli/cliCompact.js +23 -43
  14. package/build/lib/cli/cliCompact.js.map +1 -0
  15. package/build/lib/cli/cliDebug.d.ts +19 -0
  16. package/build/lib/cli/cliDebug.d.ts.map +1 -0
  17. package/{lib → build/lib}/cli/cliDebug.js +6 -13
  18. package/build/lib/cli/cliDebug.js.map +1 -0
  19. package/build/lib/cli/cliHost.d.ts +36 -0
  20. package/build/lib/cli/cliHost.d.ts.map +1 -0
  21. package/{lib → build/lib}/cli/cliHost.js +22 -47
  22. package/build/lib/cli/cliHost.js.map +1 -0
  23. package/build/lib/cli/cliLogs.d.ts +58 -0
  24. package/build/lib/cli/cliLogs.d.ts.map +1 -0
  25. package/{lib → build/lib}/cli/cliLogs.js +16 -19
  26. package/build/lib/cli/cliLogs.js.map +1 -0
  27. package/build/lib/cli/cliMessage.d.ts +10 -0
  28. package/build/lib/cli/cliMessage.d.ts.map +1 -0
  29. package/{lib → build/lib}/cli/cliMessage.js +5 -7
  30. package/build/lib/cli/cliMessage.js.map +1 -0
  31. package/build/lib/cli/cliObjects.d.ts +105 -0
  32. package/build/lib/cli/cliObjects.d.ts.map +1 -0
  33. package/{lib → build/lib}/cli/cliObjects.js +69 -110
  34. package/build/lib/cli/cliObjects.js.map +1 -0
  35. package/build/lib/cli/cliPlugin.d.ts +23 -0
  36. package/build/lib/cli/cliPlugin.d.ts.map +1 -0
  37. package/{lib → build/lib}/cli/cliPlugin.js +24 -50
  38. package/build/lib/cli/cliPlugin.js.map +1 -0
  39. package/build/lib/cli/cliProcess.d.ts +50 -0
  40. package/build/lib/cli/cliProcess.d.ts.map +1 -0
  41. package/{lib → build/lib}/cli/cliProcess.js +50 -62
  42. package/build/lib/cli/cliProcess.js.map +1 -0
  43. package/build/lib/cli/cliStates.d.ts +51 -0
  44. package/build/lib/cli/cliStates.d.ts.map +1 -0
  45. package/{lib → build/lib}/cli/cliStates.js +64 -86
  46. package/build/lib/cli/cliStates.js.map +1 -0
  47. package/build/lib/cli/cliTools.d.ts +64 -0
  48. package/build/lib/cli/cliTools.d.ts.map +1 -0
  49. package/{lib → build/lib}/cli/cliTools.js +1 -12
  50. package/build/lib/cli/cliTools.js.map +1 -0
  51. package/build/lib/cli/messages.d.ts +53 -0
  52. package/build/lib/cli/messages.d.ts.map +1 -0
  53. package/{lib → build/lib}/cli/messages.js +27 -66
  54. package/build/lib/cli/messages.js.map +1 -0
  55. package/build/lib/setup/setupBackup.d.ts +126 -0
  56. package/build/lib/setup/setupBackup.d.ts.map +1 -0
  57. package/build/lib/setup/setupBackup.js +1031 -0
  58. package/build/lib/setup/setupBackup.js.map +1 -0
  59. package/build/lib/setup/setupInstall.d.ts +23 -0
  60. package/build/lib/setup/setupInstall.d.ts.map +1 -0
  61. package/build/lib/setup/setupInstall.js +1620 -0
  62. package/build/lib/setup/setupInstall.js.map +1 -0
  63. package/build/lib/setup/setupList.d.ts +49 -0
  64. package/build/lib/setup/setupList.d.ts.map +1 -0
  65. package/build/lib/setup/setupList.js +722 -0
  66. package/build/lib/setup/setupList.js.map +1 -0
  67. package/build/lib/setup/setupPacketManager.d.ts +65 -0
  68. package/build/lib/setup/setupPacketManager.d.ts.map +1 -0
  69. package/build/lib/setup/setupPacketManager.js +216 -0
  70. package/build/lib/setup/setupPacketManager.js.map +1 -0
  71. package/build/lib/setup/setupUpload.d.ts +72 -0
  72. package/build/lib/setup/setupUpload.d.ts.map +1 -0
  73. package/build/lib/setup/setupUpload.js +784 -0
  74. package/build/lib/setup/setupUpload.js.map +1 -0
  75. package/package.json +18 -11
  76. package/lib/setup/setupList.js +0 -840
@@ -0,0 +1,784 @@
1
+ "use strict";
2
+ /**
3
+ * Upload adapter files into DB
4
+ *
5
+ * Copyright 2013-2022 bluefox <dogafox@gmail.com>
6
+ *
7
+ * MIT License
8
+ *
9
+ */
10
+ var __createBinding = (this && this.__createBinding) || (Object.create ? (function(o, m, k, k2) {
11
+ if (k2 === undefined) k2 = k;
12
+ var desc = Object.getOwnPropertyDescriptor(m, k);
13
+ if (!desc || ("get" in desc ? !m.__esModule : desc.writable || desc.configurable)) {
14
+ desc = { enumerable: true, get: function() { return m[k]; } };
15
+ }
16
+ Object.defineProperty(o, k2, desc);
17
+ }) : (function(o, m, k, k2) {
18
+ if (k2 === undefined) k2 = k;
19
+ o[k2] = m[k];
20
+ }));
21
+ var __setModuleDefault = (this && this.__setModuleDefault) || (Object.create ? (function(o, v) {
22
+ Object.defineProperty(o, "default", { enumerable: true, value: v });
23
+ }) : function(o, v) {
24
+ o["default"] = v;
25
+ });
26
+ var __importStar = (this && this.__importStar) || function (mod) {
27
+ if (mod && mod.__esModule) return mod;
28
+ var result = {};
29
+ if (mod != null) for (var k in mod) if (k !== "default" && Object.prototype.hasOwnProperty.call(mod, k)) __createBinding(result, mod, k);
30
+ __setModuleDefault(result, mod);
31
+ return result;
32
+ };
33
+ var __importDefault = (this && this.__importDefault) || function (mod) {
34
+ return (mod && mod.__esModule) ? mod : { "default": mod };
35
+ };
36
+ Object.defineProperty(exports, "__esModule", { value: true });
37
+ exports.Upload = void 0;
38
+ const fs = __importStar(require("fs-extra"));
39
+ const js_controller_common_1 = require("@iobroker/js-controller-common");
40
+ const deep_clone_1 = __importDefault(require("deep-clone"));
41
+ const util_1 = require("util");
42
+ const axios_1 = __importDefault(require("axios"));
43
+ const mime_1 = __importDefault(require("mime"));
44
+ const path_1 = require("path");
45
+ const hostname = js_controller_common_1.tools.getHostName();
46
+ class Upload {
47
+ constructor(_options) {
48
+ const options = _options || {};
49
+ if (!options.states) {
50
+ throw new Error('Invalid arguments: states is missing');
51
+ }
52
+ if (!options.objects) {
53
+ throw new Error('Invalid arguments: objects is missing');
54
+ }
55
+ this.states = options.states;
56
+ this.objects = options.objects;
57
+ this.callbackId = 1;
58
+ this.regApp = new RegExp('/' + js_controller_common_1.tools.appName.replace(/\./g, '\\.') + '\\.', 'i');
59
+ this.sendToHostFromCliAsync = js_controller_common_1.tools.promisifyNoError(this.sendToHostFromCli);
60
+ this.lastProgressUpdate = Date.now();
61
+ }
62
+ async checkHostsIfAlive(hosts) {
63
+ const result = [];
64
+ if (hosts) {
65
+ for (const host of hosts) {
66
+ const state = await this.states.getStateAsync(`${host}.alive`);
67
+ if (state && state.val) {
68
+ result.push(host);
69
+ }
70
+ }
71
+ }
72
+ return result;
73
+ }
74
+ async getHosts(onlyAlive) {
75
+ const hosts = [];
76
+ try {
77
+ // @ts-expect-error should be fixed with #1917
78
+ const arr = await this.objects.getObjectListAsync({
79
+ startkey: 'system.host.',
80
+ endkey: 'system.host.\u9999'
81
+ });
82
+ if (arr && arr.rows) {
83
+ for (const row of arr.rows) {
84
+ if (row.value.type !== 'host') {
85
+ continue;
86
+ }
87
+ hosts.push(row.value._id);
88
+ }
89
+ }
90
+ }
91
+ catch (e) {
92
+ // ignore
93
+ console.warn(`Cannot read hosts: ${e.message}`);
94
+ }
95
+ if (onlyAlive) {
96
+ return this.checkHostsIfAlive(hosts);
97
+ }
98
+ else {
99
+ return hosts;
100
+ }
101
+ }
102
+ // Check if some adapters must be restarted and restart them
103
+ async checkRestartOther(adapter) {
104
+ const adapterDir = js_controller_common_1.tools.getAdapterDir(adapter);
105
+ if (!adapterDir) {
106
+ console.error(`Adapter directory of adapter "${adapter}" not found`);
107
+ return;
108
+ }
109
+ try {
110
+ const adapterConf = await fs.readJSON((0, path_1.join)(adapterDir, 'io-package.json'));
111
+ if (adapterConf.common.restartAdapters) {
112
+ if (!Array.isArray(adapterConf.common.restartAdapters)) {
113
+ // its not an array, now it can only be a single adapter as string
114
+ if (typeof adapterConf.common.restartAdapters !== 'string') {
115
+ return;
116
+ }
117
+ adapterConf.common.restartAdapters = [adapterConf.common.restartAdapters];
118
+ }
119
+ if (adapterConf.common.restartAdapters.length && adapterConf.common.restartAdapters[0]) {
120
+ const instances = await js_controller_common_1.tools.getAllInstances(adapterConf.common.restartAdapters, this.objects);
121
+ if (instances && instances.length) {
122
+ for (const instance of instances) {
123
+ try {
124
+ const obj = await this.objects.getObjectAsync(instance);
125
+ // if instance is enabled
126
+ if (obj && obj.common && obj.common.enabled) {
127
+ obj.common.enabled = false; // disable instance
128
+ obj.from = `system.host.${js_controller_common_1.tools.getHostName()}.cli`;
129
+ obj.ts = Date.now();
130
+ await this.objects.setObjectAsync(obj._id, obj);
131
+ obj.common.enabled = true; // enable instance
132
+ obj.ts = Date.now();
133
+ await this.objects.setObjectAsync(obj._id, obj);
134
+ console.log(`Adapter "${obj._id}" restarted.`);
135
+ }
136
+ }
137
+ catch (e) {
138
+ console.error(`Cannot restart adapter "${instance}": ${e.message}`);
139
+ }
140
+ }
141
+ }
142
+ }
143
+ }
144
+ }
145
+ catch (e) {
146
+ console.error(`Cannot parse ${adapterDir}/io-package.json: ${e.message}`);
147
+ }
148
+ }
149
+ sendToHostFromCli(host, command, message, callback) {
150
+ const time = Date.now();
151
+ const from = `system.host.${hostname}_cli_${time}`;
152
+ const timeout = setTimeout(() => {
153
+ callback && callback();
154
+ callback = null;
155
+ this.states.unsubscribeMessage(from);
156
+ // @ts-expect-error todo: I don't think this works
157
+ this.states.onChange = null;
158
+ }, 60000);
159
+ // @ts-expect-error todo: I don't think this works
160
+ this.states.onChange = (id, msg) => {
161
+ if (id.endsWith(from)) {
162
+ if (msg.command === 'log' || msg.command === 'error' || msg.command === 'warn') {
163
+ // @ts-expect-error
164
+ console[msg.command](`${host} -> ${msg.text}`);
165
+ }
166
+ else if (callback) {
167
+ callback(msg && msg.message);
168
+ callback = null;
169
+ clearTimeout(timeout);
170
+ this.states.unsubscribeMessage(from);
171
+ // @ts-expect-error
172
+ this.states.onChange = null;
173
+ }
174
+ }
175
+ };
176
+ this.states.subscribeMessage(from, () => {
177
+ const obj = {
178
+ command,
179
+ message: message,
180
+ from: `system.host.${hostname}_cli_${time}`,
181
+ callback: {
182
+ message,
183
+ id: this.callbackId++,
184
+ ack: false,
185
+ time
186
+ }
187
+ };
188
+ if (this.callbackId > 0xffffffff) {
189
+ this.callbackId = 1;
190
+ }
191
+ this.callbacks = this.callbacks || {};
192
+ this.callbacks[`_${obj.callback.id}`] = { cb: callback };
193
+ // we cannot receive answers from hosts in CLI, so this command is "fire and forget"
194
+ // @ts-expect-error fixed with #1917
195
+ this.states.pushMessage(host, obj);
196
+ });
197
+ }
198
+ async uploadAdapterFullAsync(adapters) {
199
+ if (adapters && adapters.length) {
200
+ const liveHosts = await this.getHosts(true);
201
+ for (const adapter of adapters) {
202
+ // Find the host which has this adapter
203
+ const instances = await js_controller_common_1.tools.getInstances(adapter, this.objects, true);
204
+ // try to find instance on this host
205
+ let instance = instances.find(obj => obj && obj.common && obj.common.host === hostname);
206
+ // try to find enabled instance on live host
207
+ instance =
208
+ instance ||
209
+ instances.find(obj => obj && obj.common && obj.common.enabled && liveHosts.includes(obj.common.host));
210
+ // try to find any instance
211
+ instance = instance || instances.find(obj => obj && obj.common && liveHosts.includes(obj.common.host));
212
+ if (instance && instance.common.host !== hostname) {
213
+ console.log(`Send upload command to host "${instance.common.host}"... `);
214
+ // send upload message to the host
215
+ const response = await this.sendToHostFromCliAsync(instance.common.host, 'upload', adapter);
216
+ if (response) {
217
+ console.log('Upload result: ' + response.result);
218
+ }
219
+ else {
220
+ console.error('No answer from ' + instance.common.host);
221
+ }
222
+ }
223
+ else {
224
+ if (!instance) {
225
+ // no one alive instance found
226
+ const adapterDir = js_controller_common_1.tools.getAdapterDir(adapter);
227
+ if (!adapterDir || !fs.existsSync(adapterDir)) {
228
+ console.warn(`No alive host found which has the adapter ${adapter} installed! No upload possible. Skipped.`);
229
+ continue;
230
+ }
231
+ }
232
+ // try to upload on this host. It will print an error if the adapter directory not found
233
+ await this.uploadAdapter(adapter, true, true);
234
+ await this.upgradeAdapterObjects(adapter);
235
+ await this.uploadAdapter(adapter, false, true);
236
+ }
237
+ }
238
+ }
239
+ }
240
+ /**
241
+ * Uploads a file
242
+ */
243
+ async uploadFile(source, target) {
244
+ target = target.replace(/\\/g, '/');
245
+ source = source.replace(/\\/g, '/');
246
+ if (target[0] === '/') {
247
+ target = target.substring(1);
248
+ }
249
+ if (target[target.length - 1] === '/') {
250
+ let name = source.split('/').pop();
251
+ name = name.split('?')[0];
252
+ if (!name.includes('.')) {
253
+ name = 'index.html';
254
+ }
255
+ target += name;
256
+ }
257
+ const parts = target.split('/');
258
+ const adapter = parts[0];
259
+ parts.splice(0, 1);
260
+ target = parts.join('/');
261
+ if (source.match(/^http:\/\/|^https:\/\//)) {
262
+ try {
263
+ const result = await (0, axios_1.default)(source, {
264
+ responseType: 'arraybuffer',
265
+ validateStatus: status => status === 200
266
+ });
267
+ if (result && result.data) {
268
+ await this.objects.writeFileAsync(adapter, target, result.data);
269
+ }
270
+ else {
271
+ console.error(`Empty response from URL "${source}"`);
272
+ throw new Error(`Empty response from URL "${source}"`);
273
+ }
274
+ }
275
+ catch (err) {
276
+ let result;
277
+ if (err.response) {
278
+ // The request was made and the server responded with a status code
279
+ // that falls out of the range of 2xx
280
+ result = err.response.data || err.response.status;
281
+ }
282
+ else if (err.request) {
283
+ // The request was made but no response was received
284
+ // `err.request` is an instance of XMLHttpRequest in the browser and an instance of
285
+ // http.ClientRequest in node.js
286
+ result = err.request;
287
+ }
288
+ else {
289
+ // Something happened in setting up the request that triggered an Error
290
+ result = err.message;
291
+ }
292
+ console.error(`Cannot get URL "${source}": ${result}`);
293
+ throw new Error(result);
294
+ }
295
+ }
296
+ else {
297
+ try {
298
+ await this.objects.writeFileAsync(adapter, target, fs.readFileSync(source));
299
+ }
300
+ catch (err) {
301
+ console.error(`Cannot read file "${source}": ${err.message}`);
302
+ throw err;
303
+ }
304
+ }
305
+ return `${adapter}/${target}`;
306
+ }
307
+ async eraseFiles(files, logger) {
308
+ if (files && files.length) {
309
+ for (const file of files) {
310
+ try {
311
+ // @ts-expect-error should be fixed with #1917
312
+ await this.objects.unlinkAsync(file.adapter, file.path);
313
+ }
314
+ catch (err) {
315
+ logger.error(`Cannot delete file "${file.path}": ${err}`);
316
+ }
317
+ }
318
+ }
319
+ }
320
+ /**
321
+ * Collect Files of an adapter specific directory from the iobroker storage
322
+ *
323
+ * @param adapter Adaptername
324
+ * @param path path in the adapterspecific storage space
325
+ * @param logger Logger instance
326
+ */
327
+ async collectExistingFilesToDelete(adapter, path, logger) {
328
+ let _files = [];
329
+ let _dirs = [];
330
+ let files;
331
+ try {
332
+ // @ts-expect-error should be fixed with #1917
333
+ files = await this.objects.readDirAsync(adapter, path);
334
+ }
335
+ catch {
336
+ // ignore err
337
+ files = [];
338
+ }
339
+ if (files && files.length) {
340
+ for (const file of files) {
341
+ if (file.file === '.' || file.file === '..') {
342
+ continue;
343
+ }
344
+ const newPath = path + file.file;
345
+ if (file.isDir) {
346
+ if (!_dirs.find(e => e.path === newPath)) {
347
+ _dirs.push({ adapter, path: newPath });
348
+ }
349
+ try {
350
+ const result = await this.collectExistingFilesToDelete(adapter, `${newPath}/`, logger);
351
+ if (result.filesToDelete) {
352
+ _files = _files.concat(result.filesToDelete);
353
+ }
354
+ _dirs = _dirs.concat(result.dirs);
355
+ }
356
+ catch (err) {
357
+ logger.warn(`Cannot delete folder "${adapter}${newPath}/": ${err.message}`);
358
+ }
359
+ }
360
+ else if (!_files.find(e => e.path === newPath)) {
361
+ _files.push({ adapter, path: newPath });
362
+ }
363
+ }
364
+ }
365
+ return { filesToDelete: _files, dirs: _dirs };
366
+ }
367
+ async upload(adapter, isAdmin, files, id, rev, logger) {
368
+ const uploadID = `system.adapter.${adapter}.upload`;
369
+ await this.states.setStateAsync(uploadID, { val: 0, ack: true });
370
+ for (let f = 0; f < files.length; f++) {
371
+ const file = files[f];
372
+ // do not upload '.gitignore' files. Todo: add other exceptions
373
+ if (file === '.gitignore') {
374
+ continue;
375
+ }
376
+ const mimeType = mime_1.default.getType(file);
377
+ let attNameArr = file.split(this.regApp);
378
+ // try to find anyway if adapter is not lower case
379
+ if (attNameArr.length === 1 && file.toLowerCase().includes(js_controller_common_1.tools.appName.toLowerCase())) {
380
+ attNameArr = ['', file.substring(js_controller_common_1.tools.appName.length + 2)];
381
+ }
382
+ let attName = attNameArr.pop();
383
+ attName = attName.split('/').slice(2).join('/');
384
+ if (files.length - f > 100) {
385
+ (!f || !((files.length - f - 1) % 50)) &&
386
+ logger.log(`upload [${files.length - f - 1}] ${id} ${file} ${attName} ${mimeType}`);
387
+ }
388
+ else if (files.length - f - 1 > 20) {
389
+ (!f || !((files.length - f - 1) % 10)) &&
390
+ logger.log(`upload [${files.length - f - 1}] ${id} ${file} ${attName} ${mimeType}`);
391
+ }
392
+ else {
393
+ logger.log(`upload [${files.length - f - 1}] ${id} ${file} ${attName} ${mimeType}`);
394
+ }
395
+ // Update upload indicator
396
+ if (!isAdmin) {
397
+ const now = Date.now();
398
+ if (now - this.lastProgressUpdate > 1000) {
399
+ this.lastProgressUpdate = now;
400
+ await this.states.setStateAsync(uploadID, {
401
+ val: Math.round((1000 * (files.length - f)) / files.length) / 10,
402
+ ack: true
403
+ });
404
+ }
405
+ }
406
+ try {
407
+ await new Promise((resolve, reject) => {
408
+ const stream = fs.createReadStream(file);
409
+ stream.on('error', e => reject(e));
410
+ stream.pipe(this.objects.insert(id, attName, null, mimeType || {}, { rev }, (err, res) => {
411
+ err && console.log(err);
412
+ if (res) {
413
+ // @ts-expect-error it always returns null, check it also rev seems to do nothing
414
+ rev = res.rev;
415
+ }
416
+ resolve();
417
+ }));
418
+ });
419
+ }
420
+ catch (e) {
421
+ console.error(`Error: Cannot upload ${file}: ${e.message}`);
422
+ }
423
+ }
424
+ // Set upload progress to 0;
425
+ if (!isAdmin && files.length) {
426
+ await this.states.setStateAsync(uploadID, { val: 0, ack: true });
427
+ }
428
+ return adapter;
429
+ }
430
+ // Read synchronous all files recursively from local directory
431
+ walk(dir, _results) {
432
+ const results = _results || [];
433
+ try {
434
+ if (fs.existsSync(dir)) {
435
+ const list = fs.readdirSync(dir);
436
+ list.map(file => {
437
+ const stat = fs.statSync(`${dir}/${file}`);
438
+ if (stat.isDirectory()) {
439
+ this.walk(`${dir}/${file}`, results);
440
+ }
441
+ else {
442
+ if (!file.endsWith('.npmignore') && !file.endsWith('.gitignore')) {
443
+ results.push(`${dir}/${file}`);
444
+ }
445
+ }
446
+ });
447
+ }
448
+ }
449
+ catch (err) {
450
+ console.error(err);
451
+ }
452
+ return results;
453
+ }
454
+ /**
455
+ * Upload given adapter
456
+ */
457
+ async uploadAdapter(adapter, isAdmin, forceUpload, subTree, _logger) {
458
+ const id = adapter + (isAdmin ? '.admin' : '');
459
+ const adapterDir = js_controller_common_1.tools.getAdapterDir(adapter);
460
+ let dir = adapterDir ? adapterDir + (isAdmin ? '/admin' : '/www') : '';
461
+ const logger = _logger || console;
462
+ if (subTree && dir) {
463
+ dir += `/${subTree}`;
464
+ }
465
+ if (adapterDir === null || !fs.existsSync(adapterDir)) {
466
+ console.log(`INFO: Directory "${adapterDir || `for ${adapter}${isAdmin ? '.admin' : ''}`}" was not found! Nothing was uploaded or deleted.`);
467
+ return adapter;
468
+ }
469
+ let cfg;
470
+ try {
471
+ cfg = await fs.readJSON(`${adapterDir}/io-package.json`);
472
+ }
473
+ catch (err) {
474
+ // file not parsable or does not exist
475
+ console.error(`Could not read io-package.json: ${err.message}`);
476
+ }
477
+ if (!fs.existsSync(dir)) {
478
+ // www folder have not all adapters. So show warning only for admin folder
479
+ (isAdmin || (cfg && cfg.common && cfg.common.onlyWWW)) &&
480
+ console.log(`INFO: Directory "${dir || `for ${adapter}${isAdmin ? '.admin' : ''}`}" was not found! Nothing was uploaded or deleted.`);
481
+ if (isAdmin) {
482
+ return adapter;
483
+ }
484
+ else {
485
+ await this.checkRestartOther(adapter);
486
+ return adapter;
487
+ }
488
+ }
489
+ // check for common.wwwDontUpload (required for legacy adapters and admin)
490
+ if (!isAdmin && cfg && cfg.common && cfg.common.wwwDontUpload) {
491
+ return adapter;
492
+ }
493
+ // Create "upload progress" object if not exists
494
+ if (!isAdmin) {
495
+ let obj;
496
+ const uploadID = `system.adapter.${adapter}.upload`;
497
+ try {
498
+ obj = await this.objects.getObjectAsync(uploadID);
499
+ }
500
+ catch {
501
+ // ignore
502
+ }
503
+ if (!obj) {
504
+ await this.objects.setObjectAsync(uploadID, {
505
+ _id: uploadID,
506
+ type: 'state',
507
+ common: {
508
+ name: `${adapter}.upload`,
509
+ type: 'number',
510
+ role: 'indicator.state',
511
+ unit: '%',
512
+ min: 0,
513
+ max: 100,
514
+ def: 0,
515
+ desc: 'Upload process indicator',
516
+ read: true,
517
+ write: false
518
+ },
519
+ from: `system.host.${js_controller_common_1.tools.getHostName()}.cli`,
520
+ ts: Date.now(),
521
+ native: {}
522
+ });
523
+ }
524
+ // Set indicator to 0
525
+ // @ts-expect-error fixed with #1917
526
+ await this.states.setStateAsync(uploadID, 0, true);
527
+ }
528
+ let result;
529
+ try {
530
+ result = await this.objects.getObjectAsync(id);
531
+ }
532
+ catch {
533
+ // ignore
534
+ }
535
+ // Read all names with subtrees from local directory
536
+ const files = this.walk(dir);
537
+ if (!result) {
538
+ // @ts-expect-error types needed admin is not allowed for meta but it should be allowed
539
+ await this.objects.setObjectAsync(id, {
540
+ type: 'meta',
541
+ common: {
542
+ name: id.split('.').pop(),
543
+ type: isAdmin ? 'admin' : 'www'
544
+ },
545
+ from: `system.host.${js_controller_common_1.tools.getHostName()}.cli`,
546
+ ts: Date.now(),
547
+ native: {}
548
+ });
549
+ forceUpload = true;
550
+ }
551
+ if (forceUpload) {
552
+ if (cfg && cfg.common && cfg.common.eraseOnUpload) {
553
+ const { filesToDelete } = await this.collectExistingFilesToDelete(isAdmin ? adapter + '.admin' : adapter, '/', logger);
554
+ // delete old files, before upload of new
555
+ await this.eraseFiles(filesToDelete, logger);
556
+ }
557
+ if (!isAdmin) {
558
+ await this.checkRestartOther(adapter);
559
+ await new Promise(resolve => setTimeout(() => resolve(), 25));
560
+ // @ts-expect-error TODO rev is not required and should not exist on an object?
561
+ await this.upload(adapter, isAdmin, files, id, result && result.rev, logger);
562
+ }
563
+ else {
564
+ // @ts-expect-error TODO rev is not required and should not exist on an object?
565
+ await this.upload(adapter, isAdmin, files, id, result && result.rev, logger);
566
+ }
567
+ }
568
+ return adapter;
569
+ }
570
+ extendNative(target, additional) {
571
+ if (js_controller_common_1.tools.isObject(additional)) {
572
+ for (const attr of Object.keys(additional)) {
573
+ if (target[attr] === undefined) {
574
+ target[attr] = additional[attr];
575
+ }
576
+ else if (typeof additional[attr] === 'object' && !(additional[attr] instanceof Array)) {
577
+ try {
578
+ target[attr] = target[attr] || {};
579
+ }
580
+ catch {
581
+ console.warn(`Cannot update attribute ${attr} of native`);
582
+ }
583
+ if (typeof target[attr] === 'object' && target[attr] !== null) {
584
+ this.extendNative(target[attr], additional[attr]);
585
+ }
586
+ }
587
+ }
588
+ }
589
+ return target;
590
+ }
591
+ extendCommon(target, additional, instance) {
592
+ if (js_controller_common_1.tools.isObject(additional)) {
593
+ const preserveAttributes = [
594
+ 'title',
595
+ 'schedule',
596
+ 'restartSchedule',
597
+ 'mode',
598
+ 'loglevel',
599
+ 'enabled',
600
+ 'custom'
601
+ ];
602
+ for (const attr of Object.keys(additional)) {
603
+ // preserve these attributes, except, they werde undefined before and preserve titleLang if current titleLang is of type string (changed by user)
604
+ if (preserveAttributes.includes(attr) || (attr === 'titleLang' && typeof target[attr] === 'string')) {
605
+ if (target[attr] === undefined) {
606
+ target[attr] = additional[attr];
607
+ }
608
+ }
609
+ else if (typeof additional[attr] !== 'object' || additional[attr] instanceof Array) {
610
+ try {
611
+ target[attr] = additional[attr];
612
+ // dataFolder can have wildcards
613
+ if (attr === 'dataFolder' && target.dataFolder && target.dataFolder.includes('%INSTANCE%')) {
614
+ target.dataFolder = target.dataFolder.replace(/%INSTANCE%/g, instance);
615
+ }
616
+ }
617
+ catch {
618
+ console.warn(`Cannot update attribute ${attr} of common`);
619
+ }
620
+ }
621
+ else {
622
+ target[attr] = target[attr] || {};
623
+ if (typeof target[attr] !== 'object') {
624
+ target[attr] = {}; // here we clean the simple value with object
625
+ }
626
+ this.extendCommon(target[attr], additional[attr], instance);
627
+ }
628
+ }
629
+ }
630
+ return target;
631
+ }
632
+ async _upgradeAdapterObjectsHelper(name, ioPack, hostname, logger) {
633
+ // Update all instances of this host
634
+ // @ts-expect-error fixed with #1917
635
+ const res = await this.objects.getObjectViewAsync('system', 'instance', {
636
+ startkey: `system.adapter.${name}.`,
637
+ endkey: `system.adapter.${name}.\u9999`
638
+ });
639
+ if (res) {
640
+ for (const row of res.rows) {
641
+ if (row.value.common.host === hostname) {
642
+ const _obj = await this.objects.getObjectAsync(row.id);
643
+ const newObject = (0, deep_clone_1.default)(_obj);
644
+ // all common settings should be taken from new one
645
+ newObject.common = this.extendCommon(newObject.common, ioPack.common, newObject._id.split('.').pop());
646
+ newObject.native = this.extendNative(newObject.native, ioPack.native);
647
+ // protected/encryptedNative and notifications also need to be updated
648
+ // @ts-expect-error TODO needs to be added to types
649
+ newObject.protectedNative = ioPack.protectedNative || [];
650
+ // @ts-expect-error TODO needs to be added to types
651
+ newObject.encryptedNative = ioPack.encryptedNative || [];
652
+ // @ts-expect-error TODO needs to be added to types
653
+ newObject.notifications = ioPack.notifications || [];
654
+ // update instanceObjects and objects
655
+ // @ts-expect-error TODO needs to be added to types
656
+ newObject.instanceObjects = ioPack.instanceObjects || [];
657
+ // @ts-expect-error TODO needs to be added to types
658
+ newObject.objects = ioPack.objects || [];
659
+ // @ts-expect-error TODO needs to be added to types
660
+ newObject.common.version = ioPack.common.version;
661
+ // @ts-expect-error TODO needs to be added to types
662
+ newObject.common.installedVersion = ioPack.common.version;
663
+ // @ts-expect-error TODO needs to be added to types
664
+ newObject.common.installedFrom = ioPack.common.installedFrom;
665
+ // @ts-expect-error TODO needs to be added to types
666
+ if (!ioPack.common.compact && newObject.common.compact) {
667
+ // @ts-expect-error TODO needs to be added to types
668
+ newObject.common.compact = ioPack.common.compact;
669
+ }
670
+ // Compare objects to reduce restarts of instances
671
+ if (!(0, util_1.isDeepStrictEqual)(newObject, _obj)) {
672
+ logger.log(`Update "${newObject._id}"`);
673
+ newObject.from = `system.host.${js_controller_common_1.tools.getHostName()}.cli`;
674
+ newObject.ts = Date.now();
675
+ await this.objects.setObjectAsync(newObject._id, newObject);
676
+ // @ts-expect-error TODO: instance object is no state remove this part?
677
+ if (newObject.common.def !== undefined && newObject.common.def !== null) {
678
+ // set default state value
679
+ const state = await this.states.getStateAsync(newObject._id);
680
+ if (!state) {
681
+ await this.states.setStateAsync(newObject._id, {
682
+ // @ts-expect-error see above
683
+ val: newObject.common.def,
684
+ ack: true,
685
+ q: 0x40 // substitute value from device or adapter
686
+ });
687
+ }
688
+ }
689
+ }
690
+ }
691
+ }
692
+ }
693
+ // updates only "_design/system" and co "_design/*"
694
+ if (ioPack.objects && typeof ioPack.objects === 'object') {
695
+ for (const _id of Object.keys(ioPack.objects)) {
696
+ if (name === 'js-controller' && !_id.startsWith('_design/')) {
697
+ continue;
698
+ }
699
+ ioPack.objects[_id].from = `system.host.${hostname}.cli`;
700
+ ioPack.objects[_id].ts = Date.now();
701
+ try {
702
+ await this.objects.setObjectAsync(ioPack.objects[_id]._id, ioPack.objects[_id]);
703
+ }
704
+ catch (err) {
705
+ logger.error(`Cannot update object: ${err}`);
706
+ }
707
+ }
708
+ }
709
+ return name;
710
+ }
711
+ /**
712
+ * Create object from io-package json
713
+ */
714
+ async upgradeAdapterObjects(name, ioPack, _logger) {
715
+ const logger = _logger || console;
716
+ const adapterDir = js_controller_common_1.tools.getAdapterDir(name);
717
+ let ioPackFile;
718
+ try {
719
+ ioPackFile = fs.readJSONSync(adapterDir + '/io-package.json');
720
+ }
721
+ catch {
722
+ if (adapterDir) {
723
+ logger.error(`Cannot find io-package.json in ${adapterDir}`);
724
+ }
725
+ else {
726
+ logger.error(`Cannot find io-package.json for "${name}"`);
727
+ }
728
+ ioPackFile = null;
729
+ }
730
+ ioPack = ioPack || ioPackFile;
731
+ if (ioPack) {
732
+ // Always update installed From from File on disk if exists and set
733
+ if (ioPackFile && ioPackFile.common && ioPackFile.common.installedFrom) {
734
+ ioPack.common = ioPack.common || {};
735
+ ioPack.common.installedFrom = ioPackFile.common.installedFrom;
736
+ }
737
+ // Not existing? Why ever ... we recreate
738
+ let _obj;
739
+ try {
740
+ _obj = await this.objects.getObject(`system.adapter.${name}`);
741
+ }
742
+ catch {
743
+ // ignore err
744
+ }
745
+ const obj = _obj || {
746
+ common: ioPack.common,
747
+ native: ioPack.native,
748
+ type: 'adapter'
749
+ };
750
+ obj.common = ioPack.common || {};
751
+ obj.native = ioPack.native || {};
752
+ // protected/encryptedNative and notifications also need to be updated
753
+ obj.protectedNative = ioPack.protectedNative || [];
754
+ obj.encryptedNative = ioPack.encryptedNative || [];
755
+ // @ts-expect-error TODO needs to be added to types
756
+ obj.notifications = ioPack.notifications || [];
757
+ // update instanceObjects and objects
758
+ // @ts-expect-error TODO needs to be added to types
759
+ obj.instanceObjects = ioPack.instanceObjects || [];
760
+ // @ts-expect-error TODO needs to be added to types
761
+ obj.objects = ioPack.objects || [];
762
+ obj.type = 'adapter';
763
+ obj.common.installedVersion = ioPack.common.version;
764
+ // @ts-expect-error TODO needs to be added to types
765
+ if (obj.common.news) {
766
+ // @ts-expect-error TODO needs to be added to types
767
+ delete obj.common.news; // remove this information as it could be big, but it will be taken from repo
768
+ }
769
+ const hostname = js_controller_common_1.tools.getHostName();
770
+ obj.from = `system.host.${hostname}.cli`;
771
+ obj.ts = Date.now();
772
+ try {
773
+ await this.objects.setObjectAsync(`system.adapter.${name}`, obj);
774
+ }
775
+ catch (err) {
776
+ logger.error(`Cannot set system.adapter.${name}: ${err.message}`);
777
+ }
778
+ await this._upgradeAdapterObjectsHelper(name, ioPack, hostname, logger);
779
+ }
780
+ return name;
781
+ }
782
+ }
783
+ exports.Upload = Upload;
784
+ //# sourceMappingURL=setupUpload.js.map