@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
|
* 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,19 +13,18 @@
|
|
|
13
13
|
/* jshint -W061 */
|
|
14
14
|
'use strict';
|
|
15
15
|
|
|
16
|
-
const fs
|
|
17
|
-
const path
|
|
18
|
-
const InMemoryFileDB
|
|
19
|
-
const tools
|
|
20
|
-
const utils
|
|
21
|
-
const deepClone
|
|
16
|
+
const fs = require('fs-extra');
|
|
17
|
+
const path = require('path');
|
|
18
|
+
const InMemoryFileDB = require('@iobroker/db-base').inMemoryFileDB;
|
|
19
|
+
const tools = require('@iobroker/db-base').tools;
|
|
20
|
+
const utils = require('@iobroker/db-objects-redis').objectsUtils;
|
|
21
|
+
const deepClone = require('deep-clone');
|
|
22
22
|
|
|
23
23
|
/**
|
|
24
24
|
* This class inherits InMemoryFileDB class and adds all relevant logic for objects
|
|
25
25
|
* including the available methods for use by js-controller directly
|
|
26
26
|
**/
|
|
27
27
|
class ObjectsInMemoryFileDB extends InMemoryFileDB {
|
|
28
|
-
|
|
29
28
|
constructor(settings) {
|
|
30
29
|
settings = settings || {};
|
|
31
30
|
settings.fileDB = {
|
|
@@ -46,8 +45,10 @@ class ObjectsInMemoryFileDB extends InMemoryFileDB {
|
|
|
46
45
|
this.preserveSettings = ['custom'];
|
|
47
46
|
this.defaultNewAcl = this.settings.defaultNewAcl || null;
|
|
48
47
|
this.namespace = this.settings.namespace || this.settings.hostname || '';
|
|
49
|
-
this.writeFileInterval =
|
|
50
|
-
|
|
48
|
+
this.writeFileInterval =
|
|
49
|
+
this.settings.connection && typeof this.settings.connection.writeFileInterval === 'number'
|
|
50
|
+
? parseInt(this.settings.connection.writeFileInterval)
|
|
51
|
+
: 5000;
|
|
51
52
|
this.log.silly(`${this.namespace} Objects DB uses file write interval of ${this.writeFileInterval} ms`);
|
|
52
53
|
|
|
53
54
|
this.objectsDir = path.join(this.dataDir, 'files');
|
|
@@ -57,15 +58,18 @@ class ObjectsInMemoryFileDB extends InMemoryFileDB {
|
|
|
57
58
|
|
|
58
59
|
// Handle some < js-controller 2.0 broken objects and correct them
|
|
59
60
|
for (const key of Object.keys(this.dataset)) {
|
|
60
|
-
|
|
61
|
-
|
|
62
|
-
|
|
61
|
+
const obj = this.dataset[key];
|
|
62
|
+
if (tools.isObject(obj) && obj.acl && obj.acl.permissions && !obj.acl.object) {
|
|
63
|
+
obj.acl.object = obj.acl.permissions;
|
|
64
|
+
delete obj.acl.permissions;
|
|
65
|
+
this.dataset[key] = obj;
|
|
63
66
|
}
|
|
64
67
|
}
|
|
65
68
|
|
|
66
69
|
// init default new acl
|
|
67
|
-
|
|
68
|
-
|
|
70
|
+
const configObj = this.dataset['system.config'];
|
|
71
|
+
if (configObj && configObj.common && configObj.common.defaultNewAcl) {
|
|
72
|
+
this.defaultNewAcl = deepClone(configObj.common.defaultNewAcl);
|
|
69
73
|
}
|
|
70
74
|
}
|
|
71
75
|
|
|
@@ -175,17 +179,8 @@ class ObjectsInMemoryFileDB extends InMemoryFileDB {
|
|
|
175
179
|
|
|
176
180
|
const res = this._getObjectView('system', 'meta', null);
|
|
177
181
|
|
|
178
|
-
// collect
|
|
179
|
-
const
|
|
180
|
-
res.rows.forEach(obj => {
|
|
181
|
-
if (!obj || !obj.value || !obj.value.common || !obj.value.common.type) {
|
|
182
|
-
return;
|
|
183
|
-
}
|
|
184
|
-
if (limitId && obj.id !== limitId) {
|
|
185
|
-
return;
|
|
186
|
-
}
|
|
187
|
-
metaObjects[obj.id] = obj.value.common.type;
|
|
188
|
-
});
|
|
182
|
+
// collect meta ids to generate warning if non existing
|
|
183
|
+
const metaIds = res.rows.map(obj => obj.id).filter(id => !limitId || limitId === id);
|
|
189
184
|
|
|
190
185
|
if (!fs.existsSync(this.objectsDir)) {
|
|
191
186
|
return {
|
|
@@ -207,8 +202,10 @@ class ObjectsInMemoryFileDB extends InMemoryFileDB {
|
|
|
207
202
|
if (limitId && dir !== limitId) {
|
|
208
203
|
return;
|
|
209
204
|
}
|
|
210
|
-
if (!
|
|
211
|
-
resNotifies.push(
|
|
205
|
+
if (!metaIds.includes(dir)) {
|
|
206
|
+
resNotifies.push(
|
|
207
|
+
`Ignoring Directory "${dir}" because officially not created as meta object. Please remove directory!`
|
|
208
|
+
);
|
|
212
209
|
return;
|
|
213
210
|
}
|
|
214
211
|
this._loadFileSettings(dir);
|
|
@@ -219,21 +216,27 @@ class ObjectsInMemoryFileDB extends InMemoryFileDB {
|
|
|
219
216
|
return;
|
|
220
217
|
}
|
|
221
218
|
if (!this.fileOptions[dir][localFile]) {
|
|
222
|
-
const fileStat
|
|
223
|
-
const ext
|
|
224
|
-
const mime
|
|
225
|
-
const _mimeType
|
|
226
|
-
const isBinary
|
|
219
|
+
const fileStat = fs.statSync(file);
|
|
220
|
+
const ext = path.extname(localFile);
|
|
221
|
+
const mime = utils.getMimeType(ext);
|
|
222
|
+
const _mimeType = mime.mimeType;
|
|
223
|
+
const isBinary = mime.isBinary;
|
|
227
224
|
|
|
228
225
|
this.fileOptions[dir][localFile] = {
|
|
229
226
|
createdAt: fileStat.ctimeMs,
|
|
230
|
-
acl
|
|
231
|
-
owner:
|
|
232
|
-
ownerGroup:
|
|
233
|
-
|
|
227
|
+
acl: {
|
|
228
|
+
owner: (this.defaultNewAcl && this.defaultNewAcl.owner) || utils.CONSTS.SYSTEM_ADMIN_USER,
|
|
229
|
+
ownerGroup:
|
|
230
|
+
(this.defaultNewAcl && this.defaultNewAcl.ownerGroup) ||
|
|
231
|
+
utils.CONSTS.SYSTEM_ADMIN_GROUP,
|
|
232
|
+
permissions:
|
|
233
|
+
(this.defaultNewAcl && this.defaultNewAcl.file) ||
|
|
234
|
+
utils.CONSTS.ACCESS_USER_RW |
|
|
235
|
+
utils.CONSTS.ACCESS_GROUP_READ |
|
|
236
|
+
utils.CONSTS.ACCESS_EVERY_READ // 0x644
|
|
234
237
|
},
|
|
235
|
-
mimeType
|
|
236
|
-
binary
|
|
238
|
+
mimeType: _mimeType,
|
|
239
|
+
binary: isBinary,
|
|
237
240
|
modifiedAt: fileStat.mtimeMs
|
|
238
241
|
};
|
|
239
242
|
dirSynced++;
|
|
@@ -252,7 +255,7 @@ class ObjectsInMemoryFileDB extends InMemoryFileDB {
|
|
|
252
255
|
// needed by server
|
|
253
256
|
_writeFile(id, name, data, options) {
|
|
254
257
|
if (typeof options === 'string') {
|
|
255
|
-
options = {mimeType: options};
|
|
258
|
+
options = { mimeType: options };
|
|
256
259
|
}
|
|
257
260
|
if (options && options.acl) {
|
|
258
261
|
options.acl = null;
|
|
@@ -276,33 +279,49 @@ class ObjectsInMemoryFileDB extends InMemoryFileDB {
|
|
|
276
279
|
fs.mkdirSync(path.join(this.objectsDir, id));
|
|
277
280
|
}
|
|
278
281
|
} catch (e) {
|
|
279
|
-
this.log.error(
|
|
280
|
-
|
|
282
|
+
this.log.error(
|
|
283
|
+
`${this.namespace} Cannot create directories: ${path.join(this.objectsDir, id)}: ${e.message}`
|
|
284
|
+
);
|
|
285
|
+
this.log.error(
|
|
286
|
+
`${this.namespace} Check the permissions! Or run installation fixer or "iobroker fix" command!`
|
|
287
|
+
);
|
|
281
288
|
throw e;
|
|
282
289
|
}
|
|
283
290
|
|
|
284
|
-
const ext
|
|
285
|
-
const mime
|
|
286
|
-
const _mimeType
|
|
287
|
-
const isBinary
|
|
288
|
-
|
|
289
|
-
this.fileOptions[id][name]
|
|
290
|
-
this.fileOptions[id][name].acl
|
|
291
|
-
owner:
|
|
292
|
-
ownerGroup:
|
|
293
|
-
|
|
291
|
+
const ext = path.extname(name);
|
|
292
|
+
const mime = utils.getMimeType(ext);
|
|
293
|
+
const _mimeType = mime.mimeType;
|
|
294
|
+
const isBinary = mime.isBinary;
|
|
295
|
+
|
|
296
|
+
this.fileOptions[id][name] = this.fileOptions[id][name] || { createdAt: Date.now() };
|
|
297
|
+
this.fileOptions[id][name].acl = this.fileOptions[id][name].acl || {
|
|
298
|
+
owner: options.user || (this.defaultNewAcl && this.defaultNewAcl.owner) || utils.CONSTS.SYSTEM_ADMIN_USER,
|
|
299
|
+
ownerGroup:
|
|
300
|
+
options.group ||
|
|
301
|
+
(this.defaultNewAcl && this.defaultNewAcl.ownerGroup) ||
|
|
302
|
+
utils.CONSTS.SYSTEM_ADMIN_GROUP,
|
|
303
|
+
permissions:
|
|
304
|
+
options.mode ||
|
|
305
|
+
(this.defaultNewAcl && this.defaultNewAcl.file) ||
|
|
306
|
+
utils.CONSTS.ACCESS_USER_RW | utils.CONSTS.ACCESS_GROUP_READ | utils.CONSTS.ACCESS_EVERY_READ // 0x644
|
|
294
307
|
};
|
|
295
308
|
|
|
296
|
-
this.fileOptions[id][name].mimeType
|
|
297
|
-
this.fileOptions[id][name].binary
|
|
298
|
-
this.fileOptions[id][name].acl.ownerGroup =
|
|
299
|
-
|
|
309
|
+
this.fileOptions[id][name].mimeType = options.mimeType || _mimeType;
|
|
310
|
+
this.fileOptions[id][name].binary = isBinary;
|
|
311
|
+
this.fileOptions[id][name].acl.ownerGroup =
|
|
312
|
+
this.fileOptions[id][name].acl.ownerGroup ||
|
|
313
|
+
(this.defaultNewAcl && this.defaultNewAcl.ownerGroup) ||
|
|
314
|
+
utils.CONSTS.SYSTEM_ADMIN_GROUP;
|
|
315
|
+
this.fileOptions[id][name].modifiedAt = Date.now();
|
|
300
316
|
|
|
301
317
|
try {
|
|
302
318
|
// Create directories if complex structure
|
|
303
319
|
fs.ensureDirSync(path.join(this.objectsDir, id, path.dirname(name)));
|
|
304
320
|
// Store file
|
|
305
|
-
fs.writeFileSync(path.join(this.objectsDir, id, name), data, {
|
|
321
|
+
fs.writeFileSync(path.join(this.objectsDir, id, name), data, {
|
|
322
|
+
flag: 'w',
|
|
323
|
+
encoding: isBinary ? 'binary' : 'utf8'
|
|
324
|
+
});
|
|
306
325
|
|
|
307
326
|
if (isBinary) {
|
|
308
327
|
// Reload by read
|
|
@@ -314,7 +333,9 @@ class ObjectsInMemoryFileDB extends InMemoryFileDB {
|
|
|
314
333
|
// Store dir description
|
|
315
334
|
this._saveFileSettings(id);
|
|
316
335
|
} catch (e) {
|
|
317
|
-
this.log.error(
|
|
336
|
+
this.log.error(
|
|
337
|
+
`${this.namespace} Cannot write files: ${path.join(this.objectsDir, id, name)}: ${e.message}`
|
|
338
|
+
);
|
|
318
339
|
throw e;
|
|
319
340
|
}
|
|
320
341
|
}
|
|
@@ -341,19 +362,31 @@ class ObjectsInMemoryFileDB extends InMemoryFileDB {
|
|
|
341
362
|
// Create description object if not exists
|
|
342
363
|
this.fileOptions[id][name] = this.fileOptions[id][name] || {
|
|
343
364
|
acl: {
|
|
344
|
-
owner:
|
|
345
|
-
ownerGroup:
|
|
346
|
-
|
|
347
|
-
|
|
365
|
+
owner: (this.defaultNewAcl && this.defaultNewAcl.owner) || utils.CONSTS.SYSTEM_ADMIN_USER,
|
|
366
|
+
ownerGroup:
|
|
367
|
+
(this.defaultNewAcl && this.defaultNewAcl.ownerGroup) ||
|
|
368
|
+
utils.CONSTS.SYSTEM_ADMIN_GROUP,
|
|
369
|
+
permissions:
|
|
370
|
+
(this.defaultNewAcl && this.defaultNewAcl.file.permissions) ||
|
|
371
|
+
utils.CONSTS.ACCESS_USER_ALL |
|
|
372
|
+
utils.CONSTS.ACCESS_GROUP_ALL |
|
|
373
|
+
utils.CONSTS.ACCESS_EVERY_ALL // 777
|
|
348
374
|
}
|
|
349
375
|
};
|
|
350
376
|
if (typeof this.fileOptions[id][name] !== 'object') {
|
|
351
377
|
this.fileOptions[id][name] = {
|
|
352
|
-
mimeType:
|
|
378
|
+
mimeType: this.fileOptions[id][name],
|
|
353
379
|
acl: {
|
|
354
|
-
owner:
|
|
355
|
-
|
|
356
|
-
|
|
380
|
+
owner:
|
|
381
|
+
(this.defaultNewAcl && this.defaultNewAcl.owner) || utils.CONSTS.SYSTEM_ADMIN_USER,
|
|
382
|
+
ownerGroup:
|
|
383
|
+
(this.defaultNewAcl && this.defaultNewAcl.ownerGroup) ||
|
|
384
|
+
utils.CONSTS.SYSTEM_ADMIN_GROUP,
|
|
385
|
+
permissions:
|
|
386
|
+
(this.defaultNewAcl && this.defaultNewAcl.file.permissions) ||
|
|
387
|
+
utils.CONSTS.ACCESS_USER_ALL |
|
|
388
|
+
utils.CONSTS.ACCESS_GROUP_ALL |
|
|
389
|
+
utils.CONSTS.ACCESS_EVERY_ALL // 777
|
|
357
390
|
}
|
|
358
391
|
};
|
|
359
392
|
}
|
|
@@ -362,7 +395,7 @@ class ObjectsInMemoryFileDB extends InMemoryFileDB {
|
|
|
362
395
|
if (this.fileOptions[id][name].binary === undefined) {
|
|
363
396
|
const ext = path.extname(name);
|
|
364
397
|
const mimeType = utils.getMimeType(ext);
|
|
365
|
-
this.fileOptions[id][name].binary
|
|
398
|
+
this.fileOptions[id][name].binary = mimeType.isBinary;
|
|
366
399
|
this.fileOptions[id][name].mimeType = mimeType.mimeType;
|
|
367
400
|
}
|
|
368
401
|
|
|
@@ -375,7 +408,7 @@ class ObjectsInMemoryFileDB extends InMemoryFileDB {
|
|
|
375
408
|
if (this.fileOptions[id][name] !== undefined) {
|
|
376
409
|
delete this.fileOptions[id][name];
|
|
377
410
|
}
|
|
378
|
-
if (this.files[id][name]
|
|
411
|
+
if (this.files[id][name] !== undefined) {
|
|
379
412
|
delete this.files[id][name];
|
|
380
413
|
}
|
|
381
414
|
}
|
|
@@ -384,9 +417,12 @@ class ObjectsInMemoryFileDB extends InMemoryFileDB {
|
|
|
384
417
|
if (this.fileOptions[id][name] && !this.fileOptions[id][name].acl) {
|
|
385
418
|
// all files belongs to admin by default, but everyone can edit it
|
|
386
419
|
this.fileOptions[id][name].acl = {
|
|
387
|
-
owner:
|
|
388
|
-
ownerGroup:
|
|
389
|
-
|
|
420
|
+
owner: (this.defaultNewAcl && this.defaultNewAcl.owner) || utils.CONSTS.SYSTEM_ADMIN_USER,
|
|
421
|
+
ownerGroup:
|
|
422
|
+
(this.defaultNewAcl && this.defaultNewAcl.ownerGroup) || utils.CONSTS.SYSTEM_ADMIN_GROUP,
|
|
423
|
+
permissions:
|
|
424
|
+
(this.defaultNewAcl && this.defaultNewAcl.file.permissions) ||
|
|
425
|
+
utils.CONSTS.ACCESS_USER_ALL | utils.CONSTS.ACCESS_GROUP_ALL | utils.CONSTS.ACCESS_EVERY_RW // 776
|
|
390
426
|
};
|
|
391
427
|
}
|
|
392
428
|
|
|
@@ -487,7 +523,7 @@ class ObjectsInMemoryFileDB extends InMemoryFileDB {
|
|
|
487
523
|
// needed by server
|
|
488
524
|
_unlink(id, name) {
|
|
489
525
|
const _path = utils.sanitizePath(id, name);
|
|
490
|
-
id
|
|
526
|
+
id = _path.id;
|
|
491
527
|
name = _path.name;
|
|
492
528
|
|
|
493
529
|
this._loadFileSettings(id);
|
|
@@ -514,7 +550,6 @@ class ObjectsInMemoryFileDB extends InMemoryFileDB {
|
|
|
514
550
|
if (this.files[id] && this.files[id]) {
|
|
515
551
|
delete this.files[id];
|
|
516
552
|
}
|
|
517
|
-
|
|
518
553
|
} else {
|
|
519
554
|
this.log.debug('Delete file ' + path.join(id, name));
|
|
520
555
|
try {
|
|
@@ -563,7 +598,7 @@ class ObjectsInMemoryFileDB extends InMemoryFileDB {
|
|
|
563
598
|
|
|
564
599
|
this._loadFileSettings(id);
|
|
565
600
|
|
|
566
|
-
const len =
|
|
601
|
+
const len = name ? name.length : 0;
|
|
567
602
|
for (const f of Object.keys(this.fileOptions[id])) {
|
|
568
603
|
if (!name || f.substring(0, len) === name) {
|
|
569
604
|
/** @type {string|string[]} */
|
|
@@ -599,28 +634,50 @@ class ObjectsInMemoryFileDB extends InMemoryFileDB {
|
|
|
599
634
|
if (fs.existsSync(path.join(location, _files[j]))) {
|
|
600
635
|
try {
|
|
601
636
|
const stats = fs.statSync(path.join(location, _files[j]));
|
|
602
|
-
const acl =
|
|
603
|
-
|
|
604
|
-
|
|
605
|
-
|
|
606
|
-
|
|
607
|
-
|
|
608
|
-
|
|
609
|
-
|
|
610
|
-
|
|
637
|
+
const acl =
|
|
638
|
+
this.fileOptions[id][name + _files[j]] && this.fileOptions[id][name + _files[j]].acl
|
|
639
|
+
? JSON.parse(JSON.stringify(this.fileOptions[id][name + _files[j]].acl)) // copy settings
|
|
640
|
+
: {
|
|
641
|
+
read: true,
|
|
642
|
+
write: true,
|
|
643
|
+
owner:
|
|
644
|
+
(this.defaultNewAcl && this.defaultNewAcl.owner) ||
|
|
645
|
+
utils.CONSTS.SYSTEM_ADMIN_USER,
|
|
646
|
+
ownerGroup:
|
|
647
|
+
(this.defaultNewAcl && this.defaultNewAcl.ownerGroup) ||
|
|
648
|
+
utils.CONSTS.SYSTEM_ADMIN_GROUP,
|
|
649
|
+
permissions:
|
|
650
|
+
(this.defaultNewAcl && this.defaultNewAcl.file.permissions) ||
|
|
651
|
+
utils.CONSTS.ACCESS_USER_RW |
|
|
652
|
+
utils.CONSTS.ACCESS_GROUP_READ |
|
|
653
|
+
utils.CONSTS.ACCESS_EVERY_READ
|
|
654
|
+
};
|
|
611
655
|
|
|
612
656
|
// if filter for user
|
|
613
657
|
if (options.filter && acl) {
|
|
614
658
|
// If user may not write
|
|
615
|
-
if (!options.acl.file.write) {
|
|
616
|
-
|
|
659
|
+
if (!options.acl.file.write) {
|
|
660
|
+
// write
|
|
661
|
+
acl.permissions &= ~(
|
|
662
|
+
utils.CONSTS.ACCESS_USER_WRITE |
|
|
663
|
+
utils.CONSTS.ACCESS_GROUP_WRITE |
|
|
664
|
+
utils.CONSTS.ACCESS_EVERY_WRITE
|
|
665
|
+
);
|
|
617
666
|
}
|
|
618
667
|
// If user may not read
|
|
619
|
-
if (!options.acl.file.read) {
|
|
620
|
-
|
|
668
|
+
if (!options.acl.file.read) {
|
|
669
|
+
// read
|
|
670
|
+
acl.permissions &= ~(
|
|
671
|
+
utils.CONSTS.ACCESS_USER_READ |
|
|
672
|
+
utils.CONSTS.ACCESS_GROUP_READ |
|
|
673
|
+
utils.CONSTS.ACCESS_EVERY_READ
|
|
674
|
+
);
|
|
621
675
|
}
|
|
622
676
|
|
|
623
|
-
if (
|
|
677
|
+
if (
|
|
678
|
+
options.user !== utils.CONSTS.SYSTEM_ADMIN_USER &&
|
|
679
|
+
options.groups.includes(utils.CONSTS.SYSTEM_ADMIN_GROUP)
|
|
680
|
+
) {
|
|
624
681
|
if (acl.owner !== options.user) {
|
|
625
682
|
// Check if the user is in the group
|
|
626
683
|
if (options.groups.includes(acl.ownerGroup)) {
|
|
@@ -628,14 +685,14 @@ class ObjectsInMemoryFileDB extends InMemoryFileDB {
|
|
|
628
685
|
if (!(acl.permissions & utils.CONSTS.ACCESS_GROUP_RW)) {
|
|
629
686
|
continue;
|
|
630
687
|
}
|
|
631
|
-
acl.read
|
|
688
|
+
acl.read = !!(acl.permissions & utils.CONSTS.ACCESS_GROUP_READ);
|
|
632
689
|
acl.write = !!(acl.permissions & utils.CONSTS.ACCESS_GROUP_WRITE);
|
|
633
690
|
} else {
|
|
634
691
|
// everybody
|
|
635
692
|
if (!(acl.permissions & utils.CONSTS.ACCESS_EVERY_RW)) {
|
|
636
693
|
continue;
|
|
637
694
|
}
|
|
638
|
-
acl.read
|
|
695
|
+
acl.read = !!(acl.permissions & utils.CONSTS.ACCESS_EVERY_READ);
|
|
639
696
|
acl.write = !!(acl.permissions & utils.CONSTS.ACCESS_EVERY_WRITE);
|
|
640
697
|
}
|
|
641
698
|
} else {
|
|
@@ -643,26 +700,36 @@ class ObjectsInMemoryFileDB extends InMemoryFileDB {
|
|
|
643
700
|
if (!(acl.permissions & utils.CONSTS.ACCESS_USER_RW)) {
|
|
644
701
|
continue;
|
|
645
702
|
}
|
|
646
|
-
acl.read
|
|
703
|
+
acl.read = !!(acl.permissions & utils.CONSTS.ACCESS_USER_READ);
|
|
647
704
|
acl.write = !!(acl.permissions & utils.CONSTS.ACCESS_USER_WRITE);
|
|
648
705
|
}
|
|
649
706
|
} else {
|
|
650
|
-
acl.read
|
|
707
|
+
acl.read = true;
|
|
651
708
|
acl.write = true;
|
|
652
709
|
}
|
|
653
710
|
}
|
|
654
711
|
|
|
655
712
|
res.push({
|
|
656
|
-
file:
|
|
657
|
-
stats:
|
|
658
|
-
isDir:
|
|
659
|
-
acl:
|
|
660
|
-
modifiedAt: this.fileOptions[id][name + _files[j]]
|
|
661
|
-
|
|
713
|
+
file: _files[j],
|
|
714
|
+
stats: stats,
|
|
715
|
+
isDir: stats.isDirectory(),
|
|
716
|
+
acl: acl,
|
|
717
|
+
modifiedAt: this.fileOptions[id][name + _files[j]]
|
|
718
|
+
? this.fileOptions[id][name + _files[j]].modifiedAt
|
|
719
|
+
: undefined,
|
|
720
|
+
createdAt: this.fileOptions[id][name + _files[j]]
|
|
721
|
+
? this.fileOptions[id][name + _files[j]].createdAt
|
|
722
|
+
: undefined
|
|
662
723
|
});
|
|
663
|
-
|
|
664
724
|
} catch (e) {
|
|
665
|
-
this.log.error(
|
|
725
|
+
this.log.error(
|
|
726
|
+
`${this.namespace} Cannot read permissions of ${path.join(
|
|
727
|
+
this.objectsDir,
|
|
728
|
+
id,
|
|
729
|
+
name,
|
|
730
|
+
_files[j]
|
|
731
|
+
)}: ${e.message}`
|
|
732
|
+
);
|
|
666
733
|
}
|
|
667
734
|
}
|
|
668
735
|
}
|
|
@@ -723,6 +790,9 @@ class ObjectsInMemoryFileDB extends InMemoryFileDB {
|
|
|
723
790
|
this.handleSubscribe(client, 'objects', pattern);
|
|
724
791
|
}
|
|
725
792
|
|
|
793
|
+
_subscribeMeta(client, pattern) {
|
|
794
|
+
this.handleSubscribe(client, 'meta', pattern);
|
|
795
|
+
}
|
|
726
796
|
// needed by server
|
|
727
797
|
_unsubscribeConfigForClient(client, pattern) {
|
|
728
798
|
this.handleUnsubscribe(client, 'objects', pattern); // ignore options => unsubscribe may everyone
|
|
@@ -750,12 +820,55 @@ class ObjectsInMemoryFileDB extends InMemoryFileDB {
|
|
|
750
820
|
return keys.map(id => this.dataset[id]);
|
|
751
821
|
}
|
|
752
822
|
|
|
823
|
+
_ensureMetaDict() {
|
|
824
|
+
let meta = this.dataset['**META**'];
|
|
825
|
+
if (!meta) {
|
|
826
|
+
meta = {};
|
|
827
|
+
this.dataset['**META**'] = meta;
|
|
828
|
+
}
|
|
829
|
+
return meta;
|
|
830
|
+
}
|
|
831
|
+
|
|
832
|
+
/**
|
|
833
|
+
* Get value of given meta id
|
|
834
|
+
*
|
|
835
|
+
* @param {string} id
|
|
836
|
+
* @returns {*}
|
|
837
|
+
*/
|
|
838
|
+
getMeta(id) {
|
|
839
|
+
const meta = this._ensureMetaDict();
|
|
840
|
+
return meta[id];
|
|
841
|
+
}
|
|
842
|
+
|
|
843
|
+
/**
|
|
844
|
+
* Sets given value to id in metaNamespace
|
|
845
|
+
*
|
|
846
|
+
* @param {string} id
|
|
847
|
+
* @param {string} value
|
|
848
|
+
*/
|
|
849
|
+
setMeta(id, value) {
|
|
850
|
+
const meta = this._ensureMetaDict();
|
|
851
|
+
meta[id] = value;
|
|
852
|
+
// Make sure the object gets re-written, especially when using an external DB
|
|
853
|
+
this.dataset['**META**'] = meta;
|
|
854
|
+
|
|
855
|
+
setImmediate(() => {
|
|
856
|
+
// publish event in states
|
|
857
|
+
this.log.silly(`${this.namespace} memory publish meta ${id} ${value}`);
|
|
858
|
+
this.publishAll('meta', id, value);
|
|
859
|
+
});
|
|
860
|
+
|
|
861
|
+
if (!this.stateTimer) {
|
|
862
|
+
this.stateTimer = setTimeout(() => this.saveState(), this.writeFileInterval);
|
|
863
|
+
}
|
|
864
|
+
}
|
|
865
|
+
|
|
753
866
|
// needed by server
|
|
754
867
|
_setObjectDirect(id, obj) {
|
|
755
868
|
this.dataset[id] = obj;
|
|
756
869
|
|
|
757
870
|
// object updated -> if type changed to meta -> cache
|
|
758
|
-
if (
|
|
871
|
+
if (obj.type === 'meta' && this.existingMetaObjects[id] === false) {
|
|
759
872
|
this.existingMetaObjects[id] = true;
|
|
760
873
|
}
|
|
761
874
|
|
|
@@ -766,11 +879,12 @@ class ObjectsInMemoryFileDB extends InMemoryFileDB {
|
|
|
766
879
|
|
|
767
880
|
// needed by server
|
|
768
881
|
_delObject(id) {
|
|
769
|
-
|
|
770
|
-
|
|
882
|
+
const obj = this.dataset[id];
|
|
883
|
+
if (!obj) {
|
|
884
|
+
throw new Error(utils.ERRORS.ERROR_NOT_FOUND);
|
|
771
885
|
}
|
|
772
886
|
|
|
773
|
-
if (
|
|
887
|
+
if (obj.common && obj.common.dontDelete) {
|
|
774
888
|
throw new Error('Object is marked as non deletable');
|
|
775
889
|
}
|
|
776
890
|
|
|
@@ -796,7 +910,7 @@ class ObjectsInMemoryFileDB extends InMemoryFileDB {
|
|
|
796
910
|
|
|
797
911
|
// eslint-disable-next-line no-unused-vars
|
|
798
912
|
function _emit_(id, obj) {
|
|
799
|
-
result.rows.push({id: id, value: obj});
|
|
913
|
+
result.rows.push({ id: id, value: obj });
|
|
800
914
|
}
|
|
801
915
|
|
|
802
916
|
const f = eval('(' + func.map.replace(/emit/g, '_emit_') + ')');
|
|
@@ -806,13 +920,14 @@ class ObjectsInMemoryFileDB extends InMemoryFileDB {
|
|
|
806
920
|
if (params.startkey && id < params.startkey) {
|
|
807
921
|
continue;
|
|
808
922
|
}
|
|
809
|
-
if (params.endkey
|
|
923
|
+
if (params.endkey && id > params.endkey) {
|
|
810
924
|
continue;
|
|
811
925
|
}
|
|
812
926
|
}
|
|
813
|
-
|
|
927
|
+
const obj = this.dataset[id];
|
|
928
|
+
if (obj) {
|
|
814
929
|
try {
|
|
815
|
-
f(
|
|
930
|
+
f(obj);
|
|
816
931
|
} catch (e) {
|
|
817
932
|
this.log.warn(`${this.namespace} Cannot execute map: ${e.message}`);
|
|
818
933
|
}
|
|
@@ -827,7 +942,7 @@ class ObjectsInMemoryFileDB extends InMemoryFileDB {
|
|
|
827
942
|
}
|
|
828
943
|
}
|
|
829
944
|
if (max !== null) {
|
|
830
|
-
result.rows = [{id: '_stats', value: {max: max}}];
|
|
945
|
+
result.rows = [{ id: '_stats', value: { max: max } }];
|
|
831
946
|
} else {
|
|
832
947
|
result.rows = [];
|
|
833
948
|
}
|
|
@@ -838,15 +953,16 @@ class ObjectsInMemoryFileDB extends InMemoryFileDB {
|
|
|
838
953
|
|
|
839
954
|
// needed by server
|
|
840
955
|
_getObjectView(design, search, params) {
|
|
841
|
-
|
|
956
|
+
const designObj = this.dataset[`_design/${design}`];
|
|
957
|
+
if (!designObj) {
|
|
842
958
|
this.log.error(`${this.namespace} Cannot find view "${design}"`);
|
|
843
959
|
throw new Error(`Cannot find view "${design}"`);
|
|
844
960
|
}
|
|
845
|
-
if (!
|
|
961
|
+
if (!(designObj.views && designObj.views[search])) {
|
|
846
962
|
this.log.warn(`${this.namespace} Cannot find search "${search}" in "${design}"`);
|
|
847
963
|
throw new Error(`Cannot find search "${search}" in "${design}"`);
|
|
848
964
|
}
|
|
849
|
-
return this._applyView(
|
|
965
|
+
return this._applyView(designObj.views[search], params);
|
|
850
966
|
}
|
|
851
967
|
|
|
852
968
|
// Destructor of the class. Called by shutting down.
|
|
@@ -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;
|