UNPKG

36 kBMarkdownView Raw
1<p align="center">
2 <img src="http://i.imgur.com/NyusmRJ.png" alt="vantage.js" />
3</p>
4<p align="center">
5 <img src="https://travis-ci.org/dthree/vantage.svg" alt="Build Status" />
6 <a href="https://gitter.im/dthree/vantage?utm_source=badge&utm_medium=badge&utm_campaign=pr-badge">
7 <img src="https://img.shields.io/badge/gitter-join%20chat-brightgreen.svg" alt="Gitter" />
8 </a><br>
9 <a href="https://www.npmjs.com/package/vantage">
10 <img src="https://img.shields.io/npm/v/vantage.svg" alt="NPM Version" />
11 </a>
12 <a href="https://www.npmjs.com/package/vantage">
13 <img src="https://img.shields.io/npm/dm/vantage.svg" alt="NPM Downloads" />
14 </a>
15
16</p>
17
18<p align="center">
19 <i>
20 Vantage is now production ready.
21 <br>
22 <a href="https://github.com/dthree/vantage/releases">New features & updates.</a>
23 </i>
24</p>
25
26<br>
27
28`Vantage.js` = `CLI` + `SSH` + `REPL` for your live node app. In one line:
29
30`require("vantage")().listen(4000);`
31
32<br>
33
34<p align="center">
35 <img src="http://i.imgur.com/ZwAxqv4.gif" alt="vantage.js demo" />
36</p>
37
38
39* [What just happened?](#er-that-gif-im-so-confused)
40* [That's voodoo magic: show me the code](https://github.com/dthree/vantage/tree/master/examples/spiffy-gif/)
41* [Tell me more](#contents)
42
43## Contents
44
45* [Introduction](#introduction)
46* [Getting Started](#getting-started)
47 - [Tutorial](#tutorial)
48 - [Examples](#examples)
49 - [Community](#community)
50 - [Quick Start](#quick-start)
51* [Methods](#methods)
52 - [.command](#commandcommand-description)
53 - [.mode](#modecommand-description)
54 - [.delimiter](#delimiterstring)
55 - [.banner](#bannerstring)
56 - [.show](#show)
57 - [.listen](#listenapp-options)
58* [Events](#events)
59* [Automation](#automation)
60* [Firewall](#firewall)
61* [Authentication](#authentication)
62* [Extensions](#extensions)
63 - [Creating an Extension](#creating-an-extension)
64* [License](#license)
65* [Footnotes](#footnotes)
66
67## Introduction
68
69Vantage gives you a new perspective into your live node application not previously available.
70
71Inspired by and based on [commander.js](https://www.npmjs.com/package/commander), Vantage turns your live Node app into a CLI with an interactive prompt provided by [inquirer.js](https://www.npmjs.com/package/inquirer). Accessible locally or remotely, Vantage lets you build your own API and import community extensions, introducing the possibility of live activity and diagnostics for your `dev` and `prod` environments.
72
73- Node now has a first-class CLI: tab completion, history, you name it.
74- Build your own API with the familiar syntax of `commander.js`.
75- Build and use community extensions for suites of commands: coded or in realtime.
76- Production-ready, with authentication middleware and a basic firewall.
77- Built-in REPL.
78
79Unlike other REPL or CLI modules, Vantage allows you to remotely connect to your live app and access the CLI transparently, exactly as you would in an SSH session. Vantage can connect through an unlimited number of live Node instances across multiple machines, piping commands and information to and from your local terminal.
80
81## Getting Started
82
83##### Tour
84
85[This Vantage tour](https://github.com/dthree/vantage/tree/master/examples/tour) will give you a live walk-through of vantage's features.
86
87```bash
88$ npm install -g vantage
89$ vantage tour
90```
91
92##### Examples
93
94- [Standalone Vantage Server](https://github.com/dthree/vantage/tree/master/examples/server)
95- [Koa.js with Vantage](https://github.com/dthree/vantage/tree/master/examples/koa)
96- [Express.js with Vantage](https://github.com/dthree/vantage/tree/master/examples/express)
97- [Hapi.js with Vantage](https://github.com/dthree/vantage/tree/master/examples/hapi)
98- [Using the "mode" command](https://github.com/dthree/vantage/tree/master/examples/mode)
99- [Using the Firewall](https://github.com/dthree/vantage/tree/master/examples/firewall)
100
101##### Community
102
103- [Q&A? Join Gitter Chat](https://gitter.im/dthree/vantage?utm_source=badge&utm_medium=badge&utm_campaign=pr-badge)
104- [List of Vantage Extensions](https://github.com/vantagejs/awesome-vantagejs)
105
106##### Quick Start
107
108First, install `vantage` globally:
109
110```bash
111$ npm install -g vantage
112```
113
114Now, add the following to a file named `server.js`.
115
116```js
117// Create a new instance of vantage.
118var vantage = require("vantage")();
119
120// Add the command "foo", which logs "bar".
121vantage
122 .command("foo")
123 .description("Outputs 'bar'.")
124 .action(function(args, callback) {
125 this.log("bar");
126 callback();
127 });
128
129// Name your prompt delimiter
130// "websvr~$", listen on port 80
131// and show the Vantage prompt.
132vantage
133 .delimiter("websvr~$")
134 .listen(80)
135 .show();
136```
137Run `server.js`. You Node app has become a CLI.
138
139```bash
140$ node server.js
141websvr~$
142```
143
144Open another terminal. Because Vantage is listening on port 80, you can remotely connect to it:
145
146```bash
147$ vantage 80
148$ Connecting to 127.0.0.1:80 using http...
149websvr~$
150```
151
152Try out your "foo" command.
153
154```bash
155websvr~$ foo
156bar
157websvr~$
158```
159
160Now type "help" to see Vantage's built in commands in addition to "foo":
161
162```bash
163websvr~$ help
164
165 Commands
166
167 help [command] Provides help for a given command.
168 exit [options] Exits instance of Vantage.
169 use <module> Installs a vantage extension in realtime.
170 vantage [server] Connects to another application running vantage.
171 foo Outputs "bar".
172
173websvr~$
174```
175
176That's the basic idea. Once you get the hang of it, read on to learn some of the fancier things Vantage can do.
177
178## Methods
179
180### .command(command, [description])
181
182Adds a new command to your command line API. Returns a `Command` object, with the following chainable functions:
183
184* [`.description(string)`](#commanddescriptionstring): Used in automated help for your command.
185* [`.hidden()`](#commandhidden): Removes command from help menus.
186* [`.option(string, [description])`](#commandoptionstring-description): Provides command options, as in `-f` or `--force`.
187* [`.action(function)`](#commandactionfunction): Function to execute when command is executed.
188
189```js
190vantage
191 .command("foo")
192 .description("Outputs 'bar'.")
193 .action(function(args, callback) {
194 this.log("bar");
195 callback();
196 });
197```
198The syntax is similar to `commander.js` with the exception of allowing nested sub-commands for grouping large APIs into manageable chunks.
199
200```js
201// Simple command with no arguments.
202vantage.command("foo", "Description of foo.");
203
204// Optional argument.
205vantage.command("foo [bar]");
206
207// Required argument.
208vantage.command("foo <bar>");
209
210// Examples of nested subcommands:
211vantage.command("farm animals");
212vantage.command("farm tools");
213vantage.command("farm feed [animal]");
214vantage.command("farm with farmer brown and reflect on <subject>");
215```
216Descriptions can optionally be passed in as the second parameter, which are used to build the automated help.
217
218##### Sub-commands
219
220When displaying the help menu, sub-commands will be grouped separately:
221
222```bash
223webapp~$ help
224
225 Commands: ( ... )
226
227 Command Groups:
228
229 farm * 4 sub-commands.
230
231```
232Entering "farm" or "farm --help" would then drill down on the commands:
233
234```bash
235webapp~$ farm
236
237 Commands:
238
239 farm animals Lists all animals in the farm.
240 farm tools Lists all tools in the farm.
241 farm feed [animal] Feeds a given animal.
242
243 Command Groups:
244
245 farm with * 1 sub-command.
246
247```
248
249#### .command.description(string)
250
251If you don't pass a description into `vantage.command(...)` above, you can use the `description` function as an alternative.
252
253```js
254vantage
255 .command("foo")
256 .description("outputs bar")
257 // ...
258```
259
260#### .command.hidden()
261
262Makes the command invisible, though executable. Removes from all automated help menus.
263
264#### .command.option(string, [description])
265
266You can provide both short and long versions of an option. Examples:
267
268```js
269vantage
270 .command("random", "Does random things.")
271 .option('-f, --force', 'Force file overwrite.')
272 .option('-a, --amount <coffee>', 'Number of cups of coffee.')
273 .option('-v, --verbosity [level]', 'Sets verbosity level.')
274 .option('-A', 'Does amazing things.')
275 .option('--amazing', 'Does amazing things')
276 // ...
277```
278
279#### .command.autocompletion(text, iteration, callback)
280
281Registers a custom tabbed autocompletion for this command.
282
283If a user has typed part of a registered command, the default auto-completion will fill in the rest of the command:
284
285```bash
286node~$ co
287node~$ cook
288```
289
290However, after the user has fully typed the command `cook`, you can now implement command-specific auto-completion:
291
292```bash
293node~$ bake coo # tab is pressed
294node~$ bake cookies # tab is pressed again
295cake cookies pie
296node~$ bake cookies
297```
298
299This is implemented as follows:
300
301```js
302vantage
303 .command("bake", "Bakes a meal.")
304 .autocompletion(function(text, iteration, cb) {
305
306 // The meals are all of the possible actions.
307 var meals = ["cookies", "pie", "cake"];
308
309 // The iteration is the count of how many times
310 // the `tab` key was pressed in a row. You can
311 // make multiple presses return all of the options
312 // for the user's convenience.
313 if (iteration > 1) {
314
315 // By returning an array of values, Vantage
316 // will format them in a pretty fashion, as
317 // in the example above.
318 cb(void 0, meals);
319
320 } else {
321
322 // `this.match` is a helper function that will
323 // return the closest auto-completion match.
324 // Just makin' your job easier.
325 var match = this.match(text, meals);
326
327 if (match) {
328
329 // If there is a good autocomplete, return
330 // it in the callback (first param is reserved
331 // for errors).
332 cb(void 0, meals);
333 } else {
334
335 // If you don't want to do anything, just
336 // return undefined.
337 cb(void 0, void 0);
338 }
339 }
340 })
341 .action(...);
342```
343
344#### .command.action(function)
345
346This is the action execution function of a given command. It passes in an `arguments` object and `callback`.
347
348Actions are executed async and must either call the passed `callback` upon completion or return a `Promise`.
349
350```js
351// As a callback:
352command(...).action(function(args, cb){
353 var self = this;
354 doSomethingAsync(function(results){
355 self.log(results);
356 // If this is not called, Vantage will not
357 // return its CLI prompt after command completion.
358 cb();
359 });
360});
361
362// As a newly created Promise:
363command(...).action(function(args, cb){
364 return new Promise(function(resolve, reject) {
365 if (skiesAlignTonight) {
366 resolve();
367 } else {
368 reject("Better luck next time");
369 }
370 });
371});
372
373// Or as a pre-packaged promise of your app:
374command(...).action(function(args, cb){
375 return app.promisedAction(args.action);
376});
377```
378
379##### Action Arguments
380
381Given the following command:
382
383```js
384vantage
385 .command('order pizza [type]', 'Orders a type of food.')
386 .option('-s, --size <size>', 'Size of pizza.')
387 .option('-a, --anchovies', 'Include anchovies.')
388 .option('-p, --pineapple', 'Include pineapple.')
389 .option('-o', 'Include olives.')
390 .option('-d, --delivery', 'Pizza should be delivered')
391 .action(function(args, cb){
392 this.log(args);
393 cb();
394 });
395```
396Args would be returned as follows:
397
398```bash
399$webapp~$ order pizza pepperoni -pod --size "medium" --no-anchovies
400{
401 "type": "pepperoni",
402 "options": {
403 "pineapple": true,
404 "o": true,
405 "delivery": true,
406 "anchovies": false,
407 "size": "medium",
408 }
409}
410```
411
412##### Action Context (Session)
413
414The `this` variable in a `command.action` function is exposed to a special "Session" context. This context has a few functions to make use of:
415
416##### session.log(string)
417
418Any and all logging in `command.action` should be done through `this.log`, which behaves exactly like `console.log`. This ensures all output for your given Vantage session is piped back to your original TTY, regardless how many hops into other servers you have made with Vantage.
419
420```js
421vantage
422 .command("foo", "Outputs 'bar'.")
423 .action(function(args, callback) {
424
425 // This will pipe back to your terminal.
426 this.log("bar");
427
428 // This will only log on the remote terminal,
429 // and you will not see it on your local TTY.
430 console.log("bar");
431
432 callback();
433 });
434```
435
436##### session.prompt(object, [callback])
437
438Vantage supports mid-command prompting. You can make full use of [inquirer.js](https://www.npmjs.com/package/inquirer)'s `prompt` function, which is exposed through `this.prompt`.
439
440Regardless of a direct vantage connection or one proxying your request through ten hops, `vantage.prompt` will send the remote prompt request to your local client and pipe response back to the remote application.
441
442```js
443vantage.command("destroy database").action(function(args, cb){
444 var self = this;
445 this.prompt({
446 type: "confirm",
447 name: "continue",
448 default: false,
449 message: "That sounds like a really bad idea. Continue?",
450 }, function(result){
451 if (!result.continue) {
452 self.log("Good move.");
453 cb();
454 } else {
455 self.log("Time to dust off that resume.");
456 app.destroyDatabase(cb);
457 }
458 });
459});
460```
461
462```bash
463dbsvr~$ destroy database
464? That sounds like a really bad idea. Continue? y/N: N
465Good move.
466dbsvr~$
467```
468
469##### session.delimiter(string)
470
471You can change the prompt delimiter mid command through `this.delimiter`.
472
473```js
474vantage
475 .command("delimiter <string>")
476 .action(function(args, cb){
477 this.delimiter(args.string);
478 cb();
479 });
480```
481
482```bash
483websvr~$ delimiter unicornsvr~$
484unicornsvr~$
485```
486
487##### session.user
488
489The currently logged on user executing the command is exposed through `this.user`. Defaults to "guest" when there is no authentication enabled.
490
491```js
492vantage
493 .command("view classified information", "Shows all of our secrets.")
494 .action(function(args, callback) {
495 if (this.user === "president") {
496 this.log(app.classifiedInformation);
497 } else {
498 this.log("Access Denied");
499 }
500 callback(true, "Access Denied");
501 });
502```
503
504### .mode(command, [description])
505
506Mode is a special type of `command` that brings the user into a given `mode`, wherein regular Vantage commands are ignored and the full command strings are interpreted literally by the `mode.action` function. This will continue until the user exits the mode by typing `exit`.
507
508```js
509vantage
510 .mode("repl")
511 .description("Enters the user into a REPL session.")
512 .delimiter("repl:")
513 .action(function(command, callback) {
514 this.log(eval(command));
515 });
516```
517```bash
518$ node server.js
519node~$
520node~$ repl
521node~$ repl:
522node~$ repl: 6 * 7
52342
524node~$ repl: Math.random();
5250.62392647205
526node~$ repl: exit
527node~$
528```
529
530`mode`'s syntax is a duplicate of `command`'s, with the following additional / altered commands:
531
532* [`.delimiter(string)`](#modedelimiterstring): Tacks on an additional prompt delimiter for orientation.
533* [`.init(function)`](#modeinitfunction): Same as `command`'s `.action`, called once on entering the mode.
534* [`.action(function)`](#modeactionfunction): Called on each command submission while in the mode.
535
536#### .mode.delimiter(string)
537
538This will add on an additional delimiter string to one's Vantage prompt upon entering the mode, so the user can differentiate what state he is in.
539
540```js
541vantage
542 .mode('repl')
543 .delimiter('you are in repl>')
544 .action(function(command, callback) {
545 this.log(eval(command));
546 });
547```
548
549```bash
550node~$
551node~$ repl
552node~$ you are in repl>
553```
554#### .mode.init(function)
555
556Behaves exactly like `command.action`, where the function passed in is fired once when the user enters the given mode. Passed the same parameters as `command.action`: `args` and `callback`. `init` is helpful when one needs to set up the mode or inform the user of what is happening.
557
558```js
559vantage
560 .mode('sql')
561 .delimiter('sql:')
562 .init(function(args, callback){
563 this.log('Welcome to SQL mode.\nYou can now directly enter arbitrary SQL commands. To exit, type `exit`.');
564 callback();
565 })
566 .action(function(command, callback) {
567 var self = this;
568 app.query(command, function(res){
569 self.log(res);
570 callback();
571 });
572 });
573```
574
575```bash
576node~$
577node~$ sql
578Welcome to SQL mode.
579You can now directly enter arbitrary SQL commands. To exit, type `exit`.
580node~$ sql:
581node~$ sql: select first_name, last_name from persons where first_name = 'George';
582
583first_name last_name
584---------------- ----------------
585George Clooney
586George Smith
587George Stevens
588
589node~$ sql:
590node~$ sql: exit
591node~$
592```
593
594#### .mode.action(function)
595
596Similar to `command.action`, `mode.action` differs in that it is repeatedly called on each command the user types until the mode is exited. Instead of `args` passed as the first argument, the full `command` string the user typed is passed and it is expected that `mode.action` appropriately handles the command. Example given above.
597
598### .delimiter(string)
599
600Sets the prompt delimiter for the given Vantage server.
601
602```js
603new Vantage().delimiter('appsvr:3000~$').listen(3000);
604new Vantage().delimiter('appsvr:3001~$').listen(3001);
605new Vantage().delimiter('appsvr:3002~$').listen(3002);
606```
607
608```bash
609$ vantage 3000
610appsvr:3000~$
611appsvr:3000~$ vantage 3001
612appsvr:3001~$ vantage 3002
613appsvr:3002~$ exit
614appsvr:3001~$ exit
615appsvr:3000~$ exit -f
616$
617```
618
619### .banner(string)
620
621Sets a banner for display when logging into a given Vantage server.
622
623```js
624var banner =
625"######################################################################" +
626"# Welcome to joescrabshack.com #" +
627"# #" +
628"# All connections are monitored and recorded #" +
629"# Disconnect IMMEDIATELY if you are not an authorized user #" +
630"######################################################################";
631vantage
632 .delimiter('appsvr:3000~$')
633 .banner(banner)
634 .listen(3000);
635```
636
637```bash
638$ vantage 3000
639$ Connecting to 127.0.0.1:3000...
640$ Connected successfully.
641######################################################################
642# Welcome to joescrabshack.com #
643# #
644# All connections are monitored and recorded #
645# Disconnect IMMEDIATELY if you are not an authorized user #
646######################################################################
647? user:
648```
649*Note: See authentication section for auth details.*
650
651### .show()
652
653Attaches the TTY's CLI prompt to that given instance of Vantage. While useless for deployed servers, this is great for testing an application's functions mid-development.
654
655```js
656// ... (your web server code)
657
658vantage
659 .delimiter('websvr~$')
660 .show();
661
662vantage
663 .command('build api', 'Builds web server API.')
664 .action(function(args, cb){
665 return app.buildAPI();
666 });
667```
668
669```bash
670node websvr.js
671Successfully started Web Server.
672websvr~$
673websvr~$ build API
674Building API...
675...
676Successfully built API.
677websvr~$
678```
679As a note, multiple instances of Vantage can run in the same Node instance. However, only one can be "attached" to your TTY. The last instance given the `show()` command will be attached, and the previously shown instances will detach.
680
681```js
682var instances = []
683for (var i = 0; i < 3; ++i) {
684 instances[i] = new Vantage()
685 .delimiter("instance" + i + "~$")
686 .command("switch <instance>", "Switches prompt to another instance.")
687 .action(function(args, cb){
688 instances[args.instance].show();
689 cb();
690 })
691}
692
693instances[0].show();
694```
695
696```bash
697$ node server.js
698instance0~$ switch 1
699instance1~$ switch 2
700instance2~$ switch 0
701instance0~$
702```
703
704### .listen(app, [options or callback], [callback])
705
706Starts Vantage as a server.
707
708#### Vantage as a standalone web server
709
710If you just want it to listen on a port independent of your web application, simply pass in the port and Vantage will spawn a new HTTP server. Every time a client connects to Vantage, the connection callback will be thrown and include the `socket.io` connection object.
711
712```js
713var vantage = new Vantage();
714vantage.listen(80, function(socket){
715 this.log("Accepted a connection.")
716});
717```
718
719#### Vantage with an existing web server
720
721If you want Vantage to listen on the same port as your web application, you can use Vantage's `listen` function in place of your existing web server's `listen` function.
722
723This is useful when running clustered instances of your server, such as behind a reverse proxy, where every instance has a separate port that can only be accessed internally. In this way, you can hop into any running instance without having to remember a separate set of ports.
724
725##### With Koa.js
726
727```js
728var koa = require('koa');
729var Vantage = require('vantage');
730
731var vantage = new Vantage();
732var app = koa();
733
734vantage.listen(app, 80);
735```
736
737##### With Express.js
738
739```js
740var express = require('express');
741var Vantage = require('vantage');
742
743var vantage = new Vantage();
744var app = express();
745
746vantage.listen(app, 80);
747```
748
749##### With Hapi.js
750
751```js
752var Hapi = require('hapi');
753var Vantage = require('vantage');
754
755var vantage = new Vantage();
756var server = new Hapi.Server();
757
758vantage.listen(server, 80);
759
760server.start();
761```
762
763##### With SSL / advanced options
764
765You can pass detailed options to your web server with the second argument in place of the port. These options are the same options you would pass into your web server, with a few exceptions:
766
767- `options.port`: Tells vantage what port to listen on.
768- `options.ssl`: A boolean that tells Vantage whether to spawn an HTTP or HTTPs server.
769- `options.logActivity`: When true, a TTY acting as a Vantage server that receives a connection will log when clients log in and out of the server. Defaults to `false`.
770
771Default HTTPs server example:
772
773```js
774var vantage = new Vantage();
775vantage.listen(someMiddleware, {
776 port: 443,
777 ssl: true,
778 key: fs.readFileSync('./../../server.key'),
779 cert: fs.readFileSync('./../../server.crt'),
780 ca: fs.readFileSync('./../../ca.crt'),
781 requestCert: true,
782 rejectUnauthorized: false,
783});
784```
785
786## Events
787
788Vantage extends `EventEmitter.prototype`. Simply use `vantage.on('event', fn)` and `vantage.emit('event', data)`. The following events are supported:
789
790##### Socket.io client / server events
791
792Vantage uses `socket.io` to handle all communication between instances. The following events map to the default `socket.io` events:
793
794- `client_connect`: Maps to `connect` for `socket.io-client`.
795
796- `client_connect_error`: Maps to `connect_error` for `socket.io-client`.
797
798- `client_error`: Maps to `error` for `socket.io-client`.
799
800- `client_disconnect`: Maps to `disconnect` for `socket.io-client`.
801
802- `server_connection`: Maps to `connection` for `socket.io`.
803
804- `server_disconnect`: Maps to `disconnect` for `socket.io`.
805
806##### Vantage client / server events
807
808- `client_keypress`: Fires on keypress on local client terminal.
809
810- `client_prompt_submit`: Fires when the CLI prompt has been submitted with a command, including ''.
811
812- `client_command_executed`: Fires at the client once the command has been received back as executed.
813
814- `client_command_error`: Fires at the client if a command comes back with an error thrown.
815
816- `server_command_received`: Fires at the end-server actually executing a command receives the command.
817
818- `server_command_executed`: Fires at the end-server once the command has successfully executed.
819
820- `server_command_error`: Fires at the end-server if the command has thrown an error.
821
822##### Vantage general events
823
824- `command_registered`: Fires when `vantage.command` registers a new command.
825
826## Automation
827
828Vantage allows you execute your API commands from javascript synchronously, using either callbacks or promises.
829
830### .connect(server, port, [options or callback], [callback])
831
832Connects to another instance of Vantage. Returns callback or promise.
833
834```js
835// With a promise
836vantage.connect('127.0.0.1', 8001).then(function(data){
837 // ...
838}).catch(function(err){
839 console.log('Error connecting: ' + err);
840});
841
842// With a callback
843vantage.connect('127.0.0.1', 8001, function(err) {
844 if (!err) {
845 // ... connected
846 }
847});
848```
849##### Options
850
851- `ssl`: Set to true if server you are connecting to uses HTTPS.
852
853### .exec(command, [callback])
854
855Executes an API command string. Returns a callback or Promise.
856
857```js
858// Using Promises:
859vantage.exec("vantage 8001").then(function(data){
860 return vantage.exec("roll dough");
861}).then(function(data){
862 return vantage.exec("add cheese");
863}).then(function(data){
864 return vantage.exec("add pepperoni");
865}).then(function(data){
866 return vantage.exec("shape crust");
867}).then(function(data){
868 return vantage.exec("insert into oven");
869}).then(function(data){
870 return vantage.exec("wait 480000");
871}).then(function(data){
872 return vantage.exec("remove from oven");
873}).then(function(data){
874 return vantage.exec("enjoy");
875}).catch(function(err){
876 console.log("Error baking pizza: " + err);
877 app.orderOut();
878});
879
880// Using callbacks:
881vantage.exec("vantage 8001", function(err, data) {
882 if (!err) {
883 vantage.exec("bake pizza", function(err, pizza){
884 if (!err) {
885 app.eat(pizza);
886 }
887 });
888 }
889});
890```
891
892### .pipe(function)
893
894Captures all session `stdout` piped through Vantage and passes it through a custom function. The string returned from the function is then logged.
895
896```js
897var onStdout = function(stdout) {
898 app.writeToLog(stdout);
899 return "";
900}
901
902vantage
903 .pipe(onStdout)
904 .connect("127.0.0.1", 80, {});
905```
906
907## Firewall
908
909If your Vantage server is listening on a public-facing web port such as 80 or 443, your organization's firewall is not going to help you. This is a barebones IP firewall for limiting connections down to your internal subnets. For sensitive applications, this obviously does not replace authentication.
910
911### .firewall.policy(string)
912
913Sets the default policy for the firewall to either `ACCEPT` or `REJECT`. Any request that does not match a rule will fall back to this policy. Returns `vantage.firewall`.
914
915**Defaults to `ACCEPT`.**
916
917```js
918// This will reject all remote connections.
919vantage.firewall.policy("REJECT");
920```
921
922### .firewall.accept(address, [subnet])
923
924Allows a particular address / subnet to connect to Vantage. Returns `vantage.firewall`. If no arguments are passed, returns the currently-applied policy.
925
926```js
927vantage.firewall
928 .policy("REJECT")
929 .accept("10.0.0.0/8")
930 .accept("192.168.0.0", 24);
931
932console.log(vantage.firewall.policy()) // -> REJECT
933```
934
935### .firewall.reject(address, [subnet])
936
937Denies access to a particular address / subnet. Returns `vantage.firewall`.
938
939```js
940vantage.firewall
941 .policy("ACCEPT")
942 .reject("64.0.0.0", 8)
943 .reject("192.168.0.0/16");
944```
945### .firewall.rules()
946
947Returns an array of applied rules.
948
949```js
950console.log(vantage.firewall.rules());
951// -> [{ ip: "64.0.0.0", subnet: 8, rule: "REJECT" }]
952```
953
954### .firewall.reset()
955
956Reverts `vantage.firewall` to an `ACCEPT` policy and erases all rules.
957
958## Authentication
959
960Vantage supports authentication strategies as middleware. It comes with a default [Basic Authentication module](https://github.com/vantagejs/vantage-auth-basic).
961
962### vantage.auth(middleware, options)
963
964Uses a given authentication strategy. Pass the required middleware into the first variable, and any options / configuration for that middleware as given in that module's documentation into the options parameter.
965
966```js
967var pam = require("vantage-auth-pam");
968vantage.auth(pam, options);
969```
970
971Vantage Basic Auth is built in, and so can be used with the "basic" string instead of requiring a module.
972
973```js
974var users = [
975 { user: "admin", pass: "4k#842jx!%s" },
976 { user: "user", pass: "Unicorn11" }
977];
978
979var vantage = require("vantage")();
980
981vantage.auth("basic", {
982 "users": users,
983 "retry": 3,
984 "retryTime": 500,
985 "deny": 1,
986 "unlockTime": 3000
987});
988```
989
990##### Security Note
991
992If no `vantage.auth` function is declared, your app will not require authentication. As a security measure, if your `NODE_ENV` environment variable is not set to "development" and there is no authentication, Vantage will disallow remote connections. To permit remote connections without authentication, simply set your `NODE_ENV` to "development".
993
994##### Building Authentication Strategies
995
996You can publish your own custom authentication strategies for Vantage.js as its own Node module.
997
998*I am currently looking to team up with a rocket scientist like you to build a pam-based authentication strategy for Vantage. If you are interested, send me a note!*
999
1000The format for publishing a strategy is simple:
1001
1002```js
1003
1004module.exports = function(vantage, options) {
1005
1006 // The Vantage instance is exposed through
1007 // the `vantage` parameter. `options` exposes
1008 // options passed in by the strategy's user, and
1009 // is defined by you.
1010
1011 // This is where you can persist the log on state of
1012 // the users attempting to log in, etc.
1013
1014 // You return a function, which executes
1015 // in the same context as a vantage command.
1016 // Every time the user attempts to connect,
1017 // this function runs. In it you can prompt
1018 // the user, etc.
1019 return function(args, callback) {
1020
1021 /**
1022 * Args exposes several pieces of data
1023 * you can use:
1024 * {
1025 * // If the user pre-passes auth data, it will be
1026 * // available here. Otherwise, prompt him for it.
1027 * user: "admin",
1028 * pass: "Unicorn11",
1029 * // This is based on socket.io's connection handshake,
1030 * // and has a lot more data than this.
1031 * handshake: {
1032 * host: "192.168.0.1",
1033 * port: "800"
1034 * }
1035 * }
1036 */
1037
1038 // Prompt user / look up credentials, etc.
1039
1040 // Authentication is determined by your
1041 // callback: `callback(message, authenticated)`.
1042
1043 // Example of rejected auth.
1044 callback("Invalid credentials.", false);
1045
1046 // Example of accepted auth.
1047 // callback(void 0, true);
1048 }
1049
1050}
1051
1052```
1053
1054## Extensions
1055
1056Vantage supports command extensions and this is the primary reason for supporting sub-commands. For example, someone could create a suite of server diagnostic commands under the namespace `system` and publish it as `vantage-system`.
1057
1058##### Programmatic use
1059
1060Vantage has a `.use(extension)` function, which expects a Node module extension (exposed as a function). You can also pass in the string of the module as an alternative, and `vantage` will `require` it for you.
1061
1062```js
1063var system = require('vantage-system');
1064vantage.use(system);
1065
1066/*
1067 Your API would now include a suite of system commands:
1068 system list processes
1069 system status
1070 system ... etc.
1071*/
1072```
1073
1074```js
1075// Does the same thing as above.
1076vantage.use('vantage-system');
1077```
1078
1079##### Realtime use
1080
1081Forgot to install a useful extension in development and now you need it live? No problem.
1082
1083Vantage has a built-in `use` command, which will automatically import a given NPM module acting as a `vantage` extension, and register the commands contained inside while the app is still live. This import has an in-memory lifecycle and the module is dumped when the thread quits.
1084
1085```bash
1086node~$
1087node~$ use vantage-repl
1088Installing vantage-repl from the NPM registry:
1089Successfully registered 1 new command.
1090node~$
1091node~$ repl
1092node~$ repl: 6*8
109348
1094node~$ repl:
1095```
1096
1097### Creating an extension
1098
1099Creating and publishing a Vantage extension is simple. Simply expose your module as a function which takes two parameters - `vantage` and `options`. When your module is imported by `vantage`, it will pass itself in as the first object, and so you are free to add any commands or configuration that `vantage` supports.
1100
1101```js
1102module.exports = function(vantage, options) {
1103
1104 vantage.
1105 .command("foo", "Outputs 'bar'.")
1106 .action(function(args, cb){
1107 this.log("bar");
1108 cb();
1109 });
1110
1111 // ... more commands!
1112
1113}
1114```
1115
1116The options exist so the user can pass in customizations to your module. In documenting your `vantage` extension, you would lay out your supported options for the user.
1117
1118## Roadmap
1119
1120- Suggest something!
1121
1122## License
1123
1124MIT
1125
1126## Footnotes
1127
1128##### Er, that GIF... I'm so confused...
1129
1130That's okay. Here's what happened:
1131
11321. In my terminal, I started a local Node web server:
1133
1134```js
1135$ node websvr.js
1136```
1137
1138Normally, you would simply see what you logged, and would have no interaction with Node. Instead, Vantage gave us a prompt:
1139
1140```bash
1141websvr~$
1142```
1143
11442. I typed `help`, which gave me a list of all of Vantage's built-in commands as well as commands I added.
1145
11463. In my `websvr.js`, I gave Vantage a command that would turn on logging *only for* web requests. By logging domains of activity, this assists productivity in debugging. To run this, I typed `debug web`, and it started logging all web requests.
1147
11484. I then typed `debug off`, which disabled log output.
1149
11505. By then entering the `repl` command, I entered a special REPL "mode" where I can access the raw javascript and objects in my application, while it's running. This is the equivalent of running `$ node` in your terminal, except it is in the context of your live application!
1151
11526. Satisfied with `repl` mode, I exited out of it with the `exit` command.
1153
11547. So that's nice, you can access the local Node instance in your terminal. But what about remote or daemonized applications? By using the built-in `vantage` command, I remotely connect to my Node database API listening on port `5001`, by running `vantage 127.0.0.1:5001`.
1155
11568. Just like SSH, I'm now "in" the new instance, and my prompt changed to `dbsvr~$`.
1157
11589. This server supports another Vantage mode. By typing `sql`, I enter "sql mode". Using this, I typed an arbitrary SQL command and it connected to my database and executed it. When done, I entered `exit`.
1159
116010. I felt like checking out the latest trend on Hacker News. I typed `help` and was disappointed to find there was no `hacker-news` API command.
1161
116211. Fortunately, someone made an extension for that - an NPM module called `vantage-hacker-news`. To download it and import the commands into Vantage in realtime, I typed `use vantage-hacker-news`.
1163
116412. With this command, `vantage` did a temporary `npm install` on the module and loaded it into the application's memory. By typing `help` again, I can see I now have a new Vantage command registered: `hacker-news`!
1165
116613. I used the command: `hacker-news --length 3`, and this showed me the top 3 items trending on Hacker News. One of them was obviously an article on the Node event loop, because Node is awesome.
1167
116814. Satisfied, I typed `exit`, which brought me back to my web server.
1169
117015. I then typed `exit -f` (for `--force`) to actually quit the web server, which was running locally in my terminal.
1171
1172* [Ah. Show me the GIF again](#)
1173* [I get it, I get it. *Tell me more*](#contents)
1174
1175<br>
1176
1177---
1178
1179<br>
1180
1181<p align="center">
1182 <img src="http://i.imgur.com/ajsjp9E.png" alt="vantage.js" />
1183</p>