@iobroker/socket-classes 1.6.2 → 2.0.2
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/LICENSE +1 -1
- package/README.md +791 -775
- package/dist/index.d.ts +8 -0
- package/dist/index.js +21 -0
- package/dist/index.js.map +1 -0
- package/dist/lib/passportSocket.d.ts +34 -0
- package/dist/lib/passportSocket.js +91 -0
- package/dist/lib/passportSocket.js.map +1 -0
- package/dist/lib/socketAdmin.d.ts +42 -0
- package/dist/lib/socketAdmin.js +246 -0
- package/dist/lib/socketAdmin.js.map +1 -0
- package/dist/lib/socketCommands.d.ts +83 -0
- package/dist/lib/socketCommands.js +2015 -0
- package/dist/lib/socketCommands.js.map +1 -0
- package/dist/lib/socketCommandsAdmin.d.ts +145 -0
- package/dist/lib/socketCommandsAdmin.js +1586 -0
- package/dist/lib/socketCommandsAdmin.js.map +1 -0
- package/dist/lib/socketCommon.d.ts +143 -0
- package/dist/lib/socketCommon.js +521 -0
- package/dist/lib/socketCommon.js.map +1 -0
- package/dist/types.d.ts +62 -0
- package/package.json +23 -17
- package/index.js +0 -19
- package/lib/passportSocket.js +0 -105
- package/lib/socketAdmin.js +0 -203
- package/lib/socketCommands.js +0 -1886
- package/lib/socketCommandsAdmin.js +0 -1492
- package/lib/socketCommon.js +0 -480
|
@@ -0,0 +1,1586 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
var __importDefault = (this && this.__importDefault) || function (mod) {
|
|
3
|
+
return (mod && mod.__esModule) ? mod : { "default": mod };
|
|
4
|
+
};
|
|
5
|
+
var _a;
|
|
6
|
+
Object.defineProperty(exports, "__esModule", { value: true });
|
|
7
|
+
exports.SocketCommandsAdmin = void 0;
|
|
8
|
+
const axios_1 = __importDefault(require("axios"));
|
|
9
|
+
const node_path_1 = require("node:path");
|
|
10
|
+
const node_fs_1 = require("node:fs");
|
|
11
|
+
const socketCommands_1 = require("./socketCommands");
|
|
12
|
+
const ACL_READ = 4;
|
|
13
|
+
// const ACL_WRITE = 2;
|
|
14
|
+
class SocketCommandsAdmin extends socketCommands_1.SocketCommands {
|
|
15
|
+
static ALLOW_CACHE = [
|
|
16
|
+
'getRepository',
|
|
17
|
+
'getInstalled',
|
|
18
|
+
'getInstalledAdapter',
|
|
19
|
+
'getVersion',
|
|
20
|
+
'getDiagData',
|
|
21
|
+
'getLocationOnDisk',
|
|
22
|
+
'getDevList',
|
|
23
|
+
'getLogs',
|
|
24
|
+
'getHostInfo',
|
|
25
|
+
];
|
|
26
|
+
states;
|
|
27
|
+
objects;
|
|
28
|
+
thresholdInterval = null;
|
|
29
|
+
cmdSessions = {};
|
|
30
|
+
eventsThreshold;
|
|
31
|
+
cache = {};
|
|
32
|
+
cacheGB = null; // cache garbage collector
|
|
33
|
+
onThresholdChanged = null;
|
|
34
|
+
secret = '';
|
|
35
|
+
constructor(adapter, updateSession, context, objects, states) {
|
|
36
|
+
super(adapter, updateSession, context);
|
|
37
|
+
this.objects = objects;
|
|
38
|
+
this.states = states;
|
|
39
|
+
this.eventsThreshold = {
|
|
40
|
+
count: 0,
|
|
41
|
+
timeActivated: 0,
|
|
42
|
+
active: false,
|
|
43
|
+
accidents: 0,
|
|
44
|
+
repeatSeconds: 3, // how many seconds continuously must be number of events > value
|
|
45
|
+
value: parseInt(adapter.config.thresholdValue, 10) || 200, // how many events allowed in one check interval
|
|
46
|
+
checkInterval: 1000, // duration of one check interval
|
|
47
|
+
};
|
|
48
|
+
// do not send too many state updates
|
|
49
|
+
}
|
|
50
|
+
start(onThresholdChanged) {
|
|
51
|
+
// detect event bursts
|
|
52
|
+
this.thresholdInterval = setInterval(() => {
|
|
53
|
+
if (!this.eventsThreshold.active) {
|
|
54
|
+
if (this.eventsThreshold.count > this.eventsThreshold.value) {
|
|
55
|
+
this.eventsThreshold.accidents++;
|
|
56
|
+
if (this.eventsThreshold.accidents >= this.eventsThreshold.repeatSeconds) {
|
|
57
|
+
this.#enableEventThreshold();
|
|
58
|
+
}
|
|
59
|
+
}
|
|
60
|
+
else {
|
|
61
|
+
this.eventsThreshold.accidents = 0;
|
|
62
|
+
}
|
|
63
|
+
this.eventsThreshold.count = 0;
|
|
64
|
+
}
|
|
65
|
+
else if (Date.now() - this.eventsThreshold.timeActivated > 60000) {
|
|
66
|
+
this.disableEventThreshold();
|
|
67
|
+
}
|
|
68
|
+
}, this.eventsThreshold.checkInterval);
|
|
69
|
+
this.onThresholdChanged = onThresholdChanged;
|
|
70
|
+
}
|
|
71
|
+
/**
|
|
72
|
+
* Read a file with ratings from server
|
|
73
|
+
*
|
|
74
|
+
* @param uuid Unique ioBroker system identification
|
|
75
|
+
* @param _isAutoUpdate not implemented
|
|
76
|
+
*/
|
|
77
|
+
async updateRatings(uuid, _isAutoUpdate) {
|
|
78
|
+
let _uuid;
|
|
79
|
+
if (!uuid) {
|
|
80
|
+
const obj = await this.adapter.getForeignObjectAsync('system.meta.uuid');
|
|
81
|
+
_uuid = obj?.native?.uuid || '';
|
|
82
|
+
}
|
|
83
|
+
else {
|
|
84
|
+
_uuid = uuid;
|
|
85
|
+
}
|
|
86
|
+
try {
|
|
87
|
+
const response = await axios_1.default.get(`https://rating.iobroker.net/rating?uuid=${uuid}`, {
|
|
88
|
+
timeout: 15000,
|
|
89
|
+
validateStatus: status => status < 400,
|
|
90
|
+
});
|
|
91
|
+
this.context.ratings = response.data;
|
|
92
|
+
if (!this.context.ratings ||
|
|
93
|
+
typeof this.context.ratings !== 'object' ||
|
|
94
|
+
Array.isArray(this.context.ratings)) {
|
|
95
|
+
// @ts-expect-error exception
|
|
96
|
+
this.context.ratings = { uuid: _uuid };
|
|
97
|
+
}
|
|
98
|
+
else {
|
|
99
|
+
this.context.ratings.uuid = _uuid;
|
|
100
|
+
}
|
|
101
|
+
// auto update only in admin
|
|
102
|
+
if (this.adapter.name === 'admin') {
|
|
103
|
+
this.context.ratingTimeout && clearTimeout(this.context.ratingTimeout);
|
|
104
|
+
this.context.ratingTimeout = setTimeout(() => {
|
|
105
|
+
this.context.ratingTimeout = null;
|
|
106
|
+
void this.updateRatings(uuid).then(() => this.adapter.log.info('Adapter rating updated'));
|
|
107
|
+
}, 24 * 3600000);
|
|
108
|
+
}
|
|
109
|
+
return this.context.ratings;
|
|
110
|
+
}
|
|
111
|
+
catch (error) {
|
|
112
|
+
this.adapter.log.warn(`Cannot update rating: ${error.response ? error.response.data : error.message || error.code}`);
|
|
113
|
+
return null;
|
|
114
|
+
}
|
|
115
|
+
}
|
|
116
|
+
async #readInstanceConfig(id, user, isTab, configs) {
|
|
117
|
+
let obj;
|
|
118
|
+
try {
|
|
119
|
+
obj = await this.adapter.getForeignObjectAsync(`system.adapter.${id}`, {
|
|
120
|
+
user,
|
|
121
|
+
});
|
|
122
|
+
}
|
|
123
|
+
catch {
|
|
124
|
+
// ignore
|
|
125
|
+
}
|
|
126
|
+
if (obj?.common) {
|
|
127
|
+
const instance = id.split('.').pop();
|
|
128
|
+
const config = {
|
|
129
|
+
id,
|
|
130
|
+
title: obj.common.titleLang || obj.common.title,
|
|
131
|
+
desc: obj.common.desc,
|
|
132
|
+
color: obj.common.color,
|
|
133
|
+
url: '',
|
|
134
|
+
icon: obj.common.icon,
|
|
135
|
+
materialize: obj.common.materialize,
|
|
136
|
+
// @ts-expect-error it is deprecated
|
|
137
|
+
jsonConfig: obj.common.jsonConfig,
|
|
138
|
+
version: obj.common.version,
|
|
139
|
+
};
|
|
140
|
+
if (obj.common.adminUI?.config === 'materialize') {
|
|
141
|
+
config.materialize = true;
|
|
142
|
+
}
|
|
143
|
+
else if (obj.common.adminUI?.config === 'json') {
|
|
144
|
+
config.jsonConfig = true;
|
|
145
|
+
}
|
|
146
|
+
config.url = `/adapter/${obj.common.name}/${isTab ? 'tab' : 'index'}${!isTab && config.materialize ? '_m' : ''}.html${instance ? `?${instance}` : ''}`;
|
|
147
|
+
if (isTab) {
|
|
148
|
+
config.tab = true;
|
|
149
|
+
}
|
|
150
|
+
else {
|
|
151
|
+
config.config = true;
|
|
152
|
+
}
|
|
153
|
+
configs.push(config);
|
|
154
|
+
}
|
|
155
|
+
}
|
|
156
|
+
_sendToHost = (host, command, message, callback) => {
|
|
157
|
+
const hash = `${host}_${command}`;
|
|
158
|
+
if (!message && _a.ALLOW_CACHE.includes(command) && this.cache[hash]) {
|
|
159
|
+
if (Date.now() - this.cache[hash].ts < 500) {
|
|
160
|
+
if (typeof callback === 'function') {
|
|
161
|
+
setImmediate(data => callback(data), JSON.parse(this.cache[hash].res));
|
|
162
|
+
}
|
|
163
|
+
return;
|
|
164
|
+
}
|
|
165
|
+
delete this.cache[hash];
|
|
166
|
+
}
|
|
167
|
+
try {
|
|
168
|
+
this.adapter.sendToHost(host, command, message, res => {
|
|
169
|
+
if (!message && _a.ALLOW_CACHE.includes(command)) {
|
|
170
|
+
this.cache[hash] = { ts: Date.now(), res: JSON.stringify(res) };
|
|
171
|
+
this.cacheGB =
|
|
172
|
+
this.cacheGB ||
|
|
173
|
+
setInterval(() => {
|
|
174
|
+
const commands = Object.keys(this.cache);
|
|
175
|
+
commands.forEach(cmd => {
|
|
176
|
+
if (Date.now() - this.cache[cmd].ts > 500) {
|
|
177
|
+
delete this.cache[cmd];
|
|
178
|
+
}
|
|
179
|
+
});
|
|
180
|
+
if (!commands.length && this.cacheGB) {
|
|
181
|
+
clearInterval(this.cacheGB);
|
|
182
|
+
this.cacheGB = null;
|
|
183
|
+
}
|
|
184
|
+
}, 2000);
|
|
185
|
+
}
|
|
186
|
+
if (typeof callback === 'function') {
|
|
187
|
+
setImmediate(() => callback(res));
|
|
188
|
+
}
|
|
189
|
+
});
|
|
190
|
+
}
|
|
191
|
+
catch (error) {
|
|
192
|
+
this.adapter.log.error(`[sendToHost] ERROR: ${error.toString()}`);
|
|
193
|
+
typeof callback === 'function' && setImmediate(() => callback({ error }));
|
|
194
|
+
}
|
|
195
|
+
};
|
|
196
|
+
// remove this function when js.controller 4.x are mainstream
|
|
197
|
+
async #readLicenses(login, password) {
|
|
198
|
+
const config = {
|
|
199
|
+
headers: { Authorization: `Basic ${Buffer.from(`${login}:${password}`).toString('base64')}` },
|
|
200
|
+
timeout: 4000,
|
|
201
|
+
validateStatus: (status) => status < 400,
|
|
202
|
+
};
|
|
203
|
+
try {
|
|
204
|
+
const response = await axios_1.default.get(`https://iobroker.net:3001/api/v1/licenses`, config);
|
|
205
|
+
if (response?.data?.length) {
|
|
206
|
+
const now = Date.now();
|
|
207
|
+
response.data = response.data.filter((license) => !license.validTill ||
|
|
208
|
+
license.validTill === '0000-00-00 00:00:00' ||
|
|
209
|
+
new Date(license.validTill).getTime() > now);
|
|
210
|
+
}
|
|
211
|
+
return response.data;
|
|
212
|
+
}
|
|
213
|
+
catch (error) {
|
|
214
|
+
if (error.response) {
|
|
215
|
+
throw new Error((error.response.data && error.response.data.error) || error.response.data || error.response.status);
|
|
216
|
+
}
|
|
217
|
+
if (error.request) {
|
|
218
|
+
throw new Error('no response');
|
|
219
|
+
}
|
|
220
|
+
throw error;
|
|
221
|
+
}
|
|
222
|
+
}
|
|
223
|
+
// remove this function when js.controller 4.x is mainstream
|
|
224
|
+
async #updateLicenses(login, password, options) {
|
|
225
|
+
// if login and password provided in the message, just try to read without saving it in system.licenses
|
|
226
|
+
if (login && password) {
|
|
227
|
+
return this.#readLicenses(login, password);
|
|
228
|
+
}
|
|
229
|
+
// get actual object
|
|
230
|
+
const systemLicenses = await this.adapter.getForeignObjectAsync('system.licenses', options);
|
|
231
|
+
// If password and login exist
|
|
232
|
+
if (systemLicenses?.native?.password && systemLicenses.native.login) {
|
|
233
|
+
// get the secret to decode the password
|
|
234
|
+
if (!this.secret) {
|
|
235
|
+
const systemConfig = await this.adapter.getForeignObjectAsync('system.config', options);
|
|
236
|
+
if (systemConfig?.native?.secret) {
|
|
237
|
+
this.secret = systemConfig.native.secret;
|
|
238
|
+
}
|
|
239
|
+
}
|
|
240
|
+
// decode the password
|
|
241
|
+
let password = '';
|
|
242
|
+
try {
|
|
243
|
+
password = this.adapter.decrypt(this.secret, systemLicenses.native.password);
|
|
244
|
+
}
|
|
245
|
+
catch {
|
|
246
|
+
throw new Error('Cannot decode password');
|
|
247
|
+
}
|
|
248
|
+
try {
|
|
249
|
+
const licenses = await this.#readLicenses(systemLicenses.native.login, password);
|
|
250
|
+
// save licenses to system.licenses and remember the time
|
|
251
|
+
// merge the information together
|
|
252
|
+
const oldLicenses = systemLicenses.native.licenses || [];
|
|
253
|
+
systemLicenses.native.licenses = licenses;
|
|
254
|
+
oldLicenses.forEach(oldLicense => {
|
|
255
|
+
if (oldLicense.usedBy) {
|
|
256
|
+
const newLicense = licenses.find(item => item.json === oldLicense.json);
|
|
257
|
+
if (newLicense) {
|
|
258
|
+
newLicense.usedBy = oldLicense.usedBy;
|
|
259
|
+
}
|
|
260
|
+
}
|
|
261
|
+
});
|
|
262
|
+
systemLicenses.native.readTime = new Date().toISOString();
|
|
263
|
+
// save only if an object changed
|
|
264
|
+
await this.adapter.setForeignObjectAsync('system.licenses', systemLicenses, options);
|
|
265
|
+
return licenses;
|
|
266
|
+
}
|
|
267
|
+
catch (error) {
|
|
268
|
+
// if password is invalid
|
|
269
|
+
if (error.message.includes('Authentication required') ||
|
|
270
|
+
error.message.includes('Cannot decode password')) {
|
|
271
|
+
// clear existing licenses if exist
|
|
272
|
+
if (systemLicenses?.native?.licenses?.length) {
|
|
273
|
+
systemLicenses.native.licenses = [];
|
|
274
|
+
systemLicenses.native.readTime = new Date().toISOString();
|
|
275
|
+
return this.adapter
|
|
276
|
+
.setForeignObjectAsync('system.licenses', systemLicenses, options)
|
|
277
|
+
.then(() => {
|
|
278
|
+
throw error;
|
|
279
|
+
});
|
|
280
|
+
}
|
|
281
|
+
throw error;
|
|
282
|
+
}
|
|
283
|
+
else {
|
|
284
|
+
throw error;
|
|
285
|
+
}
|
|
286
|
+
}
|
|
287
|
+
}
|
|
288
|
+
else {
|
|
289
|
+
// if password or login are empty => clear existing licenses if exist
|
|
290
|
+
if (systemLicenses?.native?.licenses?.length) {
|
|
291
|
+
systemLicenses.native.licenses = [];
|
|
292
|
+
systemLicenses.native.readTime = new Date().toISOString();
|
|
293
|
+
return this.adapter.setForeignObjectAsync('system.licenses', systemLicenses, options).then(() => {
|
|
294
|
+
throw new Error('No password or login');
|
|
295
|
+
});
|
|
296
|
+
}
|
|
297
|
+
throw new Error('No password or login');
|
|
298
|
+
}
|
|
299
|
+
}
|
|
300
|
+
disableEventThreshold() {
|
|
301
|
+
if (this.eventsThreshold.active) {
|
|
302
|
+
this.eventsThreshold.accidents = 0;
|
|
303
|
+
this.eventsThreshold.count = 0;
|
|
304
|
+
this.eventsThreshold.active = false;
|
|
305
|
+
this.eventsThreshold.timeActivated = 0;
|
|
306
|
+
this.adapter.log.info('Subscribe to all states again');
|
|
307
|
+
setTimeout(async () => {
|
|
308
|
+
this.onThresholdChanged && this.onThresholdChanged(false);
|
|
309
|
+
try {
|
|
310
|
+
await this.adapter.unsubscribeForeignStatesAsync('system.adapter.*');
|
|
311
|
+
}
|
|
312
|
+
catch (e) {
|
|
313
|
+
this.adapter.log.error(`Cannot unsubscribe "system.adapter.*": ${e.message}`);
|
|
314
|
+
}
|
|
315
|
+
for (const pattern of Object.keys(this.subscribes.stateChange)) {
|
|
316
|
+
try {
|
|
317
|
+
await this.adapter.subscribeForeignStatesAsync(pattern);
|
|
318
|
+
}
|
|
319
|
+
catch (e) {
|
|
320
|
+
this.adapter.log.error(`Cannot subscribe "${pattern}": ${e.message}`);
|
|
321
|
+
}
|
|
322
|
+
}
|
|
323
|
+
}, 50);
|
|
324
|
+
}
|
|
325
|
+
}
|
|
326
|
+
#enableEventThreshold() {
|
|
327
|
+
if (!this.eventsThreshold.active) {
|
|
328
|
+
this.eventsThreshold.active = true;
|
|
329
|
+
setTimeout(async () => {
|
|
330
|
+
this.adapter.log.info(`Unsubscribe from all states, except system's, because over ${this.eventsThreshold.repeatSeconds} seconds the number of events is over ${this.eventsThreshold.value} (in last second ${this.eventsThreshold.count})`);
|
|
331
|
+
this.eventsThreshold.timeActivated = Date.now();
|
|
332
|
+
this.onThresholdChanged && this.onThresholdChanged(true);
|
|
333
|
+
for (const pattern of Object.keys(this.subscribes.stateChange)) {
|
|
334
|
+
try {
|
|
335
|
+
await this.adapter.unsubscribeForeignStatesAsync(pattern);
|
|
336
|
+
}
|
|
337
|
+
catch (e) {
|
|
338
|
+
this.adapter.log.error(`Cannot unsubscribe "${pattern}": ${e.message}`);
|
|
339
|
+
}
|
|
340
|
+
}
|
|
341
|
+
try {
|
|
342
|
+
await this.adapter.subscribeForeignStatesAsync('system.adapter.*');
|
|
343
|
+
}
|
|
344
|
+
catch (e) {
|
|
345
|
+
this.adapter.log.error(`Cannot subscribe "system.adapter.*": ${e.message}`);
|
|
346
|
+
}
|
|
347
|
+
}, 100);
|
|
348
|
+
}
|
|
349
|
+
}
|
|
350
|
+
#addUser(user, password, options, callback) {
|
|
351
|
+
if (typeof options === 'function') {
|
|
352
|
+
callback = options;
|
|
353
|
+
options = null;
|
|
354
|
+
}
|
|
355
|
+
if (!user.match(/^[-.A-Za-züäößÖÄÜа-яА-Я@+$§0-9=?!&# ]+$/)) {
|
|
356
|
+
return socketCommands_1.SocketCommands._fixCallback(callback, 'Invalid characters in the name. Only following special characters are allowed: -@+$§=?!&# and letters');
|
|
357
|
+
}
|
|
358
|
+
try {
|
|
359
|
+
void this.adapter
|
|
360
|
+
.getForeignObjectAsync(`system.user.${user}`, options)
|
|
361
|
+
.then(async (obj) => {
|
|
362
|
+
if (obj) {
|
|
363
|
+
socketCommands_1.SocketCommands._fixCallback(callback, 'User yet exists');
|
|
364
|
+
}
|
|
365
|
+
else {
|
|
366
|
+
try {
|
|
367
|
+
await this.adapter.setForeignObject(`system.user.${user}`, {
|
|
368
|
+
type: 'user',
|
|
369
|
+
common: {
|
|
370
|
+
name: user,
|
|
371
|
+
enabled: true,
|
|
372
|
+
password: '',
|
|
373
|
+
},
|
|
374
|
+
native: {},
|
|
375
|
+
}, options);
|
|
376
|
+
try {
|
|
377
|
+
await this.adapter.setPassword(user, password, options || {}, callback);
|
|
378
|
+
}
|
|
379
|
+
catch (error) {
|
|
380
|
+
this.adapter.log.error(`[#addUser] cannot set password: ${error.toString()}`);
|
|
381
|
+
socketCommands_1.SocketCommands._fixCallback(callback, error);
|
|
382
|
+
}
|
|
383
|
+
}
|
|
384
|
+
catch (error) {
|
|
385
|
+
this.adapter.log.error(`[#addUser] cannot save user: ${error.toString()}`);
|
|
386
|
+
socketCommands_1.SocketCommands._fixCallback(callback, error);
|
|
387
|
+
}
|
|
388
|
+
}
|
|
389
|
+
});
|
|
390
|
+
}
|
|
391
|
+
catch (error) {
|
|
392
|
+
this.adapter.log.error(`[#addUser] cannot read user: ${error.toString()}`);
|
|
393
|
+
socketCommands_1.SocketCommands._fixCallback(callback, error);
|
|
394
|
+
}
|
|
395
|
+
}
|
|
396
|
+
#delUser(user, options, callback) {
|
|
397
|
+
try {
|
|
398
|
+
void this.adapter.getForeignObject(`system.user.${user}`, options, (error, obj) => {
|
|
399
|
+
if (error || !obj) {
|
|
400
|
+
socketCommands_1.SocketCommands._fixCallback(callback, 'User does not exist');
|
|
401
|
+
}
|
|
402
|
+
else {
|
|
403
|
+
if (obj.common.dontDelete) {
|
|
404
|
+
socketCommands_1.SocketCommands._fixCallback(callback, 'Cannot delete user, while is system user');
|
|
405
|
+
}
|
|
406
|
+
else {
|
|
407
|
+
try {
|
|
408
|
+
this.adapter.delForeignObject(`system.user.${user}`, options || {}, error =>
|
|
409
|
+
// Remove this user from all groups in the web client
|
|
410
|
+
socketCommands_1.SocketCommands._fixCallback(callback, error));
|
|
411
|
+
}
|
|
412
|
+
catch (error) {
|
|
413
|
+
this.adapter.log.error(`[#delUser] cannot delete user: ${error.toString()}`);
|
|
414
|
+
socketCommands_1.SocketCommands._fixCallback(callback, error);
|
|
415
|
+
}
|
|
416
|
+
}
|
|
417
|
+
}
|
|
418
|
+
});
|
|
419
|
+
}
|
|
420
|
+
catch (error) {
|
|
421
|
+
this.adapter.log.error(`[#delUser] cannot read user: ${error.toString()}`);
|
|
422
|
+
socketCommands_1.SocketCommands._fixCallback(callback, error);
|
|
423
|
+
}
|
|
424
|
+
}
|
|
425
|
+
#addGroup(group, desc, acl, options, callback) {
|
|
426
|
+
let name = group;
|
|
427
|
+
if (name && name.substring(0, 1) !== name.substring(0, 1).toUpperCase()) {
|
|
428
|
+
name = name.substring(0, 1).toUpperCase() + name.substring(1);
|
|
429
|
+
}
|
|
430
|
+
group = group.substring(0, 1).toLowerCase() + group.substring(1);
|
|
431
|
+
if (!group.match(/^[-.A-Za-züäößÖÄÜа-яА-Я@+$§0-9=?!&#_ ]+$/)) {
|
|
432
|
+
return socketCommands_1.SocketCommands._fixCallback(callback, 'Invalid characters in the group name. Only following special characters are allowed: -@+$§=?!&# and letters');
|
|
433
|
+
}
|
|
434
|
+
try {
|
|
435
|
+
void this.adapter.getForeignObject(`system.group.${group}`, options, (_error, obj) => {
|
|
436
|
+
if (obj) {
|
|
437
|
+
socketCommands_1.SocketCommands._fixCallback(callback, 'Group yet exists');
|
|
438
|
+
}
|
|
439
|
+
else {
|
|
440
|
+
obj = {
|
|
441
|
+
_id: `system.group.${group}`,
|
|
442
|
+
type: 'group',
|
|
443
|
+
common: {
|
|
444
|
+
name,
|
|
445
|
+
desc: desc || undefined,
|
|
446
|
+
members: [],
|
|
447
|
+
acl: acl || {
|
|
448
|
+
object: {
|
|
449
|
+
list: false,
|
|
450
|
+
read: false,
|
|
451
|
+
write: false,
|
|
452
|
+
create: false,
|
|
453
|
+
delete: false,
|
|
454
|
+
},
|
|
455
|
+
state: {
|
|
456
|
+
list: false,
|
|
457
|
+
read: false,
|
|
458
|
+
write: false,
|
|
459
|
+
create: false,
|
|
460
|
+
delete: false,
|
|
461
|
+
},
|
|
462
|
+
users: {
|
|
463
|
+
list: false,
|
|
464
|
+
read: false,
|
|
465
|
+
write: false,
|
|
466
|
+
create: false,
|
|
467
|
+
delete: false,
|
|
468
|
+
},
|
|
469
|
+
other: {
|
|
470
|
+
execute: false,
|
|
471
|
+
http: false,
|
|
472
|
+
sendto: false,
|
|
473
|
+
},
|
|
474
|
+
file: {
|
|
475
|
+
list: false,
|
|
476
|
+
read: false,
|
|
477
|
+
write: false,
|
|
478
|
+
create: false,
|
|
479
|
+
delete: false,
|
|
480
|
+
},
|
|
481
|
+
},
|
|
482
|
+
},
|
|
483
|
+
native: {},
|
|
484
|
+
};
|
|
485
|
+
try {
|
|
486
|
+
void this.adapter.setForeignObject(`system.group.${group}`, obj, options, error => socketCommands_1.SocketCommands._fixCallback(callback, error, obj));
|
|
487
|
+
}
|
|
488
|
+
catch (error) {
|
|
489
|
+
this.adapter.log.error(`[#addGroup] cannot write group: ${error.toString()}`);
|
|
490
|
+
socketCommands_1.SocketCommands._fixCallback(callback, error);
|
|
491
|
+
}
|
|
492
|
+
}
|
|
493
|
+
});
|
|
494
|
+
}
|
|
495
|
+
catch (error) {
|
|
496
|
+
this.adapter.log.error(`[#addGroup] cannot read group: ${error.toString()}`);
|
|
497
|
+
socketCommands_1.SocketCommands._fixCallback(callback, error);
|
|
498
|
+
}
|
|
499
|
+
}
|
|
500
|
+
#delGroup(group, options, callback) {
|
|
501
|
+
try {
|
|
502
|
+
void this.adapter.getForeignObject(`system.group.${group}`, options, (error, obj) => {
|
|
503
|
+
if (error || !obj) {
|
|
504
|
+
socketCommands_1.SocketCommands._fixCallback(callback, 'Group does not exist');
|
|
505
|
+
}
|
|
506
|
+
else {
|
|
507
|
+
if (obj.common.dontDelete) {
|
|
508
|
+
socketCommands_1.SocketCommands._fixCallback(callback, 'Cannot delete group, while is system group');
|
|
509
|
+
}
|
|
510
|
+
else {
|
|
511
|
+
try {
|
|
512
|
+
this.adapter.delForeignObject(`system.group.${group}`, options, error =>
|
|
513
|
+
// Remove this group from all users in the web client
|
|
514
|
+
socketCommands_1.SocketCommands._fixCallback(callback, error));
|
|
515
|
+
}
|
|
516
|
+
catch (error) {
|
|
517
|
+
this.adapter.log.error(`[#delGroup] cannot delete group: ${error.toString()}`);
|
|
518
|
+
socketCommands_1.SocketCommands._fixCallback(callback, error);
|
|
519
|
+
}
|
|
520
|
+
}
|
|
521
|
+
}
|
|
522
|
+
});
|
|
523
|
+
}
|
|
524
|
+
catch (error) {
|
|
525
|
+
this.adapter.log.error(`[#delGroup] cannot read group: ${error.toString()}`);
|
|
526
|
+
socketCommands_1.SocketCommands._fixCallback(callback, error);
|
|
527
|
+
}
|
|
528
|
+
}
|
|
529
|
+
static #checkObject(obj, options, flag) {
|
|
530
|
+
// read the rights of the object
|
|
531
|
+
if (!obj?.common || !obj.acl || flag === 'list') {
|
|
532
|
+
return true;
|
|
533
|
+
}
|
|
534
|
+
if (options.user !== 'system.user.admin' && !options.groups.includes('system.group.administrator')) {
|
|
535
|
+
if (obj.acl.owner !== options.user) {
|
|
536
|
+
// Check if the user is in the group
|
|
537
|
+
if (options.groups.includes(obj.acl.ownerGroup)) {
|
|
538
|
+
// Check group rights
|
|
539
|
+
if (!(obj.acl.object & (flag << 4))) {
|
|
540
|
+
return false;
|
|
541
|
+
}
|
|
542
|
+
}
|
|
543
|
+
else {
|
|
544
|
+
// everybody
|
|
545
|
+
if (!(obj.acl.object & flag)) {
|
|
546
|
+
return false;
|
|
547
|
+
}
|
|
548
|
+
}
|
|
549
|
+
}
|
|
550
|
+
else {
|
|
551
|
+
// Check group rights
|
|
552
|
+
if (!(obj.acl.object & (flag << 8))) {
|
|
553
|
+
return false;
|
|
554
|
+
}
|
|
555
|
+
}
|
|
556
|
+
}
|
|
557
|
+
return true;
|
|
558
|
+
}
|
|
559
|
+
#getAllObjects(socket, callback) {
|
|
560
|
+
if (typeof callback !== 'function') {
|
|
561
|
+
return this.adapter.log.warn('[#getAllObjects] Invalid callback');
|
|
562
|
+
}
|
|
563
|
+
if (this._checkPermissions(socket, 'getObjects', callback)) {
|
|
564
|
+
if (this.objects) {
|
|
565
|
+
if (socket._acl &&
|
|
566
|
+
socket._acl?.user !== 'system.user.admin' &&
|
|
567
|
+
!socket._acl.groups.includes('system.group.administrator')) {
|
|
568
|
+
const result = {};
|
|
569
|
+
for (const id in this.objects) {
|
|
570
|
+
if (Object.prototype.hasOwnProperty.call(this.objects, id) &&
|
|
571
|
+
_a.#checkObject(this.objects[id], socket._acl, ACL_READ /* 'read' */)) {
|
|
572
|
+
result[id] = this.objects[id];
|
|
573
|
+
}
|
|
574
|
+
}
|
|
575
|
+
callback(null, result);
|
|
576
|
+
}
|
|
577
|
+
else {
|
|
578
|
+
callback(null, this.objects);
|
|
579
|
+
}
|
|
580
|
+
}
|
|
581
|
+
else {
|
|
582
|
+
try {
|
|
583
|
+
this.adapter.getObjectList({ include_docs: true }, { user: socket._acl?.user }, (_error, res) => {
|
|
584
|
+
this.adapter.log.info('received all objects');
|
|
585
|
+
const rows = res?.rows || [];
|
|
586
|
+
const objects = {};
|
|
587
|
+
if (socket._acl &&
|
|
588
|
+
socket._acl?.user !== 'system.user.admin' &&
|
|
589
|
+
!socket._acl.groups.includes('system.group.administrator')) {
|
|
590
|
+
for (let i = 0; i < rows.length; i++) {
|
|
591
|
+
if (_a.#checkObject(rows[i].doc, socket._acl, ACL_READ)) {
|
|
592
|
+
objects[rows[i].doc._id] = rows[i].doc;
|
|
593
|
+
}
|
|
594
|
+
}
|
|
595
|
+
callback(null, objects);
|
|
596
|
+
}
|
|
597
|
+
else {
|
|
598
|
+
for (let j = 0; j < rows.length; j++) {
|
|
599
|
+
objects[rows[j].doc._id] = rows[j].doc;
|
|
600
|
+
}
|
|
601
|
+
callback(null, objects);
|
|
602
|
+
}
|
|
603
|
+
});
|
|
604
|
+
}
|
|
605
|
+
catch (error) {
|
|
606
|
+
this.adapter.log.error(`[#getAllObjects] ERROR: ${error.toString()}`);
|
|
607
|
+
socketCommands_1.SocketCommands._fixCallback(callback, error);
|
|
608
|
+
}
|
|
609
|
+
}
|
|
610
|
+
}
|
|
611
|
+
}
|
|
612
|
+
_initCommandsUser() {
|
|
613
|
+
/**
|
|
614
|
+
* #DOCUMENTATION users
|
|
615
|
+
* Add a new user.
|
|
616
|
+
*
|
|
617
|
+
* @param socket - WebSocket client instance
|
|
618
|
+
* @param user - User name, e.g., `benjamin`
|
|
619
|
+
* @param pass - User password
|
|
620
|
+
* @param callback - Callback function `(error: string | null) => void`
|
|
621
|
+
*/
|
|
622
|
+
this.commands.addUser = (socket, user, pass, callback) => {
|
|
623
|
+
if (this._checkPermissions(socket, 'addUser', callback, user)) {
|
|
624
|
+
this.#addUser(user, pass, { user: socket._acl?.user || '' }, (error, ...args) => socketCommands_1.SocketCommands._fixCallback(callback, error, ...args));
|
|
625
|
+
}
|
|
626
|
+
};
|
|
627
|
+
/**
|
|
628
|
+
* #DOCUMENTATION users
|
|
629
|
+
* Delete an existing user. Admin cannot be deleted.
|
|
630
|
+
*
|
|
631
|
+
* @param socket - WebSocket client instance
|
|
632
|
+
* @param user - User name, e.g., `benjamin`
|
|
633
|
+
* @param callback - Callback function `(error: string | null) => void`
|
|
634
|
+
*/
|
|
635
|
+
this.commands.delUser = (socket, user, callback) => {
|
|
636
|
+
if (this._checkPermissions(socket, 'delUser', callback, user)) {
|
|
637
|
+
this.#delUser(user, { user: socket._acl?.user || '' }, (error, ...args) => socketCommands_1.SocketCommands._fixCallback(callback, error, ...args));
|
|
638
|
+
}
|
|
639
|
+
};
|
|
640
|
+
/**
|
|
641
|
+
* #DOCUMENTATION users
|
|
642
|
+
* Add a new group.
|
|
643
|
+
*
|
|
644
|
+
* @param socket - WebSocket client instance
|
|
645
|
+
* @param group - Group name, e.g., `users`
|
|
646
|
+
* @param desc - Optional description
|
|
647
|
+
* @param acl - Optional access control list object, e.g., `{"object":{"list":true,"read":true,"write":false,"delete":false},"state":{"list":true,"read":true,"write":true,"create":true,"delete":false},"users":{"list":true,"read":true,"write":false,"create":false,"delete":false},"other":{"execute":false,"http":true,"sendto":false},"file":{"list":true,"read":true,"write":false,"create":false,"delete":false}}`
|
|
648
|
+
* @param callback - Callback function `(error: string | null) => void`
|
|
649
|
+
*/
|
|
650
|
+
this.commands.addGroup = (socket, group, desc, acl, callback) => {
|
|
651
|
+
if (this._checkPermissions(socket, 'addGroup', callback, group)) {
|
|
652
|
+
this.#addGroup(group, desc, acl, { user: socket._acl?.user || '' }, (error, ...args) => socketCommands_1.SocketCommands._fixCallback(callback, error, ...args));
|
|
653
|
+
}
|
|
654
|
+
};
|
|
655
|
+
/**
|
|
656
|
+
* #DOCUMENTATION users
|
|
657
|
+
* Delete an existing group. Administrator group cannot be deleted.
|
|
658
|
+
*
|
|
659
|
+
* @param socket - WebSocket client instance
|
|
660
|
+
* @param group - Group name, e.g., `users`
|
|
661
|
+
* @param callback - Callback function `(error: string | null) => void`
|
|
662
|
+
*/
|
|
663
|
+
this.commands.delGroup = (socket, group, callback) => {
|
|
664
|
+
if (this._checkPermissions(socket, 'delGroup', callback, group)) {
|
|
665
|
+
this.#delGroup(group, { user: socket._acl?.user || '' }, (error, ...args) => socketCommands_1.SocketCommands._fixCallback(callback, error, ...args));
|
|
666
|
+
}
|
|
667
|
+
};
|
|
668
|
+
/**
|
|
669
|
+
* #DOCUMENTATION users
|
|
670
|
+
* Change user password.
|
|
671
|
+
*
|
|
672
|
+
* @param socket - WebSocket client instance
|
|
673
|
+
* @param user - User name, e.g., `benjamin`
|
|
674
|
+
* @param pass - New password
|
|
675
|
+
* @param callback - Callback function `(error: string | null) => void`
|
|
676
|
+
*/
|
|
677
|
+
this.commands.changePassword = (socket, user, pass, callback) => {
|
|
678
|
+
if (user === socket._acl?.user || this._checkPermissions(socket, 'changePassword', callback, user)) {
|
|
679
|
+
try {
|
|
680
|
+
void this.adapter.setPassword(user, pass, { user: socket._acl?.user }, (error, ...args) => socketCommands_1.SocketCommands._fixCallback(callback, error, ...args));
|
|
681
|
+
}
|
|
682
|
+
catch (error) {
|
|
683
|
+
this.adapter.log.error(`[_changePassword] ERROR: ${error.toString()}`);
|
|
684
|
+
socketCommands_1.SocketCommands._fixCallback(callback, error);
|
|
685
|
+
}
|
|
686
|
+
}
|
|
687
|
+
};
|
|
688
|
+
}
|
|
689
|
+
_initCommandsAdmin() {
|
|
690
|
+
/**
|
|
691
|
+
* #DOCUMENTATION admin
|
|
692
|
+
* Read the host object by IP address.
|
|
693
|
+
*
|
|
694
|
+
* @param socket - WebSocket client instance
|
|
695
|
+
* @param ip - IP address, e.g., `192.168.1.1`. IPv4 or IPv6
|
|
696
|
+
* @param callback - Callback function `(ip: string, obj: ioBroker.HostObject | null) => void`
|
|
697
|
+
*/
|
|
698
|
+
this.commands.getHostByIp = (socket, ip, callback) => {
|
|
699
|
+
if (typeof callback !== 'function') {
|
|
700
|
+
return this.adapter.log.warn('[getHostByIp] Invalid callback');
|
|
701
|
+
}
|
|
702
|
+
if (this._checkPermissions(socket, 'getHostByIp', callback, ip)) {
|
|
703
|
+
try {
|
|
704
|
+
this.adapter.getObjectView('system', 'host', {}, { user: socket._acl?.user }, (error, data) => {
|
|
705
|
+
if (data?.rows?.length) {
|
|
706
|
+
for (let i = 0; i < data.rows.length; i++) {
|
|
707
|
+
const obj = data.rows[i].value;
|
|
708
|
+
// if we requested specific name
|
|
709
|
+
if (obj.common.hostname === ip) {
|
|
710
|
+
return callback(ip, obj);
|
|
711
|
+
}
|
|
712
|
+
if (obj.native.hardware?.networkInterfaces) {
|
|
713
|
+
// try to find this IP in the list
|
|
714
|
+
const net = obj.native.hardware.networkInterfaces;
|
|
715
|
+
for (const eth in net) {
|
|
716
|
+
if (!Object.prototype.hasOwnProperty.call(net, eth) || !net[eth]) {
|
|
717
|
+
continue;
|
|
718
|
+
}
|
|
719
|
+
for (let j = 0; j < net[eth].length; j++) {
|
|
720
|
+
if (net[eth][j].address === ip) {
|
|
721
|
+
return callback(ip, obj);
|
|
722
|
+
}
|
|
723
|
+
}
|
|
724
|
+
}
|
|
725
|
+
}
|
|
726
|
+
}
|
|
727
|
+
}
|
|
728
|
+
callback(ip, null);
|
|
729
|
+
});
|
|
730
|
+
}
|
|
731
|
+
catch (error) {
|
|
732
|
+
this.adapter.log.error(`[_changePassword] ERROR: ${error.toString()}`);
|
|
733
|
+
socketCommands_1.SocketCommands._fixCallback(callback, error);
|
|
734
|
+
}
|
|
735
|
+
}
|
|
736
|
+
};
|
|
737
|
+
/**
|
|
738
|
+
* #DOCUMENTATION admin
|
|
739
|
+
* Activate or deactivate logging events. Events will be sent to the socket as `log` event. Adapter must have `common.logTransporter = true`.
|
|
740
|
+
*
|
|
741
|
+
* @param socket - WebSocket client instance
|
|
742
|
+
* @param isEnabled - Is logging enabled
|
|
743
|
+
* @param callback - Callback function `(error: string | null) => void`
|
|
744
|
+
*/
|
|
745
|
+
this.commands.requireLog = (socket, isEnabled, callback) => {
|
|
746
|
+
if (this._checkPermissions(socket, 'setObject', callback)) {
|
|
747
|
+
if (isEnabled) {
|
|
748
|
+
this.subscribe(socket, 'log', 'dummy');
|
|
749
|
+
}
|
|
750
|
+
else {
|
|
751
|
+
this.unsubscribe(socket, 'log', 'dummy');
|
|
752
|
+
}
|
|
753
|
+
this.adapter.log.level === 'debug' && this._showSubscribes(socket, 'log');
|
|
754
|
+
typeof callback === 'function' && setImmediate(callback, null);
|
|
755
|
+
}
|
|
756
|
+
};
|
|
757
|
+
/**
|
|
758
|
+
* #DOCUMENTATION admin
|
|
759
|
+
* Get the log files from the given host.
|
|
760
|
+
*
|
|
761
|
+
* @param socket - WebSocket client instance
|
|
762
|
+
* @param host - Host ID, e.g., `system.host.raspberrypi`
|
|
763
|
+
* @param callback - Callback function `(error: string | null, list?: { fileName: string; size: number }[]) => void`
|
|
764
|
+
*/
|
|
765
|
+
this.commands.readLogs = (socket, host, callback) => {
|
|
766
|
+
if (this._checkPermissions(socket, 'readLogs', callback)) {
|
|
767
|
+
let timeout = setTimeout(() => {
|
|
768
|
+
if (timeout) {
|
|
769
|
+
let result = { list: [] };
|
|
770
|
+
// deliver the file list
|
|
771
|
+
try {
|
|
772
|
+
const config = this.adapter.systemConfig;
|
|
773
|
+
// detect file log
|
|
774
|
+
if (config?.log?.transport) {
|
|
775
|
+
for (const transport in config.log.transport) {
|
|
776
|
+
if (Object.prototype.hasOwnProperty.call(config.log.transport, transport) &&
|
|
777
|
+
config.log.transport[transport].type === 'file') {
|
|
778
|
+
let fileName = config.log.transport[transport].filename || 'log/';
|
|
779
|
+
const parts = fileName.replace(/\\/g, '/').split('/');
|
|
780
|
+
parts.pop();
|
|
781
|
+
fileName = parts.join('/');
|
|
782
|
+
if (fileName[0] !== '/' && !fileName.match(/^\W:/)) {
|
|
783
|
+
const _filename = (0, node_path_1.normalize)(`${__dirname}/../../../`) + fileName;
|
|
784
|
+
if (!(0, node_fs_1.existsSync)(_filename)) {
|
|
785
|
+
fileName = (0, node_path_1.normalize)(`${__dirname}/../../`) + fileName;
|
|
786
|
+
}
|
|
787
|
+
else {
|
|
788
|
+
fileName = _filename;
|
|
789
|
+
}
|
|
790
|
+
}
|
|
791
|
+
if ((0, node_fs_1.existsSync)(fileName)) {
|
|
792
|
+
const files = (0, node_fs_1.readdirSync)(fileName);
|
|
793
|
+
for (let f = 0; f < files.length; f++) {
|
|
794
|
+
try {
|
|
795
|
+
if (!files[f].endsWith('-audit.json')) {
|
|
796
|
+
const stat = (0, node_fs_1.lstatSync)(`${fileName}/${files[f]}`);
|
|
797
|
+
if (!stat.isDirectory()) {
|
|
798
|
+
result.list?.push({
|
|
799
|
+
fileName: `log/${transport}/${files[f]}`,
|
|
800
|
+
size: stat.size,
|
|
801
|
+
});
|
|
802
|
+
}
|
|
803
|
+
}
|
|
804
|
+
}
|
|
805
|
+
catch {
|
|
806
|
+
// push unchecked
|
|
807
|
+
// result.list.push('log/' + transport + '/' + files[f]);
|
|
808
|
+
this.adapter.log.error(`Cannot check file: ${fileName}/${files[f]}`);
|
|
809
|
+
}
|
|
810
|
+
}
|
|
811
|
+
}
|
|
812
|
+
}
|
|
813
|
+
}
|
|
814
|
+
}
|
|
815
|
+
else {
|
|
816
|
+
result = { error: 'no file loggers' };
|
|
817
|
+
}
|
|
818
|
+
}
|
|
819
|
+
catch (error) {
|
|
820
|
+
this.adapter.log.error(`Cannot read logs: ${error}`);
|
|
821
|
+
result = { error };
|
|
822
|
+
}
|
|
823
|
+
socketCommands_1.SocketCommands._fixCallback(callback, result.error, result.list);
|
|
824
|
+
}
|
|
825
|
+
}, 500);
|
|
826
|
+
this._sendToHost(host, 'getLogFiles', null, (result) => {
|
|
827
|
+
if (timeout) {
|
|
828
|
+
clearTimeout(timeout);
|
|
829
|
+
timeout = null;
|
|
830
|
+
}
|
|
831
|
+
socketCommands_1.SocketCommands._fixCallback(callback, result.error, result.list);
|
|
832
|
+
});
|
|
833
|
+
}
|
|
834
|
+
};
|
|
835
|
+
/**
|
|
836
|
+
* #DOCUMENTATION states
|
|
837
|
+
* Delete a state. The corresponding object will be deleted too.
|
|
838
|
+
*
|
|
839
|
+
* @param socket - WebSocket client instance
|
|
840
|
+
* @param id - State ID
|
|
841
|
+
* @param callback - Callback function `(error: string | null) => void`
|
|
842
|
+
*/
|
|
843
|
+
this.commands.delState = (socket, id, callback) => {
|
|
844
|
+
if (this._checkPermissions(socket, 'delState', callback, id)) {
|
|
845
|
+
// clear cache
|
|
846
|
+
if (this.states?.[id]) {
|
|
847
|
+
delete this.states[id];
|
|
848
|
+
}
|
|
849
|
+
try {
|
|
850
|
+
this.adapter.delForeignState(id, { user: socket._acl?.user }, (error, ...args) => socketCommands_1.SocketCommands._fixCallback(callback, error, ...args));
|
|
851
|
+
}
|
|
852
|
+
catch (error) {
|
|
853
|
+
this.adapter.log.error(`[delState] ERROR: ${error.toString()}`);
|
|
854
|
+
socketCommands_1.SocketCommands._fixCallback(callback, error);
|
|
855
|
+
}
|
|
856
|
+
}
|
|
857
|
+
};
|
|
858
|
+
/**
|
|
859
|
+
* #DOCUMENTATION admin
|
|
860
|
+
* Execute the shell command on host/controller.
|
|
861
|
+
* Following response commands are expected: `cmdStdout`, `cmdStderr`, `cmdExit`.
|
|
862
|
+
*
|
|
863
|
+
* @param socket - WebSocket client instance
|
|
864
|
+
* @param host - Host name, e.g., `system.host.raspberrypi`
|
|
865
|
+
* @param id - Session ID, e.g., `Date.now()`. This session ID will come in events `cmdStdout`, `cmdStderr`, `cmdExit`
|
|
866
|
+
* @param cmd - Command to execute
|
|
867
|
+
* @param callback - Callback function `(error: string | null) => void`
|
|
868
|
+
*/
|
|
869
|
+
this.commands.cmdExec = (socket, host, id, cmd, callback) => {
|
|
870
|
+
if (id === undefined) {
|
|
871
|
+
this.adapter.log.error(`cmdExec no session ID for "${cmd}"`);
|
|
872
|
+
socketCommands_1.SocketCommands._fixCallback(callback, 'no session ID');
|
|
873
|
+
}
|
|
874
|
+
else if (this._checkPermissions(socket, 'cmdExec', callback, cmd)) {
|
|
875
|
+
this.adapter.log.debug(`cmdExec on ${host}(${id}): ${cmd}`);
|
|
876
|
+
// remember socket for this ID.
|
|
877
|
+
this.cmdSessions[id] = { socket };
|
|
878
|
+
try {
|
|
879
|
+
this.adapter.sendToHost(host, 'cmdExec', { data: cmd, id });
|
|
880
|
+
socketCommands_1.SocketCommands._fixCallback(callback, null);
|
|
881
|
+
}
|
|
882
|
+
catch (error) {
|
|
883
|
+
this.adapter.log.error(`[cmdExec] ERROR: ${error.toString()}`);
|
|
884
|
+
socketCommands_1.SocketCommands._fixCallback(callback, error);
|
|
885
|
+
}
|
|
886
|
+
}
|
|
887
|
+
};
|
|
888
|
+
/**
|
|
889
|
+
* #DOCUMENTATION admin
|
|
890
|
+
* Enable or disable the event threshold. Used only for admin to limit the number of events to the front-end.
|
|
891
|
+
*
|
|
892
|
+
* @param _socket - WebSocket client instance
|
|
893
|
+
* @param isActive - If true, then events will be limited
|
|
894
|
+
*/
|
|
895
|
+
this.commands.eventsThreshold = (_socket, isActive) => {
|
|
896
|
+
if (!isActive) {
|
|
897
|
+
this.disableEventThreshold();
|
|
898
|
+
}
|
|
899
|
+
else {
|
|
900
|
+
this.#enableEventThreshold();
|
|
901
|
+
}
|
|
902
|
+
};
|
|
903
|
+
/**
|
|
904
|
+
* #DOCUMENTATION admin
|
|
905
|
+
* Get the ratings of adapters.
|
|
906
|
+
*
|
|
907
|
+
* @param _socket - WebSocket client instance
|
|
908
|
+
* @param update - If true, the ratings will be read from the central server, if false from the local cache
|
|
909
|
+
* @param callback - Callback function `(error: string | null, ratings?: Ratings) => void`
|
|
910
|
+
*/
|
|
911
|
+
this.commands.getRatings = (_socket, update, callback) => {
|
|
912
|
+
if (typeof update === 'function') {
|
|
913
|
+
callback = update;
|
|
914
|
+
update = false;
|
|
915
|
+
}
|
|
916
|
+
if (update || !this.context.ratings) {
|
|
917
|
+
void this.updateRatings().then(() => socketCommands_1.SocketCommands._fixCallback(callback, null, this.context.ratings));
|
|
918
|
+
}
|
|
919
|
+
else {
|
|
920
|
+
socketCommands_1.SocketCommands._fixCallback(callback, null, this.context.ratings);
|
|
921
|
+
}
|
|
922
|
+
};
|
|
923
|
+
/**
|
|
924
|
+
* #DOCUMENTATION admin
|
|
925
|
+
* Get the current instance name, like "admin.0"
|
|
926
|
+
*
|
|
927
|
+
* @param _socket - WebSocket client instance
|
|
928
|
+
* @param callback - Callback function `(error: string | null, namespace?: string) => void`
|
|
929
|
+
*/
|
|
930
|
+
this.commands.getCurrentInstance = (_socket, callback) => {
|
|
931
|
+
socketCommands_1.SocketCommands._fixCallback(callback, null, this.adapter.namespace);
|
|
932
|
+
};
|
|
933
|
+
/**
|
|
934
|
+
* #DOCUMENTATION admin
|
|
935
|
+
* Decrypts text with the system secret key.
|
|
936
|
+
*
|
|
937
|
+
* @param socket - WebSocket client instance
|
|
938
|
+
* @param encryptedText - Encrypted text
|
|
939
|
+
* @param callback - Callback function `(error: string | null, decryptedText?: string) => void`
|
|
940
|
+
*/
|
|
941
|
+
this.commands.decrypt = (socket, encryptedText, callback) => {
|
|
942
|
+
if (this.secret) {
|
|
943
|
+
socketCommands_1.SocketCommands._fixCallback(callback, null, this.adapter.decrypt(this.secret, encryptedText));
|
|
944
|
+
}
|
|
945
|
+
else {
|
|
946
|
+
try {
|
|
947
|
+
void this.adapter.getForeignObject('system.config', { user: socket._acl?.user }, (error, obj) => {
|
|
948
|
+
if (obj && obj.native && obj.native.secret) {
|
|
949
|
+
this.secret = obj.native.secret;
|
|
950
|
+
socketCommands_1.SocketCommands._fixCallback(callback, null, this.adapter.decrypt(this.secret, encryptedText));
|
|
951
|
+
}
|
|
952
|
+
else {
|
|
953
|
+
this.adapter.log.error(`No system.config found: ${error}`);
|
|
954
|
+
socketCommands_1.SocketCommands._fixCallback(callback, error);
|
|
955
|
+
}
|
|
956
|
+
});
|
|
957
|
+
}
|
|
958
|
+
catch (error) {
|
|
959
|
+
this.adapter.log.error(`Cannot decrypt: ${error}`);
|
|
960
|
+
socketCommands_1.SocketCommands._fixCallback(callback, error);
|
|
961
|
+
}
|
|
962
|
+
}
|
|
963
|
+
};
|
|
964
|
+
/**
|
|
965
|
+
* #DOCUMENTATION admin
|
|
966
|
+
* Encrypts text with the system secret key.
|
|
967
|
+
*
|
|
968
|
+
* @param socket - WebSocket client instance
|
|
969
|
+
* @param plainText - Plain text to encrypt
|
|
970
|
+
* @param callback - Callback function `(error: string | null, encryptedText?: string) => void`
|
|
971
|
+
*/
|
|
972
|
+
this.commands.encrypt = (socket, plainText, callback) => {
|
|
973
|
+
if (this.secret) {
|
|
974
|
+
socketCommands_1.SocketCommands._fixCallback(callback, null, this.adapter.encrypt(this.secret, plainText));
|
|
975
|
+
}
|
|
976
|
+
else {
|
|
977
|
+
void this.adapter.getForeignObject('system.config', { user: socket._acl?.user }, (error, obj) => {
|
|
978
|
+
if (obj && obj.native && obj.native.secret) {
|
|
979
|
+
this.secret = obj.native.secret;
|
|
980
|
+
try {
|
|
981
|
+
const encrypted = this.adapter.encrypt(this.secret, plainText);
|
|
982
|
+
socketCommands_1.SocketCommands._fixCallback(callback, null, encrypted);
|
|
983
|
+
}
|
|
984
|
+
catch (error) {
|
|
985
|
+
this.adapter.log.error(`Cannot encrypt: ${error}`);
|
|
986
|
+
socketCommands_1.SocketCommands._fixCallback(callback, error);
|
|
987
|
+
}
|
|
988
|
+
}
|
|
989
|
+
else {
|
|
990
|
+
this.adapter.log.error(`No system.config found: ${error}`);
|
|
991
|
+
socketCommands_1.SocketCommands._fixCallback(callback, error);
|
|
992
|
+
}
|
|
993
|
+
});
|
|
994
|
+
}
|
|
995
|
+
};
|
|
996
|
+
/**
|
|
997
|
+
* #DOCUMENTATION admin
|
|
998
|
+
* Get if the admin has easy mode enabled.
|
|
999
|
+
*
|
|
1000
|
+
* @param _socket - WebSocket client instance
|
|
1001
|
+
* @param callback - Callback function `(error: string | null, isEasyModeStrict?: boolean) => void`
|
|
1002
|
+
*/
|
|
1003
|
+
this.commands.getIsEasyModeStrict = (_socket, callback) => {
|
|
1004
|
+
socketCommands_1.SocketCommands._fixCallback(callback, null, this.adapter.config.accessLimit);
|
|
1005
|
+
};
|
|
1006
|
+
/**
|
|
1007
|
+
* #DOCUMENTATION admin
|
|
1008
|
+
* Get easy mode configuration.
|
|
1009
|
+
*
|
|
1010
|
+
* @param socket - WebSocket client instance
|
|
1011
|
+
* @param callback - Callback function `(error: string | null, easyModeConfig?: { strict: boolean; configs: InstanceConfig[] }) => void`
|
|
1012
|
+
*/
|
|
1013
|
+
this.commands.getEasyMode = (socket, callback) => {
|
|
1014
|
+
if (this._checkPermissions(socket, 'getObject', callback)) {
|
|
1015
|
+
let user;
|
|
1016
|
+
if (this.adapter.config.auth) {
|
|
1017
|
+
user = socket._acl?.user || '';
|
|
1018
|
+
}
|
|
1019
|
+
else {
|
|
1020
|
+
user = this.adapter.config.defaultUser || socket._acl?.user || '';
|
|
1021
|
+
}
|
|
1022
|
+
if (!user.startsWith('system.user.')) {
|
|
1023
|
+
user = `system.user.${user}`;
|
|
1024
|
+
}
|
|
1025
|
+
if (this.adapter.config.accessLimit) {
|
|
1026
|
+
const configs = [];
|
|
1027
|
+
const promises = [];
|
|
1028
|
+
this.adapter.config.accessAllowedConfigs?.forEach(id => promises.push(this.#readInstanceConfig(id, user, false, configs)));
|
|
1029
|
+
this.adapter.config.accessAllowedTabs?.forEach(id => promises.push(this.#readInstanceConfig(id, user, true, configs)));
|
|
1030
|
+
void Promise.all(promises).then(() => {
|
|
1031
|
+
socketCommands_1.SocketCommands._fixCallback(callback, null, {
|
|
1032
|
+
strict: true,
|
|
1033
|
+
configs,
|
|
1034
|
+
});
|
|
1035
|
+
});
|
|
1036
|
+
}
|
|
1037
|
+
else {
|
|
1038
|
+
this.adapter.getObjectView('system', 'instance', { startkey: 'system.adapter.', endkey: 'system.adapter.\u9999' }, { user }, (error, doc) => {
|
|
1039
|
+
const configs = [];
|
|
1040
|
+
const promises = [];
|
|
1041
|
+
if (!error && doc?.rows?.length) {
|
|
1042
|
+
for (let i = 0; i < doc.rows.length; i++) {
|
|
1043
|
+
const obj = doc.rows[i].value;
|
|
1044
|
+
if (obj.common.noConfig && !obj.common.adminTab) {
|
|
1045
|
+
continue;
|
|
1046
|
+
}
|
|
1047
|
+
if (!obj.common.enabled) {
|
|
1048
|
+
continue;
|
|
1049
|
+
}
|
|
1050
|
+
if (!obj.common.noConfig) {
|
|
1051
|
+
promises.push(this.#readInstanceConfig(obj._id.substring('system.adapter.'.length), user, false, configs));
|
|
1052
|
+
}
|
|
1053
|
+
}
|
|
1054
|
+
}
|
|
1055
|
+
void Promise.all(promises).then(() => socketCommands_1.SocketCommands._fixCallback(callback, null, {
|
|
1056
|
+
strict: false,
|
|
1057
|
+
configs,
|
|
1058
|
+
}));
|
|
1059
|
+
});
|
|
1060
|
+
}
|
|
1061
|
+
}
|
|
1062
|
+
};
|
|
1063
|
+
/**
|
|
1064
|
+
* #DOCUMENTATION admin
|
|
1065
|
+
* Get all adapter as objects.
|
|
1066
|
+
*
|
|
1067
|
+
* @param socket - WebSocket client instance
|
|
1068
|
+
* @param adapterName - Optional adapter name
|
|
1069
|
+
* @param callback - Callback function `(error: string | null, results?: ioBroker.Object[]) => void`
|
|
1070
|
+
*/
|
|
1071
|
+
this.commands.getAdapters = (socket, adapterName, callback) => {
|
|
1072
|
+
if (typeof callback === 'function' && this._checkPermissions(socket, 'getObject', callback)) {
|
|
1073
|
+
this.adapter.getObjectView('system', 'adapter', {
|
|
1074
|
+
startkey: `system.adapter.${adapterName || ''}`,
|
|
1075
|
+
endkey: `system.adapter.${adapterName || '\u9999'}`,
|
|
1076
|
+
}, { user: socket._acl?.user }, (error, doc) => {
|
|
1077
|
+
if (error) {
|
|
1078
|
+
callback(error);
|
|
1079
|
+
}
|
|
1080
|
+
else {
|
|
1081
|
+
callback(null, doc?.rows
|
|
1082
|
+
.filter(obj => obj && (!adapterName || obj.value.common?.name === this.adapterName))
|
|
1083
|
+
.map(item => {
|
|
1084
|
+
const obj = item.value;
|
|
1085
|
+
if (obj.common) {
|
|
1086
|
+
delete obj.common.news;
|
|
1087
|
+
// @ts-expect-error to save the memory
|
|
1088
|
+
delete obj.native;
|
|
1089
|
+
}
|
|
1090
|
+
this.fixAdminUI(obj);
|
|
1091
|
+
return obj;
|
|
1092
|
+
}));
|
|
1093
|
+
}
|
|
1094
|
+
});
|
|
1095
|
+
}
|
|
1096
|
+
};
|
|
1097
|
+
/**
|
|
1098
|
+
* #DOCUMENTATION admin
|
|
1099
|
+
* Read software licenses (vis, knx, ...) from ioBroker.net cloud for given user
|
|
1100
|
+
*
|
|
1101
|
+
* @param socket - WebSocket client instance
|
|
1102
|
+
* @param login - Cloud login
|
|
1103
|
+
* @param password - Cloud password
|
|
1104
|
+
* @param callback - Callback function `(error: string | null, results?: License[]) => void`
|
|
1105
|
+
*/
|
|
1106
|
+
this.commands.updateLicenses = (socket, login, password, callback) => {
|
|
1107
|
+
if (this._checkPermissions(socket, 'setObject', callback, login, password)) {
|
|
1108
|
+
if (this.adapter.supportsFeature('CONTROLLER_LICENSE_MANAGER')) {
|
|
1109
|
+
let timeout = setTimeout(() => {
|
|
1110
|
+
if (timeout) {
|
|
1111
|
+
timeout = null;
|
|
1112
|
+
socketCommands_1.SocketCommands._fixCallback(callback, 'updateLicenses timeout');
|
|
1113
|
+
}
|
|
1114
|
+
}, 7000);
|
|
1115
|
+
if (!this.adapter.common) {
|
|
1116
|
+
throw new Error('"common" is not defined in adapter!');
|
|
1117
|
+
}
|
|
1118
|
+
this._sendToHost(this.adapter.common.host, 'updateLicenses', { login, password }, result => {
|
|
1119
|
+
if (timeout) {
|
|
1120
|
+
clearTimeout(timeout);
|
|
1121
|
+
timeout = null;
|
|
1122
|
+
socketCommands_1.SocketCommands._fixCallback(callback, result.error, result?.result);
|
|
1123
|
+
}
|
|
1124
|
+
});
|
|
1125
|
+
}
|
|
1126
|
+
else {
|
|
1127
|
+
// remove this branch when js-controller 4.x is mainstream
|
|
1128
|
+
this.#updateLicenses(login, password, { user: socket._acl?.user || '' })
|
|
1129
|
+
.then(licenses => socketCommands_1.SocketCommands._fixCallback(callback, null, licenses))
|
|
1130
|
+
.catch(error => socketCommands_1.SocketCommands._fixCallback(callback, error));
|
|
1131
|
+
}
|
|
1132
|
+
}
|
|
1133
|
+
};
|
|
1134
|
+
/**
|
|
1135
|
+
* #DOCUMENTATION admin
|
|
1136
|
+
* Get all instances in a compact form to save bandwidth.
|
|
1137
|
+
*
|
|
1138
|
+
* @param socket - WebSocket client instance
|
|
1139
|
+
* @param callback - Callback function `(error: string | null, results?: Record<string, { adminTab: boolean; name: string; icon: string; enabled: boolean }>) => void`
|
|
1140
|
+
*/
|
|
1141
|
+
this.commands.getCompactInstances = (socket, callback) => {
|
|
1142
|
+
if (typeof callback === 'function') {
|
|
1143
|
+
if (this._checkPermissions(socket, 'getObject', callback)) {
|
|
1144
|
+
this.adapter.getObjectView('system', 'instance', { startkey: `system.adapter.`, endkey: `system.adapter.\u9999` }, { user: socket._acl?.user }, (error, doc) => {
|
|
1145
|
+
if (error) {
|
|
1146
|
+
socketCommands_1.SocketCommands._fixCallback(callback, error);
|
|
1147
|
+
}
|
|
1148
|
+
else {
|
|
1149
|
+
// calculate
|
|
1150
|
+
const result = {};
|
|
1151
|
+
doc?.rows.forEach(item => {
|
|
1152
|
+
const obj = item.value;
|
|
1153
|
+
result[item.id] = {
|
|
1154
|
+
adminTab: obj.common.adminTab,
|
|
1155
|
+
name: obj.common.name,
|
|
1156
|
+
icon: obj.common.icon,
|
|
1157
|
+
enabled: obj.common.enabled,
|
|
1158
|
+
version: obj.common.version,
|
|
1159
|
+
};
|
|
1160
|
+
});
|
|
1161
|
+
socketCommands_1.SocketCommands._fixCallback(callback, null, result);
|
|
1162
|
+
}
|
|
1163
|
+
});
|
|
1164
|
+
}
|
|
1165
|
+
}
|
|
1166
|
+
};
|
|
1167
|
+
/**
|
|
1168
|
+
* #DOCUMENTATION admin
|
|
1169
|
+
* Get all adapters in a compact form to save bandwidth.
|
|
1170
|
+
*
|
|
1171
|
+
* @param socket - WebSocket client instance
|
|
1172
|
+
* @param callback - Callback function `(error: string | null, results?: Record<string, { icon: string; v: string; iv: string }>) => void`
|
|
1173
|
+
*/
|
|
1174
|
+
this.commands.getCompactAdapters = (socket, callback) => {
|
|
1175
|
+
if (typeof callback === 'function') {
|
|
1176
|
+
if (this._checkPermissions(socket, 'getObject', callback)) {
|
|
1177
|
+
this.adapter.getObjectView('system', 'adapter', { startkey: `system.adapter.`, endkey: `system.adapter.\u9999` }, { user: socket._acl?.user }, (error, doc) => {
|
|
1178
|
+
if (error) {
|
|
1179
|
+
socketCommands_1.SocketCommands._fixCallback(callback, error);
|
|
1180
|
+
}
|
|
1181
|
+
else {
|
|
1182
|
+
// calculate
|
|
1183
|
+
const result = {};
|
|
1184
|
+
doc?.rows.forEach(item => {
|
|
1185
|
+
const obj = item.value;
|
|
1186
|
+
if (obj?.common?.name) {
|
|
1187
|
+
result[obj.common.name] = { icon: obj.common.icon, v: obj.common.version };
|
|
1188
|
+
if (obj.common.ignoreVersion) {
|
|
1189
|
+
result[obj.common.name].iv = obj.common.ignoreVersion;
|
|
1190
|
+
}
|
|
1191
|
+
}
|
|
1192
|
+
});
|
|
1193
|
+
socketCommands_1.SocketCommands._fixCallback(callback, null, result);
|
|
1194
|
+
}
|
|
1195
|
+
});
|
|
1196
|
+
}
|
|
1197
|
+
}
|
|
1198
|
+
};
|
|
1199
|
+
/**
|
|
1200
|
+
* #DOCUMENTATION admin
|
|
1201
|
+
* Get all installed adapters in a compact form to save bandwidth.
|
|
1202
|
+
*
|
|
1203
|
+
* @param socket - WebSocket client instance
|
|
1204
|
+
* @param host - Host name, e.g., `system.host.raspberrypi`
|
|
1205
|
+
* @param callback - Callback function `(error: string | null, results?: Record<string, { version: string }>) => void`
|
|
1206
|
+
*/
|
|
1207
|
+
this.commands.getCompactInstalled = (socket, host, callback) => {
|
|
1208
|
+
if (typeof callback === 'function') {
|
|
1209
|
+
if (this._checkPermissions(socket, 'sendToHost', callback)) {
|
|
1210
|
+
this._sendToHost(host, 'getInstalled', null, (data) => {
|
|
1211
|
+
const castData = data;
|
|
1212
|
+
const result = {};
|
|
1213
|
+
Object.keys(castData).forEach(name => {
|
|
1214
|
+
if (name !== 'hosts') {
|
|
1215
|
+
result[name] = { version: castData[name].version };
|
|
1216
|
+
}
|
|
1217
|
+
});
|
|
1218
|
+
callback(result);
|
|
1219
|
+
});
|
|
1220
|
+
}
|
|
1221
|
+
}
|
|
1222
|
+
};
|
|
1223
|
+
/**
|
|
1224
|
+
* #DOCUMENTATION admin
|
|
1225
|
+
* Get the system configuration in a compact form to save bandwidth.
|
|
1226
|
+
*
|
|
1227
|
+
* @param socket - WebSocket client instance
|
|
1228
|
+
* @param callback - Callback function `(error: string | null, systemConfig?: { common: any; native?: { secret: string } }) => void`
|
|
1229
|
+
*/
|
|
1230
|
+
this.commands.getCompactSystemConfig = (socket, callback) => {
|
|
1231
|
+
if (this._checkPermissions(socket, 'getObject', callback)) {
|
|
1232
|
+
void this.adapter.getForeignObject('system.config', { user: socket._acl?.user }, (error, obj) => {
|
|
1233
|
+
obj ||= {};
|
|
1234
|
+
const secret = obj?.native?.secret;
|
|
1235
|
+
// @ts-expect-error to save the memory
|
|
1236
|
+
delete obj.native;
|
|
1237
|
+
if (secret) {
|
|
1238
|
+
obj.native = { secret };
|
|
1239
|
+
}
|
|
1240
|
+
socketCommands_1.SocketCommands._fixCallback(callback, error, obj);
|
|
1241
|
+
});
|
|
1242
|
+
}
|
|
1243
|
+
};
|
|
1244
|
+
/**
|
|
1245
|
+
* #DOCUMENTATION admin
|
|
1246
|
+
* Get system repositories in a compact form to save bandwidth.
|
|
1247
|
+
*
|
|
1248
|
+
* @param socket - WebSocket client instance
|
|
1249
|
+
* @param callback - Callback function `(error: string | null, systemRepositories?: { common: any; native?: { repositories: Record<string, { json: { _repoInfo: any } } } } }) => void`
|
|
1250
|
+
*/
|
|
1251
|
+
this.commands.getCompactSystemRepositories = (socket, callback) => {
|
|
1252
|
+
if (this._checkPermissions(socket, 'getObject', callback)) {
|
|
1253
|
+
void this.adapter.getForeignObject('system.repositories', { user: socket._acl?.user }, (error, obj) => {
|
|
1254
|
+
obj &&
|
|
1255
|
+
obj.native &&
|
|
1256
|
+
obj.native.repositories &&
|
|
1257
|
+
Object.keys(obj.native.repositories).forEach(name => {
|
|
1258
|
+
if (obj.native.repositories[name].json) {
|
|
1259
|
+
// limit information to _repoInfo
|
|
1260
|
+
obj.native.repositories[name].json = {
|
|
1261
|
+
_repoInfo: obj.native.repositories[name].json._repoInfo,
|
|
1262
|
+
};
|
|
1263
|
+
}
|
|
1264
|
+
});
|
|
1265
|
+
socketCommands_1.SocketCommands._fixCallback(callback, error, obj);
|
|
1266
|
+
});
|
|
1267
|
+
}
|
|
1268
|
+
};
|
|
1269
|
+
/**
|
|
1270
|
+
* #DOCUMENTATION admin
|
|
1271
|
+
* Get the repository in a compact form to save bandwidth.
|
|
1272
|
+
*
|
|
1273
|
+
* @param socket - WebSocket client instance
|
|
1274
|
+
* @param host - Host name, e.g., `system.host.raspberrypi`
|
|
1275
|
+
* @param callback - Callback function `(error: string | null, results?: Record<string, { version: string; icon?: string }>) => void`
|
|
1276
|
+
*/
|
|
1277
|
+
this.commands.getCompactRepository = (socket, host, callback) => {
|
|
1278
|
+
if (this._checkPermissions(socket, 'sendToHost', callback)) {
|
|
1279
|
+
this._sendToHost(host, 'getRepository', null, (data) => {
|
|
1280
|
+
// Extract only the version and icon
|
|
1281
|
+
const castData = data;
|
|
1282
|
+
const result = {};
|
|
1283
|
+
if (castData) {
|
|
1284
|
+
Object.keys(castData).forEach(name => (result[name] = {
|
|
1285
|
+
version: castData[name].version,
|
|
1286
|
+
icon: castData[name].extIcon,
|
|
1287
|
+
}));
|
|
1288
|
+
}
|
|
1289
|
+
callback(result);
|
|
1290
|
+
});
|
|
1291
|
+
}
|
|
1292
|
+
};
|
|
1293
|
+
/**
|
|
1294
|
+
* #DOCUMENTATION admin
|
|
1295
|
+
* Get all hosts in a compact form to save bandwidth.
|
|
1296
|
+
*
|
|
1297
|
+
* @param socket - WebSocket client instance
|
|
1298
|
+
* @param callback - Callback function `(error: string | null, results?: Record<string, { common: { name: string; icon: string; color: string; installedVersion: string }; native: { hardware: { networkInterfaces: any[] } } }>) => void`
|
|
1299
|
+
*/
|
|
1300
|
+
this.commands.getCompactHosts = (socket, callback) => {
|
|
1301
|
+
if (this._checkPermissions(socket, 'getObject', callback)) {
|
|
1302
|
+
this.adapter.getObjectView('system', 'host', { startkey: 'system.host.', endkey: 'system.host.\u9999' }, { user: socket._acl?.user }, (error, doc) => {
|
|
1303
|
+
if (error) {
|
|
1304
|
+
socketCommands_1.SocketCommands._fixCallback(callback, error);
|
|
1305
|
+
}
|
|
1306
|
+
else {
|
|
1307
|
+
const result = [];
|
|
1308
|
+
doc?.rows.map(item => {
|
|
1309
|
+
const host = item.value;
|
|
1310
|
+
if (host) {
|
|
1311
|
+
host.common ||= {};
|
|
1312
|
+
result.push({
|
|
1313
|
+
_id: host._id,
|
|
1314
|
+
common: {
|
|
1315
|
+
name: host.common.name,
|
|
1316
|
+
icon: host.common.icon,
|
|
1317
|
+
color: host.common.color,
|
|
1318
|
+
installedVersion: host.common.installedVersion,
|
|
1319
|
+
},
|
|
1320
|
+
native: {
|
|
1321
|
+
hardware: {
|
|
1322
|
+
networkInterfaces: host.native?.hardware?.networkInterfaces || undefined,
|
|
1323
|
+
},
|
|
1324
|
+
},
|
|
1325
|
+
});
|
|
1326
|
+
}
|
|
1327
|
+
});
|
|
1328
|
+
socketCommands_1.SocketCommands._fixCallback(callback, null, result);
|
|
1329
|
+
}
|
|
1330
|
+
});
|
|
1331
|
+
}
|
|
1332
|
+
};
|
|
1333
|
+
}
|
|
1334
|
+
_initCommandsCommon() {
|
|
1335
|
+
super._initCommandsCommon();
|
|
1336
|
+
this._initCommandsAdmin();
|
|
1337
|
+
this._initCommandsUser();
|
|
1338
|
+
}
|
|
1339
|
+
_initCommandsFiles() {
|
|
1340
|
+
super._initCommandsFiles();
|
|
1341
|
+
/**
|
|
1342
|
+
* #DOCUMENTATION files
|
|
1343
|
+
* Write the file into ioBroker DB as base64 string.
|
|
1344
|
+
*
|
|
1345
|
+
* @param socket - WebSocket client instance
|
|
1346
|
+
* @param adapter - Instance name, e.g., `vis.0`
|
|
1347
|
+
* @param fileName - File name, e.g., `main/vis-views.json`
|
|
1348
|
+
* @param data64 - File content as base64 string
|
|
1349
|
+
* @param options - Optional settings, e.g., `{mode: 0x0644}`
|
|
1350
|
+
* @param callback - Callback function `(error: string | null) => void`
|
|
1351
|
+
*/
|
|
1352
|
+
this.commands.writeFile = (socket, adapter, fileName, data64, options, callback) => {
|
|
1353
|
+
if (this._checkPermissions(socket, 'writeFile', callback, fileName)) {
|
|
1354
|
+
let _options;
|
|
1355
|
+
if (typeof options === 'function') {
|
|
1356
|
+
callback = options;
|
|
1357
|
+
_options = { user: socket._acl?.user };
|
|
1358
|
+
}
|
|
1359
|
+
else if (!options || options.mode === undefined) {
|
|
1360
|
+
_options = { user: socket._acl?.user };
|
|
1361
|
+
}
|
|
1362
|
+
else {
|
|
1363
|
+
_options = { user: socket._acl?.user, mode: options.mode };
|
|
1364
|
+
}
|
|
1365
|
+
try {
|
|
1366
|
+
const buffer = Buffer.from(data64, 'base64');
|
|
1367
|
+
this.adapter.writeFile(adapter, fileName, buffer, _options, (error, ...args) => socketCommands_1.SocketCommands._fixCallback(callback, error, ...args));
|
|
1368
|
+
}
|
|
1369
|
+
catch (error) {
|
|
1370
|
+
this.adapter.log.error(`[writeFile] Cannot convert data: ${error.toString()}`);
|
|
1371
|
+
if (callback) {
|
|
1372
|
+
callback(`Cannot convert data: ${error.toString()}`);
|
|
1373
|
+
}
|
|
1374
|
+
}
|
|
1375
|
+
}
|
|
1376
|
+
};
|
|
1377
|
+
}
|
|
1378
|
+
_initCommandsObjects() {
|
|
1379
|
+
super._initCommandsObjects();
|
|
1380
|
+
/**
|
|
1381
|
+
* #DOCUMENTATION objects
|
|
1382
|
+
* Read absolutely all objects.
|
|
1383
|
+
*
|
|
1384
|
+
* @param socket - WebSocket client instance
|
|
1385
|
+
* @param callback - Callback function `(error: string | null, objects?: Record<string, ioBroker.Object>) => void`
|
|
1386
|
+
*/
|
|
1387
|
+
this.commands.getAllObjects = (socket, callback) => {
|
|
1388
|
+
return this.#getAllObjects(socket, callback);
|
|
1389
|
+
};
|
|
1390
|
+
/**
|
|
1391
|
+
* #DOCUMENTATION objects
|
|
1392
|
+
* Read absolutely all objects. Same as `getAllObjects`.
|
|
1393
|
+
*
|
|
1394
|
+
* @param socket - WebSocket client instance
|
|
1395
|
+
* @param list - optional list of IDs.
|
|
1396
|
+
* @param callback - Callback function `(error: string | null, objects?: Record<string, ioBroker.Object>) => void`
|
|
1397
|
+
*/
|
|
1398
|
+
this.commands.getObjects = (socket, list, callback) => {
|
|
1399
|
+
if (typeof list === 'function') {
|
|
1400
|
+
callback = list;
|
|
1401
|
+
list = null;
|
|
1402
|
+
}
|
|
1403
|
+
if (typeof callback !== 'function') {
|
|
1404
|
+
this.adapter.log.warn('[getObjects] Invalid callback');
|
|
1405
|
+
}
|
|
1406
|
+
else if (list && !list.length) {
|
|
1407
|
+
socketCommands_1.SocketCommands._fixCallback(callback, null, {});
|
|
1408
|
+
}
|
|
1409
|
+
else if (list?.length) {
|
|
1410
|
+
if (this._checkPermissions(socket, 'getObject', callback)) {
|
|
1411
|
+
try {
|
|
1412
|
+
this.adapter.getForeignObjects(list, { user: socket._acl?.user }, (error, objs) => socketCommands_1.SocketCommands._fixCallback(callback, error, objs));
|
|
1413
|
+
}
|
|
1414
|
+
catch (error) {
|
|
1415
|
+
this.adapter.log.error(`[getObjects] ERROR: ${error.toString()}`);
|
|
1416
|
+
socketCommands_1.SocketCommands._fixCallback(callback, error);
|
|
1417
|
+
}
|
|
1418
|
+
}
|
|
1419
|
+
}
|
|
1420
|
+
else {
|
|
1421
|
+
this.#getAllObjects(socket, callback);
|
|
1422
|
+
}
|
|
1423
|
+
};
|
|
1424
|
+
/**
|
|
1425
|
+
* #DOCUMENTATION objects
|
|
1426
|
+
* Extend the existing object.
|
|
1427
|
+
*
|
|
1428
|
+
* @param socket - WebSocket client instance
|
|
1429
|
+
* @param id - Object ID
|
|
1430
|
+
* @param obj - New parts of the object, e.g., `{common: {name: 'new name'}}`
|
|
1431
|
+
* @param callback - Callback function `(error: string | null) => void`
|
|
1432
|
+
*/
|
|
1433
|
+
this.commands.extendObject = (socket, id, obj, callback) => {
|
|
1434
|
+
if (this._checkPermissions(socket, 'extendObject', callback, id)) {
|
|
1435
|
+
try {
|
|
1436
|
+
this.adapter.extendForeignObject(id, obj, { user: socket._acl?.user }, (error, ...args) => socketCommands_1.SocketCommands._fixCallback(callback, error, ...args));
|
|
1437
|
+
}
|
|
1438
|
+
catch (error) {
|
|
1439
|
+
this.adapter.log.error(`[extendObject] ERROR: ${error}`);
|
|
1440
|
+
socketCommands_1.SocketCommands._fixCallback(callback, error);
|
|
1441
|
+
}
|
|
1442
|
+
}
|
|
1443
|
+
};
|
|
1444
|
+
/**
|
|
1445
|
+
* #DOCUMENTATION objects
|
|
1446
|
+
* Read objects by pattern.
|
|
1447
|
+
*
|
|
1448
|
+
* @param socket - WebSocket client instance
|
|
1449
|
+
* @param pattern - Pattern like `system.adapter.admin.0.*`
|
|
1450
|
+
* @param type - Type of objects to delete, like `state`, `channel`, `device`, `host`, `adapter`. Default - `state`
|
|
1451
|
+
* @param callback - Callback function `(error: string | null, objects?: Record<string, ioBroker.Object>) => void`
|
|
1452
|
+
*/
|
|
1453
|
+
this.commands.getForeignObjects = (socket, pattern, type, callback) => {
|
|
1454
|
+
if (this._checkPermissions(socket, 'getObjects', callback)) {
|
|
1455
|
+
if (typeof type === 'function') {
|
|
1456
|
+
callback = type;
|
|
1457
|
+
type = undefined;
|
|
1458
|
+
}
|
|
1459
|
+
if (typeof callback === 'function') {
|
|
1460
|
+
if (type) {
|
|
1461
|
+
try {
|
|
1462
|
+
this.adapter.getForeignObjects(pattern, type, { user: socket._acl?.user }, (error, ...args) => socketCommands_1.SocketCommands._fixCallback(callback, error, ...args));
|
|
1463
|
+
}
|
|
1464
|
+
catch (error) {
|
|
1465
|
+
this.adapter.log.error(`[extendObject] ERROR: ${error}`);
|
|
1466
|
+
socketCommands_1.SocketCommands._fixCallback(callback, error);
|
|
1467
|
+
}
|
|
1468
|
+
}
|
|
1469
|
+
else {
|
|
1470
|
+
try {
|
|
1471
|
+
this.adapter.getForeignObjects(pattern, { user: socket._acl?.user }, (error, ...args) => socketCommands_1.SocketCommands._fixCallback(callback, error, ...args));
|
|
1472
|
+
}
|
|
1473
|
+
catch (error) {
|
|
1474
|
+
this.adapter.log.error(`[extendObject] ERROR: ${error}`);
|
|
1475
|
+
socketCommands_1.SocketCommands._fixCallback(callback, error);
|
|
1476
|
+
}
|
|
1477
|
+
}
|
|
1478
|
+
}
|
|
1479
|
+
else {
|
|
1480
|
+
this.adapter.log.warn('[getObjects] Invalid callback');
|
|
1481
|
+
}
|
|
1482
|
+
}
|
|
1483
|
+
};
|
|
1484
|
+
/**
|
|
1485
|
+
* #DOCUMENTATION objects
|
|
1486
|
+
* Delete an object or objects recursively.
|
|
1487
|
+
* Objects with `dontDelete` cannot be deleted.
|
|
1488
|
+
*
|
|
1489
|
+
* @param socket - WebSocket client instance
|
|
1490
|
+
* @param id - Object ID, like 'adapterName.0.channel'
|
|
1491
|
+
* @param options - Options for deletion.
|
|
1492
|
+
* @param options.recursive - Delete all sub objects in this branch
|
|
1493
|
+
* @param callback - Callback function `(error: string | null) => void`
|
|
1494
|
+
*/
|
|
1495
|
+
this.commands.delObject = (socket, id, options, callback) => {
|
|
1496
|
+
if (this._checkPermissions(socket, 'delObject', callback, id)) {
|
|
1497
|
+
let _options;
|
|
1498
|
+
if (typeof options === 'function') {
|
|
1499
|
+
callback = options;
|
|
1500
|
+
_options = { user: socket._acl?.user };
|
|
1501
|
+
}
|
|
1502
|
+
else if (options?.recursive) {
|
|
1503
|
+
_options = { user: socket._acl?.user, recursive: true };
|
|
1504
|
+
}
|
|
1505
|
+
else {
|
|
1506
|
+
_options = { user: socket._acl?.user };
|
|
1507
|
+
}
|
|
1508
|
+
try {
|
|
1509
|
+
// options.recursive = true; // the only difference between delObject and delObjects is this line
|
|
1510
|
+
this.adapter.delForeignObject(id, _options, (error, ...args) => socketCommands_1.SocketCommands._fixCallback(callback, error, ...args));
|
|
1511
|
+
}
|
|
1512
|
+
catch (error) {
|
|
1513
|
+
this.adapter.log.error(`[delObject] ERROR: ${error}`);
|
|
1514
|
+
socketCommands_1.SocketCommands._fixCallback(callback, error);
|
|
1515
|
+
}
|
|
1516
|
+
}
|
|
1517
|
+
};
|
|
1518
|
+
/**
|
|
1519
|
+
* #DOCUMENTATION objects
|
|
1520
|
+
* Delete an object or objects recursively.
|
|
1521
|
+
* Objects with `dontDelete` cannot be deleted.
|
|
1522
|
+
* Same as `delObject` but with `recursive: true`.
|
|
1523
|
+
*
|
|
1524
|
+
* @param socket - WebSocket client instance
|
|
1525
|
+
* @param id - Object ID, like 'adapterName.0.channel'
|
|
1526
|
+
* @param options - Options for deletion.
|
|
1527
|
+
* @param options.recursive - Delete all sub objects in this branch
|
|
1528
|
+
* @param callback - Callback function `(error: string | null) => void`
|
|
1529
|
+
*/
|
|
1530
|
+
this.commands.delObjects = (socket, id, options, callback) => {
|
|
1531
|
+
if (this._checkPermissions(socket, 'delObject', callback, id)) {
|
|
1532
|
+
let _options;
|
|
1533
|
+
if (typeof options === 'function') {
|
|
1534
|
+
callback = options;
|
|
1535
|
+
_options = { user: socket._acl?.user, recursive: true };
|
|
1536
|
+
}
|
|
1537
|
+
else if (options?.recursive) {
|
|
1538
|
+
_options = { user: socket._acl?.user, recursive: true };
|
|
1539
|
+
}
|
|
1540
|
+
else {
|
|
1541
|
+
_options = { user: socket._acl?.user, recursive: true };
|
|
1542
|
+
}
|
|
1543
|
+
try {
|
|
1544
|
+
this.adapter.delForeignObject(id, _options, (error, ...args) => socketCommands_1.SocketCommands._fixCallback(callback, error, ...args));
|
|
1545
|
+
}
|
|
1546
|
+
catch (error) {
|
|
1547
|
+
this.adapter.log.error(`[delObjects] ERROR: ${error}`);
|
|
1548
|
+
socketCommands_1.SocketCommands._fixCallback(callback, error);
|
|
1549
|
+
}
|
|
1550
|
+
}
|
|
1551
|
+
};
|
|
1552
|
+
}
|
|
1553
|
+
stateChange(id, state) {
|
|
1554
|
+
if (this.states) {
|
|
1555
|
+
if (!state) {
|
|
1556
|
+
if (this.states[id]) {
|
|
1557
|
+
delete this.states[id];
|
|
1558
|
+
}
|
|
1559
|
+
}
|
|
1560
|
+
else {
|
|
1561
|
+
this.states[id] = state;
|
|
1562
|
+
}
|
|
1563
|
+
}
|
|
1564
|
+
if (!this.eventsThreshold.active) {
|
|
1565
|
+
this.eventsThreshold.count++;
|
|
1566
|
+
}
|
|
1567
|
+
}
|
|
1568
|
+
sendCommand(obj) {
|
|
1569
|
+
if (obj.message && this.cmdSessions[obj.message.id]) {
|
|
1570
|
+
if (obj.command === 'cmdExit') {
|
|
1571
|
+
delete this.cmdSessions[obj.message.id];
|
|
1572
|
+
}
|
|
1573
|
+
return true;
|
|
1574
|
+
}
|
|
1575
|
+
}
|
|
1576
|
+
destroy() {
|
|
1577
|
+
this.thresholdInterval && clearInterval(this.thresholdInterval);
|
|
1578
|
+
this.thresholdInterval = null;
|
|
1579
|
+
this.cacheGB && clearInterval(this.cacheGB);
|
|
1580
|
+
this.cacheGB = null;
|
|
1581
|
+
super.destroy();
|
|
1582
|
+
}
|
|
1583
|
+
}
|
|
1584
|
+
exports.SocketCommandsAdmin = SocketCommandsAdmin;
|
|
1585
|
+
_a = SocketCommandsAdmin;
|
|
1586
|
+
//# sourceMappingURL=socketCommandsAdmin.js.map
|