├── .gitignore ├── .npmignore ├── .travis.yml ├── README.md ├── bin └── nodester.js ├── config.json ├── examples ├── davster └── mynodester.js ├── lib ├── app.js ├── appdomain.js ├── apps.js ├── client.js ├── commands.js ├── config.js ├── coupon.js ├── env.js ├── log.js ├── npm.js ├── status.js └── user.js ├── package.json └── test └── test-app.js /.gitignore: -------------------------------------------------------------------------------- 1 | .DS_Store 2 | *.log 3 | node_modules 4 | -------------------------------------------------------------------------------- /.npmignore: -------------------------------------------------------------------------------- 1 | .DS_Store 2 | *.log 3 | node_modules 4 | -------------------------------------------------------------------------------- /.travis.yml: -------------------------------------------------------------------------------- 1 | language: node_js 2 | node_js: 3 | - 0.6 -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | # Nodester API CLI access [![Build Status](https://secure.travis-ci.org/alejandromg/nodester-cli.png)](http://travis-ci.org/nodester/nodester-cli) 2 | 3 | This app is a little wrapper around the REST API for the [Nodester](http://nodester.com/) OS Node hosting platform. 4 | 5 | ## Installation 6 | 7 | npm install nodester-cli -g 8 | 9 | 10 | ## Usage 11 | 12 | In your CLI run one of these commands: 13 | 14 | $ nodester 15 | $ nodester app 16 | $ nodester user 17 | 18 | Each of them will outpot the help for the respective command. 19 | 20 | 21 | ## Local installation 22 | 23 | If you have your own instance of [Nodester](http://nodester.com/) installed on your own server, the command line app is designed to work with that too. Currently it supports 3 environment variables to change a couple of default settings. 24 | 25 | Here is a simple example: 26 | 27 | #!/bin/bash 28 | 29 | export NODESTER_BRAND=davster; 30 | export NODESTER_APIHOST=auth.davglass.com; 31 | nodester "$@" 32 | export NODESTER_BRAND=; 33 | export NODESTER_APIHOST=; 34 | 35 | **In the newest version of `nodester-cli`** you can easily switch betwen instances running: 36 | 37 | $ nodester config set 38 | 39 | after this you can access to your instance. To rollback to the nodester endpoint run: 40 | 41 | $ nodester config set api.nodester.com nodester 42 | 43 | Also you can see what is the current configuration running: 44 | 45 | $ nodester config get 46 | 47 | 48 | Also you can take a look at `examples` and see how can you roll out your personal nodester-cli 49 | 50 | 51 | ## Contributors 52 | 53 | The nodester community (`nodester authors`): 54 | 55 | - [Chris Matthieu](http://matthieu.us) 56 | - [Daniel Bartlett ](http://danb-uk.net/) 57 | - [Dav Glass ](http://twitter/@davglass) 58 | - Abraham Williams <4braham@gmail.com> 59 | - Contra 60 | - Marcos Oliveira 61 | - [Alejandro Morales](http://alejandromorales.co.cc) 62 | 63 | ## License 64 | 65 | MIT 2012 - Nodester 66 | 67 | -------------------------------------------------------------------------------- /bin/nodester.js: -------------------------------------------------------------------------------- 1 | #!/usr/bin/env node 2 | 3 | var config = require('../config'); 4 | 5 | process.nodester = { 6 | brand: config.brand, 7 | apihost: config.apihost, 8 | env : process.env.NODESTER_ENV || 'production' 9 | } 10 | 11 | var cli = require('../lib/commands'), 12 | command = process.argv[0], 13 | cmds = cli.commands; 14 | 15 | cli.run(cmds, command); 16 | -------------------------------------------------------------------------------- /config.json: -------------------------------------------------------------------------------- 1 | { 2 | "brand": "nodester", 3 | "apihost": "api.nodester.com" 4 | } -------------------------------------------------------------------------------- /examples/davster: -------------------------------------------------------------------------------- 1 | #!/bin/bash 2 | 3 | export NODESTER_BRAND=davster; 4 | export NODESTER_APIHOST=auth.davglass.com; 5 | nodester "$@" 6 | export NODESTER_BRAND=; 7 | export NODESTER_APIHOST=; 8 | 9 | -------------------------------------------------------------------------------- /examples/mynodester.js: -------------------------------------------------------------------------------- 1 | #!/usr/bin/env node 2 | 3 | /** 4 | * Setup the brand info here.. 5 | */ 6 | process.nodester = { 7 | brand: 'myapp', 8 | apihost: 'nodester.myapp.com' 9 | } 10 | 11 | //Include the CLI 12 | var cli = require('nodester-cli'), 13 | //Find the command (process.argv has already been processed here to remove node and scriptname) 14 | command = process.argv[0], 15 | //Include the commands so you can add your own 16 | cmds = cli.commands; 17 | 18 | /* 19 | * Add a new command: 20 | * mynodester davglass 21 | * mynodester davglass test 22 | * mynodester help davglass 23 | */ 24 | cmds.davglass = { 25 | test: function() { 26 | cli.log.info('DAVGLASS TEST'); 27 | }, 28 | usage: function() { 29 | cli.log.usage('davglass USAGE GOES HERE'); 30 | } 31 | } 32 | 33 | //Execute the command.. 34 | cli.run(cmds, command); 35 | 36 | -------------------------------------------------------------------------------- /lib/app.js: -------------------------------------------------------------------------------- 1 | /* 2 | * nodester-cli 3 | * A CLI tool to allow interaction with the http://nodester.com/ platform. 4 | * @division app 5 | * 6 | */ 7 | 8 | "use strict"; 9 | 10 | /*jshint node:true, noempty:true, laxcomma:true, laxbreak:false */ 11 | 12 | var exec = require('child_process').exec 13 | , util = require('util') 14 | , fs = require('fs') 15 | , path = require('path') 16 | , read = require('read') 17 | , colors = require('colors') 18 | , Node = require('nodester-api').nodester 19 | , config = require('./config') 20 | , exists = fs.existsSync || path.existsSync 21 | , log = require('./log') 22 | ; 23 | 24 | /* Shorthands */ 25 | 26 | var cfg = config 27 | , nodeapi = new Node(cfg.username, cfg.password, cfg.apihost, cfg.apisecure) 28 | ; 29 | 30 | 31 | // Show the `app` help and usage 32 | function usage() { 33 | log.info(''); 34 | log.info(' `nodester app `'.bold + ' help you to interact with your applications on nodester.'); 35 | log.info(' The availble commands are: (Prefixed with nodester ) '); 36 | log.usage(''); 37 | log.usage('\tapp or \t\t\t- is not required if inside an app directory after you call setup'); 38 | log.usage('\tapp create \t- Creates a new app named , is optional. (default to `server.js`)'); 39 | log.usage('\tapp init \t\t\t- Fetches the remote repo and sets it up.'); 40 | log.usage('\tapp setup \t\t\t- Configure this dir for future app commands'); 41 | log.usage('\tapp edit \t\t\t- Change the start file for an app (appname is optional)'); 42 | log.usage('\tapp info \t\t\t- Returns app specific information'); 43 | log.usage('\tapp logs \t\t\t- Returns app logs'); 44 | log.usage('\tapp stop|start|restart \t- Controls app status.'); 45 | log.usage('\tapp destroy \t\t\t- Deletes the app.'); 46 | log.usage('\tapp gitreset \t\t\t- Resets the app to git HEAD (in case you want a clean restart).'); 47 | log.usage('\tapp clone \t\t\t- Fetches the remote repo.'); 48 | log.usage('\tapp list \t\t\t\t- Returns the list of your apps'); 49 | log.usage(''); 50 | log.info('ok!'.green.bold); 51 | } 52 | 53 | // Create a new app 54 | function create (args) { 55 | config.check(); 56 | if (!args.length) { 57 | log.error('appname required'); 58 | return; 59 | } 60 | 61 | var name = args[0].toLowerCase(); 62 | var start = args[1] || 'server.js'; 63 | 64 | log.info('creating app:', name, start); 65 | 66 | nodeapi.app_create(name, start, function (err, data, original) { 67 | if (err) { 68 | log.error(err.message); 69 | } 70 | 71 | if (data.status == "success") { 72 | log.info('successfully created app', name.bold, 'to will run on port', ((data.port) + '').bold, 'from', start.bold); 73 | log.info('run', (config.brand + ' app init ' + name).yellow.bold, 'to setup this app.'); 74 | log.info('ok!'.green.bold); 75 | } else { 76 | log.error(data.status, ':', data.message); 77 | } 78 | }); 79 | } 80 | 81 | // Delete the app 82 | function destroy (args) { 83 | config.check(); 84 | var appname = config.appname; 85 | 86 | if (args.length) { 87 | appname = args[0].toLowerCase(); 88 | } 89 | 90 | if (!appname){ 91 | log.error('appname required'); 92 | return false; 93 | } 94 | 95 | log.warn('deleting app:', appname); 96 | log.warn('Are you sure you want to do this?'); 97 | log.plain(' Waiting 10 seconds before continuing, Ctrl+C to abort)'); 98 | util.print('.'); 99 | var timer = setInterval(function () { 100 | util.print('.'); 101 | }, 1000); 102 | setTimeout(function () { 103 | clearInterval(timer); 104 | util.print('\n'); 105 | log.warn('Really deleting app now, you were warned..'); 106 | nodeapi.app_delete(appname, function (err, data, original) { 107 | if (err) { 108 | log.error(err.message); 109 | return; 110 | } 111 | if (data && data.status == "success") { 112 | log.warn('successfully deleted app', appname.bold); 113 | log.info('ok!'.green.bold); 114 | } else { 115 | log.warn(data.status); 116 | } 117 | }); 118 | }, 10000); 119 | } 120 | 121 | // Init a folder or a fresh "hello world" app 122 | function init (args) { 123 | 124 | /** 125 | * Configure new app 126 | * @public: true 127 | * @api: true 128 | */ 129 | 130 | config.check(); 131 | var appname 132 | , folder 133 | , isWin = !!process.platform.match(/^win/) 134 | , cwd = isWin ? process.cwd() : process.env.PWD 135 | ; 136 | 137 | if (!args.length) { 138 | log.error('appname required'); 139 | return; 140 | } else { 141 | appname = args[0].toLowerCase(); 142 | folder = appname; 143 | if (args[1]) { 144 | folder = args[1].toLowerCase(); 145 | } 146 | } 147 | 148 | 149 | var question = 'nodester'.magenta + ' info ' + 150 | 'What do you want to do:\n(1) Setup a new app from scratch?' + 151 | '\n(2) You just want to setup your existent app?\n'+'note:'.italic + 152 | ' if you choose 2 be sure that you are into your app\'s dir\n'.bold; 153 | 154 | read({ prompt: question, 'default': 1}, function(err,res) { 155 | 156 | var isNewRep = false; 157 | 158 | if (res && res != 1 && res != 2) log.error('invalid option'); 159 | 160 | if (res == 1 ) { 161 | 162 | log.info('initializing git repo for', appname, 'into folder', folder); 163 | isNewRep = true; 164 | } 165 | 166 | 167 | 168 | nodeapi.app_info(appname, function (err, data, original) { 169 | 170 | if (err) { 171 | log.error(err.message); 172 | return; 173 | } 174 | 175 | if (/app\snot\sfound/g.test(data.status)){ 176 | log.error('You can\'t init `'+ appname +'` it doesn\'t exists '); 177 | return; 178 | } 179 | 180 | var joinChar = ' ; '; 181 | 182 | // Config future commands 183 | 184 | 185 | 186 | log.warn('this will take a second or two'); 187 | 188 | if (!isNewRep) { 189 | 190 | if (isWin) joinChar = ' && '; 191 | var command = 'git remote add nodester ' + data.gitrepo + joinChar + ' echo "appname='+appname +'" > .appconfig ' ; 192 | exec(command, function(error){ 193 | if (error){ 194 | log.error(error); 195 | return; 196 | } 197 | 198 | config.writeApp(appname); 199 | 200 | log.info('Your app is now configured to work with nodester.'); 201 | log.info('This app will be running on port ' + (data.port || '-') + 202 | ' if you don\'t want to hardcode this port use `process.env["app_port"]` instead'); 203 | log.info('Configured the remote `nodester` now you should push to that remote.'); 204 | log.info('Some helpful app commands:\n'); 205 | log.plain(' > cd ' + folder); 206 | log.plain(' > git add . '); 207 | log.plain(' > git commit -am "Ready to deploy"'); 208 | log.plain(' > git push nodester master '); 209 | log.plain(' ', (config.brand + ' app info').yellow); 210 | log.plain(' ', (config.brand + ' app logs').yellow); 211 | log.plain(' ', (config.brand + ' app stop|start|restart').yellow); 212 | log.info('ok!'.green.bold); 213 | }); 214 | 215 | return; 216 | } else { 217 | 218 | log.info('cloning your new app in '+ folder); 219 | 220 | exec('git clone https://github.com/nodester/defaultApp.git'+ ' ' + folder, function (error, stdout, stderr) { 221 | 222 | if (error) { 223 | return log.error(error); 224 | } 225 | 226 | log.info('clone complete'); 227 | 228 | var packageJSON; 229 | try { 230 | // Back-support for olders versions 231 | packageJSON = require(cwd + '/' + folder + '/package.json'); 232 | 233 | } catch (exp){ 234 | 235 | var fullPath = path.resolve(cwd,folder,'package.json'); 236 | packageJSON = JSON.parse(JSON.stringify(fs.readFileSync(fullPath,'utf8'))); 237 | 238 | } 239 | 240 | /** Default package.json config */ 241 | config.brand 242 | packageJSON.name = appname; 243 | packageJSON.homepage = 'http://'+ appname + '.nodester.com'; 244 | packageJSON.node = '0.8.1'; 245 | packageJSON.author = config.username; 246 | 247 | log.info('writing the default configuration'); 248 | 249 | var packPath = path.resolve(cwd,folder,'package.json'); 250 | 251 | // Write the package.json 252 | fs.writeFileSync(packPath, JSON.stringify(packageJSON, null, 2), 'utf8'); 253 | 254 | // Default server.js values 255 | var serverPath = path.resolve(cwd,folder,'server.js'); 256 | var server = fs.readFileSync(serverPath,'utf8'); 257 | 258 | server = server.replace(/\"\{\{APPPORT\}\}\"/g, data.port) 259 | .replace(/\{\{APPNAME\}\}/g, appname); 260 | 261 | // write the new server.js file 262 | fs.writeFileSync(cwd + '/' + folder + '/server.js', server ,'utf8'); 263 | 264 | var cmd = 'cd ' + path.resolve(process.env.PWD,folder)+ 265 | '&& git add .'+ 266 | '&& git remote set-url origin ' + data.gitrepo+ 267 | '&& git commit -am "Initial commit via ' + config.brand + '-cli"'+ 268 | '&& git push origin master'+ 269 | '&& nodester npm install ' + appname + ' express'; 270 | 271 | log.info('processing the initial commit'); 272 | 273 | exec(cmd, function (error, stdout, stderr) { 274 | if (error) { 275 | log.error(error); 276 | return; 277 | } 278 | if (stderr){ 279 | log.info(stderr); 280 | } 281 | 282 | log.info(appname, "started."); 283 | log.info('Some helpful app commands:\n'); 284 | log.plain(' cd ./' + folder); 285 | log.plain(' curl http://' + folder + '.'+ config.brand +'.com/'); 286 | log.plain(' ', (config.brand + ' app info').yellow); 287 | log.plain(' ', (config.brand + ' app logs').yellow); 288 | log.plain(' ', (config.brand + ' app stop|start|restart').yellow); 289 | log.info('ok!'.green.bold); 290 | }); 291 | }); 292 | } 293 | }); 294 | }); 295 | } 296 | 297 | // Clone the app from nodester 298 | function clone (args) { 299 | 300 | config.check(); 301 | var folder 302 | , appname; 303 | 304 | if (args.length) { 305 | appname = folder = args[0].toLowerCase(); 306 | if (args[1]) { 307 | folder = args[1].toLowerCase(); 308 | } 309 | } 310 | 311 | if (!appname || !folder){ 312 | log.error(appname ? 'folder doesn\'t exists' : 'appname required'); 313 | return; 314 | } 315 | 316 | log.info('initializing git repo for', appname, 'into folder', folder); 317 | 318 | try { 319 | fs.mkdirSync(folder, "0750"); 320 | } catch (e) { 321 | log.error(e.toString()); 322 | } 323 | 324 | nodeapi.app_info(appname, function (err, data, original) { 325 | 326 | if (err) { 327 | log.error(err.message); 328 | } 329 | 330 | log.info('cloning the repo', 'git clone ' + data.gitrepo + ' ' + folder); 331 | 332 | exec('git clone ' + data.gitrepo + ' ' + folder, function (error, stdout, stderr) { 333 | 334 | if (error) { 335 | log.error(error); 336 | return; 337 | } 338 | 339 | var rcfile = config.writeApp(appname, folder); 340 | fs.writeFileSync(folder + '/.gitignore', rcfile + "\n"); 341 | log.info('ok!'.green.bold); 342 | }); 343 | 344 | }); 345 | } 346 | 347 | 348 | // Create the .appconfig file for {APPNAME} 349 | function setup (args) { 350 | if (!args.length) { 351 | log.error('appname required'); 352 | return; 353 | } 354 | config.writeApp(args[0]); 355 | } 356 | 357 | // Show info about the {APPNAME} 358 | function info (args) { 359 | config.check(); 360 | var appname = config.appname; 361 | if (args.length) { 362 | appname = args[0].toLowerCase(); 363 | } 364 | if (!appname){ 365 | log.error('appname required'); 366 | return; 367 | } 368 | log.info('Gathering information about:', appname); 369 | nodeapi.app_info(appname, function (err, data, original) { 370 | if (err) { 371 | log.error(err.message); 372 | return; 373 | } else if (data.status === 'failure - app not found (' + appname+')'){ 374 | log.error(data.status); 375 | return; 376 | } 377 | 378 | var l = 'info' 379 | , r = data.running 380 | ; 381 | 382 | var state = false; 383 | if (data.running && data.running.hasOwnProperty('indexOf') && (data.running.indexOf('error') >-1|| data.running.indexOf('failed-to') > -1)) 384 | state = true; 385 | if (data.running === false || state) { 386 | l = 'warn'; 387 | if (r === false) { 388 | r = 'false'; 389 | } 390 | r = r.red; 391 | } 392 | var pid = ''; 393 | if (data.pid) { 394 | pid = '(pid: ' + data.pid + ')'; 395 | } 396 | if (!r) r = 'null'; 397 | log[l](appname, 'on port', data.port, 'running:', r.bold, pid); 398 | log.info('gitrepo:', data.gitrepo); 399 | log.info('appfile:', data.start); 400 | log.info('ok!'.green.bold); 401 | }); 402 | } 403 | 404 | // Start the app 405 | function start (args) { 406 | config.check(); 407 | var appname = config.appname; 408 | if (args.length) { 409 | appname = args[0].toLowerCase(); 410 | } 411 | 412 | if (!appname){ 413 | log.error('appname required'); 414 | return; 415 | } 416 | 417 | log.info('Attemping to start app:', appname); 418 | nodeapi.app_start(appname, function (err, data, original) { 419 | if (err) { 420 | log.error(err.message); 421 | } 422 | if (data.status == "success") { 423 | log.info('app started.'.bold.green); 424 | log.info('ok!'.green.bold); 425 | } else { 426 | log.warn(data.status); 427 | } 428 | }); 429 | } 430 | 431 | // Stop the app in params 432 | function stop (args) { 433 | config.check(); 434 | var appname = config.appname; 435 | if (args.length) { 436 | appname = args[0].toLowerCase(); 437 | } 438 | 439 | if (!appname){ 440 | log.error('appname required'); 441 | return; 442 | } 443 | 444 | log.info('Attemping to stop app:', appname); 445 | 446 | nodeapi.app_stop(appname, function (err, data, original) { 447 | if (err) { 448 | log.error(err.message); 449 | return; 450 | } 451 | if (data.status == "success") { 452 | log.info('app stopped.'); 453 | log.info('ok!'.green.bold); 454 | } else { 455 | log.warn(data.status); 456 | } 457 | }); 458 | } 459 | 460 | // Restart the app 461 | function restart (args) { 462 | config.check(); 463 | var appname = config.appname; 464 | if (args.length) { 465 | appname = args[0].toLowerCase(); 466 | } 467 | 468 | if (!appname){ 469 | log.error('appname required'); 470 | return; 471 | } 472 | 473 | log.info('Attemping to restart app:', appname); 474 | 475 | nodeapi.app_restart(appname, function (err, data, original) { 476 | if (err) { 477 | log.error(err.message); 478 | } 479 | if (data.status == "success") { 480 | log.info('app restarted.'.bold.green); 481 | log.info('ok!'.green.bold); 482 | } else { 483 | log.warn(data.status); 484 | } 485 | }); 486 | } 487 | 488 | // Get the logs from the running {APPNAME} 489 | function logs (args) { 490 | config.check(); 491 | var appname = config.appname; 492 | if (args.length) { 493 | appname = args[0].toLowerCase(); 494 | } 495 | 496 | if (!appname){ 497 | log.error('appname required'); 498 | return; 499 | } 500 | 501 | nodeapi.app_logs(appname, function (err, data, original) { 502 | if (err) { 503 | log.error(err.message); 504 | } 505 | if (data.lines && data.lines.length && data.lines[0] !== '') { 506 | log.info('Showing logs for:', appname); 507 | data.lines.forEach(function (l) { 508 | log.plain(l); 509 | }); 510 | log.info('ok!'.green.bold); 511 | } else { 512 | log.warn('no log data returned.'); 513 | } 514 | }); 515 | } 516 | 517 | // Gitreset #nuffsaid 518 | function gitreset (args) { 519 | config.check(); 520 | var appname = config.appname; 521 | if (args.length) { 522 | appname = args[0].toLowerCase(); 523 | } 524 | 525 | if (!appname){ 526 | log.error('appname required'); 527 | return; 528 | } 529 | 530 | log.warn('resetting app:', appname); 531 | 532 | nodeapi.app_gitreset(appname, function (err, data, original) { 533 | if (err) { 534 | log.error(err.message); 535 | } 536 | if (data.status == "success") { 537 | log.warn('successfully reset app', appname.bold); 538 | log.info('ok!'.green.bold); 539 | } else { 540 | log.error(data.status); 541 | } 542 | }); 543 | } 544 | 545 | // Returns the list of apps 546 | 547 | function list (args) { 548 | require(__dirname + '/apps').list(args); 549 | } 550 | 551 | // 552 | function changeAppStartFile (args) { 553 | config.check(); 554 | var appname = config.appname, 555 | p = args; 556 | 557 | if (args.length && !appname || args.length === 2) { 558 | appname = args[0]; 559 | p = args.splice(1); 560 | } 561 | 562 | if (!appname){ 563 | log.error('appname required'); 564 | return; 565 | } 566 | if (!p[0] || !p.length) return log.error('new start file is required'); 567 | log.warn('Editing app start file for:', appname); 568 | nodeapi.app_edit(appname, p[0] , function (err, data, raw){ 569 | if (err) return log.error(err.message); 570 | if (data.status === 'success') { 571 | log.info('Your new start file is:', data.start); 572 | log.info('Attemping to restart app:', appname); 573 | nodeapi.app_restart(appname, function (error, data2, original) { 574 | if (err) { 575 | return log.error(err.message); 576 | } 577 | if (data2.status == "success") { 578 | log.info('app restarted.'.bold.green); 579 | log.info('ok!'.green.bold); 580 | } else { 581 | log.warn(data2.status.red); 582 | } 583 | }); 584 | } else { 585 | log.warn(data.status); 586 | } 587 | }); 588 | } 589 | 590 | // Only exports one time 591 | 592 | var App = { 593 | usage : usage, 594 | setup : setup, 595 | info : info, 596 | logs : logs, 597 | stop : stop, 598 | start : start, 599 | restart : restart, 600 | gitreset : gitreset, 601 | destroy : destroy, 602 | create : create, 603 | init : init, 604 | clone : clone, 605 | list : list, 606 | edit : changeAppStartFile 607 | }; 608 | 609 | // Shorthands 610 | App.u = App.usage; 611 | App.s = App.setup; 612 | App.i = App.info; 613 | App.l = App.logs; 614 | App.r = App.restart; 615 | App.git = App.gitreset; 616 | App.d = App.destroy; 617 | App.c = App.create; 618 | 619 | module.exports = App; 620 | -------------------------------------------------------------------------------- /lib/appdomain.js: -------------------------------------------------------------------------------- 1 | /* 2 | * nodester-cli 3 | * A CLI tool to allow interaction with the http://nodester.com/ platform. 4 | * @division appdomain 5 | */ 6 | 7 | "use strict"; 8 | 9 | /*jshint node:true, noempty:true, laxcomma:true, laxbreak:false */ 10 | 11 | var Node = require('nodester-api').nodester 12 | , config = require('./config') 13 | , log = require('./log') 14 | ; 15 | 16 | 17 | 18 | function restartApp (appname) { 19 | config.check(); 20 | var nodeapi = new Node(config.username, config.password, config.apihost, config.apisecure); 21 | log.info('Attemping to restart app:', appname); 22 | 23 | nodeapi.app_restart(appname, function (err, data, original) { 24 | if (err) { 25 | log.error(err.message); 26 | log.error('Failed to restart...'); 27 | return; 28 | } 29 | if (data.status == "success") { 30 | log.info('app restarted.'.bold.green); 31 | log.info('ok!'.green.bold); 32 | } else { 33 | log.warn(data.status); 34 | } 35 | }); 36 | } 37 | 38 | function usage () { 39 | log.info(''); 40 | log.info(' `nodester appdomain `'.bold + ' it\'t a shortcut to `nodester domain`'); 41 | log.info(' Useful to manage your custom domains.'); 42 | log.info(' In a configured app dir, is optional. (domain aliases appdomain)'); 43 | log.info(' The availble commands are: (Prefixed with nodester ) '); 44 | log.usage(''); 45 | 46 | log.usage('\tdomain add|create \t- Add a domain router for this app'); 47 | log.usage('\tdomain remove|delete \t- Remove a domain router from this app'); 48 | log.usage('\tdomain \t\t\t\t\t\t- List domains'); 49 | 50 | log.usage(''); 51 | log.info('OK!'.green.bold); 52 | } 53 | 54 | function add (args) { 55 | config.check(); 56 | var nodeapi = new Node(config.username, config.password, config.apihost, config.apisecure); 57 | 58 | var appname = config.appname, 59 | domain; 60 | 61 | if (args.length) { 62 | if (args.length === 2) { 63 | domain = args[1]; 64 | appname = args[0]; 65 | } else { 66 | domain = args[0]; 67 | } 68 | } 69 | if (!domain) { 70 | log.error(' required'); 71 | return; 72 | } 73 | if (!appname) { 74 | log.error(' name required'); 75 | return; 76 | } 77 | 78 | log.info('adding domain', domain, 'to', appname); 79 | 80 | nodeapi.appdomain_add(appname, domain, function (err, data, original) { 81 | if (err) { 82 | log.error(err.message); 83 | } 84 | if (data.status == 'success') { 85 | log.info(data.message); 86 | restartApp(appname); 87 | log.info('OK!'.green.bold); 88 | } else { 89 | log.warn(original); 90 | } 91 | }); 92 | } 93 | 94 | /*function create (args) { 95 | Nodester.add.call(this, args); 96 | }*/ 97 | 98 | /*Nodester.remove = function (args) { 99 | Nodester.delete.call(this, args); 100 | }; */ 101 | 102 | function remove (args) { 103 | config.check(); 104 | var nodeapi = new Node(config.username, config.password, config.apihost, config.apisecure); 105 | var appname = config.appname, 106 | domain; 107 | 108 | if (args.length) { 109 | if (args.length === 2) { 110 | domain = args[1]; 111 | appname = args[0]; 112 | } else { 113 | domain = args[0]; 114 | } 115 | } 116 | 117 | if (!domain) { 118 | log.error(' required'); 119 | return; 120 | } 121 | 122 | if (!appname) { 123 | log.error(' name required'); 124 | return; 125 | } 126 | 127 | log.info('removing domain', domain, 'from', appname); 128 | 129 | nodeapi.appdomain_delete(appname, domain, function (err, data, original) { 130 | if (err) { 131 | log.error(err.message); 132 | } 133 | if (data.status == 'success') { 134 | log.info(data.message); 135 | restartApp(appname); 136 | } else { 137 | log.warn(original); 138 | } 139 | }); 140 | setTimeout(function () { 141 | log.error('No response'); 142 | }, 1000*10); 143 | } 144 | 145 | function list () { 146 | config.check(); 147 | var nodeapi = new Node(config.username, config.password, config.apihost, config.apisecure); 148 | log.info('fetching your list of domain aliases'); 149 | 150 | nodeapi.appdomains(function (err, data, original) { 151 | if (err) { 152 | log.error(err); 153 | } 154 | 155 | if (data.length > 0) { 156 | 157 | data.forEach(function (i) { 158 | log.info(i.domain.white, 'aliased to app', i.appname.white, 'running on port', i.port); 159 | }); 160 | log.info('OK!'.green.bold); 161 | 162 | } else { 163 | log.warn('no app domains to report'); 164 | } 165 | }); 166 | } 167 | 168 | var Domains = { 169 | usage : usage, 170 | add : add, 171 | create: add, 172 | remove: remove, 173 | run : usage, 174 | list : list 175 | }; 176 | 177 | // Shorthands 178 | Domains.a = Domains.add; 179 | Domains.c = Domains.create; 180 | Domains.r = Domains.remove; 181 | Domains.l = Domains.l; 182 | 183 | module.exports = Domains; 184 | -------------------------------------------------------------------------------- /lib/apps.js: -------------------------------------------------------------------------------- 1 | /* 2 | * nodester-cli 3 | * A CLI tool to allow interaction with the http://nodester.com/ platform. 4 | * @division apps 5 | */ 6 | /*jshint node:true, noempty:true, laxcomma:true, laxbreak:false */ 7 | 8 | "use strict"; 9 | 10 | var Node = require('nodester-api').nodester 11 | , config = require('./config') 12 | , log = require('./log') 13 | , Table = require('cli-table') 14 | ; 15 | 16 | function usage () { 17 | log.info(''); 18 | log.info('`nodester apps`'.bold + ' Shortcut to `nodester app list`'); 19 | log.usage(''); 20 | log.usage('\tapps list \t- list all your registered apps (shortcut for `' + config.brand + ' app list`)'); 21 | log.usage(''); 22 | log.info('ok!'.green.bold); 23 | } 24 | 25 | function list (args) { 26 | config.check(); 27 | log.info('Listing ' + config.username + '\'s apps.'); 28 | log.info('Waiting for the response...'); 29 | config.check(); 30 | var nodeapi = new Node(config.username, config.password, config.apihost, config.apisecure); 31 | nodeapi.apps_list(function (err, data, original) { 32 | if (err) { 33 | log.error(err.message); 34 | } 35 | if (data.length > 0) { 36 | 37 | var table = new Table({ 38 | head: ['No.', 'Name','Port', 'Status (running) '] 39 | , colWidths: [5, 30, 8, 30] 40 | }); 41 | 42 | for (var i in data) { 43 | var l = 'info', 44 | r = data[i].running; 45 | var state = false; 46 | if (data[i].running.hasOwnProperty('indexOf') && (data[i].running.indexOf('error') >-1|| data[i].running.indexOf('failed-to') > -1)) 47 | state = true; 48 | if (data[i].running === false || data[i].running == 'false' || state) { 49 | l = 'warn'; 50 | if (r === false || r == 'false') { 51 | r = 'false'; 52 | } 53 | r = r.red; 54 | } 55 | 56 | table.push([ parseInt(i,10) + 1, data[i].name, data[i].port, r.bold]); 57 | } 58 | log.info('Response: '); 59 | console.log(table.toString() +'\n'); 60 | log.info('ok!'.green.bold); 61 | } else { 62 | log.warn('no apps to report'); 63 | } 64 | }); 65 | } 66 | 67 | var Apps = { 68 | usage: usage, 69 | run: list, 70 | list: list 71 | }; 72 | 73 | module.exports = Apps; -------------------------------------------------------------------------------- /lib/client.js: -------------------------------------------------------------------------------- 1 | /* 2 | * nodester-cli 3 | * A CLI tool to allow interaction with the http://nodester.com/ platform. 4 | * @division clietn 5 | */ 6 | /*jshint node:true, noempty:true, laxcomma:true, laxbreak:false */ 7 | 8 | "use strict"; 9 | 10 | // node libraries 11 | var fs = require('fs') 12 | , path = require('path') 13 | , crypto = require('crypto') 14 | , resolve = path.resolve 15 | ; 16 | 17 | // Custom libs 18 | var Node = require('nodester-api').nodester 19 | , config = require('./config') 20 | , log = require('./log') 21 | , Table = require('cli-table') 22 | ; 23 | 24 | function usage () { 25 | log.info(''); 26 | log.info('`nodester client`'.bold + ' setup your own client`'); 27 | log.usage(''); 28 | log.usage('\tclient set \t- is the api endpoint of your nodester personal cloud and is the name of that cloud'); 29 | log.usage('\tclient get\t- get current configuration') 30 | log.usage(''); 31 | log.info('ok!'.green.bold); 32 | } 33 | 34 | 35 | 36 | // Config Helper 37 | // @api public 38 | // Uso: 39 | // var config = new Config(); // you can pass a config file in json format 40 | // config.get(); // gets the whole config file 41 | // config.get('version'); // gets the version if exists 42 | // config.set('version','0.0.1'); //sets the version to 0.0.1 43 | 44 | 45 | function Config (configFile) { 46 | this.packUrl = resolve(__dirname ,'../config.json'); 47 | if (configFile) { 48 | this.packUrl = resolve(__dirname, configFile); 49 | } 50 | this.configFile = require(this.packUrl); 51 | } 52 | 53 | Config.prototype.get = function(key){ 54 | if (!key) return this.configFile; 55 | return this.configFile[key]; 56 | }; 57 | 58 | Config.prototype.reload = function() { 59 | var self = this; 60 | try { 61 | fs.writeFileSync(self.packUrl, JSON.stringify(self.configFile, null, 2)); 62 | delete require.cache[require.resolve(self.packUrl)]; 63 | self.configFile = require(self.packUrl); 64 | return true; 65 | } catch (excp) { 66 | return false; 67 | } 68 | }; 69 | 70 | Config.prototype.set = function(key, value, cb){ 71 | this.configFile[key] = value; 72 | return this.reload(); 73 | }; 74 | 75 | Config.prototype.del = function(key) { 76 | delete this.configFile[key]; 77 | return this.reload(); 78 | }; 79 | 80 | var config = new Config(); 81 | var Client = { 82 | usage: usage, 83 | get: function (arg) { 84 | var current = config.get(); 85 | log.info('Current client:'); 86 | log.info('\tAPI endpoint \t-', current.apihost); 87 | log.info('\tBrand \t\t-', current.brand); 88 | log.info('ok!'.green) 89 | return; 90 | }, 91 | set: function (args) { 92 | if (!args) return log.error(' No args'); 93 | if (args.length < 2) return log.error('no enough args') 94 | var endpoint = args[0], 95 | brand = args[1]; 96 | if (config.set('apihost',endpoint) && config.set('brand',brand)) { 97 | log.info(' Saved'); 98 | log.info('ok!'.green); 99 | } else { 100 | log.error('failed to setup new creds'); 101 | } 102 | } 103 | } 104 | module.exports = Client -------------------------------------------------------------------------------- /lib/commands.js: -------------------------------------------------------------------------------- 1 | /* 2 | * nodester-cli 3 | * A CLI tool to allow interaction with the http://nodester.com/ platform. 4 | * @division commands 5 | */ 6 | /*jshint node:true, noempty:true, laxcomma:true, laxbreak:false */ 7 | 8 | "use strict"; 9 | 10 | var fs = require('fs') 11 | , path = require('path') 12 | , log = require('./log') 13 | , env = process.env 14 | , cfg = process.nodester || (process.nodester = {}) 15 | ; 16 | 17 | process.argv = process.argv.slice(2); 18 | 19 | if (env.NODESTER_APIHOST) { 20 | cfg.apihost = env.NODESTER_APIHOST; 21 | } 22 | 23 | if (env.NODESTER_APISECURE) { 24 | cfg.apisecure = true; 25 | } 26 | 27 | if (env.NODESTER_BRAND) { 28 | cfg.brand = env.NODESTER_BRAND; 29 | } 30 | 31 | 32 | var defaults = { 33 | apisecure: false, 34 | apihost: 'api.nodester.com', 35 | brand: 'nodester', 36 | appname: '', 37 | config: { 38 | username: '', 39 | password: '' 40 | } 41 | }; 42 | 43 | for (var i in defaults) { 44 | if (!cfg[i]) { 45 | cfg[i] = defaults[i]; 46 | } 47 | } 48 | 49 | 50 | try { 51 | var pack = require(path.join(__dirname, '..', 'package.json')); 52 | } catch (e) { 53 | var pack = { 54 | version: 'unknown' 55 | }; 56 | } 57 | 58 | require('./config').parse(); 59 | 60 | 61 | 62 | var commands = { 63 | status : require('./status'), 64 | coupon : require('./coupon'), 65 | apps : require('./apps'), 66 | app : require('./app'), 67 | user : require('./user'), 68 | appdomain : require('./appdomain'), 69 | domain : require('./appdomain'), 70 | npm : require('./npm'), 71 | appnpm : require('./npm'), 72 | env : require('./env'), 73 | client : require('./client'), 74 | version : { 75 | run: function() { 76 | log.plain(pack.version); 77 | } 78 | }, 79 | help: { 80 | usage: function(args) { 81 | if (args && args[0] && args[0].toLowerCase() === 'all') { 82 | showUsage(); 83 | } else { 84 | showHelp(args); 85 | } 86 | } 87 | }, 88 | authors: { 89 | usage: function(){ 90 | log.plain(["", 91 | ' _ _ ', 92 | ' _ __ ___ __| | ___ ___| |_ ___ _ __ ', 93 | " | '_ \\ / _ \\ / _ |/ _ \\ __| __/ _ \\ '__| ", 94 | ' | | | | (_) | (_| | __\\__ \\ |_ __/ | ', 95 | ' |_| |_|\\___/ \\__,_|\\___|___/\\__\\___|_| '].join('\n').bold.yellow+ 96 | '\n\n'+ 97 | ' Open Source Node.js Hosting Platform. \n'.bold.white+ 98 | ' http://github.com/nodester"\n'); 99 | 100 | if (typeof pack.author === 'object'){ 101 | pack.author = pack.author.name + ' ' + pack.author.email; 102 | } 103 | log.plain(' * ' + pack.author); 104 | pack.contributors.sort().forEach(function(author){ 105 | if (typeof author === 'object') { 106 | author = author.name + ' ' + author.email; 107 | } 108 | log.plain(' * ' + author); 109 | }); 110 | log.plain('\n'); 111 | log.info(' Bug report on: http://github.com/nodester/nodester-cli/issues'); 112 | log.info('ok!'.green.bold); 113 | } 114 | } 115 | }; 116 | 117 | var versions = ['v','--v','-v','-version','--version','VERSION']; 118 | 119 | for (var key in versions){ 120 | commands[versions[key]] = commands.version; 121 | } 122 | 123 | function run (cmds, command) { 124 | function tail (args){ 125 | if(log.inTest){ 126 | // mocha args 127 | 128 | args.pop(); 129 | args.pop(); 130 | } 131 | return args; 132 | } 133 | 134 | if (!command) { 135 | showHelp(commands); 136 | process.exit(1); 137 | } 138 | 139 | if (!cmds[command] && cfg.appname) { 140 | command = 'app'; 141 | process.argv.unshift('app'); 142 | } 143 | 144 | if (cmds[command]) { 145 | 146 | if (cmds[command][process.argv.slice(1)[0]]) { 147 | cmds[command][process.argv.slice(1)[0]](tail(process.argv.slice(2))); 148 | } else if (cmds[command].run) { 149 | cmds[command].run(tail(process.argv.slice(1)) || 'usage'); 150 | } else { 151 | var args = tail(process.argv.slice(1)); 152 | 153 | if (args.length && !cmds[args]){ 154 | log.warn('Run > nodester '+ command + ' usage '); 155 | log.error('command not found: ' + args); 156 | } else if (!log.inTest) { 157 | if (command == 'help' && args){ 158 | return cmds.help.usage(args); 159 | } else { 160 | log.info(command, 'usage:'); 161 | return cmds[command] && cmds[command].usage && cmds[command].usage(); 162 | } 163 | } else { 164 | log.info('Dieing cleanly'); 165 | } 166 | } 167 | } else { 168 | log.warn('Run > nodester '+ command + ' usage '); 169 | log.error('command not found: ' + command); 170 | } 171 | } 172 | 173 | function showHelp (args) { 174 | 175 | var cmds = commands; 176 | var target = cmds[args[0]]; 177 | if (args && target && (target.usage || target.run)) { 178 | return (target.usage && target.usage()) || (target.run && target.run()); 179 | } else { 180 | log.info(''); 181 | log.info(' \t\t\tNodester'.bold.yellow); 182 | log.info(' Open Source Node.js Hosting Platform.'); 183 | log.info(' showing all available commands:'); 184 | log.info(''); 185 | 186 | for (var i in cmds) { 187 | if (cmds[i].usage) { 188 | log.info('\t' + process.nodester.brand.grey + ' ' + i); 189 | } 190 | } 191 | log.info(''); 192 | log.info('For more help, type', cfg.brand, 'help '); 193 | log.info('ok!'.green); 194 | } 195 | } 196 | 197 | function showUsage () { 198 | log.info('show usage'); 199 | var cmds = commands; 200 | for (var i in cmds) { 201 | if (cmds[i].usage) { 202 | cmds[i].usage(); 203 | } 204 | } 205 | } 206 | 207 | module.exports = { 208 | commands : commands, 209 | run : run, 210 | showHelp : showHelp, 211 | log : log 212 | }; 213 | -------------------------------------------------------------------------------- /lib/config.js: -------------------------------------------------------------------------------- 1 | /* 2 | * nodester-cli 3 | * A CLI tool to allow interaction with the http://nodester.com/ platform. 4 | * @division config 5 | */ 6 | /*jshint node:true, noempty:true, laxcomma:true, laxbreak:false */ 7 | 8 | "use strict"; 9 | 10 | var fs = require('fs') 11 | , path = require('path') 12 | , exists = fs.existsSync || path.existsSync 13 | , crypto = require("crypto") 14 | , iniparser = require('iniparser') 15 | , log = require('./log') 16 | ; 17 | 18 | // Helpers 19 | 20 | var getKey = function(warn) { 21 | var ssh = path.join(process.env.HOME, ".ssh") 22 | , data = '' 23 | , keys = [ 24 | path.join(ssh, "id_dsa"), 25 | path.join(ssh, "id_rsa"), 26 | path.join(ssh, "identity") 27 | ] 28 | ; 29 | 30 | keys.some(function(v) { 31 | if (exists(v)) { 32 | data = fs.readFileSync(v, 'utf8') + ''; 33 | return true; 34 | } 35 | }); 36 | 37 | if (data === '' && warn) { 38 | log.warn('No key file found, encrypt is not going to be strong.'); 39 | } 40 | 41 | return data; 42 | }; 43 | 44 | 45 | var convertConfig = function(config_file) { 46 | var config = iniparser.parseSync(config_file) 47 | , str = config.username + ':' + config.password 48 | , x = cryptAuth(str) 49 | , new_config_file = path.join(process.env.HOME, "." + process.nodester.brand + '.rc') 50 | , out = 'auth=' + x + '\n' 51 | ; 52 | 53 | fs.writeFileSync(new_config_file, out, 'utf8'); 54 | log.warn('saved new config file: ' + new_config_file); 55 | log.warn('removing old config file'); 56 | fs.unlinkSync(config_file); 57 | }; 58 | 59 | /** 60 | * Based heavily on NPM's _authCrypt 61 | * https://github.com/isaacs/npm/blob/master/lib/utils/ini.js 62 | */ 63 | 64 | var decryptAuth = function(str) { 65 | var auth ; 66 | if (crypto.Decipher) { 67 | var key = getKey() 68 | , c = (new crypto.Decipher()).init("aes192", key); 69 | auth = c.update(str, "hex", "utf8"); 70 | 71 | auth += c.final("utf8"); 72 | } else { 73 | auth = str; 74 | } 75 | 76 | var b = new Buffer(auth, 'base64'); 77 | return b.toString('utf8'); 78 | }; 79 | 80 | var cryptAuth = function(str) { 81 | str = new Buffer(str).toString('base64'); 82 | var Crypt; 83 | if (crypto.Cipher) { 84 | var key = getKey(true); 85 | var c = (new crypto.Cipher()).init("aes192", key); 86 | Crypt = c.update(str, "utf8", "hex"); 87 | Crypt += c.final("hex"); 88 | } else { 89 | Crypt = str; 90 | } 91 | return Crypt; 92 | }; 93 | 94 | exports.check = function() { 95 | if (!process.nodester.config.username || !process.nodester.config.password) { 96 | log.error("Username and password not set in config.\nPlease run " + process.nodester.brand + " user setup \n"); 97 | } 98 | }; 99 | 100 | exports.__defineGetter__('username', function() { 101 | return process.nodester.config.username; 102 | }); 103 | 104 | exports.__defineGetter__('password', function() { 105 | return process.nodester.config.password; 106 | }); 107 | 108 | exports.__defineGetter__('apihost', function() { 109 | return process.nodester.apihost; 110 | }); 111 | 112 | exports.__defineGetter__('apisecure', function() { 113 | return process.nodester.apisecure; 114 | }); 115 | 116 | exports.__defineGetter__('brand', function() { 117 | return process.nodester.brand; 118 | }); 119 | 120 | exports.__defineGetter__('appname', function() { 121 | return process.nodester.appname.toLowerCase(); 122 | }); 123 | 124 | exports.writeUser = function(user, pass) { 125 | 126 | var config_file = path.join(process.env.HOME, "." + process.nodester.brand + '.rc') 127 | , str = user + ':' + pass 128 | , x = cryptAuth(str) 129 | , out = 'auth=' + x + '\n' 130 | ; 131 | 132 | log.info('writing user data to config'); 133 | fs.writeFileSync(config_file, out, 'utf8'); 134 | return config_file; 135 | }; 136 | 137 | exports.writeApp = function(appname, folder) { 138 | if (!folder) { 139 | folder = ''; 140 | } else if(!exists(folder)) { 141 | fs.mkdirSync(folder, '0777'); 142 | } 143 | 144 | var config_file = path.join("./" + folder, "." + process.nodester.brand + ".appconfig"); 145 | log.info('Writing app data to config in ' + config_file); 146 | fs.writeFileSync(config_file, "appname=" + appname.toLowerCase() + "\n"); 147 | log.info('OK!'.green.bold); 148 | return "." + process.nodester.brand + ".appconfig"; 149 | }; 150 | 151 | exports.parse = function() { 152 | var config 153 | , old_config_file = path.join(process.env.HOME, "." + process.nodester.brand + "rc") 154 | ; 155 | 156 | if (exists(old_config_file)) { 157 | log.warn('old config file found, converting..'); 158 | convertConfig(old_config_file); 159 | } 160 | 161 | var config_file = path.join(process.env.HOME, "." + process.nodester.brand + '.rc'); 162 | if (exists(config_file)) { 163 | config = iniparser.parseSync(config_file); 164 | 165 | var a = decryptAuth(config.auth).split(':'); 166 | process.nodester.config.username = a[0]; 167 | process.nodester.config.password = a[1]; 168 | 169 | } 170 | 171 | var apprcfile = "." + process.nodester.brand + ".appconfig"; 172 | if (exists(apprcfile)) { 173 | config = iniparser.parseSync(apprcfile); 174 | process.nodester.appname = config.appname.toLowerCase(); 175 | } 176 | }; 177 | -------------------------------------------------------------------------------- /lib/coupon.js: -------------------------------------------------------------------------------- 1 | /* 2 | * nodester-cli 3 | * A CLI tool to allow interaction with the http://nodester.com/ platform. 4 | * @division coupon 5 | */ 6 | /*jshint node:true, noempty:true, laxcomma:true, laxbreak:false */ 7 | 8 | "use strict"; 9 | 10 | var Node = require('nodester-api').nodester 11 | , config = require('./config') 12 | , log = require('./log') 13 | ; 14 | 15 | 16 | function usage () { 17 | log.info(''); 18 | log.info('`nodester coupon`'.bold + ' Request your coupon!'); 19 | log.usage(''); 20 | log.usage('\tcoupon \t- Request a coupon code for access'); 21 | log.usage(''); 22 | log.info('ok!'.green); 23 | } 24 | 25 | function run (args) { 26 | if (!args.length) { 27 | usage(); 28 | return; 29 | } 30 | var email = args[0]; 31 | log.info('Requesting coupon code for: ' + email); 32 | var nodeapi = new Node("", "", process.nodester.apihost, config.apisecure); 33 | 34 | nodeapi.coupon_request(email, function (err, data, original) { 35 | if (err) { 36 | log.error(err.message); 37 | } 38 | var s = data.status; 39 | if (s.indexOf('success') === 0) { 40 | s = s.replace('success', ''); 41 | log.info('SUCCESS'.green.bold + s); 42 | } else { 43 | log.info(err.message); 44 | } 45 | }); 46 | } 47 | 48 | var Coupon = { 49 | usage: usage, 50 | run: run 51 | }; 52 | 53 | module.exports = Coupon; -------------------------------------------------------------------------------- /lib/env.js: -------------------------------------------------------------------------------- 1 | /* 2 | * nodester-cli 3 | * A CLI tool to allow interaction with the http://nodester.com/ platform. 4 | * @division env 5 | */ 6 | 7 | /*jshint node:true, noempty:true, laxcomma:true, laxbreak:false */ 8 | 9 | "use strict"; 10 | 11 | var fs = require('fs') 12 | , path = require('path') 13 | , Node = require('nodester-api').nodester 14 | , exists = fs.existsSync || path.existsSync 15 | , config = require('./config') 16 | , log = require('./log') 17 | ; 18 | 19 | 20 | var cfg = config; 21 | 22 | 23 | function usage () { 24 | log.info(''); 25 | log.info('`nodester env `'.bold + ' manage Environmental keys for your apps'); 26 | log.info(' Useful to manage sensible data like db passwords or API credentials'); 27 | log.usage('In a configured app dir, is optional.'); 28 | log.usage(''); 29 | log.usage('\tenv get \t\t- Gets the list of enviroment values configured for this app.'); 30 | log.usage('\tenv set - Creates/updates an environment key/pair for this app.'); 31 | log.usage('\tenv delete \t- Deletes an environment key/value pair for this app..'); 32 | log.usage(''); 33 | log.info('ok!'.green); 34 | } 35 | 36 | function get (args) { 37 | cfg.check(); 38 | var nodeapi = new Node(cfg.username, cfg.password, cfg.apihost, cfg.apisecure) 39 | , appname = config.appname; 40 | 41 | if (args.length) { 42 | appname = args[0].toLowerCase(); 43 | } 44 | 45 | log.info('Gathering enviroment variables for:', appname); 46 | nodeapi.env_get(appname, function (err, data, original) { 47 | if (err) { 48 | log.error(err.message); 49 | } 50 | for (var x in data.message) { 51 | log.info(x, '=', data.message[x]); 52 | } 53 | }); 54 | } 55 | 56 | function set (args) { 57 | cfg.check(); 58 | var nodeapi = new Node(cfg.username, cfg.password, cfg.apihost, cfg.apisecure) 59 | , appname = config.appname, 60 | p = args; 61 | 62 | if (args.length && !appname) { 63 | appname = args[0]; 64 | p = args.splice(1); 65 | } 66 | 67 | if (p.length < 2) { 68 | log.error('no key/value pair to set.'); 69 | } 70 | 71 | log.info('for app ' + appname + ', setting: ' + p[0] + '=' + p[1]); 72 | 73 | nodeapi.env_set(appname, p[0], p[1], function (err, data, original) { 74 | if (err) { 75 | log.error(err.message); 76 | } 77 | if (data.status == "success") { 78 | log.info('environment variable set.'); 79 | } else { 80 | log.warn(data.status); 81 | } 82 | }); 83 | } 84 | 85 | function destroy (args) { 86 | cfg.check(); 87 | var nodeapi = new Node(cfg.username, cfg.password, cfg.apihost, cfg.apisecure) 88 | , appname = config.appname 89 | , p = args; 90 | 91 | if (args.length && !appname) { 92 | appname = args[0]; 93 | p = args.splice(1); 94 | } 95 | if (p.length < 1) { 96 | log.error('no key to delete.'); 97 | } 98 | log.info('for app ' + appname + ', deleting environment variable: ' + p[0]); 99 | 100 | nodeapi.env_delete(appname, p[0], function (err, data, original) { 101 | if (err) { 102 | log.error(err.message); 103 | } 104 | if (data.status == "success") { 105 | log.info('environment variable deleted.'); 106 | } else { 107 | log.warn(data.status); 108 | } 109 | }); 110 | } 111 | 112 | var Env = { 113 | usage: usage, 114 | get: get, 115 | set: set, 116 | "delete": destroy 117 | }; 118 | 119 | // Shorthands 120 | Env.g = Env.get; 121 | Env.s = Env.set; 122 | Env.d = Env['delete']; 123 | 124 | module.exports = Env; -------------------------------------------------------------------------------- /lib/log.js: -------------------------------------------------------------------------------- 1 | 2 | /*jshint node:true, noempty:true, laxcomma:true, laxbreak:false */ 3 | 4 | "use strict"; 5 | 6 | require('colors'); 7 | 8 | var util = require('util') 9 | , level = process.env.NODESTER_LOGLEVEL || 0 10 | ; 11 | 12 | // https://github.com/joyent/node/blob/master/lib/util.js#L533 13 | // #lazyweb 14 | 15 | function extend (origin, add) { 16 | if (!add || typeof add !== 'object') return origin; 17 | 18 | var keys = Object.keys(add); 19 | var i = keys.length; 20 | while (i--) { 21 | origin[keys[i]] = add[keys[i]]; 22 | } 23 | return origin; 24 | } 25 | 26 | util._extend = util._extend || extend; 27 | 28 | var strArgs = function (args) { 29 | if (args.length > 1) { 30 | var str = []; 31 | for (var i in args) { 32 | str.push(args[i]); 33 | } 34 | return str.join(' '); 35 | } else { 36 | return args[0]; 37 | } 38 | }; 39 | 40 | 41 | var INTEST = process.nodester.env != 'production' 42 | , log = {} 43 | ; 44 | 45 | 46 | if (INTEST) { 47 | log = { 48 | log : function(arg){ 49 | return true; 50 | }, 51 | warn : function(arg){ 52 | return false; 53 | }, 54 | error: function(a,b,c){ 55 | throw new Error(c); 56 | } 57 | }; 58 | } else { 59 | util._extend(log, console); 60 | } 61 | 62 | exports.__defineGetter__('inTest', function() { 63 | return INTEST; 64 | }); 65 | 66 | exports.plain = function () { 67 | log.log(strArgs(arguments)); 68 | }; 69 | 70 | exports.usage = function () { 71 | log.log(process.nodester.brand.grey, strArgs(arguments)); 72 | }; 73 | 74 | exports.info = function () { 75 | log.log(process.nodester.brand.cyan, 'info'.white, strArgs(arguments)); 76 | }; 77 | 78 | exports.warn = function (str) { 79 | log.warn(process.nodester.brand.magenta, 'warn'.yellow, strArgs(arguments)); 80 | }; 81 | 82 | exports.error = function (str) { 83 | log.error(process.nodester.brand.magenta, 'ERROR'.red.inverse, strArgs(arguments)); 84 | log.error(process.nodester.brand.magenta,'not ok!'.red.bold); 85 | if (!INTEST) return process.kill(process.pid, 'SIGINT'); 86 | }; 87 | -------------------------------------------------------------------------------- /lib/npm.js: -------------------------------------------------------------------------------- 1 | /* 2 | * nodester-cli 3 | * A CLI tool to allow interaction with the http://nodester.com/ platform. 4 | * @division npm 5 | */ 6 | /*jshint node:true, noempty:true, laxcomma:true, laxbreak:false */ 7 | 8 | "use strict"; 9 | 10 | var fs = require('fs') 11 | , path = require('path') 12 | , Node = require('nodester-api').nodester 13 | , exists = fs.existsSync || path.existsSync 14 | , config = require('./config') 15 | , log = require('./log') 16 | ; 17 | 18 | 19 | 20 | function usage () { 21 | log.info(''); 22 | log.info('`nodester npm `'.bold + ' manage npm packages for your apps'); 23 | log.info(' In a configured app dir, is optional.'); 24 | log.info(' Available commands are:'); 25 | log.usage(''); 26 | log.usage('All arguments after install|update|uninstall or appname will be sent to npm as packages.'); 27 | log.usage(''); 28 | log.usage('\tnpm list \t\t\t\t- Lists the installed npm packages for this app.'); 29 | log.usage('\tnpm install \t- Installs the list of specified packages to this app.'); 30 | log.usage('\tnpm update \t- Update the list of specified packages to this app.'); 31 | log.usage('\tnpm uninstall \t- Removes the list of specified packages to this app.'); 32 | log.usage(''); 33 | log.info('ok!'.green); 34 | } 35 | 36 | function install (args) { 37 | config.check(); 38 | var appname = config.appname, 39 | p = args; 40 | 41 | if (args.length && !appname) { 42 | appname = args[0]; 43 | p = args.splice(1); 44 | } 45 | 46 | if (!p.length) { 47 | if (exists('package.json')) { 48 | log.info('grabbing dependencies from package.json...'); 49 | var depen = JSON.parse(fs.readFileSync('package.json')).dependencies; 50 | if (!depen) { 51 | log.error('no depedencies found!'); 52 | } else { 53 | p = []; 54 | for (var dep in depen) { 55 | var version = depen[dep] || null; 56 | p.push(dep + (version ? '@' + version : '')); 57 | } 58 | } 59 | } else { 60 | log.error('no packages to install!'); 61 | } 62 | } 63 | 64 | // fix to avoid the read of appname as a module 65 | if (p.hasOwnProperty('length')) { // double check for typeof array 66 | p = p.filter(function(pack){ 67 | return pack != appname; 68 | }); 69 | } 70 | 71 | log.info('installing to app:', appname); 72 | log.info('installing these npm packages:', p); 73 | var nodeapi = new Node(config.username, config.password, config.apihost, config.apisecure); 74 | nodeapi.appnpm_install(appname, p.join(' '), function (err, data, original) { 75 | if (err) { 76 | log.error(err.message); 77 | } 78 | if (data.output) { 79 | var out = data.output.split('\n'); 80 | out.forEach(function (l) { 81 | if (l.indexOf('stdout: ') === -1) { 82 | if (l.length > 1) { 83 | l = l.replace('stderr: ', ''); 84 | l = l.split(' '); 85 | l[0] = l[0].magenta; 86 | if (l[1]) { 87 | if (l[1].toLowerCase() === 'warn') { 88 | l[1] = l[1].red; 89 | } else if (l[1].toLowerCase() === 'erro') { 90 | l[1] = l[1].red.inverse.bold; 91 | } else { 92 | l[1] = l[1].white; 93 | } 94 | } 95 | log.usage(l.join(' ')); 96 | } 97 | } 98 | }); 99 | } 100 | log.plain(''); 101 | log.info('Dependencies installed'); 102 | log.info('Attemping to restart app:', appname); 103 | nodeapi.app_restart(appname, function (err, data, original) { 104 | if (err) { 105 | log.error(err.message); 106 | } 107 | if (data.status == "success") { 108 | log.info('app restarted.'.bold.green); 109 | log.info('ok!'.green.bold); 110 | } else { 111 | log.warn(data.status); 112 | } 113 | }); 114 | }); 115 | } 116 | 117 | function list (args) { 118 | config.check(); 119 | var appname = config.appname; 120 | if (args.length && !appname) { 121 | appname = args[0]; 122 | } 123 | log.info('list npm packages for app:', appname); 124 | var nodeapi = new Node(config.username, config.password, config.apihost, config.apisecure); 125 | nodeapi.appnpm_list(appname, function (err, data, original) { 126 | if (err) { 127 | log.error(err.message); 128 | } 129 | if (data.output) { 130 | var out = data.output.split('\n'); 131 | out.forEach(function (l) { 132 | if (l.indexOf('stdout: ') === -1) { 133 | if (l.length > 1) { 134 | l = l.replace('stderr: ', ''); 135 | l = l.split(' '); 136 | l[0] = l[0].magenta; 137 | if (l[1]) { 138 | l[1] = l[1].white; 139 | } 140 | log.usage(l.join(' ')); 141 | } 142 | } 143 | }); 144 | } 145 | log.plain(''); 146 | }); 147 | } 148 | 149 | function update (args) { 150 | config.check(); 151 | var appname = config.appname, 152 | p = args; 153 | 154 | if (args.length && !appname) { 155 | appname = args[0]; 156 | p = args.splice(1); 157 | } 158 | if (!p.length) { 159 | log.error('no packages to install'); 160 | } 161 | log.info('updating to app:', appname); 162 | log.info('updating these npm packages:', p); 163 | var nodeapi = new Node(config.username, config.password, config.apihost, config.apisecure); 164 | nodeapi.appnpm_update(appname, p.join(' '), function (err, data, original) { 165 | if (err) { 166 | log.error(err.message); 167 | } 168 | if (data.output) { 169 | var out = data.output.split('\n'); 170 | out.forEach(function (l) { 171 | if (l.indexOf('stdout: ') === -1) { 172 | if (l.length > 1) { 173 | l = l.replace('stderr: ', ''); 174 | l = l.split(' '); 175 | l[0] = l[0].magenta; 176 | if (l[1]) { 177 | l[1] = l[1].white; 178 | } 179 | log.usage(l.join(' ')); 180 | } 181 | } 182 | }); 183 | } 184 | log.plain(''); 185 | log.info('Dependencies installed'); 186 | log.info('Attemping to restart app:', appname); 187 | nodeapi.app_restart(appname, function (err, data, original) { 188 | if (err) { 189 | log.error(err.message); 190 | } 191 | if (data.status == "success") { 192 | log.info('app restarted.'.bold.green); 193 | log.info('ok!'.green.bold); 194 | } else { 195 | log.warn(data.status); 196 | } 197 | }); 198 | }); 199 | } 200 | 201 | function uninstall (args) { 202 | config.check(); 203 | var appname = config.appname, 204 | p = args; 205 | 206 | if (args.length && !appname) { 207 | appname = args[0]; 208 | p = args.splice(1); 209 | } 210 | if (!p.length) { 211 | log.error('no packages to install'); 212 | } 213 | log.info('removing to app:', appname); 214 | log.info('removing these npm packages:', p); 215 | var nodeapi = new Node(config.username, config.password, config.apihost, config.apisecure); 216 | nodeapi.appnpm_uninstall(appname, p.join(' '), function (err, data, original) { 217 | if (err) { 218 | log.error(err.message); 219 | } 220 | if (data.output) { 221 | var out = data.output.split('\n'); 222 | out.forEach(function (l) { 223 | if (l.indexOf('stdout: ') === -1) { 224 | if (l.length > 1) { 225 | l = l.replace('stderr: ', ''); 226 | l = l.split(' '); 227 | l[0] = l[0].magenta; 228 | if (l[1]) { 229 | l[1] = l[1].white; 230 | } 231 | log.usage(l.join(' ')); 232 | } 233 | } 234 | }); 235 | } 236 | }); 237 | } 238 | 239 | 240 | var npm = { 241 | usage: usage, 242 | install: install, 243 | list: list, 244 | update: update, 245 | uninstall: uninstall 246 | }; 247 | 248 | // Shorthands 249 | npm.i = npm.install; 250 | npm.l = npm.list; 251 | npm.u = npm.update; 252 | npm.un = npm.uninstall; 253 | 254 | module.exports = npm; 255 | -------------------------------------------------------------------------------- /lib/status.js: -------------------------------------------------------------------------------- 1 | /* 2 | * nodester-cli 3 | * A CLI tool to allow interaction with the http://nodester.com/ platform. 4 | * @division status 5 | */ 6 | 7 | /*jshint node:true, noempty:true, laxcomma:true, laxbreak:false */ 8 | 9 | "use strict"; 10 | 11 | var Node = require('nodester-api').nodester 12 | , config = require('./config') 13 | , log = require('./log') 14 | ; 15 | 16 | var Status = module.exports; 17 | 18 | function usage () { 19 | log.usage('status - Show', config.brand, 'API status'); 20 | } 21 | 22 | function run () { 23 | log.info('checking api status for:', config.apihost); 24 | var nodeapi = new Node('', '', config.apihost, config.apisecure); 25 | nodeapi.status(function (err, data, original) { 26 | if (err) { 27 | log.error(err.message); 28 | } 29 | for (var i in data) { 30 | log.info(i, data[i].toString().bold); 31 | } 32 | }); 33 | } 34 | 35 | var Status = { 36 | usage: usage, 37 | run: run 38 | }; 39 | 40 | module.exports = Status; -------------------------------------------------------------------------------- /lib/user.js: -------------------------------------------------------------------------------- 1 | /* 2 | * nodester-cli 3 | * A CLI tool to allow interaction with the http://nodester.com/ platform. 4 | * @division status 5 | */ 6 | 7 | /*jshint node:true, noempty:true, laxcomma:true, laxbreak:false */ 8 | 9 | "use strict"; 10 | 11 | var fs = require('fs') 12 | , path = require('path') 13 | , iniparser = require('iniparser') 14 | , Node = require('nodester-api').nodester 15 | , config = require('./config') 16 | , log = require('./log') 17 | , exists = fs.existsSync || path.existsSync 18 | , tty = tty = require('tty') 19 | ; 20 | 21 | 22 | tty.setRawMode = process.stdin.setRawMode || tty.setRawMode ; 23 | 24 | var info = { 25 | email: '', 26 | coupon: '', 27 | username: '', 28 | sshkey: '' 29 | }; 30 | 31 | 32 | 33 | function usage () { 34 | log.info(''); 35 | log.info('`nodester user `'.bold + ' manage your credentials with the nodester api or create a new user'); 36 | log.usage(''); 37 | log.usage('\tuser register \t\t- Register a user'); 38 | log.usage('\tuser setup \t- Setup this user'); 39 | log.usage('\tuser setpass sendtoken \t\t\t- Sends password reset token to user email'); 40 | log.usage('\tuser setpass \t- Set a new password for this user'); 41 | log.usage('\tuser setkey \t\t- Set an sshkey (if no argument, ~/.ssh/id_rsa.pub is used)'); 42 | log.usage('\tuser create - Create a user'); 43 | log.usage(''); 44 | log.info('ok!'.green); 45 | } 46 | 47 | function create (args) { 48 | if (args.length < 5) { 49 | log.usage('user create - Create a user'); 50 | log.error('All arguments are required'); 51 | } 52 | 53 | var user = args[0] 54 | , pass = args[1] 55 | , email = args[2] 56 | // , rsakey = args[3] 57 | // , coupon = args[4] 58 | ; 59 | 60 | log.info('creating user:', user, ' <' + email + '>'); 61 | var nodeapi = new Node("", "", config.apihost, config.apisecure); 62 | args.push(function (err, data, original) { 63 | if (err) { 64 | 65 | log.error(err.message); 66 | } 67 | log.info('user successfully created'); 68 | config.writeUser(user, pass); 69 | }); 70 | nodeapi.user_create.apply(nodeapi, args); 71 | } 72 | 73 | function setKey (args) { 74 | config.check(); 75 | var key = args[0]; 76 | if (!key) { 77 | key = path.join(process.env.HOME, '.ssh', 'id_rsa.pub'); 78 | } 79 | if (!exists(key)) { 80 | log.error('sshkey was not found:', key); 81 | } 82 | var rsadata = fs.readFileSync(key).toString(); 83 | if (rsadata.length < 40) { 84 | log.error("Invalid SSH key file."); 85 | } 86 | 87 | log.info('sending sshkey:', key); 88 | var nodeapi = new Node(config.username, config.password, config.apihost, config.apisecure); 89 | nodeapi.user_setkey(rsadata, function (err, data, original) { 90 | if (err) { 91 | log.error(err.message); 92 | } 93 | log.info('ssh key successfully sent'); 94 | }); 95 | } 96 | 97 | function setPass (args) { 98 | var username, pass; 99 | if (args.length == 1 && args[0] == 'sendtoken'){ 100 | return log.error('You need to provide a valid username'); 101 | } else if (args.length == 2 && args[0] == 'sendtoken') { 102 | username = args[1]; 103 | } else { 104 | config.check(); 105 | username = config.username; 106 | } 107 | var nodeapi = new Node(username, config.apihost, config.apisecure); 108 | if (args.length == 1 && args[0] == 'sendtoken') { 109 | nodeapi.user_sendtoken(username, function (err, data, original) { 110 | if (err && !data) { 111 | log.error(err.message); 112 | } else { 113 | log.info(data.status); 114 | log.info('token for setpass has been sent to your email!'); 115 | } 116 | }); 117 | } else if (args.length == 2 && (args[1] != '' || args[1] != ' ')) { 118 | nodeapi.user_setpass(args[0], args[1], function (err, data, original) { 119 | if (err) { 120 | log.error(err.message); 121 | } 122 | log.info('password successfully changed.'); 123 | config.writeUser(username, args[0]); 124 | }); 125 | } else { 126 | log.error('Argument missing: ', 'setpass sendtoken or setpass '); 127 | } 128 | } 129 | 130 | function setup (args) { 131 | // fix args 132 | args = args.map(function(v){ 133 | return typeof v === 'string' ? v.trim(): v; 134 | }).filter(Boolean); 135 | 136 | if (args.length < 2) { 137 | log.error('Argument missing: ', ' '); 138 | } 139 | 140 | var nodeapi = new Node(args[0], args[1], config.apihost, config.apisecure); 141 | log.info('verifying credentials'); 142 | nodeapi.apps_list(function (err, data, original) { 143 | if (err) { 144 | 145 | log.error(err.message); 146 | } 147 | log.info('user verified..'); 148 | config.writeUser(args[0], args[1]); 149 | }); 150 | } 151 | 152 | function register (args) { 153 | if (!args.length) { 154 | log.error('Coupon Code Required'); 155 | } 156 | if (args[1] && args[1].indexOf('@') > -1) { 157 | info.email = args[1]; 158 | } 159 | if (!info.email && exists(path.join(process.env.HOME, '.gitconfig'))) { 160 | var git = iniparser.parseSync(path.join(process.env.HOME, '.gitconfig')); 161 | info.email = git.user.email; 162 | } else if (!info.email) { 163 | log.error('Could not find an email address as an argument or from your ~/.gitconfig, please pass one as an additional argument'); 164 | } 165 | info.coupon = args[0]; 166 | info.username = process.env.USER; 167 | var sshkey = path.join(process.env.HOME, '.ssh', 'id_rsa.pub'); 168 | if (exists(sshkey)) { 169 | info.sshkey = sshkey; 170 | } else { 171 | log.error('Could not auto find your ssh key: ', sshkey, 'use <' + config.brand + ' user create> instead'); 172 | } 173 | log.info('Registering with the following information:'); 174 | for (var i in info) { 175 | log.info(i + ':', info[i]); 176 | } 177 | log.warn('Does this information look correct? [y/N]'); 178 | log.info('(If it does not, hit Ctrl+C and use <' + config.brand + ' user create> instead)'); 179 | var stdin = process.openStdin(); 180 | stdin.setEncoding('utf8'); 181 | stdin.addListener('data', function (data) { 182 | stdin.removeAllListeners('data'); 183 | data = data.replace('\n', '').toLowerCase().substring(0, 1); 184 | if (data === 'y') { 185 | askPass(function (password) { 186 | if (!password) return log.error('Invalid password'); 187 | info.password = password; 188 | log.info('creating user:', info.username, ' <' + info.email + '>'); 189 | var nodeapi = new Node("", "", config.apihost, config.apisecure); 190 | nodeapi.user_create(info.username, info.password, info.email, info.sshkey, info.coupon, function (err, data, original) { 191 | if (err) { 192 | 193 | log.error(err.message); 194 | } 195 | log.info('user created..'); 196 | config.writeUser(info.username, info.password); 197 | }); 198 | }); 199 | } else { 200 | log.error('aborting registration'); 201 | stdin.pause(); 202 | } 203 | }); 204 | } 205 | 206 | function askPass (fn) { 207 | var stdin = process.openStdin(); 208 | stdin.setEncoding('utf8'); 209 | var p = [], 210 | c = ''; 211 | console.log('Please enter your password:'); 212 | if (!tty) { 213 | log.warn('Node version (' + process.version + ') has no tty module, passwords will be echoed to stdout'); 214 | stdin.addListener('data', function (data) { 215 | data += ''; 216 | data = data.replace('\n', ''); 217 | p.push(data); 218 | if (p.length === 2) { 219 | if (p[0] !== p[1]) { 220 | p = []; 221 | log.warn('Passwords did not match, please try again.'); 222 | console.log('Please re-enter your password:'); 223 | } else { 224 | stdin.removeAllListeners('data'); 225 | stdin.pause(); 226 | fn(p[0]); 227 | } 228 | } else { 229 | console.log('Confirm password:'); 230 | } 231 | }); 232 | } else { 233 | if (!tty.setRawMode) tty.setRawMode = stdin.setRawMode; 234 | stdin.setRawMode(true); 235 | stdin.addListener('data', function (data) { 236 | data += ''; 237 | switch (data) { 238 | case '\u0003': 239 | log.error('exiting from Ctrl+C'); 240 | process.exit(); 241 | break; 242 | case '\n': 243 | case '\r': 244 | p.push(c); 245 | c = ''; 246 | if (p.length === 1) { 247 | console.log('\nConfirm password:'); 248 | } else { 249 | if (p[0] === p[1]) { 250 | stdin.removeAllListeners('data'); 251 | console.log('\n'); 252 | tty.setRawMode(false); 253 | stdin.pause(); 254 | fn(p[0]); 255 | } else { 256 | p = []; 257 | console.log('\n'); 258 | log.warn('Passwords did not match, please try again.'); 259 | console.log('Please re-enter your password:'); 260 | } 261 | } 262 | break; 263 | default: 264 | process.stdout.write("*"); 265 | c += data; 266 | } 267 | }); 268 | } 269 | } 270 | 271 | var User = { 272 | usage: usage, 273 | create: create, 274 | setkey: setKey, 275 | setpass: setPass, 276 | setup: setup, 277 | register: register, 278 | askpass: askPass 279 | }; 280 | 281 | module.exports = User; 282 | -------------------------------------------------------------------------------- /package.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "nodester-cli", 3 | "description": "A CLI tool to allow interaction with the http://nodester.com/ platform.", 4 | "version": "1.1.1", 5 | "homepage": "http://nodester.com/", 6 | "repository": "git://github.com/nodester/nodester-cli.git", 7 | "author": "Daniel Bartlett (http://danb-uk.net/)", 8 | "contributors": [ 9 | "Chris Matthieu ", 10 | "Dav Glass @davglass", 11 | "Abraham Williams <4braham@gmail.com>", 12 | "Contra ", 13 | "Marcos Oliveira ", 14 | "Alejandro Morales " 15 | ], 16 | "preferGlobal": "true", 17 | "bin": { 18 | "nodester": "./bin/nodester.js" 19 | }, 20 | "main": "./bin/nodester.js", 21 | "scripts": { 22 | "start": "node ./bin/nodester.js", 23 | "test": "mocha test/test-*.js -R spec" 24 | }, 25 | "engines": { 26 | "node": ">0.6.x =<0.8.x" 27 | }, 28 | "dependencies": { 29 | "nodester-api": "latest", 30 | "colors": "0.3.0", 31 | "iniparser": "1.0.1", 32 | "read": "~0.1.0", 33 | "cli-table": "0.0.2" 34 | }, 35 | "devDependencies": { 36 | "mocha": "~1.0.3" 37 | } 38 | } 39 | -------------------------------------------------------------------------------- /test/test-app.js: -------------------------------------------------------------------------------- 1 | #!/usr/bin/env node 2 | 3 | process.nodester = { 4 | brand: 'nodester', 5 | apihost: 'api.nodester.com', 6 | env: 'test', 7 | config: { 8 | username: 'test', 9 | password: 'test01' 10 | } 11 | } 12 | 13 | var cli = require('../lib/commands') 14 | , cmds = cli.commands 15 | ; 16 | 17 | var assert = require('assert'); 18 | 19 | function isNumber(n) { 20 | return !isNaN(parseFloat(n)) && isFinite(n); 21 | } 22 | 23 | describe('General API test', function(){ 24 | describe('should read all the commands', function (){ 25 | for (var key in cmds){ 26 | it('should run ' + key,function(){ 27 | cli.run(cmds,key) 28 | }) 29 | } 30 | }); 31 | describe('General usage', function(){ 32 | for (var key in cmds.user){ 33 | it('should run ' + key,function(){ 34 | cli.run(cmds,'user',key) 35 | }) 36 | } 37 | }); 38 | describe('App endpoint', function(){ 39 | it('should return an error, invalid app', function(){ 40 | cli.run(cmds,'app','infods','a') 41 | }) 42 | }); 43 | describe('Errors', function(){ 44 | it('should return and error, invalid command', function(){ 45 | // assert.throws doesn't work quite well 46 | try { 47 | cli.run(cmds,'ras') 48 | } catch(ex){ 49 | assert.equal(ex && true, true) 50 | } 51 | }) 52 | }) 53 | }) 54 | --------------------------------------------------------------------------------