├── .editorconfig ├── .eslintrc ├── .gitattributes ├── .gitignore ├── .travis.yml ├── .vscode └── launch.json ├── API ├── CLIENT.md └── SERVER.md ├── LICENSE ├── README.md ├── demo.gif ├── gulpfile.js ├── lib ├── client │ ├── branding.js │ ├── help.json │ └── index.js ├── index.js └── server │ ├── branding.js │ └── index.js ├── output └── index.md ├── package.json └── test ├── client.spec.js ├── deployager_rsa └── deployager_rsa.pem /.editorconfig: -------------------------------------------------------------------------------- 1 | root = true 2 | 3 | [*] 4 | indent_style = space 5 | indent_size = 2 6 | charset = utf-8 7 | trim_trailing_whitespace = true 8 | insert_final_newline = true 9 | 10 | [*.md] 11 | trim_trailing_whitespace = false 12 | -------------------------------------------------------------------------------- /.eslintrc: -------------------------------------------------------------------------------- 1 | { 2 | "env": { 3 | "mocha": true, 4 | "node": true 5 | }, 6 | "rules":{ 7 | "no-trailing-spaces" : "off", 8 | "no-multiple-empty-lines":"off", 9 | "space-before-function-paren" : "off", 10 | "keyword-spacing" : "off", 11 | "indent" : "off", 12 | "quotes": ["warn", "double", "avoid-escape"], 13 | "eol-last" : "off", 14 | "space-before-blocks" : "off", 15 | "no-mixed-spaces-and-tabs" : "warn", 16 | "key-spacing" : "off", 17 | "comma-spacing" : "off", 18 | "no-multi-spaces" : "off", 19 | "padded-blocks" : "off", 20 | "spaced-comment": "off", 21 | "no-extra-boolean-cast" : "off", 22 | "max-statements" : ["warn", { "max": 20 }], 23 | "max-params":["error", 3], 24 | "max-nested-callbacks":["error", { "max": 3 }], 25 | "max-depth":["error", 4], 26 | "no-negated-condition":"off", 27 | "no-implicit-coercion":"off", 28 | "no-unused-vars":"warn", 29 | "no-useless-escape" : "off", 30 | "space-in-parens" : "off", 31 | "semi-spacing": "off", 32 | "no-useless-concat": "off", 33 | "max-lines": ["warn", 130], 34 | "no-lonely-if" :"off", 35 | "space-infix-ops" : "warn", 36 | "camelcase" : "off", 37 | "comma-dangle" : "warn", 38 | "no-new-func":"off", 39 | "no-prototype-builtins":"off", 40 | "no-new":"warn" 41 | } 42 | } 43 | -------------------------------------------------------------------------------- /.gitattributes: -------------------------------------------------------------------------------- 1 | * text=auto 2 | -------------------------------------------------------------------------------- /.gitignore: -------------------------------------------------------------------------------- 1 | node_modules 2 | coverage 3 | /output 4 | -------------------------------------------------------------------------------- /.travis.yml: -------------------------------------------------------------------------------- 1 | language: node_js 2 | node_js: 3 | - v4 4 | - '0.12' 5 | - '0.10' 6 | -------------------------------------------------------------------------------- /.vscode/launch.json: -------------------------------------------------------------------------------- 1 | { 2 | "version": "0.2.0", 3 | "configurations": [ 4 | { 5 | "name": "debug-client", 6 | "type": "node", 7 | "request": "launch", 8 | "program": "${workspaceRoot}/lib/index.js", 9 | "stopOnEntry": false, 10 | "args": ["client"], 11 | "cwd": "${workspaceRoot}", 12 | "preLaunchTask": null, 13 | "runtimeExecutable": null, 14 | "runtimeArgs": [ 15 | "--nolazy" 16 | ], 17 | "env": { 18 | "NODE_ENV": "development" 19 | }, 20 | "console": "internalConsole", 21 | "sourceMaps": false, 22 | "outDir": null 23 | }, 24 | { 25 | "name": "debug-mocha", 26 | "type": "node", 27 | "request": "launch", 28 | "program": "${workspaceRoot}/node_modules/mocha/bin/_mocha", 29 | "stopOnEntry": false, 30 | "args": [], 31 | "cwd": "${workspaceRoot}", 32 | "preLaunchTask": null, 33 | "runtimeExecutable": null, 34 | "runtimeArgs": [ 35 | "--nolazy" 36 | ], 37 | "env": { 38 | "NODE_ENV": "development" 39 | }, 40 | "console": "internalConsole", 41 | "sourceMaps": false, 42 | "outDir": null 43 | }, 44 | { 45 | "name": "debug-server", 46 | "type": "node", 47 | "request": "launch", 48 | "program": "${workspaceRoot}/lib/index.js", 49 | "stopOnEntry": false, 50 | "args": ["server", "--keyfolder", "./test/"], 51 | "cwd": "${workspaceRoot}", 52 | "preLaunchTask": null, 53 | "runtimeExecutable": null, 54 | "runtimeArgs": [ 55 | "--nolazy" 56 | ], 57 | "env": { 58 | "NODE_ENV": "development" 59 | }, 60 | "console": "internalConsole", 61 | "sourceMaps": false, 62 | "outDir": null 63 | }, 64 | { 65 | "name": "debug-gulp", 66 | "type": "node", 67 | "request": "launch", 68 | "program": "${workspaceRoot}/node_modules/gulp/bin/gulp.js", 69 | "stopOnEntry": false, 70 | "args": [], 71 | "cwd": "${workspaceRoot}", 72 | "preLaunchTask": null, 73 | "runtimeExecutable": null, 74 | "runtimeArgs": [ 75 | "--nolazy" 76 | ], 77 | "env": { 78 | "NODE_ENV": "development" 79 | }, 80 | "console": "internalConsole", 81 | "sourceMaps": false, 82 | "outDir": null 83 | }, 84 | { 85 | "name": "Anfügen", 86 | "type": "node", 87 | "request": "attach", 88 | "port": 5858, 89 | "address": "localhost", 90 | "restart": false, 91 | "sourceMaps": false, 92 | "outDir": null, 93 | "localRoot": "${workspaceRoot}", 94 | "remoteRoot": null 95 | }, 96 | { 97 | "name": "An den Prozess anhängen", 98 | "type": "node", 99 | "request": "attach", 100 | "processId": "${command.PickProcess}", 101 | "port": 5858, 102 | "sourceMaps": false, 103 | "outDir": null 104 | } 105 | ] 106 | } -------------------------------------------------------------------------------- /API/CLIENT.md: -------------------------------------------------------------------------------- 1 | # socketio-terminal 2 | 3 | 4 | 5 | 6 | 7 | * * * 8 | 9 | ## Class: Client 10 | 11 | 12 | ### socketio-terminal.Client..(ncli) 13 | 14 | **Parameters** 15 | 16 | **ncli**: `object`, Instance of n-cli. 17 | 18 | 19 | **Example**: 20 | ```js 21 | var Server = require("socketio-terminal/server"); 22 | var NCli = new require("n-cli"); 23 | var ncli = new NCli({ 24 | handleUncaughtException : false, 25 | argv : [ 26 | "--privatekey", "./test/deployager_rsa", 27 | "--publickey", "./test/deployager_rsa.pem", 28 | "--passphrase", "deployager", 29 | "--username", "deployager", 30 | "--host", "localhost", 31 | "--port", "8080" 32 | ] 33 | }); 34 | var server = new Server(ncli); 35 | server.run(); 36 | ``` 37 | 38 | ### socketio-terminal.Client.executeOnServer(command, done) 39 | 40 | **Parameters** 41 | 42 | **command**: `string`, shell command to execute. 43 | 44 | **done**: `function`, event handler triggered when server was started. 45 | 46 | 47 | ### socketio-terminal.Client.connect(command, handlers) 48 | 49 | **Parameters** 50 | 51 | **command**: `string`, shell command to execute. 52 | 53 | **handlers**: `object`, bundle of event handlers handlers {connect:onconnect, event:onevent, disconnect:ondisconnect}. 54 | 55 | 56 | 57 | 58 | * * * 59 | 60 | 61 | 62 | 63 | 64 | 65 | 66 | 67 | 68 | 69 | -------------------------------------------------------------------------------- /API/SERVER.md: -------------------------------------------------------------------------------- 1 | # socketio-terminal 2 | 3 | 4 | 5 | 6 | 7 | * * * 8 | 9 | ## Class: Server 10 | 11 | 12 | ### socketio-terminal.Server.Constructor(ncli) 13 | 14 | **Parameters** 15 | 16 | **ncli**: `object`, Instance of n-cli. 17 | 18 | 19 | **Example**: 20 | ```js 21 | var Server = require("socketio-terminal/server"); 22 | var NCli = new require("n-cli"); 23 | var ncli = new NCli({ 24 | handleUncaughtException : false, 25 | argv : ["--keyfolder", __dirname] 26 | }); 27 | var server = new Server(ncli); 28 | server.run(); 29 | ``` 30 | 31 | ### socketio-terminal.Server.close() 32 | 33 | Disconnect all clients and close server. 34 | 35 | 36 | ### socketio-terminal.Server.run(done) 37 | 38 | **Parameters** 39 | 40 | **done**: `function`, event handler triggered when server was started. 41 | 42 | 43 | 44 | 45 | * * * 46 | 47 | 48 | 49 | 50 | 51 | 52 | 53 | 54 | 55 | 56 | -------------------------------------------------------------------------------- /LICENSE: -------------------------------------------------------------------------------- 1 | The MIT License (MIT) 2 | 3 | Copyright (c) 2016 s-a (https://github.com/s-a) 4 | 5 | Permission is hereby granted, free of charge, to any person obtaining a copy 6 | of this software and associated documentation files (the "Software"), to deal 7 | in the Software without restriction, including without limitation the rights 8 | to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 9 | copies of the Software, and to permit persons to whom the Software is 10 | furnished to do so, subject to the following conditions: 11 | 12 | The above copyright notice and this permission notice shall be included in 13 | all copies or substantial portions of the Software. 14 | 15 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 16 | IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 17 | FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 18 | AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 19 | LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 20 | OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN 21 | THE SOFTWARE. 22 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | # socketio-terminal [![NPM version][npm-image]][npm-url] [![Build Status][travis-image]][travis-url] [![Dependency Status][daviddm-image]][daviddm-url] [![Coverage percentage][coveralls-image]][coveralls-url] 2 | > A client server module which acts like SSH but communicates via socket.io. 3 | 4 | ## Installation 5 | 6 | ```sh 7 | $ npm install [-g|--save] socketio-terminal 8 | ``` 9 | 10 | ## Usage 11 | ### Shell 12 | ```sh 13 | $ socketio-terminal server [--keyfolder ./test/] [--port 8080]; 14 | $ socketio-terminal client --privatekey ./test/deployager_rsa --publickey ./test/deployager_rsa.pem --passphrase deployager --username deployager --host localhost --port 8080; 15 | ``` 16 | ### Programmatically 17 | API was born while TDD. If you need more public APIs please send a PR. 18 | - [Client API](/API/CLIENT.md) 19 | - [Server API](/API/SERVER.md) 20 | 21 | ## Demo 22 | [![Demo][demo-image]][npm-url] 23 | 24 | ## Limitations 25 | Server cannot handle shell commands that require user stdinput while execution. If anyone knows a solution for this limitation then please add an issue at this repo! 26 | 27 | ## License 28 | 29 | MIT © [s-a](https://github.com/s-a) 30 | 31 | 32 | [npm-image]: https://badge.fury.io/js/socketio-terminal.svg 33 | [npm-url]: https://npmjs.org/package/socketio-terminal 34 | [travis-image]: https://travis-ci.org/s-a/socketio-terminal.svg?branch=master 35 | [travis-url]: https://travis-ci.org/s-a/socketio-terminal 36 | [daviddm-image]: https://david-dm.org/s-a/socketio-terminal.svg?theme=shields.io 37 | [daviddm-url]: https://david-dm.org/s-a/socketio-terminal 38 | [coveralls-image]: https://coveralls.io/repos/s-a/socketio-terminal/badge.svg 39 | [coveralls-url]: https://coveralls.io/r/s-a/socketio-terminal 40 | [demo-image]: demo.gif 41 | -------------------------------------------------------------------------------- /demo.gif: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/s-a/socketio-terminal/1444e21684cf0fcb2c34c0771517f51b0c5030e7/demo.gif -------------------------------------------------------------------------------- /gulpfile.js: -------------------------------------------------------------------------------- 1 | "use strict"; 2 | var path = require("path"); 3 | var gulp = require("gulp"); 4 | var eslint = require("gulp-eslint"); 5 | var excludeGitignore = require("gulp-exclude-gitignore"); 6 | var mocha = require("gulp-mocha"); 7 | var istanbul = require("gulp-istanbul"); 8 | var nsp = require("gulp-nsp"); 9 | var plumber = require("gulp-plumber"); 10 | var coveralls = require("gulp-coveralls"); 11 | 12 | gulp.task("static", function () { 13 | return gulp.src("**/*.js") 14 | .pipe(excludeGitignore()) 15 | .pipe(eslint()) 16 | .pipe(eslint.format()) 17 | .pipe(eslint.failAfterError()); 18 | }); 19 | 20 | gulp.task("nsp", function (cb) { 21 | nsp({package: path.resolve("package.json")}, cb); 22 | }); 23 | 24 | gulp.task("pre-test", function () { 25 | return gulp.src(["lib/**/*.js", "!lib/index.js"]) 26 | .pipe(excludeGitignore()) 27 | .pipe(istanbul({ 28 | includeUntested: true 29 | })) 30 | .pipe(istanbul.hookRequire()); 31 | }); 32 | 33 | gulp.task("test", ["pre-test"], function (cb) { 34 | var mochaErr; 35 | 36 | gulp.src("test/**/*.js") 37 | .pipe(plumber()) 38 | .pipe(mocha({reporter: "spec"})) 39 | .on("error", function (err) { 40 | mochaErr = err; 41 | }) 42 | .pipe(istanbul.writeReports()) 43 | .on("end", function () { 44 | cb(mochaErr); 45 | }); 46 | }); 47 | 48 | gulp.task("watch", function () { 49 | gulp.watch(["lib/**/*.js", "test/**"], ["test"]); 50 | }); 51 | 52 | gulp.task("coveralls", ["test"], function () { 53 | if (!process.env.CI) { 54 | return; 55 | } 56 | 57 | return gulp.src(path.join(__dirname, "coverage/lcov.info")) 58 | .pipe(coveralls()); 59 | }); 60 | 61 | gulp.task("prepublish", ["nsp"]); 62 | gulp.task("default", ["static", "test", "coveralls"]); 63 | -------------------------------------------------------------------------------- /lib/client/branding.js: -------------------------------------------------------------------------------- 1 | "use strict"; 2 | 3 | function Branding(){ 4 | return this; 5 | } 6 | 7 | Branding.prototype.log = function(server){ 8 | var cli = server.ncli; 9 | var s = ""; 10 | /* 11 | s += " __ __ .__ __ .__ .__ \n"; 12 | s += " __________ ____ | | __ _____/ |_|__| ____ _/ |_ ___________ _____ |__| ____ _____ | | \n"; 13 | s += " / ___/ _ \\_/ ___\\| |/ // __ \\ __\\ |/ _ \\ ______ \\ __\\/ __ \\_ __ \\/ \\| |/ \\\\__ \\ | | \n"; 14 | s += " \\___ ( <_> ) \\___| <\\ ___/| | | ( <_> ) /_____/ | | \\ ___/| | \\/ Y Y \\ | | \\/ __ \\| |__\n"; 15 | s += "/____ >____/ \\___ >__|_ \\\\___ >__| |__|\\____/ |__| \\___ >__| |__|_| /__|___| (____ /____/\n"; 16 | s += " \\/ \\/ \\/ \\/ \\/ \\/ \\/ \\/ \n"; 17 | */ 18 | cli.stdout(s); 19 | 20 | }; 21 | 22 | module.exports = Branding; -------------------------------------------------------------------------------- /lib/client/help.json: -------------------------------------------------------------------------------- 1 | [ 2 | { 3 | "parms" : ["", ""], 4 | "description" : "compile and deploy a project to the given environment." 5 | }, 6 | { 7 | "parms" : ["", "key", "[value]"], 8 | "description" : "setup a value for your local machine configuration." 9 | }, 10 | { 11 | "prefix" : " ", 12 | "parms" : ["set", "username", ""], 13 | "description" : "setup a username for your local machine configuration." 14 | }, 15 | { 16 | "prefix" : " ", 17 | "parms" : ["set", "passphrase"], 18 | "description" : "Prompts you to enter a passphrase for deployment." 19 | }, 20 | { 21 | "prefix" : " ", 22 | "parms" : ["set", "privatekey", ""], 23 | "description" : "Full path to you privatekey file for deployment." 24 | }, 25 | { 26 | "parms" : ["init", "[--force]"], 27 | "description" : "write a deployment configuration file to current directory." 28 | }, 29 | { 30 | "parms" : ["list"], 31 | "description" : "list projects and environments in config file of current directory." 32 | } 33 | ] 34 | -------------------------------------------------------------------------------- /lib/client/index.js: -------------------------------------------------------------------------------- 1 | /** 2 | * @module socketio-terminal 3 | */ 4 | 5 | "use strict"; 6 | 7 | var Crypt = require("ppkey"); 8 | 9 | /** 10 | * @class Client 11 | */ 12 | function Client(ncli) { 13 | this.ncli = ncli; 14 | this.machine = "server"; 15 | var Branding = require("./branding.js"); 16 | var branding = new Branding(); 17 | branding.log(this); 18 | return this; 19 | } 20 | /** 21 | * @method . 22 | * @param {object} ncli - Instance of n-cli. 23 | * @example 24 | var Server = require("socketio-terminal/server"); 25 | var NCli = new require("n-cli"); 26 | var ncli = new NCli({ 27 | handleUncaughtException : false, 28 | argv : [ 29 | "--privatekey", "./test/deployager_rsa", 30 | "--publickey", "./test/deployager_rsa.pem", 31 | "--passphrase", "deployager", 32 | "--username", "deployager", 33 | "--host", "localhost", 34 | "--port", "8080" 35 | ] 36 | }); 37 | var server = new Server(ncli); 38 | server.run(); 39 | */ 40 | 41 | Client.prototype.getCommandlinePrefix = function (){ 42 | var color = this.ncli.color.yellow; 43 | var result = color.bold(process.cwd()) + " --> " + color(this.ncli.argv.username + "@" + this.ncli.argv.host + ":" + this.ncli.argv.port + "> "); 44 | 45 | return result; 46 | }; 47 | 48 | 49 | /** 50 | * @method executeOnServer 51 | * @param {string} command - shell command to execute. 52 | * @param {function} done - event handler triggered when server was started. 53 | */ 54 | Client.prototype.executeOnServer = function(data, done){ 55 | var self = this; 56 | self.socket.emit("server-input", data); 57 | self.socket.once("server-input-done", function(data){ 58 | done(data); 59 | }); 60 | }; 61 | 62 | /** 63 | * @method connect 64 | * @param {string} command - shell command to execute. 65 | * @param {object} handlers - bundle of event handlers handlers {connect:onconnect, event:onevent, disconnect:ondisconnect}. 66 | */ 67 | Client.prototype.connect = function(handlers){ 68 | var crypt = new Crypt(); 69 | var theSign = ""; 70 | try{ 71 | //var passphrase = crypt.simpleDecrypt(this.ncli.argv.passphrase, this.ncli.argv.username); 72 | } catch(e){ 73 | // throw new this.ncli.Error("private-key-error", "error loading private key \"" + this.ncli.argv.privatekey + "\""); 74 | } 75 | crypt.loadKeys({private : this.ncli.argv.notNull("privatekey"), passphrase: this.ncli.argv.notNull("passphrase")}); 76 | try{ 77 | theSign = crypt.encrypt(this.ncli.argv.notNull("username")); 78 | } catch(e){ 79 | console.error(e); 80 | throw new this.ncli.Error("private-key-encrypt-error", "error encrypting data with private key \"" + this.ncli.argv.privatekey + "\". Maybe the wrong passphrase?"); 81 | } 82 | var url = "http://" + this.ncli.argv.host + ":" + this.ncli.argv.port + "?uid=" + this.ncli.argv.username + "&sign=" + theSign; 83 | 84 | 85 | this.ncli.stdout("connecting " + this.ncli.argv.username + "@" + this.ncli.argv.notNull("host") + ":" + this.ncli.argv.notNull("port") + "\n"); 86 | 87 | this.socket = require("socket.io-client")(url); 88 | var f = function(){}; 89 | this.init(); 90 | this.socket.on("connect", handlers.connect || f); 91 | this.socket.on("event", handlers.event || f); 92 | this.socket.on("disconnect", handlers.disconnect || f); 93 | }; 94 | 95 | Client.prototype.init = function(){ 96 | var self = this; 97 | 98 | process.stdin.on("data", function (data) { 99 | process.stdin.pause(); 100 | var command = data.toString().trim(); 101 | if (command.toLowerCase() === "exit" || command.toLowerCase() === "bye"){ 102 | self.socket.disconnect(); 103 | } else { 104 | self.ncli.stdout("...\n"); 105 | var remotecommandDone = function(data){ 106 | if (data.exitcode === 0){ 107 | self.ncli.stdout(self.ncli.color.green("exitcode: " + data.exitcode + "\n")); 108 | } else { 109 | self.ncli.stderr(self.ncli.color.red("exitcode:" + data.exitcode + " ")); 110 | self.ncli.stderr(self.ncli.color.red(data.stderr + "\n")); 111 | } 112 | self.ncli.stdout("$ "); 113 | process.stdin.resume(); 114 | }; 115 | 116 | self.executeOnServer({ 117 | command : command 118 | }, remotecommandDone); 119 | } 120 | 121 | }); 122 | 123 | if (this.socket){ 124 | this.socket.on("server-output", function(data){ 125 | self.ncli.stdout(data.stdout); 126 | }); 127 | this.socket.on("connect", function(){ 128 | self.ncli.stdout(self.getCommandlinePrefix() + "\n$ "); 129 | }); 130 | 131 | this.socket.on("error", function(data){ 132 | throw new this.ncli.Error("socket-error", data); 133 | }); 134 | 135 | this.socket.on("disconnect", function(){ 136 | self.ncli.stdout("disconnected.\n"); 137 | }); 138 | } 139 | 140 | return this; 141 | }; 142 | 143 | 144 | 145 | 146 | 147 | module.exports = Client; 148 | -------------------------------------------------------------------------------- /lib/index.js: -------------------------------------------------------------------------------- 1 | #!/usr/bin/env node 2 | 3 | "use strict"; 4 | 5 | var Cli = new require("n-cli"); 6 | var cli = new Cli({ 7 | handleUncaughtException : true 8 | }); 9 | 10 | cli.on("server", function(){ 11 | var Server = require("./server/index.js"); 12 | var server = new Server(this); 13 | server.run(function(){ 14 | cli.stdout("server started at " + server.port + "\n"); 15 | }); 16 | }); 17 | 18 | cli.on("client", function(){ 19 | var Client = require("./client/index.js"); 20 | var client = new Client(this); 21 | client.connect({connected : function(){ 22 | cli.stdout("client started at " + client.port + "\n"); 23 | }}); 24 | }); -------------------------------------------------------------------------------- /lib/server/branding.js: -------------------------------------------------------------------------------- 1 | "use strict"; 2 | 3 | function Branding(){ 4 | return this; 5 | } 6 | 7 | Branding.prototype.log = function(server){ 8 | var cli = server.ncli; 9 | var color = server.ncli.color; 10 | cli.stdout("\n"); 11 | cli.stdout(" _____===_____\n"); 12 | cli.stdout(" ______/-------------\\______\n"); 13 | cli.stdout(" `-----------| O |-----------´\n"); 14 | cli.stdout(" `-\---`---´---/-´\n"); 15 | cli.stdout(" `-._===_.-´ " + color.bold.yellow("port:") + color.yellow(server.port) + "\n"); 16 | cli.stdout("---------------------------------------------\n"); 17 | cli.stdout(color.bold.yellow("Public key folder : ")); 18 | cli.stdout(color.yellow(cli.resolvePath(cli.argv.keyfolder || cli.config.dataFolder)) + "\n\n"); 19 | }; 20 | 21 | module.exports = Branding; -------------------------------------------------------------------------------- /lib/server/index.js: -------------------------------------------------------------------------------- 1 | /** 2 | * @module socketio-terminal 3 | */ 4 | 5 | "use strict"; 6 | 7 | 8 | var Crypt = require("ppkey"); 9 | var express = require("express"); 10 | var http = require("http"); 11 | var path = require("path"); 12 | var app = express(); 13 | var sh = require("shelljs"); 14 | 15 | /** 16 | * @class Server 17 | */ 18 | function Server(ncli){ 19 | this.ncli = ncli; 20 | this.port = this.ncli.argv.port || 8080; 21 | var Branding = require("./branding.js"); 22 | var branding = new Branding(); 23 | branding.log(this); 24 | 25 | return this; 26 | } 27 | 28 | /** 29 | * @method Constructor 30 | * @param {object} ncli - Instance of n-cli. 31 | * @example 32 | var Server = require("socketio-terminal/server"); 33 | var NCli = new require("n-cli"); 34 | var ncli = new NCli({ 35 | handleUncaughtException : false, 36 | argv : ["--keyfolder", __dirname] 37 | }); 38 | var server = new Server(ncli); 39 | server.run(); 40 | */ 41 | 42 | Server.prototype.executeLocal = function(command, ondata, done){ 43 | var child = sh.exec(command, {async: true, silent:(process.env.NODE_ENV === "test")}, done); 44 | child.stdout.on("data", ondata); 45 | }; 46 | 47 | Server.prototype.onioAuthorization = function(query, handshake, callback){ 48 | var crypt = new Crypt(); 49 | var authorized = false; 50 | var publicKeyFilename; 51 | 52 | publicKeyFilename = path.join((this.ncli.argv.keyfolder || this.ncli.config.dataFolder), query.uid + "_rsa.pem"); 53 | 54 | crypt.loadKeys({public : publicKeyFilename}); 55 | if (crypt.verify(query.uid, query.sign)){ 56 | authorized = true; 57 | } else { 58 | throw this.ncli.Error("authentification-error", query ) ; 59 | } 60 | 61 | callback(null, authorized); 62 | }; 63 | 64 | Server.prototype.onioConnection = function(server, socket){ 65 | this.ncli.log("hi", socket.handshake.query.uid ); 66 | var self = this; 67 | 68 | socket.on("server-input", function(data){ 69 | self.ncli.log("server-input", socket.handshake.query.uid, data); 70 | 71 | var ondata = function(text){ 72 | socket.emit("server-output", {stdout: text}); 73 | }; 74 | 75 | var done = function(exitcode, stdout, stderr){ 76 | socket.emit("server-input-done", {exitcode:exitcode, stdout:stdout, stderr:stderr}); 77 | }; 78 | debugger; 79 | self.executeLocal(data.command.toString(), ondata, done); 80 | }); 81 | 82 | socket.on("disconnect", function(){ 83 | self.ncli.log("bye", socket.handshake.query.uid); 84 | }); 85 | 86 | socket.on("error", function(err){ 87 | self.ncli.stderr(err); 88 | }); 89 | }; 90 | 91 | /** 92 | * Disconnect all clients and close server. 93 | */ 94 | Server.prototype.close = function() { 95 | this.server.close(); 96 | }; 97 | 98 | /** 99 | * @method run 100 | * @param {function} done - event handler triggered when server was started. 101 | */ 102 | Server.prototype.run = function(done) { 103 | this.server = http.createServer(app).listen(this.port, done); 104 | var io = require("socket.io")(this.server); 105 | var self = this; 106 | 107 | // When a new socket connects 108 | io.on("connection", function(socket){ 109 | self.onioConnection(self, socket); 110 | }); 111 | 112 | io.set("authorization", function(handshake, callback){ 113 | self.onioAuthorization(handshake._query, handshake, callback); 114 | }); 115 | }; 116 | 117 | module.exports = Server; 118 | -------------------------------------------------------------------------------- /output/index.md: -------------------------------------------------------------------------------- 1 | # socketio-terminal 2 | 3 | 4 | 5 | 6 | 7 | * * * 8 | 9 | ## Class: Client 10 | 11 | 12 | ### socketio-terminal.Client..(ncli) 13 | 14 | **Parameters** 15 | 16 | **ncli**: `object`, Instance of n-cli. 17 | 18 | 19 | **Example**: 20 | ```js 21 | var Server = require("socketio-terminal/server"); 22 | var NCli = new require("n-cli"); 23 | var ncli = new NCli({ 24 | handleUncaughtException : false, 25 | argv : [ 26 | "--privatekey", "./test/deployager_rsa", 27 | "--publickey", "./test/deployager_rsa.pem", 28 | "--passphrase", "deployager", 29 | "--username", "deployager", 30 | "--host", "localhost", 31 | "--port", "8080" 32 | ] 33 | }); 34 | var server = new Server(ncli); 35 | server.run(); 36 | ``` 37 | 38 | ### socketio-terminal.Client.executeOnServer(command, done) 39 | 40 | **Parameters** 41 | 42 | **command**: `string`, shell command to execute. 43 | 44 | **done**: `function`, event handler triggered when server was started. 45 | 46 | 47 | ### socketio-terminal.Client.connect(command, handlers) 48 | 49 | **Parameters** 50 | 51 | **command**: `string`, shell command to execute. 52 | 53 | **handlers**: `object`, bundle of event handlers handlers {connect:onconnect, event:onevent, disconnect:ondisconnect}. 54 | 55 | 56 | 57 | 58 | * * * 59 | 60 | 61 | 62 | 63 | 64 | 65 | 66 | 67 | 68 | 69 | -------------------------------------------------------------------------------- /package.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "socketio-terminal", 3 | "version": "1.0.3", 4 | "description": "A client server solution which acts like SSH but communicates via socket.io.", 5 | "homepage": "https://github.com/s-a/socketio-terminal", 6 | "author": { 7 | "name": "s-a", 8 | "email": "stephan.ahlf@googlemail.com", 9 | "url": "https://github.com/s-a" 10 | }, 11 | "files": [ 12 | "lib" 13 | ], 14 | "bin": { 15 | "socketio-terminal": "lib/index.js" 16 | }, 17 | "main": "lib/index.js", 18 | "keywords": [ 19 | "terminal", 20 | "console", 21 | "client", 22 | "server", 23 | "ssh" 24 | ], 25 | "devDependencies": { 26 | "eslint": "^3.1.1", 27 | "eslint-config-xo-space": "^0.14.0", 28 | "gulp": "^3.9.0", 29 | "gulp-coveralls": "^0.1.0", 30 | "gulp-eslint": "^2.0.0", 31 | "gulp-exclude-gitignore": "^1.0.0", 32 | "gulp-istanbul": "^1.0.0", 33 | "gulp-line-ending-corrector": "^1.0.1", 34 | "gulp-mocha": "^2.0.0", 35 | "gulp-nsp": "^2.1.0", 36 | "gulp-plumber": "^1.0.0", 37 | "mocha": "^3.1.0", 38 | "should": "^11.1.0" 39 | }, 40 | "eslintConfig": { 41 | "extends": "xo-space", 42 | "env": { 43 | "mocha": true 44 | } 45 | }, 46 | "repository": "https://github.com/s-a/socketio-terminal.git", 47 | "scripts": { 48 | "prepublish": "gulp prepublish", 49 | "test": "gulp", 50 | "docs:server": "jsdox lib/server/index.js && cp ./output/index.md ./API/SERVER.md", 51 | "docs:client": "jsdox lib/client/index.js && cp ./output/index.md ./API/CLIENT.md", 52 | "docs": "npm run docs:server && npm run docs:client", 53 | "debug-server": "iron-node lib/client/index.js server", 54 | "server": "node lib/index.js server --keyfolder ./test/", 55 | "client": "node lib/index.js client --privatekey ./test/deployager_rsa --publickey ./test/deployager_rsa.pem --passphrase deployager --username deployager --host localhost --port 8080", 56 | "debug-client": "iron-node lib/client/index.js client", 57 | "bump": "node node_modules/mocha/bin/_mocha && npm version patch && git push && git push --tags && npm publish" 58 | }, 59 | "license": "MIT", 60 | "dependencies": { 61 | "express": "^4.14.0", 62 | "n-cli": "^1.0.8", 63 | "ppkey": "^1.0.1", 64 | "socket.io": "^1.5.0" 65 | } 66 | } 67 | -------------------------------------------------------------------------------- /test/client.spec.js: -------------------------------------------------------------------------------- 1 | "use strict"; 2 | process.env.NODE_ENV = "test"; 3 | 4 | var Server = require("./../lib/server/index.js"); 5 | var Cli = new require("n-cli"); 6 | var cliServer = new Cli({ 7 | handleUncaughtException : false, 8 | argv : ["--keyfolder", __dirname] 9 | }); 10 | var server = new Server(cliServer); 11 | server.run(); 12 | 13 | var Config = require("user-appdata"); 14 | Config.prototype.set = function (settings) { 15 | this.settings = settings; 16 | }; 17 | // setup 18 | 19 | var Client = require("./../lib/client/index.js"); 20 | 21 | var path = require("path"); 22 | var should = require("should"); 23 | 24 | 25 | 26 | var testClient = function(argvArray){ 27 | var NCli = new require("n-cli"); 28 | var ncli = new NCli({ 29 | handleUncaughtException : false, 30 | argv : argvArray 31 | }); 32 | 33 | var client = new Client(ncli); 34 | return client; 35 | } 36 | 37 | 38 | 39 | describe("#client", function () { 40 | it("should throw exception private-key-encrypt-error when using wrong username", function () { 41 | should(function(){ 42 | testClient(["client", 43 | "--privatekey", path.join(__dirname, "deployager_rsa"), 44 | "--publickey", path.join(__dirname, "deployager_rsa.pem"), 45 | "--passphrase", "deployaga", 46 | "--username", "deployager", 47 | "--host", "localhost", 48 | "--port", 8080]).connect(); 49 | }).throw("private-key-encrypt-error"); 50 | }); 51 | 52 | it("should throw missing-parameter", function () { 53 | should(function(){ 54 | testClient(["client"]).connect(); 55 | }).throw("missing-parameter"); 56 | }); 57 | 58 | it("should connect fine and execute command", function (done) { 59 | var c = testClient([ 60 | "client", 61 | "--privatekey", path.join(__dirname, "deployager_rsa"), 62 | "--publickey", path.join(__dirname, "deployager_rsa.pem"), 63 | "--passphrase", "deployager", 64 | "--username", "deployager", 65 | "--host", "localhost", 66 | "--port", "8080"] 67 | ); 68 | var remotecommandDone = function(/*data*/){ 69 | try{ 70 | c.socket.disconnect(); 71 | } catch(e) {} 72 | server.close(); 73 | done(); 74 | }; 75 | 76 | var handlers = { 77 | connect : function (){ 78 | c.executeOnServer({ 79 | command : "do something", 80 | event : "server-did-something" 81 | }, remotecommandDone); 82 | } 83 | }; 84 | c.connect(handlers); 85 | }); 86 | 87 | }); 88 | -------------------------------------------------------------------------------- /test/deployager_rsa: -------------------------------------------------------------------------------- 1 | -----BEGIN RSA PRIVATE KEY----- 2 | Proc-Type: 4,ENCRYPTED 3 | DEK-Info: AES-128-CBC,14CFE5DEA9E24303B49B79EB26A3EB7F 4 | 5 | 24GtvAqD3MiAYuS5LmTOe8vEBp4ROxtwprP55oPlJVuJlOchBTmZLDBKsgAsiGto 6 | af4W20y6zlefumZ6lfohxT+bo5ekqNOBDKld/9HlOORKGojgsRvcT8O2ieaDcMeR 7 | jyWArajm+ouQBEKo+L+4XnfS0FhejiHQDW9ITWnO1K46EAMBjz6wmAaqY0lNVNBX 8 | nmWC0Zcw51tbdQYyHhJR85KgzFMwDwbphjl0HOVKANGRHBvITYf+1u000aHA/ax/ 9 | rlTvj4Cc8wV9lGhRgKzNQ8wtTEHTSrfOYCG4UPMfjXAclgalKWmnz0CZjKYunOV2 10 | //DkkdYLZEGqdC40avHw0jC8WFYVSH45u2Nd8K1OYS4tCDRq30NiGfsD4JTHzOCp 11 | sAcZrYi26tKxOubeZ64I/Jv1/BlaZcEa3k5ch6cACXMzkp/t7JO5c0Bkg+Qm/yRS 12 | JkxbW1SOWWSkYn7/8OxOEVb15zTyc4LuWpDVh1z3idxwojW0vRGxWHFwL+iehiwE 13 | dmcK+ho1nhD+FUbM87FryIebgXT5FMpJN+kwL7gurLsHzqTILwTG5m+5c8rN9GEL 14 | ZLvovcfWUOkzNbuIj++NqVzOhHm/XirCIc80WW5ZwvlMZ3jzpL9me0V7oqaxYXRV 15 | 4AvHt92GA4J2afl7rNB0idKDcb2OQawoVC5xfw45ag1fCATSRRMhPUiO27wNMFt6 16 | yfnemzhX8cKx13ivl2UtymCnnyNBCGibcOEuVHkHWe8QdSMhvBZ2/6eXVYzA+XoB 17 | 5DUFBJoStdj+rl8WYlro3gXsyr7AFtmmfdl6JJNIJ9ZYhnO4D2t/1MON74zaQN2N 18 | -----END RSA PRIVATE KEY----- 19 | -------------------------------------------------------------------------------- /test/deployager_rsa.pem: -------------------------------------------------------------------------------- 1 | -----BEGIN RSA PUBLIC KEY----- 2 | MIGJAoGBANy1MmeVZA7tKjWkDjP6IHz6otx2LVuHuTNIc7DsKdgbenm+Hktmloyn 3 | 5AAcRv8ByzRfKSWQYOqAELAgGqlRZQky/H/Xljwhu0T8S2U33o/OD8MNIxxUHJ/I 4 | Zr2BkX/CBA2lNMm6N+y4zy/BPtM7LEPBWBY27rxZSxein1X6TJe/AgMBAAE= 5 | -----END RSA PUBLIC KEY----- 6 | --------------------------------------------------------------------------------