@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,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
|