├── .gitignore ├── .jscsrc ├── .jshintrc ├── LICENSE ├── README.md ├── paastor-cli ├── .gitignore ├── .jscsrc ├── .jshintrc ├── LICENSE ├── README.md ├── app.js ├── cli.js ├── client.js └── package.json ├── paastor-server ├── .jshintrc ├── bin │ └── www.js ├── config.js ├── gulpfile.js ├── lib │ ├── genrand.js │ └── hash.js ├── models.js ├── paastor.js ├── package.json ├── public │ ├── css │ │ ├── animate.css │ │ ├── bootstrap-clear.css │ │ ├── bootstrap-lgr.css │ │ └── style.css │ ├── dash.png │ ├── diagram.png │ ├── email-logo.gif │ ├── email-logo.xcf │ ├── favicon-old-white.png │ ├── favicon-old.png │ ├── favicon.png │ ├── js │ │ ├── app.js │ │ ├── controllers │ │ │ ├── AccountEditController.js │ │ │ ├── AppCreateController.js │ │ │ ├── AppSslController.js │ │ │ ├── ManageServicesController.js │ │ │ ├── MongoController.js │ │ │ ├── NavController.js │ │ │ ├── RedisController.js │ │ │ ├── VpsCreateController.js │ │ │ ├── VpsListController.js │ │ │ └── VpsViewController.js │ │ ├── directives │ │ │ └── pa-enter.js │ │ ├── readable-time.js │ │ └── services.js │ ├── p2.xcf │ ├── pages │ │ ├── changelog.html │ │ ├── concepts.html │ │ ├── contact.html │ │ ├── docs.html │ │ ├── faq.html │ │ ├── mongo.html │ │ ├── pstr-cli.html │ │ ├── redis.html │ │ ├── sheep-system.html │ │ ├── ssl.html │ │ ├── terms.html │ │ ├── usage.html │ │ └── vision.html │ ├── redis-white.png │ ├── redis.png │ └── sman.png ├── routes │ ├── api.js │ └── home.js ├── sheep-client.js ├── sheep-ssh-client.js ├── spec │ ├── api.spec.js │ ├── paastor-credentials.json.example │ ├── seed-stripe.js │ ├── sheep-client.spec.js │ ├── sheep-ssh-client.spec.js │ └── vps-credentials.json.example └── views │ ├── conf.jade │ ├── error.jade │ ├── index.jade │ ├── layout.jade │ ├── pages │ ├── changelog.jade │ ├── concepts.jade │ ├── contact.jade │ ├── docs.jade │ ├── faq.jade │ ├── mongo.jade │ ├── pstr-cli.jade │ ├── redis.jade │ ├── sheep-system.jade │ ├── ssl.jade │ ├── terms.jade │ ├── usage.jade │ └── vision.jade │ ├── parts │ └── ssl.jade │ ├── reset.jade │ └── templates │ ├── 404.jade │ ├── account-edit.jade │ ├── app-create.jade │ ├── app-ssl.jade │ ├── home.jade │ ├── manage-services.jade │ ├── mongo.jade │ ├── redis.jade │ ├── vps-create.jade │ ├── vps-list.jade │ └── vps-view.jade ├── paastor-sheep ├── .gitignore ├── api.js ├── lib │ └── hash.js ├── package.json ├── proxy.js └── scripts │ ├── sheep │ └── start.sh └── pack /.gitignore: -------------------------------------------------------------------------------- 1 | *.log 2 | 3 | # Paastor 4 | node_modules 5 | 6 | # Paastor tests 7 | paastor-server/spec/vps-credentials.json 8 | paastor-server/spec/paastor-credentials.json 9 | 10 | 11 | # Sheep 12 | # do not ignore node_modules, at this point 13 | sheep/system.json 14 | build/ 15 | sheep_apps/ 16 | sheep_logs/ 17 | sheep.cert 18 | sheep.key 19 | 20 | 21 | # CLI 22 | cli/node_modules/ 23 | -------------------------------------------------------------------------------- /.jscsrc: -------------------------------------------------------------------------------- 1 | { 2 | "requireCurlyBraces": [ 3 | "if", 4 | "else", 5 | "for", 6 | "while", 7 | "do", 8 | "try", 9 | "catch" 10 | ], 11 | "requireSpaceAfterKeywords": [ 12 | "do", 13 | "for", 14 | "if", 15 | "else", 16 | "switch", 17 | "case", 18 | "try", 19 | "void", 20 | "while", 21 | "return", 22 | "function" 23 | ], 24 | "disallowKeywords": ["with"], 25 | "requireSpaceBeforeBlockStatements": true, 26 | "requireSpacesInConditionalExpression": true, 27 | "disallowSpacesInNamedFunctionExpression": { 28 | "beforeOpeningRoundBrace": true 29 | }, 30 | "disallowSpacesInFunctionDeclaration": { 31 | "beforeOpeningRoundBrace": true 32 | }, 33 | "requireSpacesInFunction": { 34 | "beforeOpeningCurlyBrace": true 35 | }, 36 | "disallowMultipleVarDecl": true, 37 | "disallowMultipleLineBreaks": false, 38 | "requireBlocksOnNewline": 1, 39 | "disallowQuotedKeysInObjects": "allButReserved", 40 | "disallowSpaceAfterObjectKeys": true, 41 | "requireSpaceBeforeObjectValues": true, 42 | "requireCommaBeforeLineBreak": true, 43 | "disallowSpaceBeforePostfixUnaryOperators": true, 44 | "requireSpaceBeforeBinaryOperators": true, 45 | "requireSpaceAfterBinaryOperators": true, 46 | "disallowMixedSpacesAndTabs": true, 47 | "disallowTrailingWhitespace": true, 48 | "disallowTrailingComma": true, 49 | "requireLineFeedAtFileEnd": true, 50 | "requireCapitalizedConstructors": true, 51 | "disallowYodaConditions": true, 52 | "validateParameterSeparator": ", ", 53 | "validateIndentation": 4, 54 | "excludeFiles": [ 55 | "node_modules/**", 56 | "coverage/**", 57 | "assets/**", 58 | ".tmp/**" 59 | ] 60 | } 61 | -------------------------------------------------------------------------------- /.jshintrc: -------------------------------------------------------------------------------- 1 | { 2 | "node": true, 3 | "curly": true, 4 | "undef": true, 5 | "unused": false, 6 | "trailing": true, 7 | "strict": true, 8 | "laxbreak": true, 9 | "laxcomma": true, 10 | "mocha": true, 11 | "predef": [ 12 | "-Promise", 13 | "xit", 14 | "xdescribe" 15 | ] 16 | } 17 | -------------------------------------------------------------------------------- /LICENSE: -------------------------------------------------------------------------------- 1 | The MIT License (MIT) 2 | 3 | Copyright (c) 2015 Jeff H. Parrish 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 | # **paastor** 2 | 3 | *A hostable gui for deployment management of Node.js, MongoDB, and Redis apps on Ubuntu servers.* 4 | 5 | **Formerly paastor.com**, this project was open sourced and the website 6 | was shutdown because I do not have time to maintain it. Apologies to 7 | the users. 8 | 9 | # **sheep** 10 | 11 | *A proxy and api for running node apps on a slave VPS.* 12 | 13 | ## How it works 14 | 15 |  16 | 17 | ### Managing servers via gui 18 | 19 |  20 | 21 | ### Deploy with the command line tool 22 | 23 | ``` 24 | pstr push my-server myapp 25 | ``` 26 | 27 | ## Development and Running Locally 28 | 29 | requirements: 30 | 31 | * node.js 32 | * mongodb 33 | * gulp 34 | 35 | installing: 36 | 37 | git clone https://github.com/ruffrey/paastor 38 | cd paastor && npm install 39 | cd sheep && npm install 40 | 41 | sheep is an express app inside the paastor directory, hence the double npm install. 42 | 43 | running: 44 | 45 | npm start 46 | 47 | or 48 | 49 | npm run debug-start 50 | 51 | Also 52 | 53 | gulp 54 | 55 | will watch and perform tasks like recompiling jade and client JS. 56 | 57 | 58 | ## Default Server Locations 59 | 60 | * *paastor* is at `localhost:2999` 61 | * *sheep proxy* is at `localhost:3001` in development and `localhost:80` in production 62 | * *sheep api* is at `localhost:3000` 63 | 64 | ## Default Services 65 | 66 | Install mongodb and redis via gui. 67 | 68 | ## Security 69 | 70 | * header `Paastor-Secret` is sent during paastor --> sheep api communications. 71 | * secret is stored **hashed and salted** on sheep, by sheep, inside `system.json`. 72 | * secret is stored in paastor database under the `Vps.secret` property 73 | * YOU set the secret when creating a Vps 74 | * To tell **sheep** to reset and hash the secret, start the instance with an environment variable `HASH_RESET=` and your new secret. `cd /path/to/sheep && HASH_RESET=asdfpassword npm start` 75 | 76 | 77 | ## System Settings 78 | * `email` Login email address. 79 | * `name` System informational name. 80 | * `password` Hashed login password. 81 | * `sshkey` Generated from the paastor gui. System public ssh key - add this to your git repos. 82 | 83 | 84 | ## Tests 85 | 86 | First add a vps for testing at `spec/vps-credentials.json` and set up paastor (http://localhost:2999/), then add paastor credentials at `spec/paastor-credentials.json`. 87 | 88 | An example file is at `spec/vps-credentials.json.example`. 89 | 90 | ##### Running the Tests 91 | 92 | npm test 93 | 94 | If you have problems, try 95 | 96 | npm run debug-test 97 | 98 | for more verbose output. 99 | 100 | If tests fail, you might end up with node processes that have gone rogue. Find them and stop them: 101 | 102 | ps -l | grep node 103 | 104 | kill [pid goes here] 105 | 106 |  107 | 108 | 109 | ## Deploying sheep to the static site 110 | 111 | From the root directory of `paastor`: 112 | 113 | ./pack 114 | 115 | will do it. 116 | 117 | # License 118 | 119 | MIT - see LICENSE file in this repository 120 | 121 | Copyright 2015 Jeff H. Parrish 122 | -------------------------------------------------------------------------------- /paastor-cli/.gitignore: -------------------------------------------------------------------------------- 1 | node_modules/ 2 | *.log 3 | .DS_Store 4 | Thumbs.db 5 | -------------------------------------------------------------------------------- /paastor-cli/.jscsrc: -------------------------------------------------------------------------------- 1 | { 2 | "requireCurlyBraces": [ 3 | "if", 4 | "else", 5 | "for", 6 | "while", 7 | "do", 8 | "try", 9 | "catch" 10 | ], 11 | "requireSpaceAfterKeywords": [ 12 | "do", 13 | "for", 14 | "if", 15 | "else", 16 | "switch", 17 | "case", 18 | "try", 19 | "void", 20 | "while", 21 | "return", 22 | "function" 23 | ], 24 | "disallowKeywords": ["with"], 25 | "requireSpaceBeforeBlockStatements": true, 26 | "requireSpacesInConditionalExpression": true, 27 | "disallowSpacesInNamedFunctionExpression": { 28 | "beforeOpeningRoundBrace": true 29 | }, 30 | "disallowSpacesInFunctionDeclaration": { 31 | "beforeOpeningRoundBrace": true 32 | }, 33 | "requireSpacesInFunction": { 34 | "beforeOpeningCurlyBrace": true 35 | }, 36 | "disallowMultipleVarDecl": true, 37 | "disallowMultipleLineBreaks": false, 38 | "requireBlocksOnNewline": 1, 39 | "disallowQuotedKeysInObjects": "allButReserved", 40 | "disallowSpaceAfterObjectKeys": true, 41 | "requireSpaceBeforeObjectValues": true, 42 | "requireCommaBeforeLineBreak": true, 43 | "disallowSpaceBeforePostfixUnaryOperators": true, 44 | "requireSpaceBeforeBinaryOperators": true, 45 | "requireSpaceAfterBinaryOperators": true, 46 | "disallowMixedSpacesAndTabs": true, 47 | "disallowTrailingWhitespace": true, 48 | "disallowTrailingComma": true, 49 | "requireLineFeedAtFileEnd": true, 50 | "requireCapitalizedConstructors": true, 51 | "disallowYodaConditions": true, 52 | "validateParameterSeparator": ", ", 53 | "validateIndentation": 4, 54 | "excludeFiles": [ 55 | "node_modules/**", 56 | "coverage/**", 57 | "assets/**", 58 | ".tmp/**" 59 | ] 60 | } 61 | -------------------------------------------------------------------------------- /paastor-cli/.jshintrc: -------------------------------------------------------------------------------- 1 | { 2 | "node": true, 3 | "curly": true, 4 | "undef": true, 5 | "unused": false, 6 | "trailing": true, 7 | "strict": true, 8 | "laxbreak": true, 9 | "laxcomma": true, 10 | "mocha": true, 11 | "predef": [ 12 | "-Promise", 13 | "xit", 14 | "xdescribe" 15 | ] 16 | } 17 | -------------------------------------------------------------------------------- /paastor-cli/LICENSE: -------------------------------------------------------------------------------- 1 | The MIT License (MIT) 2 | 3 | Copyright (c) 2014 Jeff Parrish 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 | -------------------------------------------------------------------------------- /paastor-cli/README.md: -------------------------------------------------------------------------------- 1 | [](http://badge.fury.io/js/paastor) 2 | 3 | # Deploy to [Paastor](https://paastor.com) via command line interface (CLI) 4 | 5 | [Quickstart Usage Guide](https://paastor.com/pages/usage.html) 6 | 7 | [CLI Documentation](https://paastor.com/pages/pstr-cli.html) 8 | 9 | ```bash 10 | npm install -g paastor 11 | ``` 12 | 13 | Then use the `pstr` tool: 14 | 15 | ```bash 16 | pstr [commands go here] 17 | ``` 18 | 19 | # Use the client library programmatically 20 | 21 | ```bash 22 | npm install paastor --save 23 | ``` 24 | 25 | ```javascript 26 | var Paastor = require('paastor'); 27 | var paastor = new Paastor(); 28 | paastor.login({ email: 'asdf', password: 'asdfasdf' }, function (data, res, body) { 29 | console.log('logged in'); 30 | 31 | paastor.listServers(function (err, servers) { 32 | console.log('servers', servers); 33 | }); 34 | 35 | paastor.getLogs({ vps: 'myserver', app: 'chatty' }, function (err, logs) { 36 | console.log('logs', logs); 37 | }); 38 | 39 | }); 40 | ``` 41 | 42 | ---- 43 | ###### Copyright (c) 2014 Jeff Parrish 44 | ###### MIT License 45 | -------------------------------------------------------------------------------- /paastor-cli/app.js: -------------------------------------------------------------------------------- 1 | 'use strict'; 2 | // Test http server 3 | var http = require('http'); 4 | var port = process.env.PAASTOR_PORT || 3006; 5 | 6 | http.createServer(function (req, res) { 7 | var sometime = +new Date(); 8 | console.log(req.method, req.url, sometime); 9 | res.writeHead(200, {'Content-Type': 'application/json'}); 10 | var message = { message: "it works", sometime: sometime }; 11 | res.end(JSON.stringify(message)); 12 | }).listen(port, '127.0.0.1'); 13 | console.log('Server running at http://127.0.0.1:' + port); 14 | -------------------------------------------------------------------------------- /paastor-cli/client.js: -------------------------------------------------------------------------------- 1 | 'use strict'; 2 | 3 | /** 4 | * Generic wrapper client for Paastor. 5 | * 6 | * All `callback` functions receive three arguments: 7 | * 8 | * callback(error, res, body) 9 | * 10 | * @param {object} options 11 | * @param {string} [options.paastorUrl=https://paastor.com/api] 12 | */ 13 | function Client (options) { 14 | var self = this; 15 | options = options || {}; 16 | 17 | self.cookies = []; 18 | 19 | var paastorUrl = options.paastorUrl || "https://paastor.com/api"; 20 | 21 | var request = require('request').defaults({ 22 | // cookies 23 | jar: true, 24 | rejectUnauthorized: false 25 | }); 26 | 27 | 28 | /** 29 | * Log in. 30 | * @param {object} params 31 | * @param {string} [params.email] 32 | * @param {string} [params.password] 33 | * @param {function} callback 34 | */ 35 | self.login = function (params, callback) { 36 | request({ 37 | method: 'POST', 38 | uri: paastorUrl + '/login', 39 | json: true, 40 | body: params 41 | }, callback); 42 | }; 43 | 44 | /** 45 | * List all servers. 46 | * @param {object} params - Optional 47 | * @param {array} [params.cookies] - Optional 48 | * @param {function} callback 49 | */ 50 | self.listServers = function (params, callback) { 51 | var cookies = params.cookies || self.cookies; 52 | delete params.cookies; 53 | 54 | if (typeof params === 'function' && !callback) { 55 | callback = params; 56 | } 57 | request({ 58 | method: 'GET', 59 | uri: paastorUrl + '/vps', 60 | json: true, 61 | headers: { 62 | 'Cookie': cookies 63 | } 64 | }, callback); 65 | }; 66 | 67 | /** 68 | * Get logs for a server or an app on a server. 69 | * @param {object} params 70 | * @param {string} [params.vps] - The server _id. 71 | * @param {string} [params.app] - The app _id. 72 | * @param {array} [params.cookies] - Optional 73 | * @param {function} callback 74 | */ 75 | self.getLogs = function (params, callback) { 76 | 77 | var reqUrl = paastorUrl + '/vps/' + params.vps; 78 | if (!params.app) { 79 | reqUrl += '/logs'; 80 | } 81 | else { 82 | reqUrl += '/apps/' + params.app + '/logs'; 83 | } 84 | 85 | var cookies = params.cookies || self.cookies; 86 | delete params.cookies; 87 | 88 | request({ 89 | method: 'GET', 90 | uri: reqUrl, 91 | json: true, 92 | headers: { 93 | 'Cookie': cookies 94 | } 95 | }, callback); 96 | }; 97 | 98 | /** 99 | * Get detailed diagnostic info about a server. 100 | * @param {object} params 101 | * @param {string} [params.vps] - The server _id. 102 | * @param {array} [params.cookies] - Optional 103 | * @param {function} callback 104 | */ 105 | self.getServerInfo = function (params, callback) { 106 | var cookies = params.cookies || self.cookies; 107 | delete params.cookies; 108 | 109 | request({ 110 | method: 'GET', 111 | uri: paastorUrl + '/vps/' + params.vps, 112 | json: true, 113 | headers: { 114 | 'Cookie': cookies 115 | } 116 | }, callback); 117 | }; 118 | 119 | /** 120 | * Stop a currently running app. 121 | * @param {object} params 122 | * @param {string} [params.vps] - The server _id. 123 | * @param {string} [params.app] - The app _id. 124 | * @param {array} [params.cookies] - Optional 125 | * @param {function} callback 126 | */ 127 | self.stopApp = function (params, callback) { 128 | var cookies = params.cookies || self.cookies; 129 | delete params.cookies; 130 | 131 | request({ 132 | method: 'PUT', 133 | uri: paastorUrl + '/vps/' + params.vps + '/apps/' + params.app + '/kill', 134 | json: true, 135 | headers: { 136 | 'Cookie': cookies 137 | } 138 | }, callback); 139 | }; 140 | 141 | /** 142 | * Start a currently running app. 143 | * @param {object} params 144 | * @param {string} [params.vps] - The server _id. 145 | * @param {string} [params.app] - The app _id. 146 | * @param {array} [params.cookies] - Optional 147 | * @param {function} callback 148 | */ 149 | self.startApp = function (params, callback) { 150 | var cookies = params.cookies || self.cookies; 151 | delete params.cookies; 152 | 153 | request({ 154 | method: 'PUT', 155 | uri: paastorUrl + '/vps/' + params.vps + '/apps/' + params.app + '/start', 156 | json: true, 157 | headers: { 158 | 'Cookie': cookies 159 | } 160 | }, callback); 161 | }; 162 | 163 | /** 164 | * Start a currently running app. 165 | * @param {object} params 166 | * @param {string} [params.vps] - The server _id. 167 | * @param {string} [params.app] - The app _id. 168 | * @param {array} [params.cookies] - Optional 169 | * @param {function} callback 170 | */ 171 | self.restartApp = function (params, callback) { 172 | var cookies = params.cookies || self.cookies; 173 | delete params.cookies; 174 | 175 | request({ 176 | method: 'PUT', 177 | uri: paastorUrl + '/vps/' + params.vps + '/apps/' + params.app + '/restart', 178 | json: true, 179 | headers: { 180 | 'Cookie': cookies 181 | } 182 | }, callback); 183 | }; 184 | 185 | /** 186 | * Set / change environment variable 187 | * @param {object} params 188 | * @param {string} [params.vps] - The server _id. 189 | * @param {string} [params.app] - The app _id. 190 | * @param {array} [params.key] - The env var name 191 | * @param {array} [params.val] - The env var value 192 | * @param {function} callback 193 | */ 194 | self.setenv = function (params, callback) { 195 | 196 | var cookies = params.cookies || self.cookies; 197 | delete params.cookies; 198 | 199 | request({ 200 | method: 'PUT', 201 | uri: paastorUrl + '/vps/' + params.vps + '/apps/' + params.app + '/setvar', 202 | json: true, 203 | body: params, 204 | headers: { 205 | 'Cookie': cookies 206 | } 207 | }, callback); 208 | }; 209 | 210 | /** 211 | * Set / change environment variable 212 | * @param {object} params 213 | * @param {string} [params.vps] - The server _id. 214 | * @param {string} [params.version] - The version of Node.js to install. 215 | * @param {function} callback 216 | */ 217 | self.installNode = function (params, callback) { 218 | 219 | var cookies = params.cookies || self.cookies; 220 | delete params.cookies; 221 | 222 | request({ 223 | method: 'POST', 224 | uri: paastorUrl + '/vps/' + params.vps + '/node/' + params.version, 225 | json: true, 226 | headers: { 227 | 'Cookie': cookies 228 | } 229 | }, callback); 230 | }; 231 | 232 | self.pushPackage = function (params, callback) { 233 | 234 | var cookies = params.cookies || self.cookies; 235 | delete params.cookies; 236 | 237 | var uri = paastorUrl + '/vps/' + params.vps + '/apps/' + params.app + '/pkg'; 238 | 239 | delete params.vps; 240 | delete params.app; 241 | 242 | request({ 243 | method: 'PUT', 244 | uri: uri, 245 | json: true, 246 | headers: { 247 | 'Cookie': cookies 248 | }, 249 | body: params 250 | }, callback); 251 | }; 252 | 253 | self.createApp = function (params, callback) { 254 | var cookies = params.cookies || self.cookies; 255 | delete params.cookies; 256 | 257 | var uri = paastorUrl + '/vps/' + params.vps + '/apps'; 258 | 259 | delete params.vps; 260 | delete params.app; 261 | 262 | request({ 263 | method: 'POST', 264 | uri: uri, 265 | json: true, 266 | headers: { 267 | 'Cookie': cookies 268 | }, 269 | body: params 270 | }, callback); 271 | }; 272 | } 273 | 274 | exports = module.exports = Client; 275 | -------------------------------------------------------------------------------- /paastor-cli/package.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "paastor", 3 | "version": "0.4.7", 4 | "homepage": "https://paastor.com", 5 | "description": "CLI and Library for managing your Paastor servers and deploy apps.", 6 | "main": "client.js", 7 | "scripts": { 8 | "test": "echo \"Error: no test specified\" && exit 1" 9 | }, 10 | "preferGlobal": true, 11 | "bin": { 12 | "pstr": "./cli.js" 13 | }, 14 | "author": "hello@paastor.com", 15 | "license": "MIT", 16 | "respository": { 17 | "type": "git", 18 | "url": "https://github.com/paastor/paastor" 19 | }, 20 | "bugs": { 21 | "url": "http://github.com/paastor/paastor/issues" 22 | }, 23 | "dependencies": { 24 | "archiver": "~0.11.0", 25 | "async": "~0.9.0", 26 | "cli-table": "~0.3.0", 27 | "colors": "~0.6.2", 28 | "commander": "~2.3.0", 29 | "promptly": "~0.2.0", 30 | "request": "~2.42.0", 31 | "semver": "~3.0.1" 32 | } 33 | } 34 | -------------------------------------------------------------------------------- /paastor-server/.jshintrc: -------------------------------------------------------------------------------- 1 | { 2 | 3 | // JSHint Default Configuration File (as on JSHint website) 4 | // See http://jshint.com/docs/ for more details 5 | 6 | "maxerr" : 50, // {int} Maximum error before stopping 7 | 8 | // Enforcing 9 | "bitwise" : true, // true: Prohibit bitwise operators (&, |, ^, etc.) 10 | "camelcase" : false, // true: Identifiers must be in camelCase 11 | "curly" : true, // true: Require {} for every new block or scope 12 | "eqeqeq" : true, // true: Require triple equals (===) for comparison 13 | "forin" : false, // true: Require filtering for..in loops with obj.hasOwnProperty() 14 | "immed" : false, // true: Require immediate invocations to be wrapped in parens e.g. `(function () { } ());` 15 | "indent" : 4, // {int} Number of spaces to use for indentation 16 | "latedef" : false, // true: Require variables/functions to be defined before being used 17 | "newcap" : false, // true: Require capitalization of all constructor functions e.g. `new F()` 18 | "noarg" : true, // true: Prohibit use of `arguments.caller` and `arguments.callee` 19 | "noempty" : true, // true: Prohibit use of empty blocks 20 | "nonew" : false, // true: Prohibit use of constructors for side-effects (without assignment) 21 | "plusplus" : false, // true: Prohibit use of `++` & `--` 22 | "quotmark" : false, // Quotation mark consistency: 23 | // false : do nothing (default) 24 | // true : ensure whatever is used is consistent 25 | // "single" : require single quotes 26 | // "double" : require double quotes 27 | "undef" : true, // true: Require all non-global variables to be declared (prevents global leaks) 28 | "unused" : true, // true: Require all defined variables be used 29 | "strict" : true, // true: Requires all functions run in ES5 Strict Mode 30 | "maxparams" : false, // {int} Max number of formal params allowed per function 31 | "maxdepth" : false, // {int} Max depth of nested blocks (within functions) 32 | "maxstatements" : false, // {int} Max number statements per function 33 | "maxcomplexity" : false, // {int} Max cyclomatic complexity per function 34 | "maxlen" : false, // {int} Max number of characters per line 35 | 36 | // Relaxing 37 | "asi" : false, // true: Tolerate Automatic Semicolon Insertion (no semicolons) 38 | "boss" : false, // true: Tolerate assignments where comparisons would be expected 39 | "debug" : false, // true: Allow debugger statements e.g. browser breakpoints. 40 | "eqnull" : false, // true: Tolerate use of `== null` 41 | "es5" : false, // true: Allow ES5 syntax (ex: getters and setters) 42 | "esnext" : false, // true: Allow ES.next (ES6) syntax (ex: `const`) 43 | "moz" : false, // true: Allow Mozilla specific syntax (extends and overrides esnext features) 44 | // (ex: `for each`, multiple try/catch, function expression…) 45 | "evil" : false, // true: Tolerate use of `eval` and `new Function()` 46 | "expr" : false, // true: Tolerate `ExpressionStatement` as Programs 47 | "funcscope" : false, // true: Tolerate defining variables inside control statements 48 | "globalstrict" : false, // true: Allow global "use strict" (also enables 'strict') 49 | "iterator" : false, // true: Tolerate using the `__iterator__` property 50 | "lastsemic" : false, // true: Tolerate omitting a semicolon for the last statement of a 1-line block 51 | "laxbreak" : false, // true: Tolerate possibly unsafe line breakings 52 | "laxcomma" : false, // true: Tolerate comma-first style coding 53 | "loopfunc" : false, // true: Tolerate functions being defined in loops 54 | "multistr" : false, // true: Tolerate multi-line strings 55 | "proto" : false, // true: Tolerate using the `__proto__` property 56 | "scripturl" : false, // true: Tolerate script-targeted URLs 57 | "shadow" : false, // true: Allows re-define variables later in code e.g. `var x=1; x=2;` 58 | "sub" : false, // true: Tolerate using `[]` notation when it can still be expressed in dot notation 59 | "supernew" : false, // true: Tolerate `new function () { ... };` and `new Object;` 60 | "validthis" : false, // true: Tolerate using this in a non-constructor function 61 | 62 | // Environments 63 | "browser" : true, // Web Browser (window, document, etc) 64 | "couch" : false, // CouchDB 65 | "devel" : true, // Development/debugging (alert, confirm, etc) 66 | "dojo" : false, // Dojo Toolkit 67 | "jquery" : false, // jQuery 68 | "mootools" : false, // MooTools 69 | "node" : true, // Node.js 70 | "nonstandard" : false, // Widely adopted globals (escape, unescape, etc) 71 | "prototypejs" : false, // Prototype and Scriptaculous 72 | "rhino" : false, // Rhino 73 | "worker" : false, // Web Workers 74 | "wsh" : false, // Windows Scripting Host 75 | "yui" : false, // Yahoo User Interface 76 | 77 | // Custom Globals 78 | "globals" : { 79 | // mocha 80 | "describe": true, 81 | "it": true, 82 | "before": true, 83 | "beforeEach": true, 84 | "after": true, 85 | "afterEach": true 86 | 87 | } // additional predefined global variables 88 | } -------------------------------------------------------------------------------- /paastor-server/bin/www.js: -------------------------------------------------------------------------------- 1 | 'use strict'; 2 | var debug = require('debug')('paastor'); 3 | var logger = require('debug')('redir'); 4 | var app = require('../paastor'); 5 | var http = require('http'); 6 | var https = require('https'); 7 | var fs = require('fs'); 8 | 9 | // if (process.env.NODE_ENV === 'production') { 10 | 11 | // var sslServer = https.createServer({ 12 | // key: fs.readFileSync(__dirname + '/../paastor.key'), 13 | // cert: fs.readFileSync(__dirname + '/../paastor.crt'), 14 | // ca: [fs.readFileSync(__dirname + '/../gd_bundle-g2-g1.crt')] // godaddy ssl CA 15 | // }, app).listen(3443, function () { 16 | // debug('Paastor SSL server listening on port ' + sslServer.address().port); 17 | // }); 18 | 19 | // var server = http.createServer(function (req, res) { 20 | // var secureLocation = 'https://' + req.headers.host + req.url; 21 | // logger(+new Date(), secureLocation); 22 | // res.writeHead(302, { 23 | // 'Content-Type': 'text/plain', 24 | // 'Location': secureLocation 25 | // }); 26 | // res.end('Redirecting to SSL\n'); 27 | // }).listen(3380, function () { 28 | // debug('Paastor redirect service listening on port ' + server.address().port); 29 | // }); 30 | 31 | // } 32 | // else { 33 | var server = http.createServer(app).listen(process.env.PAASTOR_PORT || 2999, function () { 34 | debug('Paastor redirect service listening on port ' + server.address().port); 35 | }); 36 | // } 37 | -------------------------------------------------------------------------------- /paastor-server/config.js: -------------------------------------------------------------------------------- 1 | var config = { 2 | redisSession: { 3 | host: 'localhost', 4 | port: '6379', 5 | secret: '' 6 | }, 7 | mongo: "mongodb://localhost/paastor", 8 | email: { 9 | key: "", 10 | from: "hello@mydomain.com" 11 | }, 12 | url: "http://localhost:2999", 13 | stripe: { 14 | public: '', 15 | secret: '' 16 | }, 17 | sheepDownload: "http://localhost/or-something" 18 | }; 19 | 20 | if (process.env.NODE_ENV === 'production') { 21 | config.redisSession = { 22 | host: process.env.REDIS_HOST || config.redisSession.host, 23 | port: process.env.REDIS_PORT || config.redisSession.port, 24 | secret: process.env.REDIS_SECRET || config.redisSession.secret, 25 | password: process.env.REDIS_PASS || config.redisSession.password 26 | }; 27 | 28 | config.url = "https://paastor.com"; 29 | config.mongo = "some-uri" 30 | config.stripe = { 31 | public: '', 32 | secret: '' 33 | }; 34 | } 35 | 36 | exports = module.exports = config; 37 | -------------------------------------------------------------------------------- /paastor-server/gulpfile.js: -------------------------------------------------------------------------------- 1 | var gulp = require('gulp'); 2 | var jade = require('gulp-jade'); 3 | var notify = require('gulp-notify'); 4 | var browserify = require('gulp-browserify'); 5 | var uglify = require('gulp-uglify'); 6 | 7 | gulp.task('js', function () { 8 | // Single entry point to browserify 9 | gulp.src('./public/js/app.js') 10 | .pipe(browserify({ 11 | insertGlobals : true, 12 | debug: !gulp.env.production 13 | })) 14 | .pipe(uglify()) 15 | .pipe(gulp.dest('./public/build/js')) 16 | .pipe(notify({ message: 'JS recompiled' })); 17 | }); 18 | 19 | gulp.task('jade-templates', function () { 20 | return gulp.src('./views/templates/**/*.jade') 21 | .pipe(jade({ 22 | pretty: true, 23 | })) 24 | .pipe(gulp.dest('./public/build/html/')) 25 | .pipe(notify({ message: 'Jade angular views rebuilt' })); 26 | }); 27 | 28 | gulp.task('jade-pages', function () { 29 | return gulp.src(['./views/pages/**/*.jade']) 30 | .pipe(jade({ 31 | pretty: true, 32 | })) 33 | .pipe(gulp.dest('./public/pages/')) 34 | .pipe(notify({ message: 'Jade pages rebuilt' })); 35 | }); 36 | 37 | gulp.task('watch', function () { 38 | gulp.watch('./views/templates/**/*.jade', ['jade-templates']); 39 | gulp.watch(['./views/pages/**/*.jade', './views/layout.jade'], ['jade-pages']); 40 | gulp.watch('./public/js/**/*.js', ['js']); 41 | }); 42 | 43 | gulp.task('default', ['jade-templates', 'jade-pages', 'js', 'watch']); 44 | -------------------------------------------------------------------------------- /paastor-server/lib/genrand.js: -------------------------------------------------------------------------------- 1 | 'use strict'; 2 | var crypto = require('crypto'); 3 | /** 4 | * Asynchronously get a crypto random string of a certain length with chars 0-9 a-z A-Z. 5 | * @param number len 6 | * @param function cb - With arguments: [err, randOut, totalTimeReadable] 7 | */ 8 | exports = module.exports = function randomValueBase64 (len, cb) { 9 | var starttime = process.hrtime(); 10 | return crypto.randomBytes(Math.ceil(len * 3 / 4), function (err, buf) { 11 | if (err) { 12 | return cb(err); 13 | } 14 | var randOut = buf 15 | .toString('base64') // convert to base64 format 16 | .slice(0, len) // return required number of characters 17 | .replace(/\+/g, '0') // replace '+' with '0' 18 | .replace(/\//g, '0'); // replace '/' with '0' 19 | var endtime = process.hrtime(starttime); 20 | var totalTimeReadable = (endtime[0] > 0 ? endtime[0] + 's ' : '') + (endtime[1] / 1000000).toFixed(3) + 'ms' ; 21 | cb(null, randOut, totalTimeReadable); 22 | }); 23 | }; 24 | -------------------------------------------------------------------------------- /paastor-server/lib/hash.js: -------------------------------------------------------------------------------- 1 | 'use strict'; 2 | var crypto = require('crypto'); 3 | exports = module.exports = function (pass) { 4 | return crypto.createHash('sha256').update(pass).digest('base64'); 5 | }; 6 | -------------------------------------------------------------------------------- /paastor-server/paastor.js: -------------------------------------------------------------------------------- 1 | 'use strict'; 2 | var config = require('./config'); 3 | var debug = require('debug')('paastor'); 4 | 5 | /** 6 | * Handle uncaught exceptions. 7 | */ 8 | process.on('uncaughtException', function (err) { 9 | debug('\n------ uncaughtException ------\n', err.message); 10 | debug(err.stack, '\n'); 11 | process.exit(1); 12 | }); 13 | 14 | var express = require('express'); 15 | var redis = require('redis'); 16 | var session = require('express-session'); 17 | var RedisStore = require('connect-redis')(session); 18 | var express = require('express'); 19 | var redisClient = redis.createClient(config.redisSession.port, config.redisSession.host); 20 | redisClient.on('connect', function () { 21 | debug('Redis is connected'); 22 | }); 23 | redisClient.on('error', function (err) { 24 | debug('Redis error', err); 25 | }); 26 | 27 | redisClient.auth(config.redisSession.password, function (err) { 28 | if (err) { 29 | debug('Redis auth error', err); 30 | return; 31 | } 32 | debug('Redis authenticated'); 33 | }); 34 | 35 | var path = require('path'); 36 | var favicon = require('static-favicon'); 37 | var logger = require('morgan'); 38 | var cookieParser = require('cookie-parser'); 39 | var bodyParser = require('body-parser'); 40 | var Mandrill = require('node-mandrill'); 41 | 42 | var home = require('./routes/home'); 43 | var api = require('./routes/api'); 44 | 45 | var dataModels = require('./models'); 46 | var mandrill = new Mandrill(config.email.key); 47 | var stripe = require('stripe')(config.stripe.secret); 48 | 49 | var stripePlans = []; 50 | stripe.plans.list({ limit: 100 }, function (err, plans) { 51 | if (err) { 52 | debug(err); 53 | return; 54 | } 55 | plans = plans || { data: [] }; 56 | var output = plans.data.map(function (plan) { 57 | return { 58 | id: plan.id, 59 | name: plan.name, 60 | amount: plan.amount, 61 | interval: plan.interval 62 | }; 63 | }); 64 | output.sort(function (a, b) { 65 | if (a.amount > b.amount) { 66 | return 1; 67 | } 68 | if (a.amount < b.amount) { 69 | return -1; 70 | } 71 | return 0; 72 | }); 73 | debug('Got ' + output.length + ' plans from Stripe'); 74 | stripePlans = output; 75 | }); 76 | 77 | var app = express(); 78 | if (process.env.NODE_ENV === 'production') { 79 | app.enable('trust proxy'); 80 | app.use(function (req, res, next) { 81 | if (req.headers && req.headers.referrer && req.headers.referrer.indexOf('http://') !== -1) { 82 | return res.redirect(301, config.url + req.url); 83 | } 84 | if (req.host && req.host.indexOf('www.') !== -1) { 85 | return res.redirect(301, config.url + req.url); 86 | } 87 | next(); 88 | }); 89 | } 90 | 91 | // view engine setup 92 | app.set('views', path.join(__dirname, 'views')); 93 | app.set('view engine', 'jade'); 94 | 95 | app.use(dataModels); 96 | app.use(function (req, res, next) { 97 | req.mandrill = mandrill; 98 | req.plans = stripePlans; 99 | next(); 100 | }); 101 | 102 | app.use(favicon()); 103 | app.use(bodyParser.json({ limit: '100mb' })); 104 | app.use(bodyParser.urlencoded({ extended: true })); 105 | app.use(cookieParser()); 106 | app.use(express.static(path.join(__dirname, 'public'))); 107 | app.use(logger('dev')); 108 | app.use(session({ 109 | store: new RedisStore({ 110 | client: redisClient 111 | }), 112 | secret: config.redisSession.secret, 113 | resave: true, 114 | saveUninitialized: true, 115 | unset: 'destroy' 116 | })); 117 | app.use(function (req, res, next) { 118 | res.set('X-Powered-By', undefined); 119 | 120 | next(); 121 | }); 122 | app.use('/', home); 123 | app.use('/api/', api); 124 | 125 | /// catch 404 and forward to error handler 126 | app.use(function(req, res, next) { 127 | var err = new Error('Paastor route not matched'); 128 | err.status = 404; 129 | next(err); 130 | }); 131 | 132 | 133 | // will print stacktrace 134 | 135 | app.use(function errHandler(err, req, res, next) { 136 | res.status(err.status || 500); 137 | var output = { 138 | message: err.message, 139 | error: err 140 | }; 141 | if (process.env.NODE_ENV === 'production') { 142 | delete err.stack; 143 | } 144 | if (req.accepts('json')) { 145 | return res.send(output); 146 | } 147 | res.render('error', output); 148 | }); 149 | 150 | 151 | 152 | exports = module.exports = app; 153 | -------------------------------------------------------------------------------- /paastor-server/package.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "paastor", 3 | "version": "0.1.1", 4 | "main": "bin/www.js", 5 | "license": "MIT", 6 | "scripts": { 7 | "start": "DEBUG=mongoose,paastor,sheep-client,sheep-ssh-client,redir node bin/www.js" 8 | }, 9 | "dependencies": { 10 | "archiver": "^0.10.1", 11 | "async": "^0.9.0", 12 | "body-parser": "^1.6.5", 13 | "connect-redis": "^2.1.0", 14 | "cookie-parser": "^1.3.2", 15 | "cookie-session": "git+https://github.com/ForbesLindesay/cookie-session#09ef5bbc89fc470525e3743ff0e0c772eed14d33", 16 | "debug": "^0.7.4", 17 | "express": "^4.8.5", 18 | "express-session": "^1.8.2", 19 | "gift": "^0.4.2", 20 | "jade": "^1.3.1", 21 | "lodash": "^2.4.1", 22 | "mongodb": "^1.4.12", 23 | "mongoose": "^3.8.15", 24 | "morgan": "^1.2.3", 25 | "node-mandrill": "^1.0.1", 26 | "node-uuid": "^1.4.1", 27 | "redis": "^0.12.1", 28 | "request": "^2.39.0", 29 | "rimraf": "^2.2.8", 30 | "ssh-keygen": "^0.2.1", 31 | "ssh2": "^0.3.4", 32 | "static-favicon": "^1.0.2", 33 | "strength": "^0.1.4", 34 | "stripe": "^2.8.0", 35 | "uuid": "^1.4.1" 36 | }, 37 | "devDependencies": { 38 | "gulp": "^3.8.7", 39 | "gulp-browserify": "^0.5.0", 40 | "gulp-jade": "^0.7.0", 41 | "gulp-notify": "^1.5.0", 42 | "gulp-uglify": "^1.0.1", 43 | "mocha": "^1.21.3", 44 | "should": "^4.0.4", 45 | "supertest": "^0.13.0" 46 | }, 47 | "engines": { 48 | "node": "0.10.31" 49 | }, 50 | "domains": [ 51 | "paastor.com", 52 | "www.paastor.com" 53 | ] 54 | } 55 | -------------------------------------------------------------------------------- /paastor-server/public/css/style.css: -------------------------------------------------------------------------------- 1 | body { 2 | padding: 13px 21px; 3 | } 4 | 5 | .container { 6 | max-width: 800px; 7 | font-size: 16px; 8 | } 9 | 10 | .left { 11 | text-align: left; 12 | } 13 | .right { 14 | text-align: right; 15 | } 16 | .center { 17 | text-align: center; 18 | } 19 | 20 | .smaller { 21 | font-size: .8em; 22 | } 23 | 24 | .wrapall { 25 | word-break: break-word; 26 | } 27 | 28 | .monospace { 29 | font-family: 'Menlo', 'Monaco', 'Consolas', monospace; 30 | } 31 | 32 | .white { 33 | color: #ffffff; 34 | } 35 | 36 | .clickable { 37 | cursor: pointer; 38 | } 39 | 40 | #footer { 41 | position: fixed; 42 | left: 0; 43 | right: 0; 44 | bottom: 0; 45 | padding: 8px 13px 0 13px; 46 | background: #fff; 47 | box-shadow: 1px 2px 10px rgba(0,0,0,.5); 48 | z-index: 200; 49 | } 50 | 51 | /* image sizing to match fa-* */ 52 | img.img-1x { 53 | margin-top: -4px; 54 | width: 16px; 55 | height: 16px; 56 | } 57 | img.img-2x { 58 | margin-top: -4px; 59 | width: 30px; 60 | height: 30px; 61 | } 62 | img.img-3x { 63 | width: 51px; 64 | height: 51px; 65 | } 66 | .storage-panel { 67 | margin: 0 3% 6px 3%; 68 | padding: 1px 20px 69 | } 70 | -------------------------------------------------------------------------------- /paastor-server/public/dash.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/ruffrey/paastor/8e83ea8acabf33a121aca419d2a067c8b81cff1c/paastor-server/public/dash.png -------------------------------------------------------------------------------- /paastor-server/public/diagram.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/ruffrey/paastor/8e83ea8acabf33a121aca419d2a067c8b81cff1c/paastor-server/public/diagram.png -------------------------------------------------------------------------------- /paastor-server/public/email-logo.gif: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/ruffrey/paastor/8e83ea8acabf33a121aca419d2a067c8b81cff1c/paastor-server/public/email-logo.gif -------------------------------------------------------------------------------- /paastor-server/public/email-logo.xcf: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/ruffrey/paastor/8e83ea8acabf33a121aca419d2a067c8b81cff1c/paastor-server/public/email-logo.xcf -------------------------------------------------------------------------------- /paastor-server/public/favicon-old-white.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/ruffrey/paastor/8e83ea8acabf33a121aca419d2a067c8b81cff1c/paastor-server/public/favicon-old-white.png -------------------------------------------------------------------------------- /paastor-server/public/favicon-old.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/ruffrey/paastor/8e83ea8acabf33a121aca419d2a067c8b81cff1c/paastor-server/public/favicon-old.png -------------------------------------------------------------------------------- /paastor-server/public/favicon.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/ruffrey/paastor/8e83ea8acabf33a121aca419d2a067c8b81cff1c/paastor-server/public/favicon.png -------------------------------------------------------------------------------- /paastor-server/public/js/app.js: -------------------------------------------------------------------------------- 1 | angular.module('readableTime', []).filter('readableTime', require('./readable-time')); 2 | Stripe.setPublishableKey( 3 | location.hostname.indexOf('paastor.com') === -1 4 | ? 'pk_test_GdSOMC1qnIe80eWMZkhVoDXR' 5 | : 'pk_live_pcMlMokqIbmjbureLdobpP8D' 6 | ); 7 | 8 | var ngmodules = ['ngResource', 'readableTime']; 9 | if (window.location.pathname === '/') { 10 | ngmodules.push('ngRoute'); 11 | } 12 | var paastor = angular.module('paastor', ngmodules); 13 | 14 | require('./controllers/NavController')(paastor); 15 | require('./controllers/VpsCreateController')(paastor); 16 | require('./controllers/VpsListController')(paastor); 17 | require('./controllers/VpsViewController')(paastor); 18 | require('./controllers/AppCreateController')(paastor); 19 | require('./controllers/AppSslController')(paastor); 20 | require('./controllers/RedisController')(paastor); 21 | require('./controllers/MongoController')(paastor); 22 | require('./controllers/AccountEditController')(paastor); 23 | require('./controllers/ManageServicesController')(paastor); 24 | 25 | require('./services.js')(paastor); 26 | 27 | paastor.directive('paEnter', require('./directives/pa-enter.js')); 28 | 29 | if (window.location.pathname === '/') { 30 | paastor 31 | .config(['$routeProvider', 32 | function ($routeProvider) { 33 | 34 | $routeProvider 35 | .when('/', { 36 | templateUrl: 'build/html/home.html' 37 | }) 38 | .when('/manage-services', { 39 | templateUrl: 'build/html/manage-services.html', 40 | controller: 'ManageServicesController' 41 | }) 42 | .when('/account', { 43 | templateUrl: 'build/html/account-edit.html', 44 | controller: 'AccountEditController' 45 | }) 46 | .when('/list', { 47 | templateUrl: 'build/html/vps-list.html', 48 | controller: 'VpsListController' 49 | }) 50 | .when('/vps', { 51 | templateUrl: 'build/html/vps-create.html', 52 | controller: 'VpsCreateController' 53 | }) 54 | .when('/vps/:_id', { 55 | templateUrl: 'build/html/vps-view.html', 56 | controller: 'VpsViewController' 57 | }) 58 | .when('/vps/:_id/app', { 59 | templateUrl: 'build/html/app-create.html', 60 | controller: 'AppCreateController' 61 | }) 62 | .when('/vps/:vps/ssl/:app', { 63 | templateUrl: 'build/html/app-ssl.html', 64 | controller: 'AppSslController' 65 | }) 66 | .when('/vps/:vps/redis', { 67 | templateUrl: 'build/html/redis.html', 68 | controller: 'RedisController' 69 | }) 70 | .when('/vps/:vps/mongo', { 71 | templateUrl: 'build/html/mongo.html', 72 | controller: 'MongoController' 73 | }) 74 | .otherwise({ 75 | templateUrl: 'build/html/404.html' 76 | }); 77 | 78 | } 79 | ]); 80 | } 81 | else { 82 | 83 | } 84 | -------------------------------------------------------------------------------- /paastor-server/public/js/controllers/AccountEditController.js: -------------------------------------------------------------------------------- 1 | 2 | exports = module.exports = function (ngApp) { 3 | ngApp.controller('AccountEditController', [ 4 | '$scope', 5 | 'Account', 6 | 'Payments', 7 | '$rootScope', 8 | function ($scope, Account, Payments, $rootScope) { 9 | $scope.vps = {}; 10 | $scope.message = ""; 11 | $scope.password = ""; 12 | $scope.passwordConf = ""; 13 | $scope.card = null; 14 | $scope.newCard = {}; 15 | 16 | Account.getCard(function (err, card) { 17 | if (err) { 18 | $scope.message = err.error; 19 | return; 20 | } 21 | $scope.card = card; 22 | }); 23 | 24 | $scope.changePassword = function () { 25 | $scope.message = ""; 26 | if (!$scope.password) { 27 | $scope.message = "Password is required." 28 | return; 29 | } 30 | if ($scope.password !== $scope.passwordConf) { 31 | $scope.message = "Passwords don't match."; 32 | return; 33 | } 34 | Account.update({ 35 | password: $scope.password 36 | }, function (err, account) { 37 | if (err) { 38 | $scope.message = err.error; 39 | return; 40 | } 41 | $scope.password = ""; 42 | $scope.passwordConf = ""; 43 | $scope.message = "Password was changed."; 44 | }); 45 | 46 | }; 47 | 48 | $scope.setCard = function () { 49 | $scope.message = ""; 50 | Payments.card($scope.newCard, function (err, card) { 51 | console.log(err, card); 52 | if (err) { 53 | $scope.message = err.error; 54 | $scope.$apply(); 55 | return; 56 | } 57 | $scope.card = card; 58 | $scope.newCard = {}; 59 | $scope.message = "Successfully updated card."; 60 | }); 61 | }; 62 | } 63 | ]); 64 | }; -------------------------------------------------------------------------------- /paastor-server/public/js/controllers/AppCreateController.js: -------------------------------------------------------------------------------- 1 | 2 | exports = module.exports = function (ngApp) { 3 | ngApp.controller('AppCreateController', [ 4 | '$scope', 5 | 'Vps', 6 | 'App', 7 | '$routeParams', 8 | '$interval', 9 | function ($scope, Vps, App, $routeParams, $interval) { 10 | $scope.app = {vps: $routeParams._id }; 11 | $scope.message = ""; 12 | $scope.saving = false; 13 | $scope.done = false; 14 | $scope.logs = ""; 15 | 16 | $scope.save = function () { 17 | $scope.saving = true; 18 | $scope.message = "Creating and starting the app. This will take several minutes."; 19 | 20 | App.create($scope.app, function (err, data) { 21 | $scope.saving = false; 22 | if (err) { 23 | $scope.message = err.message || err.error || err; 24 | return 25 | } 26 | 27 | $scope.message = "Done."; 28 | $scope.saving = false; 29 | $scope.done = true; 30 | }); 31 | }; 32 | 33 | } 34 | ]); 35 | }; -------------------------------------------------------------------------------- /paastor-server/public/js/controllers/AppSslController.js: -------------------------------------------------------------------------------- 1 | 2 | exports = module.exports = function (ngApp) { 3 | ngApp.controller('AppSslController', [ 4 | '$scope', 5 | 'App', 6 | '$routeParams', 7 | function ($scope, App, $routeParams) { 8 | $scope.app = {vps: $routeParams.vps, _id: $routeParams.app }; 9 | $scope.message = ""; 10 | $scope.saving = false; 11 | $scope.done = false; 12 | 13 | $scope.save = function () { 14 | $scope.saving = true; 15 | $scope.message = ""; 16 | 17 | App.setSsl($scope.app, function (err, data) { 18 | $scope.saving = false; 19 | if (err) { 20 | $scope.message = err.message || err.error || err; 21 | return 22 | } 23 | $scope.done = true; 24 | $scope.saving = false; 25 | }); 26 | }; 27 | 28 | } 29 | ]); 30 | }; -------------------------------------------------------------------------------- /paastor-server/public/js/controllers/ManageServicesController.js: -------------------------------------------------------------------------------- 1 | var uuid = require('uuid'); 2 | exports = module.exports = function (ngApp) { 3 | ngApp.controller('ManageServicesController', [ 4 | '$scope', 5 | '$rootScope', 6 | 'Account', 7 | 'Payments', 8 | function ($scope, $rootScope, Account, Payments) { 9 | $scope.Math = window.Math; 10 | 11 | $scope.message = ""; 12 | $scope.hasNoCard = false; 13 | $scope.subMsg = ""; // subscription message 14 | $scope.buyMsg = ""; // purchase message 15 | 16 | // Pre load some data 17 | $scope.subscriptions = []; 18 | $scope.plans = []; 19 | $scope.card = null; 20 | 21 | Payments.getPlans(function (err, data) { 22 | if (err) { 23 | $scope.message = err.error; 24 | return; 25 | } 26 | $scope.plans = data; 27 | }); 28 | $scope.subs = function () { 29 | Payments.getSubscriptions(function (err, data) { 30 | if (err) { 31 | $scope.message = err.error; 32 | return; 33 | } 34 | $scope.subscriptions = data; 35 | }); 36 | }; 37 | $scope.subs(); 38 | Account.getCard(function (err, card) { 39 | if (err) { 40 | $scope.message = err.error; 41 | return; 42 | } 43 | if (!card || !card.last4) { 44 | $scope.hasNoCard = true; 45 | } 46 | }); 47 | 48 | 49 | $scope.addService = function (plan) { 50 | $scope.buyMsg = ""; 51 | plan.quantity = Math.floor(parseFloat(plan.quantity)); 52 | if (isNaN(plan.quantity) || plan.quantity < 1) { 53 | $scope.buyMsg = "Plan quantity is invalid."; 54 | return; 55 | } 56 | 57 | var conf = confirm('Please confirm this subscription.\n\nYou will be billed ' + plan.interval + 'ly at $' + ((plan.amount * plan.quantity)/100).toFixed(2) + ' for the following service:\n\n' +plan.quantity + 'x ' + plan.name); 58 | if (!conf) { 59 | return; 60 | } 61 | Payments.addSubscription(plan, function (err, subscriptions) { 62 | if (err) { 63 | $scope.buyMsg = err.error; 64 | return; 65 | } 66 | Account.get(function (err, account) { 67 | if (err) { 68 | return; 69 | } 70 | $rootScope.account = account; 71 | }); 72 | $scope.subscriptions = subscriptions; 73 | }); 74 | }; 75 | 76 | $scope.downgrade = function (sub) { 77 | $scope.subMsg = ""; 78 | sub.down = Math.floor(parseFloat(sub.down)); 79 | if (isNaN(sub.down) || sub.down < 1) { 80 | $scope.subMsg = "Downgrade quantity is invalid."; 81 | return; 82 | } 83 | if (sub.down > sub.quantity) { 84 | $scope.subMsg = "Downgrade quantity cannot exceed current subscription quantity."; 85 | return; 86 | } 87 | 88 | var conf = confirm("Please confirm your downgrade. ALL CREDITS will be lost and the changes will be applied IMMEDIATELY.\n\nREMOVE " + sub.down + "x " + sub.plan.name + "\n\nNew quantity will be " + (sub.quantity - sub.down) + "x"); 89 | if (!conf) { 90 | return; 91 | } 92 | var plan = { 93 | quantity: -sub.down, 94 | id: sub.plan.id, 95 | name: sub.plan.name 96 | }; 97 | Payments.addSubscription(plan, function (err, subscriptions) { 98 | if (err) { 99 | $scope.subMsg = err.error; 100 | return; 101 | } 102 | Account.get(function (err, account) { 103 | if (err) { 104 | return; 105 | } 106 | $rootScope.account = account; 107 | }); 108 | $scope.subscriptions = subscriptions; 109 | }); 110 | }; 111 | } 112 | ]); 113 | }; -------------------------------------------------------------------------------- /paastor-server/public/js/controllers/MongoController.js: -------------------------------------------------------------------------------- 1 | 2 | exports = module.exports = function (ngApp) { 3 | ngApp.controller('MongoController', [ 4 | '$scope', 5 | 'Vps', 6 | '$routeParams', 7 | '$log', 8 | function ($scope, Vps, $routeParams, $log) { 9 | $scope.vps = null; 10 | $scope.message = ""; 11 | $scope.loading = false; 12 | $scope.removed = false; 13 | $scope.installed = false; 14 | $scope.sshPassword = ""; 15 | 16 | var minport = 27000; 17 | var maxport = 27999; 18 | var notallowed = [27014, 27374, 27950]; 19 | var randPort = function () { 20 | var outport = Math.floor(Math.random() * (maxport - minport + 1)) + minport; 21 | // recursion 22 | if (notallowed.indexOf(outport) !== -1) { 23 | $log.debug('ports collided', outport); 24 | outport = randPort(); 25 | } 26 | return outport; 27 | }; 28 | $scope.portgen = function () { 29 | $scope.mongo.port = randPort(); 30 | }; 31 | 32 | $scope.mongo = { 33 | port: 27017, 34 | password: "", 35 | username: "", 36 | localOnly: true, 37 | rootPassword: "" 38 | }; 39 | 40 | Vps.get($routeParams.vps, function (err, vps) { 41 | if (err) { 42 | $scope.message = err.message || err.error || err; 43 | return; 44 | } 45 | $scope.vps = vps; 46 | }) 47 | 48 | $scope.install = function (params) { 49 | if ($scope.mongo.username && !$scope.mongo.password) { 50 | $scope.message = "Password is required when using a username."; 51 | return; 52 | } 53 | if ($scope.mongo.password && !$scope.mongo.username) { 54 | $scope.message = "Username is required when using a password."; 55 | return; 56 | } 57 | $scope.removed = false; 58 | $scope.loading = true; 59 | $scope.message = "Installing MongoDB..."; 60 | 61 | Vps.installMongo($scope.vps._id, $scope.mongo, function (err, res) { 62 | $scope.loading = false; 63 | if (err) { 64 | $scope.message = err.message || err.error || err || "Connection hung"; 65 | return; 66 | } 67 | $scope.installed = true; 68 | $scope.message = ""; 69 | }); 70 | }; 71 | 72 | $scope.uninstall = function () { 73 | $scope.installed = false; 74 | $scope.loading = true; 75 | $scope.message = "Removing MongoDB..."; 76 | 77 | Vps.uninstallMongo({ _id: $scope.vps._id, rootPassword: $scope.mongo.rootPassword}, function (err, res) { 78 | $scope.loading = false; 79 | if (err) { 80 | $scope.message = err.message || err.error || err; 81 | return; 82 | } 83 | $scope.mongo = res; 84 | $scope.vps.mongo = false; 85 | $scope.removed = true; 86 | $scope.message = ""; 87 | }); 88 | }; 89 | 90 | } 91 | ]); 92 | }; -------------------------------------------------------------------------------- /paastor-server/public/js/controllers/NavController.js: -------------------------------------------------------------------------------- 1 | var hash = require('../../../lib/hash'); 2 | 3 | exports = module.exports = function (ngApp) { 4 | ngApp.controller('NavController', [ 5 | '$scope', 6 | '$rootScope', 7 | '$location', 8 | 'Account', 9 | function ($scope, $rootScope, $location, Account) { 10 | $scope.signin = { 11 | email: "", 12 | password: "" 13 | }; 14 | $scope.signup = { 15 | email: "", 16 | password: "" 17 | }; 18 | $scope.confirming = { 19 | _id: "", 20 | conf: "" 21 | }; 22 | $scope.resetPass; 23 | $scope.message = ""; 24 | 25 | Account.get(function (err, account) { 26 | if (err && window.location.hash && window.location.hash !== '#/') { 27 | window.open('/#/', '_self'); 28 | return; 29 | } 30 | if (!account && window.location.hash && window.location.hash !== '#/') { 31 | window.open('/#/', '_self'); 32 | return; 33 | } 34 | $rootScope.account = account; 35 | }); 36 | 37 | 38 | $scope.login = function () { 39 | $scope.message = ""; 40 | 41 | if (!$scope.signin.email) { 42 | $scope.message = "Missing email"; 43 | return; 44 | } 45 | if (!$scope.signin.password) { 46 | $scope.message = "Missing password"; 47 | return; 48 | } 49 | 50 | Account.login({ email: $scope.signin.email, password: $scope.signin.password }, function (err, data) { 51 | if (err) { 52 | $scope.message = err.error; 53 | return; 54 | } 55 | $scope.signin = {}; 56 | $rootScope.account = data; 57 | $location.path('/list'); 58 | }); 59 | }; 60 | 61 | $scope.logout = function () { 62 | $scope.message = ""; 63 | Account.logout(function (err) { 64 | if (err) { 65 | $scope.message = err.error; 66 | return; 67 | } 68 | $rootScope.account = null; 69 | window.open('/#/', '_self'); 70 | }); 71 | }; 72 | 73 | $scope.register = function () { 74 | $scope.message = ""; 75 | Account.create($scope.signup, function (err, account) { 76 | if (err) { 77 | $scope.message = err.error; 78 | return; 79 | } 80 | // do not log them in. need to confirm account first. 81 | $scope.message = "Success! Check your email to confirm your account."; 82 | $scope.signup = {}; 83 | }); 84 | }; 85 | 86 | $scope.forgot = function () { 87 | $scope.message = ""; 88 | if (!$scope.signin.email) { 89 | $scope.message = "Put in your email first"; 90 | return; 91 | } 92 | Account.forgotPassword($scope.signin.email, function (err, data) { 93 | if (err) { 94 | $scope.message = err.error; 95 | return; 96 | } 97 | $scope.signin = {}; 98 | $scope.message = data.message; 99 | }); 100 | }; 101 | 102 | $scope.doPasswordReset = function () { 103 | $scope.message = ""; 104 | if (!$scope.resetPass.password) { 105 | $scope.message = "Password is required."; 106 | return; 107 | } 108 | if ($scope.resetPass.password !== $scope.resetPass.passwordConf) { 109 | $scope.message = "Passwords must match."; 110 | return; 111 | } 112 | 113 | Account.resetPassword($scope.resetPass._id, $scope.resetPass.conf, $scope.resetPass.password, function (err, account) { 114 | if (err) { 115 | $scope.message = err.error; 116 | return; 117 | } 118 | $rootScope.account = account; 119 | $scope.resetPass = {}; 120 | window.open('/#/', '_self'); 121 | }); 122 | }; 123 | } 124 | ]); 125 | }; -------------------------------------------------------------------------------- /paastor-server/public/js/controllers/RedisController.js: -------------------------------------------------------------------------------- 1 | 2 | exports = module.exports = function (ngApp) { 3 | ngApp.controller('RedisController', [ 4 | '$scope', 5 | 'Vps', 6 | '$routeParams', 7 | '$log', 8 | function ($scope, Vps, $routeParams, $log) { 9 | $scope.vps = null; 10 | $scope.message = ""; 11 | $scope.loading = false; 12 | $scope.removed = false; 13 | $scope.installed = false; 14 | $scope.sshPassword = ""; 15 | 16 | var minport = 7011; 17 | var maxport = 7470; 18 | var notallowed = [7022, 7023, 7025, 7047, 7080, 7262, 7306, 7307, 7312, 7396, 7400, 7401, 7402]; 19 | var randPort = function () { 20 | var outport = Math.floor(Math.random() * (maxport - minport + 1)) + minport; 21 | // recursion 22 | if (notallowed.indexOf(outport) !== -1) { 23 | $log.debug('ports collided', outport); 24 | outport = randPort(); 25 | } 26 | return outport; 27 | }; 28 | $scope.portgen = function () { 29 | $scope.redis.port = randPort(); 30 | }; 31 | 32 | $scope.redis = { 33 | port: 6379, 34 | password: "", 35 | generatePassword: true, 36 | noPassword: false, 37 | localOnly: true, 38 | rootPassword: "" 39 | }; 40 | 41 | Vps.get($routeParams.vps, function (err, vps) { 42 | if (err) { 43 | $scope.message = err.message || err.error || err; 44 | return; 45 | } 46 | $scope.vps = vps; 47 | }) 48 | 49 | $scope.install = function (params) { 50 | $scope.removed = false; 51 | $scope.loading = true; 52 | $scope.message = "Installing Redis..."; 53 | 54 | Vps.installRedis($scope.vps._id, $scope.redis, function (err, res) { 55 | $scope.loading = false; 56 | if (err) { 57 | $scope.message = err.message || err.error || err; 58 | return; 59 | } 60 | $scope.redis.password = res.password; 61 | $scope.vps.redis = true; 62 | $scope.installed = true; 63 | $scope.message = ""; 64 | }); 65 | }; 66 | 67 | $scope.uninstall = function () { 68 | $scope.installed = false; 69 | $scope.loading = true; 70 | $scope.message = "Removing Redis..."; 71 | 72 | Vps.uninstallRedis({ _id: $scope.vps._id, rootPassword: $scope.redis.rootPassword}, function (err, res) { 73 | $scope.loading = false; 74 | if (err) { 75 | $scope.message = err.message || err.error || err; 76 | return; 77 | } 78 | $scope.redis = res; 79 | $scope.vps.redis = false; 80 | $scope.removed = true; 81 | $scope.message = ""; 82 | }); 83 | }; 84 | 85 | } 86 | ]); 87 | }; -------------------------------------------------------------------------------- /paastor-server/public/js/controllers/VpsCreateController.js: -------------------------------------------------------------------------------- 1 | 'use strict'; 2 | exports = module.exports = function (ngApp) { 3 | ngApp.controller('VpsCreateController', [ 4 | '$scope', 5 | 'Vps', 6 | '$timeout', 7 | '$interval', 8 | function ($scope, Vps, $timeout, $interval) { 9 | $scope.vps = {}; 10 | $scope.message = ""; 11 | $scope.saving = false; 12 | $scope.done = false; 13 | $scope.logs = ""; 14 | $scope.logcheck = null; // timer 15 | var logElement = document.getElementById('server-logs'); 16 | var setScroll = function () { 17 | logElement.scrollTop = logElement.scrollHeight; 18 | }; 19 | var checkLogs = function () { 20 | Vps.get($scope.vps._id, function (err, vps) { 21 | if (err) { 22 | $scope.logs += err.message || err.error || err; 23 | } 24 | else if (vps) { 25 | $scope.logs = vps.logs; 26 | } 27 | 28 | if (vps && vps.status !== "install") { 29 | $interval.cancel($scope.logcheck); 30 | } 31 | // BAD 32 | $timeout(setScroll, 1); 33 | }); 34 | }; 35 | 36 | $scope.save = function () { 37 | $scope.saving = true; 38 | $scope.message = "Installing your server. This will take several minutes. Please do not leave the page."; 39 | 40 | $scope.logs = ""; 41 | $scope.logcheck = $interval(checkLogs, 3000); 42 | 43 | Vps.create($scope.vps, function (err, data) { 44 | $scope.saving = false; 45 | $interval.cancel($scope.logcheck); 46 | if (err) { 47 | $scope.message = err.message || err.error; 48 | if ($scope.logs) { 49 | $scope.logs += $scope.message; 50 | } 51 | if (data !== 400) { 52 | Vps.remove($scope.vps._id, function (err, data) { 53 | if (err) { 54 | console.error('error removing vps after failed creation', err); 55 | return; 56 | } 57 | console.log('removed vps after failed creation', data); 58 | }); 59 | } 60 | // BAD 61 | setScroll(); 62 | return; 63 | } 64 | $scope.message = "Done."; 65 | $scope.done = true; 66 | $scope.vps = data; 67 | }); 68 | }; 69 | 70 | } 71 | ]); 72 | }; 73 | -------------------------------------------------------------------------------- /paastor-server/public/js/controllers/VpsListController.js: -------------------------------------------------------------------------------- 1 | var uuid = require('uuid'); 2 | exports = module.exports = function (ngApp) { 3 | ngApp.controller('VpsListController', [ 4 | '$scope', 5 | '$rootScope', 6 | '$location', 7 | 'Vps', 8 | 'Account', 9 | function ($scope, $rootScope, $location, Vps, Account) { 10 | $scope.$location = $location; 11 | $scope.vpses = null; 12 | $scope.message = ""; 13 | 14 | Vps.getAll(function (err, vpses) { 15 | if (err) { 16 | $scope.message = err.error; 17 | return; 18 | } 19 | $scope.vpses = vpses; 20 | }); 21 | 22 | $scope.hidekey = function () { 23 | delete $rootScope.account.sshkey_pub; 24 | }; 25 | 26 | $scope.viewSshKey = function () { 27 | $scope.message = ""; 28 | Account.get("sshkey_pub", function (err, account) { 29 | if (err) { 30 | $scope.message = err.error; 31 | return; 32 | } 33 | $rootScope.account = account; 34 | }); 35 | }; 36 | 37 | $scope.rekeygen = function () { 38 | $scope.message = ""; 39 | var really = confirm('Confirm ssh key regeneration.\n\nThis can never be undone.'); 40 | if (!really) { 41 | return; 42 | } 43 | Account.keygen(function (err, account) { 44 | if (err) { 45 | $scope.message = err.error; 46 | return; 47 | } 48 | $rootScope.account = account; 49 | }); 50 | }; 51 | } 52 | ]); 53 | }; -------------------------------------------------------------------------------- /paastor-server/public/js/controllers/VpsViewController.js: -------------------------------------------------------------------------------- 1 | exports = module.exports = function (ngApp) { 2 | ngApp.controller('VpsViewController', [ 3 | '$scope', 4 | '$routeParams', 5 | '$interval', 6 | 'Vps', 7 | 'App', 8 | function ($scope, $routeParams, $interval, Vps, App) { 9 | $scope.isUpgrading = false; 10 | $scope.message = ""; 11 | $scope.vps = null; 12 | 13 | var getvps = function () { 14 | $scope.message = ""; 15 | Vps.get($routeParams._id, function (err, vps) { 16 | $scope.message = ""; 17 | if (err) { 18 | $scope.vps = {}; 19 | $scope.message = err.message || err.error || err; 20 | return; 21 | } 22 | // likely a static html page response 23 | if (typeof vps.info === 'string') { 24 | vps.info = { error: vps.info }; 25 | } 26 | if (!vps.info || vps.info.error) { 27 | $scope.message = "Unable to retrieve server stats. Your server is not reachable. "; 28 | if (vps.info) { 29 | if (vps.info.message) { 30 | $scope.message += vps.info.message + ' '; 31 | } 32 | if (vps.info.error && vps.info.error.message) { 33 | $scope.message += vps.info.error.message; 34 | } 35 | } 36 | } 37 | // making sure proper objects are available for view. 38 | if (!vps.info) { 39 | vps.info = { apps: [] }; 40 | } 41 | if (!vps.info.apps) { 42 | vps.info.apps = []; 43 | } 44 | vps.info.apps.forEach(function (a) { 45 | a.env = JSON.stringify(a.env); 46 | }); 47 | $scope.vps = vps || $scope.vps; 48 | }); 49 | }; 50 | // $interval(getvps, 8000); 51 | getvps(); 52 | 53 | // remove vps 54 | $scope.remove = function () { 55 | $scope.message = ''; 56 | var conf = confirm("Really remove this server?"); 57 | if (!conf) { 58 | return; 59 | } 60 | Vps.remove($scope.vps._id, function (err) { 61 | if (err) { 62 | $scope.message = err.message || err.error || err; 63 | return; 64 | } 65 | window.open('/#/list', '_self'); 66 | }); 67 | }; 68 | 69 | $scope.updateSheep = function () { 70 | $scope.message = ''; 71 | $scope.isUpgrading = true; 72 | Vps.updateSheep($scope.vps._id, { password: $scope.password }, function (err, vps) { 73 | $scope.password = ""; 74 | $scope.isUpgrading = false; 75 | if (err) { 76 | $scope.message = err.message || err.error || err; 77 | return; 78 | } 79 | getvps(); 80 | $scope.showUpdatePanel = false; 81 | }); 82 | }; 83 | 84 | $scope.removeApp = function (app) { 85 | var conf = confirm("Really remove this app?"); 86 | if (!conf) { 87 | return; 88 | } 89 | app.processing = true; 90 | app.message = ""; 91 | // in case they reinstalled the sheep on this box and apps remained 92 | app.vps = $scope.vps._id; 93 | App.remove(app, function (err, result) { 94 | app.processing = false; 95 | if (err) { 96 | app.message = err.message || err.error || err; 97 | return; 98 | } 99 | getvps(); 100 | }); 101 | }; 102 | 103 | $scope.action = function (app, action) { 104 | app.processing = true; 105 | app.message = ""; 106 | App.action({ 107 | action: action, 108 | vps: app.vps, 109 | _id: app._id 110 | }, function (err, result) { 111 | app.processing = false; 112 | if (err) { 113 | app.message = err.message || err.error || err; 114 | return; 115 | } 116 | getvps(); 117 | }); 118 | }; 119 | 120 | $scope.setEnv = function (app) { 121 | app.processing = true; 122 | app.message = ""; 123 | var newEnv; 124 | try { 125 | var newEnv = JSON.parse(app.newEnv); 126 | } 127 | catch (ignored) { 128 | app.message = "Invalid JSON."; 129 | app.processing = false; 130 | return; 131 | } 132 | App.setEnv({ 133 | vps: app.vps, 134 | _id: app._id 135 | }, newEnv, function (err, result) { 136 | app.processing = false; 137 | if (err) { 138 | app.message = err.message || err.error || err; 139 | return; 140 | } 141 | getvps(); 142 | }); 143 | }; 144 | 145 | 146 | } 147 | ]); 148 | }; 149 | -------------------------------------------------------------------------------- /paastor-server/public/js/directives/pa-enter.js: -------------------------------------------------------------------------------- 1 | 'use strict'; 2 | exports = module.exports = function () { 3 | return { 4 | link: function (scope, element, attrs) { 5 | element.bind("keypress", function (event) { 6 | if (event.which === 13 && !event.shiftKey) { 7 | scope.$apply(function () { 8 | scope.$eval(attrs.paEnter); 9 | }); 10 | event.preventDefault(); 11 | } 12 | }); 13 | } 14 | }; 15 | }; -------------------------------------------------------------------------------- /paastor-server/public/js/readable-time.js: -------------------------------------------------------------------------------- 1 | exports = module.exports = function () { 2 | return function(seconds) { 3 | var day, format, hour, minute, month, week, year; 4 | seconds = parseInt(seconds, 10); 5 | minute = 60; 6 | hour = minute * 60; 7 | day = hour * 24; 8 | week = day * 7; 9 | year = day * 365; 10 | month = year / 12; 11 | format = function(number, string) { 12 | string = number === 1 ? string : "" + string + "s"; 13 | return "" + number + " " + string; 14 | }; 15 | switch (false) { 16 | case !(seconds < minute): 17 | return format(seconds, 'second'); 18 | case !(seconds < hour): 19 | return format(Math.floor(seconds / minute), 'minute'); 20 | case !(seconds < day): 21 | return format(Math.floor(seconds / hour), 'hour'); 22 | case !(seconds < week): 23 | return format(Math.floor(seconds / day), 'day'); 24 | case !(seconds < month): 25 | return format(Math.floor(seconds / week), 'week'); 26 | case !(seconds < year): 27 | return format(Math.floor(seconds / month), 'month'); 28 | default: 29 | return format(Math.floor(seconds / year), 'year'); 30 | } 31 | }; 32 | }; -------------------------------------------------------------------------------- /paastor-server/public/p2.xcf: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/ruffrey/paastor/8e83ea8acabf33a121aca419d2a067c8b81cff1c/paastor-server/public/p2.xcf -------------------------------------------------------------------------------- /paastor-server/public/pages/changelog.html: -------------------------------------------------------------------------------- 1 | 2 | 3 |
4 |Changes to Paastor will be displayed here.
28 |New CLI command:
30 |pstr create-app [server] [app]
Improved CLI performance and bug fixes. The CLI tool pstr
is now open source on Github.
35 | An alpha version of a library now exists, when `npm install paastor` is run without the 36 | `-g` global option. 37 |
Paastor now supports an experimental service allowing you to install MongoDB in one step.
41 |MongoDB can be setup for remote connections or local-only access.
Paastor now supports an experimental service allowing you to install Redis in one step.
45 |Redis can be setup for remote connections or local-only access.
$ paastor [commands]
and the npm package paastor-cli
have been renamed.
Now install with
50 |$ npm install -g paastor
51 | and use the CLI like
52 |$ pstr [commands]
53 | Overview
28 |A service with a CLI npm install -g paastor
that helps you deploy Node.js apps in minimal steps.
Paastor will install the Sheep service on your server, then you can add as many apps as you need.
31 |32 | A thin UI on the web lets you start and stop apps, manage environment variables, install MongoDB 33 | and Redis, and upload SSL certificates. 34 |
35 |A deployment and app management service on your server.
37 |Paastor securely sends commands and apps to your server via Sheep.
38 |Sheep keeps your apps running and restarts them when they crash.
39 |Sheep has a proxy that supports SSL and an API for Paastor to manage your apps.
40 |Becoming a Sheep does not mean your server is locked down in any way.
A command line interface (CLI) to control your apps and servers on the Paastor service.
43 |hello@paastor.com
28 |pstr
, the Paastor CLI30 | Your root password enables Paastor to securely log into your server and install all of the 31 | dependencies required to run a Node.js app, MongoDB, or Redis. Things like opening ports 32 | and installing packages need to be done as root. 33 |
36 | Yes, but technically you don't need to. As soon as Paastor does it's thing, you can change 37 | your root password, if it makes you feel better. However,Paastor does not store your root password. 38 |
39 |It may help to know:
40 |No, they run as a forever-monitor subprocess of Sheep, which is spawned by forever (-g).
50 | Since HTTPS traffic to your apps (if you're using SSL) will come through a proxy, 51 | it will not appear to be secure from your application's perspective. 52 |
53 |To detect HTTPS behind the Sheep proxy, look at thereferrer
header.
referrer: https://www.example.com
Paastor now supports simple MongoDB deployments. With a few clicks, you can have MongoDB running on any of your servers.
28 |29 | From the Server Manager, hit the MongoDB checkbox for one of your servers and fill out the required 30 | fields. Take note of any security information you set - it will not be saved by Paastor. In 31 | a few moments, it will be installed and you will be ready to connect. 32 |
33 |Often people use MongoDB in a trusted environment and do not allow it to receive remote connections.
35 |If you opt for remote access, be aware of how to connect, and that it may be slightly different than connecting to local or cloud based MongoDB services. This is because local and 36 | cloud systems shield you from some of the normal MongoDB administration, and create 37 | granular permissions at the database level. 38 |
39 |Paastor creates an admin user which you can use to connect to any database, and create new users for those databases.
40 |However, you must specify that you are connecting as an admin user. This is because 41 | MongoDB stores database permissions inside each database. 42 |
43 |You must tell MongoDB that your authSource
is admin
.
var mongoose = require('mongoose');
46 |
47 | var mongoUri = "mongodb://username:password@3.3.3.3:27017/somedatabase?authSource=admin";
48 | mongoose.connect(mongoUri);
49 | var mongoose = require('mongoose');
51 |
52 | var mongoUri = "mongodb://username:password@3.3.3.3:27017/somedatabase";
53 | var options = {
54 | auth: { authSource: 'admin' }
55 | };
56 | mongoose.connect(mongoUri, options);
New databases are created immediately when you connect to them. There is no need to create one.
59 |pstr
- the Paastor CLI tool$ npm install -g paastor
29 | $ pstr --help
31 |
32 | Usage: pstr [options] [command]
33 |
34 | Commands:
35 |
36 | login [options]
37 | Sign into Paastor (prompts for credentials or use -u, --user and -p, --pass)
38 |
39 | logout
40 | Sign out of Paastor
41 |
42 | servers
43 | Get a list of servers for this account
44 |
45 | logs [server] [app]
46 | Get the logs for a server (when no app param) or an app on the server
47 |
48 | server [_id]
49 | Get detailed info about a server
50 |
51 | push [options] [server] [app]
52 | Push an app to your server (both must already exist)
53 |
54 | create-app [server] [app]
55 | Create an app
56 |
57 | stop [server] [app]
58 | Stop an app
59 |
60 | start [server] [app]
61 | Start an app
62 |
63 | restart [server] [app]
64 | Stop then immediately start an app
65 |
66 | setenv [server] [app]
67 | Initiate the process of setting an app environment variable (must restart app to take effect)
68 |
69 | install-node [server] [version]
70 | Trigger the installation of a version of Node.js, to be available to apps.
71 |
72 |
73 | Options:
74 |
75 | -h, --help output usage information
76 | -V, --version output the version number
77 |
78 |
79 |
80 | Sometimes you might has special packages or other reasons for wanting to disable npm install
.
When you use the pstr push [server] [app]
command, Sheep will attempt to run npm install
using the version of Node specified in your package.json
engines.node
.
This can be turned off with the --no-npm
flag.
$ pstr push myserver myapp --no-npm
85 | With a few clicks, you can have a Redis instance running on any of your servers.
28 |29 | From the Server Manager, hit the Redis checkbox for one of your servers and fill out the required 30 | fields. In a few moments you will have Redis installed. 31 |
32 |
34 | If you lose the Redis password, want to change it, or want to remove it, log into the
35 | server as root and edit requirepass
in /etc/redis/redis.conf
.
36 |
SSL is available on paid accounts only.28 |
You don't have to pay very much, but you have to be paying something!
Add SSL certs and keys in the Server Manager.
29 |30 | SSL certificates can be re-uploaded an unlimited number of times from the web UI - 31 | in case it changes, or you make a mistake. 32 |
33 |Sheep contains a proxy and a router with support for SSL.
35 |
36 | When you add SSL to an app, the proxy will use the certificates during the requests on behalf
37 | of your app, then route the traffic to your app on process.env.PAASTOR_PORT
.
38 |
This setup is similar to nginx or other proxy servers.
Begin by creating an accountand verifying your email address.
33 | The most common option is to purchase a VPS (virtual private server) plan from a hosting company. In many 34 | cases you can run multiple Node.js apps for $2 - $10 per month on a VPS. 35 |
36 |VPS hosting is very competitive and very cheap compared to cloud hosting. Examples of hosting companies are: Linode, Digital Ocean, Chicago VPS, and many more.Server Bear and LowEndBox are tools to help you compare VPS hosts. (Paastor is not affiliated with any of the services 37 | mentioned.) 38 |
39 |The only officially supported server OS on Paastor (for now) isUbuntu 14.04.
40 |Next, be sure to create a DNS "A" record with your DNS provider, and point to your server's IP address, for the domain you intend to serve.
43 | Log in to Paastor and press the 'New Server' button from the Server Manager screen. 44 | Fill out all the fields. This will install the Sheep service on your server and 45 | allow you to push apps to the server. 46 |
From the Paastor UI, add an app to your server. You must add an app from the UI before pushing code.
49 |Yourpackage.json
must have the following fields (these are example values):
{
51 | // a single startup script
52 | "main": "aStartupScript.js",
53 | // list of domains for the Sheep proxy router
54 | "domains": ["example.com", "www.example.com"],
55 | "engines": {
56 | // a single node version - no ranges
57 | "node": "0.10.31"
58 | }
59 | }
pstr
command line tool$ npm install -g paastor
$ pstr login
Usepstr install-node [server] [version]
to add a version of Node to your server.
$ pstr install-node myserver1 0.10.28
67 | Depending on the version, it may be installed quickly, or may need to compile from source.
Usepstr push [server] [app]
to push the app in the current directory to the specified server.
$ pstr push myserver1 someapp
71 | push myserver1 someapp
72 | Checking package.json
73 | package.json exists
74 | version ok 0.10.28
75 | Domains look ok
76 | Checking server...
77 | Found myserver
78 | Status ok
79 | 0.10.31 installed
80 | Creating package from current directory...
81 | Package created. 10.46mb
82 | Prepping package for sending...
83 | Package is ready to send.
84 | Uploading and installing...
85 | Ok
86 | Stopping app...
87 | Stopped.
88 | Starting app...
89 | Started
90 | Cleaning up package...
91 | Package cleaned.
92 | Push complete.
$ pstr logs myserver1
96 | $ pstr logs myserver1 someapp
All CLI commands can be seen by runningpstr --help
.
It's on the roadmap to release many parts of Paastor under an open source license.
102 |Until then, refer to thedocumentation about the files Paastor puts on your server.
103 |