@iobroker/js-controller-adapter 7.2.3 → 7.2.5
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/cjs/lib/_Types.d.ts +3 -0
- package/build/cjs/lib/adapter/adapter.d.ts +13 -2
- package/build/cjs/lib/adapter/adapter.js +95 -19
- package/build/cjs/lib/adapter/adapter.js.map +2 -2
- package/build/esm/lib/_Types.d.ts +3 -0
- package/build/esm/lib/_Types.d.ts.map +1 -1
- package/build/esm/lib/adapter/adapter.d.ts +13 -2
- package/build/esm/lib/adapter/adapter.d.ts.map +1 -1
- package/build/esm/lib/adapter/adapter.js +109 -27
- package/build/esm/lib/adapter/adapter.js.map +1 -1
- package/build/tsconfig.build.tsbuildinfo +1 -1
- package/package.json +12 -12
|
@@ -388,6 +388,8 @@ export interface InternalAddChannelToEnumOptions {
|
|
|
388
388
|
export interface SendToOptions {
|
|
389
389
|
/** Method throws or calls error cb, if callback not called in time, works for single targets only */
|
|
390
390
|
timeout?: number;
|
|
391
|
+
/** The user on whose behalf the message is sent */
|
|
392
|
+
user?: ioBroker.ObjectIDs.User;
|
|
391
393
|
}
|
|
392
394
|
export interface InternalSendToOptions {
|
|
393
395
|
instanceName: string;
|
|
@@ -411,6 +413,7 @@ export interface InternalSendToHostOptions {
|
|
|
411
413
|
command: string;
|
|
412
414
|
message: any;
|
|
413
415
|
callback?: ioBroker.MessageCallback | ioBroker.MessageCallbackInfo;
|
|
416
|
+
options?: SendToOptions;
|
|
414
417
|
}
|
|
415
418
|
export interface InternalGetStateOptions {
|
|
416
419
|
id: string;
|
|
@@ -162,7 +162,7 @@ export interface AdapterClass {
|
|
|
162
162
|
* Sends a message to a specific host or all hosts.
|
|
163
163
|
*/
|
|
164
164
|
sendToHostAsync(hostName: string, message: ioBroker.MessagePayload): Promise<ioBroker.Message | undefined>;
|
|
165
|
-
sendToHostAsync(hostName: string, command: string, message: ioBroker.MessagePayload): Promise<ioBroker.Message | undefined>;
|
|
165
|
+
sendToHostAsync(hostName: string, command: string, message: ioBroker.MessagePayload, options?: SendToOptions): Promise<ioBroker.Message | undefined>;
|
|
166
166
|
/**
|
|
167
167
|
* Sends a message to a specific instance or all instances of some specific adapter.
|
|
168
168
|
*/
|
|
@@ -338,6 +338,8 @@ export declare class AdapterClass extends EventEmitter {
|
|
|
338
338
|
namespace: `${string}.${number}`;
|
|
339
339
|
name: string;
|
|
340
340
|
private _systemSecret?;
|
|
341
|
+
/** Attributes of `encryptedNative`: their values in `this.config` are decrypted on start */
|
|
342
|
+
private _decryptedNativeAttributes;
|
|
341
343
|
/** Whether the adapter has already terminated */
|
|
342
344
|
private terminated;
|
|
343
345
|
/** The cache of usernames */
|
|
@@ -491,6 +493,15 @@ export declare class AdapterClass extends EventEmitter {
|
|
|
491
493
|
*
|
|
492
494
|
*/
|
|
493
495
|
disable(): ioBroker.SetObjectPromise;
|
|
496
|
+
/**
|
|
497
|
+
* Reads a parameter which is stored encrypted and returns its decrypted value.
|
|
498
|
+
*
|
|
499
|
+
* An attribute listed in `encryptedNative` is already decrypted in `this.config` when the adapter
|
|
500
|
+
* starts, and is returned as it is.
|
|
501
|
+
*
|
|
502
|
+
* @param attribute the config attribute to decrypt
|
|
503
|
+
* @param callback return result
|
|
504
|
+
*/
|
|
494
505
|
getEncryptedConfig(attribute: string, callback?: GetEncryptedConfigCallback): Promise<string | string[] | void>;
|
|
495
506
|
private _getEncryptedConfig;
|
|
496
507
|
/**
|
|
@@ -711,7 +722,7 @@ export declare class AdapterClass extends EventEmitter {
|
|
|
711
722
|
sendTo(instanceName: string, command: string, message: any, callback?: ioBroker.MessageCallback | ioBroker.MessageCallbackInfo, options?: SendToOptions): void;
|
|
712
723
|
private _sendTo;
|
|
713
724
|
sendToHost(hostName: string | null, message: any, callback?: ioBroker.MessageCallback | ioBroker.MessageCallbackInfo): void;
|
|
714
|
-
sendToHost(hostName: string | null, command: string, message: any, callback?: ioBroker.MessageCallback | ioBroker.MessageCallbackInfo): void;
|
|
725
|
+
sendToHost(hostName: string | null, command: string, message: any, callback?: ioBroker.MessageCallback | ioBroker.MessageCallbackInfo, options?: SendToOptions): void;
|
|
715
726
|
private _sendToHost;
|
|
716
727
|
sendToUI(options: SendToUserInterfaceClientOptions): Promise<void>;
|
|
717
728
|
registerNotification<Scope extends keyof ioBroker.NotificationScopes>(scope: Scope, category: ioBroker.NotificationScopes[Scope] | null, message: string, options?: NotificationOptions): Promise<void>;
|
|
@@ -110,6 +110,8 @@ class AdapterClass extends import_node_events.EventEmitter {
|
|
|
110
110
|
namespace;
|
|
111
111
|
name;
|
|
112
112
|
_systemSecret;
|
|
113
|
+
/** Attributes of `encryptedNative`: their values in `this.config` are decrypted on start */
|
|
114
|
+
_decryptedNativeAttributes = new Array();
|
|
113
115
|
/** Whether the adapter has already terminated */
|
|
114
116
|
terminated = false;
|
|
115
117
|
/** The cache of usernames */
|
|
@@ -361,7 +363,6 @@ class AdapterClass extends import_node_events.EventEmitter {
|
|
|
361
363
|
this.readFileAsync = import_js_controller_common.tools.promisify(this.readFile, this, ["file", "mimeType"]);
|
|
362
364
|
this.writeFileAsync = import_js_controller_common.tools.promisify(this.writeFile, this);
|
|
363
365
|
this.fileExistsAsync = import_js_controller_common.tools.promisify(this.fileExists, this);
|
|
364
|
-
this.sendToHostAsync = import_js_controller_common.tools.promisifyNoError(this.sendToHost, this);
|
|
365
366
|
this.setStateAsync = import_js_controller_common.tools.promisify(this.setState, this);
|
|
366
367
|
this.setStateChangedAsync = import_js_controller_common.tools.promisify(this.setStateChanged, this, ["id", "notChanged"]);
|
|
367
368
|
this.setForeignStateAsync = import_js_controller_common.tools.promisify(this.setForeignState, this);
|
|
@@ -479,7 +480,12 @@ class AdapterClass extends import_node_events.EventEmitter {
|
|
|
479
480
|
this._logger.info(`${this.namespaceLog} getSession not processed because States database not connected`);
|
|
480
481
|
return import_js_controller_common.tools.maybeCallbackWithError(options.callback, import_js_controller_common.tools.ERRORS.ERROR_DB_CLOSED);
|
|
481
482
|
}
|
|
482
|
-
this.#states.getSession(options.id,
|
|
483
|
+
this.#states.getSession(options.id, (err, session) => {
|
|
484
|
+
if (err) {
|
|
485
|
+
this._logger.error(`${this.namespaceLog} Cannot read session "${options.id}": ${err.message}`);
|
|
486
|
+
}
|
|
487
|
+
return import_js_controller_common.tools.maybeCallback(options.callback, session || null);
|
|
488
|
+
});
|
|
483
489
|
}
|
|
484
490
|
// unknown implementation guards
|
|
485
491
|
setSession(id, ttl, data, callback) {
|
|
@@ -1389,7 +1395,7 @@ class AdapterClass extends import_node_events.EventEmitter {
|
|
|
1389
1395
|
return this.setForeignObjectAsync(configObjId, obj);
|
|
1390
1396
|
}
|
|
1391
1397
|
/**
|
|
1392
|
-
* Reads
|
|
1398
|
+
* Reads a parameter which is stored encrypted and returns its decrypted value.
|
|
1393
1399
|
*
|
|
1394
1400
|
* It returns promise if no callback is provided.
|
|
1395
1401
|
*
|
|
@@ -1404,14 +1410,12 @@ class AdapterClass extends import_node_events.EventEmitter {
|
|
|
1404
1410
|
async _getEncryptedConfig(options) {
|
|
1405
1411
|
const { attribute, callback } = options;
|
|
1406
1412
|
const value = (0, import_utils.getObjectAttribute)(this.config, attribute);
|
|
1413
|
+
if ((Array.isArray(value) || typeof value === "string") && this._decryptedNativeAttributes.includes(attribute)) {
|
|
1414
|
+
return import_js_controller_common.tools.maybeCallbackWithError(callback, null, value);
|
|
1415
|
+
}
|
|
1407
1416
|
if (Array.isArray(value)) {
|
|
1408
1417
|
const secret = await this.getSystemSecret();
|
|
1409
|
-
const result =
|
|
1410
|
-
for (let i = 0; i < value.length; i++) {
|
|
1411
|
-
if (typeof value[i] === "string") {
|
|
1412
|
-
result[i] = import_js_controller_common.tools.decrypt(secret, value[i]);
|
|
1413
|
-
}
|
|
1414
|
-
}
|
|
1418
|
+
const result = value.map((item) => typeof item === "string" ? import_js_controller_common.tools.decrypt(secret, item) : item);
|
|
1415
1419
|
return import_js_controller_common.tools.maybeCallbackWithError(callback, null, result);
|
|
1416
1420
|
} else if (typeof value === "string") {
|
|
1417
1421
|
const secret = await this.getSystemSecret();
|
|
@@ -4706,13 +4710,27 @@ class AdapterClass extends import_node_events.EventEmitter {
|
|
|
4706
4710
|
this.sendTo(instanceName, command, message, callback, options);
|
|
4707
4711
|
});
|
|
4708
4712
|
}
|
|
4713
|
+
/**
|
|
4714
|
+
* The `user` field of an outbound message, from the `user` of the send options.
|
|
4715
|
+
*
|
|
4716
|
+
* Spread into the envelope, so a send without a user leaves the field out entirely instead of
|
|
4717
|
+
* putting an empty one in: a receiver that checks `obj.user` must be able to tell "nobody named a
|
|
4718
|
+
* user" from "the user is known", and an empty string is neither.
|
|
4719
|
+
*
|
|
4720
|
+
* @param options the send options the caller passed, if any
|
|
4721
|
+
*/
|
|
4722
|
+
#userOf(options) {
|
|
4723
|
+
const user = options?.user;
|
|
4724
|
+
return typeof user === "string" && user ? { user } : {};
|
|
4725
|
+
}
|
|
4709
4726
|
async _sendTo(_options) {
|
|
4710
4727
|
const { command, message, callback, options } = _options;
|
|
4711
4728
|
let { instanceName } = _options;
|
|
4712
4729
|
const obj = {
|
|
4713
4730
|
command,
|
|
4714
4731
|
message,
|
|
4715
|
-
from: `system.adapter.${this.namespace}
|
|
4732
|
+
from: `system.adapter.${this.namespace}`,
|
|
4733
|
+
...this.#userOf(options)
|
|
4716
4734
|
};
|
|
4717
4735
|
if (!instanceName) {
|
|
4718
4736
|
return import_js_controller_common.tools.maybeCallbackWithError(callback, "No instanceName provided or not a string");
|
|
@@ -4812,8 +4830,10 @@ class AdapterClass extends import_node_events.EventEmitter {
|
|
|
4812
4830
|
* if (!result) adapter.log.error('No response received');
|
|
4813
4831
|
* }
|
|
4814
4832
|
* ```
|
|
4833
|
+
* @param options optional send options: a `timeout` for the answer, or the `user` this message is
|
|
4834
|
+
* sent on behalf of - the host sees it as `obj.user`
|
|
4815
4835
|
*/
|
|
4816
|
-
sendToHost(hostName, command, message, callback) {
|
|
4836
|
+
sendToHost(hostName, command, message, callback, options) {
|
|
4817
4837
|
if (typeof message === "undefined") {
|
|
4818
4838
|
message = command;
|
|
4819
4839
|
command = "send";
|
|
@@ -4825,17 +4845,57 @@ class AdapterClass extends import_node_events.EventEmitter {
|
|
|
4825
4845
|
if (!import_js_controller_common.tools.isObject(callback)) {
|
|
4826
4846
|
import_validator.Validator.assertOptionalCallback(callback, "callback");
|
|
4827
4847
|
}
|
|
4848
|
+
if (options !== void 0) {
|
|
4849
|
+
import_validator.Validator.assertObject(options, "options");
|
|
4850
|
+
}
|
|
4828
4851
|
return this._sendToHost({
|
|
4829
4852
|
hostName,
|
|
4830
4853
|
command,
|
|
4831
4854
|
message,
|
|
4855
|
+
options,
|
|
4832
4856
|
callback
|
|
4833
4857
|
});
|
|
4834
4858
|
}
|
|
4859
|
+
/**
|
|
4860
|
+
* Async version of sendToHost
|
|
4861
|
+
*
|
|
4862
|
+
* Resolves with whatever the host answered - like the promisified version it replaces, an error
|
|
4863
|
+
* of the host reaches the caller as the resolved value, and that is left alone: an adapter that
|
|
4864
|
+
* awaits this today must not start seeing rejections.
|
|
4865
|
+
*
|
|
4866
|
+
* It rejects in one case: a `timeout` was asked for in the send options and elapsed, which is
|
|
4867
|
+
* how `sendToAsync` reports the same thing. Whoever passes that option is writing against an
|
|
4868
|
+
* option that did not exist before and can expect it; whoever passes none keeps the old
|
|
4869
|
+
* behaviour, including a host that never answers and a caller that waits for ever.
|
|
4870
|
+
*
|
|
4871
|
+
* @param hostName name of the host where the message must be sent to. E.g. "myPC" or "system.host.myPC". If argument is null, the message will be sent to all hosts.
|
|
4872
|
+
* @param command command name. One of: "cmdExec", "getRepository", "getInstalled", ...
|
|
4873
|
+
* @param message object that will be given as argument for request
|
|
4874
|
+
* @param options optional send options: a `timeout` for the answer, or the `user` the message is sent for
|
|
4875
|
+
*/
|
|
4876
|
+
sendToHostAsync(hostName, command, message, options) {
|
|
4877
|
+
const timeout = options?.timeout;
|
|
4878
|
+
return new Promise((resolve, reject) => {
|
|
4879
|
+
this.sendToHost(
|
|
4880
|
+
hostName,
|
|
4881
|
+
command,
|
|
4882
|
+
message,
|
|
4883
|
+
// The answer of a host travels through the states database and is therefore never an
|
|
4884
|
+
// `Error` instance; one can only come from here, and only where a budget was named.
|
|
4885
|
+
(result) => timeout && result instanceof Error ? reject(result) : resolve(result),
|
|
4886
|
+
options
|
|
4887
|
+
);
|
|
4888
|
+
});
|
|
4889
|
+
}
|
|
4835
4890
|
async _sendToHost(_options) {
|
|
4836
|
-
const { command, message, callback } = _options;
|
|
4891
|
+
const { command, message, callback, options } = _options;
|
|
4837
4892
|
let { hostName } = _options;
|
|
4838
|
-
const obj = {
|
|
4893
|
+
const obj = {
|
|
4894
|
+
command,
|
|
4895
|
+
message,
|
|
4896
|
+
from: `system.adapter.${this.namespace}`,
|
|
4897
|
+
...this.#userOf(options)
|
|
4898
|
+
};
|
|
4839
4899
|
if (!this.#states) {
|
|
4840
4900
|
this._logger.info(`${this.namespaceLog} sendToHost not processed because States database not connected`);
|
|
4841
4901
|
return import_js_controller_common.tools.maybeCallbackWithError(callback, import_js_controller_common.tools.ERRORS.ERROR_DB_CLOSED);
|
|
@@ -4884,7 +4944,18 @@ class AdapterClass extends import_node_events.EventEmitter {
|
|
|
4884
4944
|
if (this._callbackId >= 4294967295) {
|
|
4885
4945
|
this._callbackId = 1;
|
|
4886
4946
|
}
|
|
4887
|
-
|
|
4947
|
+
const callbackId = obj.callback.id;
|
|
4948
|
+
let timer;
|
|
4949
|
+
if (options?.timeout) {
|
|
4950
|
+
timer = setTimeout(() => {
|
|
4951
|
+
const callbackObj = this.messageCallbacks.get(callbackId);
|
|
4952
|
+
if (callbackObj) {
|
|
4953
|
+
callbackObj.cb(new Error("Timeout exceeded"));
|
|
4954
|
+
this.messageCallbacks.delete(callbackId);
|
|
4955
|
+
}
|
|
4956
|
+
}, options.timeout);
|
|
4957
|
+
}
|
|
4958
|
+
this.messageCallbacks.set(callbackId, { cb: callback, time: Date.now(), timer });
|
|
4888
4959
|
} else {
|
|
4889
4960
|
obj.callback = callback;
|
|
4890
4961
|
obj.callback.ack = true;
|
|
@@ -5238,6 +5309,9 @@ class AdapterClass extends import_node_events.EventEmitter {
|
|
|
5238
5309
|
return options;
|
|
5239
5310
|
}
|
|
5240
5311
|
_checkState(obj, options, command) {
|
|
5312
|
+
if (options.groups?.includes(import_constants.SYSTEM_ADMIN_GROUP)) {
|
|
5313
|
+
return true;
|
|
5314
|
+
}
|
|
5241
5315
|
const limitToOwnerRights = options.limitToOwnerRights === true;
|
|
5242
5316
|
if (obj?.acl) {
|
|
5243
5317
|
obj.acl.state = obj.acl.state || obj.acl.object;
|
|
@@ -7746,12 +7820,15 @@ class AdapterClass extends import_node_events.EventEmitter {
|
|
|
7746
7820
|
this.oObjects = await this.getAdapterObjectsAsync();
|
|
7747
7821
|
await this.subscribeObjectsAsync("*");
|
|
7748
7822
|
}
|
|
7749
|
-
await this.getSystemSecret();
|
|
7750
|
-
const promises = [];
|
|
7823
|
+
const secret = await this.getSystemSecret();
|
|
7751
7824
|
if (Array.isArray(adapterConfig.encryptedNative)) {
|
|
7825
|
+
this._decryptedNativeAttributes = new Array();
|
|
7752
7826
|
for (const attr of adapterConfig.encryptedNative) {
|
|
7753
|
-
|
|
7754
|
-
|
|
7827
|
+
try {
|
|
7828
|
+
(0, import_utils.decryptArray)({ obj: this.config, secret, keys: [attr] });
|
|
7829
|
+
this._decryptedNativeAttributes.push(attr);
|
|
7830
|
+
} catch (e) {
|
|
7831
|
+
this._logger.error(`${this.namespaceLog} Can not decrypt attribute ${attr}: ${e.message}`);
|
|
7755
7832
|
}
|
|
7756
7833
|
}
|
|
7757
7834
|
} else {
|
|
@@ -7760,7 +7837,6 @@ class AdapterClass extends import_node_events.EventEmitter {
|
|
|
7760
7837
|
this.SUPPORTED_FEATURES.splice(idx, 1);
|
|
7761
7838
|
}
|
|
7762
7839
|
}
|
|
7763
|
-
await Promise.all(promises);
|
|
7764
7840
|
if (!this.#states) {
|
|
7765
7841
|
return;
|
|
7766
7842
|
}
|