UNPKG

iobroker.js-controller

Version:

Updated by reinstall.js on 2018-06-11T15:19:56.688Z

1,203 lines (1,106 loc) • 127 kB
/* 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 === '*') {