@iobroker/db-objects-file 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 +114 -1
- package/lib/objects/objectsInMemFileDB.js +228 -112
- package/lib/objects/objectsInMemServerClass.js +2 -26
- package/lib/objects/objectsInMemServerRedis.js +292 -109
- package/package.json +5 -6
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
/**
|
|
2
2
|
* Objects DB in memory - Server with Redis protocol
|
|
3
3
|
*
|
|
4
|
-
* Copyright 2013-
|
|
4
|
+
* Copyright 2013-2022 bluefox <dogafox@gmail.com>
|
|
5
5
|
*
|
|
6
6
|
* MIT License
|
|
7
7
|
*
|
|
@@ -13,14 +13,14 @@
|
|
|
13
13
|
/* jshint strict:false */
|
|
14
14
|
/* jslint node: true */
|
|
15
15
|
'use strict';
|
|
16
|
-
const net
|
|
17
|
-
const fs
|
|
18
|
-
const path
|
|
16
|
+
const net = require('net');
|
|
17
|
+
const fs = require('fs-extra');
|
|
18
|
+
const path = require('path');
|
|
19
19
|
const crypto = require('crypto');
|
|
20
|
-
const utils
|
|
21
|
-
const tools
|
|
20
|
+
const utils = require('@iobroker/db-objects-redis').objectsUtils;
|
|
21
|
+
const tools = require('@iobroker/db-base').tools;
|
|
22
22
|
|
|
23
|
-
const RedisHandler
|
|
23
|
+
const RedisHandler = require('@iobroker/db-base').redisHandler;
|
|
24
24
|
const ObjectsInMemoryFileDB = require('./objectsInMemFileDB');
|
|
25
25
|
|
|
26
26
|
// settings = {
|
|
@@ -57,30 +57,47 @@ class ObjectsInMemoryServer extends ObjectsInMemoryFileDB {
|
|
|
57
57
|
super(settings);
|
|
58
58
|
|
|
59
59
|
this.serverConnections = {};
|
|
60
|
-
this.namespaceObjects
|
|
61
|
-
|
|
62
|
-
|
|
60
|
+
this.namespaceObjects =
|
|
61
|
+
(this.settings.redisNamespace || (settings.connection && settings.connection.redisNamespace) || 'cfg') +
|
|
62
|
+
'.';
|
|
63
|
+
this.namespaceFile = this.namespaceObjects + 'f.';
|
|
64
|
+
this.namespaceObj = this.namespaceObjects + 'o.';
|
|
65
|
+
this.namespaceSet = this.namespaceObjects + 's.';
|
|
66
|
+
this.namespaceSetLen = this.namespaceSet.length;
|
|
63
67
|
// this.namespaceObjectsLen = this.namespaceObjects.length;
|
|
64
|
-
this.namespaceFileLen
|
|
65
|
-
this.namespaceObjLen
|
|
68
|
+
this.namespaceFileLen = this.namespaceFile.length;
|
|
69
|
+
this.namespaceObjLen = this.namespaceObj.length;
|
|
70
|
+
this.metaNamespace = (this.settings.metaNamespace || 'meta') + '.';
|
|
71
|
+
this.metaNamespaceLen = this.metaNamespace.length;
|
|
66
72
|
|
|
67
73
|
this.knownScripts = {};
|
|
68
74
|
|
|
69
75
|
this.normalizeFileRegex1 = new RegExp('^(.*)\\$%\\$(.*)\\$%\\$(meta|data)$');
|
|
70
76
|
this.normalizeFileRegex2 = new RegExp('^(.*)\\$%\\$(.*)\\/?\\*$');
|
|
71
77
|
|
|
72
|
-
this.open()
|
|
73
|
-
|
|
74
|
-
|
|
75
|
-
|
|
78
|
+
this.open()
|
|
79
|
+
.then(() => {
|
|
80
|
+
return this._initRedisServer(this.settings.connection);
|
|
81
|
+
})
|
|
82
|
+
.then(() => {
|
|
83
|
+
this.log.debug(
|
|
84
|
+
this.namespace +
|
|
85
|
+
' ' +
|
|
86
|
+
(settings.secure ? 'Secure ' : '') +
|
|
87
|
+
' Redis inMem-objects listening on port ' +
|
|
88
|
+
(settings.port || 9001)
|
|
89
|
+
);
|
|
76
90
|
|
|
77
|
-
|
|
78
|
-
|
|
79
|
-
|
|
80
|
-
|
|
81
|
-
|
|
82
|
-
|
|
83
|
-
|
|
91
|
+
if (typeof this.settings.connected === 'function') {
|
|
92
|
+
setImmediate(() => this.settings.connected());
|
|
93
|
+
}
|
|
94
|
+
})
|
|
95
|
+
.catch(e => {
|
|
96
|
+
this.log.error(
|
|
97
|
+
this.namespace + ' Cannot start inMem-objects on port ' + (settings.port || 9001) + ': ' + e.message
|
|
98
|
+
);
|
|
99
|
+
process.exit(24); // todo: replace it with exitcode
|
|
100
|
+
});
|
|
84
101
|
}
|
|
85
102
|
|
|
86
103
|
/**
|
|
@@ -98,7 +115,7 @@ class ObjectsInMemoryServer extends ObjectsInMemoryFileDB {
|
|
|
98
115
|
if (Array.isArray(idWithNamespace)) {
|
|
99
116
|
const ids = [];
|
|
100
117
|
idWithNamespace.forEach(el => {
|
|
101
|
-
const {id, namespace} = this._normalizeId(el);
|
|
118
|
+
const { id, namespace } = this._normalizeId(el);
|
|
102
119
|
ids.push(id);
|
|
103
120
|
ns = namespace; // we ignore the pot. case from arrays with different namespaces
|
|
104
121
|
});
|
|
@@ -111,7 +128,10 @@ class ObjectsInMemoryServer extends ObjectsInMemoryFileDB {
|
|
|
111
128
|
idx = this.namespaceObjLen;
|
|
112
129
|
} else if (idWithNamespace.startsWith(this.namespaceFile)) {
|
|
113
130
|
idx = this.namespaceFileLen;
|
|
131
|
+
} else if (idWithNamespace.startsWith(this.namespaceSet)) {
|
|
132
|
+
idx = this.namespaceSetLen;
|
|
114
133
|
}
|
|
134
|
+
|
|
115
135
|
if (idx !== -1) {
|
|
116
136
|
ns = idWithNamespace.substr(0, idx);
|
|
117
137
|
id = idWithNamespace.substr(idx);
|
|
@@ -121,7 +141,7 @@ class ObjectsInMemoryServer extends ObjectsInMemoryFileDB {
|
|
|
121
141
|
if (fileIdDetails) {
|
|
122
142
|
id = fileIdDetails[1];
|
|
123
143
|
name = fileIdDetails[2] || '';
|
|
124
|
-
isMeta =
|
|
144
|
+
isMeta = fileIdDetails[3] === 'meta';
|
|
125
145
|
} else {
|
|
126
146
|
fileIdDetails = id.match(this.normalizeFileRegex2);
|
|
127
147
|
if (fileIdDetails) {
|
|
@@ -134,9 +154,15 @@ class ObjectsInMemoryServer extends ObjectsInMemoryFileDB {
|
|
|
134
154
|
}
|
|
135
155
|
}
|
|
136
156
|
}
|
|
157
|
+
} else if (idWithNamespace.startsWith(this.metaNamespace)) {
|
|
158
|
+
const idx = this.metaNamespaceLen;
|
|
159
|
+
if (idx !== -1) {
|
|
160
|
+
ns = idWithNamespace.substr(0, idx);
|
|
161
|
+
id = idWithNamespace.substr(idx);
|
|
162
|
+
}
|
|
137
163
|
}
|
|
138
164
|
}
|
|
139
|
-
return {id, namespace: ns, name, isMeta};
|
|
165
|
+
return { id, namespace: ns, name, isMeta };
|
|
140
166
|
}
|
|
141
167
|
|
|
142
168
|
/**
|
|
@@ -154,12 +180,20 @@ class ObjectsInMemoryServer extends ObjectsInMemoryFileDB {
|
|
|
154
180
|
const s = client._subscribe[type];
|
|
155
181
|
|
|
156
182
|
const found = s.find(sub => sub.regex.test(id));
|
|
183
|
+
|
|
157
184
|
if (found) {
|
|
158
|
-
|
|
159
|
-
|
|
160
|
-
|
|
161
|
-
|
|
162
|
-
|
|
185
|
+
if (type === 'meta') {
|
|
186
|
+
this.log.silly(`${this.namespace} Redis Publish Meta ${id}=${obj}`);
|
|
187
|
+
const sendPattern = this.metaNamespace + found.pattern;
|
|
188
|
+
const sendId = this.metaNamespace + id;
|
|
189
|
+
client.sendArray(null, ['pmessage', sendPattern, sendId, obj]);
|
|
190
|
+
} else {
|
|
191
|
+
const objString = JSON.stringify(obj);
|
|
192
|
+
this.log.silly(this.namespace + ' Redis Publish Object ' + id + '=' + objString);
|
|
193
|
+
const sendPattern = (type === 'objects' ? '' : this.namespaceObjects) + found.pattern;
|
|
194
|
+
const sendId = (type === 'objects' ? this.namespaceObj : this.namespaceObjects) + id;
|
|
195
|
+
client.sendArray(null, ['pmessage', sendPattern, sendId, objString]);
|
|
196
|
+
}
|
|
163
197
|
return 1;
|
|
164
198
|
}
|
|
165
199
|
return 0;
|
|
@@ -174,12 +208,11 @@ class ObjectsInMemoryServer extends ObjectsInMemoryFileDB {
|
|
|
174
208
|
*/
|
|
175
209
|
getFileId(id, name, isMeta) {
|
|
176
210
|
// e.g. ekey.admin and admin/ekey.png
|
|
177
|
-
if (id.
|
|
178
|
-
if (name.
|
|
211
|
+
if (id.endsWith('.admin')) {
|
|
212
|
+
if (name.startsWith('admin/')) {
|
|
179
213
|
name = name.replace(/^admin\//, '');
|
|
180
|
-
} else
|
|
181
|
-
|
|
182
|
-
if (name.match(/^iobroker.[-\d\w]\/admin\//i)) {
|
|
214
|
+
} else if (name.match(/^iobroker.[-\d\w]\/admin\//i)) {
|
|
215
|
+
// e.g. ekey.admin and iobroker.ekey/admin/ekey.png
|
|
183
216
|
name = name.replace(/^iobroker.[-\d\w]\/admin\//i, '');
|
|
184
217
|
}
|
|
185
218
|
}
|
|
@@ -243,33 +276,52 @@ class ObjectsInMemoryServer extends ObjectsInMemoryFileDB {
|
|
|
243
276
|
search = scriptSearch[1];
|
|
244
277
|
}
|
|
245
278
|
|
|
246
|
-
this.knownScripts[scriptChecksum] = {design: design, search: search};
|
|
279
|
+
this.knownScripts[scriptChecksum] = { design: design, search: search };
|
|
247
280
|
if (this.settings.connection.enhancedLogging) {
|
|
248
|
-
this.log.silly(
|
|
281
|
+
this.log.silly(
|
|
282
|
+
`${namespaceLog} Register View LUA Script: ${scriptChecksum} = ${JSON.stringify(
|
|
283
|
+
this.knownScripts[scriptChecksum]
|
|
284
|
+
)}`
|
|
285
|
+
);
|
|
249
286
|
}
|
|
250
287
|
handler.sendBulk(responseId, scriptChecksum);
|
|
251
288
|
} else if (scriptFunc && scriptFunc[1]) {
|
|
252
|
-
this.knownScripts[scriptChecksum] = {func: scriptFunc[1]};
|
|
289
|
+
this.knownScripts[scriptChecksum] = { func: scriptFunc[1] };
|
|
253
290
|
if (this.settings.connection.enhancedLogging) {
|
|
254
|
-
this.log.silly(
|
|
291
|
+
this.log.silly(
|
|
292
|
+
`${namespaceLog} Register Func LUA Script: ${scriptChecksum} = ${JSON.stringify(
|
|
293
|
+
this.knownScripts[scriptChecksum]
|
|
294
|
+
)}`
|
|
295
|
+
);
|
|
296
|
+
}
|
|
297
|
+
handler.sendBulk(responseId, scriptChecksum);
|
|
298
|
+
} else if (data[1].includes('-- REDLOCK SCRIPT')) {
|
|
299
|
+
// redlock scripts are currently not needed for Simulator
|
|
300
|
+
this.knownScripts[scriptChecksum] = { redlock: true };
|
|
301
|
+
if (this.settings.connection.enhancedLogging) {
|
|
302
|
+
this.log.silly(
|
|
303
|
+
`${namespaceLog} Register Func LUA Script: ${scriptChecksum} = ${JSON.stringify(
|
|
304
|
+
this.knownScripts[scriptChecksum]
|
|
305
|
+
)}`
|
|
306
|
+
);
|
|
255
307
|
}
|
|
256
308
|
handler.sendBulk(responseId, scriptChecksum);
|
|
257
309
|
} else {
|
|
258
|
-
handler.sendError(responseId, new Error(
|
|
310
|
+
handler.sendError(responseId, new Error(`Unknown LUA script ${data[1]}`));
|
|
259
311
|
}
|
|
260
312
|
} else {
|
|
261
|
-
handler.sendError(responseId, new Error(
|
|
313
|
+
handler.sendError(responseId, new Error(`Unsupported Script command ${data[0]}`));
|
|
262
314
|
}
|
|
263
315
|
});
|
|
264
316
|
|
|
265
317
|
// Handle Redis "EVALSHA" request
|
|
266
318
|
handler.on('evalsha', (data, responseId) => {
|
|
267
319
|
if (!this.knownScripts[data[0]]) {
|
|
268
|
-
return void handler.sendError(responseId, new Error(
|
|
320
|
+
return void handler.sendError(responseId, new Error(`Unknown Script ${data[0]}`));
|
|
269
321
|
}
|
|
270
322
|
if (this.knownScripts[data[0]].design) {
|
|
271
323
|
const scriptDesign = this.knownScripts[data[0]].design;
|
|
272
|
-
if (data[2] === this.namespaceObj && data.length > 4) {
|
|
324
|
+
if (typeof data[2] === 'string' && data[2].startsWith(this.namespaceObj) && data.length > 4) {
|
|
273
325
|
let scriptSearch = this.knownScripts[data[0]].search;
|
|
274
326
|
if (scriptDesign === 'system' && !scriptSearch && data[5]) {
|
|
275
327
|
scriptSearch = data[5];
|
|
@@ -278,7 +330,9 @@ class ObjectsInMemoryServer extends ObjectsInMemoryFileDB {
|
|
|
278
330
|
scriptSearch = 'state';
|
|
279
331
|
}
|
|
280
332
|
if (this.settings.connection.enhancedLogging) {
|
|
281
|
-
this.log.silly(
|
|
333
|
+
this.log.silly(
|
|
334
|
+
`${namespaceLog} Script transformed into getObjectView: design=${scriptDesign}, search=${scriptSearch}`
|
|
335
|
+
);
|
|
282
336
|
}
|
|
283
337
|
let objs;
|
|
284
338
|
try {
|
|
@@ -288,13 +342,18 @@ class ObjectsInMemoryServer extends ObjectsInMemoryFileDB {
|
|
|
288
342
|
include_docs: true
|
|
289
343
|
});
|
|
290
344
|
} catch (err) {
|
|
291
|
-
return void handler.sendError(
|
|
345
|
+
return void handler.sendError(
|
|
346
|
+
responseId,
|
|
347
|
+
new Error(
|
|
348
|
+
'_getObjectView Error for ' + scriptDesign + '/' + scriptSearch + ': ' + err.message
|
|
349
|
+
)
|
|
350
|
+
);
|
|
292
351
|
}
|
|
293
352
|
const res = objs.rows.map(obj => JSON.stringify(this.dataset[obj.value._id || obj.id]));
|
|
294
353
|
handler.sendArray(responseId, res);
|
|
295
354
|
}
|
|
296
355
|
} else if (this.knownScripts[data[0]].func && data.length > 4) {
|
|
297
|
-
const scriptFunc = {map: this.knownScripts[data[0]].func.replace('%1', data[5])};
|
|
356
|
+
const scriptFunc = { map: this.knownScripts[data[0]].func.replace('%1', data[5]) };
|
|
298
357
|
if (this.settings.connection.enhancedLogging) {
|
|
299
358
|
this.log.silly(`${namespaceLog} Script transformed into _applyView: func=${scriptFunc.map}`);
|
|
300
359
|
}
|
|
@@ -306,16 +365,20 @@ class ObjectsInMemoryServer extends ObjectsInMemoryFileDB {
|
|
|
306
365
|
const res = objs.rows.map(obj => JSON.stringify(this.dataset[obj.value._id || obj.id]));
|
|
307
366
|
|
|
308
367
|
return void handler.sendArray(responseId, res);
|
|
368
|
+
} else if (this.knownScripts[data[0]].redlock) {
|
|
369
|
+
// just return a dummy
|
|
370
|
+
return void handler.sendArray(responseId, [0]);
|
|
309
371
|
} else {
|
|
310
|
-
handler.sendError(responseId, new Error(
|
|
372
|
+
handler.sendError(responseId, new Error(`Unknown LUA script eval call ${JSON.stringify(data)}`));
|
|
311
373
|
}
|
|
312
374
|
});
|
|
313
375
|
|
|
314
376
|
// Handle Redis "PUBLISH" request
|
|
315
377
|
handler.on('publish', (data, responseId) => {
|
|
316
|
-
const {id, namespace} = this._normalizeId(data[0]);
|
|
378
|
+
const { id, namespace } = this._normalizeId(data[0]);
|
|
317
379
|
|
|
318
|
-
if (namespace === this.namespaceObj
|
|
380
|
+
if (namespace === this.namespaceObj || namespace === this.metaNamespace) {
|
|
381
|
+
// a "set" always comes afterwards, so do not publish
|
|
319
382
|
return void handler.sendInteger(responseId, 0); // do not publish for now
|
|
320
383
|
}
|
|
321
384
|
const publishCount = this.publishAll(namespace.substr(0, namespace.length - 1), id, JSON.parse(data[1]));
|
|
@@ -327,15 +390,17 @@ class ObjectsInMemoryServer extends ObjectsInMemoryFileDB {
|
|
|
327
390
|
if (!data || !data.length) {
|
|
328
391
|
return void handler.sendArray(responseId, []);
|
|
329
392
|
}
|
|
330
|
-
const {namespace, isMeta} = this._normalizeId(data[0]);
|
|
393
|
+
const { namespace, isMeta } = this._normalizeId(data[0]);
|
|
331
394
|
|
|
332
395
|
if (namespace === this.namespaceObj) {
|
|
333
396
|
const keys = [];
|
|
334
397
|
data.forEach(dataId => {
|
|
335
|
-
const {id, namespace} = this._normalizeId(dataId);
|
|
398
|
+
const { id, namespace } = this._normalizeId(dataId);
|
|
336
399
|
if (namespace !== this.namespaceObj) {
|
|
337
400
|
keys.push(null);
|
|
338
|
-
this.log.warn(
|
|
401
|
+
this.log.warn(
|
|
402
|
+
`${namespaceLog} Got MGET request for non Object-ID in Objects-ID chunk for ${namespace} / ${dataId}`
|
|
403
|
+
);
|
|
339
404
|
return;
|
|
340
405
|
}
|
|
341
406
|
keys.push(id);
|
|
@@ -346,17 +411,19 @@ class ObjectsInMemoryServer extends ObjectsInMemoryFileDB {
|
|
|
346
411
|
} catch (err) {
|
|
347
412
|
return void handler.sendError(responseId, new Error('ERROR _getObjects: ' + err.message));
|
|
348
413
|
}
|
|
349
|
-
result = result.map(el => el ? JSON.stringify(el) : null);
|
|
414
|
+
result = result.map(el => (el ? JSON.stringify(el) : null));
|
|
350
415
|
handler.sendArray(responseId, result);
|
|
351
416
|
} else if (namespace === this.namespaceFile) {
|
|
352
417
|
// Handle request for Meta data for files
|
|
353
418
|
if (isMeta) {
|
|
354
419
|
const response = [];
|
|
355
420
|
data.forEach(dataId => {
|
|
356
|
-
const {id, namespace, name} = this._normalizeId(dataId);
|
|
421
|
+
const { id, namespace, name } = this._normalizeId(dataId);
|
|
357
422
|
if (namespace !== this.namespaceFile) {
|
|
358
423
|
response.push(null);
|
|
359
|
-
this.log.warn(
|
|
424
|
+
this.log.warn(
|
|
425
|
+
`${namespaceLog} Got MGET request for non File ID in File-ID chunk for ${dataId}`
|
|
426
|
+
);
|
|
360
427
|
return;
|
|
361
428
|
}
|
|
362
429
|
this._loadFileSettings(id);
|
|
@@ -370,7 +437,9 @@ class ObjectsInMemoryServer extends ObjectsInMemoryFileDB {
|
|
|
370
437
|
obj.stats = fs.statSync(path.join(this.objectsDir, id, name));
|
|
371
438
|
} catch (err) {
|
|
372
439
|
if (!name.endsWith('/_data.json')) {
|
|
373
|
-
this.log.warn(
|
|
440
|
+
this.log.warn(
|
|
441
|
+
`${namespaceLog} Got MGET request for non existing file ${dataId}, err: ${err.message}`
|
|
442
|
+
);
|
|
374
443
|
}
|
|
375
444
|
response.push(null);
|
|
376
445
|
return;
|
|
@@ -383,13 +452,16 @@ class ObjectsInMemoryServer extends ObjectsInMemoryFileDB {
|
|
|
383
452
|
handler.sendError(responseId, new Error('MGET-UNSUPPORTED for file data'));
|
|
384
453
|
}
|
|
385
454
|
} else {
|
|
386
|
-
handler.sendError(
|
|
455
|
+
handler.sendError(
|
|
456
|
+
responseId,
|
|
457
|
+
new Error('MGET-UNSUPPORTED for namespace ' + namespace + ': Data=' + JSON.stringify(data))
|
|
458
|
+
);
|
|
387
459
|
}
|
|
388
460
|
});
|
|
389
461
|
|
|
390
462
|
// Handle Redis "GET" requests
|
|
391
463
|
handler.on('get', (data, responseId) => {
|
|
392
|
-
const {id, namespace, name, isMeta} = this._normalizeId(data[0]);
|
|
464
|
+
const { id, namespace, name, isMeta } = this._normalizeId(data[0]);
|
|
393
465
|
|
|
394
466
|
if (namespace === this.namespaceObj) {
|
|
395
467
|
const result = this._getObject(id);
|
|
@@ -408,11 +480,14 @@ class ObjectsInMemoryServer extends ObjectsInMemoryFileDB {
|
|
|
408
480
|
return void handler.sendNull(responseId);
|
|
409
481
|
}
|
|
410
482
|
if (stats.isDirectory()) {
|
|
411
|
-
return void handler.sendBulk(
|
|
412
|
-
|
|
413
|
-
|
|
414
|
-
|
|
415
|
-
|
|
483
|
+
return void handler.sendBulk(
|
|
484
|
+
responseId,
|
|
485
|
+
JSON.stringify({
|
|
486
|
+
file: name,
|
|
487
|
+
stats: {},
|
|
488
|
+
isDir: true
|
|
489
|
+
})
|
|
490
|
+
);
|
|
416
491
|
}
|
|
417
492
|
this._loadFileSettings(id);
|
|
418
493
|
if (!this.fileOptions[id] || !this.fileOptions[id][name]) {
|
|
@@ -424,9 +499,16 @@ class ObjectsInMemoryServer extends ObjectsInMemoryFileDB {
|
|
|
424
499
|
obj = {
|
|
425
500
|
mimeType: obj,
|
|
426
501
|
acl: {
|
|
427
|
-
owner:
|
|
428
|
-
|
|
429
|
-
|
|
502
|
+
owner:
|
|
503
|
+
(this.defaultNewAcl && this.defaultNewAcl.owner) || utils.CONSTS.SYSTEM_ADMIN_USER,
|
|
504
|
+
ownerGroup:
|
|
505
|
+
(this.defaultNewAcl && this.defaultNewAcl.ownerGroup) ||
|
|
506
|
+
utils.CONSTS.SYSTEM_ADMIN_GROUP,
|
|
507
|
+
permissions:
|
|
508
|
+
(this.defaultNewAcl && this.defaultNewAcl.file.permissions) ||
|
|
509
|
+
utils.CONSTS.ACCESS_USER_ALL |
|
|
510
|
+
utils.CONSTS.ACCESS_GROUP_ALL |
|
|
511
|
+
utils.CONSTS.ACCESS_EVERY_ALL // 777
|
|
430
512
|
}
|
|
431
513
|
};
|
|
432
514
|
}
|
|
@@ -447,18 +529,36 @@ class ObjectsInMemoryServer extends ObjectsInMemoryFileDB {
|
|
|
447
529
|
if (!Buffer.isBuffer(fileData) && tools.isObject(fileData)) {
|
|
448
530
|
// if its an invalid object, stringify it and log warning
|
|
449
531
|
fileData = JSON.stringify(fileData);
|
|
450
|
-
this.log.warn(
|
|
532
|
+
this.log.warn(
|
|
533
|
+
`${namespaceLog} Data of "${id}/${name}" has invalid structure at file data request: ${fileData}`
|
|
534
|
+
);
|
|
451
535
|
}
|
|
452
536
|
handler.sendBufBulk(responseId, Buffer.from(fileData));
|
|
453
537
|
}
|
|
538
|
+
} else if (namespace === this.metaNamespace) {
|
|
539
|
+
// special handling for the primaryHost
|
|
540
|
+
if (id === 'objects.primaryHost') {
|
|
541
|
+
// we are the server -> we are primary
|
|
542
|
+
handler.sendString(this.settings.hostname);
|
|
543
|
+
} else {
|
|
544
|
+
const result = this.getMeta(id);
|
|
545
|
+
if (result === undefined || result === null) {
|
|
546
|
+
handler.sendNull(responseId);
|
|
547
|
+
} else {
|
|
548
|
+
handler.sendBulk(responseId, result);
|
|
549
|
+
}
|
|
550
|
+
}
|
|
454
551
|
} else {
|
|
455
|
-
handler.sendError(
|
|
552
|
+
handler.sendError(
|
|
553
|
+
responseId,
|
|
554
|
+
new Error('GET-UNSUPPORTED for namespace ' + namespace + ': Data=' + JSON.stringify(data))
|
|
555
|
+
);
|
|
456
556
|
}
|
|
457
557
|
});
|
|
458
558
|
|
|
459
559
|
// Handle Redis "SET" requests
|
|
460
560
|
handler.on('set', (data, responseId) => {
|
|
461
|
-
const {id, namespace, name, isMeta} = this._normalizeId(data[0]);
|
|
561
|
+
const { id, namespace, name, isMeta } = this._normalizeId(data[0]);
|
|
462
562
|
|
|
463
563
|
if (namespace === this.namespaceObj) {
|
|
464
564
|
try {
|
|
@@ -480,10 +580,16 @@ class ObjectsInMemoryServer extends ObjectsInMemoryFileDB {
|
|
|
480
580
|
// only set if the meta object is already/still existing
|
|
481
581
|
if (this.fileOptions[id]) {
|
|
482
582
|
this.fileOptions[id][name] = JSON.parse(data[1].toString('utf-8'));
|
|
483
|
-
fs.writeFileSync(
|
|
583
|
+
fs.writeFileSync(
|
|
584
|
+
path.join(this.objectsDir, id, '_data.json'),
|
|
585
|
+
JSON.stringify(this.fileOptions[id])
|
|
586
|
+
);
|
|
484
587
|
}
|
|
485
588
|
} catch (err) {
|
|
486
|
-
return void handler.sendError(
|
|
589
|
+
return void handler.sendError(
|
|
590
|
+
responseId,
|
|
591
|
+
new Error(`ERROR writeFile-Meta id=${id}: ${err.message}`)
|
|
592
|
+
);
|
|
487
593
|
}
|
|
488
594
|
handler.sendString(responseId, 'OK');
|
|
489
595
|
} else {
|
|
@@ -491,12 +597,21 @@ class ObjectsInMemoryServer extends ObjectsInMemoryFileDB {
|
|
|
491
597
|
try {
|
|
492
598
|
this._writeFile(id, name, data[1]);
|
|
493
599
|
} catch (err) {
|
|
494
|
-
return void handler.sendError(
|
|
600
|
+
return void handler.sendError(
|
|
601
|
+
responseId,
|
|
602
|
+
new Error(`ERROR writeFile id=${id}: ${err.message}`)
|
|
603
|
+
);
|
|
495
604
|
}
|
|
496
605
|
handler.sendString(responseId, 'OK');
|
|
497
606
|
}
|
|
607
|
+
} else if (namespace === this.metaNamespace) {
|
|
608
|
+
this.setMeta(id, data[1].toString('utf-8'));
|
|
609
|
+
handler.sendString(responseId, 'OK');
|
|
498
610
|
} else {
|
|
499
|
-
handler.sendError(
|
|
611
|
+
handler.sendError(
|
|
612
|
+
responseId,
|
|
613
|
+
new Error(`SET-UNSUPPORTED for namespace ${namespace}: Data=${JSON.stringify(data)}`)
|
|
614
|
+
);
|
|
500
615
|
}
|
|
501
616
|
});
|
|
502
617
|
|
|
@@ -507,7 +622,10 @@ class ObjectsInMemoryServer extends ObjectsInMemoryFileDB {
|
|
|
507
622
|
|
|
508
623
|
if (oldDetails.namespace === this.namespaceFile) {
|
|
509
624
|
if (oldDetails.id !== newDetails.id) {
|
|
510
|
-
return void handler.sendError(
|
|
625
|
+
return void handler.sendError(
|
|
626
|
+
responseId,
|
|
627
|
+
new Error('ERROR renameObject: id needs to stay the same')
|
|
628
|
+
);
|
|
511
629
|
}
|
|
512
630
|
|
|
513
631
|
// Handle request for Meta data for files
|
|
@@ -523,13 +641,16 @@ class ObjectsInMemoryServer extends ObjectsInMemoryFileDB {
|
|
|
523
641
|
handler.sendString(responseId, 'OK');
|
|
524
642
|
}
|
|
525
643
|
} else {
|
|
526
|
-
handler.sendError(
|
|
644
|
+
handler.sendError(
|
|
645
|
+
responseId,
|
|
646
|
+
new Error(`RENAME-UNSUPPORTED for namespace ${oldDetails.namespace}: Data=${JSON.stringify(data)}`)
|
|
647
|
+
);
|
|
527
648
|
}
|
|
528
649
|
});
|
|
529
650
|
|
|
530
651
|
// Handle Redis "DEL" request for state and session namespace
|
|
531
652
|
handler.on('del', (data, responseId) => {
|
|
532
|
-
const {id, namespace, name, isMeta} = this._normalizeId(data[0]);
|
|
653
|
+
const { id, namespace, name, isMeta } = this._normalizeId(data[0]);
|
|
533
654
|
|
|
534
655
|
if (namespace === this.namespaceObj) {
|
|
535
656
|
try {
|
|
@@ -553,7 +674,10 @@ class ObjectsInMemoryServer extends ObjectsInMemoryFileDB {
|
|
|
553
674
|
handler.sendString(responseId, 'OK');
|
|
554
675
|
}
|
|
555
676
|
} else {
|
|
556
|
-
handler.sendError(
|
|
677
|
+
handler.sendError(
|
|
678
|
+
responseId,
|
|
679
|
+
new Error(`DEL-UNSUPPORTED for namespace ${namespace}: Data=${JSON.stringify(data)}`)
|
|
680
|
+
);
|
|
557
681
|
}
|
|
558
682
|
});
|
|
559
683
|
|
|
@@ -563,7 +687,7 @@ class ObjectsInMemoryServer extends ObjectsInMemoryFileDB {
|
|
|
563
687
|
}
|
|
564
688
|
|
|
565
689
|
// Note: we only simulate single key existence check
|
|
566
|
-
const {id, namespace, name} = this._normalizeId(data[0]);
|
|
690
|
+
const { id, namespace, name } = this._normalizeId(data[0]);
|
|
567
691
|
|
|
568
692
|
if (namespace === this.namespaceObj) {
|
|
569
693
|
let exists;
|
|
@@ -581,6 +705,9 @@ class ObjectsInMemoryServer extends ObjectsInMemoryFileDB {
|
|
|
581
705
|
return void handler.sendError(responseId, e);
|
|
582
706
|
}
|
|
583
707
|
handler.sendInteger(responseId, exists ? 1 : 0);
|
|
708
|
+
} else if (namespace === this.namespaceSet) {
|
|
709
|
+
// we are not using sets in simulator, so just say it exists
|
|
710
|
+
return void handler.sendInteger(responseId, 1);
|
|
584
711
|
} else {
|
|
585
712
|
handler.sendError(responseId, new Error(`EXISTS-UNSUPPORTED for namespace ${namespace}`));
|
|
586
713
|
}
|
|
@@ -604,33 +731,73 @@ class ObjectsInMemoryServer extends ObjectsInMemoryFileDB {
|
|
|
604
731
|
return this._handleScanOrKeys(handler, data[0], responseId);
|
|
605
732
|
});
|
|
606
733
|
|
|
734
|
+
// commands for redis SETS, just dummies
|
|
735
|
+
handler.on('sadd', (data, responseId) => {
|
|
736
|
+
return void handler.sendInteger(responseId, 1);
|
|
737
|
+
});
|
|
738
|
+
|
|
739
|
+
handler.on('srem', (data, responseId) => {
|
|
740
|
+
return void handler.sendInteger(responseId, 1);
|
|
741
|
+
});
|
|
742
|
+
|
|
743
|
+
handler.on('sscan', (data, responseId) => {
|
|
744
|
+
// for file DB it does the same as scan but data looks different
|
|
745
|
+
if (!data || data.length < 4) {
|
|
746
|
+
return void handler.sendArray(responseId, ['0', []]);
|
|
747
|
+
}
|
|
748
|
+
|
|
749
|
+
return this._handleScanOrKeys(handler, data[3], responseId, true);
|
|
750
|
+
});
|
|
751
|
+
|
|
607
752
|
// Handle Redis "PSUBSCRIBE" request for state, log and session namespace
|
|
608
753
|
handler.on('psubscribe', (data, responseId) => {
|
|
609
|
-
const {id, namespace} = this._normalizeId(data[0]);
|
|
754
|
+
const { id, namespace } = this._normalizeId(data[0]);
|
|
610
755
|
|
|
611
756
|
if (namespace === this.namespaceObj) {
|
|
612
757
|
this._subscribeConfigForClient(handler, id);
|
|
613
758
|
handler.sendArray(responseId, ['psubscribe', data[0], 1]);
|
|
759
|
+
} else if (namespace === this.metaNamespace) {
|
|
760
|
+
this._subscribeMeta(handler, id);
|
|
761
|
+
handler.sendArray(responseId, ['psubscribe', data[0], 1]);
|
|
614
762
|
} else {
|
|
615
|
-
handler.sendError(
|
|
763
|
+
handler.sendError(
|
|
764
|
+
responseId,
|
|
765
|
+
new Error('PSUBSCRIBE-UNSUPPORTED for namespace ' + namespace + ': Data=' + JSON.stringify(data))
|
|
766
|
+
);
|
|
616
767
|
}
|
|
617
768
|
});
|
|
618
769
|
|
|
619
770
|
// Handle Redis "UNSUBSCRIBE" request for state, log and session namespace
|
|
620
771
|
handler.on('punsubscribe', (data, responseId) => {
|
|
621
|
-
const {id, namespace} = this._normalizeId(data[0]);
|
|
772
|
+
const { id, namespace } = this._normalizeId(data[0]);
|
|
622
773
|
|
|
623
774
|
if (namespace === this.namespaceObj) {
|
|
624
775
|
this._unsubscribeConfigForClient(handler, id);
|
|
625
776
|
handler.sendArray(responseId, ['punsubscribe', data[0], 1]);
|
|
626
777
|
} else {
|
|
627
|
-
handler.sendError(
|
|
778
|
+
handler.sendError(
|
|
779
|
+
responseId,
|
|
780
|
+
new Error('PUNSUBSCRIBE-UNSUPPORTED for namespace ' + namespace + ': Data=' + JSON.stringify(data))
|
|
781
|
+
);
|
|
782
|
+
}
|
|
783
|
+
});
|
|
784
|
+
|
|
785
|
+
// Handle Redis "SUBSCRIBE" ... currently mainly ignored
|
|
786
|
+
handler.on('subscribe', (data, responseId) => {
|
|
787
|
+
if (data[0].startsWith('__keyevent@')) {
|
|
788
|
+
// we ignore these type of events because we publish expires anyway directly
|
|
789
|
+
handler.sendArray(responseId, ['subscribe', data[0], 1]);
|
|
790
|
+
} else {
|
|
791
|
+
handler.sendError(responseId, new Error(`SUBSCRIBE-UNSUPPORTED for ${data[0]}`));
|
|
628
792
|
}
|
|
629
793
|
});
|
|
630
794
|
|
|
631
795
|
// Handle Redis "CONFIG" ... currently mainly ignored
|
|
632
796
|
handler.on('config', (data, responseId) => {
|
|
633
|
-
if (data[0] === 'set' && data[1] === '
|
|
797
|
+
if (data[0] === 'set' && data[1] === 'notify-keyspace-events') {
|
|
798
|
+
// we ignore these type of commands for now, should only be to subscribe to keyspace events
|
|
799
|
+
handler.sendString(responseId, 'OK');
|
|
800
|
+
} else if (data[0] === 'set' && data[1] === 'lua-time-limit') {
|
|
634
801
|
// we ignore these type of commands for now, irrelevant
|
|
635
802
|
handler.sendString(responseId, 'OK');
|
|
636
803
|
} else {
|
|
@@ -661,7 +828,7 @@ class ObjectsInMemoryServer extends ObjectsInMemoryFileDB {
|
|
|
661
828
|
}
|
|
662
829
|
});
|
|
663
830
|
|
|
664
|
-
handler.on('error', err => this.log.warn(`${namespaceLog} Redis
|
|
831
|
+
handler.on('error', err => this.log.warn(`${namespaceLog} Redis objects: ${err}`));
|
|
665
832
|
}
|
|
666
833
|
|
|
667
834
|
/**
|
|
@@ -684,17 +851,19 @@ class ObjectsInMemoryServer extends ObjectsInMemoryFileDB {
|
|
|
684
851
|
delete this.serverConnections[s];
|
|
685
852
|
});
|
|
686
853
|
|
|
687
|
-
return /** @type {Promise<void>} */ (
|
|
688
|
-
|
|
689
|
-
|
|
690
|
-
|
|
691
|
-
|
|
692
|
-
|
|
693
|
-
|
|
694
|
-
|
|
695
|
-
|
|
696
|
-
|
|
697
|
-
|
|
854
|
+
return /** @type {Promise<void>} */ (
|
|
855
|
+
new Promise(resolve => {
|
|
856
|
+
if (!this.server) {
|
|
857
|
+
return void resolve();
|
|
858
|
+
}
|
|
859
|
+
try {
|
|
860
|
+
this.server.close(() => resolve());
|
|
861
|
+
} catch (e) {
|
|
862
|
+
console.log(e.message);
|
|
863
|
+
resolve();
|
|
864
|
+
}
|
|
865
|
+
})
|
|
866
|
+
);
|
|
698
867
|
}
|
|
699
868
|
}
|
|
700
869
|
|
|
@@ -708,13 +877,14 @@ class ObjectsInMemoryServer extends ObjectsInMemoryFileDB {
|
|
|
708
877
|
* @private
|
|
709
878
|
*/
|
|
710
879
|
_handleScanOrKeys(handler, pattern, responseId, isScan = false) {
|
|
711
|
-
const {id, namespace, name, isMeta} = this._normalizeId(pattern);
|
|
880
|
+
const { id, namespace, name, isMeta } = this._normalizeId(pattern);
|
|
712
881
|
|
|
713
882
|
let response = [];
|
|
714
883
|
if (namespace === this.namespaceObj || namespace === this.namespaceObjects) {
|
|
715
|
-
response =
|
|
884
|
+
response = this._getKeys(id).map(val => this.namespaceObj + val);
|
|
716
885
|
// if scan, we send the cursor as first argument
|
|
717
|
-
if (namespace !== this.namespaceObjects) {
|
|
886
|
+
if (namespace !== this.namespaceObjects) {
|
|
887
|
+
// When it was not the full DB namespace send out response
|
|
718
888
|
return void handler.sendArray(responseId, isScan ? ['0', response] : response);
|
|
719
889
|
}
|
|
720
890
|
}
|
|
@@ -725,13 +895,15 @@ class ObjectsInMemoryServer extends ObjectsInMemoryFileDB {
|
|
|
725
895
|
try {
|
|
726
896
|
res = this._readDir(id, name);
|
|
727
897
|
if (!res || !res.length) {
|
|
728
|
-
res = [
|
|
729
|
-
|
|
730
|
-
|
|
731
|
-
|
|
732
|
-
|
|
733
|
-
|
|
734
|
-
|
|
898
|
+
res = [
|
|
899
|
+
{
|
|
900
|
+
file: '_data.json',
|
|
901
|
+
stats: {},
|
|
902
|
+
isDir: false,
|
|
903
|
+
virtualFile: true,
|
|
904
|
+
notExists: true
|
|
905
|
+
}
|
|
906
|
+
];
|
|
735
907
|
}
|
|
736
908
|
} catch (err) {
|
|
737
909
|
if (!err.message.endsWith(utils.ERRORS.ERROR_NOT_FOUND)) {
|
|
@@ -758,11 +930,17 @@ class ObjectsInMemoryServer extends ObjectsInMemoryFileDB {
|
|
|
758
930
|
response.push(this.getFileId(entryId, baseName + arr.file, false));
|
|
759
931
|
});
|
|
760
932
|
handler.sendArray(responseId, isScan ? ['0', response] : response); // send out file or full db response
|
|
761
|
-
} else {
|
|
933
|
+
} else {
|
|
934
|
+
// such a request should never happen
|
|
762
935
|
handler.sendArray(responseId, isScan ? ['0', []] : []); // send out file or full db response
|
|
763
936
|
}
|
|
937
|
+
} else if (namespace === this.namespaceSet) {
|
|
938
|
+
handler.sendArray(responseId, isScan ? ['0', []] : []); // send out empty array, we have no sets
|
|
764
939
|
} else {
|
|
765
|
-
handler.sendError(
|
|
940
|
+
handler.sendError(
|
|
941
|
+
responseId,
|
|
942
|
+
new Error(`${isScan ? 'SCAN' : 'KEYS'}-UNSUPPORTED for namespace ${namespace}: Pattern=${pattern}`)
|
|
943
|
+
);
|
|
766
944
|
}
|
|
767
945
|
}
|
|
768
946
|
|
|
@@ -806,7 +984,12 @@ class ObjectsInMemoryServer extends ObjectsInMemoryFileDB {
|
|
|
806
984
|
try {
|
|
807
985
|
this.server = net.createServer();
|
|
808
986
|
this.server.on('error', err =>
|
|
809
|
-
this.log.info(
|
|
987
|
+
this.log.info(
|
|
988
|
+
`${this.namespace} ${settings.secure ? 'Secure ' : ''} Error inMem-objects listening on port ${
|
|
989
|
+
settings.port || 9001
|
|
990
|
+
}: ${err}`
|
|
991
|
+
)
|
|
992
|
+
);
|
|
810
993
|
this.server.on('connection', socket => this._initSocket(socket));
|
|
811
994
|
|
|
812
995
|
this.server.listen(
|