Initial commit

This commit is contained in:
2018-09-17 20:32:19 +08:00
commit 3a9804f020
58 changed files with 31566 additions and 0 deletions

602
lib/setup/setupBackup.js Normal file
View 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

File diff suppressed because it is too large Load Diff

76
lib/setup/setupLicense.js Normal file
View 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
View 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
View 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
View 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
View 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
View 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
View 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
View 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
View 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
View 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;