iobroker.js-controller
Version:
Updated by reinstall.js on 2018-06-11T15:19:56.688Z
1,203 lines (1,106 loc) • 127 kB
JavaScript
/* eslint-disable no-inner-declarations */
/**
*
* ioBroker Command Line Interface (CLI)
*
* 7'2014-2020 bluefox <dogafox@gmail.com>
* 2014 hobbyquaker <hq@ccu.io>
*
*/
/* jshint -W097 */
/* jshint strict:false */
/* jslint node: true */
'use strict';
// TODO need info about progress of stopping
const fs = require('fs-extra');
const pathLib = require('path');
const tools = require('./tools.js');
const cli = require('./cli/index.js');
const EXIT_CODES = require('./exitCodes');
const {enumHosts} = require('./cli/cliTools');
const deepClone = require('deep-clone');
const { isDeepStrictEqual } = require('util');
const debug = require('debug')('iobroker:cli');
// @ts-ignore
require('events').EventEmitter.prototype._maxListeners = 100;
process.setMaxListeners(0);
/** @type {import('yargs')} */
let yargs;
function initYargs() {
yargs = require('yargs')
.scriptName(tools.appName)
.locale('en')// otherwise it could be mixed, because our implementations are in english
.version(false) // disable yargs own version handling, because we have our own depending on passed instances
.completion('_createCompletion', false) // can be created via iob _createCompletion >> ~/.bashrc or ~/.bash_profile for OSX
.command('setup', 'Setup ioBroker', {
redis: {
describe: 'Setup as redis',
type: 'boolean'
},
objects: {
describe: 'Objects <host>',
default: '127.0.0.1',
type: 'number'
},
states: {
describe: 'States <host>',
default: '127.0.0.1',
type: 'number'
},
'port <port>': {
describe: 'Port of redis',
default: 6379,
type: 'number'
},
custom: {
describe: 'Custom setup',
type: 'boolean'
},
first: {
describe: 'Initial setup',
type: 'boolean'
}
})
.command('start', 'Starts the js-controller', yargs => {
yargs
.command('all', 'Starts js-controller and all adapters')
.command('<adapter>[.<instance>]', 'Starts a specified adapter instance');
})
.command('stop', 'stops the js-controller', yargs => {
yargs
.command('<adapter>[.<instance>]', 'Stops a specified adapter instance');
})
.command('restart', 'Restarts js-controller', yargs => {
yargs
.command('<adapter>[.<instance>]', 'Restarts a specified adapter instance', {});
})
.command('debug <adapter>[.<instance>]', 'Starts a Node.js debugging session for the adapter instance', {
ip: {
describe: 'IP-address <ip>',
type: 'string'
},
port: {
describe: 'Port <port>',
type: 'number'
},
wait: {
describe: 'Wait',
type: 'boolean'
}
})
.command('info', 'Shows the host info', {})
.command('logs [<adapter>]', 'Monitor log', {
'lines=1000': { // TODO: it's the only place we use = we should avoid this
describe: 'Number of lines',
type: 'string'
},
watch: {
describe: 'Watch',
type: 'boolean'
}
})
.command('add <adapter> [desiredNumber]', 'Add instance of adapter', {
enabled: {
describe: 'Enable adapter',
type: 'boolean'
},
host: {
describe: 'Host <host>',
type: 'string'
},
port: {
describe: 'Port <port>',
type: 'number'
}
})
.command('install <adapter>', 'Installs a specified adapter', {})
.command('rebuild <adapter>|self', 'Rebuilds a specified adapter', {
install: {
describe: 'Install',
type: 'boolean'
}
})
.command('url <url> [<name>]', 'Install adapter from specified url, e.g. GitHub', {})
.command('del <adapter>', 'Remove adapter from system', {})
.command('del <adapter>.<instance>', 'Remove adapter instance', {})
.command('update [<repositoryUrl>]', 'Update repository and list adapters', {
updateable: {
describe: 'Only show updateable adapters',
alias: 'u',
type: 'boolean'
},
all: {
describe: 'Show all available adapters',
alias: 'a',
type: 'boolean'
},
force: {
describe: 'Bypass hash check',
alias: 'f',
type: 'boolean'
}
})
.command('upgrade', 'Upgrade management', yargs => {
yargs
.option('yes', {
describe: 'Bypass questionnaire',
alias: 'y',
type: 'boolean'
})
.command('[<repositoryUrl>]', 'Upgrade all adapters, optionally you can specify the repository url', {})
.command('all [<repositoryUrl>]', 'Upgrade all adapters, optionally you can specify the repository url', {})
.command('self [<repositoryUrl>]', 'Upgrade js-controller, optionally you can specify the repository url', {})
.command('<adapter> [<repositoryUrl>]', 'Upgrade specified adapter, optionally you can specify the repository url', {});
})
.command('upload', 'Upload management', yargs => {
yargs
.command(`<pathToLocalFile> <pathIn${tools.appName}>`, 'Upload given files to provided path to make them available for instances', {})
.command('all', 'Upload all adapter files to make them available for instances', {})
.command('<adapter>', 'Upload specified adapter files to make them available for instances', {});
})
.command('object', 'Object management', yargs => {
yargs
.command('get <id>', 'Get object specified by id', {})
.command('set <id> <json-value>', 'Set object with the given id by providing a new json object', {})
.command('set <id> propertyname=<value or json-value>', 'Update part of the object by providing a new value or partial object', {})
.command('extend <id> <json-value>', 'Extend object with the given id by providing a new json object', {})
.command('del <id|pattern>', 'Delete object with given id or all objects matching the pattern', {
y: {
describe: 'Bypass questionnaire',
alias: 'y',
type: 'boolean'
}
})
.command('chmod <object-mode> [state-mode] <id>', 'Change object rights', {})
.command('chown <user> <group> <id>', 'Change object ownership', {})
.command('list <pattern>', 'List object matching given pattern', {});
})
.command('state', 'State management', yargs => {
yargs
.command('get <id>', 'Get state, specified by id', {})
.command('getplain <id>', 'Get plain state, specified by id', {
pretty: {
describe: 'Prettify output',
type: 'boolean'
}
})
.command('getvalue <id>', 'Get state value, specified by id', {})
.command('set <id> <value> [<ack>]', 'Set state, specified by id', {})
.command('del <id>', 'Delete state, specified by id', {});
})
.command('message <adapter>[.instance] <command> [<message>]', 'Send message to adapter instance/s', {})
.command('list <type> [<filter>]', 'List all entries, like objects', {})
.command('chmod <mode> <file>', 'Change file rights', {})
.command('chown <user> <group> <file>', 'Change file ownership', {})
.command('touch <file>', 'Touch file', {})
.command('rm <file>', 'Remove file', {})
.command('file', 'File management', yargs => {
yargs
.command(`read <${tools.appName}-path-to-read> [<filesystem-path-to-write>]`, `Read file from ${tools.appName} path and optionally write to destination`, {})
.command(`write <filesystem-path-to-read> <${tools.appName}-path-to-write>`, `Read file from path and write it to ${tools.appName} path`,{})
.command(`rm <${tools.appName}-path-to-delete>`, 'Remove file', {})
.command('sync', 'Sync files', {});
})
.command('user', 'User commands', yargs => {
yargs
.command('add <user>', 'Add new user',yargs => {
yargs.option('ingroup', {
describe: 'User group',
type: 'string'
})
.option('password', {
describe: 'User password',
type: 'string'
});
})
.command('del <user>', 'Delete user', {})
.command('passwd <user>', 'Change user password', yargs => {
yargs
.option('password', {
describe: 'User password',
type: 'string'
});
})
.command('enable <user>', 'Enable user', {})
.command('disable <user>', 'Disable user', {})
.command('get <user>', 'Get user', {})
.command('check <user>', 'Check user password', yargs => {
yargs
.option('password', {
describe: 'User password',
type: 'string'
});
});
})
.command('group', 'group management', yargs => {
yargs
.command('add <group>', 'Add group', {})
.command('del <group>', 'Remove group', {})
.command('list <group>', 'List group', {})
.command('enable <group>', 'Enable group', {})
.command('disable <group>', 'Disable group', {})
.command('get <group>', 'Get group', {})
.command('adduser <group> <user>', 'Add user to group', {})
.command('deluser <group> <user>', 'Remove user from group', {});
})
.command('host <hostname>', 'Set host to given hostname', yargs => {
yargs
.command('this', 'Initialize current host', {})
.command('set <hostname>', 'Set host with specified hostname', {})
.command('remove <hostname>', 'Remove host with specified hostname', {});
})
.command('set <adapter>.<instance>', 'Change settings of adapter config', {
customOption: {
describe: 'Set the name of the parameter you want to change as option followed by its value, e. g. --port 80'
}
})
.command('license <license.file or license.text>', 'Update license by given file', {})
.command('cert', 'Certificate management', yargs => {
yargs
.command('create', 'Create certificate', {})
.command('view [<certificate name>]', 'Show certificate', {});
})
.command('clean <yes>', 'Clears all objects and states', {})
.command('backup', 'Create backup', {})
.command('restore <backup name or path>', 'Restore a specified backup', {})
.command('validate <backup name or path>', 'Validate a specified backup', {})
.command('status [all|<adapter>.<instance>]', 'Status of ioBroker or adapter instance', {})
.command('repo [<name>]', 'Show repo information', yargs => {
yargs
.command('set <name>', 'Set active repository')
.command('del <name>', 'Remove repository')
.command('add <name> <url>', 'Add repository')
.command('addset <name> <url>', 'Add repository and set it as active one')
.command('show', 'List repositories');
})
.command('uuid', 'Show uuid of the installation', {})
.command('unsetup', 'Reset license, installation secret and language', {})
.command('fix', 'Execute the installation fixer script, this updates your ioBroker installation', {})
.command('multihost', 'Multihost management', yargs => {
yargs
.command('enable', 'Enable multihost discovery', {
secure: {
describe: 'Use secure connection',
type: 'boolean'
},
persist: {
describe: 'Enable persistent discovery',
type: 'boolean'
}
})
.command('disable', 'Disable multihost discovery')
.command('browse', 'Browse for multihost server')
.command('connect', 'Connect to multihost server');
})
.command('compact', 'compact group management', yargs => {
yargs
.command('enable', 'Enable compact mode in general')
.command('on', 'Enable compact mode in general')
.command('disable', 'Disable compact mode in general')
.command('off', 'Disable compact mode in general')
.command('<adapter>.<instance> status', 'Show if compact mode is enabled for a specific instance')
.command('<adapter>.<instance> group <group-id>', 'Define compact group of a specific adapter')
.command('<adapter>.<instance> <disable|off> [group-id]', 'Enable or disable compact mode for specified adapter instance and set compact group optionally')
.command('<adapter>.<instance> <enable|on> [group-id]', 'Enable or disable compact mode for specified adapter instance and set compact group optionally');
})
.command('plugin', 'Plugin management', yargs => {
yargs
.command('enable <pluginname>', 'Enables a plugin for the specified host or instance. If no host is specified, the current one is used', {
host: {
describe: 'Hostname',
type: 'string'
},
instance: {
describe: 'Instance, e.g. hm-rpc.0',
type: 'string'
}
})
.command('disable <pluginname>', 'Disables a plugin for the specified host or instance. If no host is specified, the current one is used', {
host: {
describe: 'Hostname',
type: 'string'
},
instance: {
describe: 'Instance, e.g. hm-rpc.0',
type: 'string'
}
})
.command('status <pluginname>', 'Checks if a plugin is enabled for the specified host or instance. If no host is specified, the current one is used', {
host: {
describe: 'Hostname',
type: 'string'
},
instance: {
describe: 'Instance, e.g. hm-rpc.0',
type: 'string'
}
});
})
.command('version [<adapter>]', 'Show version of js-controller or specified adapter')
.option('version', {
describe: 'Show version',
type: 'boolean',
alias: 'v'
})
.wrap(null)
;
return yargs;
}
/**
* Show yargs help, if processCommand is used as import, yargs won't be initialized
* @param {object} _yargs - yargs instance
*/
function showHelp(_yargs) {
if (_yargs) {
_yargs.showHelp();
} else if (yargs) {
yargs.showHelp();
}
}
let Objects; // constructor
let objects; // instance
let States; // constructor
let states; // instance
/**
* Process the given CLI command
*
* @param {string|number} command - command to execute
* @param {any[]} args - arguments passed to yargs
* @param {object} params - object with parsed params by yargs, e. g. --force is params.force
* @param {(exitCode?: number) => void} callback
*/
function processCommand(command, args, params, callback) {
if (typeof args === 'function') {
callback = args;
args = null;
}
if (typeof params === 'function') {
callback = params;
params = null;
}
if (!params) {
params = {};
}
if (!args) {
args = [];
}
if (!callback) {
callback = processExit;
}
/** @type {import('./cli/cliCommand').CLICommandContext} */
const commandContext = {dbConnect, callback, showHelp};
/** @type {import('./cli/cliCommand').CLICommandOptions} */
const commandOptions = Object.assign({}, params, commandContext);
debug(`commandOptions: ${JSON.stringify(commandOptions)}`);
debug(`args: ${args}`);
switch (command) {
case 'start':
case 'stop': {
const procCommand = new cli.command.process(commandOptions);
procCommand[command](args);
break;
}
case 'debug': {
const debugCommand = new cli.command.debug(commandOptions);
debugCommand.execute(args);
break;
}
case 'status':
case 'isrun': {
const procCommand = new cli.command.process(commandOptions);
procCommand.status(args);
break;
}
case 'r':
case 'restart': {
const procCommand = new cli.command.process(commandOptions);
procCommand.restart(args);
break;
}
case '_restart':
restartController(callback);
break;
case 'update': {
Objects = require('./objects');
const repoUrl = args[0]; // Repo url or name
dbConnect(params, (_objects, _states) => {
const Repo = require('./setup/setupRepo.js');
const repo = new Repo({
objects: _objects,
states: _states
});
repo.showRepo(repoUrl, params, () => {
setTimeout(callback, 2000);
});
});
break;
}
case 'setup': {
const Setup = require('./setup/setupSetup.js');
const setup = new Setup({
dbConnect,
processExit: callback,
cleanDatabase,
restartController,
resetDbConnect,
params
});
if (args[0] === 'custom' || params.custom) {
setup.setupCustom(callback);
} else {
let isFirst;
let isRedis;
// we support "first" and "redis" without "--" flag
for (const arg of args) {
if (arg === 'first') {
isFirst = true;
} else if (arg === 'redis') {
isRedis = true;
}
}
// and as --flag
isRedis = params.redis || isRedis;
isFirst = params.first || isFirst;
setup.setup(async (isFirst, _isRedis) => {
if (isFirst) {
// Creates all instances that are needed on a fresh installation
const createInitialInstances = async () => {
const Install = require('./setup/setupInstall.js');
const install = new Install({
objects,
states,
installNpm,
getRepository,
processExit: callback,
params
});
// In order to loop the instance creation, we need a promisified version of the method
const createInstanceAsync = tools.promisifyNoError(install.createInstance, install);
// Define the necessary instances
const initialInstances = ['admin', 'discovery', 'backitup'];
// And try to install each of them
for (const instance of initialInstances) {
try {
const path = require.resolve(tools.appName + '.' + instance);
if (path) {
await createInstanceAsync(instance, {enabled: true, ignoreIfExists: true});
}
} catch {
// not found, just continue
}
}
};
createInitialInstances()
.then(() => new Promise(resolve => {
// Creates a fresh certificate
const Cert = require('./cli/cliCert');
// Create a new instance of the cert command,
// but use the resolve method as a callback
const cert = new Cert(Object.assign({}, commandOptions, {callback: resolve}));
cert.create();
}))
.then(() => callback && callback());
} else {
// else we update existing stuff (this is executed on installation)
// Rename repositories
const Repo = require('./setup/setupRepo.js');
const repo = new Repo({
objects: objects,
states: states
});
try {
await repo.rename('default', 'stable', 'http://download.iobroker.net/sources-dist.json');
await repo.rename('latest', 'beta', 'http://download.iobroker.net/sources-dist-latest.json');
} catch (e) {
console.warn(e.message);
}
try {
const configFile = tools.getConfigFileName();
const configOrig = fs.readJSONSync(configFile);
const config = deepClone(configOrig);
config.objects.options = config.objects.options || {
'auth_pass' : null,
'retry_max_delay' : 5000
};
if (config.objects.options.retry_max_delay === 15000 || !config.objects.options.retry_max_delay) {
config.objects.options.retry_max_delay = 5000;
}
config.states.options = config.states.options || {
'auth_pass' : null,
'retry_max_delay' : 5000
};
if (config.states.options.retry_max_delay === 15000 || !config.states.options.retry_max_delay) {
config.states.options.retry_max_delay = 5000;
}
if (!isDeepStrictEqual(config, configOrig)) {
fs.writeFileSync(configFile, JSON.stringify(config, null, 2));
console.log('ioBroker configuration updated');
}
} catch(err) {
console.log('Could not update ioBroker configuration: ' + err.message);
}
return void callback();
}
}, isFirst, isRedis);
}
break;
}
case 'url': {
Objects = require('./objects');
let url = args[0];
const name = args[1];
if (!url) {
console.log('Please provide a URL to install from and optionally a name of the adapter to install');
callback(EXIT_CODES.INVALID_ARGUMENTS);
}
if (url[0] === '"' && url[url.length - 1] === '"') {
url = url.substring(1, url.length - 1);
}
url = url.trim();
dbConnect(params, () => {
const Install = require('./setup/setupInstall.js');
const install = new Install({
objects,
states,
installNpm,
getRepository,
processExit: callback,
params
});
install.installAdapterFromUrl(url, name, callback);
});
break;
}
case 'info': {
Objects = require('./objects');
dbConnect(params, objects => {
tools.getHostInfo(objects, (err, data) => {
if (err) {
console.error('Cannot read host info: '+ err);
if (!data) {
return callback(EXIT_CODES.CANNOT_GET_HOST_INFO);
}
}
const formatters = require('./formatters');
const formatInfo = {
'Uptime': formatters.formatSeconds,
'System uptime': formatters.formatSeconds,
'RAM': formatters.formatRam,
'Speed': formatters.formatSpeed,
'Disk size': formatters.formatBytes,
'Disk free': formatters.formatBytes
};
for (const attr of Object.keys(data)) {
console.log(`${attr}${attr.length < 16 ? new Array(16 - attr.length).join(' ') : ''}: ${formatInfo[attr] ? formatInfo[attr](data[attr]) : data[attr] || ''}`);
}
return void callback();
});
});
break;
}
case 'a':
case 'add':
case 'install':
case 'i': {
Objects = require('./objects');
let name = args[0];
let instance = args[1];
let repoUrl = args[2];
if (instance === 0) {
instance = '0';
}
if (repoUrl === 0) {
repoUrl = '0';
}
if (parseInt(instance, 10).toString() !== (instance || '').toString()) {
repoUrl = instance;
instance = null;
}
if (parseInt(repoUrl, 10).toString() === (repoUrl || '').toString()) {
const temp = instance;
instance = repoUrl;
repoUrl = temp;
}
if (parseInt(instance, 10).toString() === (instance || '').toString()) {
instance = parseInt(instance, 10);
params.instance = instance;
}
// If user accidentally wrote tools.appName.adapter => remove adapter
name = cli.tools.normalizeAdapterName(name);
const parsedName = cli.tools.splitAdapterOrInstanceIdentifierWithVersion(name);
if (!parsedName) {
console.log('Invalid adapter name for install');
showHelp();
return void callback(EXIT_CODES.INVALID_ADAPTER_ID);
}
// split the adapter into its parts if necessary
if (parsedName.instance !== null) {
params.instance = parsedName.instance;
}
name = parsedName.name;
const installName = parsedName.nameWithVersion;
const adapterDir = tools.getAdapterDir(name);
dbConnect(params, async () => {
const Install = require('./setup/setupInstall.js');
const install = new Install({
objects,
states,
installNpm,
getRepository,
processExit: callback,
params
});
if (params.host && params.host !== tools.getHostName()) {
// if host argument provided we should check, that host actually exists in mh environment
let obj;
try {
obj = await objects.getObjectAsync(`system.host.${params.host}`);
} catch (e) {
console.warn(`Could not check existence of host "${params.host}": ${e.message}`);
}
if (!obj) {
console.error(`Cannot add instance to non-existing host "${params.host}"`);
return void callback(EXIT_CODES.NON_EXISTING_HOST);
}
}
if (!fs.existsSync(adapterDir)) {
install.downloadPacket(repoUrl, installName, null, enableAdapterCallback => {
install.installAdapter(installName, repoUrl, () => {
enableAdapterCallback(() => {
if (command !== 'install' && command !== 'i') {
install.createInstance(name, params, callback);
} else {
return void callback();
}
});
});
});
} else {
if (command !== 'install' && command !== 'i') {
install.createInstance(name, params, callback);
} else {
console.log(`adapter "${name}" already installed. Use "upgrade" to upgrade to a newer version.`);
return void callback(EXIT_CODES.ADAPTER_ALREADY_INSTALLED);
}
}
});
break;
}
case 'rebuild': {
let name = args[0];
// If user accidentally wrote tools.appName.adapter => remove adapter
name = cli.tools.normalizeAdapterName(name);
if (name.indexOf('@') !== -1) {
name = name.split('@')[0];
}
if (!name) {
console.log('Please provide the name of the adapter to rebuild');
return void callback(EXIT_CODES.INVALID_ADAPTER_ID);
}
const rebuildCommand = params.install ? 'install' : 'rebuild';
installNpm(name, rebuildCommand, (err, _adapter) => {
if (err) {
processExit(err);
} else {
console.log();
console.log('Rebuild ' + name + ' done');
return void callback();
}
});
break;
}
case 'upload':
case 'u': {
Objects = require('./objects');
const name = args[0];
const subTree = args[1];
if (name) {
dbConnect(params, () => {
const Upload = require('./setup/setupUpload.js');
const upload = new Upload({
states: states,
objects: objects
});
if (name === 'all') {
objects.getObjectList({startkey: 'system.adapter.', endkey: 'system.adapter.\u9999'}, (_err, objs) => {
const adapters = [];
for (let i = 0; i < objs.rows.length; i++) {
if (objs.rows[i].value.type !== 'adapter') {
continue;
}
adapters.push(objs.rows[i].value.common.name);
}
upload.uploadAdapterFull(adapters, callback);
});
} else {
// if upload of file
if (name.indexOf('.') !== -1) {
if (!subTree) {
console.log('Please specify target name, like:\n ' + tools.appName + ' upload /file/picture.png /vis.0/main/img/picture.png');
return void callback(EXIT_CODES.INVALID_ARGUMENTS);
}
upload.uploadFile(name, subTree, (err, newName) => {
!err && console.log('File "' + name + '" is successfully saved under ' + newName);
return void callback(err ? EXIT_CODES.CANNOT_UPLOAD_DATA : undefined);
});
} else {
if (subTree) {
upload.uploadAdapter(name, false, true, subTree, callback);
} else {
upload.uploadAdapterFull([name], callback);
}
}
}
});
} else {
console.log('No adapter name found!');
showHelp();
return void callback(EXIT_CODES.INVALID_ADAPTER_ID);
}
break;
}
case 'delete':
case 'del': {
let adapter = args[0];
let instance = args[1];
// The adapter argument is required
if (!adapter) {
showHelp();
return void callback(EXIT_CODES.INVALID_ADAPTER_ID);
}
// If the user accidentally wrote <tools.appName>.adapter,
// remove <tools.appName> from the adapter name
adapter = cli.tools.normalizeAdapterName(adapter);
// Avoid deleting stuff we don't want to delete
// e.g. `system.adapter.*`
if (!instance) {
// Ensure that adapter contains a valid adapter (without instance nr)
// or instance (with instance nr) identifier
if (!cli.tools.validateAdapterOrInstanceIdentifier(adapter)) {
showHelp();
return void callback(EXIT_CODES.INVALID_ADAPTER_ID);
}
// split the adapter into adapter + instance if necessary
if (adapter.indexOf('.') > -1) {
([adapter, instance] = adapter.split('.', 2));
}
} else {
// ensure that adapter contains a valid adapter identifier
// and the instance is a number
if (
!cli.tools.validateAdapterIdentifier(adapter)
|| !/^\d+$/.test(instance)
) {
showHelp();
return void callback(EXIT_CODES.INVALID_ADAPTER_ID);
}
}
if (instance || instance === 0) {
dbConnect(params, () => {
const Install = require('./setup/setupInstall.js');
const install = new Install({
objects,
states,
installNpm,
getRepository,
processExit: callback,
params
});
console.log('Delete adapter "' + adapter + '.' + instance + '"');
install.deleteInstance(adapter, instance, callback);
});
} else {
dbConnect(params, () => {
const Install = require('./setup/setupInstall.js');
const install = new Install({
objects,
states,
installNpm,
getRepository,
processExit: callback,
params
});
console.log('Delete adapter "' + adapter + '"');
install.deleteAdapter(adapter, (_a, resultCode) => void callback(resultCode));
});
}
break;
}
case 'unsetup': {
const rl = require('readline').createInterface({
input: process.stdin,
output: process.stdout
});
if (params.yes || params.y || params.Y) {
unsetup(params, callback);
} else {
rl.question('UUID will be deleted. Are you sure? [y/N]: ', answer => {
rl.close();
answer = answer.toLowerCase();
if (answer === 'y' || answer === 'yes' || answer === 'ja' || answer === 'j') {
unsetup(params, callback);
} else {
console.log('Nothing deleted');
return void callback();
}
});
}
break;
}
case 'o':
case 'object': {
const objectsCommand = new cli.command.object(commandOptions);
objectsCommand.execute(args);
break;
}
case 's':
case 'state': {
const statesCommand = new cli.command.state(commandOptions);
statesCommand.execute(args);
break;
}
case 'msg':
case 'message': {
const messageCommand = new cli.command.message(commandOptions);
messageCommand.execute(args);
break;
}
case 'logs': {
const logsCommand = new cli.command.logs(commandOptions);
logsCommand.execute(args, params);
break;
}
case 'upgrade': {
Objects = require('./objects');
let adapter = cli.tools.normalizeAdapterName(args[0]);
let repoUrl = args[1];
if (adapter && !repoUrl && adapter.indexOf('/') !== -1) {
repoUrl = adapter;
adapter = null;
}
if (adapter === 'all') {
adapter = null;
}
dbConnect(params, () => {
const Upgrade = require('./setup/setupUpgrade.js');
const upgrade = new Upgrade({
objects,
states,
installNpm,
getRepository,
params,
processExit: callback,
restartController
});
if (adapter) {
if (adapter === 'self') {
states.getState(`system.host.${tools.getHostName()}.alive`, (err, hostAlive) =>
upgrade.upgradeController(repoUrl, params.force || params.f, hostAlive && hostAlive.val, callback));
} else {
upgrade.upgradeAdapter(repoUrl, adapter, params.force || params.f, params.y || params.yes, false, callback);
}
} else {
// upgrade all
getRepository(repoUrl, (err, links) => {
const result = [];
for (const name of Object.keys(links)) {
result.push(name);
}
if (err) {
console.log(err);
}
if (links) {
result.sort();
upgrade.upgradeAdapterHelper(links, result, false, params.y || params.yes, callback);
} else {
// No information
return void callback(EXIT_CODES.INVALID_REPO);
}
});
}
});
break;
}
case 'clean': {
const yes = args[0];
if (yes !== 'yes') {
console.log(`Command "clean" clears all Objects and States. To execute it write "${tools.appName} clean yes"`);
} else {
dbConnect(params, (_obj, _stat, isNotRun) => {
if (!isNotRun) {
console.error(`Stop ${tools.appName} first!`);
return void callback(EXIT_CODES.CONTROLLER_RUNNING);
}
cleanDatabase(true, count => {
console.log('Deleted ' + count + ' states');
restartController(() => {
console.log('Restarting ' + tools.appName + '...');
return void callback();
});
});
});
}
break;
}
case 'restore': {
const Backup = require('./setup/setupBackup.js');
dbConnect(params, (_obj, _stat, isNotRun) => {
if (!isNotRun) {
console.error('Stop ' + tools.appName + ' first!');
return void callback(EXIT_CODES.CONTROLLER_RUNNING);
}
const backup = new Backup({
states: states,
objects: objects,
cleanDatabase: cleanDatabase,
restartController: restartController,
processExit: callback
});
backup.restoreBackup(args[0], () => {
console.log('System successfully restored!');
return void callback(EXIT_CODES.NO_ERROR);
});
});
break;
}
case 'backup': {
const name = args[0];
const Backup = require('./setup/setupBackup.js');
dbConnect(params, () => {
const backup = new Backup({
states: states,
objects: objects,
cleanDatabase: cleanDatabase,
restartController: restartController,
processExit: callback
});
backup.createBackup(name, filePath => {
console.log('Backup created: ' + filePath);
return void callback(EXIT_CODES.NO_ERROR);
});
});
break;
}
case 'validate': {
const name = args[0];
const Backup = require('./setup/setupBackup.js');
dbConnect(params, () => {
const backup = new Backup({
states: states,
objects: objects,
cleanDatabase: cleanDatabase,
restartController: restartController,
processExit: callback
});
backup.validateBackup(name).then(() => {
console.log('Backup OK');
processExit(0);
}).catch(e => {
console.log(`Backup check failed: ${e.message}`);
processExit(1);
});
});
break;
}
case 'l':
case 'list': {
dbConnect(params, (_objects, _states, _isOffline, _objectsType, config) => {
const List = require('./setup/setupList.js');
const list = new List({
states: states,
objects: objects,
processExit: callback,
config: config
});
list.list(args[0], args[1], params);
});
break;
}
case 'touch': {
let pattern = args[0];
if (!pattern) {
console.log('No file path found. Example: "touch /vis.0/main/*"');
return void callback(EXIT_CODES.INVALID_ARGUMENTS);
}
dbConnect(params, () => {
// extract id
pattern = pattern.replace(/\\/g, '/');
if (pattern[0] === '/') {
pattern = pattern.substring(1);
}
if (pattern === '*') {
objects.getObjectList({startkey: 'system.adapter.', endkey: 'system.adapter.\u9999'}, (err, arr) => {
if (!err && arr && arr.rows) {
const files = [];
let count = 0;
for (let i = 0; i < arr.rows.length; i++) {
if (arr.rows[i].value.type !== 'adapter') {
continue;
}
count++;
objects.touch(arr.rows[i].value.common.name, '*', {user: 'system.user.admin'}, (err, processed, _id) => {
if (!err && processed) {
files.push({id: _id, processed: processed});
}
if (!--count) {
const List = require('./setup/setupList.js');
const list = new List({
states: states,
objects: objects,
processExit: callback
});
files.sort((a, b) => a.id.localeCompare(b.id));
for (let k = 0; k < files.length; k++) {
for (let t = 0; t < files[k].processed.length; t++) {
list.showFile(files[k].id, files[k].processed[t].path, files[k].processed[t]);
}
}
setTimeout(callback, 1000);
}
});
}
if (!count) {
console.log('Nothing found');
return void callback();
}
}
});
} else {
const parts = pattern.split('/');
const id = parts.shift();
const path = parts.join('/');
objects.touch(id, path, {user: 'system.user.admin'}, (err, processed) => {
if (err) {
console.error(err);
} else {
if (processed) {
const List = require('./setup/setupList.js');
const list = new List({
states: states,
objects: objects,
processExit: callback
});
for (let i = 0; i < processed.length; i++) {
list.showFile(id, processed[i].path, processed[i]);
}
}
}
setTimeout(callback, 1000);
});
}
});
break;
}
case 'rm': {
let pattern = args[0];
if (!pattern) {
console.log('No file path found. Example: "touch /vis.0/main/*"');
return void callback(EXIT_CODES.INVALID_ARGUMENTS);
}
dbConnect(params, () => {
// extract id
pattern = pattern.replace(/\\/g, '/');
if (pattern[0] === '/') {
pattern = pattern.substring(1);
}
if (pattern === '*') {