@iobroker/js-controller-cli 4.0.23 → 4.1.0-alpha.0-20220823-bd606e5c

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 (77) hide show
  1. package/build/index.d.ts +87 -0
  2. package/{index.js → build/index.js} +8 -2
  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 +6 -10
  6. package/build/lib/cli/cliCert.js.map +1 -0
  7. package/build/lib/cli/cliCommand.d.ts +41 -0
  8. package/build/lib/cli/cliCommand.d.ts.map +1 -0
  9. package/build/lib/cli/cliCommand.js +27 -0
  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 +24 -44
  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 +7 -14
  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 +23 -48
  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 +17 -21
  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 +6 -9
  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 +70 -111
  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 +25 -51
  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 +51 -63
  42. package/build/lib/cli/cliProcess.js.map +1 -0
  43. package/build/lib/cli/cliStates.d.ts +49 -0
  44. package/build/lib/cli/cliStates.d.ts.map +1 -0
  45. package/{lib → build/lib}/cli/cliStates.js +127 -122
  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 +37 -49
  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/cli/cliCommand.js +0 -58
  77. package/lib/setup/setupList.js +0 -840
@@ -0,0 +1,1031 @@
1
+ "use strict";
2
+ /**
3
+ * Backup
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
+ exports.BackupRestore = void 0;
15
+ const fs_extra_1 = __importDefault(require("fs-extra"));
16
+ const js_controller_common_1 = require("@iobroker/js-controller-common");
17
+ const path_1 = __importDefault(require("path"));
18
+ const setupUpload_1 = require("./setupUpload");
19
+ const js_controller_common_2 = require("@iobroker/js-controller-common");
20
+ const promisify_child_process_1 = __importDefault(require("promisify-child-process"));
21
+ const tar_1 = __importDefault(require("tar"));
22
+ const hostname = js_controller_common_1.tools.getHostName();
23
+ const controllerDir = js_controller_common_1.tools.getControllerDir();
24
+ const tmpDir = path_1.default.normalize(path_1.default.join(controllerDir, 'tmp'));
25
+ const bkpDir = path_1.default.normalize(path_1.default.join(controllerDir, 'backups'));
26
+ class BackupRestore {
27
+ constructor(options) {
28
+ options = options || {};
29
+ if (!options.states) {
30
+ throw new Error('Invalid arguments: states is missing');
31
+ }
32
+ if (!options.objects) {
33
+ throw new Error('Invalid arguments: objects is missing');
34
+ }
35
+ if (!options.processExit) {
36
+ throw new Error('Invalid arguments: processExit is missing');
37
+ }
38
+ if (!options.cleanDatabase) {
39
+ throw new Error('Invalid arguments: cleanDatabase is missing');
40
+ }
41
+ if (!options.restartController) {
42
+ throw new Error('Invalid arguments: restartController is missing');
43
+ }
44
+ this.objects = options.objects;
45
+ this.states = options.states;
46
+ this.processExit = options.processExit;
47
+ this.cleanDatabase = options.cleanDatabase;
48
+ this.restartController = options.restartController;
49
+ this.dbMigration = options.dbMigration || false;
50
+ this.PRESERVE_ADAPTERS = ['admin', 'backitup'];
51
+ this.upload = new setupUpload_1.Upload(options);
52
+ this.configParts = js_controller_common_1.tools.getConfigFileName().split('/');
53
+ this.configParts.pop(); // remove *.json
54
+ this.configDir = this.configParts.join('/'); // => name-data
55
+ }
56
+ // --------------------------------------- BACKUP ---------------------------------------------------
57
+ async _copyFile(id, srcPath, destPath) {
58
+ try {
59
+ // @ts-expect-error #1917
60
+ const data = await this.objects.readFileAsync(id, srcPath);
61
+ if (data) {
62
+ if (data.data !== undefined) {
63
+ fs_extra_1.default.writeFileSync(destPath, data.data);
64
+ }
65
+ else {
66
+ // @ts-expect-error #1917 revisit
67
+ fs_extra_1.default.writeFileSync(destPath, data);
68
+ }
69
+ }
70
+ }
71
+ catch (err) {
72
+ console.log(`Can not copy File ${id}${srcPath} to ${destPath}: ${err.message}`);
73
+ }
74
+ }
75
+ async copyDir(id, srcPath, destPath) {
76
+ !fs_extra_1.default.existsSync(destPath) && fs_extra_1.default.mkdirSync(destPath);
77
+ try {
78
+ // @ts-expect-error #1917
79
+ const res = await this.objects.readDirAsync(id, srcPath);
80
+ if (res) {
81
+ for (const entry of res) {
82
+ if (entry.isDir) {
83
+ await this.copyDir(id, `${srcPath}/${entry.file}`, `${destPath}/${entry.file}`);
84
+ }
85
+ else {
86
+ fs_extra_1.default.ensureDirSync(destPath);
87
+ await this._copyFile(id, `${srcPath}/${entry.file}`, `${destPath}/${entry.file}`);
88
+ }
89
+ }
90
+ }
91
+ }
92
+ catch (err) {
93
+ if (!err.message.includes('Not exists')) {
94
+ console.warn(`Directory ${id}/${srcPath} cannot be copied: ${err}`);
95
+ }
96
+ }
97
+ }
98
+ static getBackupDir() {
99
+ const dataDir = path_1.default.join(controllerDir, js_controller_common_1.tools.getDefaultDataDir());
100
+ const parts = dataDir.split('/');
101
+ parts.pop(); // remove data or appName-data
102
+ parts.pop();
103
+ return path_1.default.normalize(`${parts.join('/')}/backups/`);
104
+ }
105
+ copyFileSync(source, target) {
106
+ let targetFile = target;
107
+ try {
108
+ // if target is a directory a new file with the same name will be created
109
+ if (fs_extra_1.default.existsSync(target)) {
110
+ if (fs_extra_1.default.statSync(target).isDirectory()) {
111
+ targetFile = path_1.default.join(target, path_1.default.basename(source));
112
+ }
113
+ }
114
+ fs_extra_1.default.writeFileSync(targetFile, fs_extra_1.default.readFileSync(source));
115
+ }
116
+ catch (e) {
117
+ console.error(`Could not copy ${targetFile} to ${source}: ${e.message}`);
118
+ }
119
+ }
120
+ copyFolderRecursiveSync(source, target) {
121
+ let files = [];
122
+ if (!fs_extra_1.default.existsSync(target)) {
123
+ fs_extra_1.default.mkdirSync(target);
124
+ }
125
+ // check if folder needs to be created or integrated
126
+ const targetFolder = path_1.default.join(target, path_1.default.basename(source));
127
+ if (!fs_extra_1.default.existsSync(targetFolder)) {
128
+ fs_extra_1.default.mkdirSync(targetFolder);
129
+ }
130
+ // copy
131
+ if (fs_extra_1.default.existsSync(source) && fs_extra_1.default.statSync(source).isDirectory()) {
132
+ files = fs_extra_1.default.readdirSync(source);
133
+ files.forEach(file => {
134
+ const curSource = path_1.default.join(source, file);
135
+ if (!fs_extra_1.default.existsSync(curSource)) {
136
+ return;
137
+ }
138
+ if (fs_extra_1.default.statSync(curSource).isDirectory()) {
139
+ this.copyFolderRecursiveSync(curSource, targetFolder);
140
+ }
141
+ else {
142
+ this.copyFileSync(curSource, targetFolder);
143
+ }
144
+ });
145
+ }
146
+ }
147
+ /**
148
+ * Pack and compress the backup
149
+ *
150
+ * @param name - backup name
151
+ */
152
+ _packBackup(name) {
153
+ // 2021_10_25 BF (TODO): store letsencrypt files too
154
+ const letsEncrypt = `${this.configDir}/letsencrypt`;
155
+ if (fs_extra_1.default.existsSync(letsEncrypt)) {
156
+ this.copyFolderRecursiveSync(letsEncrypt, `${tmpDir}/backup`);
157
+ }
158
+ return new Promise(resolve => {
159
+ const f = fs_extra_1.default.createWriteStream(name);
160
+ f.on('finish', () => {
161
+ js_controller_common_1.tools.rmdirRecursiveSync(`${tmpDir}/backup`);
162
+ resolve(path_1.default.normalize(name));
163
+ });
164
+ f.on('error', err => {
165
+ console.error(`host.${hostname} Cannot pack directory ${tmpDir}/backup: ${err.message}`);
166
+ this.processExit(js_controller_common_2.EXIT_CODES.CANNOT_GZIP_DIRECTORY);
167
+ });
168
+ try {
169
+ tar_1.default.create({ gzip: true, cwd: `${tmpDir}/` }, ['backup']).pipe(f);
170
+ }
171
+ catch (err) {
172
+ console.error(`host.${hostname} Cannot pack directory ${tmpDir}/backup: ${err.message}`);
173
+ return void this.processExit(js_controller_common_2.EXIT_CODES.CANNOT_GZIP_DIRECTORY);
174
+ }
175
+ });
176
+ }
177
+ /**
178
+ * Creates backup and stores with given name
179
+ *
180
+ * @param name - name of the backup
181
+ * @param noConfig - do not store configs
182
+ */
183
+ async createBackup(name, noConfig) {
184
+ var _a;
185
+ if (!name) {
186
+ const d = new Date();
187
+ name =
188
+ d.getFullYear() +
189
+ '_' +
190
+ ('0' + (d.getMonth() + 1)).slice(-2) +
191
+ '_' +
192
+ ('0' + d.getDate()).slice(-2) +
193
+ '-' +
194
+ ('0' + d.getHours()).slice(-2) +
195
+ '_' +
196
+ ('0' + d.getMinutes()).slice(-2) +
197
+ '_' +
198
+ ('0' + d.getSeconds()).slice(-2) +
199
+ `_backup${js_controller_common_1.tools.appName}`;
200
+ }
201
+ name = name.toString().replace(/\\/g, '/');
202
+ if (!name.includes('/')) {
203
+ const backupPath = BackupRestore.getBackupDir();
204
+ // create directory if not exists
205
+ if (!fs_extra_1.default.existsSync(backupPath)) {
206
+ fs_extra_1.default.mkdirSync(backupPath);
207
+ }
208
+ if (!name.includes('.tar.gz')) {
209
+ name = `${backupPath + name}.tar.gz`;
210
+ }
211
+ else {
212
+ name = backupPath + name;
213
+ }
214
+ }
215
+ let result = { objects: null, states: {} };
216
+ const hostname = js_controller_common_1.tools.getHostName();
217
+ try {
218
+ // @ts-expect-error #1917
219
+ const res = await this.objects.getObjectListAsync({ include_docs: true });
220
+ if (res) {
221
+ result.objects = res.rows;
222
+ }
223
+ }
224
+ catch (e) {
225
+ console.error(`host.${hostname} Cannot get objects: ${e.message}`);
226
+ }
227
+ if (!noConfig) {
228
+ result.config = null;
229
+ }
230
+ if (!noConfig && fs_extra_1.default.existsSync(js_controller_common_1.tools.getConfigFileName())) {
231
+ result.config = fs_extra_1.default.readJSONSync(js_controller_common_1.tools.getConfigFileName());
232
+ }
233
+ const r = new RegExp(`^system\\.host\\.${hostname}\\.(\\w+)$`);
234
+ try {
235
+ // @ts-expect-error #1917
236
+ const keys = await this.states.getKeys('*');
237
+ // NOTE for all "replace" with $$$$ ... result will be just $$
238
+ // @ts-expect-error #1917
239
+ const objs = await this.states.getStates(keys);
240
+ // read iobroker.json
241
+ let isCustomHostname;
242
+ try {
243
+ const config = await fs_extra_1.default.readJSON(js_controller_common_1.tools.getConfigFileName());
244
+ // if a hostname is configured
245
+ isCustomHostname = !!config.system.hostname;
246
+ }
247
+ catch (e) {
248
+ console.error(`host.${hostname} Cannot read config file: ${e.message}`);
249
+ }
250
+ if (keys && objs) {
251
+ for (let i = 0; i < keys.length; i++) {
252
+ const obj = objs[i];
253
+ if (!obj) {
254
+ continue;
255
+ }
256
+ if (!isCustomHostname) {
257
+ // if its a default hostname, we will have a new default after restore and need to replace
258
+ if (obj.from === `system.host.${hostname}` || r.test(obj.from)) {
259
+ obj.from.replace(`system.host.${hostname}`, 'system.host.$$$$__hostname__$$$$');
260
+ }
261
+ if (r.test(keys[i])) {
262
+ keys[i] = keys[i].replace(hostname, '$$$$__hostname__$$$$');
263
+ }
264
+ }
265
+ result.states[keys[i]] = obj;
266
+ }
267
+ console.log(`host.${hostname} ${keys.length} states saved`);
268
+ }
269
+ }
270
+ catch (e) {
271
+ console.error(`host.${hostname} Cannot get states: ${e.message}`);
272
+ }
273
+ if (!fs_extra_1.default.existsSync(bkpDir)) {
274
+ fs_extra_1.default.mkdirSync(bkpDir);
275
+ }
276
+ if (!fs_extra_1.default.existsSync(tmpDir)) {
277
+ fs_extra_1.default.mkdirSync(tmpDir);
278
+ }
279
+ try {
280
+ js_controller_common_1.tools.rmdirRecursiveSync(`${tmpDir}/backup/`);
281
+ }
282
+ catch (e) {
283
+ console.error(`host.${hostname} Cannot clear temporary backup directory: ${e.message}`);
284
+ }
285
+ if (!fs_extra_1.default.existsSync(`${tmpDir}/backup`)) {
286
+ fs_extra_1.default.mkdirSync(`${tmpDir}/backup`);
287
+ }
288
+ if (!fs_extra_1.default.existsSync(`${tmpDir}/backup/files`)) {
289
+ fs_extra_1.default.mkdirSync(`${tmpDir}/backup/files`);
290
+ }
291
+ // try to find user files
292
+ if (result.objects) {
293
+ for (const object of result.objects) {
294
+ if (!object || !object.value || !object.value._id || !object.value.common) {
295
+ continue;
296
+ }
297
+ if (object.value._id.match(/^system\.adapter\.([\w\d_-]+).(\d+)$/) &&
298
+ object.value.common.host === hostname) {
299
+ object.value.common.host = '$$__hostname__$$';
300
+ if (object.doc) {
301
+ object.doc.common.host = '$$__hostname__$$';
302
+ }
303
+ }
304
+ else if (r.test(object.value._id)) {
305
+ object.value._id = object.value._id.replace(hostname, '$$$$__hostname__$$$$');
306
+ object.id = object.value._id;
307
+ if (object.doc) {
308
+ object.doc._id = object.value._id;
309
+ }
310
+ }
311
+ else if (object.value._id === 'system.host.' + hostname) {
312
+ object.value._id = 'system.host.$$__hostname__$$';
313
+ object.value.common.name = object.value._id;
314
+ object.value.common.hostname = '$$__hostname__$$';
315
+ if (object.value.native && object.value.native.os) {
316
+ object.value.native.os.hostname = '$$__hostname__$$';
317
+ }
318
+ object.id = object.value._id;
319
+ if (object.doc) {
320
+ object.doc._id = object.value._id;
321
+ object.doc.common.name = object.value._id;
322
+ object.doc.common.hostname = '$$__hostname__$$';
323
+ if (object.doc.native && object.value.native.os) {
324
+ object.doc.native.os.hostname = '$$__hostname__$$';
325
+ }
326
+ }
327
+ }
328
+ // Read all files
329
+ if (object.value.type === 'meta' && object.value.common && object.value.common.type === 'meta.user') {
330
+ // do not process "xxx.0. " and "xxx.0."
331
+ if (object.id.trim() === object.id && object.id[object.id.length - 1] !== '.') {
332
+ await this.copyDir(object.id, '', `${tmpDir}/backup/files/${object.id}`);
333
+ }
334
+ }
335
+ // Read all files
336
+ if (object.value.type === 'instance' && object.value.common && object.value.common.dataFolder) {
337
+ let dataFolderPath = object.value.common.dataFolder;
338
+ if (dataFolderPath[0] !== '/' && !dataFolderPath.match(/^\w:/)) {
339
+ dataFolderPath = path_1.default.join(this.configDir, dataFolderPath);
340
+ }
341
+ if (fs_extra_1.default.existsSync(dataFolderPath)) {
342
+ this.copyFolderRecursiveSync(dataFolderPath, `${tmpDir}/backup`);
343
+ }
344
+ }
345
+ }
346
+ }
347
+ // special case: copy vis vis-common-user.css file
348
+ try {
349
+ // @ts-expect-error #1917
350
+ const data = await this.objects.readFileAsync('vis', 'css/vis-common-user.css');
351
+ if (data) {
352
+ const dir = `${tmpDir}/backup/files/`;
353
+ !fs_extra_1.default.existsSync(`${dir}vis`) && fs_extra_1.default.mkdirSync(`${dir}vis`);
354
+ !fs_extra_1.default.existsSync(`${dir}vis/css`) && fs_extra_1.default.mkdirSync(`${dir}vis/css`);
355
+ // @ts-expect-error #1917
356
+ fs_extra_1.default.writeFileSync(`${dir}vis/css/vis-common-user.css`, data.data !== undefined ? data.data : data);
357
+ }
358
+ }
359
+ catch {
360
+ // do not process 'css/vis-common-user.css'
361
+ }
362
+ console.log(`host.${hostname} ${((_a = result.objects) === null || _a === void 0 ? void 0 : _a.length) || 'no'} objects saved`);
363
+ try {
364
+ fs_extra_1.default.writeFileSync(`${tmpDir}/backup/backup.json`, JSON.stringify(result, null, 2));
365
+ result = null; // ... to allow GC to clean it up because no longer needed
366
+ this._validateBackupAfterCreation();
367
+ return await this._packBackup(name);
368
+ }
369
+ catch (err) {
370
+ console.error(`host.${hostname} Backup not created: ${err.message}`);
371
+ try {
372
+ js_controller_common_1.tools.rmdirRecursiveSync(`${tmpDir}/backup/`);
373
+ }
374
+ catch (e) {
375
+ console.error(`host.${hostname} Cannot clear temporary backup directory: ${e.message}`);
376
+ }
377
+ return void this.processExit(js_controller_common_2.EXIT_CODES.CANNOT_EXTRACT_FROM_ZIP);
378
+ }
379
+ }
380
+ //--------------------------------------- RESTORE ---------------------------------------------------
381
+ /**
382
+ * Helper to restore raw states
383
+ *
384
+ * @param statesList - list of state ids
385
+ * @param stateObjects - list of state objects
386
+ */
387
+ async _setStateHelper(statesList, stateObjects) {
388
+ for (let i = 0; i < statesList.length; i++) {
389
+ try {
390
+ // @ts-expect-error #1917 TODO revisit after merge
391
+ await this.states.setRawState(statesList[i], stateObjects[statesList[i]]);
392
+ }
393
+ catch (err) {
394
+ console.log(`host.${hostname} Could not set value for state ${statesList[i]}: ${err.message}`);
395
+ }
396
+ if (i % 200 === 0) {
397
+ console.log(`host.${hostname} Processed ${i}/${statesList.length} states`);
398
+ }
399
+ }
400
+ }
401
+ /**
402
+ * Sets all objects to the db and disables all adapters
403
+ *
404
+ * @param _objects - array of all objects to be set
405
+ */
406
+ async _setObjHelper(_objects) {
407
+ for (let i = 0; i < _objects.length; i++) {
408
+ // Disable all adapters.
409
+ if (!this.dbMigration &&
410
+ _objects[i].id &&
411
+ /^system\.adapter\..+\.\d$/.test(_objects[i].id) &&
412
+ !_objects[i].id.startsWith('system.adapter.admin.') &&
413
+ !_objects[i].id.startsWith('system.adapter.backitup.')) {
414
+ if (_objects[i].doc.common && _objects[i].doc.common.enabled) {
415
+ _objects[i].doc.common.enabled = false;
416
+ }
417
+ }
418
+ try {
419
+ await this.objects.setObjectAsync(_objects[i].id, _objects[i].doc);
420
+ }
421
+ catch (err) {
422
+ console.warn(`host.${hostname} Cannot restore ${_objects[i].id}: ${err.message}`);
423
+ }
424
+ if (i % 200 === 0) {
425
+ console.log(`host.${hostname} Processed ${i}/${_objects.length} objects`);
426
+ }
427
+ }
428
+ }
429
+ /**
430
+ * Creates all provided object if non existing
431
+ *
432
+ * @param objectList - list of objects to be created
433
+ */
434
+ async _reloadAdapterObject(objectList) {
435
+ if (!Array.isArray(objectList)) {
436
+ return;
437
+ }
438
+ for (const object of objectList) {
439
+ let obj;
440
+ try {
441
+ obj = await this.objects.getObjectAsync(object._id);
442
+ }
443
+ catch {
444
+ // ignore
445
+ }
446
+ if (!obj) {
447
+ // object not existing -> create it
448
+ try {
449
+ await this.objects.setObjectAsync(object._id, object);
450
+ console.log(`host.${hostname} object ${object._id} created`);
451
+ }
452
+ catch {
453
+ // ignore
454
+ }
455
+ }
456
+ }
457
+ }
458
+ async _reloadAdaptersObjects() {
459
+ const dirs = [];
460
+ let _modules;
461
+ let p = path_1.default.join(controllerDir, 'node_modules');
462
+ if (fs_extra_1.default.existsSync(p)) {
463
+ if (!p.includes('js-controller')) {
464
+ _modules = fs_extra_1.default.readdirSync(p).filter(dir => fs_extra_1.default.existsSync(`${p}/${dir}/io-package.json`));
465
+ if (_modules) {
466
+ const regEx = new RegExp(`^${js_controller_common_1.tools.appName}\\.`, 'i');
467
+ for (const module of _modules) {
468
+ if (regEx.test(module) && !dirs.includes(module.substring(js_controller_common_1.tools.appName.length + 1))) {
469
+ dirs.push(module);
470
+ }
471
+ }
472
+ }
473
+ }
474
+ else {
475
+ p = path_1.default.join(controllerDir, '..', 'node_modules');
476
+ if (fs_extra_1.default.existsSync(p)) {
477
+ _modules = fs_extra_1.default.readdirSync(p).filter(dir => fs_extra_1.default.existsSync(`${p}/${dir}/io-package.json`));
478
+ if (_modules) {
479
+ const regEx = new RegExp(`^${js_controller_common_1.tools.appName}\\.`, 'i');
480
+ for (const module of _modules) {
481
+ if (regEx.test(module) && !dirs.includes(module.substring(js_controller_common_1.tools.appName.length + 1))) {
482
+ dirs.push(module);
483
+ }
484
+ }
485
+ }
486
+ }
487
+ }
488
+ }
489
+ // if installed as npm
490
+ if (fs_extra_1.default.existsSync(path_1.default.join(controllerDir, '..', '..', 'node_modules', `${js_controller_common_1.tools.appName}.js-controller`))) {
491
+ const p = path_1.default.join(controllerDir, '..');
492
+ _modules = fs_extra_1.default.readdirSync(p).filter(dir => fs_extra_1.default.existsSync(`${p}/${dir}/io-package.json`));
493
+ const regEx_ = new RegExp(`^${js_controller_common_1.tools.appName}\\.`, 'i');
494
+ for (const module of _modules) {
495
+ // if starting from application name + '.'
496
+ if (regEx_.test(module) &&
497
+ // If not js-controller
498
+ module.substring(js_controller_common_1.tools.appName.length + 1) !== 'js-controller' &&
499
+ !dirs.includes(module.substring(js_controller_common_1.tools.appName.length + 1))) {
500
+ dirs.push(module);
501
+ }
502
+ }
503
+ }
504
+ for (const dir of dirs) {
505
+ const adapterName = dir.replace(/^iobroker\./i, '');
506
+ await this.upload.uploadAdapter(adapterName, false, true);
507
+ await this.upload.uploadAdapter(adapterName, true, true);
508
+ let pkg = null;
509
+ if (!dir) {
510
+ console.error('Wrong');
511
+ }
512
+ const adapterDir = js_controller_common_1.tools.getAdapterDir(adapterName);
513
+ if (fs_extra_1.default.existsSync(`${adapterDir}/io-package.json`)) {
514
+ pkg = fs_extra_1.default.readJSONSync(`${adapterDir}/io-package.json`);
515
+ }
516
+ if (pkg && pkg.objects && pkg.objects.length) {
517
+ console.log(`host.${hostname} Setup "${dir}" adapter`);
518
+ await this._reloadAdapterObject(pkg.objects);
519
+ }
520
+ }
521
+ }
522
+ async _uploadUserFiles(root, uploadPath) {
523
+ uploadPath = uploadPath || '';
524
+ if (!fs_extra_1.default.existsSync(root)) {
525
+ return;
526
+ }
527
+ const files = fs_extra_1.default.readdirSync(root + uploadPath);
528
+ for (const file of files) {
529
+ const fName = path_1.default.join(root, uploadPath, file);
530
+ if (!fs_extra_1.default.existsSync(fName)) {
531
+ continue;
532
+ }
533
+ const stat = fs_extra_1.default.statSync(fName);
534
+ if (stat.isDirectory()) {
535
+ try {
536
+ await this._uploadUserFiles(root, `${uploadPath}/${file}`);
537
+ }
538
+ catch (err) {
539
+ console.error(`Error: ${err}`);
540
+ }
541
+ }
542
+ else {
543
+ const parts = uploadPath.split('/');
544
+ const adapter = parts.splice(0, 2)[1];
545
+ const _path = `${parts.join('/')}/${file}`;
546
+ console.log(`host.${hostname} Upload user file "${adapter}/${_path}`);
547
+ try {
548
+ await this.objects.writeFileAsync(adapter, _path, fs_extra_1.default.readFileSync(`${root + uploadPath}/${file}`));
549
+ }
550
+ catch (err) {
551
+ console.error(`Error: ${err.message}`);
552
+ }
553
+ }
554
+ }
555
+ }
556
+ _copyBackupedFiles(backupDir) {
557
+ try {
558
+ if (!fs_extra_1.default.existsSync(backupDir)) {
559
+ console.log('No additional files to restore');
560
+ return;
561
+ }
562
+ const dirs = fs_extra_1.default.readdirSync(backupDir);
563
+ dirs.forEach(dir => {
564
+ if (dir === 'files') {
565
+ return;
566
+ }
567
+ const backupPath = path_1.default.join(backupDir, dir);
568
+ let stat;
569
+ try {
570
+ if (!fs_extra_1.default.existsSync(backupPath)) {
571
+ return;
572
+ }
573
+ stat = fs_extra_1.default.statSync(backupPath);
574
+ }
575
+ catch (err) {
576
+ console.error(`Ignoring ${backupPath} because can not get file type: ${err.message}`);
577
+ return;
578
+ }
579
+ if (stat.isDirectory()) {
580
+ this.copyFolderRecursiveSync(backupPath, this.configDir);
581
+ }
582
+ });
583
+ }
584
+ catch (err) {
585
+ console.error(`Ignoring ${backupDir} because can not read directory: ${err.message}`);
586
+ }
587
+ }
588
+ /**
589
+ * Restore after controller has been stopped
590
+ *
591
+ * @param restartOnFinish - restart controller after restore
592
+ * @param force - skip the controller version check
593
+ * @param dontDeleteAdapters - skip adapter deletion, e.g. for setup custom db migration
594
+ */
595
+ async _restoreAfterStop(restartOnFinish, force, dontDeleteAdapters) {
596
+ // Open file
597
+ let data = fs_extra_1.default.readFileSync(`${tmpDir}/backup/backup.json`, 'utf8');
598
+ const hostname = js_controller_common_1.tools.getHostName();
599
+ // replace all hostnames of instances etc with the new host
600
+ data = data.replace(/\$\$__hostname__\$\$/g, hostname);
601
+ fs_extra_1.default.writeFileSync(`${tmpDir}/backup/backup_.json`, data);
602
+ let restore;
603
+ try {
604
+ restore = JSON.parse(data);
605
+ }
606
+ catch (err) {
607
+ console.error(`Cannot parse "${tmpDir}/backup/backup_.json": ${err.message}`);
608
+ return js_controller_common_2.EXIT_CODES.CANNOT_RESTORE_BACKUP;
609
+ }
610
+ // check that the same controller version is installed as it is contained in backup
611
+ const exitCode = this._ensureCompatibility(controllerDir, restore.config ? restore.config.system.hostname || hostname : hostname, restore.objects, force);
612
+ if (exitCode) {
613
+ // we had an error
614
+ return exitCode;
615
+ }
616
+ if (!dontDeleteAdapters) {
617
+ // prevent having wrong versions of adapters
618
+ await this._removeAllAdapters();
619
+ }
620
+ // stop all adapters
621
+ console.log(`host.${hostname} Clear all objects and states...`);
622
+ await this.cleanDatabase(false);
623
+ console.log(`host.${hostname} done.`);
624
+ // upload all data into DB
625
+ // restore ioBroker.json
626
+ if (restore.config) {
627
+ fs_extra_1.default.writeFileSync(js_controller_common_1.tools.getConfigFileName(), JSON.stringify(restore.config, null, 2));
628
+ }
629
+ const sList = Object.keys(restore.states);
630
+ await this._setObjHelper(restore.objects);
631
+ console.log(`${restore.objects.length} objects restored.`);
632
+ await this._setStateHelper(sList, restore.states);
633
+ console.log(`${sList.length} states restored.`);
634
+ // Load user files into DB
635
+ await this._uploadUserFiles(`${tmpDir}/backup/files`);
636
+ // reload objects of adapters (if some couldn't be removed - normally this shouldn't be necessary anymore)
637
+ await this._reloadAdaptersObjects();
638
+ // Reload host objects
639
+ const packageIO = fs_extra_1.default.readJSONSync(path_1.default.join(controllerDir, 'io-package.json'));
640
+ await this._reloadAdapterObject(packageIO ? packageIO.objects : null);
641
+ // copy all files into iob-data
642
+ await this._copyBackupedFiles(path_1.default.join(tmpDir, 'backup'));
643
+ // reinstall preserve adapters
644
+ await this._restorePreservedAdapters();
645
+ if (force) {
646
+ // js-controller version has changed (setup never called for this version)
647
+ console.log('Forced restore - executing setup ...');
648
+ try {
649
+ await promisify_child_process_1.default.exec(`"${process.execPath}" "${path_1.default.join(controllerDir, `${js_controller_common_1.tools.appName.toLowerCase()}.js`)}" setup`);
650
+ }
651
+ catch (e) {
652
+ console.error(`Could not execute "setup" command, please ensure "setup" is called before starting ioBroker: ${e.message}`);
653
+ }
654
+ }
655
+ if (restartOnFinish) {
656
+ this.restartController();
657
+ }
658
+ return js_controller_common_2.EXIT_CODES.NO_ERROR;
659
+ }
660
+ /**
661
+ * Removes all adapters
662
+ */
663
+ async _removeAllAdapters() {
664
+ const nodeModulePath = path_1.default.join(controllerDir, '..');
665
+ const nodeModuleDirs = fs_extra_1.default.readdirSync(nodeModulePath, { withFileTypes: true });
666
+ // we need to uninstall current adapters to get exact the same system as before backup
667
+ for (const dir of nodeModuleDirs) {
668
+ if (dir.isDirectory() &&
669
+ dir.name.startsWith(`${js_controller_common_1.tools.appName.toLowerCase()}.`) &&
670
+ dir.name !== `${js_controller_common_1.tools.appName.toLowerCase()}.js-controller`) {
671
+ try {
672
+ const packJson = fs_extra_1.default.readJsonSync(path_1.default.join(nodeModulePath, dir.name, 'package.json'));
673
+ console.log(`Removing current installation of ${packJson.name}`);
674
+ await js_controller_common_1.tools.uninstallNodeModule(packJson.name);
675
+ }
676
+ catch {
677
+ // ignore
678
+ }
679
+ }
680
+ }
681
+ }
682
+ /**
683
+ * Ensure that installed controller version matches version in backup
684
+ *
685
+ * @param controllerDir - directory of js-controller
686
+ * @param backupHostname - hostname in backup file
687
+ * @param backupObjects - the objects contained in the backup
688
+ * @param force - if force is true, only log
689
+ */
690
+ _ensureCompatibility(controllerDir, backupHostname, backupObjects, force) {
691
+ try {
692
+ const ioPackJson = fs_extra_1.default.readJsonSync(path_1.default.join(controllerDir, 'io-package.json'));
693
+ const hostObj = backupObjects.find(obj => obj.id === `system.host.${backupHostname}`);
694
+ if (!hostObj) {
695
+ console.error('No host object found, your backup seems to be corrupted!');
696
+ return js_controller_common_2.EXIT_CODES.CANNOT_RESTORE_BACKUP;
697
+ }
698
+ if (hostObj.value.common.installedVersion !== ioPackJson.common.version) {
699
+ if (!force) {
700
+ console.warn('The current version of js-controller differs from the version in the backup.');
701
+ console.warn('The js-controller version of the backup can not be restored automatically.');
702
+ console.warn(`To restore the js-controller version of the backup, execute "npm i iobroker.js-controller@${hostObj.value.common.installedVersion} --production" inside your ioBroker directory`);
703
+ console.warn('If you really want to restore the backup with the current installed js-controller, execute the restore command with the --force flag');
704
+ return js_controller_common_2.EXIT_CODES.CANNOT_RESTORE_BACKUP;
705
+ }
706
+ else {
707
+ console.info('The current version of js-controller differs from the version in the backup.');
708
+ console.info('The js-controller version of the backup can not be restored automatically.');
709
+ console.info('Note, that your backup might differ in behavior due to this version change!');
710
+ }
711
+ }
712
+ }
713
+ catch {
714
+ // ignore
715
+ }
716
+ }
717
+ /**
718
+ * Returns all backups as array
719
+ */
720
+ listBackups() {
721
+ const dir = BackupRestore.getBackupDir();
722
+ const result = [];
723
+ if (fs_extra_1.default.existsSync(dir)) {
724
+ const files = fs_extra_1.default.readdirSync(dir);
725
+ for (const file of files) {
726
+ if (file.match(/\.tar\.gz$/i)) {
727
+ result.push(file);
728
+ }
729
+ }
730
+ return result;
731
+ }
732
+ else {
733
+ return result;
734
+ }
735
+ }
736
+ /**
737
+ * Validates the backup.json and all json files inside the backup after (in temporary directory), here we only abort if backup.json is corrupted
738
+ */
739
+ _validateBackupAfterCreation() {
740
+ // eslint-disable-next-line @typescript-eslint/no-var-requires
741
+ const backupJSON = require(`${tmpDir}/backup/backup.json`);
742
+ if (!backupJSON.objects || !backupJSON.objects.length) {
743
+ throw new Error('Backup does not contain valid objects');
744
+ }
745
+ // we check all other json files, we assume them as optional, because user created files may be no valid json
746
+ try {
747
+ this._checkDirectory(`${tmpDir}/backup/files`);
748
+ }
749
+ catch (err) {
750
+ console.warn(`host.${hostname} One or more optional files are corrupted: ${err.message}`);
751
+ console.warn(`host.${hostname} Please ensure that self-created JSON files are valid`);
752
+ }
753
+ }
754
+ /**
755
+ * Validates the given backup.json and all json files in the backup, calls processExit afterwards
756
+ * @param _name - index or name of the backup
757
+ */
758
+ validateBackup(_name) {
759
+ let backups;
760
+ let name = typeof _name === 'number' ? _name.toString() : _name;
761
+ if (!name) {
762
+ backups = this.listBackups();
763
+ backups.sort((a, b) => (b > a ? 1 : b === a ? 0 : -1));
764
+ if (backups.length) {
765
+ // List all available backups
766
+ console.log('Please specify one of the backup names:');
767
+ for (const t in backups) {
768
+ console.log(`${backups[t]} or ${backups[t].replace(`_backup${js_controller_common_1.tools.appName}.tar.gz`, '')} or ${t}`);
769
+ }
770
+ }
771
+ else {
772
+ console.warn(`No backups found. Create a backup, using "${js_controller_common_1.tools.appName} backup" first`);
773
+ }
774
+ return void this.processExit(10);
775
+ }
776
+ // If number
777
+ if (parseInt(name, 10).toString() === name.toString()) {
778
+ backups = this.listBackups();
779
+ backups.sort((a, b) => (b > a ? 1 : b === a ? 0 : -1));
780
+ name = backups[parseInt(name, 10)];
781
+ if (!name) {
782
+ console.log('No matching backup found');
783
+ if (backups.length) {
784
+ console.log('Please specify one of the backup names:');
785
+ for (const t in backups) {
786
+ console.log(`${backups[t]} or ${backups[t].replace(`_backup${js_controller_common_1.tools.appName}.tar.gz`, '')} or ${t}`);
787
+ }
788
+ }
789
+ else {
790
+ console.log(`No existing backups. Create a backup, using "${js_controller_common_1.tools.appName} backup" first`);
791
+ }
792
+ return void this.processExit(10);
793
+ }
794
+ else {
795
+ console.log(`host.${hostname} Using backup file ${name}`);
796
+ }
797
+ }
798
+ name = name.toString().replace(/\\/g, '/');
799
+ if (!name.includes('/')) {
800
+ name = BackupRestore.getBackupDir() + name;
801
+ const regEx = new RegExp(`_backup${js_controller_common_1.tools.appName}`, 'i');
802
+ if (!regEx.test(name)) {
803
+ name += `_backup${js_controller_common_1.tools.appName}`;
804
+ }
805
+ if (!name.match(/\.tar\.gz$/i)) {
806
+ name += '.tar.gz';
807
+ }
808
+ }
809
+ if (!fs_extra_1.default.existsSync(name)) {
810
+ console.error(`host.${hostname} Cannot find ${name}`);
811
+ return void this.processExit(11);
812
+ }
813
+ if (fs_extra_1.default.existsSync(`${tmpDir}/backup/backup.json`)) {
814
+ fs_extra_1.default.unlinkSync(`${tmpDir}/backup/backup.json`);
815
+ }
816
+ return new Promise(resolve => {
817
+ tar_1.default.extract({
818
+ file: name,
819
+ cwd: tmpDir
820
+ }, undefined, err => {
821
+ if (err) {
822
+ console.error(`host.${hostname} Cannot extract from file "${name}": ${err.message}`);
823
+ return void this.processExit(9);
824
+ }
825
+ if (!fs_extra_1.default.existsSync(`${tmpDir}/backup/backup.json`)) {
826
+ console.error(`host.${hostname} Validation failed. Cannot find extracted file from file "${tmpDir}/backup/backup.json"`);
827
+ return void this.processExit(9);
828
+ }
829
+ console.log(`host.${hostname} Starting validation ...`);
830
+ let backupJSON;
831
+ try {
832
+ backupJSON = require(`${tmpDir}/backup/backup.json`);
833
+ }
834
+ catch (err) {
835
+ console.error(`host.${hostname} Backup corrupted. Backup ${name} does not contain a valid backup.json file: ${err.message}`);
836
+ try {
837
+ js_controller_common_1.tools.rmdirRecursiveSync(`${tmpDir}/backup/`);
838
+ }
839
+ catch (e) {
840
+ console.error(`host.${hostname} Cannot clear temporary backup directory: ${e.message}`);
841
+ }
842
+ return void this.processExit(26);
843
+ }
844
+ if (!backupJSON || !backupJSON.objects || !backupJSON.objects.length) {
845
+ console.error(`host.${hostname} Backup corrupted. Backup does not contain valid objects`);
846
+ try {
847
+ js_controller_common_1.tools.rmdirRecursiveSync(`${tmpDir}/backup/`);
848
+ }
849
+ catch (e) {
850
+ console.error(`host.${hostname} Cannot clear temporary backup directory: ${e.message}`);
851
+ }
852
+ return void this.processExit(26);
853
+ }
854
+ console.log(`host.${hostname} backup.json OK`);
855
+ try {
856
+ this._checkDirectory(`${tmpDir}/backup/files`, true);
857
+ try {
858
+ js_controller_common_1.tools.rmdirRecursiveSync(`${tmpDir}/backup/`);
859
+ }
860
+ catch (e) {
861
+ console.error(`host.${hostname} Cannot clear temporary backup directory: ${e.message}`);
862
+ }
863
+ resolve();
864
+ }
865
+ catch (err) {
866
+ console.error(`host.${hostname} Backup corrupted: ${err.message}`);
867
+ return void this.processExit(26);
868
+ }
869
+ });
870
+ });
871
+ }
872
+ /**
873
+ * Checks a directory for json files and validates them, steps down recursive in subdirectories
874
+ * @param path - path to the directory
875
+ * @param verbose - if logging should be verbose
876
+ */
877
+ _checkDirectory(path, verbose = false) {
878
+ if (fs_extra_1.default.existsSync(path)) {
879
+ const files = fs_extra_1.default.readdirSync(path);
880
+ if (!files.length) {
881
+ return;
882
+ }
883
+ for (const file of files) {
884
+ const filePath = `${path}/${file}`;
885
+ if (fs_extra_1.default.existsSync(filePath) && fs_extra_1.default.statSync(filePath).isDirectory()) {
886
+ // if directory then check it
887
+ this._checkDirectory(filePath, verbose);
888
+ }
889
+ else if (file.endsWith('.json')) {
890
+ try {
891
+ require(filePath);
892
+ if (verbose) {
893
+ console.log(`host.${hostname} ${file} OK`);
894
+ }
895
+ }
896
+ catch {
897
+ throw new Error(`host.${hostname} ${filePath} is not a valid json file`);
898
+ }
899
+ }
900
+ }
901
+ }
902
+ }
903
+ /**
904
+ * Restores a backup
905
+ *
906
+ * @param _name - backup name or index
907
+ * @param force - if force, js-controller is allowed to have a different version
908
+ * @param dontDeleteAdapters - skip adapter deletion, e.g. for setup custom db migration
909
+ * @param callback
910
+ */
911
+ restoreBackup(_name, force, dontDeleteAdapters, callback) {
912
+ let backups;
913
+ let name = typeof _name === 'number' ? _name.toString() : _name;
914
+ if (!name) {
915
+ // List all available backups
916
+ console.log('Please specify one of the backup names:');
917
+ backups = this.listBackups();
918
+ backups.sort((a, b) => (b > a ? 1 : b === a ? 0 : -1));
919
+ if (backups.length) {
920
+ backups.forEach((backup, i) => console.log(`${backup} or ${backup.replace(`_backup${js_controller_common_1.tools.appName}.tar.gz`, '')} or ${i}`));
921
+ }
922
+ else {
923
+ console.warn('No backups found');
924
+ }
925
+ return void this.processExit(10);
926
+ }
927
+ if (!this.cleanDatabase) {
928
+ throw new Error('Invalid arguments: cleanDatabase is missing');
929
+ }
930
+ if (!this.restartController) {
931
+ throw new Error('Invalid arguments: restartController is missing');
932
+ }
933
+ // If number
934
+ if (parseInt(name, 10).toString() === name.toString()) {
935
+ backups = this.listBackups();
936
+ backups.sort((a, b) => (b > a ? 1 : b === a ? 0 : -1));
937
+ name = backups[parseInt(name, 10)];
938
+ if (!name) {
939
+ console.log('No matching backup found');
940
+ if (backups.length) {
941
+ console.log('Please specify one of the backup names:');
942
+ backups.forEach((backup, i) => console.log(`${backup} or ${backup.replace(`_backup${js_controller_common_1.tools.appName}.tar.gz`, '')} or ${i}`));
943
+ }
944
+ }
945
+ else {
946
+ console.log(`host.${hostname} Using backup file ${name}`);
947
+ }
948
+ }
949
+ name = name.toString().replace(/\\/g, '/');
950
+ if (!name.includes('/')) {
951
+ name = BackupRestore.getBackupDir() + name;
952
+ const regEx = new RegExp(`_backup${js_controller_common_1.tools.appName}`, 'i');
953
+ if (!regEx.test(name)) {
954
+ name += '_backup' + js_controller_common_1.tools.appName;
955
+ }
956
+ if (!name.match(/\.tar\.gz$/i)) {
957
+ name += '.tar.gz';
958
+ }
959
+ }
960
+ if (!fs_extra_1.default.existsSync(name)) {
961
+ console.error(`host.${hostname} Cannot find ${name}`);
962
+ return void this.processExit(11);
963
+ }
964
+ // delete /backup/backup.json
965
+ fs_extra_1.default.existsSync(`${tmpDir}/backup/backup.json`) && fs_extra_1.default.unlinkSync(`${tmpDir}/backup/backup.json`);
966
+ tar_1.default.extract({
967
+ file: name,
968
+ cwd: tmpDir
969
+ }, undefined, err => {
970
+ if (err) {
971
+ console.error(`host.${hostname} Cannot extract from file "${name}": ${err.message}`);
972
+ return void this.processExit(9);
973
+ }
974
+ if (!fs_extra_1.default.existsSync(`${tmpDir}/backup/backup.json`)) {
975
+ console.error(`host.${hostname} Cannot find extracted file from file "${tmpDir}/backup/backup.json"`);
976
+ return void this.processExit(9);
977
+ }
978
+ // Stop controller
979
+ // eslint-disable-next-line @typescript-eslint/no-var-requires
980
+ const daemon = require('daemonize2').setup({
981
+ main: path_1.default.join(controllerDir, 'controller.js'),
982
+ name: `${js_controller_common_1.tools.appName} controller`,
983
+ pidfile: path_1.default.join(controllerDir, `${js_controller_common_1.tools.appName}.pid`),
984
+ cwd: controllerDir,
985
+ stopTimeout: 1000
986
+ });
987
+ daemon.on('error', async () => {
988
+ const exitCode = await this._restoreAfterStop(false, force, dontDeleteAdapters);
989
+ callback && callback(exitCode);
990
+ });
991
+ daemon.on('stopped', async () => {
992
+ const exitCode = await this._restoreAfterStop(true, force, dontDeleteAdapters);
993
+ callback && callback(exitCode);
994
+ });
995
+ daemon.on('notrunning', async () => {
996
+ console.log(`host.${hostname} OK.`);
997
+ const exitCode = await this._restoreAfterStop(false, force, dontDeleteAdapters);
998
+ callback && callback(exitCode);
999
+ });
1000
+ daemon.stop();
1001
+ });
1002
+ }
1003
+ /**
1004
+ * This method checks if adapter of PRESERVE_ADAPTERS exist, and reinstalls them if this is the case
1005
+ */
1006
+ async _restorePreservedAdapters() {
1007
+ for (const adapterName of this.PRESERVE_ADAPTERS) {
1008
+ try {
1009
+ const adapterObj = await this.objects.getObjectAsync(`system.adapter.${adapterName}`);
1010
+ if (adapterObj && adapterObj.common && adapterObj.common.version) {
1011
+ let installSource;
1012
+ // @ts-expect-error https://github.com/ioBroker/adapter-core/issues/455
1013
+ if (adapterObj.common.installedFrom) {
1014
+ // @ts-expect-error https://github.com/ioBroker/adapter-core/issues/455
1015
+ installSource = adapterObj.common.installedFrom;
1016
+ }
1017
+ else {
1018
+ installSource = `${js_controller_common_1.tools.appName.toLowerCase()}.${adapterName}@${adapterObj.common.version}`;
1019
+ }
1020
+ console.log(`Reinstalling adapter "${adapterName}" from "${installSource}"`);
1021
+ await js_controller_common_1.tools.installNodeModule(installSource);
1022
+ }
1023
+ }
1024
+ catch (e) {
1025
+ console.error(`Could not ensure existence of adapter "${adapterName}": ${e.message}`);
1026
+ }
1027
+ }
1028
+ }
1029
+ }
1030
+ exports.BackupRestore = BackupRestore;
1031
+ //# sourceMappingURL=setupBackup.js.map