Initial commit
This commit is contained in:
5102
lib/adapter.js
Normal file
5102
lib/adapter.js
Normal file
File diff suppressed because it is too large
Load Diff
78
lib/dbdump.js
Normal file
78
lib/dbdump.js
Normal file
@@ -0,0 +1,78 @@
|
||||
/**
|
||||
*
|
||||
* dpdump.js
|
||||
*
|
||||
* Utility to get JSON Database Dumps
|
||||
*
|
||||
* 7'2014 hobbyquaker <hq@ccu.io>
|
||||
*
|
||||
*/
|
||||
/* jshint -W097 */// jshint strict:false
|
||||
/*jslint node: true */
|
||||
'use strict';
|
||||
|
||||
var yargs = require('yargs')
|
||||
.usage('$0 [-d design] [-s search] [-p pattern]')
|
||||
.example('$0 -s state', 'get all objects with type=state')
|
||||
.example('$0 -s state -p hue.*', 'get all objects from the adapter hue with type=state')
|
||||
// Todo .example('$0 -p system.*', 'get all objects with _id=system.*')
|
||||
.alias('s', 'search')
|
||||
.alias('d', 'design')
|
||||
.default('design', 'system')
|
||||
;
|
||||
|
||||
if (!yargs.argv.s && !yargs.argv.p) {
|
||||
yargs.showHelp();
|
||||
process.exit(0);
|
||||
}
|
||||
|
||||
var Objects = require(__dirname + '/objects.js');
|
||||
|
||||
|
||||
var db = new Objects({
|
||||
logger: {
|
||||
silly: function (msg) { },
|
||||
debug: function (msg) { },
|
||||
info: function (msg) { },
|
||||
warn: function (msg) {
|
||||
console.log(msg);
|
||||
},
|
||||
error: function (msg) {
|
||||
console.log(msg);
|
||||
}
|
||||
},
|
||||
connected: function () {
|
||||
|
||||
if (yargs.argv.search) {
|
||||
db.getObjectView(yargs.argv.design, yargs.argv.search, {}, function (err, res) {
|
||||
if (err) {
|
||||
console.log(err);
|
||||
process.exit(1);
|
||||
}
|
||||
showResult(res);
|
||||
|
||||
});
|
||||
} else {
|
||||
db.getObjectList({include_docs: true}, function (err, res) {
|
||||
if (err) {
|
||||
console.log(err);
|
||||
process.exit(1);
|
||||
}
|
||||
showResult(res);
|
||||
});
|
||||
}
|
||||
|
||||
}
|
||||
});
|
||||
|
||||
function showResult(res) {
|
||||
var outArr = [];
|
||||
for (var i = 0; i < res.total_rows; i++) {
|
||||
var obj = res.rows[i].value;
|
||||
delete obj._rev;
|
||||
outArr.push(obj);
|
||||
}
|
||||
console.log(JSON.stringify(outArr, null, ' '));
|
||||
process.exit(0);
|
||||
}
|
||||
|
||||
60
lib/defaultObjs.js
Normal file
60
lib/defaultObjs.js
Normal file
@@ -0,0 +1,60 @@
|
||||
'use strict';
|
||||
|
||||
function createDefaults(lang, temperature, currency) {
|
||||
var defaults = {
|
||||
"level.dimmer": {
|
||||
"def": 0,
|
||||
"type": "number",
|
||||
"read": true,
|
||||
"write": true,
|
||||
"min": 0,
|
||||
"max": 100,
|
||||
"unit": "%"
|
||||
},
|
||||
"indicator.working": {
|
||||
"def": false,
|
||||
"type": "boolean",
|
||||
"read": true,
|
||||
"write": false,
|
||||
"min": false,
|
||||
"max": true
|
||||
},
|
||||
"indicator.maintenance": {
|
||||
"def": false,
|
||||
"type": "boolean",
|
||||
"read": true,
|
||||
"write": false,
|
||||
"min": false,
|
||||
"max": true
|
||||
},
|
||||
"indicator.maintenance.lowbat": {
|
||||
"def": false,
|
||||
"type": "boolean",
|
||||
"read": true,
|
||||
"write": false,
|
||||
"min": false,
|
||||
"max": true,
|
||||
"desc": "Low battery"
|
||||
},
|
||||
"indicator.maintenance.unreach": {
|
||||
"def": false,
|
||||
"type": "boolean",
|
||||
"read": true,
|
||||
"write": false,
|
||||
"min": false,
|
||||
"max": true,
|
||||
"desc": "Device unreachable"
|
||||
},
|
||||
"switch": {
|
||||
"def": false,
|
||||
"type": "boolean",
|
||||
"read": true,
|
||||
"write": true
|
||||
}
|
||||
};
|
||||
|
||||
return defaults;
|
||||
}
|
||||
|
||||
|
||||
module.exports = createDefaults;
|
||||
BIN
lib/img/iobroker.png
Normal file
BIN
lib/img/iobroker.png
Normal file
Binary file not shown.
|
After Width: | Height: | Size: 14 KiB |
182
lib/letsencrypt.js
Normal file
182
lib/letsencrypt.js
Normal file
@@ -0,0 +1,182 @@
|
||||
'use strict';
|
||||
|
||||
function createServer(app, settings, certificates, leSettings, log) {
|
||||
let server;
|
||||
|
||||
const leLog = function (debug, arg1, arg2, arg3) {
|
||||
if (debug) {
|
||||
const args = Array.prototype.slice.call(arguments);
|
||||
args.shift(); // remove debug argument
|
||||
// skip "no match"
|
||||
if (args[0][0] === 'n' && args[0][1] === 'o') return;
|
||||
log.info(arg1 + (arg2 || '') + (arg3 || ''));
|
||||
}
|
||||
};
|
||||
|
||||
if (settings.secure) {
|
||||
if (leSettings && (!leSettings.email || !leSettings.email) && settings.leEnabled) {
|
||||
log.error('Please specify the email address and domains to use Let\'s Encrypt certificates!');
|
||||
}
|
||||
|
||||
if (leSettings && leSettings.email && leSettings.domains && settings.leEnabled) {
|
||||
const tools = require(__dirname + '/tools');
|
||||
const tls = require('tls');
|
||||
const fs = require('fs');
|
||||
const LE = require('greenlock');
|
||||
let leDir;
|
||||
|
||||
let configPath = tools.getConfigFileName().replace(/\\/g, '/');
|
||||
let parts = configPath.split('/');
|
||||
parts.pop();
|
||||
configPath = parts.join('/');
|
||||
|
||||
if (leSettings.path && (leSettings.path[0] === '/' || leSettings.path.match(/^[A-Za-z]:/))) {
|
||||
leDir = leSettings.path;
|
||||
} else {
|
||||
leDir = configPath + '/' + (leSettings.path || 'letsencrypt');
|
||||
}
|
||||
|
||||
// for lex outputs
|
||||
if (!console.debug) console.debug = console.log;
|
||||
|
||||
if (!fs.existsSync(leDir)) fs.mkdirSync(leDir);
|
||||
|
||||
// prepare domains
|
||||
if (typeof leSettings.domains === 'string') {
|
||||
leSettings.domains = leSettings.domains.split(',');
|
||||
for (let d = leSettings.domains.length - 1; d >= 0; d--) {
|
||||
leSettings.domains[d] = leSettings.domains[d].trim();
|
||||
if (!leSettings.domains[d]) leSettings.domainss.splice(d, 1);
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
|
||||
let lex = LE.create({
|
||||
debug: true,
|
||||
configDir: leDir,
|
||||
agreeTos: true,
|
||||
store: require(__dirname + '/letsencryptStore.js').create({debug: true, log: leLog}),
|
||||
server: 'https://acme-v01.api.letsencrypt.org/directory', //'staging',
|
||||
email: leSettings.email,
|
||||
approvedDomains: leSettings.domains,
|
||||
log: leLog
|
||||
});
|
||||
|
||||
if (settings.leUpdate) {
|
||||
settings.lePort = parseInt(settings.lePort, 10) || 80;
|
||||
// handles acme-challenge and redirects to https
|
||||
// used for validation of requests like http://example.com/.well-known/acme-challenge/BLABALBAL
|
||||
require('http').createServer(lex.middleware(function redirectHttps(req, res) {
|
||||
res.setHeader('Location', 'https://' + req.headers.host + req.url);
|
||||
res.statusCode = 302;
|
||||
res.end('<!-- Hello Developer Person! Please use HTTPS instead -->');
|
||||
})).listen(settings.lePort, function () {
|
||||
log.info('LetsEncrypt challenge server is started on ' + settings.lePort);
|
||||
});
|
||||
}
|
||||
|
||||
let options = JSON.parse(JSON.stringify(certificates));
|
||||
let defaultTls = tls.createSecureContext(certificates);
|
||||
let hostTls;
|
||||
let running;
|
||||
|
||||
options.SNICallback = function (hostname, cb) {
|
||||
if (leSettings.domains.indexOf(hostname) !== -1) {
|
||||
if (settings.leUpdate) {
|
||||
if (running === true) {
|
||||
cb(null, hostTls || defaultTls);
|
||||
} else
|
||||
if (running) {
|
||||
running.push(cb);
|
||||
} else {
|
||||
running = [cb];
|
||||
return lex.httpsOptions.SNICallback(hostname, function (err, tls) {
|
||||
if (tls) log.debug('Got valid certificates from letsencrypt');
|
||||
if (err) log.error('Cannot get certificates: ' + err);
|
||||
lex.debug = false;
|
||||
hostTls = tls;
|
||||
for (let r = 0; r < running.length; r++) {
|
||||
running[r](err, tls || defaultTls);
|
||||
}
|
||||
running = true;
|
||||
});
|
||||
}
|
||||
} else {
|
||||
if (!hostTls) {
|
||||
// validate certificates
|
||||
lex.check({domains: leSettings.domains}).then(function (certInfo) {
|
||||
if (certInfo) {
|
||||
hostTls = tls.createSecureContext({
|
||||
key: certInfo.privkey || certInfo.key, // privkey.pem
|
||||
cert: certInfo.fullchain || certInfo.cert, // fullchain.pem (cert.pem + '\n' + chain.pem)
|
||||
ca: certInfo.ca
|
||||
});
|
||||
cb(null, hostTls);
|
||||
} else {
|
||||
log.error('No letsencrypt certificates found in "' + leDir + '"');
|
||||
cb(null, defaultTls);
|
||||
// do not register domain
|
||||
/*
|
||||
if (!running) {
|
||||
running = [cb];
|
||||
lex.letsencrypt.register({
|
||||
domains: leSettings.domains,
|
||||
email: leSettings.email,
|
||||
agreeTos: true
|
||||
}, function (err, certInfo) {
|
||||
//log.debug("[LEX] '" + hostname + "' register completed", err && err.stack || null, certInfo);
|
||||
if ((!err || !err.stack) && !certInfo) {
|
||||
log.error((new Error('[LEX] SANITY FAIL: no error and yet no certs either')).stack);
|
||||
}
|
||||
if (certInfo) {
|
||||
hostTls = tls.createSecureContext({
|
||||
key: certInfo.privkey || certInfo.key, // privkey.pem
|
||||
cert: certInfo.fullchain || certInfo.cert, // fullchain.pem (cert.pem + '\n' + chain.pem)
|
||||
ca: certInfo.ca
|
||||
});
|
||||
for (var r = 0; r < running.length; r++) {
|
||||
running[r](null, hostTls);
|
||||
}
|
||||
} else {
|
||||
for (var r = 0; r < running.length; r++) {
|
||||
running[r](null, defaultTls);
|
||||
}
|
||||
}
|
||||
running = true;
|
||||
});
|
||||
} else {
|
||||
if (running === true) {
|
||||
cb(null, defaultTls);
|
||||
} else {
|
||||
running.push(cb);
|
||||
}
|
||||
}
|
||||
*/
|
||||
}
|
||||
},
|
||||
function (err) {
|
||||
if (err) log.error(err);
|
||||
cb(null, defaultTls);
|
||||
});
|
||||
} else {
|
||||
cb(null, hostTls);
|
||||
}
|
||||
}
|
||||
} else {
|
||||
cb(null, defaultTls);
|
||||
}
|
||||
};
|
||||
|
||||
server = require('https').createServer(options, lex.middleware(app));
|
||||
} else {
|
||||
server = require('https').createServer(certificates, app);
|
||||
}
|
||||
} else {
|
||||
server = require('http').createServer(app);
|
||||
}
|
||||
|
||||
return server;
|
||||
}
|
||||
|
||||
exports.createServer = createServer;
|
||||
628
lib/letsencryptStore.js
Normal file
628
lib/letsencryptStore.js
Normal file
@@ -0,0 +1,628 @@
|
||||
// Initially copied from https://github.com/Daplie/le-store-certbot
|
||||
// but must be completely rewritten to support DB
|
||||
|
||||
'use strict';
|
||||
|
||||
var PromiseA = require('bluebird');
|
||||
var mkdirpAsync = PromiseA.promisify(require('mkdirp'));
|
||||
var path = require('path');
|
||||
var fs = PromiseA.promisifyAll(require('fs'));
|
||||
var sfs = require('safe-replace');
|
||||
|
||||
var log = function (debug) {
|
||||
if (debug) {
|
||||
var args = Array.prototype.slice.call(arguments);
|
||||
args.shift();
|
||||
args.unshift('[le-store-certbot]');
|
||||
console.log.apply(console, args);
|
||||
}
|
||||
};
|
||||
|
||||
function writeRenewalConfig(args) {
|
||||
var pyobj = args.pyobj;
|
||||
pyobj.checkpoints = parseInt(pyobj.checkpoints, 10) || 0;
|
||||
|
||||
var pyconf = PromiseA.promisifyAll(require('pyconf'));
|
||||
|
||||
var liveDir = args.liveDir || path.join(args.configDir, 'live', args.domains[0]);
|
||||
|
||||
var certPath = args.certPath || pyobj.cert || path.join(liveDir, 'cert.pem');
|
||||
var fullchainPath = args.fullchainPath || pyobj.fullchain || path.join(liveDir, 'fullchain.pem');
|
||||
var chainPath = args.chainPath || pyobj.chain || path.join(liveDir, 'chain.pem');
|
||||
var privkeyPath = args.privkeyPath || pyobj.privkey
|
||||
//|| args.domainPrivateKeyPath || args.domainKeyPath || pyobj.keyPath
|
||||
|| path.join(liveDir, 'privkey.pem');
|
||||
|
||||
log(args.debug, 'writeRenewalConfig privkeyPath', privkeyPath);
|
||||
|
||||
var updates = {
|
||||
account: args.account.id
|
||||
, configDir: args.configDir
|
||||
, domains: args.domains
|
||||
|
||||
, email: args.email
|
||||
, tos: args.agreeTos && true
|
||||
// yes, it's an array. weird, right?
|
||||
, webrootPath: args.webrootPath && [args.webrootPath] || []
|
||||
, server: args.server || args.acmeDiscoveryUrl
|
||||
|
||||
, privkey: privkeyPath
|
||||
, fullchain: fullchainPath
|
||||
, cert: certPath
|
||||
, chain: chainPath
|
||||
|
||||
, http01Port: args.http01Port
|
||||
, keyPath: args.domainPrivateKeyPath || args.privkeyPath
|
||||
, rsaKeySize: args.rsaKeySize
|
||||
, checkpoints: pyobj.checkpoints
|
||||
/* // TODO XXX what's the deal with these? they don't make sense
|
||||
// are they just old junk? or do they have a meaning that I don't know about?
|
||||
, fullchainPath: path.join(args.configDir, 'chain.pem')
|
||||
, certPath: path.join(args.configDir, 'cert.pem')
|
||||
, chainPath: path.join(args.configDir, 'chain.pem')
|
||||
*/ // TODO XXX end
|
||||
, workDir: args.workDir
|
||||
, logsDir: args.logsDir
|
||||
};
|
||||
|
||||
// final section is completely dynamic
|
||||
// :hostname = :webroot_path
|
||||
args.domains.forEach(function (hostname) {
|
||||
updates[hostname] = args.webrootPath;
|
||||
});
|
||||
|
||||
// must write back to the original pyobject or
|
||||
// annotations will be lost
|
||||
Object.keys(updates).forEach(function (key) {
|
||||
pyobj[key] = updates[key];
|
||||
});
|
||||
|
||||
return mkdirpAsync(path.dirname(args.renewalPath)).then(function () {
|
||||
return pyconf.writeFileAsync(args.renewalPath, pyobj);
|
||||
}).then(function () {
|
||||
// NOTE
|
||||
// writing twice seems to causes a bug,
|
||||
// so instead we re-read the file from the disk
|
||||
return pyconf.readFileAsync(args.renewalPath);
|
||||
});
|
||||
}
|
||||
|
||||
function pyToJson(pyobj) {
|
||||
if (!pyobj) {
|
||||
return null;
|
||||
}
|
||||
|
||||
var jsobj = {};
|
||||
Object.keys(pyobj).forEach(function (key) {
|
||||
jsobj[key] = pyobj[key];
|
||||
});
|
||||
jsobj.__lines = undefined;
|
||||
jsobj.__keys = undefined;
|
||||
|
||||
return jsobj;
|
||||
}
|
||||
|
||||
var defaults = {
|
||||
configDir: [ '~', 'letsencrypt', 'etc' ].join(path.sep) // /etc/letsencrypt/
|
||||
, logsDir: [ '~', 'letsencrypt', 'var', 'log' ].join(path.sep) // /var/log/letsencrypt/
|
||||
, workDir: [ '~', 'letsencrypt', 'var', 'lib' ].join(path.sep) // /var/lib/letsencrypt/
|
||||
|
||||
, accountsDir: [ ':configDir', 'accounts', ':serverDir' ].join(path.sep)
|
||||
, renewalPath: [ ':configDir', 'renewal', ':hostname.conf' ].join(path.sep)
|
||||
, renewalDir: [ ':configDir', 'renewal', '' ].join(path.sep)
|
||||
, serverDirGet: function (copy) {
|
||||
return (copy.server || '').replace('https://', '').replace(/(\/)$/, '').replace(/\//g, path.sep);
|
||||
}
|
||||
|
||||
, privkeyPath: ':configDir/live/:hostname/privkey.pem'.split(/\//).join(path.sep)
|
||||
, fullchainPath: [ ':configDir', 'live', ':hostname', 'fullchain.pem' ].join(path.sep)
|
||||
, certPath: [ ':configDir', 'live', ':hostname', 'cert.pem' ].join(path.sep)
|
||||
, chainPath: [ ':configDir', 'live', ':hostname', 'chain.pem' ].join(path.sep)
|
||||
|
||||
, rsaKeySize: 2048
|
||||
, webrootPath: [ ':workDir', 'acme-challenge' ].join(path.sep)
|
||||
};
|
||||
|
||||
module.exports.create = function (configs) {
|
||||
var mergedConfigs;
|
||||
|
||||
if (configs && typeof configs.log === 'function') log = configs.log;
|
||||
|
||||
var store = {
|
||||
getOptions: function () {
|
||||
if (mergedConfigs) {
|
||||
return configs;
|
||||
}
|
||||
|
||||
if (!configs.domainKeyPath) {
|
||||
configs.domainKeyPath = configs.privkeyPath || defaults.privkeyPath;
|
||||
}
|
||||
|
||||
Object.keys(defaults).forEach(function (key) {
|
||||
if (!configs[key]) {
|
||||
configs[key] = defaults[key];
|
||||
}
|
||||
});
|
||||
|
||||
mergedConfigs = configs;
|
||||
return configs;
|
||||
}
|
||||
|
||||
, keypairs: {
|
||||
checkAsync: function (keypath, format) {
|
||||
if (!keypath) {
|
||||
return null;
|
||||
}
|
||||
return fs.readFileAsync(keypath, 'ascii').then(function (key) {
|
||||
if ('jwk' === format) {
|
||||
return { privateKeyJwk: JSON.parse(key) };
|
||||
}
|
||||
else {
|
||||
return { privateKeyPem: key };
|
||||
}
|
||||
}, function (err) {
|
||||
if ('ENOENT' !== err.code) {
|
||||
throw err;
|
||||
}
|
||||
|
||||
return null;
|
||||
});
|
||||
}
|
||||
, setAsync: function (keypath, keypair, format) {
|
||||
return mkdirpAsync(path.dirname(keypath)).then(function () {
|
||||
var key;
|
||||
|
||||
if ('jwk' === format) {
|
||||
key = JSON.stringify(keypair.privateKeyJwk, null, ' ');
|
||||
}
|
||||
else {
|
||||
key = keypair.privateKeyPem;
|
||||
}
|
||||
|
||||
return fs.writeFileAsync(keypath, key, 'ascii').then(function () {
|
||||
return keypair;
|
||||
});
|
||||
});
|
||||
}
|
||||
}
|
||||
|
||||
//
|
||||
// Certificates
|
||||
//
|
||||
, certificates: {
|
||||
// Certificates
|
||||
checkKeypairAsync: function (args) {
|
||||
if (!args.domainKeyPath) {
|
||||
return PromiseA.reject(new Error("missing options.domainKeyPath"));
|
||||
}
|
||||
|
||||
return store.keypairs.checkAsync(args.domainKeyPath, 'pem');
|
||||
}
|
||||
// Certificates
|
||||
, setKeypairAsync: function (args, keypair) {
|
||||
return store.keypairs.setAsync(args.domainKeyPath, keypair, 'pem');
|
||||
}
|
||||
// Certificates
|
||||
, checkAsync: function (args) {
|
||||
if (!args.fullchainPath || !args.privkeyPath || !args.certPath || !args.chainPath) {
|
||||
return PromiseA.reject(new Error("missing one or more of privkeyPath, fullchainPath, certPath, chainPath from options"));
|
||||
}
|
||||
|
||||
//, fs.readFileAsync(fullchainPath, 'ascii')
|
||||
// note: if this ^^ gets added back in, the arrays below must change
|
||||
return PromiseA.all([
|
||||
fs.readFileAsync(args.privkeyPath, 'ascii') // 0
|
||||
, fs.readFileAsync(args.certPath, 'ascii') // 1
|
||||
, fs.readFileAsync(args.chainPath, 'ascii') // 2
|
||||
, fs.readFileAsync(args.fullchainPath, 'ascii') // 3
|
||||
|
||||
// stat the file, not the link
|
||||
, fs.statAsync(args.certPath) // 4
|
||||
]).then(function (arr) {
|
||||
return {
|
||||
privkey: arr[0] // privkey.pem
|
||||
, cert: arr[1] // cert.pem
|
||||
, chain: arr[2] // chain.pem
|
||||
, fullchain: arr[3] // fullchain.pem
|
||||
/*
|
||||
// TODO populate these values only if they are known
|
||||
, issuedAt: arr[4].mtime.valueOf()
|
||||
, expiresAt: arr[4].mtime.valueOf() + (90 * 24 * 60 * 60 * 100)
|
||||
*/
|
||||
};
|
||||
}, function (err) {
|
||||
if (args.debug) {
|
||||
console.error("[le-store-certbot] certificates.check");
|
||||
console.error(err.stack);
|
||||
}
|
||||
return null;
|
||||
});
|
||||
}
|
||||
// Certificates
|
||||
, setAsync: function (args) {
|
||||
return store.configs.getAsync(args).then(function (pyobj) {
|
||||
var pems = args.pems;
|
||||
|
||||
pyobj.checkpoints = parseInt(pyobj.checkpoints, 10) || 0;
|
||||
|
||||
var liveDir = args.liveDir || path.join(args.configDir, 'live', args.domains[0]);
|
||||
|
||||
var certPath = args.certPath || pyobj.cert || path.join(liveDir, 'cert.pem');
|
||||
var fullchainPath = args.fullchainPath || pyobj.fullchain || path.join(liveDir, 'fullchain.pem');
|
||||
var chainPath = args.chainPath || pyobj.chain || path.join(liveDir, 'chain.pem');
|
||||
var privkeyPath = args.privkeyPath || pyobj.privkey
|
||||
|| args.domainKeyPath
|
||||
|| path.join(liveDir, 'privkey.pem');
|
||||
|
||||
var archiveDir = args.archiveDir || path.join(args.configDir, 'archive', args.domains[0]);
|
||||
|
||||
var checkpoints = pyobj.checkpoints.toString();
|
||||
var certArchive = path.join(archiveDir, 'cert' + checkpoints + '.pem');
|
||||
var fullchainArchive = path.join(archiveDir, 'fullchain' + checkpoints + '.pem');
|
||||
var chainArchive = path.join(archiveDir, 'chain'+ checkpoints + '.pem');
|
||||
var privkeyArchive = path.join(archiveDir, 'privkey' + checkpoints + '.pem');
|
||||
|
||||
return mkdirpAsync(archiveDir).then(function () {
|
||||
return PromiseA.all([
|
||||
sfs.writeFileAsync(certArchive, pems.cert, 'ascii')
|
||||
, sfs.writeFileAsync(chainArchive, pems.chain, 'ascii')
|
||||
, sfs.writeFileAsync(fullchainArchive, pems.cert + pems.chain, 'ascii')
|
||||
, sfs.writeFileAsync(privkeyArchive, pems.privkey, 'ascii')
|
||||
]);
|
||||
}).then(function () {
|
||||
return mkdirpAsync(liveDir);
|
||||
}).then(function () {
|
||||
return PromiseA.all([
|
||||
sfs.writeFileAsync(certPath, pems.cert, 'ascii')
|
||||
, sfs.writeFileAsync(chainPath, pems.chain, 'ascii')
|
||||
, sfs.writeFileAsync(fullchainPath, pems.cert + pems.chain, 'ascii')
|
||||
, sfs.writeFileAsync(privkeyPath, pems.privkey, 'ascii')
|
||||
]);
|
||||
}).then(function () {
|
||||
pyobj.checkpoints += 1;
|
||||
args.checkpoints += 1;
|
||||
|
||||
// TODO other than for compatibility this is optional, right?
|
||||
// or is it actually needful for renewal? (i.e. list of domains)
|
||||
return writeRenewalConfig(args);
|
||||
}).then(function () {
|
||||
return {
|
||||
privkey: pems.privkey
|
||||
, cert: pems.cert
|
||||
, chain: pems.chain
|
||||
|
||||
/*
|
||||
// TODO populate these only if they are actually known
|
||||
, issuedAt: Date.now()
|
||||
, expiresAt: Date.now() + (90 * 24 * 60 * 60 * 100)
|
||||
*/
|
||||
};
|
||||
});
|
||||
});
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
//
|
||||
// Accounts
|
||||
//
|
||||
, accounts: {
|
||||
// Accounts
|
||||
_getAccountKeyPath: function (args) {
|
||||
var promise = PromiseA.resolve(args.accountId);
|
||||
|
||||
if (args.email && !args.accountKeyPath && !args.accountId) {
|
||||
promise = store.accounts._getAccountIdByEmail(args);
|
||||
}
|
||||
|
||||
return promise.then(function (accountId) {
|
||||
if (!accountId) {
|
||||
return null;
|
||||
}
|
||||
return args.accountKeyPath || path.join(args.accountsDir, accountId, 'private_key.json');
|
||||
});
|
||||
}
|
||||
// Accounts
|
||||
, _getAccountIdByEmail: function (args) {
|
||||
// If we read 10,000 account directories looking for
|
||||
// just one email address, that could get crazy.
|
||||
// We should have a folder per email and list
|
||||
// each account as a file in the folder
|
||||
// TODO
|
||||
var email = args.email;
|
||||
if ('string' !== typeof email) {
|
||||
log(args.debug, "No email given");
|
||||
return PromiseA.resolve(null);
|
||||
}
|
||||
return fs.readdirAsync(args.accountsDir).then(function (nodes) {
|
||||
log(args.debug, "success reading arg.accountsDir");
|
||||
|
||||
return PromiseA.all(nodes.map(function (node) {
|
||||
return fs.readFileAsync(path.join(args.accountsDir, node, 'regr.json'), 'utf8').then(function (text) {
|
||||
var regr = JSON.parse(text);
|
||||
regr.__accountId = node;
|
||||
|
||||
return regr;
|
||||
});
|
||||
})).then(function (regrs) {
|
||||
var accountId;
|
||||
|
||||
log(args.debug, "regrs.length", regrs.length);
|
||||
|
||||
regrs.some(function (regr) {
|
||||
return regr.body.contact.some(function (contact) {
|
||||
var match = contact.toLowerCase() === 'mailto:' + email.toLowerCase();
|
||||
if (match) {
|
||||
accountId = regr.__accountId;
|
||||
return true;
|
||||
}
|
||||
});
|
||||
});
|
||||
|
||||
if (!accountId) {
|
||||
return null;
|
||||
}
|
||||
|
||||
return accountId;
|
||||
});
|
||||
}).then(function (accountId) {
|
||||
return accountId;
|
||||
}, function (err) {
|
||||
if ('ENOENT' === err.code) {
|
||||
// ignore error
|
||||
return null;
|
||||
}
|
||||
|
||||
return PromiseA.reject(err);
|
||||
});
|
||||
}
|
||||
// Accounts
|
||||
, _getAccountIdByPublicKey: function (keypair) {
|
||||
// we use insecure md5 - even though we know it's bad - because that's how the python client did
|
||||
return require('crypto').createHash('md5').update(keypair.publicKeyPem).digest('hex');
|
||||
}
|
||||
// Accounts
|
||||
, checkKeypairAsync: function (args) {
|
||||
if (!(args.accountKeyPath || args.accountsDir)) {
|
||||
return PromiseA.reject(new Error("must provide one of options.accountKeyPath or options.accountsDir"));
|
||||
}
|
||||
|
||||
return store.accounts._getAccountKeyPath(args).then(function (keypath) {
|
||||
return store.keypairs.checkAsync(keypath, 'jwk');
|
||||
});
|
||||
}
|
||||
// Accounts
|
||||
, setKeypairAsync: function (args, keypair) {
|
||||
var accountId;
|
||||
|
||||
if (args.email) {
|
||||
accountId = store.accounts._getAccountIdByPublicKey(keypair);
|
||||
}
|
||||
|
||||
return store.accounts._getAccountKeyPath({
|
||||
accountsDir: args.accountsDir
|
||||
, email: args.email
|
||||
, accountId: args.accountId || accountId
|
||||
}).then(function (keypath) {
|
||||
return store.keypairs.setAsync(keypath, keypair, 'jwk');
|
||||
});
|
||||
}
|
||||
// Accounts
|
||||
, checkAsync: function (args) {
|
||||
var promise;
|
||||
var files = {};
|
||||
var accountId;
|
||||
|
||||
if (args.accountId) {
|
||||
promise = PromiseA.resolve(args.accountId);
|
||||
}
|
||||
else if (args.email) {
|
||||
promise = store.accounts._getAccountIdByEmail(args);
|
||||
}
|
||||
else {
|
||||
promise = PromiseA.reject(new Error("must provide accountId or email"));
|
||||
}
|
||||
|
||||
return promise.then(function (_accountId) {
|
||||
log(args.debug, 'accountId:', _accountId);
|
||||
if (!_accountId) {
|
||||
return false;
|
||||
}
|
||||
accountId = _accountId;
|
||||
var accountDir = path.join(args.accountsDir, accountId);
|
||||
var configs = [ 'meta.json', 'private_key.json', 'regr.json' ];
|
||||
|
||||
return PromiseA.all(configs.map(function (filename) {
|
||||
var keyname = filename.slice(0, -5);
|
||||
|
||||
return fs.readFileAsync(path.join(accountDir, filename), 'utf8').then(function (text) {
|
||||
var data;
|
||||
|
||||
try {
|
||||
data = JSON.parse(text);
|
||||
} catch(e) {
|
||||
files[keyname] = { error: e };
|
||||
return;
|
||||
}
|
||||
|
||||
files[keyname] = data;
|
||||
|
||||
return true;
|
||||
}, function (err) {
|
||||
log(args.debug, 'Error reading account files:', err);
|
||||
files[keyname] = { error: err };
|
||||
});
|
||||
}));
|
||||
}).then(function (hasAccount) {
|
||||
if (!hasAccount) {
|
||||
return null;
|
||||
}
|
||||
var err;
|
||||
|
||||
if (!Object.keys(files).every(function (key) {
|
||||
return !files[key].error;
|
||||
}) || !files.private_key || !files.private_key.n) {
|
||||
err = new Error("Account '" + accountId + "' was corrupt (had id, but was missing files).");
|
||||
err.code = 'E_ACCOUNT_CORRUPT';
|
||||
err.data = files;
|
||||
return PromiseA.reject(err);
|
||||
}
|
||||
|
||||
//files.private_key;
|
||||
//files.regr;
|
||||
//files.meta;
|
||||
files.accountId = accountId; // preserve current account id
|
||||
files.id = accountId;
|
||||
files.keypair = { privateKeyJwk: files.private_key };
|
||||
|
||||
return files;
|
||||
});
|
||||
}
|
||||
// Accounts
|
||||
, setAsync: function (args, reg) {
|
||||
var os = require("os");
|
||||
var accountId = store.accounts._getAccountIdByPublicKey(reg.keypair);
|
||||
var accountDir = path.join(args.accountsDir, accountId);
|
||||
var accountMeta = {
|
||||
creation_host: os.hostname()
|
||||
, creation_dt: new Date().toISOString()
|
||||
};
|
||||
|
||||
return mkdirpAsync(accountDir).then(function () {
|
||||
|
||||
// TODO abstract file writing
|
||||
return PromiseA.all([
|
||||
// meta.json {"creation_host": "ns1.redirect-www.org", "creation_dt": "2015-12-11T04:14:38Z"}
|
||||
fs.writeFileAsync(path.join(accountDir, 'meta.json'), JSON.stringify(accountMeta), 'utf8')
|
||||
// private_key.json { "e", "d", "n", "q", "p", "kty", "qi", "dp", "dq" }
|
||||
, fs.writeFileAsync(path.join(accountDir, 'private_key.json'), JSON.stringify(reg.keypair.privateKeyJwk), 'utf8')
|
||||
// regr.json:
|
||||
/*
|
||||
{ body: { contact: [ 'mailto:coolaj86@gmail.com' ],
|
||||
agreement: 'https://letsencrypt.org/documents/LE-SA-v1.0.1-July-27-2015.pdf',
|
||||
key: { e: 'AQAB', kty: 'RSA', n: '...' } },
|
||||
uri: 'https://acme-v01.api.letsencrypt.org/acme/reg/71272',
|
||||
new_authzr_uri: 'https://acme-v01.api.letsencrypt.org/acme/new-authz',
|
||||
terms_of_service: 'https://letsencrypt.org/documents/LE-SA-v1.0.1-July-27-2015.pdf' }
|
||||
*/
|
||||
, fs.writeFileAsync(path.join(accountDir, 'regr.json'), JSON.stringify({ body: reg.receipt }), 'utf8')
|
||||
]);
|
||||
}).then(function () {
|
||||
return {
|
||||
id: accountId
|
||||
, accountId: accountId
|
||||
, email: args.email
|
||||
, keypair: reg.keypair
|
||||
, receipt: reg.receipt
|
||||
};
|
||||
});
|
||||
}
|
||||
// Accounts
|
||||
, getAccountIdAsync: function (args) {
|
||||
var pyconf = PromiseA.promisifyAll(require('pyconf'));
|
||||
|
||||
return pyconf.readFileAsync(args.renewalPath).then(function (renewal) {
|
||||
var accountId = renewal.account;
|
||||
renewal = renewal.account;
|
||||
|
||||
return accountId;
|
||||
}, function (err) {
|
||||
if ("ENOENT" === err.code) {
|
||||
return store.accounts._getAccountIdByEmail(args);
|
||||
}
|
||||
|
||||
return PromiseA.reject(err);
|
||||
});
|
||||
}
|
||||
}
|
||||
|
||||
//
|
||||
// Configs
|
||||
//
|
||||
, configs: {
|
||||
// Configs
|
||||
checkAsync: function (copy) {
|
||||
copy.domains = [];
|
||||
|
||||
return store.configs._checkHelperAsync(copy).then(function (pyobj) {
|
||||
var exists = pyobj.checkpoints >= 0;
|
||||
if (!exists) {
|
||||
return null;
|
||||
}
|
||||
|
||||
return pyToJson(pyobj);
|
||||
});
|
||||
}
|
||||
// Configs
|
||||
, _checkHelperAsync: function (args) {
|
||||
var pyconf = PromiseA.promisifyAll(require('pyconf'));
|
||||
|
||||
return pyconf.readFileAsync(args.renewalPath).then(function (pyobj) {
|
||||
return pyobj;
|
||||
}, function () {
|
||||
return pyconf.readFileAsync(path.join(__dirname, 'renewal.conf.tpl')).then(function (pyobj) {
|
||||
return pyobj;
|
||||
});
|
||||
});
|
||||
}
|
||||
// Configs
|
||||
, getAsync: function (args) {
|
||||
return store.configs._checkHelperAsync(args).then(function (pyobj) {
|
||||
var minver = pyobj.checkpoints >= 0;
|
||||
|
||||
args.pyobj = pyobj;
|
||||
|
||||
if (!minver) {
|
||||
args.checkpoints = 0;
|
||||
pyobj.checkpoints = 0;
|
||||
return writeRenewalConfig(args);
|
||||
}
|
||||
|
||||
// args.account.id = pyobj.account
|
||||
// args.configDir = args.configDir || pyobj.configDir;
|
||||
|
||||
args.checkpoints = pyobj.checkpoints;
|
||||
|
||||
args.agreeTos = (args.agreeTos || pyobj.tos) && true;
|
||||
args.email = args.email || pyobj.email;
|
||||
args.domains = args.domains || pyobj.domains;
|
||||
|
||||
// yes, it's an array. weird, right?
|
||||
args.webrootPath = args.webrootPath || pyobj.webrootPath[0];
|
||||
args.server = args.server || args.acmeDiscoveryUrl || pyobj.server;
|
||||
|
||||
args.certPath = args.certPath || pyobj.cert;
|
||||
args.privkeyPath = args.privkeyPath || pyobj.privkey;
|
||||
args.chainPath = args.chainPath || pyobj.chain;
|
||||
args.fullchainPath = args.fullchainPath || pyobj.fullchain;
|
||||
|
||||
//, workDir: args.workDir
|
||||
//, logsDir: args.logsDir
|
||||
args.rsaKeySize = args.rsaKeySize || pyobj.rsaKeySize;
|
||||
args.http01Port = args.http01Port || pyobj.http01Port;
|
||||
args.domainKeyPath = args.domainKeyPath || args.keyPath || pyobj.keyPath;
|
||||
|
||||
return writeRenewalConfig(args);
|
||||
});
|
||||
}
|
||||
// Configs
|
||||
, allAsync: function (copy) {
|
||||
copy.domains = [];
|
||||
|
||||
return fs.readdirAsync(copy.renewalDir).then(function (nodes) {
|
||||
nodes = nodes.filter(function (node) {
|
||||
return /^[a-z0-9]+.*\.conf$/.test(node);
|
||||
});
|
||||
|
||||
return PromiseA.all(nodes.map(function (node) {
|
||||
copy.domains = [node.replace(/\.conf$/, '')];
|
||||
return store.configs.getAsync(copy);
|
||||
}));
|
||||
});
|
||||
}
|
||||
}
|
||||
|
||||
};
|
||||
|
||||
return store;
|
||||
};
|
||||
251
lib/logger.js
Normal file
251
lib/logger.js
Normal file
@@ -0,0 +1,251 @@
|
||||
/* jshint -W097 */
|
||||
/* jshint strict: false */
|
||||
/*jslint node: true */
|
||||
'use strict';
|
||||
|
||||
var winston = require('winston');
|
||||
var DailyRotateFile = require('winston-daily-rotate-file');
|
||||
var fs = require('fs');
|
||||
var path = require('path');
|
||||
var os = require('os');
|
||||
var tools = require(__dirname + '/tools.js');
|
||||
var SysLog;
|
||||
var hostname = tools.getHostName();
|
||||
|
||||
try {
|
||||
SysLog = require('winston-syslog').Syslog;
|
||||
} catch (ex) {
|
||||
//console.log('No syslog support');
|
||||
}
|
||||
|
||||
var logger = function (level, files, noStdout, prefix) {
|
||||
var userOptions = {};
|
||||
var options = {
|
||||
transports: []
|
||||
};
|
||||
|
||||
//var defaultMaxSize;// = 10 * 1024 * 1024;
|
||||
|
||||
if (typeof files === 'string') {
|
||||
files = [files];
|
||||
}
|
||||
|
||||
files = files || [];
|
||||
|
||||
var isNpm = (__dirname.replace(/\\/g, '/').toLowerCase().indexOf('node_modules/' + tools.appName.toLowerCase() + '.js-controller') !== -1);
|
||||
|
||||
if (typeof level === 'object') {
|
||||
userOptions = Object.assign({}, level);
|
||||
|
||||
level = userOptions.level;
|
||||
prefix = userOptions.prefix;
|
||||
noStdout = userOptions.noStdout;
|
||||
|
||||
if (userOptions.prefix !== undefined) delete userOptions.prefix;
|
||||
|
||||
if (userOptions.transport) {
|
||||
var fName = 0;
|
||||
for (var f in userOptions.transport) {
|
||||
if (!userOptions.transport.hasOwnProperty(f)) continue;
|
||||
if (userOptions.transport[f].type === 'file' && userOptions.transport[f].enabled !== false) {
|
||||
|
||||
userOptions.transport[f].filename = userOptions.transport[f].filename || 'log/' + tools.appName;
|
||||
|
||||
if (!userOptions.transport[f].fileext && userOptions.transport[f].filename.indexOf('.log') === -1) {
|
||||
userOptions.transport[f].fileext = '.log';
|
||||
}
|
||||
|
||||
if (!fName) userOptions.transport[f].systemLog = true;
|
||||
|
||||
userOptions.transport[f].handleExceptions = false;
|
||||
userOptions.transport[f].name = !fName ? tools.appName : 'dailyRotateFile' + fName;
|
||||
fName++;
|
||||
userOptions.transport[f].filename = userOptions.transport[f].filename.replace(/\\/g, '/');
|
||||
if (userOptions.transport[f].filename.match(/^\w:\/|^\//)) {
|
||||
userOptions.transport[f].filename = path.normalize(userOptions.transport[f].filename);
|
||||
} else {
|
||||
userOptions.transport[f].filename = path.normalize(__dirname + (isNpm ? '/../../../' : '/../') + userOptions.transport[f].filename);
|
||||
}
|
||||
|
||||
userOptions.transport[f].label = prefix || '';
|
||||
userOptions.transport[f].level = userOptions.transport[f].level || level;
|
||||
userOptions.transport[f].json = (userOptions.transport[f].json !== undefined) ? userOptions.transport[f].json : false;
|
||||
userOptions.transport[f].silent = (userOptions.transport[f].silent !== undefined) ? userOptions.transport[f].silent : false;
|
||||
userOptions.transport[f].colorize = (userOptions.transport[f].colorize !== undefined) ? userOptions.transport[f].colorize : ((userOptions.colorize === undefined) ? true : userOptions.colorize);
|
||||
userOptions.transport[f].localTime = (userOptions.transport[f].localTime !== undefined) ? userOptions.transport[f].localTime : ((userOptions.localTime === undefined) ? true : userOptions.localTime);
|
||||
// userOptions.transport[f].maxsize = (userOptions.transport[f].maxsize !== undefined) ? userOptions.transport[f].maxsize : defaultMaxSize;
|
||||
userOptions.transport[f].timestamp = timestamp;
|
||||
userOptions.transport[f].datePattern = '.yyyy-MM-dd' + (userOptions.transport[f].fileext || '');
|
||||
/*userOptions.transport[f].logException = function (message, info, next, err) {
|
||||
console.error(message);
|
||||
};*/
|
||||
|
||||
var _log = new DailyRotateFile(userOptions.transport[f]);
|
||||
options.transports.push(_log);
|
||||
} else if (userOptions.transport[f].type === 'syslog' && userOptions.transport[f].enabled !== false) {
|
||||
if (!SysLog) {
|
||||
console.error('Syslog configured, but not installed!');
|
||||
continue;
|
||||
}
|
||||
// host: The host running syslogd, defaults to localhost.
|
||||
// port: The port on the host that syslog is running on, defaults to syslogd's default port.
|
||||
// protocol: The network protocol to log over (e.g. tcp4, udp4, unix, unix-connect, etc).
|
||||
// path: The path to the syslog dgram socket (i.e. /dev/log or /var/run/syslog for OS X).
|
||||
// pid: PID of the process that log messages are coming from (Default process.pid).
|
||||
// facility: Syslog facility to use (Default: local0).
|
||||
// localhost: Host to indicate that log messages are coming from (Default: localhost).
|
||||
// sysLogType: The type of the syslog protocol to use (Default: BSD).
|
||||
// app_name: The name of the application (Default: process.title).
|
||||
// eol: The end of line character to be added to the end of the message (Default: Message without modifications).
|
||||
// replace the used by syslog attribute "type" with own "sysLogType"
|
||||
|
||||
// If no name defined, use hostname as name
|
||||
userOptions.transport[f].localhost = userOptions.transport[f].localhost || hostname;
|
||||
|
||||
if (userOptions.transport[f].sysLogType) {
|
||||
userOptions.transport[f].type = userOptions.transport[f].sysLogType;
|
||||
delete userOptions.transport[f].sysLogType;
|
||||
} else {
|
||||
delete userOptions.transport[f].type;
|
||||
}
|
||||
try {
|
||||
options.transports.push(new SysLog(userOptions.transport[f]));
|
||||
} catch (err) {
|
||||
console.log('Cannot activate syslog: ' + err);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
} else {
|
||||
for (var i = 0; i < files.length; i++) {
|
||||
var opt = {
|
||||
name: !i ? tools.appName : 'dailyRotateFile' + i,
|
||||
filename: path.normalize(isNpm ? __dirname + '/../../../log/' + files[i] : __dirname + '/../log/' + files[i]),
|
||||
datePattern: '.yyyy-MM-dd.log',
|
||||
json: false, // If true, messages will be logged as JSON (default true).
|
||||
level: level,
|
||||
silent: false,
|
||||
localTime: true,
|
||||
colorize: (userOptions.colorize === undefined) ? true : userOptions.colorize,
|
||||
timestamp: timestamp,
|
||||
label: prefix || '',
|
||||
handleExceptions: false
|
||||
//maxsize: defaultMaxSize
|
||||
};
|
||||
|
||||
options.transports.push(new DailyRotateFile(opt));
|
||||
}
|
||||
}
|
||||
|
||||
if (!noStdout) {
|
||||
options.transports.push(new winston.transports.Console({
|
||||
level: level,
|
||||
silent: false,
|
||||
colorize: (userOptions.colorize === undefined) ? true : userOptions.colorize,
|
||||
timestamp: timestamp,
|
||||
label: prefix || ''
|
||||
}));
|
||||
}
|
||||
|
||||
var log = new winston.Logger(options);
|
||||
|
||||
log.getFileName = function () {
|
||||
if (this.transports && this.transports[tools.appName]) {
|
||||
if (this.transports[tools.appName].filename) {
|
||||
return this.transports[tools.appName].dirname + '/' + this.transports[tools.appName].filename;
|
||||
} else if (this.transports[tools.appName]._getFilename) {
|
||||
return this.transports[tools.appName].dirname + '/' + this.transports[tools.appName]._getFilename();
|
||||
} else {
|
||||
return '';
|
||||
}
|
||||
} else {
|
||||
return '';
|
||||
}
|
||||
};
|
||||
|
||||
log.activateDateChecker = function (isEnabled, daysCount) {
|
||||
if (!isEnabled && this._fileChecker) {
|
||||
clearInterval(this._fileChecker);
|
||||
} else if (isEnabled && !this._fileChecker) {
|
||||
if (!daysCount) daysCount = 3;
|
||||
|
||||
// Check every hour
|
||||
this._fileChecker = setInterval(function () {
|
||||
if (this.transports[tools.appName] && fs.existsSync(this.transports[tools.appName].dirname)) {
|
||||
var files = fs.readdirSync(this.transports[tools.appName].dirname);
|
||||
var for3days = new Date();
|
||||
for3days.setDate(for3days.getDate() - daysCount);
|
||||
|
||||
for (var i = 0; i < files.length; i++) {
|
||||
var match = files[i].match(/.+\.(\d+-\d+-\d+)/);
|
||||
if (match) {
|
||||
var date = new Date(match[1]);
|
||||
if (date < for3days) {
|
||||
// delete file
|
||||
try {
|
||||
this.transports[tools.appName].log('info', 'host.' + hostname + ' Delete log file ' + files[i]);
|
||||
fs.unlinkSync(this.transports[tools.appName].dirname + '/' + files[i]);
|
||||
} catch (e) {
|
||||
// there is a bug under windows, that file stays opened and cannot be deleted
|
||||
this.log(os.platform().match(/^win/) ? 'info' : 'error', 'host.' + hostname + ' Cannot delete file "' + path.normalize(this.transports[tools.appName].dirname + '/' + files[i]) + '": ' + e);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}.bind(this), 3600000); // every hour
|
||||
}
|
||||
};
|
||||
|
||||
winston.unhandleExceptions();
|
||||
|
||||
return log;
|
||||
};
|
||||
|
||||
function timestamp() {
|
||||
var ts = new Date();
|
||||
var result = ts.getFullYear() + '-';
|
||||
|
||||
/** @type {number | string} */
|
||||
var value = ts.getMonth() + 1;
|
||||
if (value < 10) value = '0' + value;
|
||||
result += value + '-';
|
||||
|
||||
value = ts.getDate();
|
||||
if (value < 10) value = '0' + value;
|
||||
result += value + ' ';
|
||||
|
||||
value = ts.getHours();
|
||||
if (value < 10) value = '0' + value;
|
||||
result += value + ':';
|
||||
|
||||
value = ts.getMinutes();
|
||||
if (value < 10) value = '0' + value;
|
||||
result += value + ':';
|
||||
|
||||
value = ts.getSeconds();
|
||||
if (value < 10) value = '0' + value;
|
||||
result += value + '.';
|
||||
|
||||
|
||||
value = ts.getMilliseconds();
|
||||
if (value < 10) {
|
||||
value = '00' + value;
|
||||
} else
|
||||
if (value < 100) {
|
||||
value = '0' + value;
|
||||
}
|
||||
|
||||
result += value + ' ';
|
||||
|
||||
return result;
|
||||
}
|
||||
|
||||
module.exports = logger;
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
215
lib/multihostClient.js
Normal file
215
lib/multihostClient.js
Normal file
@@ -0,0 +1,215 @@
|
||||
'use strict';
|
||||
|
||||
var dgram = require('dgram');
|
||||
var crypto = null;
|
||||
var port = 50005;
|
||||
var MULTICAST_ADDR = '239.255.255.250';
|
||||
|
||||
function MHClient(hostname, logger, config, info) {
|
||||
var id = 1;
|
||||
var server;
|
||||
var timer;
|
||||
|
||||
function getIPs() {
|
||||
var ifaces = require('os').networkInterfaces();
|
||||
var ipArr = [];
|
||||
for (var dev in ifaces) {
|
||||
if (!ifaces.hasOwnProperty(dev)) continue;
|
||||
|
||||
/*jshint loopfunc:true */
|
||||
ifaces[dev].forEach(function (details) {
|
||||
//noinspection JSUnresolvedVariable
|
||||
if (!details.internal) ipArr.push(details.address);
|
||||
});
|
||||
}
|
||||
|
||||
return ipArr;
|
||||
}
|
||||
|
||||
function stopServer() {
|
||||
if (server) {
|
||||
try {
|
||||
server.close();
|
||||
} catch (e) {
|
||||
|
||||
}
|
||||
server = null;
|
||||
}
|
||||
|
||||
if (timer) {
|
||||
clearTimeout(timer);
|
||||
timer = null;
|
||||
}
|
||||
}
|
||||
|
||||
function sha(secret, salt, callback) {
|
||||
// calculate sha256
|
||||
crypto = crypto || require('crypto');
|
||||
var hash = crypto.createHash('sha256');
|
||||
|
||||
hash.on('readable', function () {
|
||||
var data = hash.read();
|
||||
if (data) {
|
||||
callback(data.toString('hex'));
|
||||
}
|
||||
});
|
||||
|
||||
hash.write(secret + salt);
|
||||
hash.end();
|
||||
}
|
||||
|
||||
function startServer(isBroadcast, timeout, onReady, onMessage, onFinished) {
|
||||
if (server) {
|
||||
onFinished('Some operation still active');
|
||||
return;
|
||||
}
|
||||
server = dgram.createSocket('udp4');
|
||||
timeout = parseInt(timeout, 10) || 2000;
|
||||
|
||||
timer = setTimeout(function () {
|
||||
stopServer();
|
||||
|
||||
if (onFinished) {
|
||||
onFinished(null);
|
||||
onFinished = null;
|
||||
}
|
||||
|
||||
}, timeout);
|
||||
|
||||
server.on('error', function (err) {
|
||||
stopServer();
|
||||
|
||||
if (onFinished) {
|
||||
onFinished(err);
|
||||
onFinished = null;
|
||||
}
|
||||
});
|
||||
|
||||
server.on('message', function (msg, rinfo) {
|
||||
msg = msg.toString();
|
||||
try {
|
||||
msg = JSON.parse(msg);
|
||||
if (onMessage) {
|
||||
if (onMessage(server, msg, rinfo)) {
|
||||
stopServer();
|
||||
onFinished = null;
|
||||
}
|
||||
}
|
||||
} catch (e) {
|
||||
console.error('Invalid answer: ' + msg);
|
||||
}
|
||||
});
|
||||
|
||||
server.on('listening', function (msg, rinfo) {
|
||||
// var address = server.address();
|
||||
if (isBroadcast) server.setBroadcast(true);
|
||||
onReady && onReady(server);
|
||||
});
|
||||
|
||||
server.bind();
|
||||
}
|
||||
|
||||
this.browse = function (timeout, isDebug, callback) {
|
||||
var result = [];
|
||||
var ownIps = getIPs();
|
||||
|
||||
startServer(true, timeout,
|
||||
function onReady(srv) {
|
||||
var text = JSON.stringify({
|
||||
cmd: 'browse',
|
||||
id: ++id
|
||||
});
|
||||
server.send(text, 0, text.length, port, MULTICAST_ADDR);
|
||||
},
|
||||
function onMessage (srv, msg, rinfo) {
|
||||
// ignore own answers
|
||||
if (isDebug || rinfo.address !== '127.0.0.1' && ownIps.indexOf(rinfo.address) === -1) {
|
||||
if (msg.result === 'not authenticated') {
|
||||
result.push({ip: rinfo.address, hostname: rinfo.address, info: 'authentication required', auth: msg.auth});
|
||||
} else if (msg.result === 'ok') {
|
||||
result.push(msg);
|
||||
} else {
|
||||
console.log('Unknown answer: ' + JSON.stringify(msg));
|
||||
}
|
||||
}
|
||||
if (isDebug) {
|
||||
console.log(JSON.stringify(msg));
|
||||
}
|
||||
},
|
||||
function onFinished(err) {
|
||||
callback(err, result);
|
||||
}
|
||||
);
|
||||
};
|
||||
|
||||
this.connect = function (ip, password, callback) {
|
||||
startServer(false, 2000,
|
||||
function onReady(srv) {
|
||||
var text = JSON.stringify({
|
||||
cmd: 'browse',
|
||||
id: ++id
|
||||
});
|
||||
server.send(text, 0, text.length, port, ip);
|
||||
},
|
||||
function onMessage (srv, msg, rinfo) {
|
||||
if (msg.cmd === 'browse' && msg.id === id) {
|
||||
if (msg.result === 'ok') {
|
||||
if (callback) {
|
||||
if (!msg.objects) {
|
||||
if (callback) {
|
||||
callback('Invalid configuration received: ' + JSON.stringify(msg));
|
||||
callback = null;
|
||||
}
|
||||
} else if (!msg.states) {
|
||||
if (callback) {
|
||||
callback('Invalid configuration received: ' + JSON.stringify(msg));
|
||||
callback = null;
|
||||
}
|
||||
} else {
|
||||
callback && callback(null, msg.objects, msg.states, rinfo.address);
|
||||
}
|
||||
}
|
||||
} else if (msg.result === 'not authenticated') {
|
||||
if (!password) {
|
||||
if (callback) {
|
||||
callback('not authenticated' + msg);
|
||||
callback = null;
|
||||
}
|
||||
} else {
|
||||
sha(password, msg.salt, function (shaText) {
|
||||
// send password
|
||||
var text = JSON.stringify({
|
||||
cmd: 'browse',
|
||||
id: ++id,
|
||||
password: shaText
|
||||
});
|
||||
server.send(text, 0, text.length, port, ip);
|
||||
});
|
||||
return false;
|
||||
}
|
||||
} else if (msg.result === 'invalid password') {
|
||||
if (callback) {
|
||||
callback('invalid password');
|
||||
callback = null;
|
||||
}
|
||||
} else {
|
||||
console.log(msg.result);
|
||||
}
|
||||
return true;
|
||||
} else {
|
||||
console.warn('Unexpected message: ' + JSON.stringify(msg));
|
||||
}
|
||||
},
|
||||
function onFinished(err) {
|
||||
if (callback) {
|
||||
callback(err);
|
||||
callback = null;
|
||||
}
|
||||
}
|
||||
);
|
||||
};
|
||||
|
||||
return this;
|
||||
}
|
||||
|
||||
module.exports = MHClient;
|
||||
275
lib/multihostServer.js
Normal file
275
lib/multihostServer.js
Normal file
@@ -0,0 +1,275 @@
|
||||
'use strict';
|
||||
var dgram = require('dgram');
|
||||
var port = 50005;
|
||||
var MULTICAST_ADDR = '239.255.255.250';
|
||||
|
||||
function MHServer(hostname, logger, config, info, ips, secret) {
|
||||
var server = null;
|
||||
var count = 0;
|
||||
var initTimer = null;
|
||||
var buffer = {};
|
||||
var lastFrame = {};
|
||||
var crypto;
|
||||
|
||||
var that = this;
|
||||
var authList = {};
|
||||
config = Object.assign({}, config); // make a copy
|
||||
|
||||
if (config.objects) {
|
||||
config.objects = {
|
||||
type: config.objects.type,
|
||||
host: config.objects.host,
|
||||
port: config.objects.port,
|
||||
user: config.objects.user,
|
||||
pass: config.objects.pass
|
||||
};
|
||||
}
|
||||
if (config.states) {
|
||||
config.states = {
|
||||
type: config.states.type,
|
||||
host: config.states.host,
|
||||
port: config.states.port,
|
||||
user: config.states.user,
|
||||
pass: config.states.pass,
|
||||
options: config.states.options,
|
||||
maxQueue: config.states.maxQueue
|
||||
};
|
||||
}
|
||||
|
||||
function send(msg, rinfo) {
|
||||
if (server) {
|
||||
setImmediate(function () {
|
||||
var text = JSON.stringify(msg);
|
||||
try {
|
||||
server.send(text, 0, text.length, rinfo.port, rinfo.address);
|
||||
} catch (e) {
|
||||
logger.warn('host.' + hostname + ' cannot send answer to ' + rinfo.address + ':' + rinfo.port + ': ' + e);
|
||||
}
|
||||
});
|
||||
}
|
||||
}
|
||||
|
||||
// delete all old connections
|
||||
function checkAuthList(ts) {
|
||||
ts = ts || new Date().getTime();
|
||||
for (var id in authList) {
|
||||
if (authList.hasOwnProperty(id)) {
|
||||
if (!authList[id]) {
|
||||
delete authList[id];
|
||||
} else if (ts - authList[id].ts > 31000) {
|
||||
delete authList[id];
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
function isSlave(oHost, sHots, ownIps) {
|
||||
return !(oHost === 'localhost' || oHost === '127.0.0.1' || ownIps.indexOf(oHost) !== -1);
|
||||
}
|
||||
|
||||
function sha(secret, salt, callback) {
|
||||
// calculate sha256
|
||||
crypto = crypto || require('crypto');
|
||||
var hash = crypto.createHash('sha256');
|
||||
|
||||
hash.on('readable', function () {
|
||||
var data = hash.read();
|
||||
if (data) {
|
||||
callback(data.toString('hex'));
|
||||
}
|
||||
});
|
||||
|
||||
hash.write(secret + salt);
|
||||
hash.end();
|
||||
}
|
||||
|
||||
// hello => auth => browse
|
||||
function process(msg, rinfo) {
|
||||
if (!msg) return;
|
||||
|
||||
var ts = new Date().getTime();
|
||||
checkAuthList(ts);
|
||||
|
||||
var id = rinfo.address + ':' + rinfo.port;
|
||||
|
||||
switch (msg.cmd) {
|
||||
case 'browse':
|
||||
if (secret && msg.password && authList[id]) {
|
||||
sha(secret, authList[id].salt, function (shaText) {
|
||||
if (shaText !== msg.password) {
|
||||
send({
|
||||
auth: config.multihostService.secure,
|
||||
cmd: msg.cmd,
|
||||
id: msg.id,
|
||||
result: 'invalid password'
|
||||
}, rinfo);
|
||||
} else {
|
||||
authList[id].auth = true;
|
||||
send({
|
||||
auth: config.multihostService.secure,
|
||||
cmd: msg.cmd,
|
||||
id: msg.id,
|
||||
objects: config.objects,
|
||||
states: config.states,
|
||||
info: info,
|
||||
hostname: hostname,
|
||||
slave: isSlave(config.objects.host, config.states.host, ips),
|
||||
result: 'ok'
|
||||
}, rinfo);
|
||||
}
|
||||
});
|
||||
return;
|
||||
}
|
||||
|
||||
if (!config.multihostService.secure || (authList[id] && authList[id].auth)) {
|
||||
send({
|
||||
auth: config.multihostService.secure,
|
||||
cmd: msg.cmd,
|
||||
id: msg.id,
|
||||
objects: config.objects,
|
||||
states: config.states,
|
||||
info: info,
|
||||
hostname: hostname,
|
||||
slave: isSlave(config.objects.host, config.states.host, ips),
|
||||
result: 'ok'
|
||||
}, rinfo);
|
||||
} else {
|
||||
authList[id] = {
|
||||
time: ts,
|
||||
salt: (Math.random() * 1000000 + ts).toString().substring(0, 16),
|
||||
auth: false
|
||||
};
|
||||
// padding
|
||||
if (authList[id].salt.length < 16) {
|
||||
authList[id].salt += new Array(16 - authList[id].salt.length).join('_');
|
||||
}
|
||||
send({
|
||||
auth: config.multihostService.secure,
|
||||
cmd: msg.cmd,
|
||||
id: msg.id,
|
||||
result: 'not authenticated',
|
||||
salt: authList[id].salt
|
||||
}, rinfo);
|
||||
}
|
||||
break;
|
||||
|
||||
default:
|
||||
send({
|
||||
cmd: msg.cmd,
|
||||
id: msg.id,
|
||||
result: 'unknown command'
|
||||
}, rinfo);
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
this.init = function () {
|
||||
if (initTimer) {
|
||||
clearTimeout(initTimer);
|
||||
initTimer = null;
|
||||
}
|
||||
|
||||
if (count > 10) {
|
||||
logger.warn('host.' + hostname + ' Port ' + port + ' is occupied. Service stopped.');
|
||||
return;
|
||||
}
|
||||
|
||||
server = dgram.createSocket('udp4');
|
||||
|
||||
server.on('error', function (err) {
|
||||
logger.error('host.' + hostname + ' multihost service error: ' + err.stack);
|
||||
server.close();
|
||||
server = null;
|
||||
|
||||
if (!initTimer) {
|
||||
initTimer = setTimeout(function () {
|
||||
initTimer = null;
|
||||
that.init();
|
||||
}, 5000);
|
||||
}
|
||||
});
|
||||
|
||||
server.on('close', function (err) {
|
||||
server = null;
|
||||
|
||||
if (!initTimer) {
|
||||
initTimer = setTimeout(function () {
|
||||
initTimer = null;
|
||||
that.init();
|
||||
}, 5000);
|
||||
}
|
||||
});
|
||||
|
||||
server.on('message', function (msg, rinfo) {
|
||||
// following messages are allowed
|
||||
var text = msg.toString();
|
||||
var now = new Date().getTime();
|
||||
var id = rinfo.address + ':' + rinfo.port;
|
||||
|
||||
for (var ids in buffer) {
|
||||
if (!lastFrame[ids]) {
|
||||
delete buffer[ids];
|
||||
} else if (now - lastFrame[ids] > 1000) {
|
||||
delete buffer[ids];
|
||||
delete lastFrame[ids];
|
||||
}
|
||||
}
|
||||
|
||||
if (lastFrame[id] && now - lastFrame[id] > 1000) {
|
||||
buffer[id] = '';
|
||||
}
|
||||
|
||||
lastFrame[id] = now;
|
||||
|
||||
if (!buffer[id] && text[0] !== '{') {
|
||||
// ignore message
|
||||
logger.warn('host.' + hostname + ' Message ignored: ' + text);
|
||||
} else {
|
||||
buffer[id] = (buffer[id] || '') + msg.toString();
|
||||
if (buffer[id] && buffer[id][buffer[id].length - 1] === '}') {
|
||||
try {
|
||||
var data = JSON.parse(buffer[id]);
|
||||
buffer[id] = '';
|
||||
if (data) {
|
||||
process(data, rinfo);
|
||||
}
|
||||
} catch (e) {
|
||||
// may be not yet complete.
|
||||
}
|
||||
}
|
||||
}
|
||||
});
|
||||
|
||||
server.on('listening', function () {
|
||||
server.addMembership(MULTICAST_ADDR);
|
||||
var address = server.address();
|
||||
logger.warn('host.' + hostname + ' multihost service started on ' + address.address + ':' + address.port);
|
||||
});
|
||||
|
||||
server.bind(50005);
|
||||
};
|
||||
|
||||
this.close = function (callback) {
|
||||
if (initTimer) {
|
||||
clearTimeout(initTimer);
|
||||
initTimer = null;
|
||||
}
|
||||
if (server) {
|
||||
try {
|
||||
server.close(callback);
|
||||
server = null;
|
||||
} catch (e) {
|
||||
server = null;
|
||||
callback && callback()
|
||||
}
|
||||
} else if (callback) {
|
||||
callback();
|
||||
}
|
||||
};
|
||||
|
||||
this.init();
|
||||
|
||||
return this;
|
||||
}
|
||||
|
||||
module.exports = MHServer;
|
||||
13
lib/objects.js
Normal file
13
lib/objects.js
Normal file
@@ -0,0 +1,13 @@
|
||||
'use strict';
|
||||
|
||||
var getConfigFileName = require(__dirname + '/tools').getConfigFileName;
|
||||
var config = JSON.parse(require('fs').readFileSync(getConfigFileName(), 'utf8'));
|
||||
if (!config.objects) config.objects = {type: 'file'};
|
||||
|
||||
if (config.objects.type === 'file') {
|
||||
module.exports = require(__dirname + '/objects/objectsInMemClient');
|
||||
} else if (config.objects.type === 'redis') {
|
||||
module.exports = require(__dirname + '/objects/objectsInRedis');
|
||||
} else {
|
||||
throw 'Unknown objects type: ' + config.objects.type;
|
||||
}
|
||||
501
lib/objects/objectsInMemClient.js
Normal file
501
lib/objects/objectsInMemClient.js
Normal file
@@ -0,0 +1,501 @@
|
||||
/**
|
||||
* Object DB in memory - Client
|
||||
*
|
||||
* Copyright 2013-2018 bluefox <dogafox@gmail.com>
|
||||
*
|
||||
* MIT License
|
||||
*
|
||||
*/
|
||||
|
||||
/* jshint -W097 */
|
||||
/* jshint strict: false */
|
||||
/* jslint node: true */
|
||||
/* jshint -W061 */
|
||||
'use strict';
|
||||
|
||||
const io = require('socket.io-client');
|
||||
const util = require('util');
|
||||
const stream = require('stream');
|
||||
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 'No callback for WMStrm.prototype._write';
|
||||
cb();
|
||||
};
|
||||
|
||||
function ObjectsInMemClient(settings) {
|
||||
let client;
|
||||
let that = this;
|
||||
let subscribes = [];
|
||||
let connectionTimeout;
|
||||
|
||||
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;
|
||||
}
|
||||
|
||||
let __construct = (function () {
|
||||
if (!settings.connection.secure) {
|
||||
client = io.connect('http://' + (settings.connection.host !=='0.0.0.0' ? settings.connection.host || '127.0.0.1' : '127.0.0.1') + ':' + (settings.connection.port || 9001));
|
||||
} else {
|
||||
client = io.connect('https://' + (settings.connection.host !=='0.0.0.0' ? settings.connection.host || '127.0.0.1' : '127.0.0.1') + ':' + (settings.connection.port || 9001));
|
||||
}
|
||||
|
||||
if (typeof settings.change === 'function') {
|
||||
client.on('message', function (pattern, channel, message) {
|
||||
log.silly(settings.namespace + ' inMem message ', pattern, channel, message);
|
||||
try {
|
||||
settings.change(channel, message);
|
||||
} catch (e) {
|
||||
log.error(settings.namespace + ' message ' + channel + ' ' + message + ' ' + e.message);
|
||||
log.error(settings.namespace + ' ' + e.stack);
|
||||
}
|
||||
});
|
||||
}
|
||||
client.on('disconnect', function (error) {
|
||||
if (connectionTimeout) {
|
||||
clearTimeout(connectionTimeout);
|
||||
connectionTimeout = null;
|
||||
}
|
||||
if (typeof settings.disconnected === 'function') {
|
||||
settings.disconnected(error);
|
||||
}
|
||||
});
|
||||
client.on('error', function (error) {
|
||||
if (connectionTimeout) {
|
||||
clearTimeout(connectionTimeout);
|
||||
connectionTimeout = null;
|
||||
}
|
||||
if (typeof settings.disconnected === 'function') {
|
||||
settings.disconnected(error);
|
||||
} else {
|
||||
log.error(settings.namespace + ' ' + error.message);
|
||||
log.error(settings.namespace + ' ' + error.stack);
|
||||
}
|
||||
});
|
||||
client.on('connect', function (error) {
|
||||
if (connectionTimeout) {
|
||||
clearTimeout(connectionTimeout);
|
||||
connectionTimeout = null;
|
||||
}
|
||||
if (typeof settings.connected === 'function') settings.connected('InMemoryDB ' + settings.connection.host + ':' + settings.connection.port);
|
||||
});
|
||||
client.on('reconnect', function (error) {
|
||||
// Re-initialise subscribes
|
||||
for (let i = 0; i < subscribes.length; i++) {
|
||||
client.emit('subscribe', subscribes[i]);
|
||||
}
|
||||
if (typeof settings.connected === 'function') settings.connected('InMemoryDB ' + settings.connection.host + ':' + settings.connection.port);
|
||||
});
|
||||
connectionTimeout = setTimeout(function () {
|
||||
if (typeof settings.connectTimeout === 'function') settings.connectTimeout('Connection timeout');
|
||||
connectionTimeout = null;
|
||||
}, 5000);
|
||||
})();
|
||||
|
||||
settings = settings || {};
|
||||
|
||||
this.subscribe = function (pattern, options) {
|
||||
if (subscribes.indexOf(pattern) === -1) subscribes.push(pattern);
|
||||
if (!client) return;
|
||||
client.emit('subscribe', pattern, options);
|
||||
};
|
||||
|
||||
this.unsubscribe = function (pattern) {
|
||||
let pos = subscribes.indexOf(pattern);
|
||||
if (pos !== -1) subscribes.splice(pos, 1);
|
||||
|
||||
if (!client) return;
|
||||
client.emit('unsubscribe', pattern);
|
||||
};
|
||||
|
||||
this.destroy = function (callback) {
|
||||
if (!client) return;
|
||||
// Client may not close the DB
|
||||
if (callback) callback();
|
||||
//client.emit('destroy', callback);
|
||||
};
|
||||
|
||||
this.enableFileCache = function (enabled, options, callback) {
|
||||
if (typeof options === 'function') {
|
||||
callback = options;
|
||||
options = null;
|
||||
}
|
||||
if (!client) return;
|
||||
client.emit('enableFileCache', enabled, options, callback);
|
||||
};
|
||||
|
||||
this.insert = function (id, attName, ignore, options, obj, callback) {
|
||||
//return pipe for write into redis
|
||||
let strm = new WMStrm(id + '/' + attName);
|
||||
strm.on('finish', function () {
|
||||
if (!memStore[id + '/' + attName]) log.error(settings.namespace + ' 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) callback(null, null);
|
||||
});
|
||||
});
|
||||
return strm;
|
||||
};
|
||||
|
||||
this.writeFile = function (id, name, data, options, callback) {
|
||||
if (typeof options === 'function') {
|
||||
callback = options;
|
||||
options = null;
|
||||
}
|
||||
if (!client) return;
|
||||
client.emit('writeFile', id, name, data, options, callback);
|
||||
};
|
||||
|
||||
this.readFile = function (id, name, options, callback) {
|
||||
if (typeof options === 'function') {
|
||||
callback = options;
|
||||
options = null;
|
||||
}
|
||||
if (!client) return;
|
||||
client.emit('readFile', id, name, options, callback);
|
||||
};
|
||||
|
||||
this.readDir = function (id, name, options, callback) {
|
||||
if (typeof options === 'function') {
|
||||
callback = options;
|
||||
options = null;
|
||||
}
|
||||
if (!client) return;
|
||||
client.emit('readDir', id, name, options, callback);
|
||||
};
|
||||
|
||||
this.readDirAsZip = function (id, name, options, callback) {
|
||||
if (typeof options === 'function') {
|
||||
callback = options;
|
||||
options = null;
|
||||
}
|
||||
if (!client) return;
|
||||
client.emit('readDirAsZip', id, name, options, callback);
|
||||
};
|
||||
|
||||
this.writeDirAsZip = function (id, name, options, data, callback) {
|
||||
if (typeof data === 'function') {
|
||||
callback = data;
|
||||
data = options;
|
||||
options = null;
|
||||
}
|
||||
if (!client) return;
|
||||
client.emit('writeDirAsZip', id, name, options, data, callback);
|
||||
};
|
||||
|
||||
this.unlink = function (id, name, options, callback) {
|
||||
if (typeof options === 'function') {
|
||||
callback = options;
|
||||
options = null;
|
||||
}
|
||||
if (!client) return;
|
||||
client.emit('unlink', id, name, options, callback);
|
||||
};
|
||||
|
||||
this.delFile = this.unlink;
|
||||
|
||||
this.rename = function (id, oldName, newName, options, callback) {
|
||||
if (typeof options === 'function') {
|
||||
callback = options;
|
||||
options = null;
|
||||
}
|
||||
if (!client) return;
|
||||
client.emit('rename', id, oldName, newName, options, callback);
|
||||
};
|
||||
|
||||
this.mkdir = function (id, dirname, options, callback) {
|
||||
if (typeof options === 'function') {
|
||||
callback = options;
|
||||
options = null;
|
||||
}
|
||||
if (!client) return;
|
||||
client.emit('mkdir', id, dirname, options, callback);
|
||||
};
|
||||
|
||||
this.chmodFile = function (id, name, options, callback) {
|
||||
if (typeof options === 'function') {
|
||||
callback = options;
|
||||
options = null;
|
||||
}
|
||||
if (!client) return;
|
||||
client.emit('chmodFile', id, name, options, callback);
|
||||
};
|
||||
|
||||
this.chownFile = function (id, name, options, callback) {
|
||||
if (typeof options === 'function') {
|
||||
callback = options;
|
||||
options = null;
|
||||
}
|
||||
if (!client) return;
|
||||
client.emit('chownFile', id, name, options, callback);
|
||||
};
|
||||
|
||||
this.touch = function (id, name, options, callback) {
|
||||
if (typeof options === 'function') {
|
||||
callback = options;
|
||||
options = null;
|
||||
}
|
||||
if (!client) return;
|
||||
client.emit('touch', id, name, options, callback);
|
||||
};
|
||||
|
||||
this.rm = function (id, name, options, callback) {
|
||||
if (typeof options === 'function') {
|
||||
callback = options;
|
||||
options = null;
|
||||
}
|
||||
if (!client) return;
|
||||
client.emit('rm', id, name, options, callback);
|
||||
};
|
||||
|
||||
this.chmodObject = function (pattern, options, callback) {
|
||||
if (typeof options === 'function') {
|
||||
callback = options;
|
||||
options = null;
|
||||
}
|
||||
if (!client) return;
|
||||
client.emit('chmodObject', pattern, options, callback);
|
||||
};
|
||||
|
||||
this.chownObject = function (pattern, options, callback) {
|
||||
if (typeof options === 'function') {
|
||||
callback = options;
|
||||
options = null;
|
||||
}
|
||||
if (!client) return;
|
||||
client.emit('chownObject', pattern, options, callback);
|
||||
};
|
||||
|
||||
this.getObjectView = function (design, search, params, options, callback) {
|
||||
if (typeof options === 'function') {
|
||||
callback = options;
|
||||
options = null;
|
||||
}
|
||||
if (!client) return;
|
||||
client.emit('getObjectView', design, search, params, options, callback);
|
||||
};
|
||||
|
||||
this.getObjectList = function (params, options, callback) {
|
||||
if (typeof options === 'function') {
|
||||
callback = options;
|
||||
options = null;
|
||||
}
|
||||
if (!client) return;
|
||||
client.emit('getObjectList', params, options, callback);
|
||||
};
|
||||
|
||||
this.getObjectListAsync = (params, options) => {
|
||||
if (!client) return Promise.reject('No client');
|
||||
return new Promise((resolve, reject) => {
|
||||
this.getObjectList(params, options, (err, arr) => {
|
||||
if (err) {
|
||||
reject(err);
|
||||
} else {
|
||||
resolve(arr);
|
||||
}
|
||||
})
|
||||
});
|
||||
};
|
||||
|
||||
this.extendObject = function (id, obj, options, callback) {
|
||||
if (typeof options === 'function') {
|
||||
callback = options;
|
||||
options = null;
|
||||
}
|
||||
if (!client) return;
|
||||
client.emit('extendObject', id, obj, options, callback);
|
||||
};
|
||||
|
||||
this.extendObjectAsync = function (id, obj, options) {
|
||||
if (!client) return Promise.reject('No client');
|
||||
return new Promise((resolve, reject) => {
|
||||
this.extendObject(id, obj, options, err => {
|
||||
if (err) {
|
||||
reject(err);
|
||||
} else {
|
||||
resolve();
|
||||
}
|
||||
});
|
||||
});
|
||||
};
|
||||
|
||||
this.getKeys = function (pattern, options, callback, dontModify) {
|
||||
if (typeof options === 'function') {
|
||||
callback = options;
|
||||
options = null;
|
||||
}
|
||||
if (!client) return;
|
||||
client.emit('getKeys', pattern, options, callback, dontModify);
|
||||
};
|
||||
this.getConfigKeys = this.getKeys;
|
||||
|
||||
this.getObjects = function (keys, options, callback, dontModify) {
|
||||
if (typeof options === 'function') {
|
||||
callback = options;
|
||||
options = null;
|
||||
}
|
||||
if (!client) return;
|
||||
client.emit('getObjects', keys, options, callback, dontModify);
|
||||
};
|
||||
|
||||
this.getObjectsByPattern = (pattern, options, callback) => {
|
||||
if (typeof options === 'function') {
|
||||
callback = options;
|
||||
options = null;
|
||||
}
|
||||
if (!client) return;
|
||||
client.emit('getObjectsByPattern', pattern, options, callback);
|
||||
};
|
||||
|
||||
this.getObjectsByPatternAsync = (pattern, options) => {
|
||||
if (!client) return Promise.reject('No client');
|
||||
return new Promise((resolve, reject) => {
|
||||
this.getObjectsByPattern(pattern, options, (err, objs) => {
|
||||
if (err) {
|
||||
reject(err);
|
||||
} else {
|
||||
resolve(objs);
|
||||
}
|
||||
});
|
||||
});
|
||||
};
|
||||
|
||||
this.setObject = function (id, obj, options, callback) {
|
||||
if (typeof options === 'function') {
|
||||
callback = options;
|
||||
options = null;
|
||||
}
|
||||
if (!client) return;
|
||||
client.emit('setObject', id, obj, options, callback);
|
||||
};
|
||||
|
||||
this.setObjectAsync = function (id, obj, options) {
|
||||
if (!client) return Promise.reject('No client');
|
||||
return new Promise((resolve, reject) => {
|
||||
this.setObject(id, obj, options, err => {
|
||||
if (err) {
|
||||
reject(err);
|
||||
} else {
|
||||
resolve();
|
||||
}
|
||||
});
|
||||
});
|
||||
};
|
||||
|
||||
this.delObject = function (id, options, callback) {
|
||||
if (typeof options === 'function') {
|
||||
callback = options;
|
||||
options = null;
|
||||
}
|
||||
if (!client) return;
|
||||
client.emit('delObject', id, options, callback);
|
||||
};
|
||||
|
||||
this.delObjectAsync = function (id, options) {
|
||||
if (!client) return Promise.reject('No client');
|
||||
return new Promise((resolve, reject) => {
|
||||
this.delObject(id, options, err => {
|
||||
if (err) {
|
||||
reject(err);
|
||||
} else {
|
||||
resolve();
|
||||
}
|
||||
});
|
||||
});
|
||||
};
|
||||
|
||||
this.getObject = function (id, options, callback) {
|
||||
if (typeof options === 'function') {
|
||||
callback = options;
|
||||
options = null;
|
||||
}
|
||||
if (!client) return;
|
||||
client.emit('getObject', id, options, callback);
|
||||
};
|
||||
|
||||
this.getObjectAsync = function (id, options) {
|
||||
if (!client) return Promise.reject('No client');
|
||||
return new Promise((resolve, reject) => {
|
||||
this.getObject(id, options, (err, obj) => {
|
||||
if (err) {
|
||||
reject(err);
|
||||
} else {
|
||||
resolve(obj);
|
||||
}
|
||||
});
|
||||
});
|
||||
};
|
||||
|
||||
this.findObject = function (idOrName, type, options, callback) {
|
||||
if (typeof options === 'function') {
|
||||
callback = options;
|
||||
options = null;
|
||||
}
|
||||
if (!client) return;
|
||||
client.emit('findObject', idOrName, type, options, callback);
|
||||
};
|
||||
|
||||
this.findObjectAsync = function (idOrName, type, options) {
|
||||
if (!client) return Promise.reject('No client');
|
||||
return new Promise((resolve, reject) => {
|
||||
this.findObject(idOrName, type, options, (err, obj) => {
|
||||
if (err) {
|
||||
reject(err);
|
||||
} else {
|
||||
resolve(obj);
|
||||
}
|
||||
});
|
||||
});
|
||||
};
|
||||
|
||||
this.destroyDB = function (options, callback) {
|
||||
if (typeof options === 'function') {
|
||||
callback = options;
|
||||
options = null;
|
||||
}
|
||||
if (!client) return;
|
||||
client.emit('destroyDB', options, callback);
|
||||
};
|
||||
}
|
||||
|
||||
module.exports = ObjectsInMemClient;
|
||||
3169
lib/objects/objectsInMemServer.js
Normal file
3169
lib/objects/objectsInMemServer.js
Normal file
File diff suppressed because it is too large
Load Diff
1928
lib/objects/objectsInRedis.js
Normal file
1928
lib/objects/objectsInRedis.js
Normal file
File diff suppressed because it is too large
Load Diff
87
lib/password.js
Normal file
87
lib/password.js
Normal file
@@ -0,0 +1,87 @@
|
||||
/**
|
||||
*
|
||||
* password hash and check
|
||||
*
|
||||
* 7'2014-2016 Bluefox <dogafox@gmail.com>
|
||||
* 2014 hobbyquaker <hq@ccu.io>
|
||||
*
|
||||
* derived from https://github.com/florianheinemann/password-hash-and-salt/ (MIT License)
|
||||
*
|
||||
* The created hash is of the following format: <algorithm>$<iterations>$<hash>$<salt>
|
||||
*
|
||||
* Usage Example:
|
||||
|
||||
var password = require('./lib/password.js');
|
||||
|
||||
password('test').hash(null, null, function (err, res) {
|
||||
console.log(res);
|
||||
|
||||
password('test').check(res, function (err, res) {
|
||||
console.log('test: ' + res);
|
||||
});
|
||||
|
||||
password('muh').check(res, function (err, res) {
|
||||
console.log('muh: ' + res);
|
||||
});
|
||||
|
||||
});
|
||||
|
||||
*
|
||||
*/
|
||||
|
||||
/* jshint -W097 */
|
||||
/* jshint strict: false */
|
||||
/* jslint node: true */
|
||||
'use strict';
|
||||
|
||||
const crypto = require('crypto');
|
||||
let version = null;
|
||||
|
||||
const password = pw => {
|
||||
return {
|
||||
hash: (salt, iterations, callback) => {
|
||||
|
||||
salt = salt || crypto.randomBytes(16).toString('hex');
|
||||
iterations = iterations || 10000;
|
||||
|
||||
// version 0.10 has no 'sha256' and this option must be ignored
|
||||
if (version === null) {
|
||||
version = process.version.replace('v', '');
|
||||
version = !version.match(/^0\.10\./);
|
||||
}
|
||||
|
||||
if (version) {
|
||||
crypto.pbkdf2(pw, salt, iterations, 256, 'sha256', (err, key) => {
|
||||
if (err) return callback(err);
|
||||
|
||||
callback(null, `pbkdf2$${iterations}$${key.toString('hex')}$${salt}`);
|
||||
});
|
||||
} else {
|
||||
crypto.pbkdf2(pw, salt, iterations, 64, (err, key) => {
|
||||
if (err) return callback(err);
|
||||
|
||||
callback(null, `pbkdf2$${iterations}$${key.toString('hex')}$${salt}`);
|
||||
});
|
||||
|
||||
}
|
||||
},
|
||||
check: function (hashedPassword, callback) {
|
||||
if (!hashedPassword || !password) return callback(null, false);
|
||||
let key = hashedPassword.split('$');
|
||||
if (key.length !== 4 || !key[2] || !key[3]) return callback('Hash not formatted correctly');
|
||||
if (key[0] !== 'pbkdf2') return callback('Unknown');
|
||||
|
||||
this.hash(key[3], parseInt(key[1], 10), function (error, newHash) {
|
||||
if (error) return callback(error);
|
||||
callback(null, newHash === hashedPassword);
|
||||
});
|
||||
|
||||
},
|
||||
complexity: callback => {
|
||||
// Todo: Check for password complexity
|
||||
return true;
|
||||
}
|
||||
};
|
||||
};
|
||||
|
||||
module.exports = password;
|
||||
159
lib/preinstall_check.js
Normal file
159
lib/preinstall_check.js
Normal file
@@ -0,0 +1,159 @@
|
||||
'use strict';
|
||||
|
||||
// we cannot use semver here, because dependencies are not installed yet
|
||||
// so the version checks get a bit messy
|
||||
|
||||
const fs = require('fs');
|
||||
const path = require('path');
|
||||
const child_process = require('child_process');
|
||||
const os = require('os');
|
||||
|
||||
// where yunkong2 is installed
|
||||
const rootDir = __dirname.substr(0, __dirname.lastIndexOf('node_modules'));
|
||||
|
||||
|
||||
function checkNpmVersion() {
|
||||
// Get npm version
|
||||
try {
|
||||
// remove local node_modules\.bin dir from path
|
||||
// or we potentially get a wrong npm version
|
||||
let newEnv = Object.assign({}, process.env);
|
||||
newEnv.PATH = (newEnv.PATH || newEnv.Path || newEnv.path)
|
||||
.split(path.delimiter)
|
||||
.filter(dir => {
|
||||
dir = dir.toLowerCase();
|
||||
return !(dir.indexOf('yunkong2') > -1 && dir.indexOf(path.join('node_modules', '.bin')) > -1);
|
||||
|
||||
})
|
||||
.join(path.delimiter);
|
||||
|
||||
let npmVersion = child_process.execSync('npm -v', {encoding: 'utf8', env: newEnv});
|
||||
npmVersion = npmVersion.trim();
|
||||
console.log('NPM version: ' + npmVersion);
|
||||
|
||||
if (gte(npmVersion, '5.0.0') && lt(npmVersion, '5.7.1')) {
|
||||
console.warn('!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!');
|
||||
console.warn('WARNING:');
|
||||
console.warn('You are using an unsupported npm version!');
|
||||
console.warn('This can lead to problems when installing further packages');
|
||||
console.warn();
|
||||
console.warn('Please use "npm install -g npm@4" to downgrade npm to 4.x or ');
|
||||
console.warn('use "npm install -g npm@latest" to install a supported version of npm 5!');
|
||||
console.warn('You need to make sure to repeat this step after installing an update to NodeJS and/or npm.');
|
||||
console.warn('!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!');
|
||||
}
|
||||
return npmVersion;
|
||||
} catch (e) {
|
||||
console.error('Could not check npm version: ' + e);
|
||||
console.error('Assuming that correct version is installed.');
|
||||
process.exit(0);
|
||||
}
|
||||
}
|
||||
|
||||
if (gte(checkNpmVersion(), '5.0.0')) {
|
||||
// disables NPM's package-lock.json on NPM >= 5 because that creates heaps of problems
|
||||
console.log('npm version >= 5: disabling package-lock');
|
||||
fs.writeFileSync(path.join(rootDir, '.npmrc'), 'package-lock=false' + os.EOL, 'utf8');
|
||||
}
|
||||
|
||||
process.exit(0);
|
||||
|
||||
// ======================================
|
||||
// all the functions to replace `semver`:
|
||||
|
||||
/**
|
||||
* @typedef {{major: number, minor: number, build: number}} Version
|
||||
*/
|
||||
/**
|
||||
* Parses a version string
|
||||
* @param {string} version The version string to parse
|
||||
* @returns {Version | null} The parsed version
|
||||
*/
|
||||
function parseVersion(version) {
|
||||
const versionRegExp = /^v?(\d+)\.(\d+)\.(\d+).*?/;
|
||||
const parsed = versionRegExp.exec(version);
|
||||
if (!parsed) return null;
|
||||
|
||||
return {
|
||||
major: +parsed[1],
|
||||
minor: +parsed[2],
|
||||
build: +parsed[3]
|
||||
};
|
||||
}
|
||||
|
||||
/**
|
||||
* Checks if v1 > v2
|
||||
* @param {Version | string} v1
|
||||
* @param {Version | string} v2
|
||||
*/
|
||||
function gt(v1, v2) {
|
||||
if (typeof v1 === "string") v1 = parseVersion(v1);
|
||||
if (typeof v2 === "string") v2 = parseVersion(v2);
|
||||
|
||||
if (v1.major > v2.major) return true;
|
||||
else if (v1.major < v2.major) return false;
|
||||
|
||||
if (v1.minor > v2.minor) return true;
|
||||
else if (v1.minor < v2.minor) return false;
|
||||
|
||||
return (v1.build > v2.build);
|
||||
}
|
||||
|
||||
/**
|
||||
* Checks if v1 < v2
|
||||
* @param {Version | string} v1
|
||||
* @param {Version | string} v2
|
||||
*/
|
||||
function lt(v1, v2) {
|
||||
if (typeof v1 === "string") v1 = parseVersion(v1);
|
||||
if (typeof v2 === "string") v2 = parseVersion(v2);
|
||||
|
||||
if (v1.major < v2.major) return true;
|
||||
else if (v1.major > v2.major) return false;
|
||||
|
||||
if (v1.minor < v2.minor) return true;
|
||||
else if (v1.minor > v2.minor) return false;
|
||||
|
||||
return (v1.build < v2.build);
|
||||
}
|
||||
|
||||
/**
|
||||
* Checks if v1 == v2
|
||||
* @param {Version | string} v1
|
||||
* @param {Version | string} v2
|
||||
*/
|
||||
function eq(v1, v2) {
|
||||
if (typeof v1 === "string") v1 = parseVersion(v1);
|
||||
if (typeof v2 === "string") v2 = parseVersion(v2);
|
||||
|
||||
if (v1.major !== v2.major) return false;
|
||||
if (v1.minor !== v2.minor) return false;
|
||||
return v1.build === v2.build;
|
||||
}
|
||||
|
||||
/**
|
||||
* Checks if v1 != v2
|
||||
* @param {Version | string} v1
|
||||
* @param {Version | string} v2
|
||||
*/
|
||||
function ne(v1, v2) {
|
||||
return !eq(v1, v2);
|
||||
}
|
||||
|
||||
/**
|
||||
* Checks if v1 >= v2
|
||||
* @param {Version | string} v1
|
||||
* @param {Version | string} v2
|
||||
*/
|
||||
function gte(v1, v2) {
|
||||
return gt(v1, v2) || eq(v1, v2);
|
||||
}
|
||||
|
||||
/**
|
||||
* Checks if v1 <= v2
|
||||
* @param {Version | string} v1
|
||||
* @param {Version | string} v2
|
||||
*/
|
||||
function lte(v1, v2) {
|
||||
return lt(v1, v2) || eq(v1, v2);
|
||||
}
|
||||
68
lib/renewal.conf.tpl
Normal file
68
lib/renewal.conf.tpl
Normal file
@@ -0,0 +1,68 @@
|
||||
#cert = :configDir/live/:hostname/cert.pem
|
||||
cert = :cert_path
|
||||
privkey = :privkey_path
|
||||
chain = :chain_path
|
||||
fullchain = :fullchain_path
|
||||
|
||||
# Options and defaults used in the renewal process
|
||||
[renewalparams]
|
||||
apache_enmod = a2enmod
|
||||
no_verify_ssl = False
|
||||
ifaces = None
|
||||
apache_dismod = a2dismod
|
||||
register_unsafely_without_email = False
|
||||
uir = None
|
||||
installer = none
|
||||
config_dir = :configDir
|
||||
text_mode = True
|
||||
# junk?
|
||||
# https://github.com/letsencrypt/letsencrypt/issues/1955
|
||||
func = <function obtain_cert at 0x30c9500>
|
||||
prepare = False
|
||||
work_dir = :work_dir
|
||||
tos = :agree_tos
|
||||
init = False
|
||||
http01_port = :http_01_port
|
||||
duplicate = False
|
||||
# this is for the domain
|
||||
key_path = :privkey_path
|
||||
nginx = False
|
||||
fullchain_path = :fullchain_path
|
||||
email = :email
|
||||
csr = None
|
||||
agree_dev_preview = None
|
||||
redirect = None
|
||||
verbose_count = -3
|
||||
config_file = None
|
||||
renew_by_default = True
|
||||
hsts = False
|
||||
authenticator = webroot
|
||||
domains = :hostnames #comma,delimited,list
|
||||
rsa_key_size = :rsa_key_size
|
||||
# starts at 0 and increments at every renewal
|
||||
checkpoints = -1
|
||||
manual_test_mode = False
|
||||
apache = False
|
||||
cert_path = :cert_path
|
||||
webroot_path = :webroot_paths # comma,delimited,list
|
||||
strict_permissions = False
|
||||
apache_server_root = /etc/apache2
|
||||
# https://github.com/letsencrypt/letsencrypt/issues/1948
|
||||
account = :account_id
|
||||
manual_public_ip_logging_ok = False
|
||||
chain_path = :chain_path
|
||||
standalone = False
|
||||
manual = False
|
||||
server = :acme_discovery_url
|
||||
standalone_supported_challenges = "http-01,tls-sni-01"
|
||||
webroot = True
|
||||
apache_init_script = None
|
||||
user_agent = None
|
||||
apache_ctl = apache2ctl
|
||||
apache_le_vhost_ext = -le-ssl.conf
|
||||
debug = False
|
||||
tls_sni_01_port = 443
|
||||
logs_dir = :logs_dir
|
||||
configurator = None
|
||||
[[webroot_map]]
|
||||
# :hostname = :webroot_path
|
||||
181
lib/restart.js
Normal file
181
lib/restart.js
Normal file
@@ -0,0 +1,181 @@
|
||||
/**
|
||||
* @fileOverview restart the controller
|
||||
* @author bluefox
|
||||
* @version 0.1
|
||||
*/
|
||||
'use strict';
|
||||
|
||||
/** @module restart */
|
||||
var fs = require('fs');
|
||||
var tools = require(__dirname + '/tools.js');
|
||||
|
||||
var logFile;
|
||||
try {
|
||||
var dir = __dirname + '/../' + tools.getDefaultDataDir() + '../log';
|
||||
if (!fs.existsSync(dir)) fs.mkdirSync(dir);
|
||||
logFile = dir + '/restart.log';
|
||||
console.log(require('path').normalize(logFile));
|
||||
if (fs.existsSync(logFile)) fs.unlinkSync(logFile);
|
||||
fs.writeFileSync(logFile, 'started\n');
|
||||
} catch (err) {
|
||||
console.error(err);
|
||||
}
|
||||
|
||||
function log(text) {
|
||||
var t = (new Date()).toString() + text;
|
||||
console.log(t);
|
||||
fs.appendFileSync(logFile, t + '\n');
|
||||
}
|
||||
|
||||
function checkRoot(callback) {
|
||||
var data = '';
|
||||
|
||||
var child = require('child_process').spawn('whoami', []);
|
||||
child.stdout.on('data', function (text) {
|
||||
data += text.toString().replace('\n', '');
|
||||
});
|
||||
child.stderr.on('data', function (text) {
|
||||
data += text.toString().replace('\n', '');
|
||||
});
|
||||
child.on('exit', function (exitCode) {
|
||||
callback(data.trim() == 'root');
|
||||
});
|
||||
}
|
||||
|
||||
function killPidsScript(callback) {
|
||||
checkRoot(function (isRoot) {
|
||||
var data = '';
|
||||
|
||||
try {
|
||||
fs.chmodSync(__dirname + '/../killall.sh', '777');
|
||||
|
||||
if (isRoot) {
|
||||
fs.writeFileSync(__dirname + '/../killall.sh', "sudo pgrep -f '^io.*' | sudo xargs kill -9\nsudo pgrep -f '^node-red*' | sudo xargs kill -9");
|
||||
} else {
|
||||
fs.writeFileSync(__dirname + '/../killall.sh', "pgrep -f '^io.*' | xargs kill -9\pgrep -f '^node-red*' | xargs kill -9");
|
||||
}
|
||||
|
||||
var child = require('child_process').spawn(__dirname + '/../killall.sh', []);
|
||||
child.stdout.on('data', function (text) {
|
||||
data += text.toString().replace('\n', '');
|
||||
});
|
||||
child.stderr.on('data', function (text) {
|
||||
data += text.toString().replace('\n', '');
|
||||
});
|
||||
child.on('exit', function (exitCode) {
|
||||
if (exitCode) log('Exit code for "killall.sh": ' + exitCode);
|
||||
callback(exitCode, data);
|
||||
});
|
||||
} catch (e) {
|
||||
log('Cannot create "' + __dirname + '/../killall.sh"');
|
||||
callback(-1);
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
function killPid(pid, callback) {
|
||||
var data = '';
|
||||
var child = require('child_process').spawn('kill', ['-KILL', pid]);
|
||||
child.stdout.on('data', function (text) {
|
||||
data += text.toString().replace('\n', '');
|
||||
});
|
||||
child.stderr.on('data', function (text) {
|
||||
data += text.toString().replace('\n', '');
|
||||
});
|
||||
child.on('exit', function (exitCode) {
|
||||
if (exitCode) log('Exit code for "kill -KILL ' + pid + '": ' + exitCode);
|
||||
callback(exitCode, data);
|
||||
});
|
||||
}
|
||||
|
||||
function killPids(pids, callback) {
|
||||
if (pids && pids.length) {
|
||||
killPid(pids.pop(), function () {
|
||||
killPids(pids, callback);
|
||||
});
|
||||
} else {
|
||||
callback();
|
||||
}
|
||||
}
|
||||
|
||||
if (require('os').platform().match(/^win/) && fs.existsSync(__dirname + '/../_service_' + tools.appName + '.bat')) {
|
||||
log('Restarting service ' + tools.appName + '...');
|
||||
|
||||
var spawn = require('child_process').spawn;
|
||||
var out;
|
||||
var err;
|
||||
var stat;
|
||||
var fileName;
|
||||
|
||||
if (fs.existsSync(__dirname + '/../../../log')) {
|
||||
fileName = __dirname + '/../../../log/restart.log';
|
||||
} else {
|
||||
fileName = __dirname + '/../log/restart.log';
|
||||
}
|
||||
|
||||
stat = fs.statSync(fileName);
|
||||
if (stat.size > 1024 * 1024) {
|
||||
try {
|
||||
fs.unlinkSync(fileName);
|
||||
} catch (e) {
|
||||
console.log('File is too big, but cannot delete restart.log: ' + e.toString());
|
||||
}
|
||||
}
|
||||
|
||||
out = fs.openSync(fileName, 'a');
|
||||
|
||||
err = out;
|
||||
|
||||
log('Starting ' + __dirname + '/../_service_' + tools.appName + '.bat');
|
||||
|
||||
var child = spawn('cmd.exe', ['/c', __dirname + '/../_service_' + tools.appName + '.bat'], {
|
||||
detached: true,
|
||||
stdio: ['ignore', out, err]
|
||||
});
|
||||
child.unref();
|
||||
process.exit();
|
||||
} else if (!fs.existsSync(__dirname + '/' + tools.appName + '.pid')) {
|
||||
log(tools.appName + ' was started manually or was not running. Please restart it manually.');
|
||||
} else {
|
||||
log('Restarting ' + tools.appName + '...');
|
||||
|
||||
var daemon = require('daemonize2').setup({
|
||||
main: '../controller.js',
|
||||
name: tools.appName + ' controller',
|
||||
pidfile: __dirname + '/' + tools.appName + '.pid',
|
||||
stopTimeout: 5000
|
||||
});
|
||||
|
||||
log('Stopping daemon ' + tools.appName + '...');
|
||||
daemon.stop(function (err, pid) {
|
||||
// force to stop all adapters
|
||||
if (fs.existsSync(__dirname + '/../pids.txt')) {
|
||||
try {
|
||||
var pids = JSON.parse(fs.readFileSync(__dirname + '/../pids.txt').toString());
|
||||
killPids(pids, function () {
|
||||
killPidsScript(function () {
|
||||
log('Starting daemon ' + tools.appName + '...');
|
||||
daemon.start(function (err, pid) {
|
||||
log('Daemon ' + tools.appName + ' started');
|
||||
process.exit();
|
||||
});
|
||||
});
|
||||
});
|
||||
} catch (e) {
|
||||
log('Error by pids.txt: ' + e);
|
||||
log('Starting daemon ' + tools.appName + '...');
|
||||
daemon.start(function (err, pid) {
|
||||
log('Daemon ' + tools.appName + ' started');
|
||||
process.exit();
|
||||
});
|
||||
|
||||
}
|
||||
} else {
|
||||
log('Starting daemon ' + tools.appName + '...');
|
||||
daemon.start(function (err, pid) {
|
||||
log('Daemon ' + tools.appName + ' started');
|
||||
process.exit();
|
||||
});
|
||||
}
|
||||
});
|
||||
}
|
||||
112
lib/scripts/scripts.js
Normal file
112
lib/scripts/scripts.js
Normal file
@@ -0,0 +1,112 @@
|
||||
var https = require('https');
|
||||
var fs = require('fs');
|
||||
var tools = require(__dirname + '/../tools.js');
|
||||
|
||||
function httpsGet(link, callback) {
|
||||
https.get(link, function (res) {
|
||||
var statusCode = res.statusCode;
|
||||
|
||||
if (statusCode !== 200) {
|
||||
// consume response data to free up memory
|
||||
res.resume();
|
||||
callback(statusCode, null, link);
|
||||
}
|
||||
|
||||
res.setEncoding('utf8');
|
||||
var rawData = '';
|
||||
res.on('data', function (chunk) {
|
||||
rawData += chunk;
|
||||
});
|
||||
res.on('end', function () {
|
||||
callback(null, rawData ? rawData.toString() : null, link);
|
||||
});
|
||||
}).on('error', function (e) {
|
||||
callback(e.message, null, link);
|
||||
});
|
||||
}
|
||||
|
||||
var stableURL = 'https://raw.githubusercontent.com/' + tools.appName + '/' + tools.appName + '.repositories/master/sources-dist-stable.json';
|
||||
|
||||
function updateVersion(name, callback, _sources) {
|
||||
if (!_sources) {
|
||||
httpsGet(stableURL, function (err, body) {
|
||||
updateVersion(name, callback, JSON.parse(body));
|
||||
});
|
||||
}
|
||||
var cmd = 'npm show ' + tools.appName + '.' + name + ' version';
|
||||
var exec = require('child_process').exec;
|
||||
var result = '';
|
||||
var child = exec(cmd, function(error, stdout, stderr){
|
||||
result = stdout;
|
||||
});
|
||||
child.stderr.pipe(process.stdout);
|
||||
child.on('exit', function (code /* , signal */) {
|
||||
if (code) {
|
||||
console.error('host.' + tools.getHostName() + ' Cannot get version of ' + tools.appName + '.' + name + ': ' + code);
|
||||
callback(code, _sources, name, null);
|
||||
} else {
|
||||
_sources[name].version = result;
|
||||
callback(null, _sources, name, result);
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
function updateVersions(callback) {
|
||||
httpsGet(stableURL, function (err, body) {
|
||||
var sources = JSON.parse(body);
|
||||
var count = 0;
|
||||
for (var name in sources) {
|
||||
if (!sources.hasOwnProperty(name)) continue;
|
||||
if (!sources[name].version) {
|
||||
count++;
|
||||
updateVersion(name, function () {
|
||||
if (!--count) callback(sources);
|
||||
}, sources);
|
||||
}
|
||||
}
|
||||
if (!count) callback(sources);
|
||||
});
|
||||
}
|
||||
|
||||
// get the sources-dist.json
|
||||
if (process.argv.indexOf('--prepublish') !== -1) {
|
||||
httpsGet(stableURL, function (err, body) {
|
||||
if (err || !body) {
|
||||
console.error('Cannot read sources file "' + stableURL + '": ' + err);
|
||||
process.exit(2);
|
||||
} else {
|
||||
fs.writeFileSync(__dirname + '/../../conf/sources-dist.json', body);
|
||||
process.exit();
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
// update versions for all adapter, which do not have the version
|
||||
if (process.argv.indexOf('--init') !== -1) {
|
||||
updateVersions(function (sources) {
|
||||
var file = process.argv.indexOf('--file');
|
||||
if (file !== -1 && process.argv[file + 1]) {
|
||||
fs.writeFileSync(file, JSON.stringify(sources, null, 2));
|
||||
} else {
|
||||
console.log(JSON.stringify(sources, null, 2));
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
// update version for one adapter
|
||||
if (process.argv.indexOf('--update') !== -1) {
|
||||
var pos = process.argv.indexOf('--update');
|
||||
if (process.argv[pos + 1]) {
|
||||
updateVersion(process.argv[pos + 1], function (sources) {
|
||||
var file = process.argv.indexOf('--file');
|
||||
if (file !== -1 && process.argv[file + 1]) {
|
||||
fs.writeFileSync(file, JSON.stringify(sources, null, 2));
|
||||
} else {
|
||||
console.log(JSON.stringify(sources, null, 2));
|
||||
}
|
||||
});
|
||||
} else {
|
||||
console.warn('Pleas specify name of adapter to update: script.js --update admin');
|
||||
process.exit(1);
|
||||
}
|
||||
}
|
||||
73
lib/session.js
Normal file
73
lib/session.js
Normal file
@@ -0,0 +1,73 @@
|
||||
'use strict';
|
||||
|
||||
module.exports = function (session, defaultTtl) {
|
||||
var Store = session.Store;
|
||||
defaultTtl = defaultTtl || 3600;
|
||||
|
||||
function AdapterStore(options) {
|
||||
var that = this;
|
||||
|
||||
this.adapter = options.adapter;
|
||||
|
||||
options = options || {};
|
||||
Store.call(this, options);
|
||||
|
||||
}
|
||||
|
||||
// Object.getPrototypeOf(AdapterStore.prototype) ?
|
||||
// AdapterStore.prototype.__proto__ = Store.prototype;
|
||||
AdapterStore.prototype = Object.create(Store.prototype);
|
||||
|
||||
/**
|
||||
* Attempt to fetch session by the given `sid`.
|
||||
*
|
||||
* @param {String} sid
|
||||
* @param {Function} fn
|
||||
* @api public
|
||||
*/
|
||||
|
||||
AdapterStore.prototype.get = function (sid, fn) {
|
||||
|
||||
this.adapter.getSession(sid, function (obj) {
|
||||
if (obj) {
|
||||
if (fn) return fn(null, obj);
|
||||
} else {
|
||||
if (fn) return fn();
|
||||
}
|
||||
});
|
||||
};
|
||||
|
||||
/**
|
||||
* Commit the given `sess` object associated with the given `sid`.
|
||||
*
|
||||
* @param {String} sid
|
||||
* @param {Session} sess
|
||||
* @param {Function} fn
|
||||
* @api public
|
||||
*/
|
||||
AdapterStore.prototype.set = function (sid, ttl, sess, fn) {
|
||||
if (typeof ttl === 'object') {
|
||||
fn = sess;
|
||||
sess = ttl;
|
||||
ttl = defaultTtl;
|
||||
}
|
||||
|
||||
ttl = ttl || defaultTtl;
|
||||
this.adapter.setSession(sid, ttl, sess, function () {
|
||||
if (fn) fn.apply(this, arguments);
|
||||
});
|
||||
};
|
||||
|
||||
/**
|
||||
* Destroy the session associated with the given `sid`.
|
||||
*
|
||||
* @param {String} sid
|
||||
* @api public
|
||||
*/
|
||||
|
||||
AdapterStore.prototype.destroy = function (sid, fn) {
|
||||
this.adapter.destroySession(sid, fn);
|
||||
};
|
||||
|
||||
return AdapterStore;
|
||||
};
|
||||
3167
lib/setup.js
Normal file
3167
lib/setup.js
Normal file
File diff suppressed because it is too large
Load Diff
602
lib/setup/setupBackup.js
Normal file
602
lib/setup/setupBackup.js
Normal file
@@ -0,0 +1,602 @@
|
||||
'use strict';
|
||||
|
||||
function BackupRestore(options) {
|
||||
var fs = require('fs');
|
||||
var tools = require(__dirname + '/../tools.js');
|
||||
var pathLib = require('path');
|
||||
var tmpDir = pathLib.normalize(__dirname + '/../../tmp');
|
||||
var bkpDir = pathLib.normalize(__dirname + '/../../backups');
|
||||
var hostname = tools.getHostName();
|
||||
|
||||
// allow use without new operator
|
||||
if (!(this instanceof BackupRestore)) return new BackupRestore(options);
|
||||
|
||||
options = options || {};
|
||||
|
||||
if (!options.states) throw 'Invalid arguments: states is missing';
|
||||
if (!options.objects) throw 'Invalid arguments: objects is missing';
|
||||
if (!options.processExit) throw 'Invalid arguments: processExit is missing';
|
||||
if (!options.cleanDatabase) throw 'Invalid arguments: cleanDatabase is missing';
|
||||
if (!options.restartController) throw 'Invalid arguments: restartController is missing';
|
||||
|
||||
var objects = options.objects;
|
||||
var states = options.states;
|
||||
var processExit = options.processExit;
|
||||
var cleanDatabase = options.cleanDatabase;
|
||||
var restartController = options.restartController;
|
||||
var mime;
|
||||
|
||||
var Upload = require(__dirname + '/setupUpload.js');
|
||||
var upload = new Upload(options);
|
||||
|
||||
// --------------------------------------- BACKUP ---------------------------------------------------
|
||||
function _copyFile(id, srcPath, destPath, callback) {
|
||||
objects.readFile(id, srcPath, "", function (err, data) {
|
||||
if (data) fs.writeFileSync(destPath, data);
|
||||
callback();
|
||||
});
|
||||
}
|
||||
|
||||
function copyDir(id, srcPath, destPath, callback) {
|
||||
var count = 0;
|
||||
if (!fs.existsSync(destPath)) fs.mkdirSync(destPath);
|
||||
objects.readDir(id, srcPath, function (err, res) {
|
||||
if (res) {
|
||||
for (var t = 0; t < res.length; t++) {
|
||||
if (res[t].isDir) {
|
||||
count++;
|
||||
copyDir(id, srcPath + '/' + res[t].file, destPath + '/' + res[t].file, function () {
|
||||
count--;
|
||||
if (!count) callback();
|
||||
});
|
||||
} else {
|
||||
if (!fs.existsSync(destPath)) fs.mkdirSync(destPath);
|
||||
count++;
|
||||
_copyFile(id, srcPath + '/' + res[t].file, destPath + '/' + res[t].file, function () {
|
||||
count--;
|
||||
if (!count) callback();
|
||||
});
|
||||
}
|
||||
}
|
||||
}
|
||||
if (!count) callback();
|
||||
});
|
||||
}
|
||||
|
||||
function getBackupDir() {
|
||||
var dataDir = tools.getDefaultDataDir();
|
||||
|
||||
// All pathes are returned always relative to /node_modules/appName.js-controller
|
||||
if (dataDir) {
|
||||
if (dataDir[0] === '.' && dataDir[1] === '.') {
|
||||
dataDir = __dirname + '/../../' + dataDir;
|
||||
} else if (dataDir[0] === '.' && dataDir[1] === '/') {
|
||||
dataDir = __dirname + '/../../' + dataDir.substring(2);
|
||||
}
|
||||
}
|
||||
dataDir = dataDir.replace(/\\/g, '/');
|
||||
if (dataDir[dataDir.length - 1] !== '/') dataDir += '/';
|
||||
|
||||
var parts = dataDir.split('/');
|
||||
parts.pop();// remove data or appName-data
|
||||
parts.pop();
|
||||
|
||||
return parts.join('/') + '/backups/';
|
||||
}
|
||||
|
||||
function copyFileSync(source, target) {
|
||||
var targetFile = target;
|
||||
|
||||
// if target is a directory a new file with the same name will be created
|
||||
if (fs.existsSync(target)) {
|
||||
if (fs.lstatSync(target).isDirectory()) {
|
||||
targetFile = pathLib.join(target, pathLib.basename(source));
|
||||
}
|
||||
}
|
||||
|
||||
fs.writeFileSync(targetFile, fs.readFileSync(source));
|
||||
}
|
||||
|
||||
function copyFolderRecursiveSync(source, target) {
|
||||
var files = [];
|
||||
|
||||
if (!fs.existsSync(target)) fs.mkdirSync(target);
|
||||
|
||||
// check if folder needs to be created or integrated
|
||||
var targetFolder = pathLib.join(target, pathLib.basename(source));
|
||||
if (!fs.existsSync(targetFolder)) fs.mkdirSync(targetFolder);
|
||||
|
||||
// copy
|
||||
if (fs.lstatSync(source).isDirectory() ) {
|
||||
files = fs.readdirSync(source);
|
||||
files.forEach(function (file) {
|
||||
var curSource = pathLib.join(source, file);
|
||||
if (fs.lstatSync(curSource).isDirectory()) {
|
||||
copyFolderRecursiveSync(curSource, targetFolder);
|
||||
} else {
|
||||
copyFileSync(curSource, targetFolder);
|
||||
}
|
||||
});
|
||||
}
|
||||
}
|
||||
|
||||
this.createBackup = function (name, callback) {
|
||||
var count = 0;
|
||||
|
||||
if (!name) {
|
||||
var d = new Date();
|
||||
name = d.getFullYear() + '_' +
|
||||
('0' + (d.getMonth() + 1)).slice(-2) + '_' +
|
||||
('0' + d.getDate()).slice(-2) + '-' +
|
||||
('0' + d.getHours()).slice(-2) + '_' +
|
||||
('0' + d.getMinutes()).slice(-2) + '_' +
|
||||
('0' + d.getSeconds()).slice(-2) + '_backup' + tools.appName;
|
||||
}
|
||||
|
||||
name = name.replace(/\\/g, '/');
|
||||
if (name.indexOf('/') === -1) {
|
||||
var path = getBackupDir();
|
||||
|
||||
// create directory if not exists
|
||||
if (!fs.existsSync(path)) {
|
||||
fs.mkdirSync(path);
|
||||
}
|
||||
|
||||
if (name.indexOf('.tar.gz') === -1) {
|
||||
name = path + name + '.tar.gz';
|
||||
} else {
|
||||
name = path + name;
|
||||
}
|
||||
}
|
||||
|
||||
objects.getObjectList({include_docs: true}, function (err, res) {
|
||||
var result = {objects: null, states: {}, config: null};
|
||||
if (err) {
|
||||
console.error('host.' + hostname + ' Cannot get objects: ' + err);
|
||||
} else {
|
||||
result.objects = res.rows;
|
||||
}
|
||||
|
||||
if (fs.existsSync(tools.getConfigFileName())) result.config = JSON.parse(fs.readFileSync(tools.getConfigFileName(), 'utf8'));
|
||||
|
||||
states.getKeys('io.*', function (err, keys) {
|
||||
/*for (var i = keys.length - 1; i >= 0; i--) {
|
||||
if (keys[i].match(/^messagebox\./) || keys[i].match(/^log\./)) {
|
||||
keys.splice(i, 1);
|
||||
}
|
||||
}*/
|
||||
|
||||
states.getStates(keys, function (err, obj) {
|
||||
var hostname = tools.getHostName();
|
||||
var r = new RegExp('^system\\.host\\.' + hostname + '\\.(\\w+)$');
|
||||
|
||||
for (var i = 0; i < keys.length; i++) {
|
||||
if (obj[i].from === 'system.host.' + hostname) {
|
||||
obj[i].from = 'system.host.$$__hostname__$$';
|
||||
}
|
||||
if (r.test(keys[i])) {
|
||||
keys[i] = keys[i].replace(hostname, '$$__hostname__$$');
|
||||
}
|
||||
result.states[keys[i]] = obj[i];
|
||||
}
|
||||
|
||||
if (!fs.existsSync(bkpDir)) fs.mkdirSync(bkpDir);
|
||||
if (!fs.existsSync(tmpDir)) fs.mkdirSync(tmpDir);
|
||||
if (!fs.existsSync(tmpDir + '/backup')) fs.mkdirSync(tmpDir + '/backup');
|
||||
if (!fs.existsSync(tmpDir + '/backup/files')) fs.mkdirSync(tmpDir + '/backup/files');
|
||||
|
||||
// try to find user files
|
||||
for (var j = 0; j < result.objects.length; j++) {
|
||||
if (!result.objects[j].value || !result.objects[j].value._id) continue;
|
||||
//if (result.objects[j].doc) delete result.objects[j].doc;
|
||||
if (result.objects[j].value._id.match(/^system\.adapter\.([\w\d_-]+).(\d+)$/) &&
|
||||
result.objects[j].value.common.host === hostname) {
|
||||
result.objects[j].value.common.host = '$$__hostname__$$';
|
||||
if (result.objects[j].doc) {
|
||||
result.objects[j].doc.common.host = '$$__hostname__$$';
|
||||
}
|
||||
} else
|
||||
if (r.test(result.objects[j].value._id)) {
|
||||
result.objects[j].value._id = result.objects[j].value._id.replace(hostname, '$$__hostname__$$');
|
||||
result.objects[j].id = result.objects[j].value._id;
|
||||
if (result.objects[j].doc) {
|
||||
result.objects[j].doc._id = result.objects[j].value._id;
|
||||
}
|
||||
} else if (result.objects[j].value._id === 'system.host.' + hostname) {
|
||||
result.objects[j].value._id = 'system.host.$$__hostname__$$';
|
||||
result.objects[j].value.common.name = result.objects[j].value._id;
|
||||
result.objects[j].value.common.hostname = '$$__hostname__$$';
|
||||
if (result.objects[j].value.native && result.objects[j].value.native.os) {
|
||||
result.objects[j].value.native.os.hostname = '$$__hostname__$$';
|
||||
}
|
||||
result.objects[j].id = result.objects[j].value._id;
|
||||
if (result.objects[j].doc) {
|
||||
result.objects[j].doc._id = result.objects[j].value._id;
|
||||
result.objects[j].doc.common.name = result.objects[j].value._id;
|
||||
result.objects[j].doc.common.hostname = '$$__hostname__$$';
|
||||
if (result.objects[j].doc.native && result.objects[j].value.native.os) {
|
||||
result.objects[j].doc.native.os.hostname = '$$__hostname__$$';
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// Read all files
|
||||
if (result.objects[j].value.type === 'meta' &&
|
||||
result.objects[j].value.common &&
|
||||
result.objects[j].value.common.type === 'meta.user') {
|
||||
count++;
|
||||
copyDir(result.objects[j].id, '', tmpDir + '/backup/files/' + result.objects[j].id, function () {
|
||||
count--;
|
||||
if (!count) {
|
||||
// todo: store letsencrypt files too => change it as letitbit will be better integrated
|
||||
var configDir = tools.getConfigFileName().split('/');
|
||||
configDir.pop();
|
||||
configDir.push('letsencrypt');
|
||||
var letsEncrypt = configDir.join('/');
|
||||
if (fs.existsSync(letsEncrypt)) {
|
||||
copyFolderRecursiveSync(letsEncrypt, tmpDir + '/backup');
|
||||
}
|
||||
|
||||
var tar = require('tar');
|
||||
|
||||
var f = fs.createWriteStream(name);
|
||||
f.on('finish', function () {
|
||||
tools.rmdirRecursiveSync(tmpDir + '/backup');
|
||||
if (callback) callback(pathLib.normalize(name));
|
||||
});
|
||||
f.on('error', function (err) {
|
||||
console.error('host.' + hostname + ' Cannot pack directory ' + pathLib.normalize(tmpDir + '/backup') + ': ' + err);
|
||||
processExit(9);
|
||||
});
|
||||
|
||||
try {
|
||||
tar.create({gzip: true, cwd: tmpDir + '/'}, ['backup']).pipe(f);
|
||||
} catch (err) {
|
||||
console.error('host.' + hostname + ' Cannot pack directory ' + pathLib.normalize(tmpDir + '/backup') + ': ' + err);
|
||||
processExit(9);
|
||||
}
|
||||
}
|
||||
});
|
||||
}
|
||||
}
|
||||
|
||||
fs.writeFileSync(tmpDir + '/backup/backup.json', JSON.stringify(result, null, 2));
|
||||
|
||||
if (!count) {
|
||||
var tar = require('tar');
|
||||
|
||||
var f = fs.createWriteStream(name);
|
||||
f.on('finish', function () {
|
||||
tools.rmdirRecursiveSync(tmpDir + '/backup');
|
||||
if (callback) callback(pathLib.normalize(name));
|
||||
});
|
||||
f.on('error', function (err) {
|
||||
console.error('host.' + hostname + ' Cannot pack directory ' + pathLib.normalize(tmpDir + '/backup') + ': ' + err);
|
||||
processExit(9);
|
||||
});
|
||||
|
||||
try {
|
||||
tar.create({gzip: true, cwd: tmpDir + '/'}, ['backup']).pipe(f);
|
||||
} catch (err) {
|
||||
console.error('host.' + hostname + ' Cannot pack directory ' + pathLib.normalize(tmpDir + '/backup') + ': ' + err);
|
||||
processExit(9);
|
||||
}
|
||||
}
|
||||
});
|
||||
});
|
||||
|
||||
});
|
||||
};
|
||||
|
||||
//--------------------------------------- RESTORE ---------------------------------------------------
|
||||
function _setStateHelper(_index, statesList, stateObjects, callback) {
|
||||
states.setRawState(statesList[_index], stateObjects[statesList[_index]], function () {
|
||||
if ((_index % 200) === 0) console.log('host.' + hostname + ' Processed ' + _index + '/' + statesList.length + ' states');
|
||||
_index++;
|
||||
if (_index < statesList.length) {
|
||||
setImmediate(_setStateHelper, _index, statesList, stateObjects, callback);
|
||||
} else {
|
||||
if (callback) callback();
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
function _setObjHelper(_index, _objects, callback) {
|
||||
// Disable all adapters.
|
||||
if (_objects[_index].id.match(/^system\.adapter\./) && !_objects[_index].id.match(/^system\.adapter\.admin/)) {
|
||||
if (_objects[_index].doc.common && _objects[_index].doc.common.enabled) {
|
||||
_objects[_index].doc.common.enabled = false;
|
||||
}
|
||||
}
|
||||
if (_objects[_index].doc && _objects[_index].doc._rev) delete _objects[_index].doc._rev;
|
||||
|
||||
objects.setObject(_objects[_index].id, _objects[_index].doc, function (err /* , obj */) {
|
||||
if (err) {
|
||||
console.warn('host.' + hostname + ' Cannot restore ' + _objects[_index].id + ': ' + err);
|
||||
}
|
||||
|
||||
if ((_index % 200) === 0) console.log('host.' + hostname + ' Processed ' + _index + '/' + _objects.length + ' objects');
|
||||
_index++;
|
||||
if (_index < _objects.length) {
|
||||
setImmediate(_setObjHelper, _index, _objects, callback);
|
||||
} else {
|
||||
if (callback) callback();
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
function reloadAdapterObject(index, objectList, callback) {
|
||||
if (objectList && index < objectList.length) {
|
||||
objects.getObject(objectList[index]._id, function (err, obj) {
|
||||
if (err || !obj) {
|
||||
objects.setObject(objectList[index]._id, objectList[index], function () {
|
||||
console.log('host.' + hostname + ' object ' + objectList[index]._id + ' created');
|
||||
index++;
|
||||
setImmediate(reloadAdapterObject, index, objectList, callback);
|
||||
});
|
||||
} else {
|
||||
index++;
|
||||
setImmediate(reloadAdapterObject, index, objectList, callback);
|
||||
}
|
||||
});
|
||||
} else {
|
||||
if (callback) callback();
|
||||
}
|
||||
}
|
||||
|
||||
function reloadAdaptersObjects(callback, dirs, index) {
|
||||
if (!dirs) {
|
||||
dirs = [];
|
||||
var _modules;
|
||||
if (fs.existsSync(__dirname + '/../../node_modules')) {
|
||||
_modules = fs.readdirSync(__dirname + '/../../node_modules');
|
||||
if (_modules) {
|
||||
var regEx = new RegExp('^' + tools.appName + '\\.', 'i');
|
||||
for (var i = 0; i < _modules.length; i++) {
|
||||
if (regEx.test(_modules[i]) &&
|
||||
dirs.indexOf(_modules[i].substring(tools.appName.length + 1)) === -1) {
|
||||
dirs.push(_modules[i]);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
// if installed as npm
|
||||
if (fs.existsSync(__dirname + '/../../../../node_modules/' + tools.appName + '.js-controller')) {
|
||||
_modules = fs.readdirSync(__dirname + '/../../..');
|
||||
var regEx_ = new RegExp('^' + tools.appName + '\\.', 'i');
|
||||
for (var j = 0; j < _modules.length; j++) {
|
||||
// if starting from application name + '.'
|
||||
if (regEx_.test(_modules[j]) &&
|
||||
// If not js-controller
|
||||
(_modules[j].substring(tools.appName.length + 1) !== 'js-controller') &&
|
||||
dirs.indexOf(_modules[j].substring(tools.appName.length + 1)) === -1) dirs.push(_modules[j]);
|
||||
}
|
||||
}
|
||||
if (dirs.length) {
|
||||
reloadAdaptersObjects(callback, dirs, 0);
|
||||
} else {
|
||||
if (callback) callback();
|
||||
}
|
||||
} else {
|
||||
if (index < dirs.length) {
|
||||
upload.uploadAdapter(dirs[index], false, true, function () {
|
||||
upload.uploadAdapter(dirs[index], true, true, function () {
|
||||
var pkg = null;
|
||||
if (!dirs[index]) {
|
||||
console.error('Wrong');
|
||||
}
|
||||
var adapterDir = tools.getAdapterDir(dirs[index]);
|
||||
if (fs.existsSync(adapterDir + '/io-package.json')) {
|
||||
pkg = JSON.parse(fs.readFileSync(adapterDir + '/io-package.json', 'utf8'));
|
||||
}
|
||||
|
||||
if (pkg && pkg.objects && pkg.objects.length) {
|
||||
console.log('host.' + hostname + ' Setup "' + dirs[index] + '" adapter');
|
||||
reloadAdapterObject(0, pkg.objects, function () {
|
||||
index++;
|
||||
setImmediate(reloadAdaptersObjects, callback, dirs, index);
|
||||
});
|
||||
} else {
|
||||
index++;
|
||||
reloadAdaptersObjects(callback, dirs, index);
|
||||
}
|
||||
});
|
||||
});
|
||||
} else {
|
||||
if (callback) callback();
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
function uploadUserFiles(root, path, callback) {
|
||||
if (typeof path === 'function') {
|
||||
callback = path;
|
||||
path = '';
|
||||
}
|
||||
|
||||
var called = false;
|
||||
if (!fs.existsSync(root)) {
|
||||
callback();
|
||||
return;
|
||||
}
|
||||
var files = fs.readdirSync(root + path);
|
||||
var count = files.length;
|
||||
for (var i = 0; i < files.length; i++) {
|
||||
var stat = fs.statSync(root + path + '/' + files[i]);
|
||||
if (stat.isDirectory()) {
|
||||
called = true;
|
||||
uploadUserFiles(root, path + '/' + files[i], function (err) {
|
||||
if (err) console.error('Error: ' + err);
|
||||
if (!--count) setImmediate(callback);
|
||||
});
|
||||
} else {
|
||||
var parts = path.split('/');
|
||||
var adapter = parts.splice(0, 2);
|
||||
adapter = adapter[1];
|
||||
var _path = parts.join('/') + '/' + files[i];
|
||||
console.log('host.' + hostname + ' Upload user file "' + adapter + "/" + _path);
|
||||
called = true;
|
||||
objects.writeFile(adapter, _path, fs.readFileSync(root + path + '/' + files[i]), null, function (err) {
|
||||
if (err) console.error('Error: ' + err);
|
||||
if (!--count) setImmediate(callback);
|
||||
});
|
||||
}
|
||||
}
|
||||
if (!called) callback();
|
||||
}
|
||||
|
||||
function restoreAfterStop(restartOnFinish, callback) {
|
||||
// Open file
|
||||
var data = fs.readFileSync(tmpDir + '/backup/backup.json').toString();
|
||||
var hostname = tools.getHostName();
|
||||
data = data.replace(/\$\$__hostname__\$\$/g, hostname);
|
||||
fs.writeFileSync(tmpDir + '/backup/backup_.json', data);
|
||||
var restore;
|
||||
try {
|
||||
restore = JSON.parse(data);
|
||||
} catch (e) {
|
||||
console.error('Cannot parse "' + tmpDir + '/backup/backup_.json": ' + e);
|
||||
if (callback) callback(31);
|
||||
}
|
||||
|
||||
// stop all adapters
|
||||
console.log('host.' + hostname + ' Clear all objects and states...');
|
||||
cleanDatabase(false, function () {
|
||||
console.log('host.' + hostname + ' done.');
|
||||
// upload all data into DB
|
||||
// restore ioBorker.json
|
||||
if (restore.config) fs.writeFileSync(tools.getConfigFileName(), JSON.stringify(restore.config, null, 2));
|
||||
|
||||
var sList = [];
|
||||
for (var state in restore.states) {
|
||||
if (restore.states.hasOwnProperty(state)) {
|
||||
sList.push(state);
|
||||
}
|
||||
}
|
||||
|
||||
_setStateHelper(0, sList, restore.states, function () {
|
||||
console.log(sList.length + ' states restored.');
|
||||
_setObjHelper(0, restore.objects, function () {
|
||||
console.log(restore.objects.length + ' objects restored.');
|
||||
// Required for upload adapter
|
||||
mime = require('mime');
|
||||
// Load user files into DB
|
||||
uploadUserFiles(tmpDir + '/backup/files', function () {
|
||||
// reload objects of adapters
|
||||
reloadAdaptersObjects(function () {
|
||||
// Reload host objects
|
||||
var pckgio = JSON.parse(fs.readFileSync(__dirname + '/../../io-package.json', 'utf8'));
|
||||
reloadAdapterObject(0, pckgio ? pckgio.objects : null, function () {
|
||||
if (restartOnFinish) {
|
||||
restartController(callback);
|
||||
} else {
|
||||
if (callback) callback();
|
||||
}
|
||||
});
|
||||
});
|
||||
|
||||
});
|
||||
});
|
||||
});
|
||||
});
|
||||
}
|
||||
|
||||
this.listBackups = function () {
|
||||
var dir = getBackupDir();
|
||||
var result = [];
|
||||
if (fs.existsSync(dir)) {
|
||||
var files = fs.readdirSync(dir);
|
||||
for (var i = 0; i < files.length; i++) {
|
||||
if (files[i].match(/\.tar\.gz$/i)) {
|
||||
result.push(files[i]);
|
||||
}
|
||||
}
|
||||
return result;
|
||||
} else {
|
||||
return result;
|
||||
}
|
||||
};
|
||||
|
||||
this.restoreBackup = function (name, callback) {
|
||||
var backups;
|
||||
if (!name && name !== 0) {
|
||||
// List all available backups
|
||||
console.log('Please specify one of the backup names:');
|
||||
backups = this.listBackups();
|
||||
backups.sort(function (a, b) {
|
||||
return b > a;
|
||||
});
|
||||
if (backups.length) {
|
||||
for (var t = 0; t < backups.length; t++){
|
||||
console.log(backups[t] + ' or ' + backups[t].replace('_backup' + tools.appName + '.tar.gz', '') + ' or ' + t);
|
||||
}
|
||||
} else {
|
||||
console.warn('No backups found');
|
||||
}
|
||||
processExit(10);
|
||||
}
|
||||
|
||||
if (!options.cleanDatabase) throw "Invalid arguments: cleanDatabase is missing";
|
||||
if (!options.restartController) throw "Invalid arguments: restartController is missing";
|
||||
|
||||
if (parseInt(name, 10).toString() === name.toString()) {
|
||||
backups = this.listBackups();
|
||||
backups.sort(function (a, b) {
|
||||
return b > a;
|
||||
});
|
||||
name = backups[parseInt(name, 10)];
|
||||
console.log('host.' + hostname + ' Using backup file ' + name);
|
||||
}
|
||||
|
||||
name = (name || '').toString().replace(/\\/g, '/');
|
||||
if (name.indexOf('/') === -1) {
|
||||
name = getBackupDir() + name;
|
||||
var regEx = new RegExp('_backup' + tools.appName, 'i');
|
||||
if (!regEx.test(name)) name += '_backup' + tools.appName;
|
||||
if (!name.match(/\.tar\.gz$/i)) name += '.tar.gz';
|
||||
}
|
||||
if (!fs.existsSync(name)) {
|
||||
console.error('host.' + hostname + ' Cannot find ' + name);
|
||||
processExit(11);
|
||||
}
|
||||
var tar = require('tar');
|
||||
if (fs.existsSync(tmpDir + '/backup/backup.json')) {
|
||||
fs.unlinkSync(tmpDir + '/backup/backup.json');
|
||||
}
|
||||
|
||||
tar.extract({
|
||||
file: name,
|
||||
cwd: tmpDir
|
||||
}, function (err) {
|
||||
if (err) {
|
||||
console.error('host.' + hostname + ' Cannot extract from file "' + name + '"');
|
||||
processExit(9);
|
||||
}
|
||||
if (!fs.existsSync(tmpDir + '/backup/backup.json')) {
|
||||
console.error('host.' + hostname + ' Cannot find extracted file from file "' + tmpDir + '/backup/backup.json"');
|
||||
processExit(9);
|
||||
}
|
||||
// Stop controller
|
||||
var daemon = require('daemonize2').setup({
|
||||
main: '../../controller.js',
|
||||
name: tools.appName + ' controller',
|
||||
pidfile: __dirname + '/../' + tools.appName + '.pid',
|
||||
cwd: '../../',
|
||||
stopTimeout: 1000
|
||||
});
|
||||
daemon.on('error', function (/* error */) {
|
||||
restoreAfterStop(false, callback);
|
||||
});
|
||||
daemon.on('stopped', function () {
|
||||
restoreAfterStop(true, callback);
|
||||
});
|
||||
daemon.on('notrunning', function () {
|
||||
console.log('host.' + hostname + ' OK.');
|
||||
restoreAfterStop(false, callback);
|
||||
});
|
||||
daemon.stop();
|
||||
});
|
||||
}
|
||||
}
|
||||
|
||||
module.exports = BackupRestore;
|
||||
1397
lib/setup/setupInstall.js
Normal file
1397
lib/setup/setupInstall.js
Normal file
File diff suppressed because it is too large
Load Diff
76
lib/setup/setupLicense.js
Normal file
76
lib/setup/setupLicense.js
Normal file
@@ -0,0 +1,76 @@
|
||||
'use strict';
|
||||
function License(options) {
|
||||
const fs = require('fs');
|
||||
const jwt = require('jsonwebtoken');
|
||||
options = options || {};
|
||||
|
||||
let objects = options.objects;
|
||||
// read info from '/etc/iob_vendor.json' and executes instructions stored there
|
||||
this.setLicense = file => {
|
||||
if (fs.existsSync(file)) {
|
||||
try {
|
||||
file = fs.readFileSync(file).toString('utf8');
|
||||
} catch (e) {
|
||||
return Promise.reject(e);
|
||||
}
|
||||
}
|
||||
// try to encode license
|
||||
let license = jwt.decode(file);
|
||||
if (!license) {
|
||||
return Promise.reject('License cannot be decoded');
|
||||
}
|
||||
if (!license.name) {
|
||||
return Promise.reject('Name not found in the license');
|
||||
}
|
||||
let adapter = license.name.split('.')[1];
|
||||
if (!adapter) {
|
||||
return Promise.reject(`Invalid license name ${license.name}`);
|
||||
}
|
||||
// read all instances of this adapter
|
||||
return objects.getObjectListAsync({
|
||||
startkey: 'system.adapter.' + adapter + '.',
|
||||
endkey: 'system.adapter.' + adapter + '.\u9999'
|
||||
}, {checked: true}).then(arr => {
|
||||
let promises = [];
|
||||
if (arr && arr.rows && arr.rows.length) {
|
||||
for (let g = 0; g < arr.rows.length; g++) {
|
||||
let obj = arr.rows[g].value;
|
||||
if (obj && obj.type === 'instance') {
|
||||
obj.native = obj.native || {};
|
||||
obj.native.license = file;
|
||||
promises.push(objects.setObjectAsync(obj._id, obj).then(() => {
|
||||
console.log(`Instance "${obj._id}" updated`);
|
||||
}).catch(err => {
|
||||
console.error(`Cannot update "${obj._id}": ${err}`);
|
||||
}));
|
||||
}
|
||||
}
|
||||
}
|
||||
if (!promises.length) {
|
||||
console.warn(`no instances of ${adapter} found`);
|
||||
if (arr && arr.rows && arr.rows.length) {
|
||||
for (let g = 0; g < arr.rows.length; g++) {
|
||||
let obj = arr.rows[g].value;
|
||||
if (obj && obj.type === 'adapter') {
|
||||
obj.native = obj.native || {};
|
||||
obj.native.license = file;
|
||||
promises.push(objects.setObjectAsync(obj._id, obj).then(() => {
|
||||
console.log(`Adapter "${obj._id}" updated`);
|
||||
}).catch(err => {
|
||||
console.error(`Cannot update "${obj._id}": ${err}`);
|
||||
}));
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
if (!promises.length) {
|
||||
console.error(`no installations of ${adapter} found`);
|
||||
}
|
||||
return Promise.all(promises);
|
||||
});
|
||||
};
|
||||
|
||||
return this;
|
||||
}
|
||||
|
||||
module.exports = License;
|
||||
627
lib/setup/setupList.js
Normal file
627
lib/setup/setupList.js
Normal file
@@ -0,0 +1,627 @@
|
||||
'use strict';
|
||||
function List(options) {
|
||||
var fs = require('fs');
|
||||
var tools = require(__dirname + '/../tools.js');
|
||||
|
||||
var that = this;
|
||||
|
||||
options = options || {};
|
||||
|
||||
if (!options.states) throw 'Invalid arguments: states is missing';
|
||||
if (!options.objects) throw 'Invalid arguments: objects is missing';
|
||||
if (!options.processExit) throw 'Invalid arguments: processExit is missing';
|
||||
|
||||
var objects = options.objects;
|
||||
var states = options.states;
|
||||
var processExit = options.processExit;
|
||||
|
||||
function perm2str(perm) {
|
||||
var result = '';
|
||||
// user
|
||||
result += (perm & 0x400) ? 'r' : '-';
|
||||
result += (perm & 0x200) ? 'w' : '-';
|
||||
result += (perm & 0x100) ? 'x' : '-';
|
||||
// group
|
||||
result += (perm & 0x040) ? 'r' : '-';
|
||||
result += (perm & 0x020) ? 'w' : '-';
|
||||
result += (perm & 0x010) ? 'x' : '-';
|
||||
// any
|
||||
result += (perm & 0x004) ? 'r' : '-';
|
||||
result += (perm & 0x002) ? 'w' : '-';
|
||||
result += (perm & 0x001) ? 'x' : '-';
|
||||
return result;
|
||||
}
|
||||
|
||||
this.showFileHeader = function () {
|
||||
console.log(' Modified at |Permission| User | Group | Size | Name');
|
||||
console.log('----------------+----------+--------------+--------------+------+---------');
|
||||
};
|
||||
|
||||
this.showFile = function (adapter, path, file) {
|
||||
//drwxr-xr-x 1 odroid odroid 43 Oct 3 2013 .xsessionrc
|
||||
var text = '';
|
||||
var time;
|
||||
if (file.modifiedAt) {
|
||||
var ts = new Date(file.modifiedAt);
|
||||
time = ts.toISOString();
|
||||
time = time.replace('T', ' ');
|
||||
time = time.substring(0, 16) + ' ';
|
||||
} else {
|
||||
time = Array(18).join(' ');
|
||||
}
|
||||
text += time;
|
||||
|
||||
if (file.acl){
|
||||
text += (file.isDir ? 'd' : '-') + perm2str(file.acl.permissions || 0);
|
||||
var owner = file.acl.owner;
|
||||
if (owner) {
|
||||
owner = owner.substring(12);
|
||||
if (owner.length < 15) owner = Array(15 - owner.length).join(' ') + owner;
|
||||
} else {
|
||||
owner = Array(15).join(' ');
|
||||
}
|
||||
text += ' ' + owner;
|
||||
var group = file.acl.ownerGroup;
|
||||
if (group) {
|
||||
group = group.substring(13);
|
||||
if (group.length < 15) group = Array(15 - group.length).join(' ') + group;
|
||||
} else {
|
||||
group = Array(15).join(' ');
|
||||
}
|
||||
|
||||
text += ' ' + group;
|
||||
} else {
|
||||
text += (file.isDir ? 'd' : '-') + '?????????' + Array(31).join(' ');
|
||||
}
|
||||
var size = (file.stats) ? file.stats.size.toString() : '';
|
||||
if (size.length < 7) size = Array(7 - size.length).join(' ') + size;
|
||||
|
||||
text += ' ' + size + ' ' + adapter + ((!path || path[0] === '/') ? '' : '/') + path + '/' + file.file;
|
||||
if (file.isDir) {
|
||||
text += '/';
|
||||
console.log(text);
|
||||
} else {
|
||||
console.log(text);
|
||||
}
|
||||
};
|
||||
|
||||
this.showObjectHeader = function () {
|
||||
console.log('ObjectAC | StateAC | User | Group | ID');
|
||||
console.log('---------+---------+--------------+--------------+--------------');
|
||||
};
|
||||
|
||||
this.showObject = function (obj) {
|
||||
//drwxr-xr-x 1 odroid odroid 43 Oct 3 2013 .xsessionrc
|
||||
var text = '';
|
||||
if (obj.acl){
|
||||
text += perm2str(obj.acl.object || 0) + ' ' + ((obj.type === 'state') ? perm2str(obj.acl.state || 0) : ' ');
|
||||
var owner = obj.acl.owner;
|
||||
if (owner) {
|
||||
owner = owner.substring(12);
|
||||
if (owner.length < 15) owner = Array(15 - owner.length).join(' ') + owner;
|
||||
} else {
|
||||
owner = Array(15).join(' ');
|
||||
}
|
||||
text += ' ' + owner;
|
||||
var group = obj.acl.ownerGroup;
|
||||
if (group) {
|
||||
group = group.substring(13);
|
||||
if (group.length < 15) group = Array(15 - group.length).join(' ') + group;
|
||||
} else {
|
||||
group = Array(15).join(' ');
|
||||
}
|
||||
text += ' ' + group;
|
||||
} else {
|
||||
text += '?????????' + ((obj.type === 'state') ? ' ?????????' : ' ') + Array(31).join(' ');
|
||||
}
|
||||
text += ' ' + obj._id;
|
||||
console.log(text);
|
||||
};
|
||||
|
||||
this.listDirectory = function (adapter, path, allFiles, callback) {
|
||||
if (typeof path === 'function') {
|
||||
callback = path;
|
||||
path = '';
|
||||
allFiles = [];
|
||||
}
|
||||
if (typeof allFiles === 'function') {
|
||||
callback = allFiles;
|
||||
allFiles = [];
|
||||
}
|
||||
allFiles = allFiles || [];
|
||||
|
||||
path = path || '';
|
||||
|
||||
objects.readDir(adapter, path, null, function (err, files) {
|
||||
if (err && err.code === 'ENOTDIR') {
|
||||
var pos = path.lastIndexOf('/');
|
||||
if (pos !== -1) {
|
||||
var dir = path.substring(0, pos);
|
||||
var fname = path.substring(pos + 1);
|
||||
objects.readDir(adapter, dir, null, function (err, files) {
|
||||
if (err) {
|
||||
console.log('Cannot read "' + path + '": ' + err);
|
||||
callback(allFiles);
|
||||
} else {
|
||||
for (var f = 0; f < files.length; f++) {
|
||||
if (files[f].file === '.' || files[f].file === '..') continue;
|
||||
if (files[f].file === fname) {
|
||||
allFiles.push({adapter: adapter, path: path, file: files[f]});
|
||||
break;
|
||||
}
|
||||
}
|
||||
callback(allFiles);
|
||||
}
|
||||
});
|
||||
} else {
|
||||
callback(allFiles);
|
||||
}
|
||||
} else {
|
||||
var count = 0;
|
||||
for (var f = 0; f < files.length; f++) {
|
||||
if (files[f].file === '.' || files[f].file === '..') continue;
|
||||
allFiles.push({adapter: adapter, path: path, file: files[f]});
|
||||
if (files[f].isDir) {
|
||||
count++;
|
||||
that.listDirectory(adapter, path + '/' + files[f].file, allFiles, function () {
|
||||
if (!--count && callback) {
|
||||
callback(allFiles);
|
||||
}
|
||||
});
|
||||
}
|
||||
}
|
||||
if (!count && callback) callback(allFiles);
|
||||
}
|
||||
});
|
||||
};
|
||||
|
||||
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;
|
||||
}
|
||||
|
||||
function sortFiles (a, b) {
|
||||
var a1 = a.path + a.file.file;
|
||||
if (a1[0] !== '/') a1 = '/' + a1;
|
||||
var b1 = b.path + b.file.file;
|
||||
if (b1[0] !== '/') b1 = '/' + b1;
|
||||
return a1.localeCompare(b1);
|
||||
}
|
||||
|
||||
this.listAdaptersFiles = function (adapters, filter, callback) {
|
||||
if (typeof filter === 'function') {
|
||||
callback = filter;
|
||||
filter = null;
|
||||
}
|
||||
if (filter) {
|
||||
filter = filter.replace('*', '');
|
||||
if (filter[filter.length - 1] === '/') filter = filter.substring(0, filter.length - 1);
|
||||
}
|
||||
|
||||
|
||||
if (adapters && adapters.length) {
|
||||
var adapter = adapters.pop();
|
||||
this.listDirectory(adapter, filter, function (files) {
|
||||
files.sort(sortFiles);
|
||||
that.showFileHeader();
|
||||
for (var k = 0; k < files.length; k++) {
|
||||
if (filter && filter !== (files[k].path + '/'+ files[k].file.file).substring(0, filter.length)) continue;
|
||||
|
||||
that.showFile(files[k].adapter, files[k].path, files[k].file);
|
||||
}
|
||||
|
||||
that.listDirectory(adapter + '.admin', filter, function (files) {
|
||||
files.sort(sortFiles);
|
||||
for (var k = 0; k < files.length; k++) {
|
||||
if (filter && filter !== (files[k].path + '/'+ files[k].file.file).substring(0, filter.length)) continue;
|
||||
|
||||
that.showFile(files[k].adapter, files[k].path, files[k].file);
|
||||
}
|
||||
|
||||
that.listAdaptersFiles(adapters, callback);
|
||||
});
|
||||
});
|
||||
} else {
|
||||
if (callback) callback ();
|
||||
}
|
||||
};
|
||||
|
||||
this.list = function (type, filter, flags) {
|
||||
switch (type) {
|
||||
case 'objects':
|
||||
case 'o':
|
||||
objects.getObjectList(null, function (err, objs) {
|
||||
var reg = filter ? new RegExp(pattern2RegEx(filter)) : null;
|
||||
for (var i = 0; i < objs.rows.length; i++) {
|
||||
if (!reg || reg.test(objs.rows[i].value._id) || (objs.rows[i].value.common && reg.test(objs.rows[i].value.common.name))) {
|
||||
if (objs.rows[i].value.type) {
|
||||
var id = objs.rows[i].value._id;
|
||||
var type = objs.rows[i].value.type;
|
||||
if (id.length < 40) id += Array(40 - id.length).join(' ');
|
||||
if (type.length < 10) type += Array(10 - type.length).join(' ');
|
||||
|
||||
console.log(id + ': ' + type + ' - ' + (objs.rows[i].value.common.name || ''));
|
||||
} else {
|
||||
console.log(objs.rows[i].value._id);
|
||||
}
|
||||
}
|
||||
}
|
||||
setTimeout(function () {
|
||||
processExit();
|
||||
}, 1000);
|
||||
});
|
||||
break;
|
||||
|
||||
case 'states':
|
||||
case 's':
|
||||
states.getKeys(filter || '*', function (err, keys) {
|
||||
if (err) {
|
||||
console.error(err);
|
||||
processExit(23);
|
||||
}
|
||||
states.getStates(keys, function (err, states) {
|
||||
if (err) {
|
||||
console.error(err);
|
||||
processExit(23);
|
||||
}
|
||||
for (var i = 0; i < states.length; i++) {
|
||||
var id = keys[i];
|
||||
var from = states[i].from || '';
|
||||
var type = typeof states[i].val;
|
||||
if (type.length < 10) type += Array(10 - type.length).join(' ');
|
||||
if (id.length < 40) id += Array(40 - id.length).join(' ');
|
||||
if (from.length < 30) from += Array(30 - from.length).join(' ');
|
||||
console.log(id + ': from [' + from + '] (' + type + ') ' + (states[i].ack ? ' ack': 'not ack') + ' ' + JSON.stringify(states[i].val));
|
||||
}
|
||||
setTimeout(function () {
|
||||
processExit();
|
||||
}, 1000);
|
||||
});
|
||||
});
|
||||
break;
|
||||
|
||||
case 'adapters':
|
||||
case 'a':
|
||||
objects.getObjectList({startkey: 'system.adapter.', endkey: 'system.adapter.\u9999'}, function (err, objs) {
|
||||
var reg = filter ? new RegExp(pattern2RegEx('system.adapter.' + filter)) : null;
|
||||
for (var i = 0; i < objs.rows.length; i++) {
|
||||
if (objs.rows[i].value.type !== 'adapter') continue;
|
||||
if (!reg || reg.test(objs.rows[i].value._id) || (objs.rows[i].value.common && reg.test(objs.rows[i].value.common.name))) {
|
||||
var id = objs.rows[i].value._id;
|
||||
var name = objs.rows[i].value.common.name;
|
||||
|
||||
if (id.length < 40) id += Array(40 - id.length).join(' ');
|
||||
if (name.length < 12) name += Array(12 - name.length).join(' ');
|
||||
|
||||
var text = id + ': ' + name + ' - ' +
|
||||
objs.rows[i].value.common.version;
|
||||
|
||||
console.log(text);
|
||||
}
|
||||
}
|
||||
setTimeout(function () {
|
||||
processExit();
|
||||
}, 1000);
|
||||
});
|
||||
break;
|
||||
|
||||
case 'instances':
|
||||
case 'i':
|
||||
objects.getObjectList({startkey: 'system.adapter.', endkey: 'system.adapter.\u9999'}, function (err, objs) {
|
||||
var reg = filter ? new RegExp(pattern2RegEx('system.adapter.' + filter)) : null;
|
||||
objs.rows.sort(function (a, b) {
|
||||
if (a.id > b.id) return 1;
|
||||
if (a.id < b.id) return -1;
|
||||
return 0;
|
||||
});
|
||||
for (var i = 0; i < objs.rows.length; i++) {
|
||||
if (objs.rows[i].value.type !== 'instance') continue;
|
||||
if (!reg || reg.test(objs.rows[i].value._id) || (objs.rows[i].value.common && reg.test(objs.rows[i].value.common.name))) {
|
||||
if (flags.enabled && !objs.rows[i].value.common.enabled) continue;
|
||||
if (flags.disabled && objs.rows[i].value.common.enabled) continue;
|
||||
if (flags.port && objs.rows[i].value.native.port === undefined) continue;
|
||||
if (flags.ssl && objs.rows[i].value.native.secure === undefined) continue;
|
||||
if (flags.ip && objs.rows[i].value.native.bind === undefined) continue;
|
||||
|
||||
var id = objs.rows[i].value._id;
|
||||
var name = objs.rows[i].value.common.name;
|
||||
|
||||
if (id.length < 40) id += Array(40 - id.length).join(' ');
|
||||
if (name && name.length < 12) name += Array(12 - name.length).join(' ');
|
||||
|
||||
var text = id + ': ' + (name || '') + ' - ' +
|
||||
(objs.rows[i].value.common.enabled ? ' enabled' : 'disabled');
|
||||
|
||||
if (objs.rows[i].value.native && objs.rows[i].value.native.port) {
|
||||
text += ', port: ' + objs.rows[i].value.native.port;
|
||||
}
|
||||
if (objs.rows[i].value.native && objs.rows[i].value.native.bind) {
|
||||
text += ', bind: ' + objs.rows[i].value.native.bind;
|
||||
}
|
||||
if (objs.rows[i].value.native && objs.rows[i].value.native.secure) {
|
||||
text += ' (SSL)';
|
||||
}
|
||||
if (objs.rows[i].value.native && objs.rows[i].value.native.defaultUser) {
|
||||
text += ', run as: ' + objs.rows[i].value.native.defaultUser;
|
||||
}
|
||||
console.log(text);
|
||||
}
|
||||
}
|
||||
setTimeout(function () {
|
||||
processExit();
|
||||
}, 1000);
|
||||
});
|
||||
break;
|
||||
|
||||
case 'users':
|
||||
case 'u':
|
||||
objects.getObjectList({startkey: 'system.user.', endkey: 'system.user.\u9999'}, function (err, objs) {
|
||||
objects.getObjectList({startkey: 'system.group.', endkey: 'system.group.\u9999'}, function (err, groups) {
|
||||
var reg = filter ? new RegExp(pattern2RegEx('system.user.' + filter)) : null;
|
||||
console.log(' ID | Name | Active | Groups');
|
||||
console.log('---------------------------------------+-------------+----------+--------------');
|
||||
for (var i = 0; i < objs.rows.length; i++) {
|
||||
if (objs.rows[i].value.type !== 'user') continue;
|
||||
|
||||
if (!reg || reg.test(objs.rows[i].value._id) || (objs.rows[i].value.common && reg.test(objs.rows[i].value.common.name))) {
|
||||
var id = objs.rows[i].value._id;
|
||||
var name = objs.rows[i].value.common.name;
|
||||
|
||||
if (id.length < 40) id += Array(40 - id.length).join(' ');
|
||||
if (name.length < 12) name += Array(12 - name.length).join(' ');
|
||||
|
||||
var text = id + '| ' + name + ' | ' +
|
||||
(objs.rows[i].value.common.enabled ? ' enabled' : 'disabled') + ' |';
|
||||
var gs = [];
|
||||
// find all groups
|
||||
for (var g = 0; g < groups.rows.length; g++) {
|
||||
if (groups.rows[g].value.common.members && groups.rows[g].value.common.members.indexOf(objs.rows[i].value._id) !== -1) {
|
||||
gs.push(groups.rows[g].value._id.substring(13));
|
||||
}
|
||||
}
|
||||
console.log(text + ' ' + gs.join(', '));
|
||||
}
|
||||
}
|
||||
setTimeout(function () {
|
||||
processExit();
|
||||
}, 1000);
|
||||
});
|
||||
});
|
||||
break;
|
||||
|
||||
case 'groups':
|
||||
case 'g':
|
||||
objects.getObjectList({startkey: 'system.group.', endkey: 'system.group.\u9999'}, function (err, objs) {
|
||||
var reg = filter ? new RegExp(pattern2RegEx('system.group.' + filter)) : null;
|
||||
console.log('');
|
||||
console.log(' system.group | object | state | file | user | others | users');
|
||||
console.log(' | l r w d | l r w d | l r w c d | w c d | |');
|
||||
console.log('--------------------+---------+---------+-----------+-------+------------------------+---------');
|
||||
for (var i = 0; i < objs.rows.length; i++) {
|
||||
if (objs.rows[i].value.type !== 'group') continue;
|
||||
if (!reg || reg.test(objs.rows[i].value._id) || (objs.rows[i].value.common && reg.test(objs.rows[i].value.common.name))) {
|
||||
var id = objs.rows[i].value._id.substring(13);
|
||||
//var name = objs.rows[i].value.common.name;
|
||||
|
||||
if (id === 'administrator') {
|
||||
objs.rows[i].value.common.acl = {
|
||||
file: {
|
||||
read: true,
|
||||
write: true,
|
||||
'delete': true,
|
||||
create: true,
|
||||
list: true
|
||||
},
|
||||
object: {
|
||||
read: true,
|
||||
write: true,
|
||||
'delete': true,
|
||||
list: true
|
||||
},
|
||||
state: {
|
||||
read: true,
|
||||
write: true,
|
||||
'delete': true,
|
||||
create: true,
|
||||
list: true
|
||||
},
|
||||
user: {
|
||||
write: true,
|
||||
create: true,
|
||||
'delete': true
|
||||
},
|
||||
other: {
|
||||
execute: true,
|
||||
http: true,
|
||||
sendto: true
|
||||
}
|
||||
};
|
||||
}
|
||||
|
||||
if (id.length < 20) id += Array(20 - id.length).join(' ');
|
||||
var text = id;
|
||||
text += ' | ';
|
||||
if (objs.rows[i].value.common.acl && objs.rows[i].value.common.acl.object) {
|
||||
text += (objs.rows[i].value.common.acl.object.list ? '+': '-') + ' ';
|
||||
text += (objs.rows[i].value.common.acl.object.read ? '+': '-') + ' ';
|
||||
text += (objs.rows[i].value.common.acl.object.write ? '+': '-') + ' ';
|
||||
text += (objs.rows[i].value.common.acl.object.delete ? '+': '-') + ' ';
|
||||
text += '|';
|
||||
} else {
|
||||
text += ' |';
|
||||
}
|
||||
if (objs.rows[i].value.common.acl && objs.rows[i].value.common.acl.state) {
|
||||
text += ' ';
|
||||
text +=(objs.rows[i].value.common.acl.state.list ? '+': '-') + ' ';
|
||||
text +=(objs.rows[i].value.common.acl.state.read ? '+': '-') + ' ';
|
||||
text +=(objs.rows[i].value.common.acl.state.write ? '+': '-') + ' ';
|
||||
text +=(objs.rows[i].value.common.acl.state.delete ? '+': '-') + ' ';
|
||||
text += '|';
|
||||
} else {
|
||||
text += ' |';
|
||||
}
|
||||
if (objs.rows[i].value.common.acl && objs.rows[i].value.common.acl.file) {
|
||||
text += ' ';
|
||||
text += (objs.rows[i].value.common.acl.file.list ? '+': '-') + ' ';
|
||||
text += (objs.rows[i].value.common.acl.file.read ? '+': '-') + ' ';
|
||||
text += (objs.rows[i].value.common.acl.file.write ? '+': '-') + ' ';
|
||||
text += (objs.rows[i].value.common.acl.file.create ? '+': '-') + ' ';
|
||||
text += (objs.rows[i].value.common.acl.file.delete ? '+': '-') + ' ';
|
||||
text += '|';
|
||||
} else {
|
||||
text += ' |';
|
||||
}
|
||||
if (objs.rows[i].value.common.acl && objs.rows[i].value.common.acl.users) {
|
||||
text += ' ';
|
||||
text += (objs.rows[i].value.common.acl.users.write ? '+': '-') + ' ';
|
||||
text += (objs.rows[i].value.common.acl.users.create ? '+': '-') + ' ';
|
||||
text += (objs.rows[i].value.common.acl.users.delete ? '+': '-') + ' ';
|
||||
text += '|';
|
||||
} else {
|
||||
text += ' |';
|
||||
}
|
||||
if (objs.rows[i].value.common.acl && objs.rows[i].value.common.acl.other) {
|
||||
text += ' ';
|
||||
var others = '';
|
||||
for (var r in objs.rows[i].value.common.acl.other) {
|
||||
others += r + (objs.rows[i].value.common.acl.other[r] ? '+' : '-') + ' ';
|
||||
}
|
||||
if (others.length < 23) others += Array(23 - others.length).join(' ');
|
||||
text += others + '|';
|
||||
} else {
|
||||
text += Array(25).join(' ') + '|';
|
||||
}
|
||||
|
||||
//if (name.length < 30) name += Array(30 - name.length).join(' ');
|
||||
if ( objs.rows[i].value.common.members) {
|
||||
for (var m = 0; m < objs.rows[i].value.common.members.length; m++) {
|
||||
objs.rows[i].value.common.members[m] = objs.rows[i].value.common.members[m].substring(12);
|
||||
}
|
||||
text += ' ' + objs.rows[i].value.common.members.join(', ');
|
||||
}
|
||||
//text += '| (' + name + ')';
|
||||
console.log(text);
|
||||
}
|
||||
}
|
||||
|
||||
console.log('--------------------+---------+---------+-----------+-------+------------------------+---------');
|
||||
console.log('Legend: (l)ist, (r)ead, (w)rite, (c)reate, (d)elete');
|
||||
setTimeout(function () {
|
||||
processExit();
|
||||
}, 1000);
|
||||
});
|
||||
break;
|
||||
|
||||
case 'h':
|
||||
case 'hosts':
|
||||
objects.getObjectList({startkey: 'system.host.', endkey: 'system.host.\u9999'}, function (err, objs) {
|
||||
states.getKeys('system.host.*', function (err, keys) {
|
||||
states.getStates(keys, function (err, states) {
|
||||
var reg = filter ? new RegExp(pattern2RegEx('system.host.' + filter)) : null;
|
||||
|
||||
for (var i = 0; i < objs.rows.length; i++) {
|
||||
if (objs.rows[i].value.type !== 'host') continue;
|
||||
if (!reg || reg.test(objs.rows[i].value._id) || (objs.rows[i].value.common && reg.test(objs.rows[i].value.common.name))) {
|
||||
var id = objs.rows[i].value._id.substring(12);
|
||||
var name = objs.rows[i].value.common.name;
|
||||
if (id.length < 20) id += Array(20 - id.length).join(' ');
|
||||
var hostname = objs.rows[i].value.common.hostname;
|
||||
if (hostname.length < 15) hostname += Array(15 - hostname.length).join(' ');
|
||||
var version = objs.rows[i].value.common.installedVersion;
|
||||
var alive = '';
|
||||
var uptime = '';
|
||||
for (var k = 0; k < keys.length; k++) {
|
||||
if (keys[k] === objs.rows[i].value._id + '.alive') {
|
||||
alive = states[k].val;
|
||||
}
|
||||
if (keys[k] === objs.rows[i].value._id + '.uptime') {
|
||||
uptime = states[k].val;
|
||||
}
|
||||
}
|
||||
alive = alive ? 'alive' : ' dead';
|
||||
//if (uptime.toString().length < 10) uptime = Array(10 - uptime.toString().length).join(' ') + uptime.toString();
|
||||
if (!uptime) uptime = '-';
|
||||
var text = id + ' ' + name + ' (version: ' + version + ', hostname: ' + hostname + ', ' + alive + ', uptime: ' + uptime + ')';
|
||||
// todo
|
||||
console.log(text);
|
||||
}
|
||||
}
|
||||
|
||||
setTimeout(function () {
|
||||
processExit();
|
||||
}, 1000);
|
||||
});
|
||||
});
|
||||
});
|
||||
break;
|
||||
|
||||
case 'enums':
|
||||
case 'e':
|
||||
objects.getObjectList({startkey: 'enum.', endkey: 'enum.\u9999'}, function (err, objs) {
|
||||
var reg = filter ? new RegExp(pattern2RegEx('enum.' + filter)) : null;
|
||||
for (var i = 0; i < objs.rows.length; i++) {
|
||||
if (objs.rows[i].value.type !== 'enum') continue;
|
||||
if (!reg || reg.test(objs.rows[i].value._id) || (objs.rows[i].value.common && reg.test(objs.rows[i].value.common.name))) {
|
||||
console.log('\n=====================================================================================');
|
||||
var id = objs.rows[i].value._id.substring(5);
|
||||
var name = objs.rows[i].value.common.name;
|
||||
if (id.length < 20) id += Array(20 - id.length).join(' ');
|
||||
console.log(id + '(' + name + ')');
|
||||
console.log('-------------------------------------------------------------------------------------');
|
||||
|
||||
if (objs.rows[i].value.common.members) {
|
||||
console.log(objs.rows[i].value.common.members.join(', '));
|
||||
}
|
||||
}
|
||||
}
|
||||
setTimeout(function () {
|
||||
processExit();
|
||||
}, 1000);
|
||||
});
|
||||
break;
|
||||
|
||||
case 'files':
|
||||
case 'f':
|
||||
objects.getObjectList({startkey: 'system.adapter.', endkey: 'system.adapter.\u9999'}, function (err, objs) {
|
||||
var adapter = filter || null;
|
||||
var names = filter ? filter.split('/') : null;
|
||||
if (names && !names[0]) names.splice(0, 1);
|
||||
|
||||
var adapters = [];
|
||||
for (var i = 0; i < objs.rows.length; i++) {
|
||||
if (objs.rows[i].value.type === 'adapter') {
|
||||
if (adapter && objs.rows[i].value.common.name !== names[0]) continue;
|
||||
adapters.push(objs.rows[i].value.common.name);
|
||||
} else if (objs.rows[i].value.type === 'instance') {
|
||||
if (adapter && objs.rows[i].value._id.substring(15) !== names[0]) continue;
|
||||
adapters.push(objs.rows[i].value._id.substring(15));
|
||||
}
|
||||
}
|
||||
if (names) names.shift();
|
||||
that.listAdaptersFiles(adapters, names ? names.join('/') : null, function () {
|
||||
setTimeout(function () {
|
||||
processExit();
|
||||
}, 1000);
|
||||
});
|
||||
});
|
||||
break;
|
||||
|
||||
default:
|
||||
if (type) {
|
||||
console.error('Unknown type: ' + type);
|
||||
processExit(23);
|
||||
} else {
|
||||
console.log('Please specify type: objects, states, instances, adapters, users, groups, enums, files');
|
||||
processExit();
|
||||
}
|
||||
break;
|
||||
}
|
||||
};
|
||||
}
|
||||
|
||||
module.exports = List;
|
||||
294
lib/setup/setupMultihost.js
Normal file
294
lib/setup/setupMultihost.js
Normal file
@@ -0,0 +1,294 @@
|
||||
'use strict';
|
||||
function Multihost(options) {
|
||||
var fs = require('fs');
|
||||
var tools = require(__dirname + '/../tools.js');
|
||||
var configName = tools.getConfigFileName();
|
||||
var that = this;
|
||||
|
||||
options = options || {};
|
||||
|
||||
var params = options.params || {};
|
||||
var objects = options.objects;
|
||||
|
||||
function getConfig() {
|
||||
var config;
|
||||
// read actual configuration
|
||||
try {
|
||||
if (fs.existsSync(configName)) {
|
||||
config = JSON.parse(fs.readFileSync(configName, 'utf8'));
|
||||
} else {
|
||||
config = require(__dirname + '/../../conf/' + tools.appName + '-dist.json');
|
||||
}
|
||||
} catch (e) {
|
||||
config = require(__dirname + '/../../conf/' + tools.appName + '-dist.json');
|
||||
}
|
||||
return config;
|
||||
}
|
||||
|
||||
function leftPad(text, len) {
|
||||
text = text || '';
|
||||
if (text.length >= len) return len;
|
||||
return new Array(len - text.length).join(' ') + text;
|
||||
}
|
||||
|
||||
this.showHosts = function (list) {
|
||||
if (!list || !list.length) {
|
||||
console.info('Nothing found');
|
||||
} else {
|
||||
for (var i = 0; i < list.length; i++) {
|
||||
console.log((i + 1) + ' | ' + leftPad(list[i].hostname, 20) + ' | ' + (list[i].slave ? 'slave' : ' host') + ' | ' + leftPad(list[i].ip, 20) + ' | ' + JSON.stringify(list[i].info));
|
||||
}
|
||||
}
|
||||
};
|
||||
|
||||
this.browse = function (callback) {
|
||||
var MHClient = require(__dirname + '/../multihostClient');
|
||||
var mhClient = new MHClient();
|
||||
mhClient.browse(2000, params.debug, function (err, list) {
|
||||
if (err) {
|
||||
callback('Cannot browse: ' + err);
|
||||
} else {
|
||||
callback(null, list);
|
||||
}
|
||||
});
|
||||
};
|
||||
|
||||
function showMHState(config, changed, callback) {
|
||||
if (config.multihostService.enabled) {
|
||||
if (config.objects.type === 'file' && (config.objects.host === '127.0.0.1' || config.objects.host === 'localhost')) {
|
||||
console.log('Server accept connections for objects on all IP addresses.');
|
||||
config.objects.host = '0.0.0.0';
|
||||
changed = true;
|
||||
} else if (config.objects.type === 'redis') {
|
||||
console.log('Please check the binding of redis service. By default it is only local: http://download.redis.io/redis-stable/redis.conf\nChange "bind 127.0.0.1" to "bind 0.0.0.0" or to others.')
|
||||
}
|
||||
if (config.states.type === 'file' && (config.states.host === '127.0.0.1' || config.states.host === 'localhost')) {
|
||||
console.log('Server accept connections for states on all IP addresses.');
|
||||
config.states.host = '0.0.0.0';
|
||||
changed = true;
|
||||
} else if (config.states.type === 'redis') {
|
||||
console.log('Please check the binding of redis service. By default it is only local: http://download.redis.io/redis-stable/redis.conf\nChange "bind 127.0.0.1" to "bind 0.0.0.0" or to others.')
|
||||
}
|
||||
}
|
||||
if (!changed) {
|
||||
console.log('Nothing changed.');
|
||||
} else {
|
||||
fs.writeFileSync(configName, JSON.stringify(config, null, 2));
|
||||
console.log('Please restart yunkong2: "yunkong2 restart"');
|
||||
}
|
||||
console.log('\n');
|
||||
console.log('Miltihost: ' + (config.multihostService.enabled ? 'enabled' : 'disabled'));
|
||||
console.log('Authentication: ' + (config.multihostService.secure ? 'enabled' : 'disabled'));
|
||||
console.log('Objects: ' + config.objects.type + ' on ' + config.objects.host);
|
||||
console.log('States: ' + config.states.type + ' on ' + config.states.host);
|
||||
callback();
|
||||
}
|
||||
|
||||
this.enable = function (isEnable, callback) {
|
||||
var changed = false;
|
||||
var config = getConfig();
|
||||
config.multihostService = config.multihostService || {enabled: false, secure: true};
|
||||
|
||||
if (isEnable && !config.multihostService.enabled) {
|
||||
changed = true;
|
||||
config.multihostService.enabled = true;
|
||||
config.multihostService.password = '';
|
||||
console.log('Miltihost activated.')
|
||||
} else if (!isEnable && config.multihostService.enabled) {
|
||||
changed = true;
|
||||
config.multihostService.enabled = false;
|
||||
config.multihostService.password = '';
|
||||
console.log('Miltihost deactivated.')
|
||||
}
|
||||
if (params.secure === undefined) {
|
||||
params.secure = true;
|
||||
}
|
||||
|
||||
if (isEnable && (config.multihostService.secure !== params.secure || (config.multihostService.secure && !config.multihostService.password))) {
|
||||
changed = true;
|
||||
config.multihostService.secure = params.secure;
|
||||
console.log('Authentication ' + (params.secure ? 'activated' : 'deactivated') + '.');
|
||||
if (config.multihostService.secure) {
|
||||
var prompt = require('prompt');
|
||||
prompt.message = '';
|
||||
prompt.delimiter = '';
|
||||
var schema = {
|
||||
properties: {
|
||||
password: {
|
||||
description: 'Enter secret phrase for connection:',
|
||||
pattern: /^[^'"]+$/,
|
||||
message: 'No " are allowed',
|
||||
hidden: true
|
||||
},
|
||||
passwordRepeat: {
|
||||
description: 'Repeat secret phrase for connection:',
|
||||
pattern: /^[^'"]+$/,
|
||||
message: 'No " are allowed',
|
||||
hidden: true
|
||||
}
|
||||
}
|
||||
};
|
||||
prompt.start();
|
||||
|
||||
prompt.get(schema, function (err, password) {
|
||||
if (password && password.password) {
|
||||
if (password.password !== password.passwordRepeat) {
|
||||
callback('Secret phrases are not equal!');
|
||||
} else {
|
||||
objects.getObject('system.config', function (err, obj) {
|
||||
tools.encryptPhrase(obj.native.secret, password.password, function (encoded) {
|
||||
config.multihostService.password = encoded;
|
||||
showMHState(config, changed, callback);
|
||||
})
|
||||
});
|
||||
}
|
||||
} else {
|
||||
callback('No secret phrase entered!');
|
||||
}
|
||||
});
|
||||
} else {
|
||||
showMHState(config, changed, callback);
|
||||
}
|
||||
} else {
|
||||
showMHState(config, changed, callback);
|
||||
}
|
||||
};
|
||||
|
||||
this.status = function (callback) {
|
||||
var config = getConfig();
|
||||
config.multihostService = config.multihostService || {enabled: false, secure: true};
|
||||
showMHState(config, false, callback);
|
||||
};
|
||||
|
||||
function readPassword(callback) {
|
||||
var readline = require('readline');
|
||||
|
||||
var rl = readline.createInterface({
|
||||
input: process.stdin,
|
||||
output: process.stdout
|
||||
});
|
||||
|
||||
function hidden(query, callback) {
|
||||
var stdin = process.openStdin();
|
||||
process.stdin.on('data', function (char) {
|
||||
char = char + '';
|
||||
switch (char) {
|
||||
case '\n':
|
||||
case '\r':
|
||||
case '\u0004':
|
||||
stdin.pause();
|
||||
break;
|
||||
|
||||
default:
|
||||
process.stdout.write('\x1B[2K\x1B[200D' + query + new Array(rl.line.length + 1).join('*'));
|
||||
break;
|
||||
}
|
||||
});
|
||||
|
||||
rl.question(query, function (value) {
|
||||
rl.history = rl.history.slice(1);
|
||||
callback(value);
|
||||
});
|
||||
}
|
||||
|
||||
hidden('Enter secret phrase for connection: ', function(password) {
|
||||
callback(password);
|
||||
});
|
||||
}
|
||||
|
||||
function connect(mhClient, ip, pass, callback) {
|
||||
mhClient.connect(ip, pass, function (err, oObjects, oStates, ipHost) {
|
||||
if (err) {
|
||||
callback('Cannot connect to "' + ip + '": ' + err);
|
||||
} else if (oObjects && oStates) {
|
||||
var config = getConfig();
|
||||
config.objects = oObjects;
|
||||
config.states = oStates;
|
||||
if (config.objects.host === '127.0.0.1' || config.objects.host === 'localhost' || config.states.host === '127.0.0.1' || config.states.host === 'localhost') {
|
||||
callback('IP Address of the host is 127.0.0.1. It accepts no connections. Please change.');
|
||||
} else {
|
||||
if (config.states.host === '0.0.0.0') {
|
||||
config.states.host = ipHost;
|
||||
}
|
||||
if (config.objects.host === '0.0.0.0') {
|
||||
config.objects.host = ipHost;
|
||||
}
|
||||
|
||||
fs.writeFileSync(configName, JSON.stringify(config, null, 2));
|
||||
console.log('Config ok. Please restart yunkong2: "yunkong2 restart"');
|
||||
callback();
|
||||
}
|
||||
} else {
|
||||
callback('No configuration received!');
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
this.connect = function (number, pass, callback) {
|
||||
if (typeof pass === 'function') {
|
||||
callback = pass;
|
||||
pass = null;
|
||||
}
|
||||
if (typeof number === 'function') {
|
||||
callback = number;
|
||||
number = null;
|
||||
}
|
||||
var MHClient = require(__dirname + '/../multihostClient');
|
||||
var mhClient = new MHClient();
|
||||
|
||||
mhClient.browse(2000, params.debug, function (err, list) {
|
||||
if (err) {
|
||||
callback('Cannot browse: ' + err);
|
||||
} else {
|
||||
that.showHosts(list);
|
||||
|
||||
if (number !== null && number !== undefined && parseInt(number, 10) > 0) {
|
||||
number = parseInt(number, 10);
|
||||
if (list && number < list.length + 1) {
|
||||
if (!pass) {
|
||||
callback('No password defined: please use "multihost connect <NUMBER> <PASSWORD>"');
|
||||
} else {
|
||||
connect(mhClient, list[number - 1].ip, pass, callback);
|
||||
}
|
||||
} else {
|
||||
callback('Invalid index: ' + number);
|
||||
}
|
||||
} else
|
||||
if (list && list.length) {
|
||||
var readline = require('readline');
|
||||
|
||||
var rl = readline.createInterface({
|
||||
input: process.stdin,
|
||||
output: process.stdout
|
||||
});
|
||||
rl.question('Please select host [1]: ', function (answer) {
|
||||
if (answer === '' || answer === null || answer === undefined) {
|
||||
answer = 1;
|
||||
}
|
||||
answer = parseInt(answer, 10) - 1;
|
||||
if (!list[answer]) {
|
||||
rl.close();
|
||||
callback('Invalid index: ' + answer);
|
||||
} else {
|
||||
if (list[answer].auth) {
|
||||
readPassword(function (password) {
|
||||
if (password) {
|
||||
connect(mhClient, list[answer].ip, password, callback);
|
||||
} else {
|
||||
callback('No password entered!');
|
||||
}
|
||||
});
|
||||
} else {
|
||||
connect(mhClient, list[answer].ip, null, callback);
|
||||
}
|
||||
}
|
||||
});
|
||||
} else {
|
||||
callback(null, list);
|
||||
}
|
||||
}
|
||||
});
|
||||
};
|
||||
}
|
||||
|
||||
module.exports = Multihost;
|
||||
279
lib/setup/setupRepo.js
Normal file
279
lib/setup/setupRepo.js
Normal file
@@ -0,0 +1,279 @@
|
||||
'use strict';
|
||||
function Repo(options) {
|
||||
const tools = require(__dirname + '/../tools.js');
|
||||
const extend = require('node.extend');
|
||||
let defaultSystemRepo = {
|
||||
"common": {
|
||||
"name": "System repositories",
|
||||
"dontDelete": true
|
||||
},
|
||||
"native": {
|
||||
"repositories": {
|
||||
"default": {
|
||||
"link": "http://download.yunkong2.net/sources-dist.json",
|
||||
"json": null
|
||||
},
|
||||
"latest": {
|
||||
"link": "http://download.yunkong2.net/sources-dist-latest.json",
|
||||
"json": null
|
||||
}
|
||||
}
|
||||
},
|
||||
"_id": "system.repositories",
|
||||
"type": "config"
|
||||
};
|
||||
|
||||
options = options || {};
|
||||
|
||||
if (!options.objects) throw 'Invalid arguments: objects is missing';
|
||||
|
||||
var objects = options.objects;
|
||||
|
||||
function updateRepo(repoUrl, callback) {
|
||||
var result = {};
|
||||
|
||||
if (!repoUrl || typeof repoUrl !== 'object') {
|
||||
tools.getRepositoryFile(repoUrl, function (err, sources) {
|
||||
updateRepo(sources, callback);
|
||||
});
|
||||
return;
|
||||
}
|
||||
var sources = repoUrl;
|
||||
var downloads = [];
|
||||
|
||||
function download() {
|
||||
if (downloads.length < 1) {
|
||||
console.log('update done');
|
||||
if (callback) callback(result);
|
||||
} else {
|
||||
var name = downloads.pop();
|
||||
|
||||
if (sources[name].version) {
|
||||
result[name] = sources[name];
|
||||
} else if (sources[name].meta) {
|
||||
tools.getJson(sources[name].meta, function (ioPack) {
|
||||
if (ioPack && ioPack.common) {
|
||||
result[name] = extend(true, sources[name], ioPack.common);
|
||||
}
|
||||
setImmediate(download);
|
||||
});
|
||||
return;
|
||||
} else if (sources[name].url) {
|
||||
console.log('Cannot get version of "' + name + '".');
|
||||
result[name] = sources[name];
|
||||
} else {
|
||||
console.log('Cannot get any information of "' + name + '". Ignored.');
|
||||
}
|
||||
setImmediate(download);
|
||||
}
|
||||
}
|
||||
|
||||
// Read repository file, local or by url
|
||||
for (var name in sources) {
|
||||
if (!sources.hasOwnProperty(name)) continue;
|
||||
downloads.push(name);
|
||||
}
|
||||
|
||||
download(/*sources*/);
|
||||
}
|
||||
|
||||
this.showRepo = function (repoUrl, flags, callback) {
|
||||
if (typeof flags === 'function') {
|
||||
callback = flags;
|
||||
flags = {};
|
||||
}
|
||||
|
||||
function showRepoResult(_name, sources) {
|
||||
var installed = tools.getInstalledInfo();
|
||||
var updatable;
|
||||
var keys = [];
|
||||
for (var key in sources) {
|
||||
if (!sources.hasOwnProperty(key)) continue;
|
||||
keys.push(key);
|
||||
}
|
||||
keys.sort();
|
||||
|
||||
for (var i = 0; i < keys.length; i++) {
|
||||
var name = keys[i];
|
||||
if (!sources.hasOwnProperty(name)) continue;
|
||||
updatable = false;
|
||||
var text = (sources[name].controller ? 'Controller ' : 'Adapter ');
|
||||
text += '"' + name + '"' + ((name.length < 15) ? new Array(15 - name.length).join(' ') : '');
|
||||
|
||||
var tLen = 10;
|
||||
if (name.length >= 15) tLen -= (name.length > tLen ? 1 : 0);
|
||||
if (tLen < 0) tLen = 0;
|
||||
|
||||
if (sources[name].version) {
|
||||
text += ': ' + sources[name].version + ((sources[name].version.length < tLen) ? new Array(tLen - sources[name].version.length).join(' ') : '');
|
||||
} else {
|
||||
text += new Array(tLen).join(' ')
|
||||
}
|
||||
if ((flags.installed || flags.i) && !installed[name]) {
|
||||
continue;
|
||||
}
|
||||
|
||||
if (installed[name] && installed[name].version) {
|
||||
text += ', installed ' + installed[name].version;
|
||||
if (sources[name].version !== installed[name].version &&
|
||||
sources[name].version &&
|
||||
!tools.upToDate(sources[name].version, installed[name].version)) {
|
||||
updatable = true;
|
||||
text += ' [Updateable]';
|
||||
}
|
||||
}
|
||||
if ((flags.updatable || flags.u) && !updatable) {
|
||||
continue;
|
||||
}
|
||||
console.log(text);
|
||||
}
|
||||
}
|
||||
|
||||
// Get the repositories
|
||||
objects.getObject('system.config', function (err, sysConfig) {
|
||||
objects.getObject('system.repositories', function (err, obj) {
|
||||
if (err || !obj) {
|
||||
console.log('Error: Object "system.config" not found');
|
||||
} else {
|
||||
if (!obj.native || !obj.native.repositories) {
|
||||
console.log('Error: no repositories found in the "system.config');
|
||||
} else {
|
||||
repoUrl = repoUrl || sysConfig.common.activeRepo;
|
||||
|
||||
console.log('Used repository: ' + repoUrl);
|
||||
|
||||
// If known repository
|
||||
if (obj.native.repositories[repoUrl]) {
|
||||
if (typeof obj.native.repositories[repoUrl] === 'string') {
|
||||
obj.native.repositories[repoUrl] = {
|
||||
link: obj.native.repositories[repoUrl],
|
||||
json: null
|
||||
};
|
||||
}
|
||||
|
||||
updateRepo(obj.native.repositories[repoUrl].link, function (sources) {
|
||||
obj.native.repositories[repoUrl].json = sources;
|
||||
obj.from = 'system.host.' + tools.getHostName() + '.cli';
|
||||
obj.ts = new Date().getTime();
|
||||
objects.setObject(obj._id, obj, function () {
|
||||
showRepoResult(repoUrl, sources);
|
||||
if (callback) callback();
|
||||
});
|
||||
});
|
||||
} else {
|
||||
updateRepo(repoUrl, function (sources) {
|
||||
showRepoResult(null, sources);
|
||||
if (callback) callback();
|
||||
});
|
||||
}
|
||||
}
|
||||
}
|
||||
});
|
||||
});
|
||||
};
|
||||
|
||||
this.showRepoStatus = function (callback) {
|
||||
objects.getObject('system.repositories', function (err, obj) {
|
||||
if (err || !obj) {
|
||||
console.error('Cannot get list: ' + err);
|
||||
callback(102);
|
||||
} else {
|
||||
if (obj.native.repositories) {
|
||||
for (var r in obj.native.repositories) {
|
||||
if (obj.native.repositories.hasOwnProperty(r)){
|
||||
console.log(r + (r.length < 12 ? new Array(12 - r.length).join(' ') : '') + ': ' + obj.native.repositories[r].link);
|
||||
}
|
||||
}
|
||||
objects.getObject('system.config', function (err, obj) {
|
||||
if (obj) {
|
||||
console.log('\nActive repo: ' + obj.common.activeRepo);
|
||||
}
|
||||
callback();
|
||||
});
|
||||
} else {
|
||||
console.error('Cannot get list: ' + err);
|
||||
callback(102);
|
||||
}
|
||||
}
|
||||
});
|
||||
};
|
||||
|
||||
this.add = function (repoName, repoUrl, callback) {
|
||||
objects.getObject('system.repositories', function (err, obj) {
|
||||
if (err) {
|
||||
callback && callback(err);
|
||||
} else
|
||||
if (!obj) {
|
||||
obj = defaultSystemRepo;
|
||||
}
|
||||
|
||||
if (obj.native.repositories[repoName]) {
|
||||
callback && callback('Repository "' + repoName + '" yet exists: ' + obj.native.repositories[repoName].link);
|
||||
} else {
|
||||
obj.native.repositories[repoName] = {
|
||||
link: repoUrl,
|
||||
json: null
|
||||
};
|
||||
obj.from = 'system.host.' + tools.getHostName() + '.cli';
|
||||
obj.ts = new Date().getTime();
|
||||
objects.setObject('system.repositories', obj, callback);
|
||||
}
|
||||
});
|
||||
};
|
||||
|
||||
this.del = function (repoName, callback) {
|
||||
objects.getObject('system.config', function (err, obj) {
|
||||
if (err) {
|
||||
callback && callback(err);
|
||||
} else {
|
||||
if (obj.common.activeRepo === repoName) {
|
||||
callback && callback('Cannot delete active repository: ' + repoName);
|
||||
} else {
|
||||
objects.getObject('system.repositories', function (err, obj) {
|
||||
if (err) {
|
||||
callback && callback(err);
|
||||
} else if (!obj) {
|
||||
callback && callback();
|
||||
} else {
|
||||
if (!obj.native.repositories[repoName]) {
|
||||
callback && callback('Repository "' + repoName + '" not found.');
|
||||
} else {
|
||||
delete obj.native.repositories[repoName];
|
||||
obj.from = 'system.host.' + tools.getHostName() + '.cli';
|
||||
obj.ts = new Date().getTime();
|
||||
objects.setObject('system.repositories', obj, callback);
|
||||
}
|
||||
}
|
||||
});
|
||||
}
|
||||
}
|
||||
});
|
||||
};
|
||||
|
||||
this.setActive = function (repoName, callback) {
|
||||
objects.getObject('system.repositories', function (err, obj) {
|
||||
if (err) {
|
||||
callback && callback(err);
|
||||
} else
|
||||
if (!obj) {
|
||||
obj = defaultSystemRepo;
|
||||
}
|
||||
if (!obj.native.repositories[repoName]) {
|
||||
callback && callback('Repository "' + repoName + '" not found.');
|
||||
} else {
|
||||
objects.getObject('system.config', function (err, obj) {
|
||||
if (err) {
|
||||
callback && callback(err);
|
||||
} else {
|
||||
obj.common.activeRepo = repoName;
|
||||
obj.from = 'system.host.' + tools.getHostName() + '.cli';
|
||||
obj.ts = new Date().getTime();
|
||||
objects.setObject('system.config', obj, callback);
|
||||
}
|
||||
});
|
||||
}
|
||||
})
|
||||
};
|
||||
}
|
||||
|
||||
module.exports = Repo;
|
||||
199
lib/setup/setupSetup.js
Normal file
199
lib/setup/setupSetup.js
Normal file
@@ -0,0 +1,199 @@
|
||||
'use strict';
|
||||
|
||||
function Setup(options) {
|
||||
var fs = require('fs');
|
||||
var tools = require(__dirname + '/../tools.js');
|
||||
|
||||
options = options || {};
|
||||
|
||||
var processExit = options.processExit;
|
||||
var dbConnect = options.dbConnect;
|
||||
var params = options.params;
|
||||
var password;
|
||||
var objects;
|
||||
|
||||
function mkpathSync(rootpath, dirpath) {
|
||||
// Remove filename
|
||||
dirpath = dirpath.split('/');
|
||||
dirpath.pop();
|
||||
if (!dirpath.length) return;
|
||||
|
||||
for (var i = 0; i < dirpath.length; i++) {
|
||||
rootpath += dirpath[i] + '/';
|
||||
if (!fs.existsSync(rootpath)) {
|
||||
if (dirpath[i] !== '..') {
|
||||
fs.mkdirSync(rootpath);
|
||||
} else {
|
||||
throw 'Cannot create ' + rootpath + dirpath.join('/');
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
function setupReady(callback) {
|
||||
if (!callback) {
|
||||
console.log('database setup done. you can add adapters and start ' + tools.appName + ' now');
|
||||
processExit(0);
|
||||
} else {
|
||||
callback();
|
||||
}
|
||||
}
|
||||
|
||||
function dbSetup(iopkg, callback) {
|
||||
if (iopkg.objects && iopkg.objects.length > 0) {
|
||||
var obj = iopkg.objects.pop();
|
||||
objects.getObject(obj._id, function (err, _obj) {
|
||||
if (err || !_obj) {
|
||||
obj.from = 'system.host.' + tools.getHostName() + '.cli';
|
||||
obj.ts = new Date().getTime();
|
||||
objects.setObject(obj._id, obj, function () {
|
||||
console.log('object ' + obj._id + ' created');
|
||||
setTimeout(dbSetup, 25, iopkg, callback);
|
||||
});
|
||||
} else {
|
||||
console.log('object ' + obj._id + ' yet exists');
|
||||
setTimeout(dbSetup, 25, iopkg, callback);
|
||||
}
|
||||
});
|
||||
} else {
|
||||
tools.createUuid(objects, function () {
|
||||
// check if encrypt secret exists
|
||||
objects.getObject('system.config', function (err, obj) {
|
||||
if (obj && (!obj.native || !obj.native.secret)) {
|
||||
require('crypto').randomBytes(24, function (ex, buf) {
|
||||
obj.native = obj.native || {};
|
||||
obj.native.secret = buf.toString('hex');
|
||||
obj.from = 'system.host.' + tools.getHostName() + '.cli';
|
||||
obj.ts = new Date().getTime();
|
||||
objects.setObject('system.config', obj, function () {
|
||||
setupReady(callback);
|
||||
});
|
||||
});
|
||||
} else {
|
||||
setupReady(callback);
|
||||
}
|
||||
});
|
||||
});
|
||||
}
|
||||
}
|
||||
|
||||
function setupObjects(callback) {
|
||||
dbConnect(params, function (_objects, _states) {
|
||||
objects = _objects;
|
||||
var iopkg = JSON.parse(fs.readFileSync(__dirname + '/../../io-package.json', 'utf8'));
|
||||
dbSetup(iopkg, callback);
|
||||
});
|
||||
}
|
||||
|
||||
this.setup = function (callback, ignoreIfExist, useRedis) {
|
||||
password = require(__dirname + '/../password');
|
||||
|
||||
var config;
|
||||
var isCreated = false;
|
||||
var platform = require('os').platform();
|
||||
var otherInstallDirs = [];
|
||||
|
||||
// Delete files for other OS
|
||||
if (platform.match(/^win/)) {
|
||||
otherInstallDirs.push(__dirname + '/../../' + tools.appName);
|
||||
otherInstallDirs.push(__dirname + '/../../killall.sh');
|
||||
otherInstallDirs.push(__dirname + '/../../reinstall.sh');
|
||||
} else {
|
||||
otherInstallDirs.push(__dirname + '/../../_service_' + tools.appName + '.bat');
|
||||
otherInstallDirs.push(__dirname + '/../../' + tools.appName + '.bat');
|
||||
// copy scripts to root directory
|
||||
if (fs.existsSync(__dirname + '/../../../../node_modules/')) {
|
||||
try {
|
||||
if (fs.existsSync(__dirname + '/../../killall.sh')) {
|
||||
fs.writeFileSync(__dirname + '/../../../../killall.sh', fs.readFileSync(__dirname + '/../../killall.sh'));
|
||||
}
|
||||
if (fs.existsSync(__dirname + '/../../reinstall.sh')) {
|
||||
fs.writeFileSync(__dirname + '/../../../../reinstall.sh', fs.readFileSync(__dirname + '/../../reinstall.sh'));
|
||||
}
|
||||
if (fs.existsSync(__dirname + '/../..' + tools.appName)) {
|
||||
fs.writeFileSync(__dirname + '/../../../..' + tools.appName, fs.readFileSync(__dirname + '/../..' + tools.appName));
|
||||
}
|
||||
} catch (e) {
|
||||
console.warn('Cannot write file. Not critical: ' + e);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
for (var t = 0; t < otherInstallDirs.length; t++) {
|
||||
if (fs.existsSync(otherInstallDirs[t])) {
|
||||
var stat = fs.statSync(otherInstallDirs[t]);
|
||||
if (stat.isDirectory()) {
|
||||
var files = fs.readdirSync(otherInstallDirs[t]);
|
||||
for (var f = 0; f < files.length; f++) {
|
||||
fs.unlinkSync(otherInstallDirs[t] + '/' + files[f]);
|
||||
}
|
||||
fs.rmdirSync(otherInstallDirs[t]);
|
||||
} else {
|
||||
try {
|
||||
fs.unlinkSync(otherInstallDirs[t]);
|
||||
}
|
||||
catch(e) {
|
||||
console.warn('Cannot delete file. Not critical: ' + e);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// Create log and tmp directory
|
||||
if (!fs.existsSync(__dirname + '/../../tmp')) fs.mkdirSync(__dirname + '/../../tmp');
|
||||
|
||||
if (!fs.existsSync(tools.getConfigFileName())) {
|
||||
isCreated = true;
|
||||
if (fs.existsSync(__dirname + '/../../conf/' + tools.appName + '-dist.json')) {
|
||||
config = require(__dirname + '/../../conf/' + tools.appName + '-dist.json');
|
||||
} else {
|
||||
config = require(__dirname + '/../../conf/' + tools.appName.toLowerCase() + '-dist.json');
|
||||
}
|
||||
console.log('creating conf/' + tools.appName + '.json');
|
||||
config.objects.host = params.objects || '127.0.0.1';
|
||||
config.states.host = params.states || '127.0.0.1';
|
||||
if (useRedis) {
|
||||
config.states.type = 'redis';
|
||||
config.states.port = 6379;
|
||||
}
|
||||
|
||||
// this path is relative to yunkong2.js-controller
|
||||
config.dataDir = tools.getDefaultDataDir();
|
||||
if (fs.existsSync(__dirname + '/../../../node_modules/' + tools.appName + '.js-controller')) {
|
||||
mkpathSync(__dirname + '/../', config.dataDir);
|
||||
} else {
|
||||
mkpathSync(__dirname + '/../', '../' + config.dataDir);
|
||||
}
|
||||
|
||||
// Create default data dir
|
||||
fs.writeFileSync(tools.getConfigFileName(), JSON.stringify(config, null, 2));
|
||||
|
||||
try {
|
||||
// Create
|
||||
if (__dirname.toLowerCase().replace(/\\/g, '/').indexOf('node_modules/' + tools.appName + '.js-controller') !== -1) {
|
||||
var parts = config.dataDir.split('/');
|
||||
// Remove appName-data/
|
||||
parts.pop();
|
||||
parts.pop();
|
||||
var path = parts.join('/');
|
||||
|
||||
if (!fs.existsSync(__dirname + '/../../' + path + '/log')) fs.mkdirSync(__dirname + '/../../' + path + '/log');
|
||||
} else {
|
||||
if (!fs.existsSync(__dirname + '/../../log')) fs.mkdirSync(__dirname + '/../../log');
|
||||
}
|
||||
} catch (e) {
|
||||
console.log('Non-critical error: ' + e.message);
|
||||
}
|
||||
} else if (ignoreIfExist) {
|
||||
if (callback) callback();
|
||||
return;
|
||||
}
|
||||
setupObjects(function () {
|
||||
if (callback) callback(isCreated);
|
||||
});
|
||||
}
|
||||
|
||||
|
||||
}
|
||||
|
||||
module.exports = Setup;
|
||||
360
lib/setup/setupUpgrade.js
Normal file
360
lib/setup/setupUpgrade.js
Normal file
@@ -0,0 +1,360 @@
|
||||
'use strict';
|
||||
|
||||
function Upgrade(options) {
|
||||
var fs = require('fs');
|
||||
var tools = require(__dirname + '/../tools.js');
|
||||
|
||||
var that = this;
|
||||
|
||||
options = options || {};
|
||||
|
||||
if (!options.objects) throw 'Invalid arguments: objects is missing';
|
||||
if (!options.processExit) throw 'Invalid arguments: processExit is missing';
|
||||
if (!options.installNpm) throw 'Invalid arguments: installNpm is missing';
|
||||
if (!options.restartController) throw 'Invalid arguments: restartController is missing';
|
||||
if (!options.getRepository) throw 'Invalid arguments: getRepository is missing';
|
||||
|
||||
var objects = options.objects;
|
||||
var processExit = options.processExit;
|
||||
var installNpm = options.installNpm;
|
||||
var restartController = options.restartController;
|
||||
var getRepository = options.getRepository;
|
||||
var params = options.params;
|
||||
var semver;
|
||||
|
||||
var Upload = require(__dirname + '/setupUpload.js');
|
||||
var upload = new Upload(options);
|
||||
|
||||
var Install = require(__dirname + '/setupInstall.js');
|
||||
var install = new Install(options);
|
||||
|
||||
this.upgradeAdapterHelper = function (repoUrl, list, i, forceDowngrade, callback) {
|
||||
that.upgradeAdapter(repoUrl, list[i], forceDowngrade, function () {
|
||||
i++;
|
||||
while (repoUrl[list[i]] && repoUrl[list[i]].controller) {
|
||||
i++;
|
||||
}
|
||||
|
||||
if (list[i]) {
|
||||
setImmediate(function () {
|
||||
that.upgradeAdapterHelper(repoUrl, list, i, forceDowngrade, callback);
|
||||
});
|
||||
} else if (callback) {
|
||||
callback();
|
||||
}
|
||||
});
|
||||
};
|
||||
|
||||
function checkDependencies(dependencies) {
|
||||
if (!dependencies) return '';
|
||||
// like [{"js-controller": ">=0.10.1"}]
|
||||
var adapters;
|
||||
if (dependencies instanceof Array) {
|
||||
adapters = {};
|
||||
for (var a = 0; a < dependencies.length; a++) {
|
||||
if (typeof dependencies[a] === 'string') continue;
|
||||
for (var b in dependencies[a]) adapters[b] = dependencies[a][b];
|
||||
}
|
||||
} else {
|
||||
adapters = dependencies;
|
||||
}
|
||||
|
||||
for (var adapter in adapters) {
|
||||
var adapterDir = tools.getAdapterDir(adapter);
|
||||
var iopack;
|
||||
if (!semver) semver = require('semver');
|
||||
if (adapter === 'js-controller') {
|
||||
try {
|
||||
iopack = JSON.parse(fs.readFileSync(__dirname + '/../../io-package.json', 'utf8'));
|
||||
} catch (e) {
|
||||
return 'Cannot find io-package.json in "' + __dirname + '/../../": ' + e;
|
||||
}
|
||||
if (!iopack || !iopack.common || !iopack.common.version) return 'No version of "js-controller"';
|
||||
if (!semver.satisfies(iopack.common.version, adapters[adapter])) return 'Invalid version of js-controler. Required ' + adapters[adapter];
|
||||
} else {
|
||||
try {
|
||||
iopack = JSON.parse(fs.readFileSync(adapterDir + '/io-package.json', 'utf8'));
|
||||
} catch (e) {
|
||||
return 'Cannot find io-package.json in "' + adapterDir + '": ' + e;
|
||||
}
|
||||
|
||||
if (!iopack || !iopack.common || !iopack.common.version) return 'No version of "' + adapter + '"';
|
||||
if (!semver.satisfies(iopack.common.version, adapters[adapter])) return 'Invalid version of "' + adapter + '"';
|
||||
}
|
||||
}
|
||||
return '';
|
||||
}
|
||||
|
||||
this.upgradeAdapter = function (repoUrl, adapter, forceDowngrade, callback) {
|
||||
if (!repoUrl || typeof repoUrl !== 'object') {
|
||||
getRepository(repoUrl, params, function (err, sources) {
|
||||
if (err) {
|
||||
processExit(err);
|
||||
} else {
|
||||
that.upgradeAdapter(sources, adapter, forceDowngrade, callback);
|
||||
}
|
||||
});
|
||||
return;
|
||||
}
|
||||
|
||||
function finishUpgrade(name, iopack, callback) {
|
||||
if (!iopack) {
|
||||
var adapterDir = tools.getAdapterDir(name);
|
||||
try {
|
||||
iopack = JSON.parse(fs.readFileSync(adapterDir + '/io-package.json', 'utf8'));
|
||||
} catch (e) {
|
||||
console.error('Cannot find io-package.json in ' + adapterDir);
|
||||
processExit(10);
|
||||
}
|
||||
}
|
||||
|
||||
var count = 0;
|
||||
installNpm(name, function (err, _name) {
|
||||
if (err) {
|
||||
processExit(err);
|
||||
} else {
|
||||
// Upload www and admin files of adapter into CouchDB
|
||||
count++;
|
||||
upload.uploadAdapter(name, false, true, function () {
|
||||
// extend all adapter instance default configs with current config
|
||||
// (introduce potentially new attributes while keeping current settings)
|
||||
upload.upgradeAdapterObjects(name, iopack, function () {
|
||||
count--;
|
||||
if (!count) {
|
||||
console.log('Adapter "' + name + '" updated');
|
||||
if (callback) callback(name);
|
||||
}
|
||||
});
|
||||
});
|
||||
count++;
|
||||
upload.uploadAdapter(name, true, true, function () {
|
||||
count--;
|
||||
if (!count) {
|
||||
console.log('Adapter "' + name + '" updated');
|
||||
if (callback) callback(name);
|
||||
}
|
||||
});
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
var sources = repoUrl;
|
||||
var version;
|
||||
if (adapter.indexOf('@') !== -1) {
|
||||
var parts = adapter.split('@');
|
||||
adapter = parts[0];
|
||||
version = parts[1];
|
||||
} else {
|
||||
version = '';
|
||||
}
|
||||
if (version) forceDowngrade = true;
|
||||
|
||||
var adapterDir = tools.getAdapterDir(adapter);
|
||||
|
||||
// Read actual description of installed adapter with version
|
||||
if (!version && !fs.existsSync(adapterDir + '/io-package.json')) {
|
||||
console.log('Adapter "' + adapter + '"' + ((adapter.length < 15) ? new Array(15 - adapter.length).join(' '): '') + ' is not installed.');
|
||||
if (callback) callback();
|
||||
return;
|
||||
}
|
||||
// Get the url of io-package.json or direct the version
|
||||
if (!repoUrl[adapter]) {
|
||||
console.log('Adapter "' + adapter + '" is not in the repository and cannot be updated.');
|
||||
if (callback) callback();
|
||||
return;
|
||||
}
|
||||
|
||||
var ioInstalled;
|
||||
if (fs.existsSync(adapterDir + '/io-package.json')) {
|
||||
ioInstalled = require(adapterDir + '/io-package.json');
|
||||
}
|
||||
if (!ioInstalled) {
|
||||
ioInstalled = {common: {version: '0.0.0'}};
|
||||
}
|
||||
|
||||
// If version is included in repository
|
||||
if (repoUrl[adapter].version) {
|
||||
if (!forceDowngrade) {
|
||||
var error = checkDependencies(repoUrl[adapter].dependencies);
|
||||
if (error) {
|
||||
console.error(error);
|
||||
if (callback) callback();
|
||||
return;
|
||||
}
|
||||
}
|
||||
|
||||
if (!forceDowngrade && (repoUrl[adapter].version === ioInstalled.common.version ||
|
||||
tools.upToDate(repoUrl[adapter].version, ioInstalled.common.version))) {
|
||||
console.log('Adapter "' + adapter + '"' + ((adapter.length < 15) ? new Array(15 - adapter.length).join(' '): '') + ' is up to date.');
|
||||
if (callback) callback();
|
||||
} else {
|
||||
console.log('Update ' + adapter + ' from @' + ioInstalled.common.version + ' to @' + (version || repoUrl[adapter].version));
|
||||
// Get the adapter from web site
|
||||
install.downloadPacket(sources, adapter + '@' + (version || repoUrl[adapter].version), null, function (name, ioPack) {
|
||||
finishUpgrade(name, ioPack, callback);
|
||||
});
|
||||
}
|
||||
} else if (repoUrl[adapter].meta) {
|
||||
// Read repository from url or file
|
||||
tools.getJson(repoUrl[adapter].meta, function (iopack) {
|
||||
if (!iopack) {
|
||||
console.error('Cannot parse file' + repoUrl[adapter].meta);
|
||||
if (callback) callback();
|
||||
return;
|
||||
}
|
||||
|
||||
if (!forceDowngrade) {
|
||||
var error = checkDependencies(iopack.common ? iopack.common.dependencies : null);
|
||||
if (error) {
|
||||
console.error(error);
|
||||
if (callback) callback();
|
||||
return;
|
||||
}
|
||||
}
|
||||
|
||||
if (!version && (iopack.common.version === ioInstalled.common.version ||
|
||||
(!forceDowngrade && tools.upToDate(iopack.common.version, ioInstalled.common.version)))) {
|
||||
console.log('Adapter "' + adapter + '"' + ((adapter.length < 15) ? new Array(15 - adapter.length).join(' '): '') + ' is up to date.');
|
||||
if (callback) callback();
|
||||
} else {
|
||||
// Get the adapter from web site
|
||||
console.log('Update ' + adapter + ' from @' + ioInstalled.common.version + ' to @' + (version || iopack.common.version));
|
||||
install.downloadPacket(sources, adapter + '@' + (version || iopack.common.version), null, function (name, ioPack) {
|
||||
finishUpgrade(name, ioPack, callback);
|
||||
});
|
||||
}
|
||||
});
|
||||
} else {
|
||||
if (forceDowngrade) {
|
||||
console.warn('Unable to get version for "' + adapter + '". Update anyway.');
|
||||
console.log('Update ' + adapter + ' from @' + ioInstalled.common.version + ' to @' + version);
|
||||
// Get the adapter from web site
|
||||
install.downloadPacket(sources, adapter + '@' + version, null, function (name, ioPack) {
|
||||
finishUpgrade(name, ioPack, callback);
|
||||
});
|
||||
} else {
|
||||
console.error('Unable to get version for "' + adapter + '".');
|
||||
if (callback) callback();
|
||||
}
|
||||
}
|
||||
};
|
||||
|
||||
this.upgradeController = function (repoUrl, forceDowngrade, callback) {
|
||||
if (!repoUrl || typeof repoUrl !== 'object') {
|
||||
getRepository(repoUrl, params, function (err, sources) {
|
||||
if (!sources) {
|
||||
console.warn('Cannot get repository under "' + repoUrl + '"');
|
||||
if (callback) callback(err);
|
||||
} else {
|
||||
that.upgradeController(sources, forceDowngrade, callback);
|
||||
}
|
||||
});
|
||||
return;
|
||||
}
|
||||
|
||||
var hostname = tools.getHostName();
|
||||
var installed = JSON.parse(fs.readFileSync(__dirname + '/../../io-package.json', 'utf8'));
|
||||
if (!installed || !installed.common || !installed.common.version) {
|
||||
console.error('Host "' + hostname + '"' + ((hostname.length < 15) ? new Array(15 - hostname.length).join(' '): '') + ' is not installed.');
|
||||
if (callback) callback();
|
||||
return;
|
||||
}
|
||||
if (!repoUrl[installed.common.name]) {
|
||||
// no info for controller
|
||||
console.error('Cannot find this controller "' + installed.common.name + '" in repository.');
|
||||
|
||||
if (callback) callback();
|
||||
return;
|
||||
}
|
||||
|
||||
if (repoUrl[installed.common.name].version) {
|
||||
if (!forceDowngrade && (repoUrl[installed.common.name].version === installed.common.version ||
|
||||
tools.upToDate(repoUrl[installed.common.name].version, installed.common.version))) {
|
||||
console.log('Host "' + hostname + '"' + ((hostname.length < 15) ? new Array(15 - hostname.length).join(' '): '') + ' is up to date.');
|
||||
if (callback) {
|
||||
callback();
|
||||
}
|
||||
} else {
|
||||
console.log('Update ' + installed.common.name + ' from @' + installed.common.version + ' to @' + repoUrl[installed.common.name].version);
|
||||
// Get the controller from web site
|
||||
install.downloadPacket(repoUrl, installed.common.name + '@' + repoUrl[installed.common.name].version, null, function (name) {
|
||||
installNpm(function (err, _name) {
|
||||
if (err) {
|
||||
processExit(err);
|
||||
} else {
|
||||
setChmod(function () {
|
||||
restartController(callback);
|
||||
});
|
||||
}
|
||||
});
|
||||
});
|
||||
}
|
||||
} else {
|
||||
tools.getJson(repoUrl[installed.common.name].meta, function (ioPack) {
|
||||
if ((!ioPack || !ioPack.common) && !forceDowngrade) {
|
||||
console.warn('Cannot read version. Write "' + tools.appName + ' upgrade self --force" to upgrade controller anyway.');
|
||||
if (callback) {
|
||||
callback();
|
||||
}
|
||||
return;
|
||||
}
|
||||
var version = ioPack && ioPack.common ? ioPack && ioPack.common.version : '';
|
||||
if (version) {
|
||||
version = '@' + version;
|
||||
}
|
||||
|
||||
if ((ioPack && ioPack.common && ioPack.common.version === installed.common.version) ||
|
||||
(!forceDowngrade && ioPack && ioPack.common && tools.upToDate(ioPack.common.version, installed.common.version))) {
|
||||
console.log('Host "' + hostname + '"' + ((hostname.length < 15) ? new Array(15 - hostname.length).join(' '): '') + ' is up to date.');
|
||||
if (callback) callback();
|
||||
} else {
|
||||
var name = (ioPack && ioPack.common && ioPack.common.name) ? ioPack.common.name : installed.common.name;
|
||||
console.log('Update ' + name + ' from @' + installed.common.version + ' to ' + version);
|
||||
// Get the controller from web site
|
||||
install.downloadPacket(repoUrl, name + version, null, function (name) {
|
||||
installNpm(function (err, _name) {
|
||||
if (err) {
|
||||
processExit(err);
|
||||
} else {
|
||||
setChmod(function () {
|
||||
restartController(callback);
|
||||
});
|
||||
}
|
||||
});
|
||||
});
|
||||
}
|
||||
|
||||
});
|
||||
}
|
||||
};
|
||||
|
||||
function setChmod(callback) {
|
||||
var platform = require('os').platform();
|
||||
console.log('Host "' + tools.getHostName() + '" (' + platform + ') updated');
|
||||
// Call command chmod +x __dirname if under linux or darwin
|
||||
if (platform === 'linux' || platform === 'darwin') {
|
||||
var exec = require('child_process').exec;
|
||||
var dir;
|
||||
if (__dirname.toLowerCase().replace(/\\/g, '/').indexOf('node_modules/' + tools.appName + '.js-controller') !== -1) {
|
||||
dir = require('path').normalize(__dirname + '/../../../..').replace(/\\/g, '/');
|
||||
} else {
|
||||
dir = require('path').normalize(__dirname + '/../..').replace(/\\/g, '/');
|
||||
}
|
||||
|
||||
var cmd = 'chmod -R 777 ' + dir;
|
||||
console.log('Execute: ' + cmd);
|
||||
var child = exec(cmd);
|
||||
child.stderr.pipe(process.stdout);
|
||||
child.on('exit', function () {
|
||||
console.log('Chmod finished. Restart controller');
|
||||
if (callback) callback();
|
||||
});
|
||||
} else {
|
||||
if (callback) callback();
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
}
|
||||
|
||||
module.exports = Upgrade;
|
||||
543
lib/setup/setupUpload.js
Normal file
543
lib/setup/setupUpload.js
Normal file
@@ -0,0 +1,543 @@
|
||||
'use strict';
|
||||
|
||||
function Upload(options) {
|
||||
const fs = require('fs');
|
||||
const tools = require(__dirname + '/../tools.js');
|
||||
|
||||
options = options || {};
|
||||
|
||||
if (!options.states) throw 'Invalid arguments: states is missing';
|
||||
if (!options.objects) throw 'Invalid arguments: objects is missing';
|
||||
|
||||
let states = options.states;
|
||||
let objects = options.objects;
|
||||
let mime;
|
||||
|
||||
// get all instances of one adapter
|
||||
function getInstances(adapter, callback) {
|
||||
objects.getObjectList({startkey: 'system.adapter.' + adapter + '.', endkey: 'system.adapter.' + adapter + '.\u9999'}, (err, arr) => {
|
||||
let instances = [];
|
||||
if (!err && arr && arr.rows) {
|
||||
for (let i = 0; i < arr.rows.length; i++) {
|
||||
if (arr.rows[i].value.type !== 'instance') continue;
|
||||
instances.push(arr.rows[i].value._id);
|
||||
}
|
||||
}
|
||||
callback(instances);
|
||||
});
|
||||
}
|
||||
|
||||
// get all instances of all adapters in the list
|
||||
function getAllInstances(adapters, callback) {
|
||||
let instances = [];
|
||||
let count = 0;
|
||||
for (let k = 0; k < adapters.length; k++) {
|
||||
if (!adapters[k]) continue;
|
||||
if (adapters[k].indexOf('.') === -1) count++;
|
||||
}
|
||||
for (let i = 0; i < adapters.length; i++) {
|
||||
if (!adapters[i]) continue;
|
||||
if (adapters[i].indexOf('.') === -1) {
|
||||
getInstances(adapters[i], inst => {
|
||||
for (let j = 0; j < inst.length; j++) {
|
||||
if (instances.indexOf(inst[j]) === -1) {
|
||||
instances.push(inst[j]);
|
||||
}
|
||||
}
|
||||
if (!--count && callback) {
|
||||
callback(instances);
|
||||
callback = null;
|
||||
}
|
||||
});
|
||||
} else {
|
||||
if (instances.indexOf(adapters[i]) === -1) {
|
||||
instances.push(adapters[i]);
|
||||
}
|
||||
}
|
||||
}
|
||||
if (!count && callback) {
|
||||
callback(instances);
|
||||
callback = null;
|
||||
}
|
||||
}
|
||||
|
||||
// Check if some adapters must be restarted and restart them
|
||||
function checkRestartOther(adapter, callback) {
|
||||
let adapterDir = tools.getAdapterDir(adapter);
|
||||
try {
|
||||
let adapterConf = JSON.parse(fs.readFileSync(adapterDir + '/io-package.json').toString());
|
||||
if (adapterConf.common.restartAdapters) {
|
||||
if (typeof adapterConf.common.restartAdapters !== 'object') adapterConf.common.restartAdapters = [adapterConf.common.restartAdapters];
|
||||
|
||||
if (adapterConf.common.restartAdapters.length && adapterConf.common.restartAdapters[0]) {
|
||||
getAllInstances(adapterConf.common.restartAdapters, instances => {
|
||||
if (!instances || !instances.length) {
|
||||
if (callback) {
|
||||
callback();
|
||||
callback = null;
|
||||
}
|
||||
} else {
|
||||
let instancesCount = instances.length;
|
||||
for (let r = 0; r < instances.length; r++) {
|
||||
objects.getObject(instances[r], (err, obj) => {
|
||||
// if instance is enabled
|
||||
if (!err && obj && obj.common.enabled) {
|
||||
|
||||
obj.common.enabled = false; // disable instance
|
||||
|
||||
obj.from = 'system.host.' + tools.getHostName() + '.cli';
|
||||
obj.ts = Date.now();
|
||||
|
||||
objects.setObject(obj._id, obj, err => {
|
||||
if (!err) {
|
||||
|
||||
obj.common.enabled = true; // enable instance
|
||||
|
||||
obj.from = 'system.host.' + tools.getHostName() + '.cli';
|
||||
obj.ts = Date.now();
|
||||
|
||||
objects.setObject(obj._id, obj, err => {
|
||||
console.log('Adapter "' + obj._id + '" restarted.');
|
||||
if (!--instancesCount && callback) {
|
||||
callback();
|
||||
callback = null;
|
||||
}
|
||||
});
|
||||
} else {
|
||||
if (err) {
|
||||
console.error('Cannot restart adapter "' + obj._id + '": ' + err);
|
||||
} else {
|
||||
console.warn('Adapter "' + obj._id + '" is disabled and cannot be restarted.');
|
||||
}
|
||||
if (!--instancesCount && callback) {
|
||||
callback();
|
||||
callback = null;
|
||||
}
|
||||
}
|
||||
});
|
||||
} else if (!--instancesCount && callback) {
|
||||
callback();
|
||||
callback = null;
|
||||
}
|
||||
});
|
||||
}
|
||||
}
|
||||
});
|
||||
} else if (callback) {
|
||||
callback();
|
||||
callback = null;
|
||||
}
|
||||
} else if (callback) {
|
||||
callback();
|
||||
callback = null;
|
||||
}
|
||||
} catch (e) {
|
||||
console.error('Cannot parse ' + adapterDir + '/io-package.json:' + e);
|
||||
if (callback) {
|
||||
callback();
|
||||
callback = null;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
this.uploadAdapterFull = (adapters, callback) => {
|
||||
if (!adapters || !adapters.length) {
|
||||
if (callback) callback();
|
||||
return;
|
||||
}
|
||||
const adapter = adapters.pop();
|
||||
this.uploadAdapter(adapter, true, true, () => {
|
||||
this.upgradeAdapterObjects(adapter, () => {
|
||||
this.uploadAdapter(adapter, false, true, () => {
|
||||
setImmediate(() => this.uploadAdapterFull(adapters, callback));
|
||||
});
|
||||
});
|
||||
});
|
||||
};
|
||||
|
||||
this.uploadFile = (source, target, callback) => {
|
||||
const request = require('request');
|
||||
target = target.replace(/\\/g, '/');
|
||||
source = source.replace(/\\/g, '/');
|
||||
if (target[0] === '/') target = target.substring(1);
|
||||
if (target[target.length - 1] === '/') {
|
||||
let name = source.split('/').pop();
|
||||
name = name.split('?')[0];
|
||||
if (name.indexOf('.') === -1) name = 'index.html';
|
||||
target += name;
|
||||
}
|
||||
let parts = target.split('/');
|
||||
const adapter = parts[0];
|
||||
parts.splice(0, 1);
|
||||
target = parts.join('/');
|
||||
|
||||
if (source.match(/^http:\/\/|^https:\/\//)) {
|
||||
request(source, (error, response, body) => {
|
||||
if (!error && response.statusCode === 200) {
|
||||
objects.writeFile(adapter, target, body, err => {
|
||||
if (err) console.error(err);
|
||||
if (typeof callback === 'function') callback(err, adapter + '/' + target);
|
||||
});
|
||||
} else {
|
||||
console.error('Cannot get URL: ' + error || response.statusCode);
|
||||
if (typeof callback === 'function') callback(error || response.statusCode, adapter + '/' + target);
|
||||
}
|
||||
});
|
||||
} else {
|
||||
try {
|
||||
objects.writeFile(adapter, target, fs.readFileSync(source), err => {
|
||||
if (err) console.error(err);
|
||||
if (typeof callback === 'function') callback(err, adapter + '/' + target);
|
||||
});
|
||||
} catch (err) {
|
||||
console.error('Cannot read file "' + source + '": ' + err);
|
||||
if (typeof callback === 'function') callback(err, adapter + '/' + target);
|
||||
}
|
||||
}
|
||||
};
|
||||
|
||||
// Upload www folder of adapter into ObjectsDB
|
||||
this.uploadAdapter = (adapter, isAdmin, forceUpload, subTree, callback) => {
|
||||
const id = adapter + (isAdmin ? '.admin' : '');
|
||||
const adapterDir = tools.getAdapterDir(adapter);
|
||||
let dir = adapterDir + (isAdmin ? '/admin' : '/www');
|
||||
let files = [];
|
||||
let rev;
|
||||
|
||||
if (typeof subTree === 'function') {
|
||||
callback = subTree;
|
||||
subTree = null;
|
||||
}
|
||||
if (subTree) {
|
||||
dir += '/' + subTree;
|
||||
}
|
||||
|
||||
if (!isAdmin) {
|
||||
let cfg;
|
||||
// check for common.wwwDontUpload (required for legacy adapter)
|
||||
if (fs.existsSync(adapterDir + '/io-package.json')) {
|
||||
cfg = require(adapterDir + '/io-package.json');
|
||||
}
|
||||
if (cfg && cfg.common && cfg.common.wwwDontUpload) {
|
||||
if (typeof callback === 'function') callback(adapter);
|
||||
return;
|
||||
}
|
||||
}
|
||||
// do not upload www dir of admin adapter
|
||||
if (adapter === 'admin' && !isAdmin) {
|
||||
// To DO remove after a while
|
||||
console.log('This should never happens!');
|
||||
if (typeof callback === 'function') callback(adapter);
|
||||
return;
|
||||
}
|
||||
|
||||
// Create "upload progress" object if not exists
|
||||
if (!isAdmin) {
|
||||
objects.getObject('system.adapter.' + adapter + '.upload', (err, obj) => {
|
||||
if (err || !obj) {
|
||||
objects.setObject('system.adapter.' + adapter + '.upload',
|
||||
{
|
||||
_id: 'system.adapter.' + adapter + '.upload',
|
||||
type: 'state',
|
||||
common: {
|
||||
name: adapter + '.upload',
|
||||
type: 'number',
|
||||
role: 'indicator.state',
|
||||
unit: '%',
|
||||
def: 0,
|
||||
desc: 'Upload process indicator'
|
||||
},
|
||||
from: 'system.host.' + tools.getHostName() + '.cli',
|
||||
ts: Date.now(),
|
||||
native: {}
|
||||
}
|
||||
);
|
||||
}
|
||||
});
|
||||
// Set indicator to 0
|
||||
states.setState('system.adapter.' + adapter + '.upload', 0, true);
|
||||
}
|
||||
|
||||
if (!mime) mime = require('mime');
|
||||
|
||||
function done(err, res) {
|
||||
if (err) {
|
||||
callback();
|
||||
} else {
|
||||
console.log('got ' + dir);
|
||||
files = res;
|
||||
setTimeout(_adapter => {
|
||||
maxFiles = files.length || 1;
|
||||
upload(_adapter);
|
||||
}, 25, adapter);
|
||||
}
|
||||
}
|
||||
|
||||
let maxFiles = 0;
|
||||
let lastProgressUpdate = (new Date()).getTime();
|
||||
|
||||
function upload(adapter) {
|
||||
let file;
|
||||
if (!files.length) {
|
||||
if (!isAdmin) {
|
||||
states.setState('system.adapter.' + adapter + '.upload', {val: 0, ack: true}, () => {
|
||||
if (typeof callback === 'function') callback(adapter);
|
||||
});
|
||||
} else {
|
||||
if (typeof callback === 'function') callback(adapter);
|
||||
}
|
||||
} else {
|
||||
file = files.pop();
|
||||
if (file === '.gitignore') {
|
||||
upload(adapter);
|
||||
return;
|
||||
}
|
||||
|
||||
const mimeType = mime.lookup(file);
|
||||
let attName;
|
||||
attName = file.split('/' + tools.appName + '.');
|
||||
if (attName.length === 1) {
|
||||
// try to find anyway if adapter is not lower case
|
||||
const pos = file.toLowerCase().indexOf(tools.appName.toLowerCase());
|
||||
if (pos !== -1) {
|
||||
attName = ['', file.substring(tools.appName.length + 2)];
|
||||
}
|
||||
}
|
||||
|
||||
attName = attName.pop();
|
||||
attName = attName.split('/').slice(2).join('/');
|
||||
if (files.length > 100) {
|
||||
if (!(files.length % 50)) {
|
||||
console.log('upload [' + files.length + ']', id, file, attName, mimeType);
|
||||
}
|
||||
|
||||
} else if (files.length > 20) {
|
||||
if (!(files.length % 10)) {
|
||||
console.log('upload [' + files.length + ']', id, file, attName, mimeType);
|
||||
}
|
||||
} else {
|
||||
console.log('upload [' + files.length + ']', id, file, attName, mimeType);
|
||||
}
|
||||
|
||||
// Update upload indicator
|
||||
if (!isAdmin) {
|
||||
const now = (new Date()).getTime();
|
||||
if (now - lastProgressUpdate > 1000) {
|
||||
lastProgressUpdate = now;
|
||||
states.setState('system.adapter.' + adapter + '.upload', {val: Math.round(1000 * (maxFiles - files.length) / maxFiles) / 10, ack: true});
|
||||
}
|
||||
}
|
||||
|
||||
fs.createReadStream(file).pipe(
|
||||
objects.insert(id, attName, null, mimeType, {
|
||||
rev: rev
|
||||
}, (err, res) => {
|
||||
if (err) {
|
||||
console.log(err);
|
||||
if (typeof callback === 'function') callback(adapter);
|
||||
}
|
||||
if (res) rev = res.rev;
|
||||
setTimeout(upload, 50, adapter);
|
||||
})
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
function walk(dir, done) {
|
||||
let results = [];
|
||||
fs.readdir(dir, (err, list) => {
|
||||
if (err) return done(err);
|
||||
let i = 0;
|
||||
(function next() {
|
||||
let file = list[i++];
|
||||
if (!file) return done(null, results);
|
||||
file = dir + '/' + file;
|
||||
fs.stat(file, (err, stat) => {
|
||||
if (stat && stat.isDirectory()) {
|
||||
walk(file, (err, res) => {
|
||||
results = results.concat(res);
|
||||
next();
|
||||
});
|
||||
} else {
|
||||
if (!file.match(/\.npmignore$/) && !file.match(/\.gitignore$/)) results.push(file);
|
||||
next();
|
||||
}
|
||||
});
|
||||
})();
|
||||
});
|
||||
}
|
||||
|
||||
objects.getObject(id, (err, res) => {
|
||||
if (err || !res) {
|
||||
objects.setObject(id, {
|
||||
type: 'meta',
|
||||
common: {
|
||||
name: id.split('.').pop(),
|
||||
type: isAdmin ? 'admin' : 'www'
|
||||
},
|
||||
from: 'system.host.' + tools.getHostName() + '.cli',
|
||||
ts: Date.now(),
|
||||
native: {}
|
||||
}, (err, res) => {
|
||||
if (res) rev = res.rev;
|
||||
if (!isAdmin) {
|
||||
checkRestartOther(adapter, () => walk(dir, done));
|
||||
} else {
|
||||
walk(dir, done);
|
||||
}
|
||||
});
|
||||
} else {
|
||||
if (!forceUpload) {
|
||||
if (typeof callback === 'function') callback(adapter);
|
||||
} else {
|
||||
rev = res.rev;
|
||||
if (!isAdmin) {
|
||||
checkRestartOther(adapter, () => walk(dir, done));
|
||||
} else {
|
||||
walk(dir, done);
|
||||
}
|
||||
}
|
||||
}
|
||||
});
|
||||
};
|
||||
|
||||
function extendNative(target, additional) {
|
||||
for (let attr in additional) {
|
||||
if (additional.hasOwnProperty(attr)) {
|
||||
if (target[attr] === undefined) {
|
||||
target[attr] = additional[attr];
|
||||
} else if (typeof additional[attr] === 'object' && !(additional[attr] instanceof Array)) {
|
||||
try {
|
||||
target[attr] = target[attr] || {};
|
||||
} catch (e) {
|
||||
console.warn(`Cannot update attribute ${attr} of native`);
|
||||
}
|
||||
extendNative(target[attr], additional[attr]);
|
||||
}
|
||||
}
|
||||
}
|
||||
return target;
|
||||
}
|
||||
|
||||
function extendCommon(target, additional) {
|
||||
for (let attr in additional) {
|
||||
if (additional.hasOwnProperty(attr)) {
|
||||
if (attr === 'title' || attr === 'schedule' || attr === 'mode' || attr === 'loglevel' || attr === 'enabled') {
|
||||
if (target[attr] === undefined) {
|
||||
target[attr] = additional[attr];
|
||||
}
|
||||
} else if (typeof additional[attr] !== 'object' || (additional[attr] instanceof Array)) {
|
||||
try {
|
||||
target[attr] = additional[attr];
|
||||
} catch (e) {
|
||||
console.warn(`Cannot update attribute ${attr} of common`);
|
||||
}
|
||||
} else {
|
||||
target[attr] = target[attr] || {};
|
||||
if (typeof target[attr] !== 'object') {
|
||||
target[attr] = {}; // here we clean the simple value with object
|
||||
}
|
||||
|
||||
extendCommon(target[attr], additional[attr]);
|
||||
}
|
||||
}
|
||||
}
|
||||
return target;
|
||||
}
|
||||
|
||||
this.upgradeAdapterObjects = (name, iopack, callback) => {
|
||||
if (typeof iopack === 'function') {
|
||||
callback = iopack;
|
||||
iopack = null;
|
||||
}
|
||||
if (!iopack) {
|
||||
const adapterDir = tools.getAdapterDir(name);
|
||||
try {
|
||||
iopack = JSON.parse(fs.readFileSync(adapterDir + '/io-package.json', 'utf8'));
|
||||
} catch (e) {
|
||||
console.error('Cannot find io-package.json in ' + adapterDir);
|
||||
iopack = null;
|
||||
}
|
||||
}
|
||||
|
||||
if (!iopack) {
|
||||
callback(name);
|
||||
} else {
|
||||
objects.getObject('system.adapter.' + name, (err, obj) => {
|
||||
if (err || !obj) {
|
||||
console.error('system.adapter.' + name + ' does not exist');
|
||||
callback(name);
|
||||
} else {
|
||||
obj.common = iopack.common || {};
|
||||
obj.native = iopack.native || {};
|
||||
|
||||
obj.common.installedVersion = iopack.common.version;
|
||||
|
||||
const hostname = tools.getHostName();
|
||||
|
||||
obj.from = 'system.host.' + tools.getHostName() + '.cli';
|
||||
obj.ts = Date.now();
|
||||
|
||||
objects.setObject('system.adapter.' + name, obj, () => {
|
||||
// Update all instances of this host
|
||||
objects.getObjectView('system', 'instance', {startkey: 'system.adapter.' + name + '.', endkey: 'system.adapter.' + name + '.\u9999'}, null, function (err, res) {
|
||||
let cntr = 0;
|
||||
|
||||
if (res) {
|
||||
for (let i = 0; i < res.rows.length; i++) {
|
||||
if (res.rows[i].value.common.host === hostname) {
|
||||
cntr++;
|
||||
objects.getObject(res.rows[i].id, (err, _obj) => {
|
||||
let newObject = JSON.parse(JSON.stringify(_obj));
|
||||
|
||||
// all common settings should be taken from new one
|
||||
newObject.common = extendCommon(newObject.common, iopack.common);
|
||||
newObject.native = extendNative(newObject.native, iopack.native);
|
||||
|
||||
newObject.common.installedVersion = iopack.common.version;
|
||||
newObject.common.version = iopack.common.version;
|
||||
|
||||
// Compare objects to reduce restarts of instances
|
||||
if (JSON.stringify(newObject) !== JSON.stringify(_obj)) {
|
||||
console.log('Update "' + newObject._id + '"');
|
||||
|
||||
newObject.from = 'system.host.' + tools.getHostName() + '.cli';
|
||||
newObject.ts = Date.now();
|
||||
|
||||
objects.setObject(newObject._id, newObject, () => {
|
||||
if (!--cntr && callback) callback(name);
|
||||
});
|
||||
} else {
|
||||
if (!--cntr && callback) callback(name);
|
||||
}
|
||||
});
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// updates "_design/system" and co
|
||||
if (iopack.objects && typeof iopack.objects === 'object') {
|
||||
for (let _id in iopack.objects) {
|
||||
if (!iopack.objects.hasOwnProperty(_id)) continue;
|
||||
cntr++;
|
||||
|
||||
iopack.objects[_id].from = 'system.host.' + tools.getHostName() + '.cli';
|
||||
iopack.objects[_id].ts = Date.now();
|
||||
|
||||
objects.setObject(iopack.objects[_id]._id, iopack.objects[_id], err => {
|
||||
if (err) console.error('Cannot update object: ' + err);
|
||||
if (!--cntr && callback) callback(name);
|
||||
});
|
||||
}
|
||||
}
|
||||
|
||||
if (!cntr && callback) callback(name);
|
||||
});
|
||||
});
|
||||
}
|
||||
});
|
||||
}
|
||||
};
|
||||
}
|
||||
|
||||
module.exports = Upload;
|
||||
522
lib/setup/setupUsers.js
Normal file
522
lib/setup/setupUsers.js
Normal file
@@ -0,0 +1,522 @@
|
||||
'use strict';
|
||||
|
||||
function Users(options) {
|
||||
var fs = require('fs');
|
||||
var tools = require(__dirname + '/../tools.js');
|
||||
|
||||
var that = this;
|
||||
|
||||
options = options || {};
|
||||
|
||||
if (!options.objects) throw 'Invalid arguments: objects is missing';
|
||||
if (!options.processExit) throw 'Invalid arguments: processExit is missing';
|
||||
|
||||
var objects = options.objects;
|
||||
var processExit = options.processExit;
|
||||
|
||||
this.addUser = function (user, pw, callback) {
|
||||
var _user = user.replace(/\s/g, '_');
|
||||
objects.getObject('system.user.' + _user, function (err, obj) {
|
||||
if (obj) {
|
||||
if (callback) callback('User yet exists');
|
||||
} else {
|
||||
objects.setObject('system.user.' + _user, {
|
||||
type: 'user',
|
||||
common: {
|
||||
name: user,
|
||||
enabled: true
|
||||
},
|
||||
from: 'system.host.' + tools.getHostName() + '.cli',
|
||||
ts: new Date().getTime(),
|
||||
native: {}
|
||||
}, function (err) {
|
||||
if (!err) {
|
||||
that.setPassword(user, pw, callback);
|
||||
} else {
|
||||
if (typeof callback === 'function') callback(err);
|
||||
}
|
||||
});
|
||||
}
|
||||
});
|
||||
};
|
||||
|
||||
this.isUser = function (user, callback) {
|
||||
var _user = user.replace(/\s/g, '_');
|
||||
objects.getObject('system.user.' + _user, function (err, obj) {
|
||||
if (callback) callback(null, !!obj);
|
||||
});
|
||||
};
|
||||
|
||||
this.setPassword = function (user, pw, callback) {
|
||||
var _user = user.replace(/\s/g, '_');
|
||||
|
||||
objects.getObject('system.user.' + _user, function (err, obj) {
|
||||
if (err || !obj) {
|
||||
if (typeof callback === 'function') callback('User does not exist');
|
||||
return;
|
||||
}
|
||||
var password = require(__dirname + '/../password');
|
||||
|
||||
password(pw).hash(null, null, function (err, res) {
|
||||
if (err) {
|
||||
if (typeof callback === 'function') callback(err);
|
||||
return;
|
||||
}
|
||||
obj.common.password = res;
|
||||
obj.from = 'system.host.' + tools.getHostName() + '.cli';
|
||||
obj.ts = new Date().getTime();
|
||||
objects.setObject('system.user.' + _user, obj, function (err) {
|
||||
if (typeof callback === 'function') callback(err);
|
||||
});
|
||||
});
|
||||
});
|
||||
};
|
||||
|
||||
this.checkPassword = function (user, pw, callback) {
|
||||
objects.getObject('system.user.' + user, function (err, obj) {
|
||||
if (err || !obj) {
|
||||
if (typeof callback === 'function') callback('User does not exist');
|
||||
return;
|
||||
}
|
||||
var password = require(__dirname + '/../password');
|
||||
|
||||
password(pw).check(obj.common.password, function (err, res) {
|
||||
if (typeof callback === 'function') callback(err, res);
|
||||
});
|
||||
});
|
||||
};
|
||||
|
||||
this.delUser = function (user, callback) {
|
||||
if (!user) {
|
||||
if (callback) callback('Please define user name, like: "userdel user"');
|
||||
return;
|
||||
}
|
||||
|
||||
var _user = user.replace(/\s/g, '_');
|
||||
|
||||
objects.getObject('system.user.' + _user, function (err, obj) {
|
||||
if (err || !obj) {
|
||||
if (callback) callback('User does not exist');
|
||||
} else {
|
||||
if (obj.common.dontDelete) {
|
||||
if (callback) callback('Cannot delete user, while is system user');
|
||||
} else {
|
||||
objects.delObject('system.user.' + _user, function (err) {
|
||||
// Remove this user from all groups
|
||||
if (!err) {
|
||||
objects.getObjectList({startkey: 'system.group.', endkey: 'system.group.\u9999'}, function (err, groups) {
|
||||
var count = 0;
|
||||
for (var i = 0; i < groups.rows.length; i++) {
|
||||
if (groups.rows[i].value.type != 'group') continue;
|
||||
// find all groups
|
||||
if (groups.rows[i].value.common.members && groups.rows[i].value.common.members.indexOf('system.user.' + _user) != -1) {
|
||||
var pos = groups.rows[i].value.common.members.indexOf('system.user.' + _user);
|
||||
groups.rows[i].value.common.members.splice(pos, 1);
|
||||
count++;
|
||||
groups.rows[i].value.from = 'system.host.' + tools.getHostName() + '.cli';
|
||||
groups.rows[i].value.ts = new Date().getTime();
|
||||
objects.setObject(groups.rows[i].value._id, groups.rows[i].value, function (err) {
|
||||
if (!(--count)) callback(err);
|
||||
});
|
||||
}
|
||||
}
|
||||
if (!count) callback();
|
||||
});
|
||||
} else if (callback) {
|
||||
callback(err);
|
||||
}
|
||||
});
|
||||
}
|
||||
}
|
||||
});
|
||||
};
|
||||
|
||||
this.addUserToGroup = function (user, group, callback) {
|
||||
var _user = user.replace(/\s/g, '_');
|
||||
if (!group.match(/^system\.group\./)) group = 'system.group.' + group;
|
||||
if (!_user.match(/^system\.user\./)) _user = 'system.user.' + _user;
|
||||
|
||||
objects.getObject(_user, function (err, obj) {
|
||||
if (err || !obj) {
|
||||
if (typeof callback === 'function') callback('User does not exist');
|
||||
return;
|
||||
}
|
||||
objects.getObject(group, function (err, obj) {
|
||||
if (err || !obj) {
|
||||
if (typeof callback === 'function') callback('Group does not exist');
|
||||
return;
|
||||
}
|
||||
obj.common = obj.common || {};
|
||||
obj.common.members = obj.common.members || [];
|
||||
|
||||
if (obj.common.members.indexOf(_user) == -1) {
|
||||
obj.common.members.push(_user);
|
||||
obj.from = 'system.host.' + tools.getHostName() + '.cli';
|
||||
obj.ts = new Date().getTime();
|
||||
objects.setObject(group, obj, function (err) {
|
||||
callback(err);
|
||||
});
|
||||
} else {
|
||||
callback();
|
||||
}
|
||||
});
|
||||
});
|
||||
};
|
||||
|
||||
this.addUserPrompt = function (user, group, password, callback) {
|
||||
if (!user) {
|
||||
if (callback) callback('Please define user name, like: "adduser newUser"');
|
||||
return;
|
||||
}
|
||||
|
||||
// Check group
|
||||
if (group.substring(0, 13) !== 'system.group.' ) group = 'system.group.' + group;
|
||||
|
||||
objects.getObject(group, function (err, obj) {
|
||||
if (!obj) {
|
||||
if (callback) callback('Unknown group: ' + group);
|
||||
return;
|
||||
}
|
||||
if (!password) {
|
||||
var prompt = require('prompt');
|
||||
prompt.message = '';
|
||||
prompt.delimiter = '';
|
||||
var schema = {
|
||||
properties: {
|
||||
password: {
|
||||
description: 'Enter your password:',
|
||||
pattern: /^[^'"]+$/,
|
||||
message: 'No " are allowed',
|
||||
hidden: true
|
||||
},
|
||||
repeatPassword: {
|
||||
description: 'Repeat your password:',
|
||||
pattern: /^[^'"]+$/,
|
||||
message: 'No " are allowed',
|
||||
hidden: true
|
||||
}
|
||||
}
|
||||
};
|
||||
prompt.start();
|
||||
|
||||
prompt.get(schema, function (err, result) {
|
||||
if (result) {
|
||||
if (result.password !== result.repeatPassword) {
|
||||
console.log('Passwords are not identical!');
|
||||
processExit(31);
|
||||
}
|
||||
//create user
|
||||
that.addUser(user, result.password, function (err) {
|
||||
if (err) {
|
||||
if (callback) callback(err);
|
||||
} else {
|
||||
that.addUserToGroup(user, group, function (err) {
|
||||
if (err) {
|
||||
if (callback) callback(err);
|
||||
} else {
|
||||
if (callback) callback();
|
||||
}
|
||||
});
|
||||
}
|
||||
});
|
||||
} else {
|
||||
if (callback) callback(err);
|
||||
}
|
||||
});
|
||||
} else {
|
||||
that.addUser(user, password, function (err) {
|
||||
if (err) {
|
||||
if (callback) callback(err);
|
||||
} else {
|
||||
that.addUserToGroup(user, group, function (err) {
|
||||
if (err) {
|
||||
if (callback) callback(err);
|
||||
} else {
|
||||
if (callback) callback();
|
||||
}
|
||||
});
|
||||
}
|
||||
});
|
||||
}
|
||||
});
|
||||
};
|
||||
|
||||
this.setUserPassword = function (user, password, callback) {
|
||||
if (!user) {
|
||||
if (callback) callback('Please define user name, like: "passwd username"');
|
||||
return;
|
||||
}
|
||||
|
||||
this.isUser(user, function (err, result) {
|
||||
if (err) console.error('Cannot read user: ' + err);
|
||||
if (!result) {
|
||||
if (callback) callback('User "' + user + '" does not exist.');
|
||||
} else {
|
||||
// Check group
|
||||
if (!password) {
|
||||
var prompt = require('prompt');
|
||||
prompt.message = '';
|
||||
prompt.delimiter = '';
|
||||
var schema = {
|
||||
properties: {
|
||||
password: {
|
||||
description: 'Enter your password:',
|
||||
pattern: /^[^'"]*$/,
|
||||
message: 'No " are allowed',
|
||||
hidden: true
|
||||
},
|
||||
repeatPassword: {
|
||||
description: 'Repeat your password:',
|
||||
pattern: /^[^'"]*$/,
|
||||
message: 'No " are allowed',
|
||||
hidden: true
|
||||
}
|
||||
}
|
||||
};
|
||||
prompt.start();
|
||||
|
||||
prompt.get(schema, function (err, result) {
|
||||
if (result) {
|
||||
if (result.password !== result.repeatPassword) {
|
||||
if (callback) callback('Passwords are not identical!');
|
||||
return;
|
||||
}
|
||||
// set user password
|
||||
that.setPassword(user, result.password, function (err) {
|
||||
if (err) {
|
||||
if (callback) callback(err);
|
||||
} else {
|
||||
if (callback) callback();
|
||||
}
|
||||
});
|
||||
} else {
|
||||
if (callback) callback('No password entered!');
|
||||
}
|
||||
});
|
||||
} else {
|
||||
that.setPassword(user, password, function (err) {
|
||||
if (err) {
|
||||
if (callback) callback(err);
|
||||
} else {
|
||||
if (callback) callback();
|
||||
}
|
||||
});
|
||||
}
|
||||
}
|
||||
});
|
||||
};
|
||||
|
||||
this.enableUser = function (user, enable, callback) {
|
||||
if (!user) {
|
||||
if (callback) callback('Please define user name, like: "enable username"');
|
||||
return;
|
||||
}
|
||||
if (user && user.match(/^system\.user\./)) user = user.substring('system.user.'.length);
|
||||
|
||||
if (user === 'admin' && !enable) {
|
||||
if (callback) callback('User admin cannot be disabled');
|
||||
return;
|
||||
}
|
||||
|
||||
objects.getObject('system.user.' + user, function (err, obj) {
|
||||
if (err) {
|
||||
if (callback) callback('Cannot read user: ' + err);
|
||||
} if (!obj) {
|
||||
if (callback) callback('User "' + user + '" not found');
|
||||
} else {
|
||||
obj.common.enabled = enable;
|
||||
obj.from = 'system.host.' + tools.getHostName() + '.cli';
|
||||
obj.ts = new Date().getTime();
|
||||
objects.setObject(obj._id, obj, function (err) {
|
||||
if (typeof callback === 'function') callback(err);
|
||||
});
|
||||
}
|
||||
});
|
||||
};
|
||||
|
||||
this.checkUserPassword = function (user, password, callback) {
|
||||
var prompt;
|
||||
var schema;
|
||||
if (!user && !password) {
|
||||
prompt = require('prompt');
|
||||
prompt.message = '';
|
||||
prompt.delimiter = '';
|
||||
schema = {
|
||||
properties: {
|
||||
username: {
|
||||
description: 'Enter username to check password:',
|
||||
pattern: /^[^'"]+$/,
|
||||
message: 'No " are allowed',
|
||||
hidden: false
|
||||
},
|
||||
password: {
|
||||
description: 'Enter current password:',
|
||||
pattern: /^[^'"]+$/,
|
||||
message: 'No " are allowed',
|
||||
hidden: true
|
||||
}
|
||||
}
|
||||
};
|
||||
prompt.start();
|
||||
|
||||
prompt.get(schema, function (err, result) {
|
||||
that.checkPassword(result.username, result.password, function (err, res) {
|
||||
if (err || !res) {
|
||||
if (callback) callback('Password for user "' + result.username + '" does not matched' + (err ? ': ' + err : ''));
|
||||
} else {
|
||||
if (callback) callback(null);
|
||||
}
|
||||
});
|
||||
});
|
||||
} else if (!password) {
|
||||
prompt = require('prompt');
|
||||
prompt.message = '';
|
||||
prompt.delimiter = '';
|
||||
schema = {
|
||||
properties: {
|
||||
password: {
|
||||
description: 'Enter current password:',
|
||||
pattern: /^[^'"]+$/,
|
||||
message: 'No " are allowed',
|
||||
hidden: true
|
||||
}
|
||||
}
|
||||
};
|
||||
prompt.start();
|
||||
|
||||
prompt.get(schema, function (err, result) {
|
||||
that.checkPassword(user, result.password, function (err, res) {
|
||||
if (err || !res) {
|
||||
if (callback) callback('Password for user "' + user + '" does not matched' + (err ? ': ' + err : ''));
|
||||
} else {
|
||||
if (callback) callback(null);
|
||||
}
|
||||
});
|
||||
});
|
||||
} else{
|
||||
this.checkPassword(user, password, function (err, res) {
|
||||
if (err || !res) {
|
||||
if (callback) callback('Password for user "' + user + '" does not matched' + (err ? ': ' + err : ''));
|
||||
} else {
|
||||
if (callback) callback(null);
|
||||
}
|
||||
});
|
||||
}
|
||||
};
|
||||
|
||||
this.getUser = function (user, callback) {
|
||||
objects.getObject('system.user.' + user, function (err, obj) {
|
||||
if (err) {
|
||||
if (callback) callback('Cannot read user: ' + err);
|
||||
} if (!obj) {
|
||||
if (callback) callback('User "' + user + '" not found');
|
||||
} else {
|
||||
if (callback) callback(null, obj.common.enabled);
|
||||
}
|
||||
});
|
||||
};
|
||||
|
||||
this.getGroup = function (group, callback) {
|
||||
objects.getObject('system.group.' + group, function (err, obj) {
|
||||
if (err) {
|
||||
if (callback) callback('Cannot read group: ' + err);
|
||||
} if (!obj) {
|
||||
if (callback) callback('Group "' + group + '" not found');
|
||||
} else {
|
||||
if (callback) callback(null, obj.common.enabled, obj.common.members);
|
||||
}
|
||||
});
|
||||
};
|
||||
|
||||
this.enableGroup = function (group, enable, callback) {
|
||||
if (!group) {
|
||||
if (callback) callback('Please define group name, like: "enable groupname"');
|
||||
return;
|
||||
}
|
||||
if (group && group.match(/^system\.group\./)) group = group.substring('system.group.'.length);
|
||||
|
||||
if (group === 'administrator' && !enable) {
|
||||
if (callback) callback('Group "administrator" cannot be disabled');
|
||||
return;
|
||||
}
|
||||
|
||||
objects.getObject('system.group.' + group, function (err, obj) {
|
||||
if (err) {
|
||||
if (callback) callback('Cannot read group: ' + err);
|
||||
} if (!obj) {
|
||||
if (callback) callback('Group "' + group + '" not found');
|
||||
} else {
|
||||
obj.common.enabled = enable;
|
||||
obj.from = 'system.host.' + tools.getHostName() + '.cli';
|
||||
obj.ts = new Date().getTime();
|
||||
objects.setObject(obj._id, obj, function (err) {
|
||||
if (typeof callback === 'function') callback(err);
|
||||
});
|
||||
}
|
||||
});
|
||||
};
|
||||
|
||||
this.addGroup = function (group, callback) {
|
||||
var _group = group.replace(/\s/g, '_');
|
||||
objects.getObject('system.group.' + _group, function (err, obj) {
|
||||
if (obj) {
|
||||
if (callback) callback('Group yet exists');
|
||||
} else {
|
||||
objects.setObject('system.group.' + _group, {
|
||||
type: 'group',
|
||||
common: {
|
||||
name: group,
|
||||
enabled: true,
|
||||
members: []
|
||||
},
|
||||
from: 'system.host.' + tools.getHostName() + '.cli',
|
||||
ts: new Date().getTime(),
|
||||
native: {}
|
||||
}, function (err) {
|
||||
if (typeof callback === 'function') callback(err);
|
||||
});
|
||||
}
|
||||
});
|
||||
};
|
||||
|
||||
this.delGroup = function (group, callback) {
|
||||
var _group = group.replace(/\s/g, '_');
|
||||
|
||||
if (group === 'administrator') {
|
||||
if (typeof callback === 'function') callback('Group "administrator" cannot be deleted');
|
||||
} else {
|
||||
objects.getObject('system.group.' + _group, function (err, obj) {
|
||||
if (!obj) {
|
||||
if (callback) callback('Group does not exists');
|
||||
} else {
|
||||
objects.delObject('system.group.' + _group, function (err) {
|
||||
if (typeof callback === 'function') callback(err);
|
||||
});
|
||||
}
|
||||
});
|
||||
}
|
||||
};
|
||||
|
||||
this.removeUserFromGroup = function (user, group, callback) {
|
||||
var _group = group.replace(/\s/g, '_');
|
||||
objects.getObject('system.group.' + _group, function (err, obj) {
|
||||
if (!obj) {
|
||||
if (callback) callback('Group does not exists');
|
||||
} else {
|
||||
var pos = obj.common.members.indexOf('system.user.' + user);
|
||||
if (pos === -1) {
|
||||
if (typeof callback === 'function') callback('User not in group');
|
||||
} else {
|
||||
obj.common.members.splice(pos, 1);
|
||||
obj.from = 'system.host.' + tools.getHostName() + '.cli';
|
||||
obj.ts = new Date().getTime();
|
||||
objects.setObject(obj._id, obj, function (err) {
|
||||
if (typeof callback === 'function') callback(err);
|
||||
});
|
||||
}
|
||||
}
|
||||
});
|
||||
};
|
||||
}
|
||||
|
||||
module.exports = Users;
|
||||
197
lib/setup/setupVendor.js
Normal file
197
lib/setup/setupVendor.js
Normal file
@@ -0,0 +1,197 @@
|
||||
'use strict';
|
||||
function Vendor(options) {
|
||||
const fs = require('fs');
|
||||
const tools = require(__dirname + '/../tools.js');
|
||||
options = options || {};
|
||||
|
||||
let objects = options.objects;
|
||||
// read info from '/etc/iob_vendor.json' and executes instructions stored there
|
||||
this.checkVendor = (file, password) => {
|
||||
file = file || '/etc/iob-vendor.json';
|
||||
let data;
|
||||
if (fs.existsSync(file)) {
|
||||
try {
|
||||
data = JSON.parse(fs.readFileSync(file).toString('utf8'));
|
||||
} catch (e) {
|
||||
return Promise.reject(`cannot read or parse "${file}": ${JSON.stringify(e)}`);
|
||||
}
|
||||
} else {
|
||||
return Promise.reject(`"${file}" does not exist`);
|
||||
}
|
||||
|
||||
let promises = [];
|
||||
if (data.uuid) {
|
||||
const uuid = data.uuid;
|
||||
data.uuid = null;
|
||||
|
||||
// check UUID
|
||||
promises.push(objects.getObjectAsync('system.meta.uuid').then(obj => {
|
||||
if (obj && obj.native) {
|
||||
if (obj.native.uuid !== uuid) {
|
||||
obj.native.uuid = uuid;
|
||||
|
||||
console.log(`Update "system.meta.uuid:native.uuid" = "${obj.native.uuid}"`);
|
||||
|
||||
obj.nonEdit = obj.nonEdit || {};
|
||||
obj.nonEdit.password = password;
|
||||
return objects.setObjectAsync('system.meta.uuid', obj).then(() => {
|
||||
console.log('object system.meta.uuid updated: ' + uuid);
|
||||
}).catch(err => {
|
||||
console.error(`Cannot update system.meta.uuid: ${err}`);
|
||||
});
|
||||
}
|
||||
} else {
|
||||
return objects.setObjectAsync('system.meta.uuid', {
|
||||
type: 'meta',
|
||||
common: {
|
||||
name: 'uuid',
|
||||
type: 'uuid'
|
||||
},
|
||||
ts: new Date().getTime(),
|
||||
from: 'system.host.' + tools.getHostName() + '.tools',
|
||||
native: {
|
||||
uuid: uuid
|
||||
}
|
||||
}).then(() => {
|
||||
console.log('object system.meta.uuid created: ' + uuid);
|
||||
}).catch(err => {
|
||||
console.error(`Cannot create system.meta.uuid: ${err}`);
|
||||
});
|
||||
}
|
||||
}));
|
||||
}
|
||||
|
||||
if (data.model) {
|
||||
const model = data.model;
|
||||
data.model = null;
|
||||
const hostname = tools.getHostName();
|
||||
promises.push(objects.getObjectAsync('system.host.' + hostname).then(obj => {
|
||||
if (obj && obj.common) {
|
||||
if ((model.name && model.name !== 'JS controller' && obj.common.title === 'JS controller') ||
|
||||
(model.icon && !obj.common.icon) ||
|
||||
(model.color && !obj.common.color)) {
|
||||
obj.common.title = model.name;
|
||||
obj.common.icon = model.icon;
|
||||
obj.common.color = model.color;
|
||||
|
||||
obj.nonEdit = obj.nonEdit || {};
|
||||
obj.nonEdit.password = password;
|
||||
|
||||
console.log(`Update "system.host.${hostname}:common.title" = "${obj.common.title}"`);
|
||||
console.log(`Update "system.host.${hostname}:common.icon" = "${!!obj.common.icon}"`);
|
||||
console.log(`Update "system.host.${hostname}:common.color" = "${obj.common.color}"`);
|
||||
|
||||
return objects.setObjectAsync(obj._id, obj).then(() => {
|
||||
console.log(`object "system.host.${hostname}" updated`);
|
||||
}).catch(err => {
|
||||
console.error(`Cannot update "system.host.${hostname}": ${err}`);
|
||||
});
|
||||
}
|
||||
}
|
||||
}));
|
||||
}
|
||||
|
||||
if (data.vendor) {
|
||||
const vendor = JSON.parse(JSON.stringify(data.vendor));
|
||||
data._vendor = JSON.parse(JSON.stringify(vendor));
|
||||
data.vendor = null;
|
||||
|
||||
// store vendor
|
||||
promises.push(objects.getObjectAsync('system.config').then(obj => {
|
||||
if (obj && obj.native) {
|
||||
if (JSON.stringify(obj.native.vendor) !== JSON.stringify(vendor)) {
|
||||
obj.native.vendor = vendor;
|
||||
obj.nonEdit = obj.nonEdit || {};
|
||||
obj.nonEdit.password = password;
|
||||
return objects.setObjectAsync(obj._id, obj).then(() => {
|
||||
console.log('object system.config updated');
|
||||
}).catch(err => {
|
||||
console.error(`Cannot update system.config: ${err}`);
|
||||
});
|
||||
}
|
||||
}
|
||||
}));
|
||||
}
|
||||
|
||||
return Promise.all(promises).then(() => {
|
||||
let _promises = [];
|
||||
// update all existing objects according to vendor
|
||||
if (data.objects) {
|
||||
for (let id in data.objects) {
|
||||
if (!data.objects.hasOwnProperty(id)) continue;
|
||||
if (id.indexOf('*') === -1) {
|
||||
((_id, _obj) => {
|
||||
_promises.push(objects.getObjectAsync(_id).then(obj => {
|
||||
if (obj) {
|
||||
obj.nonEdit = obj.nonEdit || {};
|
||||
const originalObj = JSON.stringify(obj);
|
||||
_obj.nonEdit = _obj.nonEdit || {};
|
||||
_obj.nonEdit.passHash = obj.nonEdit.passHash;
|
||||
// merge objects
|
||||
tools.copyAttributes(_obj, obj);
|
||||
|
||||
if (originalObj !== JSON.stringify(obj)) {
|
||||
delete obj.nonEdit.passHash;
|
||||
obj.nonEdit.password = password;
|
||||
console.log(`Update "${obj._id}"`);
|
||||
return objects.setObjectAsync(obj._id, obj).then(() => {
|
||||
console.log(`object "${obj._id}" updated`);
|
||||
}).catch(err => {
|
||||
console.error(`Cannot update "${obj._id}": ${err}`);
|
||||
});
|
||||
}
|
||||
} else {
|
||||
return objects.setObjectAsync(_id, _obj).then(() => {
|
||||
console.log(`object "${obj._id}" updated`);
|
||||
}).catch(err => {
|
||||
console.error(`Cannot update "${obj._id}": ${err}`);
|
||||
});
|
||||
}
|
||||
}));
|
||||
})(id, data.objects[id]);
|
||||
} else {
|
||||
id = id.replace('*', '');
|
||||
((_id, _obj) => {
|
||||
_promises.push(objects.getObjectListAsync({
|
||||
startkey: _id,
|
||||
endkey: _id + '\u9999'
|
||||
}, {checked: true}).then(arr => {
|
||||
let tasks = [];
|
||||
if (arr && arr.rows && arr.rows.length) {
|
||||
for (let g = 0; g < arr.rows.length; g++) {
|
||||
let obj = arr.rows[g].value;
|
||||
if (obj) {
|
||||
obj.nonEdit = obj.nonEdit || {};
|
||||
const originalObj = JSON.stringify(obj);
|
||||
_obj.nonEdit = _obj.nonEdit || {};
|
||||
_obj.nonEdit.passHash = obj.nonEdit.passHash;
|
||||
// merge objects
|
||||
tools.copyAttributes(_obj, obj);
|
||||
if (originalObj !== JSON.stringify(obj)) {
|
||||
delete obj.nonEdit.passHash;
|
||||
obj.nonEdit.password = password;
|
||||
console.log(`Update "${obj._id}"`);
|
||||
tasks.push(objects.setObjectAsync(obj._id, obj).then(() => {
|
||||
console.log(`object "${obj._id}" updated`);
|
||||
}).catch(err => {
|
||||
console.error(`Cannot update "${obj._id}": ${err}`);
|
||||
}));
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
return Promise.all(tasks);
|
||||
}));
|
||||
})(id, data.objects[id]);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
return Promise.all(_promises);
|
||||
});
|
||||
};
|
||||
|
||||
return this;
|
||||
}
|
||||
|
||||
module.exports = Vendor;
|
||||
175
lib/setup/setupVisDebug.js
Normal file
175
lib/setup/setupVisDebug.js
Normal file
@@ -0,0 +1,175 @@
|
||||
'use strict';
|
||||
|
||||
function VisDebug(options) {
|
||||
var fs = require('fs');
|
||||
var tools = require(__dirname + '/../tools.js');
|
||||
var path = require('path');
|
||||
|
||||
// allow use without new operator
|
||||
if (!(this instanceof VisDebug)) return new VisDebug(options);
|
||||
|
||||
options = options || {};
|
||||
|
||||
if (!options.objects) throw 'Invalid arguments: objects is missing';
|
||||
if (!options.processExit) throw 'Invalid arguments: processExit is missing';
|
||||
|
||||
var objects = options.objects;
|
||||
var processExit = options.processExit;
|
||||
|
||||
// upload widget directory to vis directory
|
||||
function uploadWidgets(dir, adapter, path, callback) {
|
||||
var dirs = fs.readdirSync(dir);
|
||||
var count = 0;
|
||||
for (var d = 0; d < dirs.length; d++) {
|
||||
var stat = fs.statSync(dir + '/' + dirs[d]);
|
||||
count++;
|
||||
if (stat.isDirectory()) {
|
||||
uploadWidgets(dir + '/' + dirs[d], adapter, path + '/' + dirs[d], function () {
|
||||
if (!--count && callback) callback();
|
||||
});
|
||||
} else {
|
||||
console.log('Upload "' + dir + '/' + dirs[d] + '"');
|
||||
objects.writeFile(adapter, path + '/' + dirs[d], fs.readFileSync(dir + '/' + dirs[d]), function () {
|
||||
if (!--count && callback) callback();
|
||||
});
|
||||
}
|
||||
}
|
||||
if (!count && callback) callback();
|
||||
}
|
||||
|
||||
this.enableDebug = function (widgetset) {
|
||||
|
||||
if (widgetset) {
|
||||
// Try to find out the adapter directory out of a list of options
|
||||
var adapterDir;
|
||||
var adapterNames2Try = ['vis-' + widgetset, widgetset];
|
||||
if (adapterNames2Try[0] === adapterNames2Try[1]) adapterNames2Try.splice(1, 1);
|
||||
for (var i = 0; i < adapterNames2Try.length; i++) {
|
||||
try {
|
||||
var adapterDir2Try = tools.getAdapterDir(adapterNames2Try[i]);
|
||||
// Query the entry
|
||||
var stats = fs.lstatSync(adapterDir2Try);
|
||||
|
||||
// Is it a directory?
|
||||
if (stats.isDirectory()) {
|
||||
//found it!
|
||||
adapterDir = adapterDir2Try;
|
||||
break;
|
||||
}
|
||||
} catch (e) {
|
||||
|
||||
}
|
||||
}
|
||||
|
||||
if (!adapterDir) throw 'Adapter not found. Tried: ' + adapterNames2Try.join(', ');
|
||||
}
|
||||
|
||||
|
||||
// copy index.html.original to index.html
|
||||
// copy edit.html.original to edit.html
|
||||
// correct appName.json
|
||||
// correct config.js
|
||||
var visDir = __dirname + '/../../node_modules/' + tools.appName + '.vis';
|
||||
if (!fs.existsSync(visDir)) {
|
||||
visDir = __dirname + '/../../node_modules/' + tools.appName.toLowerCase() + '.vis';
|
||||
if (!fs.existsSync(visDir)) {
|
||||
visDir = __dirname + '/../../../' + tools.appName + '.vis';
|
||||
if (!fs.existsSync(visDir)) {
|
||||
visDir = __dirname + '/../../../' + tools.appName.toLowerCase() + '.vis';
|
||||
if (!fs.existsSync(visDir)) {
|
||||
console.error('Cannot find ' + tools.appName + '.vis');
|
||||
processExit(40);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
console.log('Upload "' + path.normalize(visDir + '/www/index.html.original') + '"');
|
||||
var file = fs.readFileSync(visDir + '/www/index.html.original');
|
||||
objects.writeFile('vis', 'index.html', file);
|
||||
|
||||
console.log('Upload "' + path.normalize(visDir + '/www/edit.html.original') + '"');
|
||||
file = fs.readFileSync(visDir + '/www/edit.html.original');
|
||||
objects.writeFile('vis', 'edit.html', file);
|
||||
|
||||
console.log('Modify "' + path.normalize(visDir + '/www/cache.manifest') + '"');
|
||||
file = fs.readFileSync(visDir + '/www/cache.manifest', 'utf8');
|
||||
var n = file.match(/# dev build (\d+)/, '5');
|
||||
n = n[1];
|
||||
file = file.replace('# dev build '+ n, '# dev build ' + (parseInt(n, 10) + 1));
|
||||
objects.writeFile('vis', 'cache.manifest', file);
|
||||
|
||||
file = fs.readFileSync(tools.getConfigFileName(), 'utf8');
|
||||
file = JSON.parse(file);
|
||||
|
||||
var count = 0;
|
||||
if (!file.objects.noFileCache) {
|
||||
file.objects.noFileCache = true;
|
||||
fs.writeFileSync(tools.getConfigFileName(), JSON.stringify(file, null, 2));
|
||||
count++;
|
||||
objects.enableFileCache(false, function (err, actual) {
|
||||
console.log('Disable cache');
|
||||
if (!--count) processExit();
|
||||
});
|
||||
}
|
||||
|
||||
if (widgetset) {
|
||||
count++;
|
||||
objects.readFile('vis', 'js/config.js', null, function (err, data) {
|
||||
data = data.replace(/[\r\n]/g, '');
|
||||
var json = JSON.parse(data.match(/"widgetSets":\s(.*)};/)[1]);
|
||||
var found = false;
|
||||
for (var f = 0; f < json.length; f++) {
|
||||
if (json[f] === widgetset || json[f].name === widgetset) {
|
||||
found = true;
|
||||
break;
|
||||
}
|
||||
}
|
||||
// if widget-set not found in config.js
|
||||
if (!found) {
|
||||
console.log('Modify config.js');
|
||||
var pckg = JSON.parse(fs.readFileSync(adapterDir + '/io-package.json').toString());
|
||||
if (pckg.native && pckg.native.dependencies && pckg.native.dependencies.length){
|
||||
json.push({
|
||||
name: widgetset,
|
||||
depends: pckg.native.dependencies
|
||||
});
|
||||
} else {
|
||||
json.push(widgetset);
|
||||
}
|
||||
|
||||
data = data.replace(/"widgetSets":\s+.*};/, '"widgetSets": ' + JSON.stringify(json, null, 2) + '};');
|
||||
|
||||
objects.writeFile('vis', 'js/config.js', data, function () {
|
||||
// upload all files into vis
|
||||
console.log('Upload ' + adapterDir + '/widgets');
|
||||
uploadWidgets(adapterDir + '/widgets', 'vis', 'widgets', function () {
|
||||
if (!--count) {
|
||||
// timeoout to print all messages
|
||||
setTimeout(function () {
|
||||
processExit();
|
||||
}, 100);
|
||||
}
|
||||
});
|
||||
});
|
||||
} else {
|
||||
// upload all files into vis
|
||||
console.log('Upload "' + adapterDir + '/widgets' + '"');
|
||||
uploadWidgets(adapterDir + '/widgets', 'vis', 'widgets', function () {
|
||||
if (!--count) {
|
||||
// timeoout to print all messages
|
||||
setTimeout(function () {
|
||||
processExit();
|
||||
}, 100);
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
});
|
||||
} else {
|
||||
if (!count) processExit();
|
||||
}
|
||||
};
|
||||
}
|
||||
|
||||
module.exports = VisDebug;
|
||||
13
lib/states.js
Normal file
13
lib/states.js
Normal file
@@ -0,0 +1,13 @@
|
||||
'use strict';
|
||||
|
||||
var getConfigFileName = require(__dirname + '/tools').getConfigFileName;
|
||||
var config = JSON.parse(require('fs').readFileSync(getConfigFileName(), 'utf8'));
|
||||
if (!config.states) config.states = {type: 'file'};
|
||||
|
||||
if (config.states.type === 'file') {
|
||||
module.exports = require(__dirname + '/states/statesInMemClient');
|
||||
} else if (config.states.type === 'redis') {
|
||||
module.exports = require(__dirname + '/states/statesInRedis');
|
||||
} else {
|
||||
throw 'Unknown objects type: ' + config.objects.type;
|
||||
}
|
||||
368
lib/states/statesInMemClient.js
Normal file
368
lib/states/statesInMemClient.js
Normal file
@@ -0,0 +1,368 @@
|
||||
/**
|
||||
* States DB in memory - Client
|
||||
*
|
||||
* Copyright 2013-2018 bluefox <dogafox@gmail.com>
|
||||
*
|
||||
* MIT License
|
||||
*
|
||||
*/
|
||||
|
||||
/** @module statesRedis */
|
||||
|
||||
/* jshint -W097 */
|
||||
/* jshint strict: false */
|
||||
/* jslint node: true */
|
||||
'use strict';
|
||||
|
||||
const io = require('socket.io-client');
|
||||
|
||||
function StatesInMemClient(settings) {
|
||||
var client;
|
||||
var subscribes = {};
|
||||
var connectionTimeout;
|
||||
|
||||
var 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;
|
||||
}
|
||||
|
||||
var __construct = (function () {
|
||||
if (!settings.connection.secure) {
|
||||
client = io.connect('http://' + ((settings.connection.host && settings.connection.host !== '0.0.0.0') ? settings.connection.host : '127.0.0.1') + ':' + (settings.connection.port || 9000));
|
||||
} else {
|
||||
client = io.connect('https://' + ((settings.connection.host && settings.connection.host !== '0.0.0.0') ? settings.connection.host : '127.0.0.1') + ':' + (settings.connection.port || 9000));
|
||||
}
|
||||
|
||||
if (typeof settings.change === 'function') {
|
||||
client.on('message', function (pattern, channel, message) {
|
||||
log.silly(settings.namespace + ' inMem message ', pattern, channel, message);
|
||||
try {
|
||||
settings.change(channel, message);
|
||||
} catch (e) {
|
||||
log.error(settings.namespace + ' message ' + channel + ' ' + message + ' ' + e.message);
|
||||
log.error(settings.namespace + ' ' + e.stack);
|
||||
}
|
||||
});
|
||||
}
|
||||
client.on('disconnect', function (error) {
|
||||
if (connectionTimeout) {
|
||||
clearTimeout(connectionTimeout);
|
||||
connectionTimeout = null;
|
||||
}
|
||||
if (typeof settings.disconnected === 'function') {
|
||||
settings.disconnected(error);
|
||||
} else {
|
||||
log.silly(settings.namespace + ' ' + error);
|
||||
}
|
||||
});
|
||||
client.on('error', function (error) {
|
||||
if (connectionTimeout) {
|
||||
clearTimeout(connectionTimeout);
|
||||
connectionTimeout = null;
|
||||
}
|
||||
if (typeof settings.disconnected === 'function') {
|
||||
settings.disconnected(error);
|
||||
} else {
|
||||
log.error(settings.namespace + ' ' + error.message);
|
||||
log.error(settings.namespace + ' ' + error.stack);
|
||||
}
|
||||
});
|
||||
client.on('connect', function (error) {
|
||||
if (connectionTimeout) {
|
||||
clearTimeout(connectionTimeout);
|
||||
connectionTimeout = null;
|
||||
}
|
||||
if (typeof settings.connected === 'function') settings.connected();
|
||||
});
|
||||
client.on('reconnect', function (error) {
|
||||
// Re-init subscribes
|
||||
for (var t in subscribes) {
|
||||
for (var i = 0; i < subscribes[t].length; i++) {
|
||||
client.emit(t, subscribes[t][i]);
|
||||
}
|
||||
}
|
||||
if (typeof settings.connected === 'function') settings.connected();
|
||||
});
|
||||
connectionTimeout = setTimeout(function () {
|
||||
if (typeof settings.connectTimeout === 'function') settings.connectTimeout('Connection timeout');
|
||||
connectionTimeout = null;
|
||||
}, 5000);
|
||||
})();
|
||||
|
||||
/**
|
||||
* @method setState
|
||||
* @param id {String} the id of the value. '<namespace>.' will be prepended
|
||||
* @param state {any}
|
||||
*
|
||||
*
|
||||
* an object containing the actual value and some metadata:<br>
|
||||
* setState(id, {'val': val, 'ts': ts, 'ack': ack, 'from': from, 'lc': lc})
|
||||
*
|
||||
* if no object is given state is treated as val:<br>
|
||||
* setState(id, val)
|
||||
*
|
||||
* <ul><li><b>val</b> the actual value. Can be any JSON-stringifiable object. If undefined the
|
||||
* value is kept unchanged.</li>
|
||||
*
|
||||
* <li><b>ack</b> a boolean that can be used to mark a value as confirmed, used in bidirectional systems which
|
||||
* acknowledge that a value has been successfully set. Will be set to false if undefined.</li>
|
||||
*
|
||||
* <li><b>ts</b> a unix timestamp indicating the last write-operation on the state. Will be set by the
|
||||
* setState method if undefined.</li>
|
||||
*
|
||||
* <li><b>lc</b> a unix timestamp indicating the last change of the actual value. this should be undefined
|
||||
* when calling setState, it will be set by the setValue method itself.</li></ul>
|
||||
*
|
||||
* @param callback {Function} will be called when redis confirmed reception of the command
|
||||
*
|
||||
*
|
||||
*/
|
||||
this.setState = function (id, state, callback) {
|
||||
if (!client) return;
|
||||
client.emit('setState', id, state, callback);
|
||||
};
|
||||
|
||||
// Used for restore function (do not call it
|
||||
this.setRawState = function (id, state, callback) {
|
||||
if (!client) return;
|
||||
client.emit('setRawState', id, state, callback);
|
||||
};
|
||||
|
||||
/**
|
||||
* @method getState
|
||||
*
|
||||
* @param {String} id
|
||||
* @param callback
|
||||
*/
|
||||
this.getState = function (id, callback) {
|
||||
if (!client) return;
|
||||
client.emit('getState', id, callback);
|
||||
};
|
||||
|
||||
this.getStates = function (keys, callback) {
|
||||
if (!client) return;
|
||||
client.emit('getStates', keys, function (err, res) {
|
||||
if (callback) callback(err, res);
|
||||
});
|
||||
};
|
||||
|
||||
this.delState = function (id, callback) {
|
||||
if (!client) return;
|
||||
client.emit('delState', id, callback);
|
||||
};
|
||||
|
||||
this.getKeys = function (pattern, callback, dontModify) {
|
||||
if (!client) return;
|
||||
client.emit('getKeys', pattern, callback);
|
||||
};
|
||||
|
||||
/**
|
||||
* @method subscribe
|
||||
*
|
||||
* @param {string} pattern
|
||||
* @param {function} callback
|
||||
*/
|
||||
this.subscribe = function (pattern, callback) {
|
||||
subscribes.subscribe = subscribes.subscribe || [];
|
||||
if (subscribes.subscribe.indexOf(pattern) === -1) subscribes.subscribe.push(pattern);
|
||||
if (!client) return;
|
||||
client.emit('subscribe', pattern, callback);
|
||||
};
|
||||
|
||||
this.unsubscribe = function (pattern, callback) {
|
||||
if (subscribes.subscribe) {
|
||||
var pos = subscribes.subscribe.indexOf(pattern);
|
||||
if (pos !== -1) subscribes.subscribe.splice(pos, 1);
|
||||
}
|
||||
if (!client) return;
|
||||
client.emit('unsubscribe', pattern, callback);
|
||||
};
|
||||
|
||||
// this.pushFifoExists = function (id, state, callback) {
|
||||
// if (!client) return;
|
||||
// client.emit('pushFifoExists', id, state, callback);
|
||||
// };
|
||||
//
|
||||
// this.pushFifo = function (id, state, callback) {
|
||||
// if (!client) return;
|
||||
// client.emit('pushFifo', id, state, callback);
|
||||
// };
|
||||
//
|
||||
// this.lenFifo = function (id, callback) {
|
||||
// if (!client) return;
|
||||
// client.emit('lenFifo', id, callback);
|
||||
// };
|
||||
//
|
||||
// this.getFifo = function (id, callback) {
|
||||
// if (!client) return;
|
||||
// client.emit('getFifo', id, callback);
|
||||
// };
|
||||
//
|
||||
// this.getFifoRange = function (id, start, end, callback) {
|
||||
// if (!client) return;
|
||||
// client.emit('getFifoRange', id, start, end, callback);
|
||||
// };
|
||||
//
|
||||
// this.trimFifo = function (id, minLength, maxLength, callback) {
|
||||
// if (!client) return;
|
||||
// client.emit('trimFifo', id, minLength, maxLength, callback);
|
||||
// };
|
||||
|
||||
this.pushMessage = function (id, state, callback) {
|
||||
if (!client) return;
|
||||
client.emit('pushMessage', id, state, callback);
|
||||
};
|
||||
|
||||
this.lenMessage = function (id, callback) {
|
||||
if (!client) return;
|
||||
client.emit('lenMessage', id, callback);
|
||||
};
|
||||
|
||||
this.getMessage = function (id, callback) {
|
||||
if (!client) return;
|
||||
client.emit('getMessage', id, callback);
|
||||
};
|
||||
|
||||
this.delMessage = function (id, messageId, callback) {
|
||||
if (!client) return;
|
||||
client.emit('delMessage', id, messageId, callback);
|
||||
};
|
||||
|
||||
this.subscribeMessage = function (pattern, callback) {
|
||||
subscribes.subscribeMessage = subscribes.subscribeMessage || [];
|
||||
if (subscribes.subscribeMessage.indexOf(pattern) === -1) subscribes.subscribeMessage.push(pattern);
|
||||
if (!client) return;
|
||||
client.emit('subscribeMessage', pattern, callback);
|
||||
};
|
||||
|
||||
this.unsubscribeMessage = function (pattern, callback) {
|
||||
if (subscribes.subscribeMessage) {
|
||||
var pos = subscribes.subscribeMessage.indexOf(pattern);
|
||||
if (pos !== -1) subscribes.subscribeMessage.splice(pos, 1);
|
||||
}
|
||||
if (!client) return;
|
||||
client.emit('unsubscribeMessage', pattern, callback);
|
||||
};
|
||||
|
||||
this.pushLog = function (id, state, callback) {
|
||||
if (!client) return;
|
||||
client.emit('pushLog', id, state, callback);
|
||||
};
|
||||
|
||||
this.lenLog = function (id, callback) {
|
||||
if (!client) return;
|
||||
client.emit('lenLog', id, callback);
|
||||
};
|
||||
|
||||
this.getLog = function (id, callback) {
|
||||
if (!client) return;
|
||||
client.emit('getLog', id, callback);
|
||||
};
|
||||
|
||||
this.delLog = function (id, logId, callback) {
|
||||
if (!client) return;
|
||||
client.emit('delLog', id, logId, callback);
|
||||
};
|
||||
|
||||
this.subscribeLog = function (pattern, callback) {
|
||||
subscribes.subscribeLog = subscribes.subscribeLog || [];
|
||||
if (subscribes.subscribeLog.indexOf(pattern) === -1) subscribes.subscribeLog.push(pattern);
|
||||
|
||||
if (!client) return;
|
||||
client.emit('subscribeLog', pattern, callback);
|
||||
};
|
||||
|
||||
this.unsubscribeLog = function (pattern, callback) {
|
||||
if (subscribes.subscribeLog) {
|
||||
var pos = subscribes.subscribeLog.indexOf(pattern);
|
||||
if (pos !== -1) subscribes.subscribeLog.splice(pos, 1);
|
||||
}
|
||||
if (!client) return;
|
||||
client.emit('unsubscribeLog', pattern, callback);
|
||||
};
|
||||
|
||||
this.getSession = function (id, callback) {
|
||||
if (!client) return;
|
||||
client.emit('getSession', id, callback);
|
||||
};
|
||||
|
||||
this.setSession = function (id, expire, obj, callback) {
|
||||
if (!client) return;
|
||||
client.emit('setSession', id, expire, obj, callback);
|
||||
};
|
||||
|
||||
this.destroySession = function (id, callback) {
|
||||
if (!client) return;
|
||||
client.emit('destroySession', id, callback);
|
||||
};
|
||||
|
||||
this.getConfig = function (id, callback) {
|
||||
if (!client) return;
|
||||
client.emit('getConfig', id, callback);
|
||||
};
|
||||
|
||||
this.getConfigKeys = function (pattern, callback, dontModify) {
|
||||
if (!client) return;
|
||||
client.emit('getConfigKeys', pattern, callback);
|
||||
};
|
||||
|
||||
this.getConfigs = function (keys, callback, dontModify) {
|
||||
if (!client) return;
|
||||
client.emit('getConfigs', keys, callback);
|
||||
};
|
||||
|
||||
this.setConfig = function (id, obj, callback) {
|
||||
if (!client) return;
|
||||
client.emit('setConfig', id, obj, callback);
|
||||
};
|
||||
|
||||
this.delConfig = function (id, callback) {
|
||||
if (!client) return;
|
||||
client.emit('delConfig', id, callback);
|
||||
};
|
||||
|
||||
this.subscribeConfig = function (pattern, callback) {
|
||||
subscribes.subscribeConfig = subscribes.subscribeConfig || [];
|
||||
if (subscribes.subscribeConfig.indexOf(pattern) === -1) subscribes.subscribeConfig.push(pattern);
|
||||
if (!client) return;
|
||||
client.emit('subscribeConfig', pattern, callback);
|
||||
};
|
||||
|
||||
this.unsubscribeConfig = function (pattern, callback) {
|
||||
if (subscribes.subscribeConfig) {
|
||||
var pos = subscribes.subscribeConfig.indexOf(pattern);
|
||||
if (pos !== -1) subscribes.subscribeConfig.splice(pos, 1);
|
||||
}
|
||||
if (!client) return;
|
||||
client.emit('unsubscribeConfig', pattern, callback);
|
||||
};
|
||||
|
||||
this.setBinaryState = function (id, data, callback) {
|
||||
if (!client) return;
|
||||
client.emit('setBinaryState', id, data, callback);
|
||||
};
|
||||
|
||||
this.getBinaryState = function (id, callback) {
|
||||
if (!client) return;
|
||||
client.emit('getBinaryState', id, callback);
|
||||
};
|
||||
|
||||
this.delBinaryState = function (id, callback) {
|
||||
if (!client) return;
|
||||
client.emit('delBinaryState', id, callback);
|
||||
};
|
||||
}
|
||||
|
||||
module.exports = StatesInMemClient;
|
||||
1036
lib/states/statesInMemServer.js
Normal file
1036
lib/states/statesInMemServer.js
Normal file
File diff suppressed because it is too large
Load Diff
636
lib/states/statesInRedis.js
Normal file
636
lib/states/statesInRedis.js
Normal file
@@ -0,0 +1,636 @@
|
||||
/**
|
||||
* States DB in redis - Client
|
||||
*
|
||||
* Copyright 2013-2018 bluefox <dogafox@gmail.com>
|
||||
* Copyright 2013-2014 hobbyquaker
|
||||
*
|
||||
* MIT License
|
||||
*
|
||||
*/
|
||||
/** @module statesRedis */
|
||||
|
||||
/* jshint -W097 */
|
||||
/* jshint strict: false */
|
||||
/* jslint node: true */
|
||||
'use strict';
|
||||
|
||||
const redis = require('redis');
|
||||
|
||||
function StateRedis(settings) {
|
||||
|
||||
const redisNamespace = (settings.redisNamespace || 'io') + '.';
|
||||
const namespaceMsg = (settings.namespaceMsg || 'messagebox') + '.';
|
||||
const namespaceLog = (settings.namespaceLog || 'log') + '.';
|
||||
const namespaceSession = (settings.namespaceSession || 'session') + '.';
|
||||
// const namespaceConfig = (settings.namespaceConfig || 'config') + '.';
|
||||
const onChange = settings.change; // on change handler
|
||||
let globalMessageId = Math.round(Math.random() * 100000000);
|
||||
let globalLogId = Math.round(Math.random() * 100000000);
|
||||
settings.namespace = settings.namespace || settings.hostname || '';
|
||||
|
||||
let client;
|
||||
let clientBin;
|
||||
let sub;
|
||||
const ioRegExp = new RegExp('^' + redisNamespace);
|
||||
|
||||
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;
|
||||
}
|
||||
|
||||
// limit max number of log entries in the list
|
||||
settings.connection.maxQueue = settings.connection.maxQueue || 1000;
|
||||
|
||||
if (settings.connection.options) {
|
||||
if (settings.connection.options.retry_max_delay) {
|
||||
const retry_max_delay = settings.connection.options.retry_max_delay;
|
||||
// convert redis 0.1 options to redis 3.0
|
||||
settings.connection.options.retry_strategy = function (options) {
|
||||
// A function that receives an options object as parameter including the retry attempt,
|
||||
// the total_retry_time indicating how much time passed since the last time connected,
|
||||
// the error why the connection was lost and the number of times_connected in total.
|
||||
// If you return a number from this function, the retry will happen exactly after that
|
||||
// time in milliseconds. If you return a non-number, no further retry will happen and
|
||||
// all offline commands are flushed with errors. Return an error to return that
|
||||
// specific error to all offline commands.
|
||||
|
||||
return retry_max_delay;
|
||||
/*if (options.error.code === 'ECONNREFUSED') {
|
||||
// End reconnecting on a specific error and flush all commands with a individual error
|
||||
return new Error('The server refused the connection');
|
||||
}
|
||||
if (options.total_retry_time > 1000 * 60 * 60) {
|
||||
// End reconnecting after a specific timeout and flush all commands with a individual error
|
||||
return new Error('Retry time exhausted');
|
||||
}
|
||||
if (options.times_connected > 10) {
|
||||
// End reconnecting with built in error
|
||||
return undefined;
|
||||
}
|
||||
// reconnect after
|
||||
return Math.max(options.attempt * 100, 3000);*/
|
||||
};
|
||||
delete settings.connection.options.retry_max_delay;
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* @method setState
|
||||
* @param id {String} the id of the value. '<redisNamespace>.' will be prepended
|
||||
* @param state {any}
|
||||
*
|
||||
*
|
||||
* an object containing the actual value and some metadata:<br>
|
||||
* setState(id, {'val': val, 'ts': ts, 'ack': ack, 'from': from, 'lc': lc})
|
||||
*
|
||||
* if no object is given state is treated as val:<br>
|
||||
* setState(id, val)
|
||||
*
|
||||
* <ul><li><b>val</b> the actual value. Can be any JSON-stringifiable object. If undefined the
|
||||
* value is kept unchanged.</li>
|
||||
*
|
||||
* <li><b>ack</b> a boolean that can be used to mark a value as confirmed, used in bidirectional systems which
|
||||
* acknowledge that a value has been successfully set. Will be set to false if undefined.</li>
|
||||
*
|
||||
* <li><b>ts</b> a unix timestamp indicating the last write-operation on the state. Will be set by the
|
||||
* setState method if undefined.</li>
|
||||
*
|
||||
* <li><b>lc</b> a unix timestamp indicating the last change of the actual value. this should be undefined
|
||||
* when calling setState, it will be set by the setValue method itself.</li></ul>
|
||||
*
|
||||
* @param callback {Function} will be called when redis confirmed reception of the command
|
||||
*/
|
||||
this.setState = function (id, state, callback) {
|
||||
let expire;
|
||||
if (state.expire) {
|
||||
expire = state.expire;
|
||||
delete state.expire;
|
||||
}
|
||||
//var that = this;
|
||||
let obj = {};
|
||||
|
||||
if (typeof state !== 'object') {
|
||||
state = {
|
||||
val: state
|
||||
};
|
||||
}
|
||||
|
||||
client.get(redisNamespace + id, function (err, oldObj) {
|
||||
// TODO Error Handling
|
||||
if (err) log.warn(settings.namespace + ' ' + err);
|
||||
|
||||
if (!oldObj) {
|
||||
oldObj = {};
|
||||
} else {
|
||||
try {
|
||||
oldObj = JSON.parse(oldObj);
|
||||
} catch (e) {
|
||||
oldObj = {};
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
if (state.val !== undefined) {
|
||||
obj.val = state.val;
|
||||
} else {
|
||||
obj.val = oldObj.val;
|
||||
}
|
||||
|
||||
if (state.ack !== undefined) {
|
||||
obj.ack = state.ack;
|
||||
} else {
|
||||
obj.ack = false;
|
||||
}
|
||||
|
||||
if (state.ts !== undefined) {
|
||||
obj.ts = (state.ts < 946681200000) ? state.ts * 1000 : state.ts; // if less 2000.01.01 00:00:00
|
||||
} else {
|
||||
obj.ts = (new Date()).getTime();
|
||||
}
|
||||
|
||||
if (state.q !== undefined) {
|
||||
obj.q = state.q;
|
||||
} else {
|
||||
obj.q = 0;
|
||||
}
|
||||
|
||||
obj.from = state.from;
|
||||
|
||||
let hasChanged;
|
||||
|
||||
if (state.lc !== undefined) {
|
||||
obj.lc = state.lc;
|
||||
} else {
|
||||
if (typeof obj.val === 'object') {
|
||||
hasChanged = JSON.stringify(oldObj.val) !== JSON.stringify(obj.val);
|
||||
} else {
|
||||
hasChanged = oldObj.val !== obj.val;
|
||||
}
|
||||
if (!oldObj.lc || hasChanged) {
|
||||
obj.lc = obj.ts;
|
||||
} else {
|
||||
obj.lc = oldObj.lc;
|
||||
}
|
||||
}
|
||||
|
||||
// publish event in redis
|
||||
log.silly(settings.namespace + ' redis publish ' + redisNamespace + id + ' ' + JSON.stringify(obj));
|
||||
client.publish(redisNamespace + id, JSON.stringify(obj));
|
||||
|
||||
|
||||
// set object in redis
|
||||
if (expire) {
|
||||
//console.log('setex',redisNamespace + id, expire, JSON.stringify(obj));
|
||||
client.setex(redisNamespace + id, expire, JSON.stringify(obj), function () {
|
||||
if (typeof callback === 'function') {
|
||||
callback();
|
||||
}
|
||||
});
|
||||
} else {
|
||||
//console.log('set',redisNamespace + id, JSON.stringify(obj));
|
||||
client.set(redisNamespace + id, JSON.stringify(obj), function () {
|
||||
if (typeof callback === 'function') {
|
||||
callback();
|
||||
}
|
||||
});
|
||||
}
|
||||
});
|
||||
};
|
||||
|
||||
// Used for restore function (do not call it)
|
||||
this.setRawState = function (id, state, callback) {
|
||||
//console.log('set',redisNamespace + id, JSON.stringify(obj));
|
||||
client.set(redisNamespace + id, state, function () {
|
||||
if (typeof callback === 'function') {
|
||||
callback();
|
||||
}
|
||||
});
|
||||
};
|
||||
|
||||
/**
|
||||
* @method getState
|
||||
*
|
||||
* @param {String} id
|
||||
* @param callback
|
||||
*/
|
||||
this.getState = function (id, callback) {
|
||||
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);
|
||||
}
|
||||
if (typeof callback === 'function') {
|
||||
callback(err, obj ? JSON.parse(obj) : null);
|
||||
}
|
||||
});
|
||||
};
|
||||
|
||||
this.getStates = function (keys, callback, dontModify) {
|
||||
if (!keys) {
|
||||
if (callback) callback('no keys', null);
|
||||
return;
|
||||
}
|
||||
if (!keys.length) {
|
||||
if (callback) 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, obj) {
|
||||
if (err) {
|
||||
log.warn(settings.namespace + ' redis mget ' + ((!obj) ? 0 : obj.length) + ' ' + _keys.length + ', err: ' + err);
|
||||
} else {
|
||||
log.silly(settings.namespace + ' redis mget ' + ((!obj) ? 0 : obj.length) + ' ' + _keys.length);
|
||||
}
|
||||
if (typeof callback === 'function') callback(err, obj);
|
||||
});
|
||||
};
|
||||
|
||||
// Destructor of the class. Called by shutting down.
|
||||
this.destroy = function () {
|
||||
if (client) {
|
||||
client.end(true);
|
||||
client = null;
|
||||
}
|
||||
if (sub) {
|
||||
sub.end();
|
||||
sub = null;
|
||||
}
|
||||
};
|
||||
|
||||
this.delState = function (id, callback) {
|
||||
client.del(redisNamespace + id, function (err) {
|
||||
if (err) {
|
||||
log.warn(settings.namespace + ' redis del ' + id + ', error - ' + err);
|
||||
} else {
|
||||
client.publish(redisNamespace + id, 'null');
|
||||
log.silly(settings.namespace + ' redis del ' + id + ', ok');
|
||||
}
|
||||
if (typeof callback === 'function') callback(err);
|
||||
});
|
||||
};
|
||||
|
||||
this.getKeys = function (pattern, callback, dontModify) {
|
||||
client.keys(redisNamespace + pattern, function (err, obj) {
|
||||
log.silly(settings.namespace + ' redis keys ' + obj.length + ' ' + pattern);
|
||||
if (typeof callback === 'function') {
|
||||
if (obj && !dontModify) {
|
||||
const len = redisNamespace.length;
|
||||
for (let i = 0; i < obj.length; i++) {
|
||||
obj[i] = obj[i].substring(len);
|
||||
}
|
||||
}
|
||||
callback(err, obj);
|
||||
}
|
||||
});
|
||||
};
|
||||
/**
|
||||
* @method subscribe
|
||||
*
|
||||
* @param pattern
|
||||
* @param {function} callback callback function (optional)
|
||||
*/
|
||||
this.subscribe = function (pattern, callback) {
|
||||
log.silly(settings.namespace + ' redis psubscribe ' + redisNamespace + pattern);
|
||||
sub.psubscribe(redisNamespace + pattern, function (err) {
|
||||
if (typeof callback === 'function') callback(err);
|
||||
});
|
||||
};
|
||||
|
||||
this.unsubscribe = function (pattern, callback) {
|
||||
log.silly(settings.namespace + ' redis punsubscribe ' + redisNamespace + pattern);
|
||||
sub.punsubscribe(redisNamespace + pattern, function (err) {
|
||||
if (typeof callback === 'function') callback(err);
|
||||
});
|
||||
};
|
||||
|
||||
this.pushMessage = function (id, state, callback) {
|
||||
state._id = globalMessageId++;
|
||||
if (globalMessageId >= 0xFFFFFFFF) globalMessageId = 0;
|
||||
client.publish(namespaceMsg + id, JSON.stringify(state));
|
||||
if (typeof callback === 'function') callback(null, id);
|
||||
};
|
||||
|
||||
// todo: delete it
|
||||
this.lenMessage = function (id, callback) {
|
||||
if (typeof callback === 'function') callback(null, 0, id);
|
||||
};
|
||||
|
||||
// todo: delete it
|
||||
this.getMessage = function (id, callback) {
|
||||
if (typeof callback === 'function') callback(null, null, id);
|
||||
};
|
||||
|
||||
// todo: delete it
|
||||
this.delMessage = function (id, messageId, callback) {
|
||||
if (typeof callback === 'function') callback(null, id);
|
||||
};
|
||||
|
||||
// todo: delete it
|
||||
this.clearAllMessages = function (callback) {
|
||||
client.keys(namespaceLog + '*', function (err, obj) {
|
||||
if (obj) {
|
||||
for (let i = 0; i < obj.length; i++) {
|
||||
log.silly('redis clear message for ' + obj[i]);
|
||||
client.del(obj[i]);
|
||||
}
|
||||
}
|
||||
|
||||
if (typeof callback === 'function') callback(err);
|
||||
});
|
||||
};
|
||||
|
||||
this.subscribeMessage = function (id, callback) {
|
||||
if (id && id[0] === '.') id = id.substring(1);
|
||||
log.silly('redis subscribeMessage ' + namespaceMsg + id);
|
||||
sub.psubscribe(namespaceMsg + id, function (err) {
|
||||
if (typeof callback === 'function') callback(err);
|
||||
});
|
||||
};
|
||||
|
||||
this.unsubscribeMessage = function (id, callback) {
|
||||
if (id && id[0] === '.') id = id.substring(1);
|
||||
log.silly('redis unsubscribeMessage ' + namespaceMsg + id);
|
||||
sub.punsubscribe(namespaceMsg + id, function (err) {
|
||||
if (typeof callback === 'function') callback(err);
|
||||
});
|
||||
};
|
||||
|
||||
this.pushLog = function (id, log, callback) {
|
||||
log._id = globalLogId++;
|
||||
if (globalLogId >= 0xFFFFFFFF) globalLogId = 0;
|
||||
client.publish(namespaceLog + id, JSON.stringify(log));
|
||||
if (typeof callback === 'function') callback(null, id);
|
||||
};
|
||||
|
||||
// todo: delete it
|
||||
this.lenLog = function (id, callback) {
|
||||
if (typeof callback === 'function') callback('Not exists', 0, id);
|
||||
// client.llen(namespaceLog + id, function (err, obj) {
|
||||
// if (typeof callback === 'function') callback(err, obj, id);
|
||||
// });
|
||||
};
|
||||
|
||||
// todo: delete it
|
||||
this.getLog = function (id, callback) {
|
||||
if (typeof callback === 'function') {
|
||||
callback('Not exists', null, 0);
|
||||
}
|
||||
};
|
||||
|
||||
// todo: delete it
|
||||
this.delLog = function (id, logId, callback) {
|
||||
if (typeof callback === 'function') {
|
||||
callback('Not exists');
|
||||
}
|
||||
};
|
||||
|
||||
// todo: delete it
|
||||
this.clearAllLogs = function (callback) {
|
||||
client.keys(namespaceLog + '*', function (err, obj) {
|
||||
if (obj) {
|
||||
for (let i = 0; i < obj.length; i++) {
|
||||
log.silly(settings.namespace + ' redis clear log for ' + obj[i]);
|
||||
client.del(obj[i]);
|
||||
}
|
||||
}
|
||||
|
||||
if (typeof callback === 'function') {
|
||||
callback(err);
|
||||
}
|
||||
});
|
||||
};
|
||||
|
||||
this.subscribeLog = function (id, callback) {
|
||||
log.silly(settings.namespace + ' redis subscribeMessage ' + namespaceLog + id);
|
||||
sub.psubscribe(namespaceLog + id, function (err) {
|
||||
if (typeof callback === 'function') callback(err);
|
||||
});
|
||||
};
|
||||
|
||||
this.unsubscribeLog = function (id, callback) {
|
||||
log.silly(settings.namespace + ' redis unsubscribeMessage ' + namespaceLog + id);
|
||||
sub.punsubscribe(namespaceLog + id, function (err) {
|
||||
if (typeof callback === 'function') callback(err);
|
||||
});
|
||||
};
|
||||
|
||||
this.getSession = function (id, callback) {
|
||||
client.get(namespaceSession + id, function (err, obj) {
|
||||
log.silly(settings.namespace + ' redis get ' + id + ' ' + obj);
|
||||
if (typeof callback === 'function') callback(obj ? JSON.parse(obj) : null);
|
||||
});
|
||||
};
|
||||
|
||||
this.setSession = function (id, expire, obj, callback) {
|
||||
client.setex(namespaceSession + id, expire, JSON.stringify(obj), function () {
|
||||
log.silly(settings.namespace + ' redis setex', id, expire, obj);
|
||||
if (typeof callback === 'function') callback();
|
||||
});
|
||||
};
|
||||
|
||||
this.destroySession = function (id, callback) {
|
||||
id = namespaceSession + id;
|
||||
log.silly(settings.namespace + ' redis del ' + id);
|
||||
client.del(id, function () {
|
||||
if (typeof callback === 'function') callback();
|
||||
});
|
||||
};
|
||||
|
||||
/* this.getConfig = function (id, callback) {
|
||||
id = namespaceConfig + id;
|
||||
client.get(id, function (err, obj) {
|
||||
log.silly(settings.namespace + ' redis get ' + id + ' ' + obj);
|
||||
if (typeof callback === 'function') callback(err, obj ? JSON.parse(obj) : null);
|
||||
});
|
||||
};
|
||||
|
||||
this.getConfigKeys = function (pattern, callback, dontModify) {
|
||||
client.keys(namespaceConfig + pattern, function (err, obj) {
|
||||
log.silly(settings.namespace + ' redis config keys ' + obj.length + ' ' + pattern);
|
||||
if (typeof callback === 'function') {
|
||||
if (obj && !dontModify) {
|
||||
var len = redisNamespace.length;
|
||||
for (var i = 0; i < obj.length; i++) {
|
||||
obj[i] = obj[i].substring(len);
|
||||
}
|
||||
}
|
||||
callback(err, obj);
|
||||
}
|
||||
});
|
||||
};
|
||||
|
||||
this.getConfigs = function (keys, callback, dontModify) {
|
||||
if (!keys) {
|
||||
if (callback) callback('no keys', null);
|
||||
return;
|
||||
}
|
||||
if (!keys.length) {
|
||||
if (callback) callback(null, []);
|
||||
return;
|
||||
}
|
||||
var _keys;
|
||||
if (!dontModify) {
|
||||
_keys = [];
|
||||
for (var i = 0; i < keys.length; i++) {
|
||||
_keys[i] = namespaceConfig + keys[i];
|
||||
}
|
||||
} else {
|
||||
_keys = keys;
|
||||
}
|
||||
|
||||
client.mget(_keys, function (err, obj) {
|
||||
if (err) {
|
||||
log.warn(settings.namespace + ' redis mget ' + ((!obj) ? 0 : obj.length) + ' ' + _keys.length + ', err: ' + err);
|
||||
} else {
|
||||
log.silly(settings.namespace + ' redis mget ' + ((!obj) ? 0 : obj.length) + ' ' + _keys.length);
|
||||
}
|
||||
if (typeof callback === 'function') callback(err, obj);
|
||||
});
|
||||
};
|
||||
|
||||
this.setConfig = function (id, obj, callback) {
|
||||
id = namespaceConfig + id;
|
||||
client.set(id, JSON.stringify(obj), function (err) {
|
||||
log.silly(settings.namespace + ' redis set', id, obj);
|
||||
client.publish(id, JSON.stringify(obj));
|
||||
if (typeof callback === 'function') callback(err, {id: id});
|
||||
});
|
||||
};
|
||||
|
||||
this.delConfig = function (id, callback) {
|
||||
id = namespaceConfig + id;
|
||||
log.silly(settings.namespace + ' redis del ' + id);
|
||||
client.del(id, function (err) {
|
||||
client.publish(id, null);
|
||||
if (typeof callback === 'function') callback(err);
|
||||
});
|
||||
};
|
||||
|
||||
this.subscribeConfig = function (id, callback) {
|
||||
log.silly(settings.namespace + ' redis subscribeConfig ' + namespaceConfig + id);
|
||||
sub.psubscribe(namespaceConfig + id, function (err) {
|
||||
if (typeof callback === 'function') callback(err);
|
||||
});
|
||||
};
|
||||
|
||||
this.unsubscribeConfig = function (id, callback) {
|
||||
log.silly(settings.namespace + ' redis unsubscribeConfig ' + namespaceConfig + id);
|
||||
sub.punsubscribe(namespaceConfig + id, function (err) {
|
||||
if (typeof callback === 'function') callback(err);
|
||||
});
|
||||
};*/
|
||||
|
||||
function _createBinaryClient() {
|
||||
if (!clientBin) {
|
||||
settings.connection.options = settings.connection.options || {};
|
||||
let opt = JSON.parse(JSON.stringify(settings.connection.options));
|
||||
opt.return_buffers = true;
|
||||
if (settings.connection.port === 0) {
|
||||
// initiate a unix socket connection using the parameter 'host'
|
||||
clientBin = redis.createClient(settings.connection.host, opt);
|
||||
} else {
|
||||
clientBin = redis.createClient(settings.connection.port, settings.connection.host, opt);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
this.setBinaryState = function (id, data, callback) {
|
||||
if (!clientBin) _createBinaryClient ();
|
||||
clientBin.set(id, data, callback);
|
||||
};
|
||||
|
||||
this.getBinaryState = function (id, callback) {
|
||||
if (!clientBin) _createBinaryClient ();
|
||||
clientBin.get(id, function (err, data) {
|
||||
if (!err && data) {
|
||||
if (callback) callback(err, new Buffer(data, 'binary'));
|
||||
} else {
|
||||
if (callback) callback(err);
|
||||
|
||||
}
|
||||
});
|
||||
};
|
||||
|
||||
this.delBinaryState = function (id, callback) {
|
||||
if (!clientBin) _createBinaryClient ();
|
||||
clientBin.del(id, function () {
|
||||
if (typeof callback === 'function') callback();
|
||||
});
|
||||
};
|
||||
(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', function (pattern, channel, message) {
|
||||
log.debug(settings.namespace + ' redis pmessage ', pattern, channel, message);
|
||||
try {
|
||||
if (ioRegExp.test(channel)) {
|
||||
onChange(channel.slice(redisNamespace.length), message ? JSON.parse(message) : null);
|
||||
} else {
|
||||
onChange(channel, message ? JSON.parse(message) : null);
|
||||
}
|
||||
} 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 + ' States connected to redis: ' + settings.connection.host);
|
||||
} else {
|
||||
log.info(settings.namespace + ' States connected to redis: ' + settings.connection.host + ':' + settings.connection.port);
|
||||
}
|
||||
});
|
||||
|
||||
client.on('connect', error => {
|
||||
if (typeof settings.connected === 'function') settings.connected();
|
||||
});
|
||||
})();
|
||||
|
||||
return this;
|
||||
}
|
||||
|
||||
module.exports = StateRedis;
|
||||
1203
lib/tools.js
Normal file
1203
lib/tools.js
Normal file
File diff suppressed because it is too large
Load Diff
144
lib/uploadFiles.js
Normal file
144
lib/uploadFiles.js
Normal file
@@ -0,0 +1,144 @@
|
||||
/**
|
||||
*
|
||||
* upload.js
|
||||
*
|
||||
* bulk upload a folder as attachment into one CouchDB object
|
||||
*
|
||||
* 8'2014 hobbyquaker <hq@ccu.io>
|
||||
*
|
||||
*/
|
||||
'use strict';
|
||||
|
||||
var yargs = require('yargs')
|
||||
.alias('d', 'dir')
|
||||
.alias('o', 'object')
|
||||
.alias('p', 'prefix')
|
||||
.demand(['object'])
|
||||
.usage('$0 --object object._id [--dir directory] [--prefix prefix]\n' +
|
||||
'Example: \n' +
|
||||
' Upload the content of the folder "images" into the virtual folder "img" of the virtual filesystem "fs.www"\n' +
|
||||
' $0 -o fs.www -d images -p img')
|
||||
;
|
||||
|
||||
var fs = require('fs');
|
||||
var mime = require('mime');
|
||||
var Objects = require(__dirname + '/objects.js');
|
||||
var tools = require('./tools');
|
||||
|
||||
var files = [];
|
||||
var rev;
|
||||
var dir;
|
||||
var prefix;
|
||||
|
||||
var db = new Objects({
|
||||
logger: {
|
||||
silly: function (msg) { },
|
||||
debug: function (msg) { },
|
||||
info: function (msg) { },
|
||||
warn: function (msg) {
|
||||
console.log(msg);
|
||||
},
|
||||
error: function (msg) {
|
||||
console.log(msg);
|
||||
}
|
||||
},
|
||||
connected: function (type) {
|
||||
|
||||
var id = yargs.argv.object;
|
||||
|
||||
|
||||
dir = yargs.argv.dir || './';
|
||||
prefix = yargs.argv.prefix || '';
|
||||
db.getObject(yargs.argv.object, function (err, res) {
|
||||
if (err || !res) {
|
||||
db.setObject(yargs.argv.object, {
|
||||
type: 'fs',
|
||||
parent: 'fs',
|
||||
common: {
|
||||
name: yargs.argv.object.split('.').pop()
|
||||
},
|
||||
ts: new Date().getTime(),
|
||||
from: 'system.host.' + tools.getHostName() + '.cli',
|
||||
native: {}
|
||||
}, function (err, res) {
|
||||
rev = res.rev;
|
||||
main();
|
||||
});
|
||||
|
||||
} else {
|
||||
rev = res._rev;
|
||||
main();
|
||||
}
|
||||
|
||||
|
||||
});
|
||||
|
||||
}
|
||||
});
|
||||
|
||||
function main() {
|
||||
walk(dir, function (err, res) {
|
||||
if (err) {
|
||||
console.log(err);
|
||||
process.exit(1);
|
||||
}
|
||||
files = res;
|
||||
upload();
|
||||
});
|
||||
}
|
||||
|
||||
function upload() {
|
||||
var file;
|
||||
if (!files.length) {
|
||||
console.log('done. ' + yargs.argv.object + '._rev=' + rev);
|
||||
process.exit(0);
|
||||
} else {
|
||||
file = files.pop();
|
||||
var mimeType = mime.lookup(file);
|
||||
var attName = prefix + (file.split('/').slice(1).join('/'));
|
||||
console.log('upload', file, attName, mimeType);
|
||||
|
||||
|
||||
fs.createReadStream(file).pipe(
|
||||
db.insert(yargs.argv.object, attName, null, mimeType, {
|
||||
rev: rev
|
||||
}, function (err, res) {
|
||||
if (err) {
|
||||
console.log(err);
|
||||
process.exit(1);
|
||||
}
|
||||
rev = res.rev;
|
||||
setTimeout(function () {
|
||||
upload();
|
||||
}, 50);
|
||||
})
|
||||
);
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
function walk(dir, done) {
|
||||
var results = [];
|
||||
fs.readdir(dir, function (err, list) {
|
||||
if (err) return done(err);
|
||||
var i = 0;
|
||||
(function next() {
|
||||
var file = list[i++];
|
||||
if (!file) return done(null, results);
|
||||
file = dir + '/' + file;
|
||||
fs.stat(file, function (err, stat) {
|
||||
if (stat && stat.isDirectory()) {
|
||||
walk(file, function (err, res) {
|
||||
results = results.concat(res);
|
||||
next();
|
||||
});
|
||||
} else {
|
||||
results.push(file);
|
||||
next();
|
||||
}
|
||||
});
|
||||
})();
|
||||
});
|
||||
}
|
||||
|
||||
|
||||
53
lib/vis/states.js
Normal file
53
lib/vis/states.js
Normal file
@@ -0,0 +1,53 @@
|
||||
var getUsedObjectIDs = require(__dirname + '/../www/js/visUtils').getUsedObjectIDs;
|
||||
|
||||
function calcProject(objects, projects, instance, result, callback) {
|
||||
if (!projects || !projects.length) {
|
||||
callback(null, result || []);
|
||||
return;
|
||||
}
|
||||
result = result || [];
|
||||
var project = projects.shift();
|
||||
if (!project || !project.isDir) {
|
||||
setImmediate(calcProject, objects, projects, instance, result, callback);
|
||||
return;
|
||||
}
|
||||
|
||||
// calculate datapoints in one project
|
||||
objects.readFile('vis.' + instance, '/' + project.file + '/vis-views.json', function (err, data) {
|
||||
var json;
|
||||
try {
|
||||
json = JSON.parse(data);
|
||||
} catch (e) {
|
||||
console.error('Cannot parse "/' + project.file + '/vis-views.json');
|
||||
setImmediate(calcProject, objects, projects, instance, result, callback);
|
||||
return;
|
||||
}
|
||||
var dps = getUsedObjectIDs(json, false);
|
||||
if (dps && dps.IDs) {
|
||||
result.push({id: 'vis.' + instance + '.datapoints.' + project.file.replace(/[.\\s]/g, '_'), val: dps.IDs.length});
|
||||
}
|
||||
setImmediate(calcProject, objects, projects, instance, result, callback);
|
||||
});
|
||||
}
|
||||
|
||||
function calcProjects(objects, states, instance, config, callback) {
|
||||
objects.readDir('vis.' + instance, '/', function (err, projects) {
|
||||
if (err || !projects || !projects.length) {
|
||||
callback && callback(err || null, [{id: 'vis.' + instance + '.datapoints.total', val: 0}]);
|
||||
} else {
|
||||
calcProject(objects, projects, instance, [], function (err, result) {
|
||||
if (result && result.length) {
|
||||
var total = 0;
|
||||
for (var r = 0; r < result.length; r++) {
|
||||
total += result[r].val;
|
||||
}
|
||||
result.push({id: 'vis.' + instance + '.datapoints.total', val: total});
|
||||
}
|
||||
|
||||
callback && callback(err, result);
|
||||
});
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
module.exports = calcProjects;
|
||||
537
lib/www/js/visUtils.js
Normal file
537
lib/www/js/visUtils.js
Normal file
@@ -0,0 +1,537 @@
|
||||
function getWidgetGroup(views, view, widget) {
|
||||
var widgets = views[view].widgets;
|
||||
var members;
|
||||
for (var w in widgets) {
|
||||
if (!widgets.hasOwnProperty(w)) continue;
|
||||
members = views[view].widgets[w].data.members;
|
||||
if (members && members.indexOf(widget) !== -1) {
|
||||
return w;
|
||||
}
|
||||
}
|
||||
return null;
|
||||
}
|
||||
|
||||
function extractBinding(format) {
|
||||
var oid = format.match(/{(.+?)}/g);
|
||||
var result = null;
|
||||
if (oid) {
|
||||
if (oid.length > 50) {
|
||||
console.warn('Too many bindings in one widget: ' + oid.length + '[max = 50]');
|
||||
}
|
||||
for (var p = 0; p < oid.length && p < 50; p++) {
|
||||
var _oid = oid[p].substring(1, oid[p].length - 1);
|
||||
if (_oid[0] === '{') continue;
|
||||
// If first symbol '"' => it is JSON
|
||||
if (_oid && _oid[0] === '"') continue;
|
||||
var parts = _oid.split(';');
|
||||
result = result || [];
|
||||
var systemOid = parts[0].trim();
|
||||
var visOid = systemOid;
|
||||
|
||||
var test1 = visOid.substring(visOid.length - 4);
|
||||
var test2 = visOid.substring(visOid.length - 3);
|
||||
|
||||
if (visOid && test1 !== '.val' && test2 !== '.ts' && test2 !== '.lc' && test1 !== '.ack') {
|
||||
visOid = visOid + '.val';
|
||||
}
|
||||
|
||||
var isSeconds = (test2 === '.ts' || test2 === '.lc');
|
||||
|
||||
test1 = systemOid.substring(systemOid.length - 4);
|
||||
test2 = systemOid.substring(systemOid.length - 3);
|
||||
|
||||
if (test1 === '.val' || test1 === '.ack') {
|
||||
systemOid = systemOid.substring(0, systemOid.length - 4);
|
||||
} else if (test2 === '.lc' || test2 === '.ts') {
|
||||
systemOid = systemOid.substring(0, systemOid.length - 3);
|
||||
}
|
||||
var operations = null;
|
||||
var isEval = visOid.match(/[\d\w_.]+:[-\d\w_.]+/) || (!visOid.length && parts.length > 0);//(visOid.indexOf(':') !== -1) && (visOid.indexOf('::') === -1);
|
||||
|
||||
if (isEval) {
|
||||
var xx = visOid.split(':', 2);
|
||||
var yy = systemOid.split(':', 2);
|
||||
visOid = xx[1];
|
||||
systemOid = yy[1];
|
||||
operations = operations || [];
|
||||
operations.push({
|
||||
op: 'eval',
|
||||
arg: [{
|
||||
name: xx[0],
|
||||
visOid: visOid,
|
||||
systemOid: systemOid
|
||||
}]
|
||||
});
|
||||
}
|
||||
|
||||
for (var u = 1; u < parts.length; u++) {
|
||||
// eval construction
|
||||
if (isEval) {
|
||||
if (parts[u].trim().match(/^[\d\w_.]+:[-.\d\w_]+$/)) {//parts[u].indexOf(':') !== -1 && parts[u].indexOf('::') === -1) {
|
||||
var _systemOid = parts[u].trim();
|
||||
var _visOid = _systemOid;
|
||||
|
||||
test1 = _visOid.substring(_visOid.length - 4);
|
||||
test2 = _visOid.substring(_visOid.length - 3);
|
||||
|
||||
if (test1 !== '.val' && test2 !== '.ts' && test2 !== '.lc' && test1 !== '.ack') {
|
||||
_visOid = _visOid + '.val';
|
||||
}
|
||||
|
||||
test1 = systemOid.substring(_systemOid.length - 4);
|
||||
test2 = systemOid.substring(_systemOid.length - 3);
|
||||
|
||||
if (test1 === '.val' || test1 === '.ack') {
|
||||
_systemOid = _systemOid.substring(0, _systemOid.length - 4);
|
||||
} else if (test2 === '.lc' || test2 === '.ts') {
|
||||
_systemOid = _systemOid.substring(0, _systemOid.length - 3);
|
||||
}
|
||||
var x1 = _visOid.split(':', 2);
|
||||
var y1 = _systemOid.split(':', 2);
|
||||
|
||||
operations[0].arg.push({
|
||||
name: x1[0],
|
||||
visOid: x1[1],
|
||||
systemOid: y1[1]
|
||||
});
|
||||
} else {
|
||||
parts[u] = parts[u].replace(/::/g, ':');
|
||||
if (operations[0].formula) {
|
||||
var n = JSON.parse(JSON.stringify(operations[0]));
|
||||
n.formula = parts[u];
|
||||
operations.push(n);
|
||||
} else {
|
||||
operations[0].formula = parts[u];
|
||||
}
|
||||
}
|
||||
} else {
|
||||
var parse = parts[u].match(/([\w\s\/+*-]+)(\(.+\))?/);
|
||||
if (parse && parse[1]) {
|
||||
parse[1] = parse[1].trim();
|
||||
// operators requires parameter
|
||||
if (parse[1] === '*' ||
|
||||
parse[1] === '+' ||
|
||||
parse[1] === '-' ||
|
||||
parse[1] === '/' ||
|
||||
parse[1] === '%' ||
|
||||
parse[1] === 'min' ||
|
||||
parse[1] === 'max') {
|
||||
if (parse[2] === undefined) {
|
||||
console.log('Invalid format of format string: ' + format);
|
||||
parse[2] = null;
|
||||
} else {
|
||||
parse[2] = (parse[2] || '').trim().replace(',', '.');
|
||||
parse[2] = parse[2].substring(1, parse[2].length - 1);
|
||||
parse[2] = parseFloat(parse[2].trim());
|
||||
|
||||
if (parse[2].toString() === 'NaN') {
|
||||
console.log('Invalid format of format string: ' + format);
|
||||
parse[2] = null;
|
||||
} else {
|
||||
operations = operations || [];
|
||||
operations.push({op: parse[1], arg: parse[2]});
|
||||
}
|
||||
}
|
||||
} else
|
||||
// date formatting
|
||||
if (parse[1] === 'date') {
|
||||
operations = operations || [];
|
||||
parse[2] = (parse[2] || '').trim();
|
||||
parse[2] = parse[2].substring(1, parse[2].length - 1);
|
||||
operations.push({op: parse[1], arg: parse[2]});
|
||||
} else
|
||||
// returns array[value]. e.g.: {id.ack;array(ack is false,ack is true)}
|
||||
if (parse[1] === 'array') {
|
||||
operations = operations || [];
|
||||
param = (parse[2] || '').trim();
|
||||
param = param.substring(1, param.length - 1);
|
||||
param = param.split(',');
|
||||
if (Array.isArray(param)) {
|
||||
operations.push ({op: parse[1], arg: param}); //xxx
|
||||
}
|
||||
} else
|
||||
// value formatting
|
||||
if (parse[1] === 'value') {
|
||||
operations = operations || [];
|
||||
var param = (parse[2] === undefined) ? '(2)' : (parse[2] || '');
|
||||
param = param.trim();
|
||||
param = param.substring(1, param.length - 1);
|
||||
operations.push({op: parse[1], arg: param});
|
||||
} else
|
||||
// operators have optional parameter
|
||||
if (parse[1] === 'pow' || parse[1] === 'round' || parse[1] === 'random') {
|
||||
if (parse[2] === undefined) {
|
||||
operations = operations || [];
|
||||
operations.push({op: parse[1]});
|
||||
} else {
|
||||
parse[2] = (parse[2] || '').trim().replace(',', '.');
|
||||
parse[2] = parse[2].substring(1, parse[2].length - 1);
|
||||
parse[2] = parseFloat(parse[2].trim());
|
||||
|
||||
if (parse[2].toString() === 'NaN') {
|
||||
console.log('Invalid format of format string: ' + format);
|
||||
parse[2] = null;
|
||||
} else {
|
||||
operations = operations || [];
|
||||
operations.push({op: parse[1], arg: parse[2]});
|
||||
}
|
||||
}
|
||||
} else
|
||||
// operators without parameter
|
||||
{
|
||||
operations = operations || [];
|
||||
operations.push({op: parse[1]});
|
||||
}
|
||||
} else {
|
||||
console.log('Invalid format ' + format);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
result.push({
|
||||
visOid: visOid,
|
||||
systemOid: systemOid,
|
||||
token: oid[p],
|
||||
operations: operations ? operations : undefined,
|
||||
format: format,
|
||||
isSeconds: isSeconds
|
||||
});
|
||||
}
|
||||
}
|
||||
return result;
|
||||
}
|
||||
|
||||
function getUsedObjectIDs(views, isByViews) {
|
||||
if (!views) {
|
||||
console.log('Check why views are not yet loaded!');
|
||||
return null;
|
||||
}
|
||||
|
||||
var _views = isByViews ? {} : null;
|
||||
var IDs = [];
|
||||
var visibility = {};
|
||||
var bindings = {};
|
||||
var lastChanges = {};
|
||||
var signals = {};
|
||||
|
||||
var view;
|
||||
var id;
|
||||
var sidd;
|
||||
for (view in views) {
|
||||
if (!views.hasOwnProperty(view)) continue;
|
||||
|
||||
if (view === '___settings') continue;
|
||||
|
||||
if (_views) _views[view] = [];
|
||||
|
||||
for (id in views[view].widgets) {
|
||||
if (!views[view].widgets.hasOwnProperty(id)) continue;
|
||||
// Check all attributes
|
||||
var data = views[view].widgets[id].data;
|
||||
var style = views[view].widgets[id].style;
|
||||
|
||||
// fix error in naming
|
||||
if (views[view].widgets[id].groupped) {
|
||||
views[view].widgets[id].grouped = true;
|
||||
delete views[view].widgets[id].groupped;
|
||||
}
|
||||
|
||||
// rename hqWidgets => hqwidgets
|
||||
if (views[view].widgets[id].widgetSet === 'hqWidgets') {
|
||||
views[view].widgets[id].widgetSet = 'hqwidgets';
|
||||
}
|
||||
|
||||
// rename RGraph => rgraph
|
||||
if (views[view].widgets[id].widgetSet === 'RGraph') {
|
||||
views[view].widgets[id].widgetSet = 'rgraph';
|
||||
}
|
||||
|
||||
// rename timeAndWeather => timeandweather
|
||||
if (views[view].widgets[id].widgetSet === 'timeAndWeather') {
|
||||
views[view].widgets[id].widgetSet = 'timeandweather';
|
||||
}
|
||||
|
||||
// convert "Show on Value" to HTML
|
||||
if (views[view].widgets[id].tpl === 'tplShowValue') {
|
||||
views[view].widgets[id].tpl = 'tplHtml';
|
||||
views[view].widgets[id].data['visibility-oid'] = views[view].widgets[id].data.oid;
|
||||
views[view].widgets[id].data['visibility-val'] = views[view].widgets[id].data.value;
|
||||
delete views[view].widgets[id].data.oid;
|
||||
delete views[view].widgets[id].data.value;
|
||||
}
|
||||
|
||||
// convert "Hide on >0/True" to HTML
|
||||
if (views[view].widgets[id].tpl === 'tplHideTrue') {
|
||||
views[view].widgets[id].tpl = 'tplHtml';
|
||||
views[view].widgets[id].data['visibility-cond'] = '!=';
|
||||
views[view].widgets[id].data['visibility-oid'] = views[view].widgets[id].data.oid;
|
||||
views[view].widgets[id].data['visibility-val'] = true;
|
||||
delete views[view].widgets[id].data.oid;
|
||||
}
|
||||
|
||||
// convert "Hide on 0/False" to HTML
|
||||
if (views[view].widgets[id].tpl === 'tplHide') {
|
||||
views[view].widgets[id].tpl = 'tplHtml';
|
||||
views[view].widgets[id].data['visibility-cond'] = '!=';
|
||||
views[view].widgets[id].data['visibility-oid'] = views[view].widgets[id].data.oid;
|
||||
views[view].widgets[id].data['visibility-val'] = false;
|
||||
delete views[view].widgets[id].data.oid;
|
||||
}
|
||||
|
||||
// convert "Door/Window sensor" to HTML
|
||||
if (views[view].widgets[id].tpl === 'tplHmWindow') {
|
||||
views[view].widgets[id].tpl = 'tplValueBool';
|
||||
views[view].widgets[id].data.html_false = views[view].widgets[id].data.html_closed;
|
||||
views[view].widgets[id].data.html_true = views[view].widgets[id].data.html_open;
|
||||
delete views[view].widgets[id].data.html_closed;
|
||||
delete views[view].widgets[id].data.html_open;
|
||||
}
|
||||
|
||||
// convert "Door/Window sensor" to HTML
|
||||
if (views[view].widgets[id].tpl === 'tplHmWindowRotary') {
|
||||
views[view].widgets[id].tpl = 'tplValueListHtml8';
|
||||
views[view].widgets[id].data.count = 2;
|
||||
views[view].widgets[id].data.value0 = views[view].widgets[id].data.html_closed;
|
||||
views[view].widgets[id].data.value1 = views[view].widgets[id].data.html_open;
|
||||
views[view].widgets[id].data.value2 = views[view].widgets[id].data.html_tilt;
|
||||
delete views[view].widgets[id].data.html_closed;
|
||||
delete views[view].widgets[id].data.html_open;
|
||||
delete views[view].widgets[id].data.html_tilt;
|
||||
}
|
||||
|
||||
// convert "tplBulbOnOff" to tplBulbOnOffCtrl
|
||||
if (views[view].widgets[id].tpl === 'tplBulbOnOff') {
|
||||
views[view].widgets[id].tpl = 'tplBulbOnOffCtrl';
|
||||
views[view].widgets[id].data.readOnly = true;
|
||||
}
|
||||
|
||||
// convert "tplValueFloatBarVertical" to tplValueFloatBar
|
||||
if (views[view].widgets[id].tpl === 'tplValueFloatBarVertical') {
|
||||
views[view].widgets[id].tpl = 'tplValueFloatBar';
|
||||
views[view].widgets[id].data.orientation = 'vertical';
|
||||
}
|
||||
|
||||
for (var attr in data) {
|
||||
if (!data.hasOwnProperty(attr) || !attr) continue;
|
||||
/* TODO DO do not forget remove it after a while. Required for import from DashUI */
|
||||
if (attr === 'state_id') {
|
||||
data.state_oid = data[attr];
|
||||
delete data[attr];
|
||||
attr = 'state_oid';
|
||||
} else
|
||||
if (attr === 'number_id') {
|
||||
data.number_oid = data[attr];
|
||||
delete data[attr];
|
||||
attr = 'number_oid';
|
||||
} else
|
||||
if (attr === 'toggle_id') {
|
||||
data.toggle_oid = data[attr];
|
||||
delete data[attr];
|
||||
attr = 'toggle_oid';
|
||||
} else
|
||||
if (attr === 'set_id') {
|
||||
data.set_oid = data[attr];
|
||||
delete data[attr];
|
||||
attr = 'set_oid';
|
||||
} else
|
||||
if (attr === 'temp_id') {
|
||||
data.temp_oid = data[attr];
|
||||
delete data[attr];
|
||||
attr = 'temp_oid';
|
||||
} else
|
||||
if (attr === 'drive_id') {
|
||||
data.drive_oid = data[attr];
|
||||
delete data[attr];
|
||||
attr = 'drive_oid';
|
||||
} else
|
||||
if (attr === 'content_id') {
|
||||
data.content_oid = data[attr];
|
||||
delete data[attr];
|
||||
attr = 'content_oid';
|
||||
} else
|
||||
if (attr === 'dialog_id') {
|
||||
data.dialog_oid = data[attr];
|
||||
delete data[attr];
|
||||
attr = 'dialog_oid';
|
||||
} else
|
||||
if (attr === 'max_value_id') {
|
||||
data.max_value_oid = data[attr];
|
||||
delete data[attr];
|
||||
attr = 'max_value_oid';
|
||||
} else
|
||||
if (attr === 'dialog_id') {
|
||||
data.dialog_oid = data[attr];
|
||||
delete data[attr];
|
||||
attr = 'dialog_oid';
|
||||
} else
|
||||
if (attr === 'weoid') {
|
||||
data.woeid = data[attr];
|
||||
delete data[attr];
|
||||
attr = 'woeid';
|
||||
}
|
||||
|
||||
if (typeof data[attr] === 'string') {
|
||||
var m;
|
||||
var oids = extractBinding(data[attr]);
|
||||
if (oids) {
|
||||
for (var t = 0; t < oids.length; t++) {
|
||||
var ssid = oids[t].systemOid;
|
||||
if (ssid) {
|
||||
if (IDs.indexOf(ssid) === -1) IDs.push(ssid);
|
||||
if (_views && _views[view].indexOf(ssid) === -1) _views[view].push(ssid);
|
||||
if (!bindings[ssid]) bindings[ssid] = [];
|
||||
oids[t].type = 'data';
|
||||
oids[t].attr = attr;
|
||||
oids[t].view = view;
|
||||
oids[t].widget = id;
|
||||
|
||||
bindings[ssid].push(oids[t]);
|
||||
}
|
||||
|
||||
if (oids[t].operations && oids[t].operations[0].arg instanceof Array) {
|
||||
for (var ww = 0; ww < oids[t].operations[0].arg.length; ww++) {
|
||||
ssid = oids[t].operations[0].arg[ww].systemOid;
|
||||
if (!ssid) continue;
|
||||
if (IDs.indexOf(ssid) === -1) IDs.push(ssid);
|
||||
if (_views && _views[view].indexOf(ssid) === -1) _views[view].push(ssid);
|
||||
if (!bindings[ssid]) bindings[ssid] = [];
|
||||
bindings[ssid].push(oids[t]);
|
||||
}
|
||||
}
|
||||
}
|
||||
} else
|
||||
if (attr !== 'oidTrueValue' && attr !== 'oidFalseValue' && ((attr.match(/oid\d{0,2}$/) || attr.match(/^oid/) || attr.match(/^signals-oid-/) || attr === 'lc-oid') && data[attr])) {
|
||||
if (data[attr] && data[attr] !== 'nothing_selected') {
|
||||
if (IDs.indexOf(data[attr]) === -1) IDs.push(data[attr]);
|
||||
if (_views && _views[view].indexOf(data[attr]) === -1) _views[view].push(data[attr]);
|
||||
}
|
||||
|
||||
// Visibility binding
|
||||
if (attr === 'visibility-oid' && data['visibility-oid']) {
|
||||
var vid = data['visibility-oid'];
|
||||
if (vid.match(/^groupAttr(\d+)$/)) {
|
||||
var vgroup = getWidgetGroup(views, view, id);
|
||||
if (vgroup) vid = views[view].widgets[vgroup].data[vid];
|
||||
}
|
||||
|
||||
if (!visibility[vid]) visibility[vid] = [];
|
||||
visibility[vid].push({view: view, widget: id});
|
||||
}
|
||||
|
||||
// Signal binding
|
||||
if (attr.match(/^signals-oid-/) && data[attr]) {
|
||||
var sid = data[attr];
|
||||
if (sid.match(/^groupAttr(\d+)$/)) {
|
||||
var group = getWidgetGroup(views, view, id);
|
||||
if (group) sid = views[view].widgets[group].data[sid];
|
||||
}
|
||||
|
||||
if (!signals[sid]) signals[sid] = [];
|
||||
signals[sid].push({
|
||||
view: view,
|
||||
widget: id,
|
||||
index: parseInt(attr.substring('signals-oid-'.length), 10)
|
||||
});
|
||||
}
|
||||
if (attr === 'lc-oid') {
|
||||
var lcsid = data[attr];
|
||||
if (lcsid.match(/^groupAttr(\d+)$/)) {
|
||||
var ggroup = getWidgetGroup(views, view, id);
|
||||
if (ggroup) lcsid = views[view].widgets[ggroup].data[lcsid];
|
||||
}
|
||||
|
||||
if (!lastChanges[lcsid]) lastChanges[lcsid] = [];
|
||||
lastChanges[lcsid].push({
|
||||
view: view,
|
||||
widget: id
|
||||
});
|
||||
}
|
||||
} else
|
||||
if ((m = attr.match(/^attrType(\d+)$/)) && data[attr] === 'id') {
|
||||
var _id = 'groupAttr' + m[1];
|
||||
if (data[_id]) {
|
||||
if (IDs.indexOf(data[_id]) === -1) IDs.push(data[_id]);
|
||||
if (_views && _views[view].indexOf(data[_id]) === -1) _views[view].push(data[_id]);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// build bindings for styles
|
||||
if (style) {
|
||||
for (var cssAttr in style) {
|
||||
if (!style.hasOwnProperty(cssAttr) || !cssAttr) continue;
|
||||
if (typeof style[cssAttr] === 'string') {
|
||||
var objIDs = extractBinding(style[cssAttr]);
|
||||
if (objIDs) {
|
||||
for (var tt = 0; tt < objIDs.length; tt++) {
|
||||
sidd = objIDs[tt].systemOid;
|
||||
if (sidd) {
|
||||
if (IDs.indexOf(sidd) === -1) IDs.push(sidd);
|
||||
if (_views && _views[view].indexOf(sidd) === -1) _views[view].push(sidd);
|
||||
if (!bindings[sidd]) bindings[sidd] = [];
|
||||
|
||||
objIDs[tt].type = 'style';
|
||||
objIDs[tt].attr = cssAttr;
|
||||
objIDs[tt].view = view;
|
||||
objIDs[tt].widget = id;
|
||||
|
||||
bindings[sidd].push(objIDs[tt]);
|
||||
}
|
||||
|
||||
if (objIDs[tt].operations && objIDs[tt].operations[0].arg instanceof Array) {
|
||||
for (var w = 0; w < objIDs[tt].operations[0].arg.length; w++) {
|
||||
sidd = objIDs[tt].operations[0].arg[w].systemOid;
|
||||
if (!sidd) continue;
|
||||
if (IDs.indexOf(sidd) === -1) IDs.push(sidd);
|
||||
if (_views && _views[view].indexOf(sidd) === -1) _views[view].push(sidd);
|
||||
if (!bindings[sidd]) bindings[sidd] = [];
|
||||
bindings[sidd].push(objIDs[tt]);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
if (_views) {
|
||||
var changed;
|
||||
do {
|
||||
changed = false;
|
||||
// Check containers
|
||||
for (view in views) {
|
||||
if (!views.hasOwnProperty(view)) continue;
|
||||
|
||||
if (view === '___settings') continue;
|
||||
|
||||
for (id in views[view].widgets) {
|
||||
if (!views[view].widgets.hasOwnProperty(id)) continue;
|
||||
|
||||
// Add all OIDs from this view to parent
|
||||
if (views[view].widgets[id].tpl === 'tplContainerView' && views[view].widgets[id].data.contains_view) {
|
||||
var ids = _views[views[view].widgets[id].data.contains_view];
|
||||
if (ids) {
|
||||
for (var a = 0; a < ids.length; a++) {
|
||||
if (ids[a] && _views[view].indexOf(ids[a]) === -1) {
|
||||
_views[view].push(ids[a]);
|
||||
changed = true;
|
||||
}
|
||||
}
|
||||
} else {
|
||||
console.warn('View does not exist: "' + views[view].widgets[id].data.contains_view + '"');
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
} while (changed);
|
||||
}
|
||||
|
||||
return {IDs: IDs, byViews: _views, visibility: visibility, bindings: bindings, lastChanges: lastChanges, signals: signals};
|
||||
}
|
||||
|
||||
if (module && module.parent) {
|
||||
module.exports.getUsedObjectIDs = getUsedObjectIDs;
|
||||
}
|
||||
366
lib/zipFiles.js
Normal file
366
lib/zipFiles.js
Normal file
@@ -0,0 +1,366 @@
|
||||
'use strict';
|
||||
|
||||
var JSZip;
|
||||
var tools = require(__dirname + '/tools');
|
||||
|
||||
function _getAllFilesInDir(objects, id, name, options, callback, result) {
|
||||
result = result || [];
|
||||
objects.readDir(id, name, options, function (err, files) {
|
||||
var count = 0;
|
||||
var errors = [];
|
||||
if (files) {
|
||||
for (var f = 0; f < files.length; f++) {
|
||||
if (files[f].isDir) {
|
||||
count++;
|
||||
_getAllFilesInDir(objects, id, name + '/' + files[f].file, options, function (err, _result) {
|
||||
if (err) errors.push(err);
|
||||
if (!--count) callback(errors.length ? errors : null, _result);
|
||||
}, result);
|
||||
} else {
|
||||
result.push(name + '/' + files[f].file);
|
||||
}
|
||||
}
|
||||
}
|
||||
if (!count) callback(null, result);
|
||||
});
|
||||
}
|
||||
|
||||
function _addFile(objects, id, name, options, zip, files, callback) {
|
||||
objects.readFile(id, name, options, function (err, data, mime) {
|
||||
if (!zip) {
|
||||
console.log(err);
|
||||
callback('Cannot read file "' + name + '": ' + err, files);
|
||||
} else {
|
||||
// if handler installed
|
||||
if (options.stringify) {
|
||||
try {
|
||||
data = options.stringify(name, data, options ? options.settings : null, files);
|
||||
} catch (error) {
|
||||
console.error('Cannot stringify file "' + name + '": ' + error);
|
||||
if (!err) err = 'Cannot stringify file "' + name + '": ' + error;
|
||||
}
|
||||
}
|
||||
var parts = name.split('/');
|
||||
if (parts.length > 1) {
|
||||
parts.shift();
|
||||
name = parts.join('/');
|
||||
}
|
||||
|
||||
zip.file(name, data);
|
||||
setImmediate(function () {
|
||||
callback(err, files);
|
||||
});
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
// pack all files as zip and send it back
|
||||
function readDirAsZip(objects, id, name, options, callback) {
|
||||
if (typeof options === 'function') {
|
||||
callback = options;
|
||||
options = null;
|
||||
}
|
||||
if (name[0] === '/') name = name.substring(1);
|
||||
options = options || {};
|
||||
var adapter = id;
|
||||
if (adapter.indexOf('.') !== -1) {
|
||||
adapter = id.split('.')[0];
|
||||
}
|
||||
|
||||
// try to load processor of adapter
|
||||
try {
|
||||
options.stringify = require(tools.appName + '.' + adapter + '/lib/convert.js').stringify;
|
||||
} catch (e) {
|
||||
|
||||
}
|
||||
|
||||
_getAllFilesInDir(objects, id, name, options, function (err, files) {
|
||||
var count = 0;
|
||||
if (files) {
|
||||
JSZip = JSZip || require('jszip');
|
||||
var zip = new JSZip();
|
||||
var additional = [];
|
||||
for (var f = 0; f < files.length; f++) {
|
||||
count++;
|
||||
_addFile(objects, id, files[f], options, zip, additional, function (err, additional) {
|
||||
if (!--count) {
|
||||
if (additional && additional.length) {
|
||||
for (var ff = 0; ff < additional.length; ff++) {
|
||||
if (!additional[ff] || typeof additional[ff] !== 'string') continue;
|
||||
count++;
|
||||
|
||||
if (additional[ff][0] === '/') additional[ff] = additional[ff].substring(1);
|
||||
var parts = additional[ff].split('/');
|
||||
var adapter = parts.shift();
|
||||
|
||||
_addFile(objects, adapter, parts.join('/'), options, zip, null, function (err) {
|
||||
if (!--count) {
|
||||
zip.generateAsync({type: 'base64'})
|
||||
.then(function (base64) {
|
||||
callback(err, base64);
|
||||
}, function (err) {
|
||||
callback(err);
|
||||
});
|
||||
zip = null;
|
||||
}
|
||||
});
|
||||
}
|
||||
if (!count) {
|
||||
zip.generateAsync({type: 'base64'})
|
||||
.then(function (base64) {
|
||||
callback(err, base64);
|
||||
}, function (err) {
|
||||
callback(err);
|
||||
});
|
||||
zip = null;
|
||||
}
|
||||
} else {
|
||||
zip.generateAsync({type: 'base64'})
|
||||
.then(function (base64) {
|
||||
callback(err, base64);
|
||||
}, function (err) {
|
||||
callback(err);
|
||||
});
|
||||
zip = null;
|
||||
}
|
||||
}
|
||||
});
|
||||
}
|
||||
}
|
||||
if (!count) {
|
||||
callback(err, null);
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
function _checkDir(objects, id, root, parts, options, callback) {
|
||||
if (!parts || !parts.length) {
|
||||
callback();
|
||||
return;
|
||||
}
|
||||
root += '/' + parts.shift();
|
||||
objects.readDir(id, root, options, function (err, files) {
|
||||
if (err === 'Not exists') {
|
||||
objects.mkdir(id, root, options, function (err) {
|
||||
_checkDir(objects, id, root, parts, options, callback);
|
||||
});
|
||||
} else {
|
||||
_checkDir(objects, id, root, parts, options, callback);
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
function _writeOneFile(objects, zip, id, name, filename, options, callback) {
|
||||
zip.files[filename].async('nodebuffer').then(function (data) {
|
||||
var _err;
|
||||
if (options.parse) {
|
||||
try {
|
||||
data = options.parse(name, filename, data, options ? options.settings : null);
|
||||
} catch (e) {
|
||||
_err = e;
|
||||
}
|
||||
}
|
||||
var fName = name + filename;
|
||||
var parts = fName.split('/');
|
||||
parts.pop();
|
||||
_checkDir(objects, id, '', parts, options, function () {
|
||||
objects.writeFile(id, name + filename, data, options, function (err) {
|
||||
callback(_err || err);
|
||||
});
|
||||
});
|
||||
}, function (err) {
|
||||
callback(err);
|
||||
});
|
||||
}
|
||||
|
||||
function writeDirAsZip(objects, id, name, data, options, callback) {
|
||||
JSZip = JSZip || require('jszip');
|
||||
var zip = new JSZip();
|
||||
|
||||
options = options || {};
|
||||
|
||||
var adapter = id;
|
||||
if (adapter.indexOf('.') !== -1) {
|
||||
adapter = id.split('.')[0];
|
||||
}
|
||||
|
||||
// try to load processor of adapter
|
||||
try {
|
||||
options.parse = require(tools.appName + '.' + adapter + '/lib/convert.js').parse;
|
||||
} catch (e) {
|
||||
|
||||
}
|
||||
|
||||
try {
|
||||
zip.loadAsync(data).then(function () {
|
||||
var count = 0;
|
||||
var error = [];
|
||||
if (name[name.length - 1] !== '/') name += '/';
|
||||
for (var filename in zip.files) {
|
||||
if (!filename || filename[filename.length - 1] === '/') continue;
|
||||
count++;
|
||||
try {
|
||||
_writeOneFile(objects, zip, id, name, filename, options, function (err) {
|
||||
if (err) {
|
||||
error.push('Cannot write file "' + filename + '":' + err.toString());
|
||||
}
|
||||
if (!--count && callback) {
|
||||
callback(error.length ? error.join(', ') : null);
|
||||
callback = null;
|
||||
}
|
||||
});
|
||||
} catch (error) {
|
||||
if (callback) {
|
||||
callback(error.toString());
|
||||
callback = null;
|
||||
}
|
||||
}
|
||||
}
|
||||
}, function (error) {
|
||||
if (callback) {
|
||||
callback(error.toString());
|
||||
callback = null;
|
||||
}
|
||||
});
|
||||
} catch (error) {
|
||||
if (callback) {
|
||||
callback(error.toString());
|
||||
callback = null;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// pack all files as zip and send it back
|
||||
function readObjectsAsZip(objects, rootId, adapter, options, callback) {
|
||||
if (typeof options === 'function') {
|
||||
callback = options;
|
||||
options = null;
|
||||
}
|
||||
options = options || {};
|
||||
|
||||
if (adapter) {
|
||||
// try to load processor of adapter
|
||||
try {
|
||||
options.stringify = require(tools.appName + '.' + adapter + '/lib/convert.js').stringify;
|
||||
} catch (e) {
|
||||
|
||||
}
|
||||
}
|
||||
|
||||
objects.getConfigKeys(rootId + '.*', options, function (err, keys) {
|
||||
objects.getObjects(keys, options, function(err, objs) {
|
||||
JSZip = JSZip || require('jszip');
|
||||
var zip = new JSZip();
|
||||
for (var f = 0; f < objs.length; f++) {
|
||||
var data = {id: keys[f], data: objs[f]};
|
||||
|
||||
if (options.stringify) {
|
||||
try {
|
||||
data = options.stringify(data, options ? options.settings : null);
|
||||
} catch (e) {
|
||||
data.id = keys[f].replace(/\./g, '/').substring(rootId.length + 1) + '.json';
|
||||
}
|
||||
} else {
|
||||
data.id = keys[f].replace(/\./g, '/').substring(rootId.length + 1) + '.json';
|
||||
}
|
||||
if (typeof data.data === 'object') data.data = JSON.stringify(data.data, null, 2);
|
||||
|
||||
zip.file(data.id, data.data);
|
||||
}
|
||||
zip.generateAsync({type: 'base64'})
|
||||
.then(function (base64) {
|
||||
callback(err, base64);
|
||||
}, function (err) {
|
||||
callback(err);
|
||||
});
|
||||
});
|
||||
});
|
||||
}
|
||||
|
||||
function _writeOneObject(objects, zip, rootId, filename, options, callback) {
|
||||
zip.files[filename].async('nodebuffer').then(function (data) {
|
||||
data = {data: data.toString(), id: filename};
|
||||
if (options.parse){
|
||||
try {
|
||||
data = options.parse(data, options ? options.settings : null);
|
||||
} catch (e) {
|
||||
callback('Cannot custom parse "' + data.id + '": ' + e);
|
||||
return;
|
||||
}
|
||||
} else {
|
||||
data.id = (rootId ? (rootId + '.') : '') + data.id.replace(/\//g, '.').replace(/\.json$/, '');
|
||||
}
|
||||
if (typeof data.data !== 'object') {
|
||||
try {
|
||||
data.data = JSON.parse(data.data);
|
||||
} catch (e) {
|
||||
callback('Cannot parse "' + data.id + '": ' + e);
|
||||
return;
|
||||
}
|
||||
}
|
||||
if (data && data.id && data.data) {
|
||||
options.ts = new Date().getTime();
|
||||
options.from = 'system.host.' + tools.getHostName() + '.cli';
|
||||
objects.setObject(data.id, data.data, options, function (err) {
|
||||
callback(err);
|
||||
});
|
||||
} else {
|
||||
if (data && data.error) {
|
||||
callback(data.error);
|
||||
} else {
|
||||
callback();
|
||||
}
|
||||
}
|
||||
}, function (err) {
|
||||
callback('Cannot parse unzip: ' + err);
|
||||
});
|
||||
}
|
||||
|
||||
function writeObjectsAsZip(objects, rootId, adapter, data, options, callback) {
|
||||
JSZip = JSZip || require('jszip');
|
||||
|
||||
options = options || {};
|
||||
|
||||
if (adapter) {
|
||||
// try to load processor of adapter
|
||||
try {
|
||||
options.parse = require(tools.appName + '.' + adapter + '/lib/convert.js').parse;
|
||||
} catch (e) {
|
||||
|
||||
}
|
||||
}
|
||||
|
||||
var zip = new JSZip();
|
||||
try {
|
||||
zip.loadAsync(data).then(function () {
|
||||
var count = 0;
|
||||
var error = [];
|
||||
for (var filename in zip.files) {
|
||||
if (filename[filename.length - 1] === '/') continue;
|
||||
count++;
|
||||
_writeOneObject(objects, zip, rootId, filename, options, function (err) {
|
||||
if (err) error.push(err.toString());
|
||||
if (!--count && callback) {
|
||||
callback(error.length ? error.join(', ') : null);
|
||||
callback = null;
|
||||
}
|
||||
});
|
||||
}
|
||||
}, function (error) {
|
||||
if (callback) {
|
||||
callback(error.toString());
|
||||
callback = null;
|
||||
}
|
||||
});
|
||||
} catch (error) {
|
||||
if (callback) {
|
||||
callback(error.toString());
|
||||
callback = null;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
module.exports.readDirAsZip = readDirAsZip;
|
||||
module.exports.writeDirAsZip = writeDirAsZip;
|
||||
module.exports.readObjectsAsZip = readObjectsAsZip;
|
||||
module.exports.writeObjectsAsZip = writeObjectsAsZip;
|
||||
Reference in New Issue
Block a user