@iobroker/db-objects-jsonl 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
CHANGED
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
/**
|
|
2
2
|
* Object DB in memory - Server
|
|
3
3
|
*
|
|
4
|
-
* Copyright 2013-
|
|
4
|
+
* Copyright 2013-2022 bluefox <dogafox@gmail.com>
|
|
5
5
|
*
|
|
6
6
|
* MIT License
|
|
7
7
|
*
|
|
@@ -13,24 +13,28 @@
|
|
|
13
13
|
/* jshint -W061 */
|
|
14
14
|
'use strict';
|
|
15
15
|
|
|
16
|
-
const ObjectsInMemoryFileDB
|
|
16
|
+
const ObjectsInMemoryFileDB = require('@iobroker/db-objects-file').ObjectsInMemoryFileDB;
|
|
17
17
|
const { JsonlDB } = require('@alcalzone/jsonl-db');
|
|
18
18
|
const path = require('path');
|
|
19
|
+
const fs = require('fs');
|
|
20
|
+
const os = require('os');
|
|
21
|
+
const { tools } = require('@iobroker/js-controller-common');
|
|
19
22
|
|
|
20
23
|
/**
|
|
21
24
|
* This class inherits InMemoryFileDB class and adds all relevant logic for objects
|
|
22
25
|
* including the available methods for use by js-controller directly
|
|
23
26
|
**/
|
|
24
27
|
class ObjectsInMemoryJsonlDB extends ObjectsInMemoryFileDB {
|
|
25
|
-
|
|
26
28
|
constructor(settings) {
|
|
27
29
|
settings = settings || {};
|
|
28
30
|
settings.fileDB = {
|
|
29
31
|
fileName: 'objects.json',
|
|
30
32
|
backupDirName: 'backup-objects'
|
|
31
33
|
};
|
|
34
|
+
super(settings);
|
|
35
|
+
|
|
32
36
|
/** @type {import("@alcalzone/jsonl-db").JsonlDBOptions<any>} */
|
|
33
|
-
const jsonlOptions = {
|
|
37
|
+
const jsonlOptions = this.settings.connection.jsonlOptions || {
|
|
34
38
|
autoCompress: {
|
|
35
39
|
sizeFactor: 2,
|
|
36
40
|
sizeFactorMinimumSize: 25000
|
|
@@ -38,24 +42,21 @@ class ObjectsInMemoryJsonlDB extends ObjectsInMemoryFileDB {
|
|
|
38
42
|
ignoreReadErrors: true,
|
|
39
43
|
throttleFS: {
|
|
40
44
|
intervalMs: 60000,
|
|
41
|
-
maxBufferedCommands:
|
|
45
|
+
maxBufferedCommands: 1000
|
|
42
46
|
}
|
|
43
47
|
};
|
|
44
48
|
settings.jsonlDB = {
|
|
45
|
-
fileName: 'objects.jsonl'
|
|
46
|
-
options: jsonlOptions
|
|
49
|
+
fileName: 'objects.jsonl'
|
|
47
50
|
};
|
|
48
|
-
super(settings);
|
|
49
51
|
|
|
50
52
|
/** @type {JsonlDB<any>} */
|
|
51
|
-
this._db = new JsonlDB(
|
|
52
|
-
path.join(this.dataDir, settings.jsonlDB.fileName),
|
|
53
|
-
jsonlOptions
|
|
54
|
-
);
|
|
53
|
+
this._db = new JsonlDB(path.join(this.dataDir, settings.jsonlDB.fileName), jsonlOptions);
|
|
55
54
|
}
|
|
56
55
|
|
|
57
56
|
async open() {
|
|
58
|
-
await this.
|
|
57
|
+
if (!(await this._maybeMigrateFileDB())) {
|
|
58
|
+
await this._db.open();
|
|
59
|
+
}
|
|
59
60
|
|
|
60
61
|
// Create an object-like wrapper around the internal Map
|
|
61
62
|
this.dataset = new Proxy(this._db, {
|
|
@@ -92,18 +93,128 @@ class ObjectsInMemoryJsonlDB extends ObjectsInMemoryFileDB {
|
|
|
92
93
|
};
|
|
93
94
|
}
|
|
94
95
|
});
|
|
96
|
+
|
|
97
|
+
if (this.settings.backup && this.settings.backup.period && !this.settings.backup.disabled) {
|
|
98
|
+
this._backupInterval = setInterval(() => {
|
|
99
|
+
this.saveBackup();
|
|
100
|
+
}, this.settings.backup.period);
|
|
101
|
+
}
|
|
102
|
+
}
|
|
103
|
+
|
|
104
|
+
/**
|
|
105
|
+
* Checks if an existing file DB should be migrated to JSONL
|
|
106
|
+
* @returns {Promise<boolean>} true if the file DB was migrated. false if not.
|
|
107
|
+
* If this returns true, the jsonl DB was opened and doesn't need to be opened again.
|
|
108
|
+
*/
|
|
109
|
+
async _maybeMigrateFileDB() {
|
|
110
|
+
const jsonlFileName = path.join(this.dataDir, this.settings.jsonlDB.fileName);
|
|
111
|
+
const jsonFileName = path.join(this.dataDir, this.settings.fileDB.fileName);
|
|
112
|
+
const bakFileName = path.join(this.dataDir, this.settings.fileDB.fileName + '.bak');
|
|
113
|
+
|
|
114
|
+
// Check the timestamps of each file, defaulting to 0 if they don't exist
|
|
115
|
+
let jsonlTimeStamp = 0;
|
|
116
|
+
let jsonTimeStamp = 0;
|
|
117
|
+
let bakTimeStamp = 0;
|
|
118
|
+
try {
|
|
119
|
+
const stat = fs.statSync(jsonlFileName);
|
|
120
|
+
if (stat.isFile()) {
|
|
121
|
+
jsonlTimeStamp = stat.mtime;
|
|
122
|
+
}
|
|
123
|
+
} catch {
|
|
124
|
+
// ignore
|
|
125
|
+
}
|
|
126
|
+
try {
|
|
127
|
+
const stat = fs.statSync(jsonFileName);
|
|
128
|
+
if (stat.isFile()) {
|
|
129
|
+
jsonTimeStamp = stat.mtime;
|
|
130
|
+
}
|
|
131
|
+
} catch {
|
|
132
|
+
// ignore
|
|
133
|
+
}
|
|
134
|
+
try {
|
|
135
|
+
const stat = fs.statSync(bakFileName);
|
|
136
|
+
if (stat.isFile()) {
|
|
137
|
+
bakTimeStamp = stat.mtime;
|
|
138
|
+
}
|
|
139
|
+
} catch {
|
|
140
|
+
// ignore
|
|
141
|
+
}
|
|
142
|
+
|
|
143
|
+
// Figure out which file needs to be imported
|
|
144
|
+
/** @type {string} */
|
|
145
|
+
let importFilename;
|
|
146
|
+
if (jsonTimeStamp > 0 && jsonTimeStamp >= bakTimeStamp && jsonTimeStamp >= jsonlTimeStamp) {
|
|
147
|
+
importFilename = jsonFileName;
|
|
148
|
+
} else if (bakTimeStamp > 0 && bakTimeStamp >= jsonTimeStamp && bakTimeStamp >= jsonlTimeStamp) {
|
|
149
|
+
importFilename = bakFileName;
|
|
150
|
+
} else {
|
|
151
|
+
// None of the File DB files are newer than the JSONL file
|
|
152
|
+
// There is nothing to restore, we're done
|
|
153
|
+
return false;
|
|
154
|
+
}
|
|
155
|
+
|
|
156
|
+
await this._db.open();
|
|
157
|
+
this._db.clear();
|
|
158
|
+
await this._db.importJson(importFilename);
|
|
159
|
+
|
|
160
|
+
// And rename the existing files to avoid redoing the work next time
|
|
161
|
+
if (fs.existsSync(jsonFileName)) {
|
|
162
|
+
try {
|
|
163
|
+
fs.renameSync(jsonFileName, `${jsonFileName}.migrated`);
|
|
164
|
+
} catch {
|
|
165
|
+
// ignore
|
|
166
|
+
}
|
|
167
|
+
}
|
|
168
|
+
if (fs.existsSync(bakFileName)) {
|
|
169
|
+
try {
|
|
170
|
+
fs.renameSync(bakFileName, `${bakFileName}.migrated`);
|
|
171
|
+
} catch {
|
|
172
|
+
// ignore
|
|
173
|
+
}
|
|
174
|
+
}
|
|
175
|
+
|
|
176
|
+
// Signal to the caller that the DB is already open
|
|
177
|
+
return true;
|
|
95
178
|
}
|
|
96
179
|
|
|
97
180
|
async saveState() {
|
|
98
181
|
// Nothing to do, the DB saves behind the scenes
|
|
99
182
|
}
|
|
100
183
|
|
|
184
|
+
// Is regularly called and stores a compressed backup of the DB
|
|
185
|
+
async saveBackup() {
|
|
186
|
+
const now = Date.now();
|
|
187
|
+
const tmpBackupFileName = path.join(os.tmpdir(), `${this.getTimeStr(now)}_${this.settings.jsonlDB.fileName}`);
|
|
188
|
+
const backupFileName = path.join(
|
|
189
|
+
this.backupDir,
|
|
190
|
+
`${this.getTimeStr(now)}_${this.settings.jsonlDB.fileName}.gz`
|
|
191
|
+
);
|
|
192
|
+
try {
|
|
193
|
+
if (fs.existsSync(backupFileName)) {
|
|
194
|
+
return;
|
|
195
|
+
}
|
|
196
|
+
|
|
197
|
+
// Create a DB dump
|
|
198
|
+
await this._db.dump(tmpBackupFileName);
|
|
199
|
+
// and zip it
|
|
200
|
+
await tools.compressFileGZip(tmpBackupFileName, backupFileName, { deleteInput: true });
|
|
201
|
+
// figure out if older files need to be deleted
|
|
202
|
+
this.deleteOldBackupFiles(this.settings.jsonlDB.fileName);
|
|
203
|
+
} catch (e) {
|
|
204
|
+
this.log.error(`${this.namespace} Cannot save backup ${backupFileName}: ${e.message}`);
|
|
205
|
+
}
|
|
206
|
+
}
|
|
207
|
+
|
|
101
208
|
async destroy() {
|
|
209
|
+
await super.destroy();
|
|
210
|
+
|
|
102
211
|
if (this._db) {
|
|
103
212
|
await this._db.close();
|
|
104
213
|
}
|
|
214
|
+
if (this._backupInterval) {
|
|
215
|
+
clearInterval(this._backupInterval);
|
|
216
|
+
}
|
|
105
217
|
}
|
|
106
|
-
|
|
107
218
|
}
|
|
108
219
|
|
|
109
220
|
module.exports = ObjectsInMemoryJsonlDB;
|
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
/**
|
|
2
2
|
* States 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
|
*
|
|
@@ -18,28 +18,12 @@ const ObjectsInRedisClient = require('@iobroker/db-objects-redis').Client;
|
|
|
18
18
|
const ObjectsInMemServer = require('./objectsInMemServerRedis');
|
|
19
19
|
|
|
20
20
|
class ObjectsInMemoryServerClass extends ObjectsInRedisClient {
|
|
21
|
-
|
|
22
21
|
constructor(settings) {
|
|
23
22
|
settings.autoConnect = false; // delay Client connection to when we need it
|
|
24
|
-
|
|
25
|
-
// hack around testing problem where subscribe was called before connect
|
|
26
|
-
// Should be removed for a later release
|
|
27
|
-
const origConnected = settings.connected;
|
|
28
|
-
settings.connected = () => {
|
|
29
|
-
this.clientConnected = true;
|
|
30
|
-
if (Array.isArray(this.storedSubscribes) && this.storedSubscribes.length) {
|
|
31
|
-
this.log.warn(`${this.namespace} Replay ${this.storedSubscribes.length} subscription calls for Objects Server that were done before the client was connected initially`);
|
|
32
|
-
this.storedSubscribes.forEach((s => this.subscribe(s.pattern, s.options, s.callback)));
|
|
33
|
-
this.storedSubscribes = [];
|
|
34
|
-
}
|
|
35
|
-
origConnected();
|
|
36
|
-
};
|
|
37
23
|
super(settings);
|
|
38
|
-
this.clientConnected = false;
|
|
39
|
-
this.storedSubscribes = [];
|
|
40
24
|
|
|
41
25
|
const serverSettings = {
|
|
42
|
-
namespace:
|
|
26
|
+
namespace: settings.namespace ? `${settings.namespace}-Server` : 'Server',
|
|
43
27
|
connection: settings.connection,
|
|
44
28
|
logger: settings.logger,
|
|
45
29
|
hostname: settings.hostname,
|
|
@@ -66,13 +50,5 @@ class ObjectsInMemoryServerClass extends ObjectsInRedisClient {
|
|
|
66
50
|
dirExists(id, name) {
|
|
67
51
|
return this.objectsServer.dirExists(id, name);
|
|
68
52
|
}
|
|
69
|
-
|
|
70
|
-
subscribe(pattern, options, callback) {
|
|
71
|
-
if (!this.clientConnected) {
|
|
72
|
-
this.storedSubscribes.push({pattern, options, callback}); // we ignore the promise return because not used for this testing issue we work around here
|
|
73
|
-
} else {
|
|
74
|
-
return super.subscribe(pattern, options, callback);
|
|
75
|
-
}
|
|
76
|
-
}
|
|
77
53
|
}
|
|
78
54
|
module.exports = ObjectsInMemoryServerClass;
|
|
@@ -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 ObjectsInMemoryJsonlDB = require('./objectsInMemJsonlDB');
|
|
25
25
|
|
|
26
26
|
// settings = {
|
|
@@ -57,30 +57,48 @@ class ObjectsInMemoryServer extends ObjectsInMemoryJsonlDB {
|
|
|
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;
|
|
67
|
+
|
|
63
68
|
// this.namespaceObjectsLen = this.namespaceObjects.length;
|
|
64
|
-
this.namespaceFileLen
|
|
65
|
-
this.namespaceObjLen
|
|
69
|
+
this.namespaceFileLen = this.namespaceFile.length;
|
|
70
|
+
this.namespaceObjLen = this.namespaceObj.length;
|
|
71
|
+
this.metaNamespace = (this.settings.metaNamespace || 'meta') + '.';
|
|
72
|
+
this.metaNamespaceLen = this.metaNamespace.length;
|
|
66
73
|
|
|
67
74
|
this.knownScripts = {};
|
|
68
75
|
|
|
69
76
|
this.normalizeFileRegex1 = new RegExp('^(.*)\\$%\\$(.*)\\$%\\$(meta|data)$');
|
|
70
77
|
this.normalizeFileRegex2 = new RegExp('^(.*)\\$%\\$(.*)\\/?\\*$');
|
|
71
78
|
|
|
72
|
-
this.open()
|
|
73
|
-
|
|
74
|
-
|
|
75
|
-
|
|
79
|
+
this.open()
|
|
80
|
+
.then(() => {
|
|
81
|
+
return this._initRedisServer(this.settings.connection);
|
|
82
|
+
})
|
|
83
|
+
.then(() => {
|
|
84
|
+
this.log.debug(
|
|
85
|
+
this.namespace +
|
|
86
|
+
' ' +
|
|
87
|
+
(settings.secure ? 'Secure ' : '') +
|
|
88
|
+
' Redis inMem-objects listening on port ' +
|
|
89
|
+
(settings.port || 9001)
|
|
90
|
+
);
|
|
76
91
|
|
|
77
|
-
|
|
78
|
-
|
|
79
|
-
|
|
80
|
-
|
|
81
|
-
|
|
82
|
-
|
|
83
|
-
|
|
92
|
+
if (typeof this.settings.connected === 'function') {
|
|
93
|
+
setImmediate(() => this.settings.connected());
|
|
94
|
+
}
|
|
95
|
+
})
|
|
96
|
+
.catch(e => {
|
|
97
|
+
this.log.error(
|
|
98
|
+
this.namespace + ' Cannot start inMem-objects on port ' + (settings.port || 9001) + ': ' + e.message
|
|
99
|
+
);
|
|
100
|
+
process.exit(24); // todo: replace it with exitcode
|
|
101
|
+
});
|
|
84
102
|
}
|
|
85
103
|
|
|
86
104
|
/**
|
|
@@ -98,7 +116,7 @@ class ObjectsInMemoryServer extends ObjectsInMemoryJsonlDB {
|
|
|
98
116
|
if (Array.isArray(idWithNamespace)) {
|
|
99
117
|
const ids = [];
|
|
100
118
|
idWithNamespace.forEach(el => {
|
|
101
|
-
const {id, namespace} = this._normalizeId(el);
|
|
119
|
+
const { id, namespace } = this._normalizeId(el);
|
|
102
120
|
ids.push(id);
|
|
103
121
|
ns = namespace; // we ignore the pot. case from arrays with different namespaces
|
|
104
122
|
});
|
|
@@ -111,6 +129,8 @@ class ObjectsInMemoryServer extends ObjectsInMemoryJsonlDB {
|
|
|
111
129
|
idx = this.namespaceObjLen;
|
|
112
130
|
} else if (idWithNamespace.startsWith(this.namespaceFile)) {
|
|
113
131
|
idx = this.namespaceFileLen;
|
|
132
|
+
} else if (idWithNamespace.startsWith(this.namespaceSet)) {
|
|
133
|
+
idx = this.namespaceSetLen;
|
|
114
134
|
}
|
|
115
135
|
if (idx !== -1) {
|
|
116
136
|
ns = idWithNamespace.substr(0, idx);
|
|
@@ -121,7 +141,7 @@ class ObjectsInMemoryServer extends ObjectsInMemoryJsonlDB {
|
|
|
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 ObjectsInMemoryJsonlDB {
|
|
|
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
|
/**
|
|
@@ -155,11 +181,18 @@ class ObjectsInMemoryServer extends ObjectsInMemoryJsonlDB {
|
|
|
155
181
|
|
|
156
182
|
const found = s.find(sub => sub.regex.test(id));
|
|
157
183
|
if (found) {
|
|
158
|
-
|
|
159
|
-
|
|
160
|
-
|
|
161
|
-
|
|
162
|
-
|
|
184
|
+
if (type === 'meta') {
|
|
185
|
+
this.log.silly(`${this.namespace} Redis Publish Meta ${id}=${obj}`);
|
|
186
|
+
const sendPattern = this.metaNamespace + found.pattern;
|
|
187
|
+
const sendId = this.metaNamespace + id;
|
|
188
|
+
client.sendArray(null, ['pmessage', sendPattern, sendId, obj]);
|
|
189
|
+
} else {
|
|
190
|
+
const objString = JSON.stringify(obj);
|
|
191
|
+
this.log.silly(this.namespace + ' Redis Publish Object ' + id + '=' + objString);
|
|
192
|
+
const sendPattern = (type === 'objects' ? '' : this.namespaceObjects) + found.pattern;
|
|
193
|
+
const sendId = (type === 'objects' ? this.namespaceObj : this.namespaceObjects) + id;
|
|
194
|
+
client.sendArray(null, ['pmessage', sendPattern, sendId, objString]);
|
|
195
|
+
}
|
|
163
196
|
return 1;
|
|
164
197
|
}
|
|
165
198
|
return 0;
|
|
@@ -174,12 +207,11 @@ class ObjectsInMemoryServer extends ObjectsInMemoryJsonlDB {
|
|
|
174
207
|
*/
|
|
175
208
|
getFileId(id, name, isMeta) {
|
|
176
209
|
// e.g. ekey.admin and admin/ekey.png
|
|
177
|
-
if (id.
|
|
178
|
-
if (name.
|
|
210
|
+
if (id.endsWith('.admin')) {
|
|
211
|
+
if (name.startsWith('admin/')) {
|
|
179
212
|
name = name.replace(/^admin\//, '');
|
|
180
|
-
} else
|
|
181
|
-
|
|
182
|
-
if (name.match(/^iobroker.[-\d\w]\/admin\//i)) {
|
|
213
|
+
} else if (name.match(/^iobroker.[-\d\w]\/admin\//i)) {
|
|
214
|
+
// e.g. ekey.admin and iobroker.ekey/admin/ekey.png
|
|
183
215
|
name = name.replace(/^iobroker.[-\d\w]\/admin\//i, '');
|
|
184
216
|
}
|
|
185
217
|
}
|
|
@@ -243,22 +275,41 @@ class ObjectsInMemoryServer extends ObjectsInMemoryJsonlDB {
|
|
|
243
275
|
search = scriptSearch[1];
|
|
244
276
|
}
|
|
245
277
|
|
|
246
|
-
this.knownScripts[scriptChecksum] = {design: design, search: search};
|
|
278
|
+
this.knownScripts[scriptChecksum] = { design: design, search: search };
|
|
247
279
|
if (this.settings.connection.enhancedLogging) {
|
|
248
|
-
this.log.silly(
|
|
280
|
+
this.log.silly(
|
|
281
|
+
`${namespaceLog} Register View LUA Script: ${scriptChecksum} = ${JSON.stringify(
|
|
282
|
+
this.knownScripts[scriptChecksum]
|
|
283
|
+
)}`
|
|
284
|
+
);
|
|
249
285
|
}
|
|
250
286
|
handler.sendBulk(responseId, scriptChecksum);
|
|
251
287
|
} else if (scriptFunc && scriptFunc[1]) {
|
|
252
|
-
this.knownScripts[scriptChecksum] = {func: scriptFunc[1]};
|
|
288
|
+
this.knownScripts[scriptChecksum] = { func: scriptFunc[1] };
|
|
253
289
|
if (this.settings.connection.enhancedLogging) {
|
|
254
|
-
this.log.silly(
|
|
290
|
+
this.log.silly(
|
|
291
|
+
`${namespaceLog} Register Func LUA Script: ${scriptChecksum} = ${JSON.stringify(
|
|
292
|
+
this.knownScripts[scriptChecksum]
|
|
293
|
+
)}`
|
|
294
|
+
);
|
|
295
|
+
}
|
|
296
|
+
handler.sendBulk(responseId, scriptChecksum);
|
|
297
|
+
} else if (data[1].includes('-- REDLOCK SCRIPT')) {
|
|
298
|
+
// redlock scripts are currently not needed for Simulator
|
|
299
|
+
this.knownScripts[scriptChecksum] = { redlock: true };
|
|
300
|
+
if (this.settings.connection.enhancedLogging) {
|
|
301
|
+
this.log.silly(
|
|
302
|
+
`${namespaceLog} Register Func LUA Script: ${scriptChecksum} = ${JSON.stringify(
|
|
303
|
+
this.knownScripts[scriptChecksum]
|
|
304
|
+
)}`
|
|
305
|
+
);
|
|
255
306
|
}
|
|
256
307
|
handler.sendBulk(responseId, scriptChecksum);
|
|
257
308
|
} else {
|
|
258
|
-
handler.sendError(responseId, new Error(
|
|
309
|
+
handler.sendError(responseId, new Error(`Unknown LUA script ${data[1]}`));
|
|
259
310
|
}
|
|
260
311
|
} else {
|
|
261
|
-
handler.sendError(responseId, new Error(
|
|
312
|
+
handler.sendError(responseId, new Error(`Unsupported Script command ${data[0]}`));
|
|
262
313
|
}
|
|
263
314
|
});
|
|
264
315
|
|
|
@@ -269,7 +320,7 @@ class ObjectsInMemoryServer extends ObjectsInMemoryJsonlDB {
|
|
|
269
320
|
}
|
|
270
321
|
if (this.knownScripts[data[0]].design) {
|
|
271
322
|
const scriptDesign = this.knownScripts[data[0]].design;
|
|
272
|
-
if (data[2] === this.namespaceObj && data.length > 4) {
|
|
323
|
+
if (typeof data[2] === 'string' && data[2].startsWith(this.namespaceObj) && data.length > 4) {
|
|
273
324
|
let scriptSearch = this.knownScripts[data[0]].search;
|
|
274
325
|
if (scriptDesign === 'system' && !scriptSearch && data[5]) {
|
|
275
326
|
scriptSearch = data[5];
|
|
@@ -278,7 +329,9 @@ class ObjectsInMemoryServer extends ObjectsInMemoryJsonlDB {
|
|
|
278
329
|
scriptSearch = 'state';
|
|
279
330
|
}
|
|
280
331
|
if (this.settings.connection.enhancedLogging) {
|
|
281
|
-
this.log.silly(
|
|
332
|
+
this.log.silly(
|
|
333
|
+
`${namespaceLog} Script transformed into getObjectView: design=${scriptDesign}, search=${scriptSearch}`
|
|
334
|
+
);
|
|
282
335
|
}
|
|
283
336
|
let objs;
|
|
284
337
|
try {
|
|
@@ -288,13 +341,17 @@ class ObjectsInMemoryServer extends ObjectsInMemoryJsonlDB {
|
|
|
288
341
|
include_docs: true
|
|
289
342
|
});
|
|
290
343
|
} catch (err) {
|
|
291
|
-
return void handler.sendError(
|
|
344
|
+
return void handler.sendError(
|
|
345
|
+
responseId,
|
|
346
|
+
new Error(`_getObjectView Error for ${scriptDesign}/${scriptSearch}: ${err.message}`)
|
|
347
|
+
);
|
|
292
348
|
}
|
|
349
|
+
|
|
293
350
|
const res = objs.rows.map(obj => JSON.stringify(this.dataset[obj.value._id || obj.id]));
|
|
294
351
|
handler.sendArray(responseId, res);
|
|
295
352
|
}
|
|
296
353
|
} else if (this.knownScripts[data[0]].func && data.length > 4) {
|
|
297
|
-
const scriptFunc = {map: this.knownScripts[data[0]].func.replace('%1', data[5])};
|
|
354
|
+
const scriptFunc = { map: this.knownScripts[data[0]].func.replace('%1', data[5]) };
|
|
298
355
|
if (this.settings.connection.enhancedLogging) {
|
|
299
356
|
this.log.silly(`${namespaceLog} Script transformed into _applyView: func=${scriptFunc.map}`);
|
|
300
357
|
}
|
|
@@ -306,16 +363,20 @@ class ObjectsInMemoryServer extends ObjectsInMemoryJsonlDB {
|
|
|
306
363
|
const res = objs.rows.map(obj => JSON.stringify(this.dataset[obj.value._id || obj.id]));
|
|
307
364
|
|
|
308
365
|
return void handler.sendArray(responseId, res);
|
|
366
|
+
} else if (this.knownScripts[data[0]].redlock) {
|
|
367
|
+
// just return a dummy
|
|
368
|
+
return void handler.sendArray(responseId, [0]);
|
|
309
369
|
} else {
|
|
310
|
-
handler.sendError(responseId, new Error(
|
|
370
|
+
handler.sendError(responseId, new Error(`Unknown LUA script eval call ${JSON.stringify(data)}`));
|
|
311
371
|
}
|
|
312
372
|
});
|
|
313
373
|
|
|
314
374
|
// Handle Redis "PUBLISH" request
|
|
315
375
|
handler.on('publish', (data, responseId) => {
|
|
316
|
-
const {id, namespace} = this._normalizeId(data[0]);
|
|
376
|
+
const { id, namespace } = this._normalizeId(data[0]);
|
|
317
377
|
|
|
318
|
-
if (namespace === this.namespaceObj
|
|
378
|
+
if (namespace === this.namespaceObj || namespace === this.metaNamespace) {
|
|
379
|
+
// a "set" always comes afterwards, so do not publish
|
|
319
380
|
return void handler.sendInteger(responseId, 0); // do not publish for now
|
|
320
381
|
}
|
|
321
382
|
const publishCount = this.publishAll(namespace.substr(0, namespace.length - 1), id, JSON.parse(data[1]));
|
|
@@ -327,15 +388,17 @@ class ObjectsInMemoryServer extends ObjectsInMemoryJsonlDB {
|
|
|
327
388
|
if (!data || !data.length) {
|
|
328
389
|
return void handler.sendArray(responseId, []);
|
|
329
390
|
}
|
|
330
|
-
const {namespace, isMeta} = this._normalizeId(data[0]);
|
|
391
|
+
const { namespace, isMeta } = this._normalizeId(data[0]);
|
|
331
392
|
|
|
332
393
|
if (namespace === this.namespaceObj) {
|
|
333
394
|
const keys = [];
|
|
334
395
|
data.forEach(dataId => {
|
|
335
|
-
const {id, namespace} = this._normalizeId(dataId);
|
|
396
|
+
const { id, namespace } = this._normalizeId(dataId);
|
|
336
397
|
if (namespace !== this.namespaceObj) {
|
|
337
398
|
keys.push(null);
|
|
338
|
-
this.log.warn(
|
|
399
|
+
this.log.warn(
|
|
400
|
+
`${namespaceLog} Got MGET request for non Object-ID in Objects-ID chunk for ${namespace} / ${dataId}`
|
|
401
|
+
);
|
|
339
402
|
return;
|
|
340
403
|
}
|
|
341
404
|
keys.push(id);
|
|
@@ -346,17 +409,19 @@ class ObjectsInMemoryServer extends ObjectsInMemoryJsonlDB {
|
|
|
346
409
|
} catch (err) {
|
|
347
410
|
return void handler.sendError(responseId, new Error('ERROR _getObjects: ' + err.message));
|
|
348
411
|
}
|
|
349
|
-
result = result.map(el => el ? JSON.stringify(el) : null);
|
|
412
|
+
result = result.map(el => (el ? JSON.stringify(el) : null));
|
|
350
413
|
handler.sendArray(responseId, result);
|
|
351
414
|
} else if (namespace === this.namespaceFile) {
|
|
352
415
|
// Handle request for Meta data for files
|
|
353
416
|
if (isMeta) {
|
|
354
417
|
const response = [];
|
|
355
418
|
data.forEach(dataId => {
|
|
356
|
-
const {id, namespace, name} = this._normalizeId(dataId);
|
|
419
|
+
const { id, namespace, name } = this._normalizeId(dataId);
|
|
357
420
|
if (namespace !== this.namespaceFile) {
|
|
358
421
|
response.push(null);
|
|
359
|
-
this.log.warn(
|
|
422
|
+
this.log.warn(
|
|
423
|
+
`${namespaceLog} Got MGET request for non File ID in File-ID chunk for ${dataId}`
|
|
424
|
+
);
|
|
360
425
|
return;
|
|
361
426
|
}
|
|
362
427
|
this._loadFileSettings(id);
|
|
@@ -370,7 +435,9 @@ class ObjectsInMemoryServer extends ObjectsInMemoryJsonlDB {
|
|
|
370
435
|
obj.stats = fs.statSync(path.join(this.objectsDir, id, name));
|
|
371
436
|
} catch (err) {
|
|
372
437
|
if (!name.endsWith('/_data.json')) {
|
|
373
|
-
this.log.warn(
|
|
438
|
+
this.log.warn(
|
|
439
|
+
`${namespaceLog} Got MGET request for non existing file ${dataId}, err: ${err.message}`
|
|
440
|
+
);
|
|
374
441
|
}
|
|
375
442
|
response.push(null);
|
|
376
443
|
return;
|
|
@@ -383,13 +450,16 @@ class ObjectsInMemoryServer extends ObjectsInMemoryJsonlDB {
|
|
|
383
450
|
handler.sendError(responseId, new Error('MGET-UNSUPPORTED for file data'));
|
|
384
451
|
}
|
|
385
452
|
} else {
|
|
386
|
-
handler.sendError(
|
|
453
|
+
handler.sendError(
|
|
454
|
+
responseId,
|
|
455
|
+
new Error('MGET-UNSUPPORTED for namespace ' + namespace + ': Data=' + JSON.stringify(data))
|
|
456
|
+
);
|
|
387
457
|
}
|
|
388
458
|
});
|
|
389
459
|
|
|
390
460
|
// Handle Redis "GET" requests
|
|
391
461
|
handler.on('get', (data, responseId) => {
|
|
392
|
-
const {id, namespace, name, isMeta} = this._normalizeId(data[0]);
|
|
462
|
+
const { id, namespace, name, isMeta } = this._normalizeId(data[0]);
|
|
393
463
|
|
|
394
464
|
if (namespace === this.namespaceObj) {
|
|
395
465
|
const result = this._getObject(id);
|
|
@@ -408,11 +478,14 @@ class ObjectsInMemoryServer extends ObjectsInMemoryJsonlDB {
|
|
|
408
478
|
return void handler.sendNull(responseId);
|
|
409
479
|
}
|
|
410
480
|
if (stats.isDirectory()) {
|
|
411
|
-
return void handler.sendBulk(
|
|
412
|
-
|
|
413
|
-
|
|
414
|
-
|
|
415
|
-
|
|
481
|
+
return void handler.sendBulk(
|
|
482
|
+
responseId,
|
|
483
|
+
JSON.stringify({
|
|
484
|
+
file: name,
|
|
485
|
+
stats: {},
|
|
486
|
+
isDir: true
|
|
487
|
+
})
|
|
488
|
+
);
|
|
416
489
|
}
|
|
417
490
|
this._loadFileSettings(id);
|
|
418
491
|
if (!this.fileOptions[id] || !this.fileOptions[id][name]) {
|
|
@@ -424,9 +497,16 @@ class ObjectsInMemoryServer extends ObjectsInMemoryJsonlDB {
|
|
|
424
497
|
obj = {
|
|
425
498
|
mimeType: obj,
|
|
426
499
|
acl: {
|
|
427
|
-
owner:
|
|
428
|
-
|
|
429
|
-
|
|
500
|
+
owner:
|
|
501
|
+
(this.defaultNewAcl && this.defaultNewAcl.owner) || utils.CONSTS.SYSTEM_ADMIN_USER,
|
|
502
|
+
ownerGroup:
|
|
503
|
+
(this.defaultNewAcl && this.defaultNewAcl.ownerGroup) ||
|
|
504
|
+
utils.CONSTS.SYSTEM_ADMIN_GROUP,
|
|
505
|
+
permissions:
|
|
506
|
+
(this.defaultNewAcl && this.defaultNewAcl.file.permissions) ||
|
|
507
|
+
utils.CONSTS.ACCESS_USER_ALL |
|
|
508
|
+
utils.CONSTS.ACCESS_GROUP_ALL |
|
|
509
|
+
utils.CONSTS.ACCESS_EVERY_ALL // 777
|
|
430
510
|
}
|
|
431
511
|
};
|
|
432
512
|
}
|
|
@@ -447,18 +527,36 @@ class ObjectsInMemoryServer extends ObjectsInMemoryJsonlDB {
|
|
|
447
527
|
if (!Buffer.isBuffer(fileData) && tools.isObject(fileData)) {
|
|
448
528
|
// if its an invalid object, stringify it and log warning
|
|
449
529
|
fileData = JSON.stringify(fileData);
|
|
450
|
-
this.log.warn(
|
|
530
|
+
this.log.warn(
|
|
531
|
+
`${namespaceLog} Data of "${id}/${name}" has invalid structure at file data request: ${fileData}`
|
|
532
|
+
);
|
|
451
533
|
}
|
|
452
534
|
handler.sendBufBulk(responseId, Buffer.from(fileData));
|
|
453
535
|
}
|
|
536
|
+
} else if (namespace === this.metaNamespace) {
|
|
537
|
+
// special handling for the primaryHost
|
|
538
|
+
if (id === 'objects.primaryHost') {
|
|
539
|
+
// we are the server -> we are primary
|
|
540
|
+
handler.sendString(this.settings.hostname);
|
|
541
|
+
} else {
|
|
542
|
+
const result = this.getMeta(id);
|
|
543
|
+
if (result === undefined || result === null) {
|
|
544
|
+
handler.sendNull(responseId);
|
|
545
|
+
} else {
|
|
546
|
+
handler.sendBulk(responseId, result);
|
|
547
|
+
}
|
|
548
|
+
}
|
|
454
549
|
} else {
|
|
455
|
-
handler.sendError(
|
|
550
|
+
handler.sendError(
|
|
551
|
+
responseId,
|
|
552
|
+
new Error('GET-UNSUPPORTED for namespace ' + namespace + ': Data=' + JSON.stringify(data))
|
|
553
|
+
);
|
|
456
554
|
}
|
|
457
555
|
});
|
|
458
556
|
|
|
459
557
|
// Handle Redis "SET" requests
|
|
460
558
|
handler.on('set', (data, responseId) => {
|
|
461
|
-
const {id, namespace, name, isMeta} = this._normalizeId(data[0]);
|
|
559
|
+
const { id, namespace, name, isMeta } = this._normalizeId(data[0]);
|
|
462
560
|
|
|
463
561
|
if (namespace === this.namespaceObj) {
|
|
464
562
|
try {
|
|
@@ -480,10 +578,16 @@ class ObjectsInMemoryServer extends ObjectsInMemoryJsonlDB {
|
|
|
480
578
|
// only set if the meta object is already/still existing
|
|
481
579
|
if (this.fileOptions[id]) {
|
|
482
580
|
this.fileOptions[id][name] = JSON.parse(data[1].toString('utf-8'));
|
|
483
|
-
fs.writeFileSync(
|
|
581
|
+
fs.writeFileSync(
|
|
582
|
+
path.join(this.objectsDir, id, '_data.json'),
|
|
583
|
+
JSON.stringify(this.fileOptions[id])
|
|
584
|
+
);
|
|
484
585
|
}
|
|
485
586
|
} catch (err) {
|
|
486
|
-
return void handler.sendError(
|
|
587
|
+
return void handler.sendError(
|
|
588
|
+
responseId,
|
|
589
|
+
new Error(`ERROR writeFile-Meta id=${id}: ${err.message}`)
|
|
590
|
+
);
|
|
487
591
|
}
|
|
488
592
|
handler.sendString(responseId, 'OK');
|
|
489
593
|
} else {
|
|
@@ -491,12 +595,21 @@ class ObjectsInMemoryServer extends ObjectsInMemoryJsonlDB {
|
|
|
491
595
|
try {
|
|
492
596
|
this._writeFile(id, name, data[1]);
|
|
493
597
|
} catch (err) {
|
|
494
|
-
return void handler.sendError(
|
|
598
|
+
return void handler.sendError(
|
|
599
|
+
responseId,
|
|
600
|
+
new Error(`ERROR writeFile id=${id}: ${err.message}`)
|
|
601
|
+
);
|
|
495
602
|
}
|
|
496
603
|
handler.sendString(responseId, 'OK');
|
|
497
604
|
}
|
|
605
|
+
} else if (namespace === this.metaNamespace) {
|
|
606
|
+
this.setMeta(id, data[1].toString('utf-8'));
|
|
607
|
+
handler.sendString(responseId, 'OK');
|
|
498
608
|
} else {
|
|
499
|
-
handler.sendError(
|
|
609
|
+
handler.sendError(
|
|
610
|
+
responseId,
|
|
611
|
+
new Error(`SET-UNSUPPORTED for namespace ${namespace}: Data=${JSON.stringify(data)}`)
|
|
612
|
+
);
|
|
500
613
|
}
|
|
501
614
|
});
|
|
502
615
|
|
|
@@ -507,7 +620,10 @@ class ObjectsInMemoryServer extends ObjectsInMemoryJsonlDB {
|
|
|
507
620
|
|
|
508
621
|
if (oldDetails.namespace === this.namespaceFile) {
|
|
509
622
|
if (oldDetails.id !== newDetails.id) {
|
|
510
|
-
return void handler.sendError(
|
|
623
|
+
return void handler.sendError(
|
|
624
|
+
responseId,
|
|
625
|
+
new Error('ERROR renameObject: id needs to stay the same')
|
|
626
|
+
);
|
|
511
627
|
}
|
|
512
628
|
|
|
513
629
|
// Handle request for Meta data for files
|
|
@@ -523,13 +639,16 @@ class ObjectsInMemoryServer extends ObjectsInMemoryJsonlDB {
|
|
|
523
639
|
handler.sendString(responseId, 'OK');
|
|
524
640
|
}
|
|
525
641
|
} else {
|
|
526
|
-
handler.sendError(
|
|
642
|
+
handler.sendError(
|
|
643
|
+
responseId,
|
|
644
|
+
new Error(`RENAME-UNSUPPORTED for namespace ${oldDetails.namespace}: Data=${JSON.stringify(data)}`)
|
|
645
|
+
);
|
|
527
646
|
}
|
|
528
647
|
});
|
|
529
648
|
|
|
530
649
|
// Handle Redis "DEL" request for state and session namespace
|
|
531
650
|
handler.on('del', (data, responseId) => {
|
|
532
|
-
const {id, namespace, name, isMeta} = this._normalizeId(data[0]);
|
|
651
|
+
const { id, namespace, name, isMeta } = this._normalizeId(data[0]);
|
|
533
652
|
|
|
534
653
|
if (namespace === this.namespaceObj) {
|
|
535
654
|
try {
|
|
@@ -553,7 +672,10 @@ class ObjectsInMemoryServer extends ObjectsInMemoryJsonlDB {
|
|
|
553
672
|
handler.sendString(responseId, 'OK');
|
|
554
673
|
}
|
|
555
674
|
} else {
|
|
556
|
-
handler.sendError(
|
|
675
|
+
handler.sendError(
|
|
676
|
+
responseId,
|
|
677
|
+
new Error(`DEL-UNSUPPORTED for namespace ${namespace}: Data=${JSON.stringify(data)}`)
|
|
678
|
+
);
|
|
557
679
|
}
|
|
558
680
|
});
|
|
559
681
|
|
|
@@ -563,7 +685,7 @@ class ObjectsInMemoryServer extends ObjectsInMemoryJsonlDB {
|
|
|
563
685
|
}
|
|
564
686
|
|
|
565
687
|
// Note: we only simulate single key existence check
|
|
566
|
-
const {id, namespace, name} = this._normalizeId(data[0]);
|
|
688
|
+
const { id, namespace, name } = this._normalizeId(data[0]);
|
|
567
689
|
|
|
568
690
|
if (namespace === this.namespaceObj) {
|
|
569
691
|
let exists;
|
|
@@ -581,6 +703,9 @@ class ObjectsInMemoryServer extends ObjectsInMemoryJsonlDB {
|
|
|
581
703
|
return void handler.sendError(responseId, e);
|
|
582
704
|
}
|
|
583
705
|
handler.sendInteger(responseId, exists ? 1 : 0);
|
|
706
|
+
} else if (namespace === this.namespaceSet) {
|
|
707
|
+
// we are not using sets in simulator, so just say it exists
|
|
708
|
+
return void handler.sendInteger(responseId, 1);
|
|
584
709
|
} else {
|
|
585
710
|
handler.sendError(responseId, new Error(`EXISTS-UNSUPPORTED for namespace ${namespace}`));
|
|
586
711
|
}
|
|
@@ -604,33 +729,73 @@ class ObjectsInMemoryServer extends ObjectsInMemoryJsonlDB {
|
|
|
604
729
|
return this._handleScanOrKeys(handler, data[0], responseId);
|
|
605
730
|
});
|
|
606
731
|
|
|
732
|
+
// commands for redis SETS, just dummies
|
|
733
|
+
handler.on('sadd', (data, responseId) => {
|
|
734
|
+
return void handler.sendInteger(responseId, 1);
|
|
735
|
+
});
|
|
736
|
+
|
|
737
|
+
handler.on('srem', (data, responseId) => {
|
|
738
|
+
return void handler.sendInteger(responseId, 1);
|
|
739
|
+
});
|
|
740
|
+
|
|
741
|
+
handler.on('sscan', (data, responseId) => {
|
|
742
|
+
// for file DB it does the same as scan but data looks different
|
|
743
|
+
if (!data || data.length < 4) {
|
|
744
|
+
return void handler.sendArray(responseId, ['0', []]);
|
|
745
|
+
}
|
|
746
|
+
|
|
747
|
+
return this._handleScanOrKeys(handler, data[3], responseId, true);
|
|
748
|
+
});
|
|
749
|
+
|
|
607
750
|
// Handle Redis "PSUBSCRIBE" request for state, log and session namespace
|
|
608
751
|
handler.on('psubscribe', (data, responseId) => {
|
|
609
|
-
const {id, namespace} = this._normalizeId(data[0]);
|
|
752
|
+
const { id, namespace } = this._normalizeId(data[0]);
|
|
610
753
|
|
|
611
754
|
if (namespace === this.namespaceObj) {
|
|
612
755
|
this._subscribeConfigForClient(handler, id);
|
|
613
756
|
handler.sendArray(responseId, ['psubscribe', data[0], 1]);
|
|
757
|
+
} else if (namespace === this.metaNamespace) {
|
|
758
|
+
this._subscribeMeta(handler, id);
|
|
759
|
+
handler.sendArray(responseId, ['psubscribe', data[0], 1]);
|
|
614
760
|
} else {
|
|
615
|
-
handler.sendError(
|
|
761
|
+
handler.sendError(
|
|
762
|
+
responseId,
|
|
763
|
+
new Error('PSUBSCRIBE-UNSUPPORTED for namespace ' + namespace + ': Data=' + JSON.stringify(data))
|
|
764
|
+
);
|
|
616
765
|
}
|
|
617
766
|
});
|
|
618
767
|
|
|
619
768
|
// Handle Redis "UNSUBSCRIBE" request for state, log and session namespace
|
|
620
769
|
handler.on('punsubscribe', (data, responseId) => {
|
|
621
|
-
const {id, namespace} = this._normalizeId(data[0]);
|
|
770
|
+
const { id, namespace } = this._normalizeId(data[0]);
|
|
622
771
|
|
|
623
772
|
if (namespace === this.namespaceObj) {
|
|
624
773
|
this._unsubscribeConfigForClient(handler, id);
|
|
625
774
|
handler.sendArray(responseId, ['punsubscribe', data[0], 1]);
|
|
626
775
|
} else {
|
|
627
|
-
handler.sendError(
|
|
776
|
+
handler.sendError(
|
|
777
|
+
responseId,
|
|
778
|
+
new Error('PUNSUBSCRIBE-UNSUPPORTED for namespace ' + namespace + ': Data=' + JSON.stringify(data))
|
|
779
|
+
);
|
|
780
|
+
}
|
|
781
|
+
});
|
|
782
|
+
|
|
783
|
+
// Handle Redis "SUBSCRIBE" ... currently mainly ignored
|
|
784
|
+
handler.on('subscribe', (data, responseId) => {
|
|
785
|
+
if (data[0].startsWith('__keyevent@')) {
|
|
786
|
+
// we ignore these type of events because we publish expires anyway directly
|
|
787
|
+
handler.sendArray(responseId, ['subscribe', data[0], 1]);
|
|
788
|
+
} else {
|
|
789
|
+
handler.sendError(responseId, new Error(`SUBSCRIBE-UNSUPPORTED for ${data[0]}`));
|
|
628
790
|
}
|
|
629
791
|
});
|
|
630
792
|
|
|
631
793
|
// Handle Redis "CONFIG" ... currently mainly ignored
|
|
632
794
|
handler.on('config', (data, responseId) => {
|
|
633
|
-
if (data[0] === 'set' && data[1] === '
|
|
795
|
+
if (data[0] === 'set' && data[1] === 'notify-keyspace-events') {
|
|
796
|
+
// we ignore these type of commands for now, should only be to subscribe to keyspace events
|
|
797
|
+
handler.sendString(responseId, 'OK');
|
|
798
|
+
} else if (data[0] === 'set' && data[1] === 'lua-time-limit') {
|
|
634
799
|
// we ignore these type of commands for now, irrelevant
|
|
635
800
|
handler.sendString(responseId, 'OK');
|
|
636
801
|
} else {
|
|
@@ -684,17 +849,19 @@ class ObjectsInMemoryServer extends ObjectsInMemoryJsonlDB {
|
|
|
684
849
|
delete this.serverConnections[s];
|
|
685
850
|
});
|
|
686
851
|
|
|
687
|
-
return /** @type {Promise<void>} */ (
|
|
688
|
-
|
|
689
|
-
|
|
690
|
-
|
|
691
|
-
|
|
692
|
-
|
|
693
|
-
|
|
694
|
-
|
|
695
|
-
|
|
696
|
-
|
|
697
|
-
|
|
852
|
+
return /** @type {Promise<void>} */ (
|
|
853
|
+
new Promise(resolve => {
|
|
854
|
+
if (!this.server) {
|
|
855
|
+
return void resolve();
|
|
856
|
+
}
|
|
857
|
+
try {
|
|
858
|
+
this.server.close(() => resolve());
|
|
859
|
+
} catch (e) {
|
|
860
|
+
console.log(e.message);
|
|
861
|
+
resolve();
|
|
862
|
+
}
|
|
863
|
+
})
|
|
864
|
+
);
|
|
698
865
|
}
|
|
699
866
|
}
|
|
700
867
|
|
|
@@ -708,13 +875,14 @@ class ObjectsInMemoryServer extends ObjectsInMemoryJsonlDB {
|
|
|
708
875
|
* @private
|
|
709
876
|
*/
|
|
710
877
|
_handleScanOrKeys(handler, pattern, responseId, isScan = false) {
|
|
711
|
-
const {id, namespace, name, isMeta} = this._normalizeId(pattern);
|
|
878
|
+
const { id, namespace, name, isMeta } = this._normalizeId(pattern);
|
|
712
879
|
|
|
713
880
|
let response = [];
|
|
714
881
|
if (namespace === this.namespaceObj || namespace === this.namespaceObjects) {
|
|
715
|
-
response =
|
|
882
|
+
response = this._getKeys(id).map(val => this.namespaceObj + val);
|
|
716
883
|
// if scan, we send the cursor as first argument
|
|
717
|
-
if (namespace !== this.namespaceObjects) {
|
|
884
|
+
if (namespace !== this.namespaceObjects) {
|
|
885
|
+
// When it was not the full DB namespace send out response
|
|
718
886
|
return void handler.sendArray(responseId, isScan ? ['0', response] : response);
|
|
719
887
|
}
|
|
720
888
|
}
|
|
@@ -725,13 +893,15 @@ class ObjectsInMemoryServer extends ObjectsInMemoryJsonlDB {
|
|
|
725
893
|
try {
|
|
726
894
|
res = this._readDir(id, name);
|
|
727
895
|
if (!res || !res.length) {
|
|
728
|
-
res = [
|
|
729
|
-
|
|
730
|
-
|
|
731
|
-
|
|
732
|
-
|
|
733
|
-
|
|
734
|
-
|
|
896
|
+
res = [
|
|
897
|
+
{
|
|
898
|
+
file: '_data.json',
|
|
899
|
+
stats: {},
|
|
900
|
+
isDir: false,
|
|
901
|
+
virtualFile: true,
|
|
902
|
+
notExists: true
|
|
903
|
+
}
|
|
904
|
+
];
|
|
735
905
|
}
|
|
736
906
|
} catch (err) {
|
|
737
907
|
if (!err.message.endsWith(utils.ERRORS.ERROR_NOT_FOUND)) {
|
|
@@ -758,11 +928,17 @@ class ObjectsInMemoryServer extends ObjectsInMemoryJsonlDB {
|
|
|
758
928
|
response.push(this.getFileId(entryId, baseName + arr.file, false));
|
|
759
929
|
});
|
|
760
930
|
handler.sendArray(responseId, isScan ? ['0', response] : response); // send out file or full db response
|
|
761
|
-
} else {
|
|
931
|
+
} else {
|
|
932
|
+
// such a request should never happen
|
|
762
933
|
handler.sendArray(responseId, isScan ? ['0', []] : []); // send out file or full db response
|
|
763
934
|
}
|
|
935
|
+
} else if (namespace === this.namespaceSet) {
|
|
936
|
+
handler.sendArray(responseId, isScan ? ['0', []] : []); // send out empty array, we have no sets
|
|
764
937
|
} else {
|
|
765
|
-
handler.sendError(
|
|
938
|
+
handler.sendError(
|
|
939
|
+
responseId,
|
|
940
|
+
new Error(`${isScan ? 'SCAN' : 'KEYS'}-UNSUPPORTED for namespace ${namespace}: Pattern=${pattern}`)
|
|
941
|
+
);
|
|
766
942
|
}
|
|
767
943
|
}
|
|
768
944
|
|
|
@@ -806,7 +982,12 @@ class ObjectsInMemoryServer extends ObjectsInMemoryJsonlDB {
|
|
|
806
982
|
try {
|
|
807
983
|
this.server = net.createServer();
|
|
808
984
|
this.server.on('error', err =>
|
|
809
|
-
this.log.info(
|
|
985
|
+
this.log.info(
|
|
986
|
+
`${this.namespace} ${settings.secure ? 'Secure ' : ''} Error inMem-objects listening on port ${
|
|
987
|
+
settings.port || 9001
|
|
988
|
+
}: ${err}`
|
|
989
|
+
)
|
|
990
|
+
);
|
|
810
991
|
this.server.on('connection', socket => this._initSocket(socket));
|
|
811
992
|
|
|
812
993
|
this.server.listen(
|
package/package.json
CHANGED
|
@@ -1,17 +1,16 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@iobroker/db-objects-jsonl",
|
|
3
|
-
"version": "4.0.
|
|
3
|
+
"version": "4.0.3",
|
|
4
4
|
"engines": {
|
|
5
5
|
"node": ">=12.0.0"
|
|
6
6
|
},
|
|
7
7
|
"dependencies": {
|
|
8
|
-
"@alcalzone/jsonl-db": "
|
|
9
|
-
"@iobroker/db-base": "4.0.
|
|
10
|
-
"@iobroker/db-objects-file": "4.0.
|
|
11
|
-
"@iobroker/db-objects-redis": "4.0.
|
|
8
|
+
"@alcalzone/jsonl-db": "~2.4.1",
|
|
9
|
+
"@iobroker/db-base": "4.0.3",
|
|
10
|
+
"@iobroker/db-objects-file": "4.0.3",
|
|
11
|
+
"@iobroker/db-objects-redis": "4.0.3",
|
|
12
12
|
"deep-clone": "^3.0.3",
|
|
13
|
-
"fs-extra": "^10.0.0"
|
|
14
|
-
"node.extend": "^2.0.2"
|
|
13
|
+
"fs-extra": "^10.0.0"
|
|
15
14
|
},
|
|
16
15
|
"keywords": [
|
|
17
16
|
"ioBroker",
|
|
@@ -38,5 +37,5 @@
|
|
|
38
37
|
"lib/",
|
|
39
38
|
"index.js"
|
|
40
39
|
],
|
|
41
|
-
"gitHead": "
|
|
40
|
+
"gitHead": "79a76a082db91399d0e432ef02bf2981a6739107"
|
|
42
41
|
}
|