Files
yunkong2.js-controller/lib/objects/objectsInRedis.js
2018-09-17 20:32:19 +08:00

1928 lines
74 KiB
JavaScript

/**
* Object DB in REDIS - Client
*
* Copyright 2014-2018 bluefox <dogafox@gmail.com>
*
* MIT License
*
*/
/* jshint -W097 */
/* jshint strict:false */
/* jslint node: true */
/* jshint -W061 */
'use strict';
const extend = require('node.extend');
const tools = require(__dirname + '/../tools');
const redis = require('redis');
const stream = require('stream');
const util = require('util');
const Writable = stream.Writable;
let memStore = {};
/* Writable memory stream */
function WMStrm(key, options) {
// allow use without new operator
if (!(this instanceof WMStrm)) return new WMStrm(key, options);
Writable.call(this, options); // init super
this.key = key; // save key
memStore[key] = new Buffer(''); // empty
}
util.inherits(WMStrm, Writable);
WMStrm.prototype._write = function (chunk, enc, cb) {
if (chunk) {
// our memory store stores things in buffers
let buffer = (Buffer.isBuffer(chunk)) ?
chunk : // already is Buffer use it
new Buffer(chunk, enc); // string, convert
// concatenate to the buffer already there
if (!memStore[this.key]) {
memStore[this.key] = new Buffer('');
console.log('memstore for ' + this.key + ' is null');
}
memStore[this.key] = Buffer.concat([memStore[this.key], buffer]);
}
if (!cb) throw 'Callback is empty';
cb();
};
function ObjectsInRedis(settings) {
settings = settings || {};
const redisNamespace = (settings.redisNamespace || 'config') + '.';
const ioRegExp = new RegExp('^' + redisNamespace);
const onChange = settings.change; // on change handler
const redisNamespaceL = redisNamespace.length;
let client;
let sub;
let that = this;
let users = {};
let groups = {};
let fileOptions = {};
let preserveSettings = [];
let regUser = /^system\.user/;
let regGroup = /^system\.group/;
let defaultAcl = {
groups: [],
acl: {
file: {
list: false,
read: false,
write: false,
create: false,
'delete': false
},
object: {
list: false,
read: false,
write: false,
'delete': false
}
}
};
let defaultNewAcl = settings.defaultNewAcl || null;
let log = settings.logger;
if (!log) {
log = {
silly: function (msg) {/*console.log(msg);*/},
debug: function (msg) {/*console.log(msg);*/},
info: function (msg) {/*console.log(msg);*/},
warn: function (msg) {
console.log(msg);
},
error: function (msg) {
console.log(msg);
}
};
} else if (!log.silly) {
log.silly = log.debug;
}
// -------------- FILE FUNCTIONS -------------------------------------------
function checkFile(id, name, options, flag) {
// read file settings from redis
if (typeof fileOptions[id][name].acl !== 'object') {
fileOptions[id][name] = {
mimeType: fileOptions[id][name],
acl: {
owner: (defaultNewAcl && defaultNewAcl.owner) || 'system.user.admin',
ownerGroup: (defaultNewAcl && defaultNewAcl.ownerGroup) || 'system.group.administrator',
permissions: (defaultNewAcl && defaultNewAcl.file) || 0x644 // '0644'
}
};
}
// Set default owner group
fileOptions[id][name].acl.ownerGroup = fileOptions[id][name].acl.ownerGroup || (defaultNewAcl && defaultNewAcl.ownerGroup) || 'system.group.administrator';
fileOptions[id][name].acl.owner = fileOptions[id][name].acl.owner || (defaultNewAcl && defaultNewAcl.owner) || 'system.user.admin';
fileOptions[id][name].acl.permissions = fileOptions[id][name].acl.permissions || (defaultNewAcl && defaultNewAcl.file) || 0x644; // '0644'
if (options.user !== 'system.user.admin' &&
options.groups.indexOf('system.group.administrator') === -1 &&
fileOptions[id][name].acl) {
if (fileOptions[id][name].acl.owner !== options.user) {
// Check if the user is in the group
if (options.groups.indexOf(fileOptions[id][name].acl.ownerGroup) !== -1) {
// Check group rights
if (!(fileOptions[id][name].acl.permissions & (flag << 4))) {
return false;
}
} else {
// everybody
if (!(fileOptions[id][name].acl.permissions & flag)) {
return false;
}
}
} else {
// Check user rights
if (!(fileOptions[id][name].acl.permissions & (flag << 8))) {
return false;
}
}
}
return true;
}
function checkFileRights(id, name, options, flag, callback) {
options = options || {};
if (!options.user) {
// Before files converted, lets think: if no options it is admin
options = {
user: 'system.user.admin',
params: options,
group: 'system.group.administrator'
};
}
if (options.checked) {
return callback(null, options);
}
if (!options.acl) {
that.getUserGroup(options.user, function (user, groups, acl) {
options.acl = acl || {};
options.groups = groups;
options.group = groups ? groups[0] : null;
checkFileRights(id, name, options, flag, callback);
});
return;
}
// If user may write
if (flag === 2 && !options.acl.file.write) {// write
return callback('permissionError', options);
}
// If user may read
if (flag === 4 && !options.acl.file.read) {// read
return callback('permissionError', options);
}
// read rights of file
if (!fileOptions[id]) {
// Redis actually does not support the file rights for users
/*if (fs.existsSync(objectsDir + id + '/_data.json')) {
try {
fileOptions[id] = JSON.parse(fs.readFileSync(objectsDir + id + '/_data.json', 'utf8'));
} catch (e) {
log.error('Cannot parse ' + objectsDir + id + '/_data.json: ' + e);
}
} else*/ {
fileOptions[id] = {};
}
}
options.checked = true;
if (!name || !fileOptions[id] || !fileOptions[id][name]) {
return callback(null, options);
}
if (checkFile(id, name, options,flag)) {
return callback(null, options);
} else {
return callback('permissionError', options);
}
/*if (typeof fileOptions[id][name].acl != 'object') {
fileOptions[id][name] = {
mimeType: fileOptions[id][name],
acl: {
owner: 'system.user.admin',
permissions: 0x644,
ownerGroup: 'system.group.administrator'
}
};
}
// Set default onwer group
fileOptions[id][name].acl.ownerGroup = fileOptions[id][name].acl.ownerGroup || 'system.group.administrator';
if (options.user != 'system.user.admin' &&
options.groups.indexOf('system.group.administrator') == -1 &&
fileOptions[id][name].acl) {
if (fileOptions[id][name].acl.owner != options.user) {
// Check if the user is in the group
if (options.groups.indexOf(fileOptions[id][name].acl.ownerGroup) != -1) {
// Check group rights
if (!(fileOptions[id][name].acl.permissions & (flag << 4))) {
return callback('permissionError', options);
}
} else {
// everybody
if (!(fileOptions[id][name].acl.permissions & flag)) {
return callback('permissionError', options);
}
}
} else {
// Check user rights
if (!(fileOptions[id][name].acl.permissions & (flag << 8))) {
return callback('permissionError', options);
}
}
}
return callback(null, options);*/
}
function setDefaultAcl(callback) {
try {
defaultNewAcl = Object.assign({}, objects['system.config'].common.defaultNewAcl);
} catch (e) {
defaultNewAcl = {
owner: 'system.user.admin',
ownerGroup: 'system.group.administrator',
object: 0x664,
state: 0x664,
file: 0x664
};
objects['system.config'].common.defaultNewAcl = Object.assign({}, defaultNewAcl);
}
let count = 0;
// Set all objects without ACL to this one
// todo
/*
for (let id in objects) {
if (objects.hasOwnProperty(id) && objects[id] && !objects[id].acl) {
objects[id].acl = Object.assign({}, defaultNewAcl);
delete objects[id].acl.file;
if (objects[id].type !== 'state') {
delete objects[id].acl.state;
}
count++;
}
}
*/
if (typeof callback === 'function') callback(null, count);
}
this.getUserGroup = function (user, callback) {
if (!user || typeof user !== 'string' || !user.match(/^system\.user\./)) {
console.log('invalid user name: ' + user);
user = JSON.stringify(user);
return callback.call(that, user, [], Object.assign({}, defaultAcl.acl));
}
if (users[user]) {
return callback.call(that, user, users[user].groups, users[user].acl);
}
// Read all groups
this.getObjectList({startkey: 'system.group.', endkey: 'system.group.\u9999'}, {checked: true}, function (err, arr) {
if (err) log.error(err);
groups = [];
if (arr) {
// Read all groups
for (let g = 0; g < arr.rows.length; g++) {
groups[g] = arr.rows[g].value;
if (groups[g]._id === 'system.group.administrator') {
groups[g].common.acl = {
file: {
list: true,
read: true,
write: true,
create: true,
'delete': true
},
object: {
list: true,
read: true,
write: true,
create: true,
'delete': true
},
users: {
list: true,
read: true,
write: true,
create: true,
'delete': true
}
};
}
}
}
that.getObjectList({startkey: 'system.user.', endkey: 'system.user.\u9999'}, {checked: true}, function (err, arr) {
if (err) log.error(err);
users = {};
if (arr) {
for (let i = 0; i < arr.rows.length; i++) {
users[arr.rows[i].value._id] = Object.assign({}, defaultAcl);
if (arr.rows[i].value._id === 'system.user.admin') {
users['system.user.admin'].acl.file = {
list: true,
read: true,
write: true,
create: true,
'delete': true
};
users['system.user.admin'].acl.object = {
create: true,
list: true,
read: true,
write: true,
'delete': true
};
users['system.user.admin'].acl.users = {
create: true,
list: true,
read: true,
write: true,
'delete': true
};
}
}
}
for (let g = 0; g < groups.length; g++) {
if (!groups[g].common.members) continue;
for (let m = 0; m < groups[g].common.members.length; m++) {
let u = groups[g].common.members[m];
users[u].groups.push(groups[g]._id);
if (groups[g].common.acl && groups[g].common.acl.file) {
if (!users[u].acl || !users[u].acl.file) {
users[u].acl = users[u].acl || {};
users[u].acl.file = users[u].acl.file || {};
users[u].acl.file.create = groups[g].common.acl.file.create;
users[u].acl.file.read = groups[g].common.acl.file.read;
users[u].acl.file.write = groups[g].common.acl.file.write;
users[u].acl.file['delete'] = groups[g].common.acl.file['delete'];
users[u].acl.file.list = groups[g].common.acl.file.list;
} else {
users[u].acl.file.create = users[u].acl.file.create || groups[g].common.acl.file.create;
users[u].acl.file.read = users[u].acl.file.read || groups[g].common.acl.file.read;
users[u].acl.file.write = users[u].acl.file.write || groups[g].common.acl.file.write;
users[u].acl.file['delete'] = users[u].acl.file['delete'] || groups[g].common.acl.file['delete'];
users[u].acl.file.list = users[u].acl.file.list || groups[g].common.acl.file.list;
}
}
if (groups[g].common.acl && groups[g].common.acl.object) {
if (!users[u].acl || !users[u].acl.object) {
users[u].acl = users[u].acl || {};
users[u].acl.object = users[u].acl.object || {};
users[u].acl.object.create = groups[g].common.acl.object.create;
users[u].acl.object.read = groups[g].common.acl.object.read;
users[u].acl.object.write = groups[g].common.acl.object.write;
users[u].acl.object['delete'] = groups[g].common.acl.object['delete'];
users[u].acl.object.list = groups[g].common.acl.object.list;
} else {
users[u].acl.object.create = users[u].acl.object.create || groups[g].common.acl.object.create;
users[u].acl.object.read = users[u].acl.object.read || groups[g].common.acl.object.read;
users[u].acl.object.write = users[u].acl.object.write || groups[g].common.acl.object.write;
users[u].acl.object['delete'] = users[u].acl.object['delete'] || groups[g].common.acl.object['delete'];
users[u].acl.object.list = users[u].acl.object.list || groups[g].common.acl.object.list;
}
}
if (groups[g].common.acl && groups[g].common.acl.users) {
if (!users[u].acl || !users[u].acl.users) {
users[u].acl = users[u].acl || {};
users[u].acl.users = users[u].acl.users || {};
users[u].acl.users.create = groups[g].common.acl.users.create;
users[u].acl.users.read = groups[g].common.acl.users.read;
users[u].acl.users.write = groups[g].common.acl.users.write;
users[u].acl.users['delete'] = groups[g].common.acl.users['delete'];
users[u].acl.users.list = groups[g].common.acl.users.list;
} else {
users[u].acl.users.create = users[u].acl.users.create || groups[g].common.acl.users.create;
users[u].acl.users.read = users[u].acl.users.read || groups[g].common.acl.users.read;
users[u].acl.users.write = users[u].acl.users.write || groups[g].common.acl.users.write;
users[u].acl.users['delete'] = users[u].acl.users['delete'] || groups[g].common.acl.users['delete'];
users[u].acl.users.list = users[u].acl.users.list || groups[g].common.acl.users.list;
}
}
}
}
callback.call(that, user, users[user] ? users[user].groups : [], users[user] ? users[user].acl : Object.assign({}, defaultAcl.acl));
});
});
};
this.getMimeType = function (ext) {
if (ext instanceof Array) ext = ext[0];
let _mimeType = 'text/javascript';
let isBinary = false;
if (ext === '.css') {
_mimeType = 'text/css';
} else if (ext === '.bmp') {
_mimeType = 'image/bmp';
isBinary = true;
} else if (ext === '.png') {
isBinary = true;
_mimeType = 'image/png';
} else if (ext === '.jpg') {
isBinary = true;
_mimeType = 'image/jpeg';
} else if (ext === '.jpeg') {
isBinary = true;
_mimeType = 'image/jpeg';
} else if (ext === '.gif') {
isBinary = true;
_mimeType = 'image/gif';
} else if (ext === '.tif') {
isBinary = true;
_mimeType = 'image/tiff';
} else if (ext === '.js') {
_mimeType = 'application/javascript';
} else if (ext === '.html') {
_mimeType = 'text/html';
} else if (ext === '.htm') {
_mimeType = 'text/html';
} else if (ext === '.json') {
_mimeType = 'application/json';
} else if (ext === '.xml') {
_mimeType = 'text/xml';
} else if (ext === '.svg') {
_mimeType = 'image/svg+xml';
} else if (ext === '.eot') {
isBinary = true;
_mimeType = 'application/vnd.ms-fontobject';
} else if (ext === '.ttf') {
isBinary = true;
_mimeType = 'application/font-sfnt';
} else if (ext === '.cur') {
isBinary = true;
_mimeType = 'application/x-win-bitmap';
} else if (ext === '.woff') {
isBinary = true;
_mimeType = 'application/font-woff';
} else if (ext === '.wav') {
isBinary = true;
_mimeType = 'audio/wav';
} else if (ext === '.mp3') {
isBinary = true;
_mimeType = 'audio/mpeg3';
} else if (ext === '.avi') {
isBinary = true;
_mimeType = 'video/avi';
} else if (ext === '.mp4') {
isBinary = true;
_mimeType = 'video/mp4';
} else if (ext === '.mkv') {
isBinary = true;
_mimeType = 'video/mkv';
} else if (ext === '.zip') {
isBinary = true;
_mimeType = 'application/zip';
} else if (ext === '.ogg') {
isBinary = true;
_mimeType = 'audio/ogg';
} else if (ext === '.manifest') {
_mimeType = 'text/cache-manifest';
} else {
_mimeType = 'text/javascript';
}
return {mimeType: _mimeType, isBinary: isBinary};
};
this.insert = function (id, attName, ignore, options, obj, callback) {
if (typeof options === 'string') {
options = {mimeType: options};
}
//return pipe for write into redis
let strm = new WMStrm(id + '/' + attName);
strm.on('finish', function () {
if (!memStore[id + '/' + attName]) log.error('File ' + id + ' / ' + attName + ' is empty');
that.writeFile(id, attName, memStore[id + '/' + attName] || '', options, function () {
if (memStore[id + '/' + attName] !== undefined) delete memStore[id + '/' + attName];
if (callback) setImmediate(callback, null, null);
});
});
return strm;
};
this.writeFile = function (id, name, data, options, callback) {
if (typeof options === 'function') {
callback = options;
options = null;
}
if (typeof options === 'string') {
options = {mimeType: options};
}
if (name[0] === '/') name = name.substring(1);
// If file yet exists => check the permissions
if (!options || !options.checked) {
return checkFileRights(id, name, options, 0x2/*write*/, function (err, options) {
if (err) {
if (typeof callback === 'function') callback(err);
} else {
return that.writeFile(id, name, data, options, callback);
}
});
}
let isBinary;
let ext = name.match(/\.[^.]+$/);
let mime = that.getMimeType(ext);
let _mimeType = mime.mimeType;
isBinary = mime.isBinary;
if (!fileOptions[id][name]) {
fileOptions[id][name] = {createdAt: (new Date()).getTime()};
}
if (!fileOptions[id][name].acl) {
fileOptions[id][name].acl = {
owner: options.user || (defaultNewAcl && defaultNewAcl.owner) || 'system.user.admin',
ownerGroup: options.group || (defaultNewAcl && defaultNewAcl.ownerGroup) || 'system.group.administrator',
permissions: options.mode || (defaultNewAcl && defaultNewAcl.file) || 0x644
};
}
fileOptions[id][name].mimeType = options.mimeType || _mimeType;
fileOptions[id][name].binary = isBinary;
fileOptions[id][name].acl.ownerGroup = fileOptions[id][name].acl.ownerGroup || (defaultNewAcl && defaultNewAcl.ownerGroup) || 'system.group.administrator';
fileOptions[id][name].modifiedAt = (new Date()).getTime();
states.getConfig(id, function (err, _obj) {
if (_obj) {
_obj._attachments = _obj._attachments || {};
if (!_obj._attachments[name]) {
_obj._attachments[name] = fileOptions[id][name];
}
states.setConfig(id, _obj, function () {
states.setBinaryState('_files/' + id + '/' + name, data, callback);
});
} else {
states.setBinaryState('_files/' + id + '/' + name, data, callback);
}
});
};
this.readFile = function (id, name, options, callback) {
if (typeof options === 'function') {
callback = options;
options = null;
}
if (name[0] === '/') name = name.substring(1);
if (!options || !options.checked) {
checkFileRights(id, name, options, 0x4/*read*/, function (err, options) {
if (err) {
if (typeof callback === 'function') callback(err);
} else {
return that.readFile(id, name, options, callback);
}
});
return;
}
states.getConfig(id, function (err, _obj) {
states.getBinaryState('_files/' + id + '/' + name, function (err, buffer) {
if (typeof callback === 'function') {
let mimeType;
if (_obj && _obj._attachments && _obj._attachments.mimeType) {
mimeType = _obj._attachments.mimeType;
if (!_obj._attachments.binary) {
buffer = buffer.toString();
}
} else {
let ext = name.match(/\.[^.]+$/);
let mime = that.getMimeType(ext);
mimeType = mime.mimeType;
}
callback(err, buffer, mimeType);
}
});
});
};
this.unlink = function (id, name, options, callback) {
if (typeof options === 'function') {
callback = options;
options = null;
}
if (name[0] === '/') name = name.substring(1);
if (!options || !options.checked) {
checkFileRights(id, name, options, 0x2/*write*/, function (err, options) {
if (err) {
if (typeof callback === 'function') callback(err);
} else {
if (!options.acl.file['delete']) {
if (typeof callback === 'function') callback('permissionError');
} else {
return that.unlink(id, name, options, callback);
}
}
});
return;
}
states.getConfig(id, function (err, _obj) {
if (_obj && _obj._attachments) {
if (_obj._attachments[name]) {
delete _obj._attachments[name];
}
states.setConfig(id, _obj);
}
states.delBinaryState('_files/' + id + '/' + name, callback);
});
};
this.readDir = function (id, name, options, callback) {
if (typeof options === 'function') {
callback = options;
options = null;
}
if (!options || !options.checked) {
checkFileRights(id, name, options, 0x4/*read*/, function (err, options) {
if (err) {
if (typeof callback === 'function') callback(err);
} else {
if (!options.acl.file.list) {
if (typeof callback === 'function') callback('permissionError');
} else {
return that.readDir(id, name, options, callback);
}
}
});
return;
}
// TODO
if (typeof callback === 'function') {
setImmediate(function () {
callback('not implemented');
});
}
};
this.rename = function (id, oldName, newName, options, callback) {
if (typeof options === 'function') {
callback = options;
options = null;
}
if (oldName[0] === '/') oldName = oldName.substring(1);
if (newName[0] === '/') newName = newName.substring(1);
if (!options || !options.checked) {
checkFileRights(id, oldName, options, 0x2/*write*/, function (err, options) {
if (err) {
if (typeof callback === 'function') callback(err);
} else {
if (!options.acl.file.write) {
if (typeof callback === 'function') callback('permissionError');
} else {
return that.rename(id, oldName, newName, options, callback);
}
}
});
return;
}
// TODO
if (typeof callback === 'function') callback('not implemented');
};
this.touch = function (id, name, options, callback) {
if (typeof options === 'function') {
callback = options;
options = null;
}
if (!options || !options.checked) {
checkFileRights(id, null, options, 0x2/*write*/, function (err, options) {
if (err) {
if (typeof callback === 'function') callback(err);
} else {
return that.touch(id, name, options, callback);
}
});
return;
}
// TODO
if (typeof callback === 'function') callback('not implemented');
};
this.rm = function (id, name, options, callback) {
if (typeof options === 'function') {
callback = options;
options = null;
}
if (!options || !options.checked) {
checkFileRights(id, null, options, 0x2/*write*/, function (err, options) {
if (err) {
if (typeof callback === 'function') callback(err);
} else {
if (!options.acl.file['delete']) {
if (typeof callback === 'function') callback('permissionError');
} else {
return that.rm(id, name, options, callback);
}
}
});
return;
}
// TODO
if (typeof callback === 'function') callback('not implemented');
};
this.mkdir = function (id, dirname, options, callback) {
if (typeof options === 'function') {
callback = options;
options = null;
}
if (dirname[0] === '/') dirname = dirname.substring(1);
if (!options || !options.checked) {
checkFileRights(id, dirname, options, 0x2/*write*/, function (err, options) {
if (err) {
if (typeof callback === 'function') callback(err);
} else {
if (!options.acl.file.write) {
if (typeof callback === 'function') callback('permissionError');
} else {
return that.mkdir(id, dirname, options, callback);
}
}
});
return;
}
// TODO
if (typeof callback === 'function') callback('not implemented');
};
this.chownFile = function (id, name, options, callback) {
if (typeof options === 'function') {
callback = options;
options = null;
}
options = options || {};
if (typeof options !== 'object') {
options = {owner: options};
}
if (name[0] === '/') name = name.substring(1);
if (!options.ownerGroup && options.group) options.ownerGroup = options.group;
if (!options.owner && options.user) options.owner = options.user;
if (!options.owner) {
log.error('user is not defined');
if (typeof callback === 'function') callback('invalid parameter');
return;
}
if (!options.ownerGroup) {
// get user group
this.getUserGroup(options.owner, function (user, groups /* , permissions */) {
if (!groups || !groups[0]) {
if (typeof callback === 'function') callback('user "' + options.owner + '" belongs to no group');
return;
} else {
options.ownerGroup = groups[0];
}
that.chownFile(id, name, options, callback);
});
return;
}
if (!options.checked) {
checkFileRights(id, null, options, 0x2/*write*/, function (err, options) {
if (err) {
if (typeof callback === 'function') callback(err);
} else {
if (!options.acl.file.write) {
if (typeof callback === 'function') callback('permissionError');
} else {
return that.chownFile(id, name, options, callback);
}
}
});
return;
}
// TODO
if (typeof callback === 'function') callback('not implemented');
};
this.chmodFile = function (id, name, options, callback) {
if (typeof options === 'function') {
callback = options;
options = null;
}
options = options || {};
if (name[0] === '/') name = name.substring(1);
if (typeof options !== 'object') {
options = {mode: options};
}
if (options.mode === undefined) {
log.error('mode is not defined');
if (typeof callback === 'function') callback('invalid parameter');
return;
} else if (typeof options.mode === 'string') {
options.mode = parseInt(options.mode, 16);
}
if (!options.checked) {
checkFileRights(id, null, options, 0x2/*write*/, function (err, options) {
if (err) {
if (typeof callback === 'function') callback(err);
} else {
if (!options.acl.file.write) {
if (typeof callback === 'function') callback('permissionError');
} else {
return that.chmodFile(id, name, options, callback);
}
}
});
return;
}
// TODO
if (typeof callback === 'function') callback('not implemented');
};
this.enableFileCache = function (enabled, options, callback) {
if (typeof options === 'function') {
callback = options;
options = null;
}
if (!options || !options.checked) {
checkObjectRights(options, 0x2/*write*/, function (err, options) {
if (err) {
if (typeof callback === 'function') callback(err);
} else {
return this.enableFileCache(enabled, options, callback);
}
}.bind(this));
return;
}
if (typeof callback === 'function') {
// cache cannot be enabled
setImmediate(function () {
callback(null, false);
});
}
};
// -------------- OBJECT FUNCTIONS -------------------------------------------
function checkObject(obj, options, flag) {
// read rights of object
if (!obj || !obj.common || !obj.acl || flag === 'list') {
return true;
}
if (options.user !== 'system.user.admin' &&
options.groups && options.groups.indexOf('system.group.administrator') === -1) {
if (obj.acl.owner !== options.user) {
// Check if the user is in the group
if (options.groups.indexOf(obj.acl.ownerGroup) !== -1) {
// Check group rights
if (!(obj.acl.object & (flag << 4))) {
return false;
}
} else {
// everybody
if (!(obj.acl.object & flag)) {
return false;
}
}
} else {
// Check group rights
if (!(obj.acl.object & (flag << 8))) {
return false;
}
}
}
return true;
}
function checkObjectRights(options, flag, callback) {
options = options || {};
if (!options.user) {
// Before files converted, lets think: if no options it is admin
options = {
user: 'system.user.admin',
params: options,
group: 'system.group.administrator',
acl: {
object: {
read: true,
write: true,
'delete': true,
create: true,
list: true
},
file: {
read: true,
write: true,
'delete': true,
create: true,
list: true
},
/* state: {
read: true,
write: true,
'delete': true,
create: true,
list: true
},*/
users: {
read: true,
write: true,
create: true,
'delete': true,
list: true
}
}
};
}
if (options.checked) {
return callback(null, options);
}
if (!options.acl) {
that.getUserGroup(options.user, function (user, groups, acl) {
options.acl = acl || {};
options.groups = groups;
options.group = groups ? groups[0] : null;
checkObjectRights(options, flag, callback);
});
return;
}
// if user or group objects
if (regUser.test(id) || regGroup.test(id)) {
// If user may write
if (flag === 2 && !options.acl.users.write) {// write
return callback('permissionError', options);
}
// If user may read
if (flag === 4 && !options.acl.users.read) {// read
return callback('permissionError', options);
}
// If user may delete
if (flag === 'delete' && !options.acl.users.delete) {// delete
return callback('permissionError', options);
}
// If user may list
if (flag === 'list' && !options.acl.users.list) {// list
return callback('permissionError', options);
}
// If user may create
if (flag === 'create' && !options.acl.users.create) {// create
return callback('permissionError', options);
}
if (flag === 'delete') flag = 2; // write
}
// If user may write
if (flag === 2 && !options.acl.object.write) {// write
return callback('permissionError', options);
}
// If user may read
if (flag === 4 && !options.acl.object.read) {// read
return callback('permissionError', options);
}
// If user may delete
if (flag === 'delete' && !options.acl.object.delete) {// delete
return callback('permissionError', options);
}
// If user may list
if (flag === 'list' && !options.acl.object.list) {// list
return callback('permissionError', options);
}
if (flag === 'delete') flag = 2; // write
options.checked = true;
return callback(null, options);
}
function clone(obj) {
if (obj === null || obj === undefined || typeof obj !== 'object')
return obj;
let temp = obj.constructor(); // changed
for (let key in obj) {
if (obj.hasOwnProperty(key)) {
temp[key] = clone(obj[key]);
}
}
return temp;
}
this.subscribe = (pattern, options, callback) => {
if (!options || !options.checked) {
checkObjectRights(options, 'list', (err, options) => {
if (err) {
if (typeof callback === 'function') callback(err);
} else {
return this.subscribe(pattern, options, callback);
}
});
return;
}
log.silly(settings.namespace + ' redis psubscribe ' + redisNamespace + pattern);
sub.psubscribe(redisNamespace + pattern, function (err) {
if (typeof callback === 'function') callback(err);
});
};
this.unsubscribe = (pattern, options, callback) => {
if (!options || !options.checked) {
checkObjectRights(options, 'list', (err, options) => {
if (err) {
if (typeof callback === 'function') callback(err);
} else {
return this.unsubscribe(pattern, options, callback);
}
});
return;
}
log.silly(settings.namespace + ' redis punsubscribe ' + redisNamespace + pattern);
sub.punsubscribe(redisNamespace + pattern, function (err) {
if (typeof callback === 'function') callback(err);
});
};
this.chownObject = function (pattern, options, callback) {
options = options || {};
if (typeof options !== 'object') {
options = {owner: options};
}
// renaming
if (!options.ownerGroup && options.group) options.ownerGroup = options.group;
if (!options.owner && options.user) options.owner = options.user;
if (!options.owner) {
log.error('user is not defined');
if (typeof callback === 'function') callback('invalid parameter');
return;
}
if (!options.ownerGroup) {
// get user group
this.getUserGroup(options.owner, (user, groups /* , permissions */) => {
if (!groups || !groups[0]) {
if (typeof callback === 'function') callback('user "' + options.owner + '" belongs to no group');
return;
} else {
options.ownerGroup = groups[0];
}
this.chownObject(pattern, options, callback);
});
return;
}
if (!options.checked) {
checkObjectRights(options, 0x2/*write*/, (err, options) => {
if (err) {
if (typeof callback === 'function') callback(err);
} else {
if (!options.acl.object || !options.acl.object.write) {
if (typeof callback === 'function') callback('permissionError');
} else {
return this.chownObject(pattern, options, callback);
}
}
});
}
// TODO
};
this.chmodObject = function (pattern, options, callback) {
options = options || {};
if (typeof options !== 'object') {
options = {object: options};
}
if (options.mode && !options.object) options.object = options.mode;
if (options.object === undefined) {
log.error('mode is not defined');
if (typeof callback === 'function') callback('invalid parameter');
return;
} else if (typeof options.mode === 'string') {
options.mode = parseInt(options.mode, 16);
}
if (!options.checked) {
checkObjectRights(options, 0x2/*write*/, (err, options) => {
if (err) {
if (typeof callback === 'function') callback(err);
} else {
if (!options.acl.file.write) {
if (typeof callback === 'function') callback('permissionError');
} else {
return that.chmodObject(pattern, options, callback);
}
}
});
}
// TODO
};
this.getObject = function (id, options, callback) {
if (typeof options === 'function') {
callback = options;
options = null;
}
if (!options || !options.checked) {
checkObjectRights(options, 0x4/*read*/, (err, options) => {
if (err) {
if (typeof callback === 'function') callback(err);
} else {
return this.getObject(id, options, callback);
}
});
return;
}
if (typeof callback === 'function') {
client.get(redisNamespace + id, function (err, obj) {
if (err) {
log.warn(settings.namespace + ' redis get ' + id + ', error - ' + err);
} else {
log.silly(settings.namespace + ' redis get ' + id + ' ok: ' + obj);
}
try {
obj = obj ? JSON.parse(obj) : null;
} catch (e) {
log.error(`Cannot parse ${id} - ${obj}: ${JSON.stringify(e)}`);
}
callback(err, obj ? JSON.parse(obj) : null);
});
}
};
function pattern2RegEx(pattern) {
if (pattern !== '*') {
if (pattern[0] === '*' && pattern[pattern.length - 1] !== '*') pattern += '$';
if (pattern[0] !== '*' && pattern[pattern.length - 1] === '*') pattern = '^' + pattern;
}
pattern = pattern.replace(/\./g, '\\.');
pattern = pattern.replace(/\*/g, '.*');
return pattern;
}
this.getKeys = function (pattern, options, callback, dontModify) {
if (typeof options === 'function') {
callback = options;
options = null;
}
if (!options || !options.checked) {
checkObjectRights(options, 'list', (err, options) => {
if (err) {
if (typeof callback === 'function') callback(err);
} else {
return this.getConfigKeys(pattern, options, callback, dontModify);
}
});
return;
}
let r = new RegExp(pattern2RegEx(pattern));
client.keys(redisNamespace + pattern, (err, keys) => {
log.silly(settings.namespace + ' redis keys ' + obj.length + ' ' + pattern);
if (typeof callback === 'function') {
let result = [];
if (keys) {
keys.sort();
if (options.user === 'system.user.admin' || options.group === 'system.group.administrator') {
setImmediate(function () {
callback(err, keys);
});
} else {
let result = [];
// Check permissions
client.mget(keys, function (err, objs) {
for (let i = 0; i < keys.length; i++) {
if (r.test(keys[i]) && checkObject(objs[i], options, 0x4/*read*/)) {
if (!dontModify) {
result.push(keys[i].substring(redisNamespaceL));
} else {
result.push(keys[i]);
}
}
}
setImmediate(function () {
callback(err, result);
});
})
}
} else {
setImmediate(function () {
callback(err, result);
});
}
}
});
};
this.getConfigKeys = this.getKeys;
this.getObjects = function (keys, options, callback, dontModify) {
if (typeof options === 'function') {
callback = options;
options = null;
}
if (!options || !options.checked) {
checkObjectRights(options, 0x4/*read*/, (err, options) => {
if (err) {
if (typeof callback === 'function') callback(err);
} else {
return this.getObjects(keys, options, callback, dontModify);
}
});
return;
}
if (!keys) {
if (typeof callback === 'function') callback('no keys', null);
return;
}
if (!keys.length) {
if (typeof callback === 'function') callback(null, []);
return;
}
let _keys;
if (!dontModify) {
_keys = [];
for (let i = 0; i < keys.length; i++) {
_keys[i] = redisNamespace + keys[i];
}
} else {
_keys = keys;
}
client.mget(_keys, function (err, objs) {
let result = [];
if (err) {
log.warn(settings.namespace + ' redis mget ' + ((!objs) ? 0 : objs.length) + ' ' + _keys.length + ', err: ' + err);
} else {
log.silly(settings.namespace + ' redis mget ' + ((!objs) ? 0 : objs.length) + ' ' + _keys.length);
}
if (objs) {
if (options.user !== 'system.user.admin' && options.group !== 'system.group.administrator') {
for (let i = 0; i < objs.length; i++) {
if (checkObject(objs[i], options, 4 /*read*/)) {
result.push(objs[i]);
} else {
result.push({error: 'permissionError'});
}
}
} else {
result = objs;
}
}
if (typeof callback === 'function') {
setImmediate(function () {
callback(null, result);
});
}
});
};
this.getObjectsByPattern = (pattern, options, callback) => {
if (typeof options === 'function') {
callback = options;
options = null;
}
if (!options || !options.checked) {
checkObjectRights(options, 0x4/*read*/, (err, options) => {
if (err) {
if (typeof callback === 'function') callback(err);
} else {
return this.getObjectsByPattern(keys, options, callback);
}
});
return;
}
client.keys(redisNamespace + pattern, (err, keys) => {
log.silly(settings.namespace + ' redis keys ' + obj.length + ' ' + pattern);
if (typeof callback === 'function') {
let result = [];
if (keys) {
keys.sort();
// read objects
client.mget(keys, function (err, objs) {
let result = [];
if (err) {
log.warn(settings.namespace + ' redis mget ' + ((!objs) ? 0 : objs.length) + ' ' + keys.length + ', err: ' + err);
} else {
log.silly(settings.namespace + ' redis mget ' + ((!objs) ? 0 : objs.length) + ' ' + keys.length);
}
if (objs) {
if (options.user !== 'system.user.admin' && options.group !== 'system.group.administrator') {
const r = new RegExp(pattern2RegEx(pattern));
for (let i = 0; i < objs.length; i++) {
if (!objs[i] || (r.test(objs[i]._id) && checkObject(objs[i], options, 4 /*read*/))) {
result.push(objs[i]);
} else {
result.push({error: 'permissionError'});
}
}
} else {
result = objs;
}
}
if (typeof callback === 'function') {
setImmediate(function () {
callback(null, result);
});
}
});
} else {
setImmediate(function () {
callback(err, result);
});
}
}
});
};
/**
* set anew or update object
*
* This function writes the object into DB
*
* @alias setObject
* @memberof objectsInMemServer
* @param {string} id ID of the object
* @param {object} obj
* @param {object} options options for access control are optional
* @param {function} callback return function
*/
this.setObject = function (id, obj, options, callback) {
if (typeof options === 'function') {
callback = options;
options = null;
}
if (!options || !options.checked) {
checkObjectRights(options, 0x2/*write*/, function (err, options) {
if (err) {
if (typeof callback === 'function') callback(err);
} else {
return this.setObject(id, obj, options, callback);
}
}.bind(this));
return;
}
if (!obj) {
log.error('setObject: Argument object is null');
if (typeof callback === 'function') callback('obj is null');
return;
}
obj._id = id;
// read object
client.get(redisNamespace + id, (err, oldObj) => {
if (err) {
log.warn(settings.namespace + ' redis get ' + id + ', error - ' + err);
} else {
log.silly(settings.namespace + ' redis get ' + id + ' ok: ' + oldObj);
}
try {
oldObj = oldObj ? JSON.parse(oldObj) : null;
} catch (e) {
log.error(`Cannot parse ${id} - ${oldObj}: ${JSON.stringify(e)}`);
}
if (!checkObject(oldObj, options, 0x2/*write*/)) {
if (typeof callback === 'function') {
callback({error: 'permissionError'});
}
return;
}
if (id === 'system.config' && obj && obj.common && oldObj && oldObj.common && JSON.stringify(obj.common.defaultNewAcl) !== JSON.stringify(oldObj.common.defaultNewAcl)) {
oldObj = obj;
return setDefaultAcl(function () {
that.setObject(id, obj, options, callback);
});
}
if (!tools.checkNonEditable(oldObj, obj)) {
if (typeof callback === 'function') {
callback('Invalid password for update of vendor information');
}
return;
}
// do not delete common settings, like "history" or "mobile". It can be erased only with "null"
if (oldObj && oldObj.common) {
for (let i = 0; i < preserveSettings.length; i++) {
// remove settings if desired
if (obj.common && obj.common[preserveSettings[i]] === null) {
delete obj.common[preserveSettings[i]];
continue;
}
if (oldObj.common[preserveSettings[i]] !== undefined && (!obj.common || obj.common[preserveSettings[i]] === undefined)) {
if (!obj.common) obj.common = {};
obj.common[preserveSettings[i]] = oldObj.common[preserveSettings[i]];
}
}
}
if (oldObj && oldObj.acl && !obj.acl) {
obj.acl = oldObj.acl;
}
// add user default rights
if (defaultNewAcl && !obj.acl) {
obj.acl = Object.assign({}, defaultNewAcl);
delete obj.acl.file;
if (obj.type !== 'state') {
delete obj.acl.state;
}
if (options.owner) {
obj.acl.owner = options.owner;
if (!options.ownerGroup) {
obj.acl.ownerGroup = null;
this.getUserGroup(options.owner, (user, groups /* , permissions */) => {
if (!groups || !groups[0]) {
options.ownerGroup = (defaultNewAcl && defaultNewAcl.ownerGroup) || 'system.group.administrator';
} else {
options.ownerGroup = groups[0];
}
this.setObject(id, obj, options, callback);
});
return;
}
}
}
if (defaultNewAcl && obj.acl && !obj.acl.ownerGroup && options.ownerGroup) {
obj.acl.ownerGroup = options.ownerGroup;
}
const jsonObj = JSON.stringify(obj);
client.set(redisNamespace + id, jsonObj, function (err) {
log.silly(settings.namespace + ' redis set', id, obj);
client.publish(redisNamespace + id, jsonObj);
if (typeof callback === 'function') {
callback(err, {id: id});
}
})
});
};
this.delObject = function (id, options, callback) {
if (typeof options === 'function') {
callback = options;
options = null;
}
if (!options || !options.checked) {
checkObjectRights(options, 'delete', (err, options) => {
if (err) {
if (typeof callback === 'function') callback(err);
} else {
return this.delObject(id, options, callback);
}
});
return;
}
// read object
client.get(redisNamespace + id, (err, oldObj) => {
if (err) {
log.warn(settings.namespace + ' redis get ' + id + ', error - ' + err);
} else {
log.silly(settings.namespace + ' redis get ' + id + ' ok: ' + oldObj);
}
try {
oldObj = oldObj ? JSON.parse(oldObj) : null;
} catch (e) {
log.error(`Cannot parse ${id} - ${oldObj}: ${JSON.stringify(e)}`);
}
if (!checkObject(oldObj, options, 2/* write */)) {
if (typeof callback === 'function') {
callback({error: 'permissionError'});
}
return;
}
client.del(redisNamespace + id, function (err) {
client.publish(redisNamespace + id, null);
if (typeof callback === 'function') callback(err);
});
});
};
// this function is very ineffective. Because reads all objects and then process them
function _applyView(func, params, options, callback) {
let result = {
rows: []
};
function _emit_(id, obj) {
result.rows.push({id: id, value: obj});
}
params = params || {startkey: '', endkey: '\u9999'};
client.keys(redisNamespace + '*', (err, keys) => {
keys.sort();
client.mget(keys, (err, objs) => {
let f = eval('(' + func.map.replace(/emit/g, '_emit_') + ')');
for (let i = 0; i < keys.length; i++) {
const id = keys[i];
if (params) {
if (params.startkey && id < params.startkey) continue;
if (params.endkey && id > params.endkey) continue;
}
if (objects[id]) {
try {
f(objects[id]);
} catch (e) {
console.log('Cannot execute map: ' + e.message);
}
}
}
// Calculate max
if (func.reduce === '_stats') {
let max = null;
for (let i = 0; i < result.rows.length; i++) {
if (max === null || result.rows[i].value > max) {
max = result.rows[i].value;
}
}
if (max !== null) {
result.rows = [{id: '_stats', value: {max: max}}];
} else {
result.rows = [];
}
}
if (typeof callback === 'function') callback(null, result);
});
});
}
this.getObjectView = function (design, search, params, options, callback) {
if (typeof options === 'function') {
callback = options;
options = null;
}
if (!options || !options.checked) {
checkObjectRights(options, 4/*read*/, (err, options) => {
if (err) {
if (typeof callback === 'function') callback(err);
} else {
return this.getObjectView(design, search, params, options, callback);
}
});
return;
}
if (typeof callback === 'function') {
client.get(redisNamespace + '_design/' + design, (err, obj) => {
if (err) {
console.error('Cannot find view "' + design + '": ' + err);
}
if (obj && obj.views && obj.views[search]) {
this.getObjectList(params, options, (err, _result) => {
const func = obj.views[search];
let f = eval('(' + func.map.replace(/emit/g, '_emit_') + ')');
let result = {rows: []};
// required for getObjectView
function _emit_(id, obj) {
result.rows.push({id: id, value: obj});
}
for (let i = 0; i < _result.rows.length; i++) {
const id = _result.rows[i].id;
const obj = _result.rows[i].value;
if (obj) {
try {
f(obj);
} catch (e) {
console.log('Cannot execute map: ' + e.message);
}
}
}
// Calculate max
if (func.reduce === '_stats') {
let max = null;
for (let i = 0; i < result.rows.length; i++) {
if (max === null || result.rows[i].value > max) {
max = result.rows[i].value;
}
}
if (max !== null) {
result.rows = [{id: '_stats', value: {max: max}}];
} else {
result.rows = [];
}
}
callback(null, result);
});
} else {
console.log('Cannot find view "' + design + '"');
callback('Cannot find view "' + design + '"');
}
});
}
};
this.getObjectList = function (params, options, callback) {
if (typeof options === 'function') {
callback = options;
options = null;
}
if (!options || !options.checked) {
checkObjectRights(options, 4/*read*/, (err, options) => {
if (err) {
if (typeof callback === 'function') callback(err);
} else {
return this.getObjectList(params, options, callback);
}
});
return;
}
if (typeof callback === 'function') {
//params = {startkey, endkey, include_docs}
params = params || {startkey: '', endkey: '\u9999'};
let pattern = (params.endkey.substring(0, params.startkey.length) === params.startkey) ? redisNamespace + params.startkey + '*' : redisNamespace + '*';
client.keys(pattern, (err, keys) => {
let _keys = [];
for (let i = 0; i < keys.length; i++) {
if (params.startkey && keys[i] < params.startkey) continue;
if (params.endkey && keys[i] > params.endkey) continue;
if (!params.include_docs && keys[i][redisNamespaceL] === '_') continue;
_keys.push(keys[i]);
}
keys.sort();
client.mget(_keys, function (err, objs) {
// return rows with id and doc
let result = {
rows: []
};
for (let r = 0; r < objs.length; r++) {
let obj = JSON.parse(objs[r]);
if (!checkObject(obj, options, 4/*read*/)) continue;
result.rows.push({id: keys[r], value: obj, doc: obj});
}
callback(null, result);
});
});
}
};
this.getObjectListAsync = (params, options) => {
return new Promise((resolve, reject) => {
this.getObjectList(params, options, (err, arr) => {
if (err) {
reject(err);
} else {
resolve(arr);
}
})
});
};
// could be optimised, to read object only once. Now it will read 3 times
this.extendObject = function (id, obj, options, callback) {
if (typeof options === 'function') {
callback = options;
options = null;
}
if (!options || !options.checked) {
checkObjectRights(options, 2/*write*/, (err, options) => {
if (err) {
if (typeof callback === 'function') callback(err);
} else {
return this.extendObject(id, obj, options, callback);
}
});
return;
}
this.getObject(id, options, function (err, data) {
let newObj = extend(true, data || {}, obj);
that.setObject(id, newObj, options, function (err, res) {
if (typeof callback === 'function') callback(err, {id: id, value: res}, id);
});
});
};
this.setConfig = this.setObject;
this.delConfig = this.delObject;
this.getConfig = this.getObject;
this.getConfigs = this.getObjects;
this.findObject = function (idOrName, type, options, callback) {
if (typeof options === 'function') {
callback = options;
options = null;
}
if (!options || !options.checked) {
checkObjectRights(options, 'list', function (err, options) {
if (err) {
if (typeof callback === 'function') callback(err);
} else {
return this.findObject(idOrName, type, options, callback);
}
}.bind(this));
return;
}
if (!objects) {
if (typeof callback === 'function') callback('Not implemented');
return;
}
// Assume it is ID
if (objects[idOrName] && (!type || (objects[idOrName].common && objects[idOrName].common.type === type))) {
if (typeof callback === 'function') callback(null, idOrName, objects[idOrName].common.name);
} else {
// Assume it is name
for (let id in objects) {
if (!checkObject(objects[id], options, 4/*read*/)) continue;
if (objects[id].common &&
objects[id].common.name === idOrName &&
(!type || (objects[id].common && objects[id].common.type === type))) {
if (typeof callback === 'function') callback(null, id, idOrName);
return;
}
}
if (typeof callback === 'function') callback(null, null, idOrName);
}
};
// can be called only from js-controller
this.addPreserveSettings = function (settings) {
if (typeof settings !== 'object') settings = [settings];
for (let s = 0; s < settings.length; s++) {
if (preserveSettings.indexOf(settings[s]) === -1) preserveSettings.push(settings[s]);
}
};
this.destroyDB = function (options, callback) {
if (typeof options === 'function') {
callback = options;
options = null;
}
options = options || {};
if (!options.checked) {
checkObjectRights(options, 0x2/*write*/, function (err, options) {
if (err) {
if (typeof callback === 'function') callback(err);
} else {
if (!options.acl.file.write) {
if (typeof callback === 'function') callback('permissionError');
} else {
return that.destroyDB(options, callback);
}
}
});
return;
}
states.getConfigKeys('*', function (err, obj) {
for (let t = 0; t < obj.length; t++) {
states.delConfig(obj.substring(7));
}
if (typeof callback === 'function') callback();
});
};
// Destructor of the class. Called by shutting down.
this.destroy = () => {
if (client) {
client.end(true);
client = null;
}
if (sub) {
sub.end();
sub = null;
}
};
(function __construct() {
if (settings.connection.port === 0) {
// initiate a unix socket connection using the parameter 'host'
client = redis.createClient(settings.connection.host, settings.connection.options);
sub = redis.createClient(settings.connection.host, settings.connection.options);
} else {
client = redis.createClient(settings.connection.port, settings.connection.host, settings.connection.options);
sub = redis.createClient(settings.connection.port, settings.connection.host, settings.connection.options);
}
if (typeof onChange === 'function') {
sub.on('pmessage', (pattern, channel, message) => {
log.debug(settings.namespace + ' redis pmessage ', pattern, channel, message);
try {
if (ioRegExp.test(channel)) {
const id = channel.substring(redisNamespaceL);
try {
const obj = message ? JSON.parse(message) : null;
onChange(id, obj);
} catch (e) {
log.error(`Cannot parse ${id} - ${message}: ${JSON.stringify(e)}`);
}
} else {
log.error(`Received unexpected pmessage: ${channel}`);
}
} catch (e) {
log.error(settings.namespace + ' pmessage ' + channel + ' ' + message + ' ' + e.message);
log.error(settings.namespace + ' ' + e.stack);
}
});
}
client.on('error', error => {
if (typeof settings.disconnected === 'function') {
settings.disconnected(error);
} else {
log.error(settings.namespace + ' ' + error.message);
log.error(settings.namespace + ' ' + error.stack);
}
});
sub.on('error', error => {
log.error(settings.namespace + ' No redis connection!');
});
sub.on('connect', error => {
if (settings.connection.port === 0) {
log.info(settings.namespace + ' Objects connected to redis: ' + settings.connection.host);
} else {
log.info(settings.namespace + ' Objects connected to redis: ' + settings.connection.host + ':' + settings.connection.port);
}
});
client.on('connect', error => {
if (typeof settings.connected === 'function') settings.connected();
});
})();
return this;
}
module.exports = ObjectsInRedis;