First working version.
This commit is contained in:
parent
b526067eeb
commit
d9e615e696
@ -10,11 +10,22 @@
|
||||
#### [npm](https://npmjs.org/package/xo-cli)
|
||||
|
||||
```
|
||||
npm install -f xo-cli
|
||||
npm install -g xo-cli
|
||||
```
|
||||
|
||||
## Usage
|
||||
|
||||
#### Register your XO instance
|
||||
|
||||
```
|
||||
xo-cli add-server
|
||||
xo-cli register --host xo.my-company.net --email admin@admin.net --password admin
|
||||
```
|
||||
|
||||
Note: only a token will be saved in the configuration file.
|
||||
|
||||
#### Adds a new Xen server
|
||||
|
||||
|
||||
```
|
||||
xo-cli add-server --host xen1.my-company.net --user root --password secure%password
|
||||
```
|
||||
|
@ -8,14 +8,171 @@ var Promise = require('bluebird');
|
||||
|
||||
//--------------------------------------------------------------------
|
||||
|
||||
var config = require('./config');
|
||||
var prompt = require('./prompt');
|
||||
var Xo = require('./xo');
|
||||
|
||||
//====================================================================
|
||||
|
||||
// Handles a promise in a CLI environment.
|
||||
var handlePromise = function (promise) {
|
||||
promise.then(function (value) {
|
||||
var connect = function () {
|
||||
return config.load().bind({}).then(function (config) {
|
||||
if (!config.server)
|
||||
{
|
||||
throw 'no server to connect to!';
|
||||
}
|
||||
|
||||
if (!config.token)
|
||||
{
|
||||
throw 'no token available';
|
||||
}
|
||||
|
||||
this.xo = new Xo(config.server);
|
||||
|
||||
return this.xo.send('session.signInWithToken', {
|
||||
token: config.token,
|
||||
});
|
||||
}).then(function () {
|
||||
return this.xo;
|
||||
}).bind();
|
||||
};
|
||||
|
||||
//====================================================================
|
||||
|
||||
module.exports = function (argv) {
|
||||
var command;
|
||||
var commandName;
|
||||
var commandOpts;
|
||||
|
||||
var registerCommand = function (name, def, fn) {
|
||||
var cmdParser = nomnom.command(name);
|
||||
|
||||
if (def.description)
|
||||
{
|
||||
cmdParser.help(def.description);
|
||||
}
|
||||
|
||||
if (def.args)
|
||||
{
|
||||
var interactive = process.stdout.isTTY;
|
||||
|
||||
_.each(def.args, function (def, name) {
|
||||
cmdParser.option(name, {
|
||||
abbr: def.abbr,
|
||||
flag: (def.type === 'boolean'),
|
||||
help: def.description,
|
||||
|
||||
// Do not mark options as required if the program is run in
|
||||
// interactive mode, they will be asked.
|
||||
required: !interactive && def.required,
|
||||
});
|
||||
});
|
||||
|
||||
// TODO: alters `fn` to prompt for each missing argument.
|
||||
fn = (function (fn, args) {
|
||||
return function (opts) {
|
||||
var prompts = [];
|
||||
|
||||
_.each(args, function (def, name) {
|
||||
if (!(name in opts))
|
||||
{
|
||||
prompts.push({
|
||||
name: name,
|
||||
message: def.prompt || def.description,
|
||||
type: def.promptType || 'input',
|
||||
});
|
||||
}
|
||||
});
|
||||
|
||||
if (prompts.length)
|
||||
{
|
||||
return prompt(prompts).then(function (answers) {
|
||||
return fn(_.extend(opts, answers));
|
||||
});
|
||||
}
|
||||
|
||||
return fn(opts);
|
||||
};
|
||||
})(fn, def.args);
|
||||
}
|
||||
|
||||
cmdParser.callback(function (opts) {
|
||||
command = fn;
|
||||
commandName = name;
|
||||
commandOpts = opts;
|
||||
});
|
||||
};
|
||||
|
||||
registerCommand('register', {
|
||||
description: 'registers the XO instance',
|
||||
args: {
|
||||
host: {
|
||||
description: 'host/ip optionally followed by `:port` if not 80',
|
||||
required: true,
|
||||
},
|
||||
email: {
|
||||
description: 'email to use to connect',
|
||||
required: true,
|
||||
},
|
||||
password: {
|
||||
description: 'password to use to connect',
|
||||
promptType: 'password',
|
||||
required: true,
|
||||
},
|
||||
}
|
||||
}, function (opts) {
|
||||
return Promise.bind({opts: opts || {}}).then(function () {
|
||||
this.xo = new Xo(opts.host);
|
||||
|
||||
return this.xo.send('session.signInWithPassword', {
|
||||
email: this.opts.email,
|
||||
password: this.opts.password,
|
||||
});
|
||||
}).then(function (user) {
|
||||
console.log('Successfully logged with', user.email);
|
||||
|
||||
return this.xo.send('token.create');
|
||||
}).then(function (token) {
|
||||
console.log('Token created:', token);
|
||||
|
||||
return config.set({
|
||||
server: this.opts.host,
|
||||
token: token,
|
||||
});
|
||||
}).bind();
|
||||
});
|
||||
|
||||
registerCommand('add-server', {
|
||||
description: 'adds a new Xen server',
|
||||
args: {
|
||||
host: {
|
||||
description: 'host of the server',
|
||||
required: true,
|
||||
},
|
||||
username: {
|
||||
description: 'username to use to connect',
|
||||
required: true,
|
||||
},
|
||||
password: {
|
||||
description: 'password to use to connect',
|
||||
promptType: 'password',
|
||||
required: true,
|
||||
},
|
||||
},
|
||||
}, function (opts) {
|
||||
return connect().then(function (connection) {
|
||||
return connection.send('server.add', {
|
||||
host: opts.host,
|
||||
username: opts.username,
|
||||
password: opts.password,
|
||||
});
|
||||
}).return('ok');
|
||||
});
|
||||
|
||||
// TODO: handle global `--config FILE` option.
|
||||
nomnom.parse(argv);
|
||||
|
||||
// Executes the selected command.
|
||||
Promise.try(command, [commandOpts]).then(function (value) {
|
||||
if (_.isString(value))
|
||||
{
|
||||
console.log(value);
|
||||
@ -23,165 +180,20 @@ var handlePromise = function (promise) {
|
||||
|
||||
process.exit(0);
|
||||
}).catch(function (error) {
|
||||
if (_.isString(error))
|
||||
if (error === undefined)
|
||||
{
|
||||
console.error(error);
|
||||
// Nothing to do.
|
||||
undefined;
|
||||
}
|
||||
else if (_.isNumber(error))
|
||||
{
|
||||
process.exit(error);
|
||||
}
|
||||
else
|
||||
{
|
||||
console.error(error.stack || error);
|
||||
}
|
||||
|
||||
process.exit(1);
|
||||
});
|
||||
};
|
||||
|
||||
//--------------------------------------------------------------------
|
||||
|
||||
var connect = function (opts) {
|
||||
var config, xo;
|
||||
|
||||
return Promise.try(function () {
|
||||
opts || (opts = {});
|
||||
|
||||
// TODO: reads the configuration file.
|
||||
config = {
|
||||
server: 'ws://localhost:9000/api/',
|
||||
};
|
||||
|
||||
if (!config.server)
|
||||
{
|
||||
throw 'no server to connect to!';
|
||||
}
|
||||
|
||||
xo = new Xo(config.server);
|
||||
|
||||
if (config.token && !opts.noAutoSignIn)
|
||||
{
|
||||
return xo.send('session.signInWithToken', {
|
||||
token: config.token,
|
||||
});
|
||||
}
|
||||
});
|
||||
};
|
||||
|
||||
//--------------------------------------------------------------------
|
||||
|
||||
var addServer = function (host, username, password) {
|
||||
return connect().then(function (connection) {
|
||||
return connection.send('server.add', {
|
||||
host: host,
|
||||
username: username,
|
||||
password: password,
|
||||
});
|
||||
}).return('ok');
|
||||
};
|
||||
|
||||
var register = function (url, email, password) {
|
||||
var xo;
|
||||
return Promise.try(function () {
|
||||
xo = new Xo(url);
|
||||
|
||||
return xo.send('session.signInWithPassword', {
|
||||
email: email,
|
||||
password: password,
|
||||
});
|
||||
}).then(function (user) {
|
||||
console.log('Successfully logged with', user.email);
|
||||
|
||||
return xo.send('token.create');
|
||||
}).then(function (token) {
|
||||
return getConfig().set({
|
||||
server: url,
|
||||
token: token,
|
||||
});
|
||||
});
|
||||
};
|
||||
|
||||
//====================================================================
|
||||
|
||||
module.exports = function (argv) {
|
||||
nomnom.command('register')
|
||||
.help('signs in XO using email/password')
|
||||
.options({
|
||||
url: {
|
||||
help: 'URL of the API endpoint',
|
||||
},
|
||||
email: {
|
||||
help: 'email to use to connect',
|
||||
},
|
||||
password: {
|
||||
help: 'password to use to connect',
|
||||
},
|
||||
})
|
||||
.callback(function (opts) {
|
||||
Promise.try(function () {
|
||||
if (opts.url)
|
||||
{
|
||||
return prompt.input('URL').then(function (url) {
|
||||
opts.url = url;
|
||||
});
|
||||
}
|
||||
}).then(function () {
|
||||
if (!opts.email)
|
||||
{
|
||||
return prompt.input('Email').then(function (email) {
|
||||
opts.email = email;
|
||||
});
|
||||
}
|
||||
}).then(function () {
|
||||
if (!opts.password)
|
||||
{
|
||||
return prompt.password('Password').then(function (password) {
|
||||
opts.password = password;
|
||||
});
|
||||
}
|
||||
}).then(function () {
|
||||
return register(opts.url, opts.email, opts.password);
|
||||
});
|
||||
})
|
||||
;
|
||||
|
||||
nomnom.command('add-server')
|
||||
.help('adds a new server')
|
||||
.options({
|
||||
host: {
|
||||
help: 'hostname or ip of the server',
|
||||
},
|
||||
username: {
|
||||
help: 'username to use to connect',
|
||||
},
|
||||
password: {
|
||||
help: 'password to use to connect',
|
||||
},
|
||||
})
|
||||
.callback(function (opts) {
|
||||
Promise.try(function () {
|
||||
if (opts.host)
|
||||
{
|
||||
return prompt.input('Hostname or ip').then(function (host) {
|
||||
opts.host = host;
|
||||
});
|
||||
}
|
||||
}).then(function () {
|
||||
if (!opts.username)
|
||||
{
|
||||
return prompt.input('Username').then(function (username) {
|
||||
opts.username = username;
|
||||
});
|
||||
}
|
||||
}).then(function () {
|
||||
if (!opts.password)
|
||||
{
|
||||
return prompt.password('Password').then(function (password) {
|
||||
opts.password = password;
|
||||
});
|
||||
}
|
||||
}).then(function () {
|
||||
return addServer(opts.host, opts.username, opts.password);
|
||||
});
|
||||
})
|
||||
;
|
||||
|
||||
nomnom.parse(argv);
|
||||
};
|
||||
|
@ -2,10 +2,47 @@
|
||||
|
||||
//====================================================================
|
||||
|
||||
var xdg = require('xdg').basedir;
|
||||
var fs = require('fs');
|
||||
|
||||
//--------------------------------------------------------------------
|
||||
|
||||
var _ = require('lodash');
|
||||
var l33t = require('l33teral');
|
||||
var mkdirp = require('mkdirp');
|
||||
var Promise = require('bluebird');
|
||||
var xdg = require('xdg');
|
||||
|
||||
//====================================================================
|
||||
|
||||
var Config = function () {
|
||||
this.data =
|
||||
var configPath = xdg.basedir.configPath('xo-cli');
|
||||
var configFile = configPath +'/config.json';
|
||||
|
||||
var mkdirp = Promise.promisify(mkdirp);
|
||||
var readFile = Promise.promisify(fs.readFile);
|
||||
var writeFile = Promise.promisify(fs.writeFile);
|
||||
|
||||
//====================================================================
|
||||
|
||||
var load = exports.load = function () {
|
||||
return readFile(configFile).then(JSON.parse).catch(function () {
|
||||
return {};
|
||||
});
|
||||
};
|
||||
|
||||
exports.get = function (path) {
|
||||
return load().then(function (config) {
|
||||
return l33t(config).tap(path);
|
||||
});
|
||||
};
|
||||
|
||||
var save = exports.save = function (config) {
|
||||
return mkdirp(configPath).then(function () {
|
||||
return writeFile(configFile, JSON.stringify(config));
|
||||
});
|
||||
};
|
||||
|
||||
exports.set = function (data) {
|
||||
return load().then(function (config) {
|
||||
return save(_.extend(config, data));
|
||||
});
|
||||
};
|
||||
|
@ -13,7 +13,9 @@
|
||||
"bluebird": "^1.1.0",
|
||||
"chalk": "^0.4.0",
|
||||
"inquirer": "^0.4.1",
|
||||
"l33teral": "^2.0.3",
|
||||
"lodash": "^2.4.1",
|
||||
"mkdirp": "^0.3.5",
|
||||
"nomnom": "^1.6.2",
|
||||
"ws": "^0.4.31",
|
||||
"xdg": "^0.1.1"
|
||||
@ -30,5 +32,6 @@
|
||||
"bin": {
|
||||
"xo-cli": "bin/xo-cli"
|
||||
},
|
||||
"preferGlobal": true,
|
||||
"license": "AGPL3"
|
||||
}
|
||||
|
@ -15,7 +15,7 @@ var prompts = module.exports = function (prompts) {
|
||||
return deferred.promise;
|
||||
};
|
||||
|
||||
exports.input = function (message) {
|
||||
prompts.input = function (message) {
|
||||
return prompts({
|
||||
type: 'input',
|
||||
name: 'question',
|
||||
@ -23,7 +23,7 @@ exports.input = function (message) {
|
||||
}).get('question');
|
||||
};
|
||||
|
||||
exports.password = function (message) {
|
||||
prompts.password = function (message) {
|
||||
return prompts({
|
||||
type: 'password',
|
||||
name: 'question',
|
||||
|
@ -8,7 +8,9 @@ var Promise = require('bluebird');
|
||||
|
||||
// Supports browsers.
|
||||
// FIXME: wraps in an anonymous function.
|
||||
var WebSocket = 'WebSocket' in window ? window.WebSocket : require('ws');
|
||||
// jshint ignore: start
|
||||
var WebSocket = (this && 'WebSocket' in this) ? this.WebSocket : require('ws');
|
||||
// jshint ignore: end
|
||||
|
||||
//====================================================================
|
||||
|
||||
@ -32,6 +34,13 @@ var Xo = function (url) {
|
||||
};
|
||||
|
||||
_.extend(Xo.prototype, {
|
||||
close: function () {
|
||||
if (this._socket)
|
||||
{
|
||||
this._socket.close();
|
||||
}
|
||||
},
|
||||
|
||||
connect: function () {
|
||||
if (this.status === 'connected')
|
||||
{
|
||||
@ -48,13 +57,22 @@ _.extend(Xo.prototype, {
|
||||
socket.on('open', function () {
|
||||
this.status = 'connected';
|
||||
|
||||
// Reopens accesses.
|
||||
// (Re)Opens accesses.
|
||||
delete this.send;
|
||||
|
||||
// Resolves the promise.
|
||||
deferred.resolve();
|
||||
}.bind(this));
|
||||
|
||||
socket.on('message', function (event) {
|
||||
socket.on('message', function (data) {
|
||||
// `ws` API is lightly different from standard API.
|
||||
if (data.data)
|
||||
{
|
||||
data = data.data;
|
||||
}
|
||||
|
||||
// TODO: Wraps in a promise to prevent releasing the Zalgo.
|
||||
var response = JSON.parse(event.data);
|
||||
var response = JSON.parse(data);
|
||||
|
||||
var id = response.id;
|
||||
|
||||
@ -80,7 +98,7 @@ _.extend(Xo.prototype, {
|
||||
message: 'invalid response received',
|
||||
object: response,
|
||||
});
|
||||
});
|
||||
}.bind(this));
|
||||
|
||||
socket.on('close', function () {
|
||||
// Closes accesses.
|
||||
@ -95,6 +113,11 @@ _.extend(Xo.prototype, {
|
||||
this._deferreds = {};
|
||||
}.bind(this));
|
||||
|
||||
socket.on('error', function (error) {
|
||||
// Fails the connect promise if possible.
|
||||
deferred.reject(error);
|
||||
});
|
||||
|
||||
return deferred.promise;
|
||||
},
|
||||
|
||||
@ -114,7 +137,7 @@ _.extend(Xo.prototype, {
|
||||
var deferred = this._deferreds[id] = Promise.defer();
|
||||
|
||||
return deferred.promise;
|
||||
});
|
||||
}.bind(this));
|
||||
},
|
||||
});
|
||||
|
||||
|
Loading…
Reference in New Issue
Block a user