@iobroker/db-objects-redis 4.0.0-alpha.9-20211115-5dac659e → 4.0.3
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/README.md +1 -1
- package/index.js +1 -1
- package/lib/objects/{lua → lua-v3}/custom.lua +8 -5
- package/lib/objects/{lua → lua-v3}/filter.lua +7 -3
- package/lib/objects/{lua → lua-v3}/programs.lua +6 -3
- package/lib/objects/{lua → lua-v3}/script.lua +6 -3
- package/lib/objects/{lua → lua-v3}/variables.lua +6 -3
- package/lib/objects/lua-v4/custom.lua +20 -0
- package/lib/objects/lua-v4/filter.lua +16 -0
- package/lib/objects/lua-v4/programs.lua +27 -0
- package/lib/objects/lua-v4/redlock_acquire.lua +12 -0
- package/lib/objects/lua-v4/redlock_extend.lua +12 -0
- package/lib/objects/lua-v4/redlock_release.lua +10 -0
- package/lib/objects/lua-v4/script.lua +17 -0
- package/lib/objects/lua-v4/variables.lua +28 -0
- package/lib/objects/lua-v4-no-sets/custom.lua +27 -0
- package/lib/objects/lua-v4-no-sets/filter.lua +25 -0
- package/lib/objects/lua-v4-no-sets/programs.lua +27 -0
- package/lib/objects/lua-v4-no-sets/redlock_acquire.lua +12 -0
- package/lib/objects/lua-v4-no-sets/redlock_extend.lua +12 -0
- package/lib/objects/lua-v4-no-sets/redlock_release.lua +10 -0
- package/lib/objects/lua-v4-no-sets/script.lua +24 -0
- package/lib/objects/lua-v4-no-sets/variables.lua +28 -0
- package/lib/objects/objectsInRedisClient.js +1382 -564
- package/lib/objects/objectsUtils.js +327 -276
- package/package.json +6 -5
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
/**
|
|
2
2
|
* Object DB in REDIS - Client
|
|
3
3
|
*
|
|
4
|
-
* Copyright (c) 2018-
|
|
4
|
+
* Copyright (c) 2018-2022 ioBroker GmbH - All rights reserved.
|
|
5
5
|
*
|
|
6
6
|
* You may not to use, modify or distribute this package in any form without explicit agreement from ioBroker GmbH.
|
|
7
7
|
*
|
|
@@ -16,37 +16,31 @@
|
|
|
16
16
|
/* jshint -W061 */
|
|
17
17
|
'use strict';
|
|
18
18
|
|
|
19
|
-
const extend
|
|
20
|
-
const Redis
|
|
21
|
-
const tools
|
|
22
|
-
const fs
|
|
23
|
-
const path
|
|
24
|
-
const crypto
|
|
19
|
+
const extend = require('node.extend');
|
|
20
|
+
const Redis = require('ioredis');
|
|
21
|
+
const { tools } = require('@iobroker/db-base');
|
|
22
|
+
const fs = require('fs');
|
|
23
|
+
const path = require('path');
|
|
24
|
+
const crypto = require('crypto');
|
|
25
25
|
const { isDeepStrictEqual } = require('util');
|
|
26
|
-
const deepClone
|
|
27
|
-
const utils
|
|
28
|
-
|
|
29
|
-
function initScriptFiles() {
|
|
30
|
-
const scripts = {};
|
|
31
|
-
try {
|
|
32
|
-
fs.readdirSync(__dirname + '/lib/objects/lua')
|
|
33
|
-
.forEach(name => scripts[name.replace(/.lua$/, '')] = fs.readFileSync(path.join(__dirname, 'lua', name)).toString('utf8'));
|
|
34
|
-
} catch {
|
|
35
|
-
// TODO
|
|
36
|
-
}
|
|
37
|
-
return scripts;
|
|
38
|
-
}
|
|
39
|
-
const scriptFiles = initScriptFiles();
|
|
26
|
+
const deepClone = require('deep-clone');
|
|
27
|
+
const utils = require('./objectsUtils.js');
|
|
28
|
+
const semver = require('semver');
|
|
40
29
|
|
|
41
30
|
class ObjectsInRedisClient {
|
|
42
|
-
|
|
43
31
|
constructor(settings) {
|
|
44
32
|
this.settings = settings || {};
|
|
45
|
-
this.redisNamespace =
|
|
33
|
+
this.redisNamespace =
|
|
34
|
+
(this.settings.redisNamespace ||
|
|
35
|
+
(this.settings.connection && this.settings.connection.redisNamespace) ||
|
|
36
|
+
'cfg') + '.';
|
|
46
37
|
this.fileNamespace = this.redisNamespace + 'f.';
|
|
47
38
|
this.fileNamespaceL = this.fileNamespace.length;
|
|
48
39
|
this.objNamespace = this.redisNamespace + 'o.';
|
|
40
|
+
this.setNamespace = this.redisNamespace + 's.';
|
|
41
|
+
this.metaNamespace = (this.settings.metaNamespace || 'meta') + '.';
|
|
49
42
|
this.objNamespaceL = this.objNamespace.length;
|
|
43
|
+
this.supportedProtocolVersions = ['4'];
|
|
50
44
|
|
|
51
45
|
this.stop = false;
|
|
52
46
|
this.client = null;
|
|
@@ -55,6 +49,8 @@ class ObjectsInRedisClient {
|
|
|
55
49
|
this.preserveSettings = ['custom', 'smartName', 'material', 'habpanel', 'mobile'];
|
|
56
50
|
this.defaultNewAcl = this.settings.defaultNewAcl || null;
|
|
57
51
|
this.namespace = this.settings.namespace || this.settings.hostname || '';
|
|
52
|
+
/** @type {string} */
|
|
53
|
+
this.hostname = this.settings.hostname || tools.getHostName();
|
|
58
54
|
this.scripts = {};
|
|
59
55
|
|
|
60
56
|
// cached meta objects for file operations
|
|
@@ -67,11 +63,42 @@ class ObjectsInRedisClient {
|
|
|
67
63
|
}
|
|
68
64
|
}
|
|
69
65
|
|
|
66
|
+
/**
|
|
67
|
+
* Checks if we are allowed to start and sets the protocol version accordingly
|
|
68
|
+
*
|
|
69
|
+
* @returns {Promise<void>}
|
|
70
|
+
* @private
|
|
71
|
+
*/
|
|
72
|
+
async _determineProtocolVersion() {
|
|
73
|
+
let protoVersion;
|
|
74
|
+
try {
|
|
75
|
+
protoVersion = await this.client.get(`${this.metaNamespace}objects.protocolVersion`);
|
|
76
|
+
} catch (e) {
|
|
77
|
+
if (e.message.includes('GET-UNSUPPORTED')) {
|
|
78
|
+
// secondary updated and primary < 4.0
|
|
79
|
+
return;
|
|
80
|
+
}
|
|
81
|
+
}
|
|
82
|
+
|
|
83
|
+
if (!protoVersion) {
|
|
84
|
+
// if no proto version existent yet, we set ours
|
|
85
|
+
const highestVersion = Math.max(...this.supportedProtocolVersions);
|
|
86
|
+
await this.setProtocolVersion(highestVersion);
|
|
87
|
+
this.activeProtocolVersion = highestVersion.toString();
|
|
88
|
+
return;
|
|
89
|
+
}
|
|
90
|
+
|
|
91
|
+
// check if we can support this version
|
|
92
|
+
if (this.supportedProtocolVersions.includes(protoVersion)) {
|
|
93
|
+
this.activeProtocolVersion = protoVersion;
|
|
94
|
+
} else {
|
|
95
|
+
throw new Error(`This host does not support protocol version "${protoVersion}"`);
|
|
96
|
+
}
|
|
97
|
+
}
|
|
98
|
+
|
|
70
99
|
connectDb() {
|
|
71
100
|
this.settings.connection = this.settings.connection || {};
|
|
72
101
|
|
|
73
|
-
const ioRegExp = new RegExp('^' + this.objNamespace.replace(/\./g, '\\.') + '[_A-Za-z0-9ÄÖÜäöüа-яА-Я]+'); // cfg.o.[_A-Za-z0-9]+
|
|
74
|
-
|
|
75
102
|
const onChange = this.settings.change; // on change handler
|
|
76
103
|
const onChangeUser = this.settings.changeUser; // on change handler for User events
|
|
77
104
|
|
|
@@ -131,11 +158,17 @@ class ObjectsInRedisClient {
|
|
|
131
158
|
delete this.settings.connection.options.retry_max_delay;
|
|
132
159
|
this.settings.connection.options.enableReadyCheck = true;
|
|
133
160
|
|
|
134
|
-
if (this.settings.connection.port === 0) {
|
|
161
|
+
if (this.settings.connection.port === 0) {
|
|
162
|
+
// Port = 0 means unix socket
|
|
135
163
|
// initiate a unix socket connection
|
|
136
164
|
this.settings.connection.options.path = this.settings.connection.host;
|
|
137
|
-
this.log.debug(
|
|
138
|
-
|
|
165
|
+
this.log.debug(
|
|
166
|
+
this.namespace +
|
|
167
|
+
' Redis Objects: Use File Socket for connection: ' +
|
|
168
|
+
this.settings.connection.options.path
|
|
169
|
+
);
|
|
170
|
+
} else if (Array.isArray(this.settings.connection.host)) {
|
|
171
|
+
// Host is an array means we use a sentinel
|
|
139
172
|
const defaultPort = Array.isArray(this.settings.connection.port) ? null : this.settings.connection.port;
|
|
140
173
|
|
|
141
174
|
this.settings.connection.options.sentinels = this.settings.connection.host.map((redisNode, idx) => ({
|
|
@@ -143,16 +176,31 @@ class ObjectsInRedisClient {
|
|
|
143
176
|
port: defaultPort || this.settings.connection.port[idx]
|
|
144
177
|
}));
|
|
145
178
|
|
|
146
|
-
this.settings.connection.options.name = this.settings.connection.sentinelName
|
|
147
|
-
|
|
179
|
+
this.settings.connection.options.name = this.settings.connection.sentinelName
|
|
180
|
+
? this.settings.connection.sentinelName
|
|
181
|
+
: 'mymaster';
|
|
182
|
+
this.log.debug(
|
|
183
|
+
this.namespace +
|
|
184
|
+
' Redis Objects: Use Sentinel for connection: ' +
|
|
185
|
+
this.settings.connection.options.name +
|
|
186
|
+
', ' +
|
|
187
|
+
JSON.stringify(this.settings.connection.options.sentinels)
|
|
188
|
+
);
|
|
148
189
|
} else {
|
|
149
190
|
this.settings.connection.options.host = this.settings.connection.host;
|
|
150
191
|
this.settings.connection.options.port = this.settings.connection.port;
|
|
151
|
-
this.log.debug(
|
|
192
|
+
this.log.debug(
|
|
193
|
+
this.namespace +
|
|
194
|
+
' Redis Objects: Use Redis connection: ' +
|
|
195
|
+
this.settings.connection.options.host +
|
|
196
|
+
':' +
|
|
197
|
+
this.settings.connection.options.port
|
|
198
|
+
);
|
|
152
199
|
}
|
|
153
200
|
this.settings.connection.options.db = this.settings.connection.options.db || 0;
|
|
154
201
|
this.settings.connection.options.family = this.settings.connection.options.family || 0;
|
|
155
|
-
this.settings.connection.options.password =
|
|
202
|
+
this.settings.connection.options.password =
|
|
203
|
+
this.settings.connection.options.auth_pass || this.settings.connection.pass || null;
|
|
156
204
|
|
|
157
205
|
this.settings.connection.options.autoResubscribe = false; // We do our own resubscribe because other sometimes not work
|
|
158
206
|
// REDIS does not allow whitespaces, we have some because of pid
|
|
@@ -161,7 +209,18 @@ class ObjectsInRedisClient {
|
|
|
161
209
|
this.client = new Redis(this.settings.connection.options);
|
|
162
210
|
|
|
163
211
|
this.client.on('error', error => {
|
|
164
|
-
this.settings.connection.enhancedLogging &&
|
|
212
|
+
this.settings.connection.enhancedLogging &&
|
|
213
|
+
this.log.silly(
|
|
214
|
+
this.namespace +
|
|
215
|
+
' Redis ERROR Objects: (' +
|
|
216
|
+
ignoreErrors +
|
|
217
|
+
'/' +
|
|
218
|
+
this.stop +
|
|
219
|
+
') ' +
|
|
220
|
+
error.message +
|
|
221
|
+
' / ' +
|
|
222
|
+
error.stack
|
|
223
|
+
);
|
|
165
224
|
if (this.stop) {
|
|
166
225
|
return;
|
|
167
226
|
}
|
|
@@ -169,30 +228,40 @@ class ObjectsInRedisClient {
|
|
|
169
228
|
initError = true;
|
|
170
229
|
// Seems we have a socket.io server
|
|
171
230
|
if (!ignoreErrors && error.message.startsWith('Protocol error, got "H" as reply type byte.')) {
|
|
172
|
-
this.log.error(
|
|
231
|
+
this.log.error(
|
|
232
|
+
this.namespace +
|
|
233
|
+
' Could not connect to objects database at ' +
|
|
234
|
+
this.settings.connection.options.host +
|
|
235
|
+
':' +
|
|
236
|
+
this.settings.connection.options.port +
|
|
237
|
+
' (invalid protocol). Please make sure the configured IP and port points to a host running JS-Controller >= 2.0. and that the port is not occupied by other software!'
|
|
238
|
+
);
|
|
173
239
|
}
|
|
174
240
|
return;
|
|
175
241
|
}
|
|
176
|
-
this.log.error(this.namespace
|
|
242
|
+
this.log.error(`${this.namespace} Objects database error: ${error.message}`);
|
|
177
243
|
errorLogged = true;
|
|
178
244
|
});
|
|
179
245
|
|
|
180
246
|
this.client.on('end', () => {
|
|
181
|
-
this.settings.connection.enhancedLogging &&
|
|
247
|
+
this.settings.connection.enhancedLogging &&
|
|
248
|
+
this.log.silly(`${this.namespace} Objects-Redis Event end (stop=${this.stop})`);
|
|
182
249
|
ready && typeof this.settings.disconnected === 'function' && this.settings.disconnected();
|
|
183
250
|
});
|
|
184
251
|
|
|
185
252
|
this.client.on('connect', () => {
|
|
186
|
-
this.settings.connection.enhancedLogging &&
|
|
253
|
+
this.settings.connection.enhancedLogging &&
|
|
254
|
+
this.log.silly(`${this.namespace} Objects-Redis Event connect (stop=${this.stop})`);
|
|
187
255
|
connected = true;
|
|
188
256
|
if (errorLogged) {
|
|
189
|
-
this.log.info(this.namespace
|
|
257
|
+
this.log.info(`${this.namespace} Objects database successfully reconnected`);
|
|
190
258
|
errorLogged = false;
|
|
191
259
|
}
|
|
192
260
|
});
|
|
193
261
|
|
|
194
262
|
this.client.on('close', () => {
|
|
195
|
-
this.settings.connection.enhancedLogging &&
|
|
263
|
+
this.settings.connection.enhancedLogging &&
|
|
264
|
+
this.log.silly(`${this.namespace} Objects-Redis Event close (stop=${this.stop})`);
|
|
196
265
|
//if (ready && typeof this.settings.disconnected === 'function') this.settings.disconnected();
|
|
197
266
|
});
|
|
198
267
|
|
|
@@ -201,10 +270,19 @@ class ObjectsInRedisClient {
|
|
|
201
270
|
reconnectCounter++;
|
|
202
271
|
}
|
|
203
272
|
|
|
204
|
-
this.settings.connection.enhancedLogging &&
|
|
273
|
+
this.settings.connection.enhancedLogging &&
|
|
274
|
+
this.log.silly(
|
|
275
|
+
`${this.namespace} Objects-Redis Event reconnect (reconnectCounter=${reconnectCounter}, stop=${this.stop})`
|
|
276
|
+
);
|
|
205
277
|
|
|
206
|
-
if (reconnectCounter > 2) {
|
|
207
|
-
|
|
278
|
+
if (reconnectCounter > 2) {
|
|
279
|
+
// fallback logic for nodejs <10
|
|
280
|
+
this.log.error(
|
|
281
|
+
this.namespace +
|
|
282
|
+
' The DB port ' +
|
|
283
|
+
this.settings.connection.options.port +
|
|
284
|
+
' is occupied by something that is not a Redis protocol server. Please check other software running on this port or, if you use iobroker, make sure to update to js-controller 2.0 or higher!'
|
|
285
|
+
);
|
|
208
286
|
return;
|
|
209
287
|
}
|
|
210
288
|
connected = false;
|
|
@@ -218,7 +296,7 @@ class ObjectsInRedisClient {
|
|
|
218
296
|
initError = false;
|
|
219
297
|
ignoreErrors = false;
|
|
220
298
|
|
|
221
|
-
this.log.debug(this.namespace
|
|
299
|
+
this.log.debug(`${this.namespace} Objects client ready ... initialize now`);
|
|
222
300
|
try {
|
|
223
301
|
await this.client.config('set', ['lua-time-limit', 10000]); // increase LUA timeout TODO needs better fix
|
|
224
302
|
} catch (e) {
|
|
@@ -228,46 +306,119 @@ class ObjectsInRedisClient {
|
|
|
228
306
|
let initCounter = 0;
|
|
229
307
|
if (!this.subSystem && typeof onChange === 'function') {
|
|
230
308
|
initCounter++;
|
|
231
|
-
this.log.debug(this.namespace
|
|
309
|
+
this.log.debug(`${this.namespace} Objects create System PubSub Client`);
|
|
232
310
|
this.subSystem = new Redis(this.settings.connection.options);
|
|
233
311
|
this.subSystem.ioBrokerSubscriptions = {};
|
|
234
312
|
|
|
313
|
+
if (typeof this.settings.primaryHostLost === 'function') {
|
|
314
|
+
try {
|
|
315
|
+
// enable Expiry/Evicted events in server - same as states (could be same db)
|
|
316
|
+
await this.client.config('set', ['notify-keyspace-events', 'Exe']);
|
|
317
|
+
} catch (e) {
|
|
318
|
+
this.log.warn(
|
|
319
|
+
`${this.namespace} Unable to enable Expiry Keyspace events from Redis Server: ${e.message}`
|
|
320
|
+
);
|
|
321
|
+
}
|
|
322
|
+
|
|
323
|
+
this.subSystem.on('message', (channel, message) => {
|
|
324
|
+
if (
|
|
325
|
+
channel === `__keyevent@${this.settings.connection.options.db}__:expired` ||
|
|
326
|
+
channel === `__keyevent@${this.settings.connection.options.db}__:evicted`
|
|
327
|
+
) {
|
|
328
|
+
this.log.silly(`${this.namespace} redis message expired/evicted ${channel}:${message}`);
|
|
329
|
+
|
|
330
|
+
if (message === `${this.metaNamespace}objects.primaryHost`) {
|
|
331
|
+
this.settings.primaryHostLost();
|
|
332
|
+
}
|
|
333
|
+
}
|
|
334
|
+
});
|
|
335
|
+
}
|
|
336
|
+
|
|
235
337
|
if (typeof onChange === 'function') {
|
|
236
338
|
this.subSystem.on('pmessage', (pattern, channel, message) =>
|
|
237
339
|
setImmediate(() => {
|
|
238
|
-
this.log.silly(
|
|
340
|
+
this.log.silly(
|
|
341
|
+
`${this.namespace} Objects system redis pmessage ${pattern}/${channel}:${message}`
|
|
342
|
+
);
|
|
343
|
+
|
|
344
|
+
if (channel.startsWith(this.metaNamespace)) {
|
|
345
|
+
if (
|
|
346
|
+
channel === `${this.metaNamespace}objects.protocolVersion` &&
|
|
347
|
+
message !== this.activeProtocolVersion
|
|
348
|
+
) {
|
|
349
|
+
if (typeof this.settings.disconnected === 'function') {
|
|
350
|
+
// protocol version has changed, restart controller
|
|
351
|
+
this.log.info(
|
|
352
|
+
`${this.namespace} Objects protocol version has changed, disconnecting!`
|
|
353
|
+
);
|
|
354
|
+
this.settings.disconnected();
|
|
355
|
+
}
|
|
356
|
+
} else if (channel === `${this.metaNamespace}objects.features.useSets`) {
|
|
357
|
+
const newUseSets = !!parseInt(message);
|
|
358
|
+
if (newUseSets !== this.useSets) {
|
|
359
|
+
this.log.info(
|
|
360
|
+
`${this.namespace} Sets ${
|
|
361
|
+
newUseSets ? 'activated' : 'deactivated'
|
|
362
|
+
}: restarting ...`
|
|
363
|
+
);
|
|
364
|
+
this.useSets = newUseSets;
|
|
365
|
+
// luas are no longer up to date, lets restart
|
|
366
|
+
if (typeof this.settings.disconnected === 'function') {
|
|
367
|
+
this.settings.disconnected();
|
|
368
|
+
}
|
|
369
|
+
}
|
|
370
|
+
}
|
|
371
|
+
return;
|
|
372
|
+
}
|
|
373
|
+
|
|
239
374
|
try {
|
|
240
|
-
if (
|
|
375
|
+
if (channel.startsWith(this.objNamespace) && channel.length > this.objNamespaceL) {
|
|
241
376
|
const id = channel.substring(this.objNamespaceL);
|
|
242
377
|
try {
|
|
243
378
|
const obj = message ? JSON.parse(message) : null;
|
|
244
379
|
|
|
245
|
-
if (
|
|
380
|
+
if (
|
|
381
|
+
id === 'system.config' &&
|
|
246
382
|
obj &&
|
|
247
383
|
obj.common &&
|
|
248
384
|
obj.common.defaultNewAcl &&
|
|
249
|
-
!isDeepStrictEqual(obj.common.defaultNewAcl, this.defaultNewAcl)
|
|
385
|
+
!isDeepStrictEqual(obj.common.defaultNewAcl, this.defaultNewAcl)
|
|
386
|
+
) {
|
|
250
387
|
this.defaultNewAcl = JSON.parse(JSON.stringify(obj.common.defaultNewAcl));
|
|
251
388
|
this.settings.controller && this.setDefaultAcl(this.defaultNewAcl);
|
|
252
389
|
}
|
|
253
390
|
|
|
254
391
|
onChange(id, obj);
|
|
255
392
|
} catch (e) {
|
|
256
|
-
this.log.warn(
|
|
393
|
+
this.log.warn(
|
|
394
|
+
`${this.namespace} Objects Cannot process system pmessage ${id} - ${message}: ${e.message}`
|
|
395
|
+
);
|
|
257
396
|
this.log.warn(`${this.namespace} ${e.stack}`);
|
|
258
397
|
}
|
|
259
398
|
} else {
|
|
260
|
-
this.log.warn(
|
|
399
|
+
this.log.warn(
|
|
400
|
+
`${this.namespace} Objects Received unexpected system pmessage: ${channel}`
|
|
401
|
+
);
|
|
261
402
|
}
|
|
262
403
|
} catch (e) {
|
|
263
|
-
this.log.warn(
|
|
264
|
-
|
|
404
|
+
this.log.warn(
|
|
405
|
+
this.namespace +
|
|
406
|
+
' Objects system pmessage ' +
|
|
407
|
+
channel +
|
|
408
|
+
' ' +
|
|
409
|
+
JSON.stringify(message) +
|
|
410
|
+
' ' +
|
|
411
|
+
e.message
|
|
412
|
+
);
|
|
413
|
+
this.log.warn(`${this.namespace} ${e.stack}`);
|
|
265
414
|
}
|
|
266
|
-
})
|
|
415
|
+
})
|
|
416
|
+
);
|
|
267
417
|
}
|
|
268
418
|
|
|
269
419
|
this.subSystem.on('end', () => {
|
|
270
|
-
this.settings.connection.enhancedLogging &&
|
|
420
|
+
this.settings.connection.enhancedLogging &&
|
|
421
|
+
this.log.silly(`${this.namespace} Objects-Redis System Event end sub (stop=${this.stop})`);
|
|
271
422
|
ready && typeof this.settings.disconnected === 'function' && this.settings.disconnected();
|
|
272
423
|
});
|
|
273
424
|
|
|
@@ -275,37 +426,68 @@ class ObjectsInRedisClient {
|
|
|
275
426
|
if (this.stop) {
|
|
276
427
|
return;
|
|
277
428
|
}
|
|
278
|
-
this.settings.connection.enhancedLogging &&
|
|
429
|
+
this.settings.connection.enhancedLogging &&
|
|
430
|
+
this.log.silly(
|
|
431
|
+
this.namespace +
|
|
432
|
+
' PubSub System client Objects No redis connection: ' +
|
|
433
|
+
JSON.stringify(error)
|
|
434
|
+
);
|
|
279
435
|
});
|
|
280
436
|
|
|
281
437
|
if (this.settings.connection.enhancedLogging) {
|
|
282
438
|
this.subSystem.on('connect', () =>
|
|
283
|
-
this.log.silly(
|
|
439
|
+
this.log.silly(
|
|
440
|
+
`${this.namespace} PubSub System client Objects-Redis Event connect (stop=${this.stop})`
|
|
441
|
+
)
|
|
442
|
+
);
|
|
284
443
|
|
|
285
444
|
this.subSystem.on('close', () =>
|
|
286
|
-
this.log.silly(
|
|
445
|
+
this.log.silly(
|
|
446
|
+
`${this.namespace} PubSub System client Objects-Redis Event close (stop=${this.stop})`
|
|
447
|
+
)
|
|
448
|
+
);
|
|
287
449
|
|
|
288
450
|
this.subSystem.on('reconnecting', reconnectCounter =>
|
|
289
|
-
this.log.silly(
|
|
451
|
+
this.log.silly(
|
|
452
|
+
`${this.namespace} PubSub System client Objects-Redis Event reconnect (reconnectCounter=${reconnectCounter}, stop=${this.stop})`
|
|
453
|
+
)
|
|
454
|
+
);
|
|
290
455
|
}
|
|
291
456
|
|
|
292
457
|
this.subSystem.on('ready', async () => {
|
|
293
458
|
if (--initCounter < 1) {
|
|
294
459
|
if (this.settings.connection.port === 0) {
|
|
295
|
-
this.log.debug(
|
|
460
|
+
this.log.debug(
|
|
461
|
+
`${this.namespace} Objects ${ready ? 'system re' : ''}connected to redis: ${
|
|
462
|
+
this.settings.connection.host
|
|
463
|
+
}`
|
|
464
|
+
);
|
|
296
465
|
} else {
|
|
297
|
-
this.log.debug(
|
|
466
|
+
this.log.debug(
|
|
467
|
+
`${this.namespace} Objects ${ready ? 'system re' : ''}connected to redis: ${
|
|
468
|
+
this.settings.connection.host
|
|
469
|
+
}:${this.settings.connection.port}`
|
|
470
|
+
);
|
|
298
471
|
}
|
|
299
472
|
!ready && typeof this.settings.connected === 'function' && this.settings.connected();
|
|
300
473
|
ready = true;
|
|
301
474
|
}
|
|
302
475
|
// subscribe on system.config anytime because also adapters need stuff like defaultNewAcl (especially admin)
|
|
303
476
|
try {
|
|
304
|
-
this.subSystem && await this.subSystem.psubscribe(`${this.objNamespace}system.config`);
|
|
477
|
+
this.subSystem && (await this.subSystem.psubscribe(`${this.objNamespace}system.config`));
|
|
305
478
|
} catch {
|
|
306
479
|
// ignore
|
|
307
480
|
}
|
|
308
481
|
|
|
482
|
+
// subscribe to meta changes
|
|
483
|
+
try {
|
|
484
|
+
this.subSystem && (await this.subSystem.psubscribe(`${this.metaNamespace}*`));
|
|
485
|
+
} catch (e) {
|
|
486
|
+
this.log.warn(
|
|
487
|
+
`${this.namespace} Unable to subscribe to meta namespace "${this.metaNamespace}" changes: ${e.message}`
|
|
488
|
+
);
|
|
489
|
+
}
|
|
490
|
+
|
|
309
491
|
if (this.subSystem) {
|
|
310
492
|
for (const sub of Object.keys(this.subSystem.ioBrokerSubscriptions)) {
|
|
311
493
|
try {
|
|
@@ -320,36 +502,47 @@ class ObjectsInRedisClient {
|
|
|
320
502
|
|
|
321
503
|
if (!this.sub && typeof onChangeUser === 'function') {
|
|
322
504
|
initCounter++;
|
|
323
|
-
this.log.debug(this.namespace
|
|
505
|
+
this.log.debug(`${this.namespace} Objects create User PubSub Client`);
|
|
324
506
|
this.sub = new Redis(this.settings.connection.options);
|
|
325
507
|
this.sub.ioBrokerSubscriptions = {};
|
|
326
508
|
|
|
327
509
|
this.sub.on('pmessage', (pattern, channel, message) => {
|
|
328
510
|
setImmediate(() => {
|
|
329
|
-
this.log.silly(
|
|
511
|
+
this.log.silly(
|
|
512
|
+
`${this.namespace} Objects user redis pmessage ${pattern}/${channel}:${message}`
|
|
513
|
+
);
|
|
330
514
|
try {
|
|
331
|
-
if (
|
|
515
|
+
if (channel.startsWith(this.objNamespace) && channel.length > this.objNamespaceL) {
|
|
332
516
|
const id = channel.substring(this.objNamespaceL);
|
|
333
517
|
try {
|
|
334
518
|
const obj = message ? JSON.parse(message) : null;
|
|
335
519
|
|
|
336
520
|
onChangeUser(id, obj);
|
|
337
521
|
} catch (e) {
|
|
338
|
-
this.log.warn(
|
|
522
|
+
this.log.warn(
|
|
523
|
+
`${this.namespace} Objects user cannot process pmessage ${id} - ${message}: ${e.message}`
|
|
524
|
+
);
|
|
339
525
|
this.log.warn(`${this.namespace} ${e.stack}`);
|
|
340
526
|
}
|
|
341
527
|
} else {
|
|
342
|
-
this.log.warn(
|
|
528
|
+
this.log.warn(
|
|
529
|
+
`${this.namespace} Objects user received unexpected pmessage: ${channel}`
|
|
530
|
+
);
|
|
343
531
|
}
|
|
344
532
|
} catch (e) {
|
|
345
|
-
this.log.warn(
|
|
346
|
-
|
|
533
|
+
this.log.warn(
|
|
534
|
+
`${this.namespace} Objects user pmessage ${channel} ${JSON.stringify(message)} ${
|
|
535
|
+
e.message
|
|
536
|
+
}`
|
|
537
|
+
);
|
|
538
|
+
this.log.warn(`${this.namespace} ${e.stack}`);
|
|
347
539
|
}
|
|
348
540
|
});
|
|
349
541
|
});
|
|
350
542
|
|
|
351
543
|
this.sub.on('end', () => {
|
|
352
|
-
this.settings.connection.enhancedLogging &&
|
|
544
|
+
this.settings.connection.enhancedLogging &&
|
|
545
|
+
this.log.silly(`${this.namespace} Objects-Redis Event end user sub (stop=${this.stop})`);
|
|
353
546
|
ready && typeof this.settings.disconnected === 'function' && this.settings.disconnected();
|
|
354
547
|
});
|
|
355
548
|
|
|
@@ -357,26 +550,46 @@ class ObjectsInRedisClient {
|
|
|
357
550
|
if (this.stop) {
|
|
358
551
|
return;
|
|
359
552
|
}
|
|
360
|
-
this.settings.connection.enhancedLogging &&
|
|
553
|
+
this.settings.connection.enhancedLogging &&
|
|
554
|
+
this.log.silly(
|
|
555
|
+
`${this.namespace} PubSub user client Objects No redis connection: ${JSON.stringify(error)}`
|
|
556
|
+
);
|
|
361
557
|
});
|
|
362
558
|
|
|
363
559
|
if (this.settings.connection.enhancedLogging) {
|
|
364
560
|
this.sub.on('connect', () =>
|
|
365
|
-
this.log.silly(
|
|
561
|
+
this.log.silly(
|
|
562
|
+
`${this.namespace} PubSub user client Objects-Redis Event connect (stop=${this.stop})`
|
|
563
|
+
)
|
|
564
|
+
);
|
|
366
565
|
|
|
367
566
|
this.sub.on('close', () =>
|
|
368
|
-
this.log.silly(
|
|
567
|
+
this.log.silly(
|
|
568
|
+
`${this.namespace} PubSub user client Objects-Redis Event close (stop=${this.stop})`
|
|
569
|
+
)
|
|
570
|
+
);
|
|
369
571
|
|
|
370
572
|
this.sub.on('reconnecting', reconnectCounter =>
|
|
371
|
-
this.log.silly(
|
|
573
|
+
this.log.silly(
|
|
574
|
+
`${this.namespace} PubSub user client Objects-Redis Event reconnect (reconnectCounter=${reconnectCounter}, stop=${this.stop})`
|
|
575
|
+
)
|
|
576
|
+
);
|
|
372
577
|
}
|
|
373
578
|
|
|
374
579
|
this.sub.on('ready', async () => {
|
|
375
580
|
if (--initCounter < 1) {
|
|
376
581
|
if (this.settings.connection.port === 0) {
|
|
377
|
-
this.log.debug(
|
|
582
|
+
this.log.debug(
|
|
583
|
+
`${this.namespace} Objects ${ready ? 'user re' : ''}connected to redis: ${
|
|
584
|
+
this.settings.connection.host
|
|
585
|
+
}`
|
|
586
|
+
);
|
|
378
587
|
} else {
|
|
379
|
-
this.log.debug(
|
|
588
|
+
this.log.debug(
|
|
589
|
+
`${this.namespace} Objects ${ready ? 'user re' : ''}connected to redis: ${
|
|
590
|
+
this.settings.connection.host
|
|
591
|
+
}:${this.settings.connection.port}`
|
|
592
|
+
);
|
|
380
593
|
}
|
|
381
594
|
!ready && typeof this.settings.connected === 'function' && this.settings.connected();
|
|
382
595
|
ready = true;
|
|
@@ -392,21 +605,82 @@ class ObjectsInRedisClient {
|
|
|
392
605
|
});
|
|
393
606
|
}
|
|
394
607
|
|
|
395
|
-
|
|
608
|
+
if (!this.client) {
|
|
609
|
+
return;
|
|
610
|
+
}
|
|
611
|
+
|
|
612
|
+
// do this before starting with async calls ;-)
|
|
396
613
|
initCounter++;
|
|
614
|
+
|
|
615
|
+
try {
|
|
616
|
+
// check if we are allowed to use sets
|
|
617
|
+
this.useSets = !!parseInt(await this.client.get(`${this.metaNamespace}objects.features.useSets`));
|
|
618
|
+
} catch (e) {
|
|
619
|
+
// if unsupported we have a legacy host
|
|
620
|
+
if (!e.message.includes('UNSUPPORTED')) {
|
|
621
|
+
throw e;
|
|
622
|
+
} else {
|
|
623
|
+
this.useSets = false;
|
|
624
|
+
}
|
|
625
|
+
}
|
|
626
|
+
|
|
627
|
+
try {
|
|
628
|
+
await this._determineProtocolVersion();
|
|
629
|
+
} catch (e) {
|
|
630
|
+
this.log.error(`${this.namespace} ${e.message}`);
|
|
631
|
+
throw new Error('Objects DB is not allowed to start in the current Multihost environment');
|
|
632
|
+
}
|
|
633
|
+
|
|
634
|
+
// for controller v4 we have to check if we can use the new lua scripts and set logic
|
|
635
|
+
// TODO: remove this backward shim if controller v4.0 is old enough
|
|
636
|
+
let keys = await this._getKeysViaScan(`${this.objNamespace}system.host.*`);
|
|
637
|
+
|
|
638
|
+
// filter out obvious non-host objects
|
|
639
|
+
const hostRegex = new RegExp(`^${this.objNamespace.replace(/\./g, '\\.')}system\\.host\\.[^.]+$`);
|
|
640
|
+
keys = keys.filter(id => hostRegex.test(id));
|
|
641
|
+
/** if false we have a host smaller 4 (no proto version for this existing) */
|
|
642
|
+
this.noLegacyMultihost = true;
|
|
643
|
+
|
|
644
|
+
try {
|
|
645
|
+
if (keys.length) {
|
|
646
|
+
// else no host known yet - so we are single host
|
|
647
|
+
const objs = await this.client.mget(keys);
|
|
648
|
+
for (const strObj of objs) {
|
|
649
|
+
const obj = JSON.parse(strObj);
|
|
650
|
+
if (
|
|
651
|
+
obj &&
|
|
652
|
+
obj.type === 'host' &&
|
|
653
|
+
obj._id !== `system.host.${this.hostname}` &&
|
|
654
|
+
obj.common &&
|
|
655
|
+
obj.common.installedVersion &&
|
|
656
|
+
semver.lt(obj.common.installedVersion, '4.0.0')
|
|
657
|
+
) {
|
|
658
|
+
// one of the host has a version smaller 4, we have to use legacy db
|
|
659
|
+
this.noLegacyMultihost = false;
|
|
660
|
+
this.log.info(`${this.namespace} Sets unsupported`);
|
|
661
|
+
}
|
|
662
|
+
}
|
|
663
|
+
}
|
|
664
|
+
} catch (e) {
|
|
665
|
+
this.log.error(
|
|
666
|
+
`${this.namespace} Cannot determine Lua scripts strategy: ${e.message} ${JSON.stringify(keys)}`
|
|
667
|
+
);
|
|
668
|
+
return;
|
|
669
|
+
}
|
|
670
|
+
|
|
671
|
+
this.log.debug(`${this.namespace} Objects client initialize lua scripts`);
|
|
672
|
+
|
|
397
673
|
try {
|
|
398
674
|
await this.loadLuaScripts();
|
|
399
675
|
} catch (err) {
|
|
400
676
|
this.log.error(`${this.namespace} Cannot initialize database scripts: ${err.message}`);
|
|
401
677
|
return;
|
|
402
678
|
}
|
|
403
|
-
|
|
404
|
-
return;
|
|
405
|
-
}
|
|
679
|
+
|
|
406
680
|
// init default new acl
|
|
407
681
|
let obj;
|
|
408
682
|
try {
|
|
409
|
-
obj = await this.client.get(this.objNamespace
|
|
683
|
+
obj = await this.client.get(`${this.objNamespace}system.config`);
|
|
410
684
|
} catch {
|
|
411
685
|
// ignore
|
|
412
686
|
}
|
|
@@ -426,9 +700,23 @@ class ObjectsInRedisClient {
|
|
|
426
700
|
|
|
427
701
|
if (--initCounter < 1) {
|
|
428
702
|
if (this.settings.connection.port === 0) {
|
|
429
|
-
this.log.debug(
|
|
703
|
+
this.log.debug(
|
|
704
|
+
this.namespace +
|
|
705
|
+
' Objects ' +
|
|
706
|
+
(ready ? 'client re' : '') +
|
|
707
|
+
'connected to redis: ' +
|
|
708
|
+
this.settings.connection.host
|
|
709
|
+
);
|
|
430
710
|
} else {
|
|
431
|
-
this.log.debug(
|
|
711
|
+
this.log.debug(
|
|
712
|
+
this.namespace +
|
|
713
|
+
' Objects ' +
|
|
714
|
+
(ready ? 'client re' : '') +
|
|
715
|
+
'connected to redis: ' +
|
|
716
|
+
this.settings.connection.host +
|
|
717
|
+
':' +
|
|
718
|
+
this.settings.connection.port
|
|
719
|
+
);
|
|
432
720
|
}
|
|
433
721
|
!ready && typeof this.settings.connected === 'function' && this.settings.connected();
|
|
434
722
|
ready = true;
|
|
@@ -437,7 +725,7 @@ class ObjectsInRedisClient {
|
|
|
437
725
|
}
|
|
438
726
|
|
|
439
727
|
getStatus() {
|
|
440
|
-
return {type: 'redis', server: false};
|
|
728
|
+
return { type: 'redis', server: false };
|
|
441
729
|
}
|
|
442
730
|
|
|
443
731
|
/**
|
|
@@ -468,7 +756,7 @@ class ObjectsInRedisClient {
|
|
|
468
756
|
// -------------- FILE FUNCTIONS -------------------------------------------
|
|
469
757
|
async _setBinaryState(id, data, callback) {
|
|
470
758
|
if (!this.client) {
|
|
471
|
-
return
|
|
759
|
+
return tools.maybeCallbackWithRedisError(callback, utils.ERRORS.ERROR_DB_CLOSED);
|
|
472
760
|
}
|
|
473
761
|
if (!Buffer.isBuffer(data)) {
|
|
474
762
|
data = Buffer.from(data);
|
|
@@ -526,12 +814,11 @@ class ObjectsInRedisClient {
|
|
|
526
814
|
getFileId(id, name, isMeta) {
|
|
527
815
|
name = this.normalizeFilename(name);
|
|
528
816
|
// e.g. ekey.admin and admin/ekey.png
|
|
529
|
-
if (id.
|
|
530
|
-
if (name.
|
|
817
|
+
if (id.endsWith('.admin')) {
|
|
818
|
+
if (name.startsWith('admin/')) {
|
|
531
819
|
name = name.replace(/^admin\//, '');
|
|
532
|
-
} else
|
|
533
|
-
|
|
534
|
-
if (name.match(/^iobroker.[-\d\w]\/admin\//i)) {
|
|
820
|
+
} else if (name.match(/^iobroker.[-\d\w]\/admin\//i)) {
|
|
821
|
+
// e.g. ekey.admin and iobroker.ekey/admin/ekey.png
|
|
535
822
|
name = name.replace(/^iobroker.[-\d\w]\/admin\//i, '');
|
|
536
823
|
}
|
|
537
824
|
}
|
|
@@ -554,15 +841,15 @@ class ObjectsInRedisClient {
|
|
|
554
841
|
// read file settings from redis
|
|
555
842
|
const fileId = this.getFileId(id, name, true);
|
|
556
843
|
if (!fileId) {
|
|
557
|
-
const fileOptions = {
|
|
844
|
+
const fileOptions = { notExists: true };
|
|
558
845
|
if (utils.checkFile(fileOptions, options, flag, this.defaultNewAcl)) {
|
|
559
|
-
return
|
|
846
|
+
return tools.maybeCallback(callback, false, options, fileOptions); // NO error
|
|
560
847
|
} else {
|
|
561
|
-
return
|
|
848
|
+
return tools.maybeCallback(callback, true, options); // error
|
|
562
849
|
}
|
|
563
850
|
}
|
|
564
851
|
if (!this.client) {
|
|
565
|
-
return
|
|
852
|
+
return tools.maybeCallbackWithRedisError(callback, utils.ERRORS.ERROR_DB_CLOSED, options);
|
|
566
853
|
}
|
|
567
854
|
let fileOptions;
|
|
568
855
|
try {
|
|
@@ -575,7 +862,7 @@ class ObjectsInRedisClient {
|
|
|
575
862
|
fileOptions = JSON.parse(fileOptions);
|
|
576
863
|
} catch {
|
|
577
864
|
this.log.error(`${this.namespace} Cannot parse JSON ${id}: ${fileOptions}`);
|
|
578
|
-
fileOptions = {notExists: true};
|
|
865
|
+
fileOptions = { notExists: true };
|
|
579
866
|
}
|
|
580
867
|
if (utils.checkFile(fileOptions, options, flag, this.defaultNewAcl)) {
|
|
581
868
|
return typeof callback === 'function' && callback(false, options, fileOptions); // NO error
|
|
@@ -594,12 +881,9 @@ class ObjectsInRedisClient {
|
|
|
594
881
|
this.getObject(id, (err, obj) => {
|
|
595
882
|
if (obj && !obj.acl) {
|
|
596
883
|
obj.acl = defaultAcl;
|
|
597
|
-
this.setObject(id, obj, null, () =>
|
|
598
|
-
setImmediate(() =>
|
|
599
|
-
this._setDefaultAcl(ids, defaultAcl)));
|
|
884
|
+
this.setObject(id, obj, null, () => setImmediate(() => this._setDefaultAcl(ids, defaultAcl)));
|
|
600
885
|
} else {
|
|
601
|
-
setImmediate(() =>
|
|
602
|
-
this._setDefaultAcl(ids, defaultAcl));
|
|
886
|
+
setImmediate(() => this._setDefaultAcl(ids, defaultAcl));
|
|
603
887
|
}
|
|
604
888
|
});
|
|
605
889
|
}
|
|
@@ -622,7 +906,7 @@ class ObjectsInRedisClient {
|
|
|
622
906
|
if (error) {
|
|
623
907
|
this.log.error(`${this.namespace} ${error}`);
|
|
624
908
|
}
|
|
625
|
-
return tools.
|
|
909
|
+
return tools.maybeCallback(callback, user, userGroups, userAcl);
|
|
626
910
|
});
|
|
627
911
|
}
|
|
628
912
|
|
|
@@ -631,10 +915,10 @@ class ObjectsInRedisClient {
|
|
|
631
915
|
}
|
|
632
916
|
|
|
633
917
|
async _writeFile(id, name, data, options, callback, meta) {
|
|
634
|
-
const ext
|
|
635
|
-
const mime
|
|
636
|
-
const _mimeType
|
|
637
|
-
const isBinary
|
|
918
|
+
const ext = name.match(/\.[^.]+$/);
|
|
919
|
+
const mime = utils.getMimeType(ext);
|
|
920
|
+
const _mimeType = mime.mimeType;
|
|
921
|
+
const isBinary = mime.isBinary;
|
|
638
922
|
|
|
639
923
|
const metaID = this.getFileId(id, name, true);
|
|
640
924
|
if (!this.client) {
|
|
@@ -654,12 +938,18 @@ class ObjectsInRedisClient {
|
|
|
654
938
|
}
|
|
655
939
|
} else {
|
|
656
940
|
if (!meta) {
|
|
657
|
-
meta = {createdAt: Date.now()};
|
|
941
|
+
meta = { createdAt: Date.now() };
|
|
658
942
|
}
|
|
659
943
|
if (!meta.acl) {
|
|
660
944
|
meta.acl = {
|
|
661
|
-
owner:
|
|
662
|
-
|
|
945
|
+
owner:
|
|
946
|
+
options.user ||
|
|
947
|
+
(this.defaultNewAcl && this.defaultNewAcl.owner) ||
|
|
948
|
+
utils.CONSTS.SYSTEM_ADMIN_USER,
|
|
949
|
+
ownerGroup:
|
|
950
|
+
options.group ||
|
|
951
|
+
(this.defaultNewAcl && this.defaultNewAcl.ownerGroup) ||
|
|
952
|
+
utils.CONSTS.SYSTEM_ADMIN_GROUP,
|
|
663
953
|
permissions: options.mode || (this.defaultNewAcl && this.defaultNewAcl.file) || 0x644
|
|
664
954
|
};
|
|
665
955
|
}
|
|
@@ -672,7 +962,10 @@ class ObjectsInRedisClient {
|
|
|
672
962
|
|
|
673
963
|
meta.mimeType = options.mimeType || _mimeType;
|
|
674
964
|
meta.binary = isBinary;
|
|
675
|
-
meta.acl.ownerGroup =
|
|
965
|
+
meta.acl.ownerGroup =
|
|
966
|
+
meta.acl.ownerGroup ||
|
|
967
|
+
(this.defaultNewAcl && this.defaultNewAcl.ownerGroup) ||
|
|
968
|
+
utils.CONSTS.SYSTEM_ADMIN_GROUP;
|
|
676
969
|
meta.modifiedAt = Date.now();
|
|
677
970
|
|
|
678
971
|
this._setBinaryState(this.getFileId(id, name, false), data, async err => {
|
|
@@ -692,7 +985,7 @@ class ObjectsInRedisClient {
|
|
|
692
985
|
options = null;
|
|
693
986
|
}
|
|
694
987
|
if (typeof options === 'string') {
|
|
695
|
-
options = {mimeType: options};
|
|
988
|
+
options = { mimeType: options };
|
|
696
989
|
}
|
|
697
990
|
|
|
698
991
|
if (options && options.acl) {
|
|
@@ -700,9 +993,11 @@ class ObjectsInRedisClient {
|
|
|
700
993
|
}
|
|
701
994
|
|
|
702
995
|
if (!callback) {
|
|
703
|
-
return /** @type {Promise<void>} */ (
|
|
704
|
-
|
|
705
|
-
err ? reject(err) : resolve()))
|
|
996
|
+
return /** @type {Promise<void>} */ (
|
|
997
|
+
new Promise((resolve, reject) =>
|
|
998
|
+
this.writeFile(id, name, data, options, err => (err ? reject(err) : resolve()))
|
|
999
|
+
)
|
|
1000
|
+
);
|
|
706
1001
|
}
|
|
707
1002
|
|
|
708
1003
|
try {
|
|
@@ -735,9 +1030,11 @@ class ObjectsInRedisClient {
|
|
|
735
1030
|
}
|
|
736
1031
|
|
|
737
1032
|
writeFileAsync(id, name, data, options) {
|
|
738
|
-
return /** @type {Promise<void>} */ (
|
|
739
|
-
|
|
740
|
-
err ? reject(err) : resolve()))
|
|
1033
|
+
return /** @type {Promise<void>} */ (
|
|
1034
|
+
new Promise((resolve, reject) =>
|
|
1035
|
+
this.writeFile(id, name, data, options, err => (err ? reject(err) : resolve()))
|
|
1036
|
+
)
|
|
1037
|
+
);
|
|
741
1038
|
}
|
|
742
1039
|
|
|
743
1040
|
async _readFile(id, name, options, callback, meta) {
|
|
@@ -764,7 +1061,7 @@ class ObjectsInRedisClient {
|
|
|
764
1061
|
readFile(id, name, options, callback) {
|
|
765
1062
|
if (typeof options === 'function') {
|
|
766
1063
|
callback = options;
|
|
767
|
-
options
|
|
1064
|
+
options = null;
|
|
768
1065
|
}
|
|
769
1066
|
if (options && options.acl) {
|
|
770
1067
|
options.acl = null;
|
|
@@ -773,7 +1070,9 @@ class ObjectsInRedisClient {
|
|
|
773
1070
|
if (!callback) {
|
|
774
1071
|
return new Promise((resolve, reject) =>
|
|
775
1072
|
this.readFile(id, name, options, (err, res, mimeType) =>
|
|
776
|
-
err ? reject(err) : resolve({data: res, mimeType: mimeType})
|
|
1073
|
+
err ? reject(err) : resolve({ data: res, mimeType: mimeType })
|
|
1074
|
+
)
|
|
1075
|
+
);
|
|
777
1076
|
}
|
|
778
1077
|
|
|
779
1078
|
if (typeof name !== 'string' || !name.length || name === '/') {
|
|
@@ -797,7 +1096,9 @@ class ObjectsInRedisClient {
|
|
|
797
1096
|
readFileAsync(id, name, options) {
|
|
798
1097
|
return new Promise((resolve, reject) =>
|
|
799
1098
|
this.readFile(id, name, options, (err, res, mimeType) =>
|
|
800
|
-
err ? reject(err) : resolve({data: res, mimeType: mimeType})
|
|
1099
|
+
err ? reject(err) : resolve({ data: res, mimeType: mimeType })
|
|
1100
|
+
)
|
|
1101
|
+
);
|
|
801
1102
|
}
|
|
802
1103
|
|
|
803
1104
|
/**
|
|
@@ -809,22 +1110,24 @@ class ObjectsInRedisClient {
|
|
|
809
1110
|
*/
|
|
810
1111
|
async objectExists(id, options) {
|
|
811
1112
|
if (!this.client) {
|
|
812
|
-
|
|
1113
|
+
throw new Error(utils.ERRORS.ERROR_DB_CLOSED);
|
|
813
1114
|
}
|
|
814
1115
|
if (!id || typeof id !== 'string') {
|
|
815
|
-
|
|
1116
|
+
throw new Error(`invalid id ${JSON.stringify(id)}`);
|
|
816
1117
|
}
|
|
817
1118
|
|
|
818
1119
|
try {
|
|
819
|
-
await /** @type {Promise<void>} */ (
|
|
820
|
-
|
|
821
|
-
|
|
822
|
-
|
|
823
|
-
|
|
824
|
-
|
|
825
|
-
|
|
826
|
-
|
|
827
|
-
|
|
1120
|
+
await /** @type {Promise<void>} */ (
|
|
1121
|
+
new Promise((resolve, reject) => {
|
|
1122
|
+
utils.checkObjectRights(this, null, null, options, utils.CONSTS.ACCESS_LIST, err => {
|
|
1123
|
+
if (err) {
|
|
1124
|
+
reject(err);
|
|
1125
|
+
} else {
|
|
1126
|
+
resolve();
|
|
1127
|
+
}
|
|
1128
|
+
});
|
|
1129
|
+
})
|
|
1130
|
+
);
|
|
828
1131
|
const exists = await this.client.exists(this.objNamespace + id);
|
|
829
1132
|
return !!exists;
|
|
830
1133
|
} catch (e) {
|
|
@@ -851,15 +1154,17 @@ class ObjectsInRedisClient {
|
|
|
851
1154
|
}
|
|
852
1155
|
|
|
853
1156
|
try {
|
|
854
|
-
await /** @type {Promise<void>} */ (
|
|
855
|
-
|
|
856
|
-
|
|
857
|
-
|
|
858
|
-
|
|
859
|
-
|
|
860
|
-
|
|
861
|
-
|
|
862
|
-
|
|
1157
|
+
await /** @type {Promise<void>} */ (
|
|
1158
|
+
new Promise((resolve, reject) => {
|
|
1159
|
+
this.checkFileRights(id, name, options, utils.CONSTS.ACCESS_READ, err => {
|
|
1160
|
+
if (err) {
|
|
1161
|
+
reject(err);
|
|
1162
|
+
} else {
|
|
1163
|
+
resolve();
|
|
1164
|
+
}
|
|
1165
|
+
});
|
|
1166
|
+
})
|
|
1167
|
+
);
|
|
863
1168
|
id = this.getFileId(id, name, false);
|
|
864
1169
|
const exists = await this.client.exists(id);
|
|
865
1170
|
return !!exists;
|
|
@@ -892,7 +1197,7 @@ class ObjectsInRedisClient {
|
|
|
892
1197
|
unlink(id, name, options, callback) {
|
|
893
1198
|
if (typeof options === 'function') {
|
|
894
1199
|
callback = options;
|
|
895
|
-
options
|
|
1200
|
+
options = null;
|
|
896
1201
|
}
|
|
897
1202
|
if (options && options.acl) {
|
|
898
1203
|
options.acl = null;
|
|
@@ -920,9 +1225,9 @@ class ObjectsInRedisClient {
|
|
|
920
1225
|
}
|
|
921
1226
|
|
|
922
1227
|
unlinkAsync(id, name, options) {
|
|
923
|
-
return /** @type {Promise<void>} */ (
|
|
924
|
-
this.unlink(id, name, options, err =>
|
|
925
|
-
|
|
1228
|
+
return /** @type {Promise<void>} */ (
|
|
1229
|
+
new Promise((resolve, reject) => this.unlink(id, name, options, err => (err ? reject(err) : resolve())))
|
|
1230
|
+
);
|
|
926
1231
|
}
|
|
927
1232
|
|
|
928
1233
|
delFile(id, name, options, callback) {
|
|
@@ -938,7 +1243,8 @@ class ObjectsInRedisClient {
|
|
|
938
1243
|
if (!this.client) {
|
|
939
1244
|
return tools.maybeCallbackWithError(callback, utils.ERRORS.ERROR_DB_CLOSED);
|
|
940
1245
|
}
|
|
941
|
-
if (id === '') {
|
|
1246
|
+
if (id === '') {
|
|
1247
|
+
// special case for "root"
|
|
942
1248
|
const dirID = this.getFileId('*', '*');
|
|
943
1249
|
|
|
944
1250
|
let keys;
|
|
@@ -993,21 +1299,19 @@ class ObjectsInRedisClient {
|
|
|
993
1299
|
if (!keys || !keys.length) {
|
|
994
1300
|
return tools.maybeCallbackWithError(callback, utils.ERRORS.ERROR_NOT_FOUND, []);
|
|
995
1301
|
}
|
|
996
|
-
keys = keys
|
|
997
|
-
.
|
|
998
|
-
|
|
999
|
-
if (
|
|
1000
|
-
|
|
1001
|
-
|
|
1002
|
-
|
|
1003
|
-
|
|
1004
|
-
|
|
1005
|
-
if (dirs.indexOf(dir) === -1) {
|
|
1006
|
-
dirs.push(dir);
|
|
1007
|
-
}
|
|
1302
|
+
keys = keys.sort().filter(key => {
|
|
1303
|
+
if (key.endsWith('$%$meta')) {
|
|
1304
|
+
const parts = key.substr(start, key.length - end).split('/');
|
|
1305
|
+
if (parts.length === deepLevel) {
|
|
1306
|
+
return !key.includes('/_data.json$%$') && key !== '_data.json'; // sort out "virtual" files that are used to mark directories
|
|
1307
|
+
} else {
|
|
1308
|
+
const dir = parts[deepLevel - 1];
|
|
1309
|
+
if (dirs.indexOf(dir) === -1) {
|
|
1310
|
+
dirs.push(dir);
|
|
1008
1311
|
}
|
|
1009
1312
|
}
|
|
1010
|
-
}
|
|
1313
|
+
}
|
|
1314
|
+
});
|
|
1011
1315
|
if (!keys.length) {
|
|
1012
1316
|
const result = [];
|
|
1013
1317
|
while (dirs.length) {
|
|
@@ -1030,9 +1334,9 @@ class ObjectsInRedisClient {
|
|
|
1030
1334
|
|
|
1031
1335
|
const result = [];
|
|
1032
1336
|
const dontCheck =
|
|
1033
|
-
|
|
1034
|
-
|
|
1035
|
-
|
|
1337
|
+
options.user === utils.CONSTS.SYSTEM_ADMIN_USER ||
|
|
1338
|
+
options.group !== utils.CONSTS.SYSTEM_ADMIN_GROUP ||
|
|
1339
|
+
(options.groups && options.groups.indexOf(utils.CONSTS.SYSTEM_ADMIN_GROUP) !== -1);
|
|
1036
1340
|
|
|
1037
1341
|
objs = objs || [];
|
|
1038
1342
|
for (let i = 0; i < keys.length; i++) {
|
|
@@ -1056,11 +1360,14 @@ class ObjectsInRedisClient {
|
|
|
1056
1360
|
continue;
|
|
1057
1361
|
} // virtual file, ignore
|
|
1058
1362
|
objs[i].acl = objs[i].acl || {};
|
|
1059
|
-
if (
|
|
1060
|
-
|
|
1363
|
+
if (
|
|
1364
|
+
options.user !== utils.CONSTS.SYSTEM_ADMIN_USER &&
|
|
1365
|
+
options.groups.indexOf(utils.CONSTS.SYSTEM_ADMIN_GROUP) === -1
|
|
1366
|
+
) {
|
|
1367
|
+
objs[i].acl.read = !!(objs[i].acl.permissions & utils.CONSTS.ACCESS_EVERY_READ);
|
|
1061
1368
|
objs[i].acl.write = !!(objs[i].acl.permissions & utils.CONSTS.ACCESS_EVERY_WRITE);
|
|
1062
1369
|
} else {
|
|
1063
|
-
objs[i].acl.read
|
|
1370
|
+
objs[i].acl.read = true;
|
|
1064
1371
|
objs[i].acl.write = true;
|
|
1065
1372
|
}
|
|
1066
1373
|
result.push({
|
|
@@ -1069,7 +1376,7 @@ class ObjectsInRedisClient {
|
|
|
1069
1376
|
isDir: false,
|
|
1070
1377
|
acl: objs[i].acl,
|
|
1071
1378
|
modifiedAt: objs[i].modifiedAt,
|
|
1072
|
-
createdAt:
|
|
1379
|
+
createdAt: objs[i].createdAt
|
|
1073
1380
|
});
|
|
1074
1381
|
}
|
|
1075
1382
|
}
|
|
@@ -1119,8 +1426,8 @@ class ObjectsInRedisClient {
|
|
|
1119
1426
|
|
|
1120
1427
|
readDirAsync(id, name, options) {
|
|
1121
1428
|
return new Promise((resolve, reject) =>
|
|
1122
|
-
this.readDir(id, name, options, (err, res) =>
|
|
1123
|
-
|
|
1429
|
+
this.readDir(id, name, options, (err, res) => (err ? reject(err) : resolve(res)))
|
|
1430
|
+
);
|
|
1124
1431
|
}
|
|
1125
1432
|
|
|
1126
1433
|
async _renameHelper(keys, oldBase, newBase, callback) {
|
|
@@ -1133,7 +1440,10 @@ class ObjectsInRedisClient {
|
|
|
1133
1440
|
for (const id of keys) {
|
|
1134
1441
|
try {
|
|
1135
1442
|
try {
|
|
1136
|
-
await this.client.rename(
|
|
1443
|
+
await this.client.rename(
|
|
1444
|
+
id.replace(/\$%\$meta$/, '$%$data'),
|
|
1445
|
+
id.replace(oldBase, newBase).replace(/\$%\$meta$/, '$%$data')
|
|
1446
|
+
);
|
|
1137
1447
|
} catch (e) {
|
|
1138
1448
|
// _data.json is not having a data key, so ignore error
|
|
1139
1449
|
if (!(id.endsWith('/_data.json$%$meta') && e.message.includes('no such key'))) {
|
|
@@ -1190,9 +1500,7 @@ class ObjectsInRedisClient {
|
|
|
1190
1500
|
return tools.maybeCallbackWithError(callback, utils.ERRORS.ERROR_NOT_FOUND);
|
|
1191
1501
|
}
|
|
1192
1502
|
|
|
1193
|
-
keys = keys
|
|
1194
|
-
.sort()
|
|
1195
|
-
.filter(key => key.match(/\$%\$meta$/));
|
|
1503
|
+
keys = keys.sort().filter(key => key.endsWith('$%$meta'));
|
|
1196
1504
|
|
|
1197
1505
|
if (!keys.length) {
|
|
1198
1506
|
return tools.maybeCallbackWithError(callback, utils.ERRORS.ERROR_NOT_FOUND);
|
|
@@ -1206,9 +1514,9 @@ class ObjectsInRedisClient {
|
|
|
1206
1514
|
}
|
|
1207
1515
|
let result;
|
|
1208
1516
|
const dontCheck =
|
|
1209
|
-
|
|
1210
|
-
|
|
1211
|
-
|
|
1517
|
+
options.user === utils.CONSTS.SYSTEM_ADMIN_USER ||
|
|
1518
|
+
options.group !== utils.CONSTS.SYSTEM_ADMIN_GROUP ||
|
|
1519
|
+
(options.groups && options.groups.indexOf(utils.CONSTS.SYSTEM_ADMIN_GROUP) !== -1);
|
|
1212
1520
|
|
|
1213
1521
|
objs = objs || [];
|
|
1214
1522
|
if (!dontCheck) {
|
|
@@ -1247,8 +1555,16 @@ class ObjectsInRedisClient {
|
|
|
1247
1555
|
if (options && options.acl) {
|
|
1248
1556
|
options.acl = null;
|
|
1249
1557
|
}
|
|
1250
|
-
if (
|
|
1251
|
-
typeof
|
|
1558
|
+
if (
|
|
1559
|
+
typeof oldName !== 'string' ||
|
|
1560
|
+
!oldName.length ||
|
|
1561
|
+
oldName === '/' ||
|
|
1562
|
+
oldName === '//' ||
|
|
1563
|
+
typeof newName !== 'string' ||
|
|
1564
|
+
!newName.length ||
|
|
1565
|
+
newName === '/' ||
|
|
1566
|
+
newName === '//'
|
|
1567
|
+
) {
|
|
1252
1568
|
return tools.maybeCallbackWithError(callback, utils.ERRORS.ERROR_NOT_FOUND);
|
|
1253
1569
|
}
|
|
1254
1570
|
if (oldName.startsWith('/')) {
|
|
@@ -1278,9 +1594,11 @@ class ObjectsInRedisClient {
|
|
|
1278
1594
|
}
|
|
1279
1595
|
|
|
1280
1596
|
renameAsync(id, oldName, newName, options) {
|
|
1281
|
-
return /** @type {Promise<void>} */ (
|
|
1282
|
-
|
|
1283
|
-
err ? reject(err) : resolve()))
|
|
1597
|
+
return /** @type {Promise<void>} */ (
|
|
1598
|
+
new Promise((resolve, reject) =>
|
|
1599
|
+
this.rename(id, oldName, newName, options, err => (err ? reject(err) : resolve()))
|
|
1600
|
+
)
|
|
1601
|
+
);
|
|
1284
1602
|
}
|
|
1285
1603
|
|
|
1286
1604
|
async _touch(id, name, options, callback, meta) {
|
|
@@ -1327,9 +1645,9 @@ class ObjectsInRedisClient {
|
|
|
1327
1645
|
}
|
|
1328
1646
|
|
|
1329
1647
|
touchAsync(id, name, options) {
|
|
1330
|
-
return /** @type {Promise<void>} */ (
|
|
1331
|
-
this.touch(id, name, options, err =>
|
|
1332
|
-
|
|
1648
|
+
return /** @type {Promise<void>} */ (
|
|
1649
|
+
new Promise((resolve, reject) => this.touch(id, name, options, err => (err ? reject(err) : resolve())))
|
|
1650
|
+
);
|
|
1333
1651
|
}
|
|
1334
1652
|
|
|
1335
1653
|
async _rmHelper(keys, callback) {
|
|
@@ -1364,7 +1682,7 @@ class ObjectsInRedisClient {
|
|
|
1364
1682
|
}
|
|
1365
1683
|
name = this.normalizeFilename(name);
|
|
1366
1684
|
// it could be dir
|
|
1367
|
-
if (!
|
|
1685
|
+
if (!name.endsWith('/*')) {
|
|
1368
1686
|
name += '/*';
|
|
1369
1687
|
} else if (name.endsWith('/')) {
|
|
1370
1688
|
name += '*';
|
|
@@ -1383,9 +1701,7 @@ class ObjectsInRedisClient {
|
|
|
1383
1701
|
return tools.maybeCallbackWithError(callback, utils.ERRORS.ERROR_NOT_FOUND);
|
|
1384
1702
|
}
|
|
1385
1703
|
|
|
1386
|
-
keys = keys
|
|
1387
|
-
.sort()
|
|
1388
|
-
.filter(key => key.match(/\$%\$meta$/));
|
|
1704
|
+
keys = keys.sort().filter(key => key.endsWith('$%$meta'));
|
|
1389
1705
|
|
|
1390
1706
|
if (!keys.length) {
|
|
1391
1707
|
return tools.maybeCallbackWithError(callback, utils.ERRORS.ERROR_NOT_FOUND);
|
|
@@ -1399,9 +1715,9 @@ class ObjectsInRedisClient {
|
|
|
1399
1715
|
}
|
|
1400
1716
|
let result;
|
|
1401
1717
|
const dontCheck =
|
|
1402
|
-
|
|
1403
|
-
|
|
1404
|
-
|
|
1718
|
+
options.user === utils.CONSTS.SYSTEM_ADMIN_USER ||
|
|
1719
|
+
options.group !== utils.CONSTS.SYSTEM_ADMIN_GROUP ||
|
|
1720
|
+
(options.groups && options.groups.indexOf(utils.CONSTS.SYSTEM_ADMIN_GROUP) !== -1);
|
|
1405
1721
|
|
|
1406
1722
|
objs = objs || [];
|
|
1407
1723
|
if (!dontCheck) {
|
|
@@ -1424,9 +1740,9 @@ class ObjectsInRedisClient {
|
|
|
1424
1740
|
const name = key.substring(this.fileNamespaceL + id.length + 3, key.length - 7);
|
|
1425
1741
|
const pos = name.lastIndexOf('/');
|
|
1426
1742
|
if (pos !== -1) {
|
|
1427
|
-
return {file: name.substring(pos + 1), path: name.substring(0, pos)};
|
|
1743
|
+
return { file: name.substring(pos + 1), path: name.substring(0, pos) };
|
|
1428
1744
|
} else {
|
|
1429
|
-
return {file: id, path: ''};
|
|
1745
|
+
return { file: id, path: '' };
|
|
1430
1746
|
}
|
|
1431
1747
|
});
|
|
1432
1748
|
this._rmHelper(result, () => {
|
|
@@ -1463,8 +1779,8 @@ class ObjectsInRedisClient {
|
|
|
1463
1779
|
|
|
1464
1780
|
rmAsync(id, name, options) {
|
|
1465
1781
|
return new Promise((resolve, reject) =>
|
|
1466
|
-
this.rm(id, name, options, (err, files) =>
|
|
1467
|
-
|
|
1782
|
+
this.rm(id, name, options, (err, files) => (err ? reject(err) : resolve(files)))
|
|
1783
|
+
);
|
|
1468
1784
|
}
|
|
1469
1785
|
|
|
1470
1786
|
// simulate. redis has no dirs
|
|
@@ -1499,9 +1815,9 @@ class ObjectsInRedisClient {
|
|
|
1499
1815
|
}
|
|
1500
1816
|
|
|
1501
1817
|
mkdirAsync(id, dirName, options) {
|
|
1502
|
-
return /** @type {Promise<void>} */ (
|
|
1503
|
-
this.mkdir(id, dirName, options, err =>
|
|
1504
|
-
|
|
1818
|
+
return /** @type {Promise<void>} */ (
|
|
1819
|
+
new Promise((resolve, reject) => this.mkdir(id, dirName, options, err => (err ? reject(err) : resolve())))
|
|
1820
|
+
);
|
|
1505
1821
|
}
|
|
1506
1822
|
|
|
1507
1823
|
async _chownFileHelper(keys, metas, options, callback) {
|
|
@@ -1546,20 +1862,22 @@ class ObjectsInRedisClient {
|
|
|
1546
1862
|
}
|
|
1547
1863
|
const nameArr = name.split('/');
|
|
1548
1864
|
const file = nameArr.pop();
|
|
1549
|
-
const res = [
|
|
1550
|
-
|
|
1551
|
-
|
|
1552
|
-
|
|
1553
|
-
|
|
1554
|
-
|
|
1555
|
-
|
|
1556
|
-
|
|
1557
|
-
|
|
1865
|
+
const res = [
|
|
1866
|
+
{
|
|
1867
|
+
path: nameArr.join('/'),
|
|
1868
|
+
file: file,
|
|
1869
|
+
stats: meta.stats,
|
|
1870
|
+
isDir: false,
|
|
1871
|
+
acl: meta.acl || {},
|
|
1872
|
+
modifiedAt: meta.modifiedAt,
|
|
1873
|
+
createdAt: meta.createdAt
|
|
1874
|
+
}
|
|
1875
|
+
];
|
|
1558
1876
|
return tools.maybeCallbackWithError(callback, null, res);
|
|
1559
1877
|
}
|
|
1560
1878
|
|
|
1561
1879
|
// it could be dir
|
|
1562
|
-
if (!
|
|
1880
|
+
if (!name.endsWith('/*')) {
|
|
1563
1881
|
name += '/*';
|
|
1564
1882
|
} else if (name.endsWith('/')) {
|
|
1565
1883
|
name += '*';
|
|
@@ -1580,9 +1898,7 @@ class ObjectsInRedisClient {
|
|
|
1580
1898
|
return tools.maybeCallbackWithError(callback, utils.ERRORS.ERROR_NOT_FOUND);
|
|
1581
1899
|
}
|
|
1582
1900
|
|
|
1583
|
-
keys = keys
|
|
1584
|
-
.sort()
|
|
1585
|
-
.filter(key => key.match(/\$%\$meta$/));
|
|
1901
|
+
keys = keys.sort().filter(key => key.endsWith('$%$meta'));
|
|
1586
1902
|
|
|
1587
1903
|
// Check permissions
|
|
1588
1904
|
let metas;
|
|
@@ -1591,9 +1907,10 @@ class ObjectsInRedisClient {
|
|
|
1591
1907
|
} catch (e) {
|
|
1592
1908
|
return tools.maybeCallbackWithRedisError(callback, e);
|
|
1593
1909
|
}
|
|
1594
|
-
const dontCheck =
|
|
1595
|
-
|
|
1596
|
-
|
|
1910
|
+
const dontCheck =
|
|
1911
|
+
options.user === utils.CONSTS.SYSTEM_ADMIN_USER ||
|
|
1912
|
+
options.group !== utils.CONSTS.SYSTEM_ADMIN_GROUP ||
|
|
1913
|
+
(options.groups && options.groups.indexOf(utils.CONSTS.SYSTEM_ADMIN_GROUP) !== -1);
|
|
1597
1914
|
const keysFiltered = [];
|
|
1598
1915
|
const objsFiltered = [];
|
|
1599
1916
|
const processed = [];
|
|
@@ -1619,13 +1936,13 @@ class ObjectsInRedisClient {
|
|
|
1619
1936
|
const nameArr = name.split('/');
|
|
1620
1937
|
const file = nameArr.pop();
|
|
1621
1938
|
processed.push({
|
|
1622
|
-
path:
|
|
1623
|
-
file:
|
|
1624
|
-
stats:
|
|
1625
|
-
isDir:
|
|
1626
|
-
acl:
|
|
1939
|
+
path: nameArr.join('/'),
|
|
1940
|
+
file: file,
|
|
1941
|
+
stats: metas[i].stats || {},
|
|
1942
|
+
isDir: false,
|
|
1943
|
+
acl: metas[i].acl || {},
|
|
1627
1944
|
modifiedAt: metas[i].modifiedAt,
|
|
1628
|
-
createdAt:
|
|
1945
|
+
createdAt: metas[i].createdAt
|
|
1629
1946
|
});
|
|
1630
1947
|
}
|
|
1631
1948
|
}
|
|
@@ -1641,7 +1958,7 @@ class ObjectsInRedisClient {
|
|
|
1641
1958
|
}
|
|
1642
1959
|
options = options || {};
|
|
1643
1960
|
if (typeof options !== 'object') {
|
|
1644
|
-
options = {owner: options};
|
|
1961
|
+
options = { owner: options };
|
|
1645
1962
|
}
|
|
1646
1963
|
|
|
1647
1964
|
if (typeof name !== 'string' || !name.length || name === '/') {
|
|
@@ -1655,8 +1972,8 @@ class ObjectsInRedisClient {
|
|
|
1655
1972
|
if (!options.ownerGroup && options.group) {
|
|
1656
1973
|
options.ownerGroup = options.group;
|
|
1657
1974
|
}
|
|
1658
|
-
if (!options.owner
|
|
1659
|
-
options.owner
|
|
1975
|
+
if (!options.owner && options.user) {
|
|
1976
|
+
options.owner = options.user;
|
|
1660
1977
|
}
|
|
1661
1978
|
|
|
1662
1979
|
if (!options.owner) {
|
|
@@ -1691,9 +2008,11 @@ class ObjectsInRedisClient {
|
|
|
1691
2008
|
}
|
|
1692
2009
|
|
|
1693
2010
|
chownFileAsync(id, name, options) {
|
|
1694
|
-
return /** @type {Promise<void>} */ (
|
|
1695
|
-
|
|
1696
|
-
err ? reject(err) : resolve(processed)))
|
|
2011
|
+
return /** @type {Promise<void>} */ (
|
|
2012
|
+
new Promise((resolve, reject) =>
|
|
2013
|
+
this.chownFile(id, name, options, (err, processed) => (err ? reject(err) : resolve(processed)))
|
|
2014
|
+
)
|
|
2015
|
+
);
|
|
1697
2016
|
}
|
|
1698
2017
|
|
|
1699
2018
|
/**
|
|
@@ -1746,15 +2065,17 @@ class ObjectsInRedisClient {
|
|
|
1746
2065
|
|
|
1747
2066
|
const nameArr = name.split('/');
|
|
1748
2067
|
const file = nameArr.pop();
|
|
1749
|
-
const res = [
|
|
1750
|
-
|
|
1751
|
-
|
|
1752
|
-
|
|
1753
|
-
|
|
1754
|
-
|
|
1755
|
-
|
|
1756
|
-
|
|
1757
|
-
|
|
2068
|
+
const res = [
|
|
2069
|
+
{
|
|
2070
|
+
path: nameArr.join('/'),
|
|
2071
|
+
file: file,
|
|
2072
|
+
stats: meta.stats,
|
|
2073
|
+
isDir: false,
|
|
2074
|
+
acl: meta.acl || {},
|
|
2075
|
+
modifiedAt: meta.modifiedAt,
|
|
2076
|
+
createdAt: meta.createdAt
|
|
2077
|
+
}
|
|
2078
|
+
];
|
|
1758
2079
|
return tools.maybeCallbackWithError(callback, null, res);
|
|
1759
2080
|
}
|
|
1760
2081
|
|
|
@@ -1780,9 +2101,7 @@ class ObjectsInRedisClient {
|
|
|
1780
2101
|
return tools.maybeCallbackWithError(callback, utils.ERRORS.ERROR_NOT_FOUND);
|
|
1781
2102
|
}
|
|
1782
2103
|
|
|
1783
|
-
keys = keys
|
|
1784
|
-
.sort()
|
|
1785
|
-
.filter(key => key.match(/\$%\$meta$/));
|
|
2104
|
+
keys = keys.sort().filter(key => key.endsWith('$%$meta'));
|
|
1786
2105
|
|
|
1787
2106
|
// Check permissions
|
|
1788
2107
|
let objs;
|
|
@@ -1793,9 +2112,9 @@ class ObjectsInRedisClient {
|
|
|
1793
2112
|
}
|
|
1794
2113
|
|
|
1795
2114
|
const dontCheck =
|
|
1796
|
-
|
|
1797
|
-
|
|
1798
|
-
|
|
2115
|
+
options.user === utils.CONSTS.SYSTEM_ADMIN_USER ||
|
|
2116
|
+
options.group !== utils.CONSTS.SYSTEM_ADMIN_GROUP ||
|
|
2117
|
+
(options.groups && options.groups.indexOf(utils.CONSTS.SYSTEM_ADMIN_GROUP) !== -1);
|
|
1799
2118
|
|
|
1800
2119
|
const keysFiltered = [];
|
|
1801
2120
|
const objsFiltered = [];
|
|
@@ -1822,18 +2141,19 @@ class ObjectsInRedisClient {
|
|
|
1822
2141
|
const nameArr = name.split('/');
|
|
1823
2142
|
const file = nameArr.pop();
|
|
1824
2143
|
processed.push({
|
|
1825
|
-
path:
|
|
1826
|
-
file:
|
|
1827
|
-
stats:
|
|
1828
|
-
isDir:
|
|
1829
|
-
acl:
|
|
2144
|
+
path: nameArr.join('/'),
|
|
2145
|
+
file: file,
|
|
2146
|
+
stats: objs[i].stats,
|
|
2147
|
+
isDir: false,
|
|
2148
|
+
acl: objs[i].acl || {},
|
|
1830
2149
|
modifiedAt: objs[i].modifiedAt,
|
|
1831
|
-
createdAt:
|
|
2150
|
+
createdAt: objs[i].createdAt
|
|
1832
2151
|
});
|
|
1833
2152
|
}
|
|
1834
2153
|
}
|
|
1835
2154
|
this._chmodFileHelper(keysFiltered, objsFiltered, options, err =>
|
|
1836
|
-
tools.maybeCallbackWithError(callback, err, processed)
|
|
2155
|
+
tools.maybeCallbackWithError(callback, err, processed)
|
|
2156
|
+
);
|
|
1837
2157
|
}
|
|
1838
2158
|
|
|
1839
2159
|
chmodFile(id, name, options, callback) {
|
|
@@ -1852,7 +2172,7 @@ class ObjectsInRedisClient {
|
|
|
1852
2172
|
}
|
|
1853
2173
|
|
|
1854
2174
|
if (typeof options !== 'object') {
|
|
1855
|
-
options = {mode: options};
|
|
2175
|
+
options = { mode: options };
|
|
1856
2176
|
}
|
|
1857
2177
|
|
|
1858
2178
|
if (options.mode === undefined) {
|
|
@@ -1876,9 +2196,11 @@ class ObjectsInRedisClient {
|
|
|
1876
2196
|
}
|
|
1877
2197
|
|
|
1878
2198
|
chmodFileAsync(id, name, options) {
|
|
1879
|
-
return /** @type {Promise<void>} */ (
|
|
1880
|
-
|
|
1881
|
-
err ? reject(err) : resolve(processed)))
|
|
2199
|
+
return /** @type {Promise<void>} */ (
|
|
2200
|
+
new Promise((resolve, reject) =>
|
|
2201
|
+
this.chmodFile(id, name, options, (err, processed) => (err ? reject(err) : resolve(processed)))
|
|
2202
|
+
)
|
|
2203
|
+
);
|
|
1882
2204
|
}
|
|
1883
2205
|
|
|
1884
2206
|
enableFileCache(enabled, options, callback) {
|
|
@@ -1903,8 +2225,8 @@ class ObjectsInRedisClient {
|
|
|
1903
2225
|
|
|
1904
2226
|
enableFileCacheAsync(enabled, options) {
|
|
1905
2227
|
return new Promise((resolve, reject) =>
|
|
1906
|
-
this.enableFileCache(enabled, options, (err, res) =>
|
|
1907
|
-
|
|
2228
|
+
this.enableFileCache(enabled, options, (err, res) => (err ? reject(err) : resolve(res)))
|
|
2229
|
+
);
|
|
1908
2230
|
}
|
|
1909
2231
|
|
|
1910
2232
|
// -------------- OBJECT FUNCTIONS -------------------------------------------
|
|
@@ -1924,7 +2246,6 @@ class ObjectsInRedisClient {
|
|
|
1924
2246
|
return tools.maybeCallbackWithError(callback, err);
|
|
1925
2247
|
}
|
|
1926
2248
|
});
|
|
1927
|
-
|
|
1928
2249
|
});
|
|
1929
2250
|
} else {
|
|
1930
2251
|
this.log.silly(this.namespace + ' redis psubscribe ' + this.objNamespace + pattern);
|
|
@@ -1944,7 +2265,7 @@ class ObjectsInRedisClient {
|
|
|
1944
2265
|
}
|
|
1945
2266
|
utils.checkObjectRights(this, null, null, options, 'list', (err, options) => {
|
|
1946
2267
|
if (err) {
|
|
1947
|
-
|
|
2268
|
+
return tools.maybeCallbackWithRedisError(callback, err);
|
|
1948
2269
|
} else {
|
|
1949
2270
|
return this._subscribe(pattern, options, this.subSystem, callback);
|
|
1950
2271
|
}
|
|
@@ -1956,9 +2277,9 @@ class ObjectsInRedisClient {
|
|
|
1956
2277
|
}
|
|
1957
2278
|
|
|
1958
2279
|
subscribeAsync(pattern, options) {
|
|
1959
|
-
return /** @type {Promise<void>} */ (
|
|
1960
|
-
this.subscribe(pattern, options, err =>
|
|
1961
|
-
|
|
2280
|
+
return /** @type {Promise<void>} */ (
|
|
2281
|
+
new Promise((resolve, reject) => this.subscribe(pattern, options, err => (err ? reject(err) : resolve())))
|
|
2282
|
+
);
|
|
1962
2283
|
}
|
|
1963
2284
|
|
|
1964
2285
|
subscribeUser(pattern, options, callback) {
|
|
@@ -1968,7 +2289,7 @@ class ObjectsInRedisClient {
|
|
|
1968
2289
|
}
|
|
1969
2290
|
utils.checkObjectRights(this, null, null, options, 'list', (err, options) => {
|
|
1970
2291
|
if (err) {
|
|
1971
|
-
|
|
2292
|
+
return tools.maybeCallbackWithRedisError(callback, err);
|
|
1972
2293
|
} else {
|
|
1973
2294
|
return this._subscribe(pattern, options, this.sub, callback);
|
|
1974
2295
|
}
|
|
@@ -1976,14 +2297,16 @@ class ObjectsInRedisClient {
|
|
|
1976
2297
|
}
|
|
1977
2298
|
|
|
1978
2299
|
subscribeUserAsync(pattern, options) {
|
|
1979
|
-
return /** @type {Promise<void>} */ (
|
|
1980
|
-
|
|
1981
|
-
err ? reject(err) : resolve()))
|
|
2300
|
+
return /** @type {Promise<void>} */ (
|
|
2301
|
+
new Promise((resolve, reject) =>
|
|
2302
|
+
this.subscribeUser(pattern, options, err => (err ? reject(err) : resolve()))
|
|
2303
|
+
)
|
|
2304
|
+
);
|
|
1982
2305
|
}
|
|
1983
2306
|
|
|
1984
2307
|
_unsubscribe(pattern, options, subClient, callback) {
|
|
1985
2308
|
if (!subClient) {
|
|
1986
|
-
return
|
|
2309
|
+
return tools.maybeCallbackWithRedisError(callback, utils.ERRORS.ERROR_DB_CLOSED);
|
|
1987
2310
|
}
|
|
1988
2311
|
if (Array.isArray(pattern)) {
|
|
1989
2312
|
let count = pattern.length;
|
|
@@ -2014,7 +2337,7 @@ class ObjectsInRedisClient {
|
|
|
2014
2337
|
}
|
|
2015
2338
|
utils.checkObjectRights(this, null, null, options, 'list', (err, options) => {
|
|
2016
2339
|
if (err) {
|
|
2017
|
-
|
|
2340
|
+
return tools.maybeCallbackWithRedisError(callback, err);
|
|
2018
2341
|
} else {
|
|
2019
2342
|
return this._unsubscribe(pattern, options, this.subSystem, callback);
|
|
2020
2343
|
}
|
|
@@ -2026,9 +2349,9 @@ class ObjectsInRedisClient {
|
|
|
2026
2349
|
}
|
|
2027
2350
|
|
|
2028
2351
|
unsubscribeAsync(pattern, options) {
|
|
2029
|
-
return /** @type {Promise<void>} */ (
|
|
2030
|
-
this.unsubscribe(pattern, options, err =>
|
|
2031
|
-
|
|
2352
|
+
return /** @type {Promise<void>} */ (
|
|
2353
|
+
new Promise((resolve, reject) => this.unsubscribe(pattern, options, err => (err ? reject(err) : resolve())))
|
|
2354
|
+
);
|
|
2032
2355
|
}
|
|
2033
2356
|
|
|
2034
2357
|
unsubscribeUser(pattern, options, callback) {
|
|
@@ -2038,7 +2361,7 @@ class ObjectsInRedisClient {
|
|
|
2038
2361
|
}
|
|
2039
2362
|
utils.checkObjectRights(this, null, null, options, 'list', (err, options) => {
|
|
2040
2363
|
if (err) {
|
|
2041
|
-
|
|
2364
|
+
return tools.maybeCallbackWithError(callback, err);
|
|
2042
2365
|
} else {
|
|
2043
2366
|
return this._unsubscribe(pattern, options, this.sub, callback);
|
|
2044
2367
|
}
|
|
@@ -2046,14 +2369,16 @@ class ObjectsInRedisClient {
|
|
|
2046
2369
|
}
|
|
2047
2370
|
|
|
2048
2371
|
unsubscribeUserAsync(pattern, options) {
|
|
2049
|
-
return /** @type {Promise<void>} */ (
|
|
2050
|
-
|
|
2051
|
-
err ? reject(err) : resolve()))
|
|
2372
|
+
return /** @type {Promise<void>} */ (
|
|
2373
|
+
new Promise((resolve, reject) =>
|
|
2374
|
+
this.unsubscribeUser(pattern, options, err => (err ? reject(err) : resolve()))
|
|
2375
|
+
)
|
|
2376
|
+
);
|
|
2052
2377
|
}
|
|
2053
2378
|
|
|
2054
2379
|
async _objectHelper(keys, objs, callback) {
|
|
2055
2380
|
if (!keys || !keys.length) {
|
|
2056
|
-
|
|
2381
|
+
return tools.maybeCallback(callback);
|
|
2057
2382
|
} else {
|
|
2058
2383
|
if (!this.client) {
|
|
2059
2384
|
return typeof callback === 'function' && callback(utils.ERRORS.ERROR_DB_CLOSED);
|
|
@@ -2062,7 +2387,28 @@ class ObjectsInRedisClient {
|
|
|
2062
2387
|
const obj = objs.shift();
|
|
2063
2388
|
const message = JSON.stringify(obj);
|
|
2064
2389
|
try {
|
|
2065
|
-
|
|
2390
|
+
const commands = [];
|
|
2391
|
+
if (this.useSets) {
|
|
2392
|
+
if (obj.type) {
|
|
2393
|
+
// e.g. _design/ has no type
|
|
2394
|
+
// add the object to the set + set object atomic
|
|
2395
|
+
commands.push(['sadd', `${this.setNamespace}object.type.${obj.type}`, id]);
|
|
2396
|
+
}
|
|
2397
|
+
|
|
2398
|
+
if (obj.common && obj.common.custom) {
|
|
2399
|
+
// add to "common" set
|
|
2400
|
+
commands.push(['sadd', `${this.setNamespace}object.common.custom`, id]);
|
|
2401
|
+
}
|
|
2402
|
+
}
|
|
2403
|
+
|
|
2404
|
+
if (!commands.length) {
|
|
2405
|
+
// only set
|
|
2406
|
+
await this.client.set(id, message);
|
|
2407
|
+
} else {
|
|
2408
|
+
// set all commands atomic
|
|
2409
|
+
commands.push(['set', id, message]);
|
|
2410
|
+
await this.client.multi(commands).exec();
|
|
2411
|
+
}
|
|
2066
2412
|
await this.client.publish(id, message);
|
|
2067
2413
|
} catch (e) {
|
|
2068
2414
|
return tools.maybeCallbackWithRedisError(callback, e);
|
|
@@ -2073,52 +2419,67 @@ class ObjectsInRedisClient {
|
|
|
2073
2419
|
}
|
|
2074
2420
|
|
|
2075
2421
|
_chownObject(pattern, options, callback) {
|
|
2076
|
-
this.getConfigKeys(
|
|
2077
|
-
|
|
2078
|
-
|
|
2079
|
-
|
|
2080
|
-
|
|
2081
|
-
|
|
2082
|
-
|
|
2422
|
+
this.getConfigKeys(
|
|
2423
|
+
pattern,
|
|
2424
|
+
options,
|
|
2425
|
+
async (err, keys) => {
|
|
2426
|
+
if (err) {
|
|
2427
|
+
return tools.maybeCallbackWithError(callback, err);
|
|
2428
|
+
}
|
|
2429
|
+
if (!this.client) {
|
|
2430
|
+
return tools.maybeCallbackWithError(callback, utils.ERRORS.ERROR_DB_CLOSED);
|
|
2431
|
+
}
|
|
2083
2432
|
|
|
2084
|
-
|
|
2085
|
-
try {
|
|
2086
|
-
objects = await this.client.mget(keys);
|
|
2087
|
-
} catch (e) {
|
|
2088
|
-
return tools.maybeCallbackWithRedisError(callback, e);
|
|
2089
|
-
}
|
|
2090
|
-
const filteredKeys = [];
|
|
2091
|
-
const filteredObjs = [];
|
|
2092
|
-
objects = objects || [];
|
|
2093
|
-
for (let k = 0; k < keys.length; k++) {
|
|
2433
|
+
let objects;
|
|
2094
2434
|
try {
|
|
2095
|
-
objects
|
|
2096
|
-
} catch {
|
|
2097
|
-
|
|
2098
|
-
continue;
|
|
2099
|
-
}
|
|
2100
|
-
if (!utils.checkObject(objects[k], options, utils.CONSTS.ACCESS_WRITE)) {
|
|
2101
|
-
continue;
|
|
2435
|
+
objects = await this.client.mget(keys);
|
|
2436
|
+
} catch (e) {
|
|
2437
|
+
return tools.maybeCallbackWithRedisError(callback, e);
|
|
2102
2438
|
}
|
|
2103
|
-
|
|
2104
|
-
|
|
2105
|
-
|
|
2106
|
-
|
|
2107
|
-
|
|
2108
|
-
|
|
2109
|
-
|
|
2110
|
-
|
|
2439
|
+
const filteredKeys = [];
|
|
2440
|
+
const filteredObjs = [];
|
|
2441
|
+
objects = objects || [];
|
|
2442
|
+
for (let k = 0; k < keys.length; k++) {
|
|
2443
|
+
try {
|
|
2444
|
+
objects[k] = JSON.parse(objects[k]);
|
|
2445
|
+
} catch {
|
|
2446
|
+
this.log.error(`${this.namespace} Cannot parse JSON ${keys[k]}: ${objects[k]}`);
|
|
2447
|
+
continue;
|
|
2448
|
+
}
|
|
2449
|
+
if (!utils.checkObject(objects[k], options, utils.CONSTS.ACCESS_WRITE)) {
|
|
2450
|
+
continue;
|
|
2451
|
+
}
|
|
2452
|
+
if (!objects[k].acl) {
|
|
2453
|
+
objects[k].acl = {
|
|
2454
|
+
owner: (this.defaultNewAcl && this.defaultNewAcl.owner) || utils.CONSTS.SYSTEM_ADMIN_USER,
|
|
2455
|
+
ownerGroup:
|
|
2456
|
+
(this.defaultNewAcl && this.defaultNewAcl.ownerGroup) ||
|
|
2457
|
+
utils.CONSTS.SYSTEM_ADMIN_GROUP,
|
|
2458
|
+
object:
|
|
2459
|
+
(this.defaultNewAcl && this.defaultNewAcl.object) ||
|
|
2460
|
+
utils.CONSTS.ACCESS_USER_RW |
|
|
2461
|
+
utils.CONSTS.ACCESS_GROUP_READ |
|
|
2462
|
+
utils.CONSTS.ACCESS_EVERY_READ // '0644'
|
|
2463
|
+
};
|
|
2464
|
+
if (objects[k].type === 'state') {
|
|
2465
|
+
objects[k].acl.state =
|
|
2466
|
+
(this.defaultNewAcl && this.defaultNewAcl.state) ||
|
|
2467
|
+
utils.CONSTS.ACCESS_USER_RW |
|
|
2468
|
+
utils.CONSTS.ACCESS_GROUP_READ |
|
|
2469
|
+
utils.CONSTS.ACCESS_EVERY_READ; // '0644'
|
|
2470
|
+
}
|
|
2111
2471
|
}
|
|
2472
|
+
objects[k].acl.owner = options.owner;
|
|
2473
|
+
objects[k].acl.ownerGroup = options.ownerGroup;
|
|
2474
|
+
filteredKeys.push(keys[k]);
|
|
2475
|
+
filteredObjs.push(objects[k]);
|
|
2112
2476
|
}
|
|
2113
|
-
|
|
2114
|
-
|
|
2115
|
-
|
|
2116
|
-
|
|
2117
|
-
|
|
2118
|
-
|
|
2119
|
-
return tools.maybeCallbackWithError(callback, null, filteredObjs);
|
|
2120
|
-
});
|
|
2121
|
-
}, true);
|
|
2477
|
+
this._objectHelper(filteredKeys, filteredObjs, () => {
|
|
2478
|
+
return tools.maybeCallbackWithError(callback, null, filteredObjs);
|
|
2479
|
+
});
|
|
2480
|
+
},
|
|
2481
|
+
true
|
|
2482
|
+
);
|
|
2122
2483
|
}
|
|
2123
2484
|
|
|
2124
2485
|
chownObject(pattern, options, callback) {
|
|
@@ -2130,13 +2491,13 @@ class ObjectsInRedisClient {
|
|
|
2130
2491
|
options.acl = null;
|
|
2131
2492
|
|
|
2132
2493
|
if (typeof options !== 'object') {
|
|
2133
|
-
options = {owner: options};
|
|
2494
|
+
options = { owner: options };
|
|
2134
2495
|
}
|
|
2135
2496
|
|
|
2136
2497
|
if (!options.ownerGroup && options.group) {
|
|
2137
2498
|
options.ownerGroup = options.group;
|
|
2138
2499
|
}
|
|
2139
|
-
if (!options.owner && options.user)
|
|
2500
|
+
if (!options.owner && options.user) {
|
|
2140
2501
|
options.owner = options.user;
|
|
2141
2502
|
}
|
|
2142
2503
|
|
|
@@ -2160,7 +2521,7 @@ class ObjectsInRedisClient {
|
|
|
2160
2521
|
|
|
2161
2522
|
utils.checkObjectRights(this, null, null, options, utils.CONSTS.ACCESS_WRITE, (err, options) => {
|
|
2162
2523
|
if (err) {
|
|
2163
|
-
return tools.
|
|
2524
|
+
return tools.maybeCallbackWithRedisError(callback, err);
|
|
2164
2525
|
} else {
|
|
2165
2526
|
if (!options.acl.object || !options.acl.object.write) {
|
|
2166
2527
|
return tools.maybeCallbackWithError(callback, utils.ERRORS.ERROR_PERMISSION);
|
|
@@ -2173,62 +2534,77 @@ class ObjectsInRedisClient {
|
|
|
2173
2534
|
|
|
2174
2535
|
chownObjectAsync(pattern, options) {
|
|
2175
2536
|
return new Promise((resolve, reject) =>
|
|
2176
|
-
this.chownObject(pattern, options, (err, list) =>
|
|
2177
|
-
|
|
2537
|
+
this.chownObject(pattern, options, (err, list) => (err ? reject(err) : resolve(list)))
|
|
2538
|
+
);
|
|
2178
2539
|
}
|
|
2179
2540
|
|
|
2180
2541
|
_chmodObject(pattern, options, callback) {
|
|
2181
|
-
this.getConfigKeys(
|
|
2182
|
-
|
|
2183
|
-
|
|
2184
|
-
|
|
2185
|
-
|
|
2186
|
-
|
|
2187
|
-
|
|
2188
|
-
|
|
2189
|
-
|
|
2190
|
-
|
|
2191
|
-
objects = await this.client.mget(keys);
|
|
2192
|
-
} catch (e) {
|
|
2193
|
-
return tools.maybeCallbackWithRedisError(callback, e);
|
|
2194
|
-
}
|
|
2542
|
+
this.getConfigKeys(
|
|
2543
|
+
pattern,
|
|
2544
|
+
options,
|
|
2545
|
+
async (err, keys) => {
|
|
2546
|
+
if (err) {
|
|
2547
|
+
return tools.maybeCallbackWithRedisError(callback, err);
|
|
2548
|
+
}
|
|
2549
|
+
if (!this.client) {
|
|
2550
|
+
return tools.maybeCallbackWithRedisError(callback, utils.ERRORS.ERROR_DB_CLOSED);
|
|
2551
|
+
}
|
|
2195
2552
|
|
|
2196
|
-
|
|
2197
|
-
const filteredObjs = [];
|
|
2198
|
-
objects = objects || [];
|
|
2199
|
-
for (let k = 0; k < keys.length; k++) {
|
|
2553
|
+
let objects;
|
|
2200
2554
|
try {
|
|
2201
|
-
objects
|
|
2202
|
-
} catch {
|
|
2203
|
-
|
|
2204
|
-
continue;
|
|
2205
|
-
}
|
|
2206
|
-
if (!utils.checkObject(objects[k], options, utils.CONSTS.ACCESS_WRITE)) {
|
|
2207
|
-
continue;
|
|
2555
|
+
objects = await this.client.mget(keys);
|
|
2556
|
+
} catch (e) {
|
|
2557
|
+
return tools.maybeCallbackWithRedisError(callback, e);
|
|
2208
2558
|
}
|
|
2209
|
-
|
|
2210
|
-
|
|
2211
|
-
|
|
2212
|
-
|
|
2213
|
-
|
|
2214
|
-
|
|
2215
|
-
|
|
2216
|
-
|
|
2559
|
+
|
|
2560
|
+
const filteredKeys = [];
|
|
2561
|
+
const filteredObjs = [];
|
|
2562
|
+
objects = objects || [];
|
|
2563
|
+
for (let k = 0; k < keys.length; k++) {
|
|
2564
|
+
try {
|
|
2565
|
+
objects[k] = JSON.parse(objects[k]);
|
|
2566
|
+
} catch {
|
|
2567
|
+
this.log.error(`${this.namespace} Cannot parse JSON ${keys[k]}: ${objects[k]}`);
|
|
2568
|
+
continue;
|
|
2217
2569
|
}
|
|
2570
|
+
if (!utils.checkObject(objects[k], options, utils.CONSTS.ACCESS_WRITE)) {
|
|
2571
|
+
continue;
|
|
2572
|
+
}
|
|
2573
|
+
if (!objects[k].acl) {
|
|
2574
|
+
objects[k].acl = {
|
|
2575
|
+
owner: (this.defaultNewAcl && this.defaultNewAcl.owner) || utils.CONSTS.SYSTEM_ADMIN_USER,
|
|
2576
|
+
ownerGroup:
|
|
2577
|
+
(this.defaultNewAcl && this.defaultNewAcl.ownerGroup) ||
|
|
2578
|
+
utils.CONSTS.SYSTEM_ADMIN_GROUP,
|
|
2579
|
+
object:
|
|
2580
|
+
(this.defaultNewAcl && this.defaultNewAcl.object) ||
|
|
2581
|
+
utils.CONSTS.ACCESS_USER_RW |
|
|
2582
|
+
utils.CONSTS.ACCESS_GROUP_READ |
|
|
2583
|
+
utils.CONSTS.ACCESS_EVERY_READ // '0644'
|
|
2584
|
+
};
|
|
2585
|
+
if (objects[k].type === 'state') {
|
|
2586
|
+
objects[k].acl.state =
|
|
2587
|
+
(this.defaultNewAcl && this.defaultNewAcl.state) ||
|
|
2588
|
+
utils.CONSTS.ACCESS_USER_RW |
|
|
2589
|
+
utils.CONSTS.ACCESS_GROUP_READ |
|
|
2590
|
+
utils.CONSTS.ACCESS_EVERY_READ; // '0644'
|
|
2591
|
+
}
|
|
2592
|
+
}
|
|
2593
|
+
if (options.object !== undefined) {
|
|
2594
|
+
objects[k].acl.object = options.object;
|
|
2595
|
+
}
|
|
2596
|
+
if (options.state !== undefined) {
|
|
2597
|
+
objects[k].acl.state = options.state;
|
|
2598
|
+
}
|
|
2599
|
+
filteredKeys.push(keys[k]);
|
|
2600
|
+
filteredObjs.push(objects[k]);
|
|
2218
2601
|
}
|
|
2219
|
-
|
|
2220
|
-
|
|
2221
|
-
}
|
|
2222
|
-
|
|
2223
|
-
|
|
2224
|
-
|
|
2225
|
-
filteredKeys.push(keys[k]);
|
|
2226
|
-
filteredObjs.push(objects[k]);
|
|
2227
|
-
}
|
|
2228
|
-
this._objectHelper(filteredKeys, filteredObjs, () => {
|
|
2229
|
-
return tools.maybeCallbackWithError(callback, null, filteredObjs);
|
|
2230
|
-
});
|
|
2231
|
-
}, true);
|
|
2602
|
+
this._objectHelper(filteredKeys, filteredObjs, () => {
|
|
2603
|
+
return tools.maybeCallbackWithError(callback, null, filteredObjs);
|
|
2604
|
+
});
|
|
2605
|
+
},
|
|
2606
|
+
true
|
|
2607
|
+
);
|
|
2232
2608
|
}
|
|
2233
2609
|
|
|
2234
2610
|
chmodObject(pattern, options, callback) {
|
|
@@ -2240,7 +2616,7 @@ class ObjectsInRedisClient {
|
|
|
2240
2616
|
options.acl = null;
|
|
2241
2617
|
|
|
2242
2618
|
if (typeof options !== 'object') {
|
|
2243
|
-
options = {object: options};
|
|
2619
|
+
options = { object: options };
|
|
2244
2620
|
}
|
|
2245
2621
|
|
|
2246
2622
|
if (options.mode && !options.object) {
|
|
@@ -2248,18 +2624,18 @@ class ObjectsInRedisClient {
|
|
|
2248
2624
|
}
|
|
2249
2625
|
|
|
2250
2626
|
if (options.object === undefined) {
|
|
2251
|
-
this.log.error(this.namespace
|
|
2252
|
-
return
|
|
2627
|
+
this.log.error(`${this.namespace} mode is not defined`);
|
|
2628
|
+
return tools.maybeCallbackWithError(callback, 'invalid parameter');
|
|
2253
2629
|
} else if (typeof options.mode === 'string') {
|
|
2254
2630
|
options.mode = parseInt(options.mode, 16);
|
|
2255
2631
|
}
|
|
2256
2632
|
|
|
2257
2633
|
utils.checkObjectRights(this, null, null, options, utils.CONSTS.ACCESS_WRITE, (err, options) => {
|
|
2258
2634
|
if (err) {
|
|
2259
|
-
|
|
2635
|
+
return tools.maybeCallbackWithRedisError(callback, err);
|
|
2260
2636
|
} else {
|
|
2261
2637
|
if (!options.acl.file.write) {
|
|
2262
|
-
|
|
2638
|
+
return tools.maybeCallbackWithError(callback, utils.ERRORS.ERROR_PERMISSION);
|
|
2263
2639
|
} else {
|
|
2264
2640
|
return this._chmodObject(pattern, options, callback);
|
|
2265
2641
|
}
|
|
@@ -2269,8 +2645,8 @@ class ObjectsInRedisClient {
|
|
|
2269
2645
|
|
|
2270
2646
|
chmodObjectAsync(pattern, options) {
|
|
2271
2647
|
return new Promise((resolve, reject) =>
|
|
2272
|
-
this.chmodObject(pattern, options, (err, list) =>
|
|
2273
|
-
|
|
2648
|
+
this.chmodObject(pattern, options, (err, list) => (err ? reject(err) : resolve(list)))
|
|
2649
|
+
);
|
|
2274
2650
|
}
|
|
2275
2651
|
|
|
2276
2652
|
async _getObject(id, options, callback) {
|
|
@@ -2317,8 +2693,8 @@ class ObjectsInRedisClient {
|
|
|
2317
2693
|
}
|
|
2318
2694
|
if (!callback) {
|
|
2319
2695
|
return new Promise((resolve, reject) =>
|
|
2320
|
-
this.getObject(id, options, (err, obj) =>
|
|
2321
|
-
|
|
2696
|
+
this.getObject(id, options, (err, obj) => (err ? reject(err) : resolve(obj)))
|
|
2697
|
+
);
|
|
2322
2698
|
}
|
|
2323
2699
|
|
|
2324
2700
|
if (typeof callback === 'function') {
|
|
@@ -2337,8 +2713,8 @@ class ObjectsInRedisClient {
|
|
|
2337
2713
|
|
|
2338
2714
|
getObjectAsync(id, options) {
|
|
2339
2715
|
return new Promise((resolve, reject) =>
|
|
2340
|
-
this.getObject(id, options, (err, obj) =>
|
|
2341
|
-
|
|
2716
|
+
this.getObject(id, options, (err, obj) => (err ? reject(err) : resolve(obj)))
|
|
2717
|
+
);
|
|
2342
2718
|
}
|
|
2343
2719
|
|
|
2344
2720
|
async _getKeys(pattern, options, callback, dontModify) {
|
|
@@ -2367,9 +2743,9 @@ class ObjectsInRedisClient {
|
|
|
2367
2743
|
keys.sort();
|
|
2368
2744
|
const result = [];
|
|
2369
2745
|
const dontCheck =
|
|
2370
|
-
|
|
2371
|
-
|
|
2372
|
-
|
|
2746
|
+
options.user === utils.CONSTS.SYSTEM_ADMIN_USER ||
|
|
2747
|
+
options.group !== utils.CONSTS.SYSTEM_ADMIN_GROUP ||
|
|
2748
|
+
(options.groups && options.groups.indexOf(utils.CONSTS.SYSTEM_ADMIN_GROUP) !== -1);
|
|
2373
2749
|
|
|
2374
2750
|
if (dontCheck) {
|
|
2375
2751
|
for (let i = 0; i < keys.length; i++) {
|
|
@@ -2422,13 +2798,13 @@ class ObjectsInRedisClient {
|
|
|
2422
2798
|
}
|
|
2423
2799
|
if (!callback) {
|
|
2424
2800
|
return new Promise((resolve, reject) =>
|
|
2425
|
-
this.getKeys(pattern, options, (err, obj) =>
|
|
2426
|
-
|
|
2801
|
+
this.getKeys(pattern, options, (err, obj) => (err ? reject(err) : resolve(obj)), dontModify)
|
|
2802
|
+
);
|
|
2427
2803
|
}
|
|
2428
2804
|
if (typeof callback === 'function') {
|
|
2429
2805
|
utils.checkObjectRights(this, null, null, options, 'list', (err, options) => {
|
|
2430
2806
|
if (err) {
|
|
2431
|
-
|
|
2807
|
+
return tools.maybeCallbackWithRedisError(callback, err);
|
|
2432
2808
|
} else {
|
|
2433
2809
|
return this._getKeys(pattern, options, callback, dontModify);
|
|
2434
2810
|
}
|
|
@@ -2438,8 +2814,8 @@ class ObjectsInRedisClient {
|
|
|
2438
2814
|
|
|
2439
2815
|
getKeysAsync(id, options) {
|
|
2440
2816
|
return new Promise((resolve, reject) =>
|
|
2441
|
-
this.getKeys(id, options, (err, keys) =>
|
|
2442
|
-
|
|
2817
|
+
this.getKeys(id, options, (err, keys) => (err ? reject(err) : resolve(keys)))
|
|
2818
|
+
);
|
|
2443
2819
|
}
|
|
2444
2820
|
|
|
2445
2821
|
getConfigKeys(pattern, options, callback, dontModify) {
|
|
@@ -2471,7 +2847,8 @@ class ObjectsInRedisClient {
|
|
|
2471
2847
|
let objs;
|
|
2472
2848
|
try {
|
|
2473
2849
|
objs = await this.client.mget(_keys);
|
|
2474
|
-
this.settings.connection.enhancedLogging &&
|
|
2850
|
+
this.settings.connection.enhancedLogging &&
|
|
2851
|
+
this.log.silly(this.namespace + ' redis mget ' + (!objs ? 0 : objs.length) + ' ' + _keys.length);
|
|
2475
2852
|
} catch (e) {
|
|
2476
2853
|
this.log.warn(`${this.namespace} redis mget ${!objs ? 0 : objs.length} ${_keys.length}, err: ${e.message}`);
|
|
2477
2854
|
}
|
|
@@ -2479,9 +2856,9 @@ class ObjectsInRedisClient {
|
|
|
2479
2856
|
|
|
2480
2857
|
if (objs) {
|
|
2481
2858
|
const dontCheck =
|
|
2482
|
-
|
|
2483
|
-
|
|
2484
|
-
|
|
2859
|
+
options.user === utils.CONSTS.SYSTEM_ADMIN_USER ||
|
|
2860
|
+
options.group !== utils.CONSTS.SYSTEM_ADMIN_GROUP ||
|
|
2861
|
+
(options.groups && options.groups.indexOf(utils.CONSTS.SYSTEM_ADMIN_GROUP) !== -1);
|
|
2485
2862
|
|
|
2486
2863
|
if (!dontCheck) {
|
|
2487
2864
|
for (let i = 0; i < objs.length; i++) {
|
|
@@ -2489,13 +2866,13 @@ class ObjectsInRedisClient {
|
|
|
2489
2866
|
objs[i] = JSON.parse(objs[i]);
|
|
2490
2867
|
} catch {
|
|
2491
2868
|
this.log.error(`${this.namespace} Cannot parse JSON ${_keys[i]}: ${objs[i]}`);
|
|
2492
|
-
result.push({error: utils.ERRORS.ERROR_PERMISSION});
|
|
2869
|
+
result.push({ error: utils.ERRORS.ERROR_PERMISSION });
|
|
2493
2870
|
continue;
|
|
2494
2871
|
}
|
|
2495
2872
|
if (utils.checkObject(objs[i], options, utils.CONSTS.ACCESS_READ)) {
|
|
2496
2873
|
result.push(objs[i]);
|
|
2497
2874
|
} else {
|
|
2498
|
-
result.push({error: utils.ERRORS.ERROR_PERMISSION});
|
|
2875
|
+
result.push({ error: utils.ERRORS.ERROR_PERMISSION });
|
|
2499
2876
|
}
|
|
2500
2877
|
}
|
|
2501
2878
|
} else {
|
|
@@ -2519,8 +2896,8 @@ class ObjectsInRedisClient {
|
|
|
2519
2896
|
}
|
|
2520
2897
|
if (!callback) {
|
|
2521
2898
|
return new Promise((resolve, reject) =>
|
|
2522
|
-
this.getObjects(keys, options, (err, objs) =>
|
|
2523
|
-
|
|
2899
|
+
this.getObjects(keys, options, (err, objs) => (err ? reject(err) : resolve(objs)), dontModify)
|
|
2900
|
+
);
|
|
2524
2901
|
}
|
|
2525
2902
|
|
|
2526
2903
|
if (options && options.acl) {
|
|
@@ -2529,7 +2906,7 @@ class ObjectsInRedisClient {
|
|
|
2529
2906
|
if (typeof callback === 'function') {
|
|
2530
2907
|
utils.checkObjectRights(this, null, null, options, utils.CONSTS.ACCESS_READ, (err, options) => {
|
|
2531
2908
|
if (err) {
|
|
2532
|
-
|
|
2909
|
+
return tools.maybeCallbackWithRedisError(callback, err);
|
|
2533
2910
|
} else {
|
|
2534
2911
|
return this._getObjects(keys, options, callback, dontModify);
|
|
2535
2912
|
}
|
|
@@ -2539,18 +2916,17 @@ class ObjectsInRedisClient {
|
|
|
2539
2916
|
|
|
2540
2917
|
getObjectsAsync(id, options) {
|
|
2541
2918
|
return new Promise((resolve, reject) =>
|
|
2542
|
-
this.getObjects(id, options, (err, objs) =>
|
|
2543
|
-
|
|
2919
|
+
this.getObjects(id, options, (err, objs) => (err ? reject(err) : resolve(objs)))
|
|
2920
|
+
);
|
|
2544
2921
|
}
|
|
2545
2922
|
|
|
2546
2923
|
async _getObjectsByPattern(pattern, options, callback) {
|
|
2547
2924
|
if (!pattern || typeof pattern !== 'string') {
|
|
2548
|
-
|
|
2549
|
-
return;
|
|
2925
|
+
return tools.maybeCallbackWithError(callback, `invalid pattern ${JSON.stringify(pattern)}`);
|
|
2550
2926
|
}
|
|
2551
2927
|
|
|
2552
2928
|
if (!this.client) {
|
|
2553
|
-
return
|
|
2929
|
+
return tools.maybeCallbackWithRedisError(callback, utils.ERRORS.ERROR_DB_CLOSED);
|
|
2554
2930
|
}
|
|
2555
2931
|
|
|
2556
2932
|
let keys;
|
|
@@ -2563,7 +2939,8 @@ class ObjectsInRedisClient {
|
|
|
2563
2939
|
return tools.maybeCallbackWithError(callback, utils.ERRORS.ERROR_DB_CLOSED);
|
|
2564
2940
|
}
|
|
2565
2941
|
|
|
2566
|
-
this.settings.connection.enhancedLogging &&
|
|
2942
|
+
this.settings.connection.enhancedLogging &&
|
|
2943
|
+
this.log.silly(`${this.namespace} redis keys ${keys.length} ${pattern}`);
|
|
2567
2944
|
this._getObjects(keys, options, callback, true);
|
|
2568
2945
|
}
|
|
2569
2946
|
|
|
@@ -2574,8 +2951,8 @@ class ObjectsInRedisClient {
|
|
|
2574
2951
|
}
|
|
2575
2952
|
if (!callback) {
|
|
2576
2953
|
return new Promise((resolve, reject) =>
|
|
2577
|
-
this.getObjectsByPattern(pattern, options, (err, obj) =>
|
|
2578
|
-
|
|
2954
|
+
this.getObjectsByPattern(pattern, options, (err, obj) => (err ? reject(err) : resolve(obj)))
|
|
2955
|
+
);
|
|
2579
2956
|
}
|
|
2580
2957
|
if (options && options.acl) {
|
|
2581
2958
|
options.acl = null;
|
|
@@ -2583,7 +2960,7 @@ class ObjectsInRedisClient {
|
|
|
2583
2960
|
if (typeof callback === 'function') {
|
|
2584
2961
|
utils.checkObjectRights(this, null, null, options, utils.CONSTS.ACCESS_READ, (err, options) => {
|
|
2585
2962
|
if (err) {
|
|
2586
|
-
|
|
2963
|
+
return tools.maybeCallbackWithRedisError(callback, err);
|
|
2587
2964
|
} else {
|
|
2588
2965
|
return this._getObjectsByPattern(pattern, options, callback);
|
|
2589
2966
|
}
|
|
@@ -2593,25 +2970,25 @@ class ObjectsInRedisClient {
|
|
|
2593
2970
|
|
|
2594
2971
|
getObjectsByPatternAsync(pattern, options) {
|
|
2595
2972
|
return new Promise((resolve, reject) =>
|
|
2596
|
-
this.getObjectsByPattern(pattern, options, (err, objs) =>
|
|
2597
|
-
|
|
2973
|
+
this.getObjectsByPattern(pattern, options, (err, objs) => (err ? reject(err) : resolve(objs)))
|
|
2974
|
+
);
|
|
2598
2975
|
}
|
|
2599
2976
|
|
|
2600
2977
|
async _setObject(id, obj, options, callback) {
|
|
2601
2978
|
if (!id || typeof id !== 'string' || utils.regCheckId.test(id)) {
|
|
2602
|
-
return
|
|
2979
|
+
return tools.maybeCallbackWithError(callback, `Invalid ID: ${id}`);
|
|
2603
2980
|
}
|
|
2604
2981
|
|
|
2605
2982
|
if (!obj) {
|
|
2606
2983
|
this.log.warn(this.namespace + ' setObject: Argument object is null');
|
|
2607
|
-
return
|
|
2984
|
+
return tools.maybeCallbackWithError(callback, 'obj is null');
|
|
2608
2985
|
}
|
|
2609
2986
|
if (!tools.isObject(obj)) {
|
|
2610
2987
|
this.log.warn(this.namespace + ' setObject: Argument object is no object: ' + obj);
|
|
2611
|
-
return
|
|
2988
|
+
return tools.maybeCallbackWithError(callback, 'obj is no object');
|
|
2612
2989
|
}
|
|
2613
2990
|
if (!this.client) {
|
|
2614
|
-
return
|
|
2991
|
+
return tools.maybeCallbackWithRedisError(callback, utils.ERRORS.ERROR_DB_CLOSED);
|
|
2615
2992
|
}
|
|
2616
2993
|
// make a copy of object, because we will modify it
|
|
2617
2994
|
obj = deepClone(obj);
|
|
@@ -2636,17 +3013,29 @@ class ObjectsInRedisClient {
|
|
|
2636
3013
|
return tools.maybeCallbackWithError(callback, 'Invalid password for update of vendor information');
|
|
2637
3014
|
}
|
|
2638
3015
|
|
|
3016
|
+
// we need to know if custom has been added/deleted
|
|
3017
|
+
const oldObjHasCustom = oldObj && oldObj.common && oldObj.common && oldObj.common.custom;
|
|
3018
|
+
|
|
2639
3019
|
// do not delete common settings, like "history" or "mobile". It can be erased only with "null"
|
|
2640
3020
|
if (oldObj && oldObj.common) {
|
|
2641
3021
|
this.preserveSettings.forEach(commonSetting => {
|
|
2642
3022
|
// special case if "custom"
|
|
2643
3023
|
if (commonSetting === 'custom') {
|
|
2644
3024
|
// we had broken objects where common.custom was a "non-object" ... check and fix them here, no warning because users will most likely have no idea how to deal with it
|
|
2645
|
-
if (
|
|
3025
|
+
if (
|
|
3026
|
+
oldObj.common.custom !== undefined &&
|
|
3027
|
+
oldObj.common.custom !== null &&
|
|
3028
|
+
!tools.isObject(oldObj.common.custom)
|
|
3029
|
+
) {
|
|
2646
3030
|
delete oldObj.common.custom;
|
|
2647
3031
|
}
|
|
2648
3032
|
// also remove invalid data from new objects ... should not happen because adapter.js checks too
|
|
2649
|
-
if (
|
|
3033
|
+
if (
|
|
3034
|
+
obj.common &&
|
|
3035
|
+
obj.common.custom !== undefined &&
|
|
3036
|
+
obj.common.custom !== null &&
|
|
3037
|
+
!tools.isObject(obj.common.custom)
|
|
3038
|
+
) {
|
|
2650
3039
|
delete obj.common.custom;
|
|
2651
3040
|
}
|
|
2652
3041
|
|
|
@@ -2680,9 +3069,11 @@ class ObjectsInRedisClient {
|
|
|
2680
3069
|
// remove settings if desired
|
|
2681
3070
|
if (obj.common && obj.common[commonSetting] === null) {
|
|
2682
3071
|
delete obj.common[commonSetting];
|
|
2683
|
-
} else
|
|
2684
|
-
|
|
2685
|
-
|
|
3072
|
+
} else if (
|
|
3073
|
+
// if old setting present and new setting is absent
|
|
3074
|
+
oldObj.common[commonSetting] !== undefined &&
|
|
3075
|
+
(!obj.common || obj.common[commonSetting] === undefined)
|
|
3076
|
+
) {
|
|
2686
3077
|
obj.common = obj.common || {};
|
|
2687
3078
|
obj.common[commonSetting] = oldObj.common[commonSetting];
|
|
2688
3079
|
}
|
|
@@ -2693,19 +3084,19 @@ class ObjectsInRedisClient {
|
|
|
2693
3084
|
if (obj.common && obj.common.alias && obj.common.alias.id) {
|
|
2694
3085
|
if (typeof obj.common.alias.id === 'object') {
|
|
2695
3086
|
if (typeof obj.common.alias.id.write !== 'string' || typeof obj.common.alias.id.read !== 'string') {
|
|
2696
|
-
return
|
|
3087
|
+
return tools.maybeCallbackWithError(callback, 'Invalid alias ID');
|
|
2697
3088
|
}
|
|
2698
3089
|
|
|
2699
3090
|
if (obj.common.alias.id.write.startsWith('alias.') || obj.common.alias.id.read.startsWith('alias.')) {
|
|
2700
|
-
return
|
|
3091
|
+
return tools.maybeCallbackWithError(callback, 'Cannot make alias on alias');
|
|
2701
3092
|
}
|
|
2702
3093
|
} else {
|
|
2703
3094
|
if (typeof obj.common.alias.id !== 'string') {
|
|
2704
|
-
return
|
|
3095
|
+
return tools.maybeCallbackWithError(callback, 'Invalid alias ID');
|
|
2705
3096
|
}
|
|
2706
3097
|
|
|
2707
3098
|
if (obj.common.alias.id.startsWith('alias.')) {
|
|
2708
|
-
return
|
|
3099
|
+
return tools.maybeCallbackWithError(callback, 'Cannot make alias on alias');
|
|
2709
3100
|
}
|
|
2710
3101
|
}
|
|
2711
3102
|
}
|
|
@@ -2738,7 +3129,39 @@ class ObjectsInRedisClient {
|
|
|
2738
3129
|
try {
|
|
2739
3130
|
const message = JSON.stringify(obj);
|
|
2740
3131
|
|
|
2741
|
-
|
|
3132
|
+
const commands = [];
|
|
3133
|
+
if (this.useSets) {
|
|
3134
|
+
if (obj.type && (!oldObj || !oldObj.type)) {
|
|
3135
|
+
// new object or oldObj had no type -> add to set + set object
|
|
3136
|
+
commands.push(['sadd', `${this.setNamespace}object.type.${obj.type}`, this.objNamespace + id]);
|
|
3137
|
+
} else if (obj.type && oldObj && oldObj.type && oldObj.type !== obj.type) {
|
|
3138
|
+
// the old obj had a type which differs from the new type -> rem old, add new
|
|
3139
|
+
commands.push(
|
|
3140
|
+
['sadd', `${this.setNamespace}object.type.${obj.type}`, this.objNamespace + id],
|
|
3141
|
+
['srem', `${this.setNamespace}object.type.${oldObj.type}`, this.objNamespace + id]
|
|
3142
|
+
);
|
|
3143
|
+
} else if (oldObj && oldObj.type && !obj.type) {
|
|
3144
|
+
// the oldObj had a type, the new one has no -> rem
|
|
3145
|
+
commands.push(['srem', `${this.setNamespace}object.type.${obj.type}`, this.objNamespace + id]);
|
|
3146
|
+
}
|
|
3147
|
+
|
|
3148
|
+
if (obj.common && obj.common.custom && !oldObjHasCustom) {
|
|
3149
|
+
// we now have custom, old object had no custom
|
|
3150
|
+
commands.push(['sadd', `${this.setNamespace}object.common.custom`, this.objNamespace + id]);
|
|
3151
|
+
} else if (oldObjHasCustom && (!obj.common || !obj.common.custom)) {
|
|
3152
|
+
// we no longer have custom
|
|
3153
|
+
commands.push(['srem', `${this.setNamespace}object.common.custom`, this.objNamespace + id]);
|
|
3154
|
+
}
|
|
3155
|
+
}
|
|
3156
|
+
|
|
3157
|
+
if (!commands.length) {
|
|
3158
|
+
await this.client.set(this.objNamespace + id, message);
|
|
3159
|
+
} else {
|
|
3160
|
+
// set all commands atomic
|
|
3161
|
+
commands.push(['set', this.objNamespace + id, message]);
|
|
3162
|
+
await this.client.multi(commands).exec();
|
|
3163
|
+
}
|
|
3164
|
+
|
|
2742
3165
|
//this.settings.connection.enhancedLogging && this.log.silly(this.namespace + ' redis publish ' + this.objNamespace + id + ' ' + message);
|
|
2743
3166
|
// object updated -> if type changed to meta -> cache
|
|
2744
3167
|
if (oldObj && oldObj.type === 'meta' && this.existingMetaObjects[id] === false) {
|
|
@@ -2747,9 +3170,9 @@ class ObjectsInRedisClient {
|
|
|
2747
3170
|
|
|
2748
3171
|
await this.client.publish(this.objNamespace + id, message);
|
|
2749
3172
|
|
|
2750
|
-
return tools.maybeCallbackWithError(callback, null, {id});
|
|
3173
|
+
return tools.maybeCallbackWithError(callback, null, { id });
|
|
2751
3174
|
} catch (e) {
|
|
2752
|
-
return tools.maybeCallbackWithRedisError(callback, e, {id});
|
|
3175
|
+
return tools.maybeCallbackWithRedisError(callback, e, { id });
|
|
2753
3176
|
}
|
|
2754
3177
|
}
|
|
2755
3178
|
|
|
@@ -2772,8 +3195,8 @@ class ObjectsInRedisClient {
|
|
|
2772
3195
|
}
|
|
2773
3196
|
if (!callback) {
|
|
2774
3197
|
return new Promise((resolve, reject) =>
|
|
2775
|
-
this.setObject(id, obj, options, (err, res) =>
|
|
2776
|
-
|
|
3198
|
+
this.setObject(id, obj, options, (err, res) => (err ? reject(err) : resolve(res)))
|
|
3199
|
+
);
|
|
2777
3200
|
}
|
|
2778
3201
|
if (options && options.acl) {
|
|
2779
3202
|
options.acl = null;
|
|
@@ -2782,7 +3205,7 @@ class ObjectsInRedisClient {
|
|
|
2782
3205
|
utils.checkObjectRights(this, null, null, options, utils.CONSTS.ACCESS_WRITE, err => {
|
|
2783
3206
|
// do not use options from checkObjectRights because this will mess up configured default acl
|
|
2784
3207
|
if (err) {
|
|
2785
|
-
|
|
3208
|
+
return tools.maybeCallbackWithRedisError(callback, err);
|
|
2786
3209
|
} else {
|
|
2787
3210
|
return this._setObject(id, obj, options || {}, callback);
|
|
2788
3211
|
}
|
|
@@ -2791,8 +3214,8 @@ class ObjectsInRedisClient {
|
|
|
2791
3214
|
|
|
2792
3215
|
setObjectAsync(id, obj, options) {
|
|
2793
3216
|
return new Promise((resolve, reject) =>
|
|
2794
|
-
this.setObject(id, obj, options, (err, res) =>
|
|
2795
|
-
|
|
3217
|
+
this.setObject(id, obj, options, (err, res) => (err ? reject(err) : resolve(res)))
|
|
3218
|
+
);
|
|
2796
3219
|
}
|
|
2797
3220
|
|
|
2798
3221
|
async _delObject(id, options, callback) {
|
|
@@ -2811,7 +3234,8 @@ class ObjectsInRedisClient {
|
|
|
2811
3234
|
this.log.warn(`${this.namespace} redis get ${id}, error - ${e.message}`);
|
|
2812
3235
|
}
|
|
2813
3236
|
|
|
2814
|
-
if (!oldObj) {
|
|
3237
|
+
if (!oldObj) {
|
|
3238
|
+
// Not existent, so goal reached :-)
|
|
2815
3239
|
return tools.maybeCallback(callback);
|
|
2816
3240
|
}
|
|
2817
3241
|
|
|
@@ -2826,7 +3250,34 @@ class ObjectsInRedisClient {
|
|
|
2826
3250
|
return tools.maybeCallbackWithError(callback, utils.ERRORS.ERROR_PERMISSION);
|
|
2827
3251
|
} else {
|
|
2828
3252
|
try {
|
|
2829
|
-
|
|
3253
|
+
const commands = [];
|
|
3254
|
+
|
|
3255
|
+
if (this.useSets) {
|
|
3256
|
+
if (oldObj.type) {
|
|
3257
|
+
// e.g. _design/ has no type
|
|
3258
|
+
// del the object from the set + del object atomic
|
|
3259
|
+
commands.push([
|
|
3260
|
+
'srem',
|
|
3261
|
+
`${this.setNamespace}object.type.${oldObj.type}`,
|
|
3262
|
+
this.objNamespace + id
|
|
3263
|
+
]);
|
|
3264
|
+
}
|
|
3265
|
+
|
|
3266
|
+
if (oldObj.common && oldObj.common.custom) {
|
|
3267
|
+
// del the object from "custom" set
|
|
3268
|
+
commands.push(['srem', `${this.setNamespace}object.common.custom`, this.objNamespace + id]);
|
|
3269
|
+
}
|
|
3270
|
+
}
|
|
3271
|
+
|
|
3272
|
+
if (!commands.length) {
|
|
3273
|
+
// only del
|
|
3274
|
+
await this.client.del(this.objNamespace + id);
|
|
3275
|
+
} else {
|
|
3276
|
+
// set all commands atomic
|
|
3277
|
+
commands.push(['del', this.objNamespace + id]);
|
|
3278
|
+
await this.client.multi(commands).exec();
|
|
3279
|
+
}
|
|
3280
|
+
|
|
2830
3281
|
// object has been deleted -> remove from cached meta if there
|
|
2831
3282
|
if (this.existingMetaObjects[id]) {
|
|
2832
3283
|
this.existingMetaObjects[id] = false;
|
|
@@ -2847,8 +3298,8 @@ class ObjectsInRedisClient {
|
|
|
2847
3298
|
}
|
|
2848
3299
|
if (!callback) {
|
|
2849
3300
|
return new Promise((resolve, reject) =>
|
|
2850
|
-
this.delObject(id, options, (err, obj) =>
|
|
2851
|
-
|
|
3301
|
+
this.delObject(id, options, (err, obj) => (err ? reject(err) : resolve(obj)))
|
|
3302
|
+
);
|
|
2852
3303
|
}
|
|
2853
3304
|
|
|
2854
3305
|
if (options && options.acl) {
|
|
@@ -2864,9 +3315,9 @@ class ObjectsInRedisClient {
|
|
|
2864
3315
|
}
|
|
2865
3316
|
|
|
2866
3317
|
delObjectAsync(id, options) {
|
|
2867
|
-
return /** @type {Promise<void>} */ (
|
|
2868
|
-
this.delObject(id, options, err =>
|
|
2869
|
-
|
|
3318
|
+
return /** @type {Promise<void>} */ (
|
|
3319
|
+
new Promise((resolve, reject) => this.delObject(id, options, err => (err ? reject(err) : resolve())))
|
|
3320
|
+
);
|
|
2870
3321
|
}
|
|
2871
3322
|
|
|
2872
3323
|
// this function is very ineffective. Because reads all objects and then process them
|
|
@@ -2906,7 +3357,7 @@ class ObjectsInRedisClient {
|
|
|
2906
3357
|
|
|
2907
3358
|
params = params || {};
|
|
2908
3359
|
params.startkey = params.startkey || '';
|
|
2909
|
-
params.endkey
|
|
3360
|
+
params.endkey = params.endkey || '\u9999';
|
|
2910
3361
|
const wildcardPos = params.endkey.indexOf('\u9999');
|
|
2911
3362
|
let wildCardLastPos = true;
|
|
2912
3363
|
if (wildcardPos !== -1 && wildcardPos !== params.endkey.length - 1) {
|
|
@@ -2920,7 +3371,13 @@ class ObjectsInRedisClient {
|
|
|
2920
3371
|
}
|
|
2921
3372
|
|
|
2922
3373
|
// filter by type
|
|
2923
|
-
if (
|
|
3374
|
+
if (
|
|
3375
|
+
wildCardLastPos &&
|
|
3376
|
+
func &&
|
|
3377
|
+
func.map &&
|
|
3378
|
+
this.scripts.filter &&
|
|
3379
|
+
(m = func.map.match(/if\s\(doc\.type\s?===?\s?'(\w+)'\)\semit\(([^,]+),\s?doc\s?\)/))
|
|
3380
|
+
) {
|
|
2924
3381
|
let cursor = '0';
|
|
2925
3382
|
let filterRequired = true;
|
|
2926
3383
|
do {
|
|
@@ -2930,7 +3387,16 @@ class ObjectsInRedisClient {
|
|
|
2930
3387
|
|
|
2931
3388
|
let objs;
|
|
2932
3389
|
try {
|
|
2933
|
-
objs = await this.client.evalsha([
|
|
3390
|
+
objs = await this.client.evalsha([
|
|
3391
|
+
this.scripts.filter,
|
|
3392
|
+
6,
|
|
3393
|
+
this.objNamespace,
|
|
3394
|
+
params.startkey,
|
|
3395
|
+
params.endkey,
|
|
3396
|
+
m[1],
|
|
3397
|
+
cursor,
|
|
3398
|
+
`${this.setNamespace}object.type.${m[1]}`
|
|
3399
|
+
]);
|
|
2934
3400
|
} catch (e) {
|
|
2935
3401
|
this.log.warn(`${this.namespace} Cannot get view: ${e.message}`);
|
|
2936
3402
|
}
|
|
@@ -2949,23 +3415,23 @@ class ObjectsInRedisClient {
|
|
|
2949
3415
|
obj = JSON.parse(obj);
|
|
2950
3416
|
} catch {
|
|
2951
3417
|
this.log.error(`${this.namespace} Cannot parse JSON: ${obj}`);
|
|
2952
|
-
return {id: 'parseError', value: null};
|
|
3418
|
+
return { id: 'parseError', value: null };
|
|
2953
3419
|
}
|
|
2954
3420
|
if (m[2] && m[2].trim() === 'doc._id') {
|
|
2955
|
-
return {id: obj._id, value: obj};
|
|
3421
|
+
return { id: obj._id, value: obj };
|
|
2956
3422
|
} else if (m[2] && m[2].trim() === 'doc.common.name' && obj.common) {
|
|
2957
3423
|
if (typeof obj.common.name === 'object') {
|
|
2958
3424
|
if (obj.common.name.en) {
|
|
2959
|
-
return {id: obj.common.name.en, value: obj};
|
|
3425
|
+
return { id: obj.common.name.en, value: obj };
|
|
2960
3426
|
} else {
|
|
2961
|
-
return {id: JSON.stringify(obj.common.name), value: obj};
|
|
3427
|
+
return { id: JSON.stringify(obj.common.name), value: obj };
|
|
2962
3428
|
}
|
|
2963
3429
|
} else {
|
|
2964
|
-
return {id: obj.common.name, value: obj};
|
|
3430
|
+
return { id: obj.common.name, value: obj };
|
|
2965
3431
|
}
|
|
2966
3432
|
} else {
|
|
2967
3433
|
this.log.error(`${this.namespace} Cannot filter "${m[2]}": ${JSON.stringify(obj)}`);
|
|
2968
|
-
return {id: 'parseError', value: null};
|
|
3434
|
+
return { id: 'parseError', value: null };
|
|
2969
3435
|
}
|
|
2970
3436
|
});
|
|
2971
3437
|
if (currRows.length) {
|
|
@@ -2982,7 +3448,7 @@ class ObjectsInRedisClient {
|
|
|
2982
3448
|
}
|
|
2983
3449
|
}
|
|
2984
3450
|
if (max !== null) {
|
|
2985
|
-
result.rows = [{id: '_stats', value: {max: max}}];
|
|
3451
|
+
result.rows = [{ id: '_stats', value: { max: max } }];
|
|
2986
3452
|
} else {
|
|
2987
3453
|
result.rows = [];
|
|
2988
3454
|
}
|
|
@@ -2991,9 +3457,14 @@ class ObjectsInRedisClient {
|
|
|
2991
3457
|
// apply filter if needed
|
|
2992
3458
|
result.rows = filterEntries(result.rows, filterRequired);
|
|
2993
3459
|
return tools.maybeCallbackWithError(callback, null, result);
|
|
2994
|
-
} else
|
|
2995
|
-
|
|
2996
|
-
|
|
3460
|
+
} else if (
|
|
3461
|
+
// filter by script
|
|
3462
|
+
wildCardLastPos &&
|
|
3463
|
+
func &&
|
|
3464
|
+
func.map &&
|
|
3465
|
+
this.scripts.script &&
|
|
3466
|
+
func.map.indexOf('doc.common.engineType') !== -1
|
|
3467
|
+
) {
|
|
2997
3468
|
let cursor = '0';
|
|
2998
3469
|
let filterRequired = true;
|
|
2999
3470
|
do {
|
|
@@ -3002,9 +3473,17 @@ class ObjectsInRedisClient {
|
|
|
3002
3473
|
}
|
|
3003
3474
|
let objs;
|
|
3004
3475
|
try {
|
|
3005
|
-
objs = await this.client.evalsha([
|
|
3476
|
+
objs = await this.client.evalsha([
|
|
3477
|
+
this.scripts.script,
|
|
3478
|
+
5,
|
|
3479
|
+
this.objNamespace,
|
|
3480
|
+
params.startkey,
|
|
3481
|
+
params.endkey,
|
|
3482
|
+
cursor,
|
|
3483
|
+
`${this.setNamespace}object.type.script`
|
|
3484
|
+
]);
|
|
3006
3485
|
} catch (e) {
|
|
3007
|
-
this.log.warn(`${this.namespace} Cannot get view: ${e.message}`);
|
|
3486
|
+
this.log.warn(`${this.namespace} Cannot get "scripts" view: ${e.message}`);
|
|
3008
3487
|
}
|
|
3009
3488
|
// if real redis we will have e.g. [[objs..], '0'], else [{}, .., {}]
|
|
3010
3489
|
if (Array.isArray(objs[0])) {
|
|
@@ -3020,9 +3499,9 @@ class ObjectsInRedisClient {
|
|
|
3020
3499
|
obj = JSON.parse(obj);
|
|
3021
3500
|
} catch {
|
|
3022
3501
|
this.log.error(`${this.namespace} Cannot parse JSON: ${obj}`);
|
|
3023
|
-
return {id: 'parseError', value: null};
|
|
3502
|
+
return { id: 'parseError', value: null };
|
|
3024
3503
|
}
|
|
3025
|
-
return {id: obj._id, value: obj};
|
|
3504
|
+
return { id: obj._id, value: obj };
|
|
3026
3505
|
});
|
|
3027
3506
|
if (currRows.length) {
|
|
3028
3507
|
result.rows = [...result.rows, ...currRows];
|
|
@@ -3032,9 +3511,15 @@ class ObjectsInRedisClient {
|
|
|
3032
3511
|
// apply filter if needed
|
|
3033
3512
|
result.rows = filterEntries(result.rows, filterRequired);
|
|
3034
3513
|
return tools.maybeCallbackWithError(callback, null, result);
|
|
3035
|
-
} else
|
|
3036
|
-
|
|
3037
|
-
|
|
3514
|
+
} else if (
|
|
3515
|
+
// filter by hm-rega programs
|
|
3516
|
+
|
|
3517
|
+
wildCardLastPos &&
|
|
3518
|
+
func &&
|
|
3519
|
+
func.map &&
|
|
3520
|
+
this.scripts.programs &&
|
|
3521
|
+
func.map.indexOf("doc.native.TypeName === 'PROGRAM'") !== -1
|
|
3522
|
+
) {
|
|
3038
3523
|
let cursor = '0';
|
|
3039
3524
|
let filterRequired = true;
|
|
3040
3525
|
do {
|
|
@@ -3044,7 +3529,15 @@ class ObjectsInRedisClient {
|
|
|
3044
3529
|
|
|
3045
3530
|
let objs;
|
|
3046
3531
|
try {
|
|
3047
|
-
objs = await this.client.evalsha([
|
|
3532
|
+
objs = await this.client.evalsha([
|
|
3533
|
+
this.scripts.programs,
|
|
3534
|
+
5,
|
|
3535
|
+
`${this.objNamespace}hm-rega.`,
|
|
3536
|
+
params.startkey,
|
|
3537
|
+
params.endkey,
|
|
3538
|
+
cursor,
|
|
3539
|
+
`${this.setNamespace}object.type.channel`
|
|
3540
|
+
]);
|
|
3048
3541
|
} catch (e) {
|
|
3049
3542
|
this.log.warn(`${this.namespace} Cannot get view: ${e.message}`);
|
|
3050
3543
|
}
|
|
@@ -3062,21 +3555,26 @@ class ObjectsInRedisClient {
|
|
|
3062
3555
|
obj = JSON.parse(obj);
|
|
3063
3556
|
} catch {
|
|
3064
3557
|
this.log.error(`${this.namespace} Cannot parse JSON: ${obj}`);
|
|
3065
|
-
return {id: 'parseError', value: null};
|
|
3558
|
+
return { id: 'parseError', value: null };
|
|
3066
3559
|
}
|
|
3067
|
-
return {id: obj._id, value: obj};
|
|
3560
|
+
return { id: obj._id, value: obj };
|
|
3068
3561
|
});
|
|
3069
3562
|
if (currRows.length) {
|
|
3070
3563
|
result.rows = [...result.rows, ...currRows];
|
|
3071
3564
|
} // endIf
|
|
3072
|
-
} while(cursor !== '0');
|
|
3565
|
+
} while (cursor !== '0');
|
|
3073
3566
|
|
|
3074
3567
|
// apply filter if needed
|
|
3075
3568
|
result.rows = filterEntries(result.rows, filterRequired);
|
|
3076
3569
|
return tools.maybeCallbackWithError(callback, null, result);
|
|
3077
|
-
} else
|
|
3078
|
-
|
|
3079
|
-
|
|
3570
|
+
} else if (
|
|
3571
|
+
// filter by hm-rega variables
|
|
3572
|
+
wildCardLastPos &&
|
|
3573
|
+
func &&
|
|
3574
|
+
func.map &&
|
|
3575
|
+
this.scripts.variables &&
|
|
3576
|
+
func.map.indexOf("doc.native.TypeName === 'ALARMDP'") !== -1
|
|
3577
|
+
) {
|
|
3080
3578
|
let cursor = '0';
|
|
3081
3579
|
let filterRequired = true;
|
|
3082
3580
|
do {
|
|
@@ -3086,7 +3584,15 @@ class ObjectsInRedisClient {
|
|
|
3086
3584
|
|
|
3087
3585
|
let objs;
|
|
3088
3586
|
try {
|
|
3089
|
-
objs = await this.client.evalsha([
|
|
3587
|
+
objs = await this.client.evalsha([
|
|
3588
|
+
this.scripts.variables,
|
|
3589
|
+
5,
|
|
3590
|
+
`${this.objNamespace}hm-rega.`,
|
|
3591
|
+
params.startkey,
|
|
3592
|
+
params.endkey,
|
|
3593
|
+
cursor,
|
|
3594
|
+
`${this.setNamespace}object.type.state`
|
|
3595
|
+
]);
|
|
3090
3596
|
} catch (e) {
|
|
3091
3597
|
this.log.warn(`${this.namespace} Cannot get view ${e.message}`);
|
|
3092
3598
|
}
|
|
@@ -3104,9 +3610,9 @@ class ObjectsInRedisClient {
|
|
|
3104
3610
|
obj = JSON.parse(obj);
|
|
3105
3611
|
} catch {
|
|
3106
3612
|
this.log.error(`${this.namespace} Cannot parse JSON: ${obj}`);
|
|
3107
|
-
return {id: 'parseError', value: null};
|
|
3613
|
+
return { id: 'parseError', value: null };
|
|
3108
3614
|
}
|
|
3109
|
-
return {id: obj._id, value: obj};
|
|
3615
|
+
return { id: obj._id, value: obj };
|
|
3110
3616
|
});
|
|
3111
3617
|
if (currRows.length) {
|
|
3112
3618
|
result.rows = [...result.rows, ...currRows];
|
|
@@ -3116,9 +3622,14 @@ class ObjectsInRedisClient {
|
|
|
3116
3622
|
// apply filter if needed
|
|
3117
3623
|
result.rows = filterEntries(result.rows, filterRequired);
|
|
3118
3624
|
typeof callback === 'function' && callback(null, result);
|
|
3119
|
-
} else
|
|
3120
|
-
|
|
3121
|
-
|
|
3625
|
+
} else if (
|
|
3626
|
+
// filter by custom, redis also returns if common.custom is not present
|
|
3627
|
+
wildCardLastPos &&
|
|
3628
|
+
func &&
|
|
3629
|
+
func.map &&
|
|
3630
|
+
this.scripts.custom &&
|
|
3631
|
+
func.map.indexOf('doc.common.custom') !== -1
|
|
3632
|
+
) {
|
|
3122
3633
|
let cursor = '0';
|
|
3123
3634
|
let filterRequired = true;
|
|
3124
3635
|
do {
|
|
@@ -3127,7 +3638,15 @@ class ObjectsInRedisClient {
|
|
|
3127
3638
|
}
|
|
3128
3639
|
let objs;
|
|
3129
3640
|
try {
|
|
3130
|
-
objs = await this.client.evalsha([
|
|
3641
|
+
objs = await this.client.evalsha([
|
|
3642
|
+
this.scripts.custom,
|
|
3643
|
+
5,
|
|
3644
|
+
this.objNamespace,
|
|
3645
|
+
params.startkey,
|
|
3646
|
+
params.endkey,
|
|
3647
|
+
cursor,
|
|
3648
|
+
`${this.setNamespace}object.common.custom`
|
|
3649
|
+
]);
|
|
3131
3650
|
} catch (e) {
|
|
3132
3651
|
this.log.warn(`${this.namespace} Cannot get view: ${e.message}`);
|
|
3133
3652
|
}
|
|
@@ -3148,7 +3667,7 @@ class ObjectsInRedisClient {
|
|
|
3148
3667
|
obj = null;
|
|
3149
3668
|
}
|
|
3150
3669
|
if (obj && obj.common && obj.common.custom) {
|
|
3151
|
-
result.rows.push({id: obj._id, value: obj.common.custom});
|
|
3670
|
+
result.rows.push({ id: obj._id, value: obj.common.custom });
|
|
3152
3671
|
}
|
|
3153
3672
|
});
|
|
3154
3673
|
} while (cursor !== '0');
|
|
@@ -3158,13 +3677,16 @@ class ObjectsInRedisClient {
|
|
|
3158
3677
|
return tools.maybeCallbackWithError(callback, null, result);
|
|
3159
3678
|
} else {
|
|
3160
3679
|
if (!wildCardLastPos) {
|
|
3161
|
-
this.log.debug(
|
|
3680
|
+
this.log.debug(
|
|
3681
|
+
`${this.namespace} Search can't be optimized because wildcard not at the end, fallback to keys!: ${func.map}`
|
|
3682
|
+
);
|
|
3162
3683
|
} else {
|
|
3163
3684
|
this.log.debug(`${this.namespace} No suitable Lua script, fallback to keys!: ${func.map}`);
|
|
3164
3685
|
}
|
|
3165
3686
|
|
|
3166
3687
|
let searchKeys = this.objNamespace + '*';
|
|
3167
|
-
if (wildcardPos !== -1) {
|
|
3688
|
+
if (wildcardPos !== -1) {
|
|
3689
|
+
// Wildcard included
|
|
3168
3690
|
searchKeys = this.objNamespace + params.endkey.replace(/\u9999/g, '*');
|
|
3169
3691
|
}
|
|
3170
3692
|
|
|
@@ -3181,7 +3703,7 @@ class ObjectsInRedisClient {
|
|
|
3181
3703
|
|
|
3182
3704
|
const endAfterWildcard = params.endkey.substr(wildcardPos + 1);
|
|
3183
3705
|
params.startkey = this.objNamespace + params.startkey;
|
|
3184
|
-
params.endkey
|
|
3706
|
+
params.endkey = this.objNamespace + params.endkey;
|
|
3185
3707
|
|
|
3186
3708
|
keys = keys.sort().filter(key => {
|
|
3187
3709
|
if (key && !utils.regCheckId.test(key)) {
|
|
@@ -3189,11 +3711,10 @@ class ObjectsInRedisClient {
|
|
|
3189
3711
|
if (params.startkey && key < params.startkey) {
|
|
3190
3712
|
return false;
|
|
3191
3713
|
}
|
|
3192
|
-
if (params.endkey
|
|
3714
|
+
if (params.endkey && key > params.endkey) {
|
|
3193
3715
|
return false;
|
|
3194
3716
|
}
|
|
3195
|
-
} else
|
|
3196
|
-
if (params && wildcardPos === 0) {
|
|
3717
|
+
} else if (params && wildcardPos === 0) {
|
|
3197
3718
|
if (!key.endsWith(endAfterWildcard)) {
|
|
3198
3719
|
return false;
|
|
3199
3720
|
}
|
|
@@ -3212,7 +3733,7 @@ class ObjectsInRedisClient {
|
|
|
3212
3733
|
}
|
|
3213
3734
|
|
|
3214
3735
|
const _emit_ = (id, obj) => {
|
|
3215
|
-
result.rows.push({id: id, value: obj});
|
|
3736
|
+
result.rows.push({ id: id, value: obj });
|
|
3216
3737
|
};
|
|
3217
3738
|
|
|
3218
3739
|
const f = eval('(' + func.map.replace(/^function\(([a-z0-9A-Z_]+)\)/g, 'function($1, emit)') + ')');
|
|
@@ -3246,7 +3767,7 @@ class ObjectsInRedisClient {
|
|
|
3246
3767
|
}
|
|
3247
3768
|
}
|
|
3248
3769
|
if (max !== null) {
|
|
3249
|
-
result.rows = [{id: '_stats', value: {max: max}}];
|
|
3770
|
+
result.rows = [{ id: '_stats', value: { max: max } }];
|
|
3250
3771
|
} else {
|
|
3251
3772
|
result.rows = [];
|
|
3252
3773
|
}
|
|
@@ -3262,8 +3783,8 @@ class ObjectsInRedisClient {
|
|
|
3262
3783
|
}
|
|
3263
3784
|
if (!callback) {
|
|
3264
3785
|
return new Promise((resolve, reject) =>
|
|
3265
|
-
this._applyView(func, params, options, (err, obj) =>
|
|
3266
|
-
|
|
3786
|
+
this._applyView(func, params, options, (err, obj) => (err ? reject(err) : resolve(obj)))
|
|
3787
|
+
);
|
|
3267
3788
|
}
|
|
3268
3789
|
|
|
3269
3790
|
if (options && options.acl) {
|
|
@@ -3283,7 +3804,7 @@ class ObjectsInRedisClient {
|
|
|
3283
3804
|
|
|
3284
3805
|
async _getObjectView(design, search, params, options, callback) {
|
|
3285
3806
|
if (!this.client) {
|
|
3286
|
-
return
|
|
3807
|
+
return tools.maybeCallbackWithRedisError(callback, utils.ERRORS.ERROR_DB_CLOSED);
|
|
3287
3808
|
}
|
|
3288
3809
|
|
|
3289
3810
|
let obj;
|
|
@@ -3291,7 +3812,7 @@ class ObjectsInRedisClient {
|
|
|
3291
3812
|
obj = await this.client.get(this.objNamespace + '_design/' + design);
|
|
3292
3813
|
} catch (e) {
|
|
3293
3814
|
this.log.error(`${this.namespace} Cannot find view "${design}" for search "${search}" : ${e.message}`);
|
|
3294
|
-
return tools.
|
|
3815
|
+
return tools.maybeCallbackWithError(callback, new Error(`Cannot find view "${design}"`));
|
|
3295
3816
|
}
|
|
3296
3817
|
|
|
3297
3818
|
if (obj) {
|
|
@@ -3299,14 +3820,20 @@ class ObjectsInRedisClient {
|
|
|
3299
3820
|
obj = JSON.parse(obj);
|
|
3300
3821
|
} catch {
|
|
3301
3822
|
this.log.error(`${this.namespace} Cannot parse JSON: ${obj}`);
|
|
3302
|
-
return tools.maybeCallbackWithError(
|
|
3823
|
+
return tools.maybeCallbackWithError(
|
|
3824
|
+
callback,
|
|
3825
|
+
new Error(`Cannot parse JSON: "_design/${design}" / "${obj}"`)
|
|
3826
|
+
);
|
|
3303
3827
|
}
|
|
3304
3828
|
|
|
3305
3829
|
if (obj.views && obj.views[search]) {
|
|
3306
3830
|
return this._applyViewFunc(obj.views[search], params, options, callback);
|
|
3307
3831
|
} else {
|
|
3308
3832
|
this.log.error(`${this.namespace} Cannot find search "${search}" in "${design}"`);
|
|
3309
|
-
return tools.maybeCallbackWithError(
|
|
3833
|
+
return tools.maybeCallbackWithError(
|
|
3834
|
+
callback,
|
|
3835
|
+
new Error(`Cannot find search "${search}" in "${design}"`)
|
|
3836
|
+
);
|
|
3310
3837
|
}
|
|
3311
3838
|
} else {
|
|
3312
3839
|
this.log.error(`${this.namespace} Cannot find view "${design}" for search "${search}"`);
|
|
@@ -3321,8 +3848,8 @@ class ObjectsInRedisClient {
|
|
|
3321
3848
|
}
|
|
3322
3849
|
if (!callback) {
|
|
3323
3850
|
return new Promise((resolve, reject) =>
|
|
3324
|
-
this.getObjectView(design, search, params, options, (err, obj) =>
|
|
3325
|
-
|
|
3851
|
+
this.getObjectView(design, search, params, options, (err, obj) => (err ? reject(err) : resolve(obj)))
|
|
3852
|
+
);
|
|
3326
3853
|
}
|
|
3327
3854
|
|
|
3328
3855
|
if (options && options.acl) {
|
|
@@ -3332,7 +3859,7 @@ class ObjectsInRedisClient {
|
|
|
3332
3859
|
if (typeof callback === 'function') {
|
|
3333
3860
|
utils.checkObjectRights(this, null, null, options, 'list', (err, options) => {
|
|
3334
3861
|
if (err) {
|
|
3335
|
-
|
|
3862
|
+
return tools.maybeCallbackWithRedisError(callback, err);
|
|
3336
3863
|
} else {
|
|
3337
3864
|
return this._getObjectView(design, search, params, options, callback);
|
|
3338
3865
|
}
|
|
@@ -3342,8 +3869,8 @@ class ObjectsInRedisClient {
|
|
|
3342
3869
|
|
|
3343
3870
|
getObjectViewAsync(design, search, params, options) {
|
|
3344
3871
|
return new Promise((resolve, reject) =>
|
|
3345
|
-
this.getObjectView(design, search, params, options, (err, arr) =>
|
|
3346
|
-
|
|
3872
|
+
this.getObjectView(design, search, params, options, (err, arr) => (err ? reject(err) : resolve(arr)))
|
|
3873
|
+
);
|
|
3347
3874
|
}
|
|
3348
3875
|
|
|
3349
3876
|
async _getObjectList(params, options, callback) {
|
|
@@ -3354,7 +3881,10 @@ class ObjectsInRedisClient {
|
|
|
3354
3881
|
params = params || {};
|
|
3355
3882
|
params.startkey = params.startkey || '';
|
|
3356
3883
|
params.endkey = params.endkey || '\u9999';
|
|
3357
|
-
const pattern =
|
|
3884
|
+
const pattern =
|
|
3885
|
+
params.endkey.substring(0, params.startkey.length) === params.startkey
|
|
3886
|
+
? this.objNamespace + params.startkey + '*'
|
|
3887
|
+
: this.objNamespace + '*';
|
|
3358
3888
|
|
|
3359
3889
|
// todo: use lua script for this
|
|
3360
3890
|
let keys;
|
|
@@ -3408,7 +3938,7 @@ class ObjectsInRedisClient {
|
|
|
3408
3938
|
if (!objs[r] || !utils.checkObject(objs[r], options, utils.CONSTS.ACCESS_READ)) {
|
|
3409
3939
|
continue;
|
|
3410
3940
|
}
|
|
3411
|
-
result.rows.push({id: objs[r]._id, value: objs[r], doc: objs[r]});
|
|
3941
|
+
result.rows.push({ id: objs[r]._id, value: objs[r], doc: objs[r] });
|
|
3412
3942
|
}
|
|
3413
3943
|
}
|
|
3414
3944
|
return tools.maybeCallbackWithError(callback, null, result);
|
|
@@ -3421,8 +3951,8 @@ class ObjectsInRedisClient {
|
|
|
3421
3951
|
}
|
|
3422
3952
|
if (!callback) {
|
|
3423
3953
|
return new Promise((resolve, reject) =>
|
|
3424
|
-
this.getObjectList(params, options, (err, obj) =>
|
|
3425
|
-
|
|
3954
|
+
this.getObjectList(params, options, (err, obj) => (err ? reject(err) : resolve(obj)))
|
|
3955
|
+
);
|
|
3426
3956
|
}
|
|
3427
3957
|
|
|
3428
3958
|
if (options && options.acl) {
|
|
@@ -3432,7 +3962,7 @@ class ObjectsInRedisClient {
|
|
|
3432
3962
|
if (typeof callback === 'function') {
|
|
3433
3963
|
utils.checkObjectRights(this, null, null, options, 'list', (err, options) => {
|
|
3434
3964
|
if (err) {
|
|
3435
|
-
|
|
3965
|
+
return tools.maybeCallbackWithRedisError(callback, err);
|
|
3436
3966
|
} else {
|
|
3437
3967
|
return this._getObjectList(params, options, callback);
|
|
3438
3968
|
}
|
|
@@ -3442,17 +3972,17 @@ class ObjectsInRedisClient {
|
|
|
3442
3972
|
|
|
3443
3973
|
getObjectListAsync(params, options) {
|
|
3444
3974
|
return new Promise((resolve, reject) =>
|
|
3445
|
-
this.getObjectList(params, options, (err, arr) =>
|
|
3446
|
-
|
|
3975
|
+
this.getObjectList(params, options, (err, arr) => (err ? reject(err) : resolve(arr)))
|
|
3976
|
+
);
|
|
3447
3977
|
}
|
|
3448
3978
|
|
|
3449
3979
|
// could be optimised, to read object only once. Now it will read 3 times
|
|
3450
3980
|
async _extendObject(id, obj, options, callback, _iteration) {
|
|
3451
3981
|
if (!id || typeof id !== 'string' || utils.regCheckId.test(id)) {
|
|
3452
|
-
return
|
|
3982
|
+
return tools.maybeCallbackWithError(callback, `Invalid ID: ${id}`);
|
|
3453
3983
|
}
|
|
3454
3984
|
if (!this.client) {
|
|
3455
|
-
return
|
|
3985
|
+
return tools.maybeCallbackWithRedisError(callback, utils.ERRORS.ERROR_DB_CLOSED);
|
|
3456
3986
|
}
|
|
3457
3987
|
|
|
3458
3988
|
let oldObj;
|
|
@@ -3479,12 +4009,24 @@ class ObjectsInRedisClient {
|
|
|
3479
4009
|
_oldObj = deepClone(oldObj);
|
|
3480
4010
|
}
|
|
3481
4011
|
|
|
4012
|
+
// we need to know if custom has been added/deleted
|
|
4013
|
+
const oldObjHasCustom = oldObj && oldObj.common && oldObj.common && oldObj.common.custom;
|
|
4014
|
+
|
|
3482
4015
|
oldObj = oldObj || {};
|
|
3483
4016
|
obj = deepClone(obj); // copy here to prevent "sandboxed" objects from JavaScript adapter
|
|
3484
|
-
if (
|
|
4017
|
+
if (
|
|
4018
|
+
oldObj.common &&
|
|
4019
|
+
oldObj.common.custom !== undefined &&
|
|
4020
|
+
oldObj.common.custom !== null &&
|
|
4021
|
+
!tools.isObject(oldObj.common.custom)
|
|
4022
|
+
) {
|
|
4023
|
+
// custom has to be an object, else clean up
|
|
3485
4024
|
delete oldObj.common.custom;
|
|
3486
4025
|
}
|
|
3487
4026
|
|
|
4027
|
+
// we need to check if type has changed
|
|
4028
|
+
const oldType = oldObj.type;
|
|
4029
|
+
|
|
3488
4030
|
oldObj = extend(true, oldObj, obj);
|
|
3489
4031
|
oldObj._id = id;
|
|
3490
4032
|
|
|
@@ -3503,7 +4045,9 @@ class ObjectsInRedisClient {
|
|
|
3503
4045
|
oldObj.acl.ownerGroup = null;
|
|
3504
4046
|
return this.getUserGroup(options.owner, (user, groups /*, permissions */) => {
|
|
3505
4047
|
if (!groups || !groups[0]) {
|
|
3506
|
-
options.ownerGroup =
|
|
4048
|
+
options.ownerGroup =
|
|
4049
|
+
(this.defaultNewAcl && this.defaultNewAcl.ownerGroup) ||
|
|
4050
|
+
utils.CONSTS.SYSTEM_ADMIN_GROUP;
|
|
3507
4051
|
} else {
|
|
3508
4052
|
options.ownerGroup = groups[0];
|
|
3509
4053
|
}
|
|
@@ -3520,30 +4064,62 @@ class ObjectsInRedisClient {
|
|
|
3520
4064
|
if (obj.common && obj.common.alias && obj.common.alias.id) {
|
|
3521
4065
|
if (typeof obj.common.alias.id === 'object') {
|
|
3522
4066
|
if (typeof obj.common.alias.id.write !== 'string' || typeof obj.common.alias.id.read !== 'string') {
|
|
3523
|
-
return
|
|
4067
|
+
return tools.maybeCallbackWithError(callback, 'Invalid alias ID');
|
|
3524
4068
|
}
|
|
3525
4069
|
|
|
3526
4070
|
if (obj.common.alias.id.write.startsWith('alias.') || obj.common.alias.id.read.startsWith('alias.')) {
|
|
3527
|
-
return
|
|
4071
|
+
return tools.maybeCallbackWithError(callback, 'Cannot make alias on alias');
|
|
3528
4072
|
}
|
|
3529
4073
|
} else {
|
|
3530
4074
|
if (typeof obj.common.alias.id !== 'string') {
|
|
3531
|
-
return
|
|
4075
|
+
return tools.maybeCallbackWithError(callback, 'Invalid alias ID');
|
|
3532
4076
|
}
|
|
3533
4077
|
|
|
3534
4078
|
if (obj.common.alias.id.startsWith('alias.')) {
|
|
3535
|
-
return
|
|
4079
|
+
return tools.maybeCallbackWithError(callback, 'Cannot make alias on alias');
|
|
3536
4080
|
}
|
|
3537
4081
|
}
|
|
3538
4082
|
}
|
|
3539
4083
|
|
|
3540
4084
|
if (_oldObj && !tools.checkNonEditable(_oldObj, oldObj)) {
|
|
3541
|
-
return
|
|
4085
|
+
return tools.maybeCallbackWithError(callback, 'Invalid password for update of vendor information');
|
|
3542
4086
|
}
|
|
3543
4087
|
const message = JSON.stringify(oldObj);
|
|
3544
4088
|
|
|
3545
4089
|
try {
|
|
3546
|
-
|
|
4090
|
+
const commands = [];
|
|
4091
|
+
if (this.useSets) {
|
|
4092
|
+
// what is called oldObj is acutally the obj we set, because it has been extended
|
|
4093
|
+
if (oldObj.type && !oldType) {
|
|
4094
|
+
// new object or oldObj had no type -> add to set + set object
|
|
4095
|
+
commands.push(['sadd', `${this.setNamespace}object.type.${obj.type}`, this.objNamespace + id]);
|
|
4096
|
+
} else if (oldObj.type && oldType && oldObj.type !== oldType) {
|
|
4097
|
+
// the old obj had a type which differs from the new type -> rem old, add new
|
|
4098
|
+
commands.push(
|
|
4099
|
+
['sadd', `${this.setNamespace}object.type.${obj.type}`, this.objNamespace + id],
|
|
4100
|
+
['srem', `${this.setNamespace}object.type.${oldObj.type}`, this.objNamespace + id]
|
|
4101
|
+
);
|
|
4102
|
+
} else if (oldType && !oldObj.type) {
|
|
4103
|
+
// the oldObj had a type, the new one has no -> rem
|
|
4104
|
+
commands.push(['srem', `${this.setNamespace}object.type.${obj.type}`, this.objNamespace + id]);
|
|
4105
|
+
}
|
|
4106
|
+
}
|
|
4107
|
+
|
|
4108
|
+
if (oldObj.common && oldObj.common.custom && !oldObjHasCustom) {
|
|
4109
|
+
// we now have custom, old object had no custom
|
|
4110
|
+
commands.push(['sadd', `${this.setNamespace}object.common.custom`, this.objNamespace + id]);
|
|
4111
|
+
} else if (oldObjHasCustom && (!oldObj.common || !oldObj.common.custom)) {
|
|
4112
|
+
// we no longer have custom
|
|
4113
|
+
commands.push(['srem', `${this.setNamespace}object.common.custom`, this.objNamespace + id]);
|
|
4114
|
+
}
|
|
4115
|
+
|
|
4116
|
+
if (!commands.length) {
|
|
4117
|
+
await this.client.set(this.objNamespace + id, message);
|
|
4118
|
+
} else {
|
|
4119
|
+
// set all commands atomic
|
|
4120
|
+
commands.push(['set', this.objNamespace + id, message]);
|
|
4121
|
+
await this.client.multi(commands).exec();
|
|
4122
|
+
}
|
|
3547
4123
|
|
|
3548
4124
|
// extended -> if its now type meta and currently marked as not -> cache
|
|
3549
4125
|
if (this.existingMetaObjects[id] === false && oldObj && oldObj.type === 'meta') {
|
|
@@ -3551,7 +4127,7 @@ class ObjectsInRedisClient {
|
|
|
3551
4127
|
}
|
|
3552
4128
|
//this.settings.connection.enhancedLogging && this.log.silly(this.namespace + ' redis publish ' + this.objNamespace + id + ' ' + message);
|
|
3553
4129
|
await this.client.publish(this.objNamespace + id, message);
|
|
3554
|
-
return tools.maybeCallbackWithError(callback, null, {id: id, value: oldObj}, id);
|
|
4130
|
+
return tools.maybeCallbackWithError(callback, null, { id: id, value: oldObj }, id);
|
|
3555
4131
|
} catch (e) {
|
|
3556
4132
|
return tools.maybeCallbackWithRedisError(callback, e);
|
|
3557
4133
|
}
|
|
@@ -3564,8 +4140,8 @@ class ObjectsInRedisClient {
|
|
|
3564
4140
|
}
|
|
3565
4141
|
if (!callback) {
|
|
3566
4142
|
return new Promise((resolve, reject) =>
|
|
3567
|
-
this.extendObject(id, obj, options, (err, res) =>
|
|
3568
|
-
|
|
4143
|
+
this.extendObject(id, obj, options, (err, res) => (err ? reject(err) : resolve(res)))
|
|
4144
|
+
);
|
|
3569
4145
|
}
|
|
3570
4146
|
|
|
3571
4147
|
if (options && options.acl) {
|
|
@@ -3574,7 +4150,7 @@ class ObjectsInRedisClient {
|
|
|
3574
4150
|
|
|
3575
4151
|
utils.checkObjectRights(this, null, null, options, utils.CONSTS.ACCESS_WRITE, (err, options) => {
|
|
3576
4152
|
if (err) {
|
|
3577
|
-
|
|
4153
|
+
return tools.maybeCallbackWithRedisError(callback, err);
|
|
3578
4154
|
} else {
|
|
3579
4155
|
return this._extendObject(id, obj, options, callback);
|
|
3580
4156
|
}
|
|
@@ -3583,8 +4159,8 @@ class ObjectsInRedisClient {
|
|
|
3583
4159
|
|
|
3584
4160
|
extendObjectAsync(id, obj, options) {
|
|
3585
4161
|
return new Promise((resolve, reject) =>
|
|
3586
|
-
this.extendObject(id, obj, options, (err, res) =>
|
|
3587
|
-
|
|
4162
|
+
this.extendObject(id, obj, options, (err, res) => (err ? reject(err) : resolve(res)))
|
|
4163
|
+
);
|
|
3588
4164
|
}
|
|
3589
4165
|
|
|
3590
4166
|
setConfig(id, obj, options, callback) {
|
|
@@ -3606,33 +4182,45 @@ class ObjectsInRedisClient {
|
|
|
3606
4182
|
_findObject(idOrName, type, options, callback) {
|
|
3607
4183
|
this._getObject(idOrName, options, (err, obj) => {
|
|
3608
4184
|
// Assume it is ID
|
|
3609
|
-
if (
|
|
4185
|
+
if (
|
|
4186
|
+
obj &&
|
|
4187
|
+
utils.checkObject(obj, options, utils.CONSTS.ACCESS_READ) &&
|
|
4188
|
+
(!type || (obj.common && obj.common.type === type))
|
|
4189
|
+
) {
|
|
3610
4190
|
return tools.maybeCallbackWithError(callback, null, idOrName, obj.common.name);
|
|
3611
4191
|
} else {
|
|
3612
|
-
this._getKeys(
|
|
3613
|
-
|
|
3614
|
-
|
|
3615
|
-
|
|
3616
|
-
|
|
3617
|
-
return tools.maybeCallbackWithRedisError(callback, e);
|
|
3618
|
-
}
|
|
3619
|
-
objs = objs || [];
|
|
3620
|
-
// Assume it is name
|
|
3621
|
-
for (let i = 0; i < keys.length; i++) {
|
|
4192
|
+
this._getKeys(
|
|
4193
|
+
'*',
|
|
4194
|
+
options,
|
|
4195
|
+
async (err, keys) => {
|
|
4196
|
+
let objs;
|
|
3622
4197
|
try {
|
|
3623
|
-
objs
|
|
3624
|
-
} catch {
|
|
3625
|
-
|
|
3626
|
-
continue;
|
|
4198
|
+
objs = await this.client.mget(keys);
|
|
4199
|
+
} catch (e) {
|
|
4200
|
+
return tools.maybeCallbackWithRedisError(callback, e);
|
|
3627
4201
|
}
|
|
3628
|
-
|
|
4202
|
+
objs = objs || [];
|
|
4203
|
+
// Assume it is name
|
|
4204
|
+
for (let i = 0; i < keys.length; i++) {
|
|
4205
|
+
try {
|
|
4206
|
+
objs[i] = JSON.parse(objs[i]);
|
|
4207
|
+
} catch {
|
|
4208
|
+
this.log.error(`${this.namespace} Cannot parse JSON ${keys[i]}: ${objs[i]}`);
|
|
4209
|
+
continue;
|
|
4210
|
+
}
|
|
4211
|
+
if (
|
|
4212
|
+
objs[i] &&
|
|
4213
|
+
objs[i].common &&
|
|
3629
4214
|
objs[i].common.name === idOrName &&
|
|
3630
|
-
(!type || objs[i].common.type === type)
|
|
3631
|
-
|
|
4215
|
+
(!type || objs[i].common.type === type)
|
|
4216
|
+
) {
|
|
4217
|
+
return tools.maybeCallbackWithError(callback, null, objs[i]._id, idOrName);
|
|
4218
|
+
}
|
|
3632
4219
|
}
|
|
3633
|
-
|
|
3634
|
-
|
|
3635
|
-
|
|
4220
|
+
return tools.maybeCallbackWithError(callback, null, null, idOrName);
|
|
4221
|
+
},
|
|
4222
|
+
true
|
|
4223
|
+
);
|
|
3636
4224
|
}
|
|
3637
4225
|
});
|
|
3638
4226
|
}
|
|
@@ -3649,8 +4237,8 @@ class ObjectsInRedisClient {
|
|
|
3649
4237
|
}
|
|
3650
4238
|
if (!callback) {
|
|
3651
4239
|
return new Promise((resolve, reject) =>
|
|
3652
|
-
this.findObject(idOrName, type, options, (err, id, _idOrName) =>
|
|
3653
|
-
|
|
4240
|
+
this.findObject(idOrName, type, options, (err, id, _idOrName) => (err ? reject(err) : resolve(id)))
|
|
4241
|
+
);
|
|
3654
4242
|
}
|
|
3655
4243
|
|
|
3656
4244
|
if (options && options.acl) {
|
|
@@ -3670,8 +4258,8 @@ class ObjectsInRedisClient {
|
|
|
3670
4258
|
|
|
3671
4259
|
findObjectAsync(idOrName, type, options) {
|
|
3672
4260
|
return new Promise((resolve, reject) =>
|
|
3673
|
-
this.findObject(idOrName, type, options, (err, id, _idOrName) =>
|
|
3674
|
-
|
|
4261
|
+
this.findObject(idOrName, type, options, (err, id, _idOrName) => (err ? reject(err) : resolve(id)))
|
|
4262
|
+
);
|
|
3675
4263
|
}
|
|
3676
4264
|
|
|
3677
4265
|
// can be called only from js-controller
|
|
@@ -3729,10 +4317,10 @@ class ObjectsInRedisClient {
|
|
|
3729
4317
|
|
|
3730
4318
|
utils.checkObjectRights(this, null, null, options, utils.CONSTS.ACCESS_WRITE, (err, options) => {
|
|
3731
4319
|
if (err) {
|
|
3732
|
-
|
|
4320
|
+
return tools.maybeCallbackWithRedisError(callback, err);
|
|
3733
4321
|
} else {
|
|
3734
4322
|
if (!options.acl.file.write || options.user !== utils.CONSTS.SYSTEM_ADMIN_USER) {
|
|
3735
|
-
|
|
4323
|
+
return tools.maybeCallbackWithError(callback, utils.ERRORS.ERROR_PERMISSION);
|
|
3736
4324
|
} else {
|
|
3737
4325
|
return this._destroyDB(options, callback);
|
|
3738
4326
|
}
|
|
@@ -3741,9 +4329,9 @@ class ObjectsInRedisClient {
|
|
|
3741
4329
|
}
|
|
3742
4330
|
|
|
3743
4331
|
destroyDBAsync(options) {
|
|
3744
|
-
return /** @type {Promise<void>} */ (
|
|
3745
|
-
this.destroyDB(options, err =>
|
|
3746
|
-
|
|
4332
|
+
return /** @type {Promise<void>} */ (
|
|
4333
|
+
new Promise((resolve, reject) => this.destroyDB(options, err => (err ? reject(err) : resolve())))
|
|
4334
|
+
);
|
|
3747
4335
|
}
|
|
3748
4336
|
|
|
3749
4337
|
// Destructor of the class. Called by shutting down.
|
|
@@ -3756,7 +4344,6 @@ class ObjectsInRedisClient {
|
|
|
3756
4344
|
} catch {
|
|
3757
4345
|
// ignore error
|
|
3758
4346
|
}
|
|
3759
|
-
|
|
3760
4347
|
}
|
|
3761
4348
|
if (this.sub) {
|
|
3762
4349
|
try {
|
|
@@ -3777,28 +4364,26 @@ class ObjectsInRedisClient {
|
|
|
3777
4364
|
}
|
|
3778
4365
|
|
|
3779
4366
|
async loadLuaScripts() {
|
|
3780
|
-
let
|
|
3781
|
-
|
|
3782
|
-
|
|
3783
|
-
|
|
3784
|
-
|
|
3785
|
-
|
|
3786
|
-
_scripts.push({
|
|
3787
|
-
name,
|
|
3788
|
-
text: buf,
|
|
3789
|
-
hash: shasum.digest('hex')
|
|
3790
|
-
});
|
|
3791
|
-
}
|
|
4367
|
+
let luaDirName;
|
|
4368
|
+
|
|
4369
|
+
if (this.noLegacyMultihost && this.useSets) {
|
|
4370
|
+
luaDirName = 'lua-v4';
|
|
4371
|
+
} else if (this.noLegacyMultihost) {
|
|
4372
|
+
luaDirName = 'lua-v4-no-sets';
|
|
3792
4373
|
} else {
|
|
3793
|
-
|
|
3794
|
-
const shasum = crypto.createHash('sha1');
|
|
3795
|
-
const script = fs.readFileSync(__dirname + '/lua/' + name);
|
|
3796
|
-
shasum.update(script);
|
|
3797
|
-
const hash = shasum.digest('hex');
|
|
3798
|
-
return {name: name.replace(/\.lua$/, ''), text: script, hash};
|
|
3799
|
-
});
|
|
4374
|
+
luaDirName = 'lua-v3';
|
|
3800
4375
|
}
|
|
3801
|
-
|
|
4376
|
+
|
|
4377
|
+
const luaPath = path.join(__dirname, luaDirName);
|
|
4378
|
+
const scripts = fs.readdirSync(luaPath).map(name => {
|
|
4379
|
+
const shasum = crypto.createHash('sha1');
|
|
4380
|
+
const script = fs.readFileSync(path.join(luaPath, name));
|
|
4381
|
+
shasum.update(script);
|
|
4382
|
+
const hash = shasum.digest('hex');
|
|
4383
|
+
return { name: name.replace(/\.lua$/, ''), text: script, hash };
|
|
4384
|
+
});
|
|
4385
|
+
|
|
4386
|
+
const hashes = scripts.map(e => e.hash);
|
|
3802
4387
|
hashes.unshift('EXISTS');
|
|
3803
4388
|
|
|
3804
4389
|
if (!this.client) {
|
|
@@ -3812,15 +4397,15 @@ class ObjectsInRedisClient {
|
|
|
3812
4397
|
// ignore
|
|
3813
4398
|
}
|
|
3814
4399
|
|
|
3815
|
-
arr &&
|
|
4400
|
+
arr && scripts.forEach((e, i) => (scripts[i].loaded = !!arr[i]));
|
|
3816
4401
|
|
|
3817
4402
|
if (!this.client) {
|
|
3818
4403
|
throw new Error(tools.ERRORS.ERROR_DB_CLOSED);
|
|
3819
4404
|
}
|
|
3820
4405
|
|
|
3821
|
-
for (let i = 0; i <
|
|
3822
|
-
if (!
|
|
3823
|
-
const script =
|
|
4406
|
+
for (let i = 0; i < scripts.length; i++) {
|
|
4407
|
+
if (!scripts[i].loaded) {
|
|
4408
|
+
const script = scripts[i];
|
|
3824
4409
|
let hash;
|
|
3825
4410
|
try {
|
|
3826
4411
|
hash = await this.client.script(['LOAD', script.text]);
|
|
@@ -3834,20 +4419,20 @@ class ObjectsInRedisClient {
|
|
|
3834
4419
|
}
|
|
3835
4420
|
}
|
|
3836
4421
|
this.scripts = {};
|
|
3837
|
-
|
|
4422
|
+
scripts.forEach(e => (this.scripts[e.name] = e.hash));
|
|
3838
4423
|
}
|
|
3839
4424
|
|
|
3840
4425
|
/**
|
|
3841
4426
|
* Get all keys matching a pattern using redis SCAN command, duplicates are filtered out
|
|
3842
4427
|
*
|
|
3843
4428
|
* @param {string} pattern - pattern to match, e. g. io.hm-rpc.0*
|
|
3844
|
-
* @param {number} count - count argument used by redis SCAN, default is
|
|
4429
|
+
* @param {number} count - count argument used by redis SCAN, default is 250
|
|
3845
4430
|
* @return {Promise<string[]>}
|
|
3846
4431
|
* @private
|
|
3847
4432
|
*/
|
|
3848
4433
|
_getKeysViaScan(pattern, count = 250) {
|
|
3849
4434
|
return new Promise(resolve => {
|
|
3850
|
-
const stream = this.client.scanStream({match: pattern, count: count});
|
|
4435
|
+
const stream = this.client.scanStream({ match: pattern, count: count });
|
|
3851
4436
|
let uniqueKeys = [];
|
|
3852
4437
|
|
|
3853
4438
|
stream.on('data', resultKeys => {
|
|
@@ -3861,6 +4446,239 @@ class ObjectsInRedisClient {
|
|
|
3861
4446
|
});
|
|
3862
4447
|
});
|
|
3863
4448
|
}
|
|
4449
|
+
|
|
4450
|
+
/**
|
|
4451
|
+
* Checks if a given set exists
|
|
4452
|
+
* @param {string} id - id of the set
|
|
4453
|
+
* @private
|
|
4454
|
+
* @return {Promise<boolean>}
|
|
4455
|
+
*/
|
|
4456
|
+
async setExists(id) {
|
|
4457
|
+
if (!this.client) {
|
|
4458
|
+
throw new Error(utils.ERRORS.ERROR_DB_CLOSED);
|
|
4459
|
+
}
|
|
4460
|
+
|
|
4461
|
+
const exists = await this.client.exists(this.setNamespace + id);
|
|
4462
|
+
return !!exists;
|
|
4463
|
+
}
|
|
4464
|
+
|
|
4465
|
+
/**
|
|
4466
|
+
* Migrate all objects to sets
|
|
4467
|
+
* @return {Promise<number>}
|
|
4468
|
+
*/
|
|
4469
|
+
async migrateToSets() {
|
|
4470
|
+
if (!this.useSets) {
|
|
4471
|
+
return 0;
|
|
4472
|
+
}
|
|
4473
|
+
|
|
4474
|
+
if (!this.client) {
|
|
4475
|
+
throw new Error(utils.ERRORS.ERROR_DB_CLOSED);
|
|
4476
|
+
}
|
|
4477
|
+
|
|
4478
|
+
// to be safe we remove all sets before migration
|
|
4479
|
+
const keys = await this._getKeysViaScan(`${this.setNamespace}object.type.*`);
|
|
4480
|
+
for (const key of keys) {
|
|
4481
|
+
await this.client.del(key);
|
|
4482
|
+
}
|
|
4483
|
+
|
|
4484
|
+
let noMigrated = 0;
|
|
4485
|
+
// get all objs without using view
|
|
4486
|
+
const objs = await this.getObjectList({ startkey: '', endkey: '\u9999' });
|
|
4487
|
+
for (const obj of objs.rows) {
|
|
4488
|
+
if (obj.value.type) {
|
|
4489
|
+
// e.g. _design/.. has no type
|
|
4490
|
+
// 1 if added else 0 (mostly always part of the set)
|
|
4491
|
+
const migrated = await this.client.sadd(
|
|
4492
|
+
`${this.setNamespace}object.type.${obj.value.type}`,
|
|
4493
|
+
this.objNamespace + obj.id
|
|
4494
|
+
);
|
|
4495
|
+
noMigrated += migrated;
|
|
4496
|
+
}
|
|
4497
|
+
|
|
4498
|
+
// check for custom
|
|
4499
|
+
if (obj.value.common && obj.value.common.custom) {
|
|
4500
|
+
const migrated = await this.client.sadd(
|
|
4501
|
+
`${this.setNamespace}object.common.custom`,
|
|
4502
|
+
this.objNamespace + obj.id
|
|
4503
|
+
);
|
|
4504
|
+
noMigrated += migrated;
|
|
4505
|
+
}
|
|
4506
|
+
}
|
|
4507
|
+
return noMigrated;
|
|
4508
|
+
}
|
|
4509
|
+
|
|
4510
|
+
/**
|
|
4511
|
+
* Returns the protocol version from DB
|
|
4512
|
+
*
|
|
4513
|
+
* @returns {Promise<string>}
|
|
4514
|
+
*/
|
|
4515
|
+
getProtocolVersion() {
|
|
4516
|
+
if (!this.client) {
|
|
4517
|
+
throw new Error(utils.ERRORS.ERROR_DB_CLOSED);
|
|
4518
|
+
}
|
|
4519
|
+
|
|
4520
|
+
return this.client.get(`${this.metaNamespace}objects.protocolVersion`);
|
|
4521
|
+
}
|
|
4522
|
+
|
|
4523
|
+
/**
|
|
4524
|
+
* Extend the primary host lock time
|
|
4525
|
+
* Value will expire after ms milliseconds
|
|
4526
|
+
*
|
|
4527
|
+
* {number} ms - ms until value expires
|
|
4528
|
+
* @return {Promise<number>} 1 if extended else 0
|
|
4529
|
+
*/
|
|
4530
|
+
extendPrimaryHostLock(ms) {
|
|
4531
|
+
if (!this.client) {
|
|
4532
|
+
throw new Error(utils.ERRORS.ERROR_DB_CLOSED);
|
|
4533
|
+
}
|
|
4534
|
+
|
|
4535
|
+
// we have a host version smaller 3 no one can be primary
|
|
4536
|
+
if (!this.noLegacyMultihost) {
|
|
4537
|
+
return Promise.resolve(0);
|
|
4538
|
+
}
|
|
4539
|
+
|
|
4540
|
+
// try to extend lock
|
|
4541
|
+
return this.client.evalsha([
|
|
4542
|
+
this.scripts['redlock_extend'],
|
|
4543
|
+
3,
|
|
4544
|
+
`${this.metaNamespace}objects.primaryHost`,
|
|
4545
|
+
this.hostname,
|
|
4546
|
+
ms
|
|
4547
|
+
]);
|
|
4548
|
+
}
|
|
4549
|
+
|
|
4550
|
+
/**
|
|
4551
|
+
* Sets current host as primary if no primary host active
|
|
4552
|
+
* Value will expire after ms milliseconds
|
|
4553
|
+
*
|
|
4554
|
+
* {number} ms - ms until value expires
|
|
4555
|
+
* @return {Promise<number>} 1 if lock acquired else 0
|
|
4556
|
+
*/
|
|
4557
|
+
setPrimaryHost(ms) {
|
|
4558
|
+
if (!this.client) {
|
|
4559
|
+
throw new Error(utils.ERRORS.ERROR_DB_CLOSED);
|
|
4560
|
+
}
|
|
4561
|
+
|
|
4562
|
+
// we have a host version smaller 3 no one can be primary
|
|
4563
|
+
if (!this.noLegacyMultihost) {
|
|
4564
|
+
return Promise.resolve(0);
|
|
4565
|
+
}
|
|
4566
|
+
|
|
4567
|
+
// try to acquire lock
|
|
4568
|
+
return this.client.evalsha([
|
|
4569
|
+
this.scripts['redlock_acquire'],
|
|
4570
|
+
3,
|
|
4571
|
+
`${this.metaNamespace}objects.primaryHost`,
|
|
4572
|
+
this.hostname,
|
|
4573
|
+
ms
|
|
4574
|
+
]);
|
|
4575
|
+
}
|
|
4576
|
+
|
|
4577
|
+
/**
|
|
4578
|
+
* Get name of the primary host
|
|
4579
|
+
*
|
|
4580
|
+
* @return {Promise<string>}
|
|
4581
|
+
*/
|
|
4582
|
+
getPrimaryHost() {
|
|
4583
|
+
if (!this.client) {
|
|
4584
|
+
throw new Error(utils.ERRORS.ERROR_DB_CLOSED);
|
|
4585
|
+
}
|
|
4586
|
+
|
|
4587
|
+
// we have a host version smaller 3 no one can be primary
|
|
4588
|
+
if (!this.noLegacyMultihost) {
|
|
4589
|
+
return new Promise.resolve('');
|
|
4590
|
+
}
|
|
4591
|
+
|
|
4592
|
+
return this.client.get(`${this.metaNamespace}objects.primaryHost`);
|
|
4593
|
+
}
|
|
4594
|
+
|
|
4595
|
+
/**
|
|
4596
|
+
* Ensure we are no longer the primary host
|
|
4597
|
+
*
|
|
4598
|
+
* @return Promise<void>
|
|
4599
|
+
*/
|
|
4600
|
+
releasePrimaryHost() {
|
|
4601
|
+
if (!this.client) {
|
|
4602
|
+
throw new Error(utils.ERRORS.ERROR_DB_CLOSED);
|
|
4603
|
+
}
|
|
4604
|
+
|
|
4605
|
+
if (!this.noLegacyMultihost) {
|
|
4606
|
+
return new Promise.resolve();
|
|
4607
|
+
}
|
|
4608
|
+
|
|
4609
|
+
// try to release lock
|
|
4610
|
+
return this.client.evalsha([
|
|
4611
|
+
this.scripts['redlock_release'],
|
|
4612
|
+
4,
|
|
4613
|
+
`${this.metaNamespace}objects.primaryHost`,
|
|
4614
|
+
this.hostname,
|
|
4615
|
+
this.settings.connection.options.db,
|
|
4616
|
+
`${this.metaNamespace}objects.primaryHost`
|
|
4617
|
+
]);
|
|
4618
|
+
}
|
|
4619
|
+
|
|
4620
|
+
/**
|
|
4621
|
+
* Sets the protocol version to the DB
|
|
4622
|
+
* @param {number|string} version - protocol version
|
|
4623
|
+
* @returns {Promise<void>}
|
|
4624
|
+
*/
|
|
4625
|
+
async setProtocolVersion(version) {
|
|
4626
|
+
if (!this.client) {
|
|
4627
|
+
throw new Error(utils.ERRORS.ERROR_DB_CLOSED);
|
|
4628
|
+
}
|
|
4629
|
+
|
|
4630
|
+
version = version.toString();
|
|
4631
|
+
// we can only set a protocol if we actually support it
|
|
4632
|
+
if (this.supportedProtocolVersions.includes(version)) {
|
|
4633
|
+
await this.client.set(`${this.metaNamespace}objects.protocolVersion`, version);
|
|
4634
|
+
await this.client.publish(`${this.metaNamespace}objects.protocolVersion`, version);
|
|
4635
|
+
} else {
|
|
4636
|
+
throw new Error('Cannot set an unsupported protocol version on the current host');
|
|
4637
|
+
}
|
|
4638
|
+
}
|
|
4639
|
+
|
|
4640
|
+
/**
|
|
4641
|
+
* Subscribe to expired events to get expiration of primary host
|
|
4642
|
+
* @return {Promise<void>}
|
|
4643
|
+
*/
|
|
4644
|
+
async subscribePrimaryHost() {
|
|
4645
|
+
if (this.subSystem) {
|
|
4646
|
+
await this.subSystem.subscribe(`__keyevent@${this.settings.connection.options.db}__:expired`);
|
|
4647
|
+
await this.subSystem.subscribe(`__keyevent@${this.settings.connection.options.db}__:evicted`);
|
|
4648
|
+
}
|
|
4649
|
+
}
|
|
4650
|
+
|
|
4651
|
+
/**
|
|
4652
|
+
* Activates the usage of sets
|
|
4653
|
+
* @return {Promise<void>}
|
|
4654
|
+
*/
|
|
4655
|
+
async activateSets() {
|
|
4656
|
+
this.useSets = true;
|
|
4657
|
+
await this.client.set(`${this.metaNamespace}objects.features.useSets`, '1');
|
|
4658
|
+
}
|
|
4659
|
+
|
|
4660
|
+
/**
|
|
4661
|
+
* Deactivates the usage of sets
|
|
4662
|
+
* @return {Promise<void>}
|
|
4663
|
+
*/
|
|
4664
|
+
async deactivateSets() {
|
|
4665
|
+
this.useSets = false;
|
|
4666
|
+
await this.client.set(`${this.metaNamespace}objects.features.useSets`, '0');
|
|
4667
|
+
}
|
|
4668
|
+
|
|
4669
|
+
/**
|
|
4670
|
+
* Get value from meta namespace
|
|
4671
|
+
*
|
|
4672
|
+
* @param {string} id redis key
|
|
4673
|
+
* @return {Promise<string>}
|
|
4674
|
+
*/
|
|
4675
|
+
getMeta(id) {
|
|
4676
|
+
if (!this.client) {
|
|
4677
|
+
throw new Error(utils.ERRORS.ERROR_DB_CLOSED);
|
|
4678
|
+
}
|
|
4679
|
+
|
|
4680
|
+
return this.client.get(this.metaNamespace + id);
|
|
4681
|
+
}
|
|
3864
4682
|
}
|
|
3865
4683
|
|
|
3866
4684
|
module.exports = ObjectsInRedisClient;
|