├── .gitignore ├── .sequelizerc ├── README.md ├── bin └── www ├── config ├── config.json ├── jwt.js └── server.json ├── database ├── .gitignore ├── migrations │ ├── 20181112123257-create-user.js │ ├── 20181112123632-create-role.js │ └── 20181112124003-create-user-role.js └── seeders │ ├── 20181112185653-user.js │ ├── 20181112185703-role.js │ └── 20181112185709-user_role.js ├── index.js ├── package-lock.json ├── package.json ├── public └── stylesheets │ └── style.css ├── server ├── controllers │ ├── api │ │ ├── index.js │ │ └── v1 │ │ │ ├── index.js │ │ │ └── restricted.controller.js │ ├── authentication.controller.js │ ├── index.js │ └── users.controller.js ├── index.js ├── middleware │ ├── index.js │ └── jwt.middleware.js ├── models │ ├── index.js │ ├── role.js │ ├── user.js │ └── userrole.js ├── repositories │ ├── index.js │ └── users.repository.js ├── routes │ ├── api │ │ ├── index.js │ │ └── v1 │ │ │ ├── index.js │ │ │ └── restricted.router.js │ ├── authentication.router.js │ ├── index.js │ └── users.router.js └── services │ ├── index.js │ └── jwt.service.js └── views ├── error.ejs └── index.ejs /.gitignore: -------------------------------------------------------------------------------- 1 | # See http://help.github.com/ignore-files/ for more about ignoring files. 2 | 3 | # compiled output 4 | /dist 5 | /tmp 6 | /out-tsc 7 | 8 | # dependencies 9 | /node_modules 10 | 11 | # IDEs and editors 12 | /.idea 13 | .project 14 | .classpath 15 | .c9/ 16 | *.launch 17 | .settings/ 18 | *.sublime-workspace 19 | 20 | # IDE - VSCode 21 | .vscode/* 22 | !.vscode/settings.json 23 | !.vscode/tasks.json 24 | !.vscode/launch.json 25 | !.vscode/extensions.json 26 | 27 | # misc 28 | /.sass-cache 29 | /connect.lock 30 | /coverage 31 | /libpeerconnection.log 32 | npm-debug.log 33 | yarn-error.log 34 | testem.log 35 | /typings 36 | 37 | # System Files 38 | .DS_Store 39 | Thumbs.db 40 | 41 | # encryption keys 42 | /config/*.key 43 | -------------------------------------------------------------------------------- /.sequelizerc: -------------------------------------------------------------------------------- 1 | const path = require('path'); 2 | 3 | module.exports = { 4 | "config": path.resolve('./config', 'config.json'), 5 | "models-path": path.resolve('./app/models'), 6 | "seeders-path": path.resolve('./database/seeders'), 7 | "migrations-path": path.resolve('./database/migrations') 8 | }; 9 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | # Node Express JWT Authentication / Authorization Example 2 | 3 | An example project implementing JWT authentication and role based authorization. 4 | 5 | ### Demo Setup 6 | 1. Clone the repository with `git clone https://github.com/jmw5598/node-express-jwt-example.git` 7 | 2. Generate RSA256 public private keys and place them in the `config` folder with file names `public.key` and `private.key`. A 512bit key size should be fine. 8 | - [Online RSA Key Generator][1] 9 | 3. Install dependencies with `npm install`. 10 | 4. Run migrations and seed that Sqlite database with `sequelize db:migrate` and `sequelize db:seed:all`. 11 | 5. Run the project with `node index.js`. 12 | 13 | ### Default Credentials 14 | 15 | | Username | Password | Description | 16 | | - | - | - | 17 | | admin | admin | User with `ADMIN` and `USER` roles. | 18 | | user | user | User with only `USER` role. | 19 | 20 | ### Default Routes 21 | 22 | | Path | Method | Description | 23 | | - | - | - | 24 | | `/auth` | `POST` | Authenticate user, returns JWT token. Jwt token can the be place in `Authorization` header prefixed with `Bearer`| 25 | | `/users` | `GET` | List all users [requires ADMIN role] | 26 | | `/users` | `POST` | Create new user [requires ADMIN role] | 27 | | `/api/v1/restricted/usersOnly` | `GET` | Users only path | 28 | | `/api/v1/restricted/adminOnly` | `GET` | Admin only path | 29 | 30 | 31 | ### Middleware Methods 32 | 33 | | Method | Description | 34 | | - | - | 35 | | `verify()` | Verifies token passed in `Authorization` request header. Token prefixed with `Bearer` | 36 | | `hasRole(role)` | Ensures the authenticated user has appropriate role | 37 | | `hasAnyRole([role, role])` | Ensures the authenticated user has ANY of the appropriate roles | 38 | | `hasAllRoles([role, role])` | Ensures the authenticated user has ALL the appropriate roles | 39 | 40 | 41 | [1]: http://travistidwell.com/jsencrypt/demo/ 42 | -------------------------------------------------------------------------------- /bin/www: -------------------------------------------------------------------------------- 1 | #!/usr/bin/env node 2 | 3 | /** 4 | * Module dependencies. 5 | */ 6 | 7 | var app = require('../app'); 8 | var debug = require('debug')('node-express-jwt-example:server'); 9 | var http = require('http'); 10 | 11 | /** 12 | * Get port from environment and store in Express. 13 | */ 14 | 15 | var port = normalizePort(process.env.PORT || '3000'); 16 | app.set('port', port); 17 | 18 | /** 19 | * Create HTTP server. 20 | */ 21 | 22 | var server = http.createServer(app); 23 | 24 | /** 25 | * Listen on provided port, on all network interfaces. 26 | */ 27 | 28 | server.listen(port); 29 | server.on('error', onError); 30 | server.on('listening', onListening); 31 | 32 | /** 33 | * Normalize a port into a number, string, or false. 34 | */ 35 | 36 | function normalizePort(val) { 37 | var port = parseInt(val, 10); 38 | 39 | if (isNaN(port)) { 40 | // named pipe 41 | return val; 42 | } 43 | 44 | if (port >= 0) { 45 | // port number 46 | return port; 47 | } 48 | 49 | return false; 50 | } 51 | 52 | /** 53 | * Event listener for HTTP server "error" event. 54 | */ 55 | 56 | function onError(error) { 57 | if (error.syscall !== 'listen') { 58 | throw error; 59 | } 60 | 61 | var bind = typeof port === 'string' 62 | ? 'Pipe ' + port 63 | : 'Port ' + port; 64 | 65 | // handle specific listen errors with friendly messages 66 | switch (error.code) { 67 | case 'EACCES': 68 | console.error(bind + ' requires elevated privileges'); 69 | process.exit(1); 70 | break; 71 | case 'EADDRINUSE': 72 | console.error(bind + ' is already in use'); 73 | process.exit(1); 74 | break; 75 | default: 76 | throw error; 77 | } 78 | } 79 | 80 | /** 81 | * Event listener for HTTP server "listening" event. 82 | */ 83 | 84 | function onListening() { 85 | var addr = server.address(); 86 | var bind = typeof addr === 'string' 87 | ? 'pipe ' + addr 88 | : 'port ' + addr.port; 89 | debug('Listening on ' + bind); 90 | } 91 | -------------------------------------------------------------------------------- /config/config.json: -------------------------------------------------------------------------------- 1 | { 2 | "development": { 3 | "storage": "./database/dev.sqlite", 4 | "dialect": "sqlite" 5 | }, 6 | "test": { 7 | "storage": "./database/test.sqlite3", 8 | "dialect": "sqlite" 9 | }, 10 | "production": { 11 | "storage": "./database/prod.sqlite3", 12 | "dialect": "sqlite" 13 | } 14 | } 15 | -------------------------------------------------------------------------------- /config/jwt.js: -------------------------------------------------------------------------------- 1 | module.exports = { 2 | payload: {}, 3 | options: { 4 | issuer: 'TEST', 5 | audience: 'https://www.testing.com', 6 | expiresIn: '12h', 7 | algorithm: 'RS256' 8 | } 9 | }; 10 | -------------------------------------------------------------------------------- /config/server.json: -------------------------------------------------------------------------------- 1 | { 2 | "development": { 3 | "env": "dev", 4 | "hostname": "localhost", 5 | "port": "3000", 6 | "viewDir": "./app/views", 7 | "staticDir": "./app/public" 8 | }, 9 | "test": {}, 10 | "production": {} 11 | } 12 | -------------------------------------------------------------------------------- /database/.gitignore: -------------------------------------------------------------------------------- 1 | # Except this file 2 | *.sqlite 3 | -------------------------------------------------------------------------------- /database/migrations/20181112123257-create-user.js: -------------------------------------------------------------------------------- 1 | 'use strict'; 2 | module.exports = { 3 | up: (queryInterface, Sequelize) => { 4 | return queryInterface.createTable('users', { 5 | id: { 6 | allowNull: false, 7 | autoIncrement: true, 8 | primaryKey: true, 9 | type: Sequelize.INTEGER 10 | }, 11 | username: { 12 | type: Sequelize.STRING, 13 | allowNull: false, 14 | unique: true 15 | }, 16 | password: { 17 | type: Sequelize.STRING, 18 | allowNull: false 19 | } 20 | }); 21 | }, 22 | down: (queryInterface, Sequelize) => { 23 | return queryInterface.dropTable('users'); 24 | } 25 | }; 26 | -------------------------------------------------------------------------------- /database/migrations/20181112123632-create-role.js: -------------------------------------------------------------------------------- 1 | 'use strict'; 2 | module.exports = { 3 | up: (queryInterface, Sequelize) => { 4 | return queryInterface.createTable('roles', { 5 | id: { 6 | allowNull: false, 7 | autoIncrement: true, 8 | primaryKey: true, 9 | type: Sequelize.INTEGER 10 | }, 11 | role: { 12 | type: Sequelize.STRING, 13 | allowNull: false, 14 | unique: true 15 | } 16 | }); 17 | }, 18 | down: (queryInterface, Sequelize) => { 19 | return queryInterface.dropTable('roles'); 20 | } 21 | }; 22 | -------------------------------------------------------------------------------- /database/migrations/20181112124003-create-user-role.js: -------------------------------------------------------------------------------- 1 | 'use strict'; 2 | module.exports = { 3 | up: (queryInterface, Sequelize) => { 4 | return queryInterface.createTable('user_roles', { 5 | user_id: { 6 | type: Sequelize.INTEGER, 7 | allowNull: false, 8 | primaryKey: true 9 | }, 10 | role_id: { 11 | type: Sequelize.INTEGER, 12 | allowNull: false, 13 | primaryKey: true 14 | } 15 | }); 16 | }, 17 | down: (queryInterface, Sequelize) => { 18 | return queryInterface.dropTable('user_roles'); 19 | } 20 | }; 21 | -------------------------------------------------------------------------------- /database/seeders/20181112185653-user.js: -------------------------------------------------------------------------------- 1 | 'use strict'; 2 | 3 | module.exports = { 4 | up: (queryInterface, Sequelize) => { 5 | return queryInterface.bulkInsert('users', [ 6 | { id: 1, username: 'admin', password: '$2a$10$T1W8ybvUt4CSqdqoIA/CkOttiDusVHDoxRirVca11DjGa3ZX.PKX2' }, 7 | { id: 2, username: 'user', password: '$2a$10$6JMo4paEWMLED5SGhuE75eEyv.j5wxktBpZIBKel/btGpMwCrXkVe' } 8 | ], {}) 9 | .catch((error) => console.log(error)); 10 | 11 | }, 12 | 13 | down: (queryInterface, Sequelize) => { 14 | return queryInterface.bulkDelete('users', null, {}); 15 | } 16 | }; 17 | -------------------------------------------------------------------------------- /database/seeders/20181112185703-role.js: -------------------------------------------------------------------------------- 1 | 'use strict'; 2 | 3 | module.exports = { 4 | up: (queryInterface, Sequelize) => { 5 | return queryInterface.bulkInsert('roles', [ 6 | { id: 1, role: 'USER' }, 7 | { id: 2, role: 'ADMIN' } 8 | ], {}) 9 | .catch((error) => console.log("ROLE ERROR", error)); 10 | }, 11 | 12 | down: (queryInterface, Sequelize) => { 13 | return queryInterface.bulkDelete('roles', null, {}); 14 | } 15 | }; 16 | -------------------------------------------------------------------------------- /database/seeders/20181112185709-user_role.js: -------------------------------------------------------------------------------- 1 | 'use strict'; 2 | 3 | module.exports = { 4 | up: (queryInterface, Sequelize) => { 5 | return queryInterface.bulkInsert('user_roles', [ 6 | { user_id: 1, role_id: 1 }, 7 | { user_id: 1, role_id: 2 }, 8 | { user_id: 2, role_id: 1 } 9 | ], {}) 10 | }, 11 | 12 | down: (queryInterface, Sequelize) => { 13 | return queryInterface.bulkDelete('user_roles', null, {}); 14 | } 15 | }; 16 | -------------------------------------------------------------------------------- /index.js: -------------------------------------------------------------------------------- 1 | const fs = require('fs'); 2 | const server = require('./server'); 3 | const config = JSON.parse(fs.readFileSync('config/server.json', 'utf8')); 4 | 5 | server.setup(config.development); 6 | server.start(); 7 | 8 | module.exports = server; 9 | -------------------------------------------------------------------------------- /package-lock.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "node-express-jwt-example", 3 | "version": "0.0.0", 4 | "lockfileVersion": 1, 5 | "requires": true, 6 | "dependencies": { 7 | "@types/geojson": { 8 | "version": "1.0.6", 9 | "resolved": "https://registry.npmjs.org/@types/geojson/-/geojson-1.0.6.tgz", 10 | "integrity": "sha512-Xqg/lIZMrUd0VRmSRbCAewtwGZiAk3mEUDvV4op1tGl+LvyPcb/MIOSxTl9z+9+J+R4/vpjiCAT4xeKzH9ji1w==" 11 | }, 12 | "@types/node": { 13 | "version": "10.12.9", 14 | "resolved": "https://registry.npmjs.org/@types/node/-/node-10.12.9.tgz", 15 | "integrity": "sha512-eajkMXG812/w3w4a1OcBlaTwsFPO5F7fJ/amy+tieQxEMWBlbV1JGSjkFM+zkHNf81Cad+dfIRA+IBkvmvdAeA==" 16 | }, 17 | "abbrev": { 18 | "version": "1.1.1", 19 | "resolved": "https://registry.npmjs.org/abbrev/-/abbrev-1.1.1.tgz", 20 | "integrity": "sha512-nne9/IiQ/hzIhY6pdDnbBtz7DjPTKrY00P/zvPSm5pOFkl6xuGrGnXn/VtTNNfNtAfZ9/1RtehkszU9qcTii0Q==" 21 | }, 22 | "accepts": { 23 | "version": "1.3.5", 24 | "resolved": "https://registry.npmjs.org/accepts/-/accepts-1.3.5.tgz", 25 | "integrity": "sha1-63d99gEXI6OxTopywIBcjoZ0a9I=", 26 | "requires": { 27 | "mime-types": "~2.1.18", 28 | "negotiator": "0.6.1" 29 | } 30 | }, 31 | "ajv": { 32 | "version": "6.5.5", 33 | "resolved": "https://registry.npmjs.org/ajv/-/ajv-6.5.5.tgz", 34 | "integrity": "sha512-7q7gtRQDJSyuEHjuVgHoUa2VuemFiCMrfQc9Tc08XTAc4Zj/5U1buQJ0HU6i7fKjXU09SVgSmxa4sLvuvS8Iyg==", 35 | "requires": { 36 | "fast-deep-equal": "^2.0.1", 37 | "fast-json-stable-stringify": "^2.0.0", 38 | "json-schema-traverse": "^0.4.1", 39 | "uri-js": "^4.2.2" 40 | } 41 | }, 42 | "ansi-regex": { 43 | "version": "2.1.1", 44 | "resolved": "https://registry.npmjs.org/ansi-regex/-/ansi-regex-2.1.1.tgz", 45 | "integrity": "sha1-w7M6te42DYbg5ijwRorn7yfWVN8=" 46 | }, 47 | "aproba": { 48 | "version": "1.2.0", 49 | "resolved": "https://registry.npmjs.org/aproba/-/aproba-1.2.0.tgz", 50 | "integrity": "sha512-Y9J6ZjXtoYh8RnXVCMOU/ttDmk1aBjunq9vO0ta5x85WDQiQfUF9sIPBITdbiiIVcBo03Hi3jMxigBtsddlXRw==" 51 | }, 52 | "are-we-there-yet": { 53 | "version": "1.1.5", 54 | "resolved": "https://registry.npmjs.org/are-we-there-yet/-/are-we-there-yet-1.1.5.tgz", 55 | "integrity": "sha512-5hYdAkZlcG8tOLujVDTgCT+uPX0VnpAH28gWsLfzpXYm7wP6mp5Q/gYyR7YQ0cKVJcXJnl3j2kpBan13PtQf6w==", 56 | "requires": { 57 | "delegates": "^1.0.0", 58 | "readable-stream": "^2.0.6" 59 | } 60 | }, 61 | "argparse": { 62 | "version": "1.0.10", 63 | "resolved": "https://registry.npmjs.org/argparse/-/argparse-1.0.10.tgz", 64 | "integrity": "sha512-o5Roy6tNG4SL/FOkCAN6RzjiakZS25RLYFrcMttJqbdd8BWrnA+fGz57iN5Pb06pvBGvl5gQ0B48dJlslXvoTg==", 65 | "requires": { 66 | "sprintf-js": "~1.0.2" 67 | } 68 | }, 69 | "array-flatten": { 70 | "version": "1.1.1", 71 | "resolved": "https://registry.npmjs.org/array-flatten/-/array-flatten-1.1.1.tgz", 72 | "integrity": "sha1-ml9pkFGx5wczKPKgCJaLZOopVdI=" 73 | }, 74 | "asn1": { 75 | "version": "0.2.4", 76 | "resolved": "https://registry.npmjs.org/asn1/-/asn1-0.2.4.tgz", 77 | "integrity": "sha512-jxwzQpLQjSmWXgwaCZE9Nz+glAG01yF1QnWgbhGwHI5A6FRIEY6IVqtHhIepHqI7/kyEyQEagBC5mBEFlIYvdg==", 78 | "requires": { 79 | "safer-buffer": "~2.1.0" 80 | } 81 | }, 82 | "assert-plus": { 83 | "version": "1.0.0", 84 | "resolved": "https://registry.npmjs.org/assert-plus/-/assert-plus-1.0.0.tgz", 85 | "integrity": "sha1-8S4PPF13sLHN2RRpQuTpbB5N1SU=" 86 | }, 87 | "asynckit": { 88 | "version": "0.4.0", 89 | "resolved": "https://registry.npmjs.org/asynckit/-/asynckit-0.4.0.tgz", 90 | "integrity": "sha1-x57Zf380y48robyXkLzDZkdLS3k=" 91 | }, 92 | "aws-sign2": { 93 | "version": "0.7.0", 94 | "resolved": "https://registry.npmjs.org/aws-sign2/-/aws-sign2-0.7.0.tgz", 95 | "integrity": "sha1-tG6JCTSpWR8tL2+G1+ap8bP+dqg=" 96 | }, 97 | "aws4": { 98 | "version": "1.8.0", 99 | "resolved": "https://registry.npmjs.org/aws4/-/aws4-1.8.0.tgz", 100 | "integrity": "sha512-ReZxvNHIOv88FlT7rxcXIIC0fPt4KZqZbOlivyWtXLt8ESx84zd3kMC6iK5jVeS2qt+g7ftS7ye4fi06X5rtRQ==" 101 | }, 102 | "balanced-match": { 103 | "version": "1.0.0", 104 | "resolved": "https://registry.npmjs.org/balanced-match/-/balanced-match-1.0.0.tgz", 105 | "integrity": "sha1-ibTRmasr7kneFk6gK4nORi1xt2c=" 106 | }, 107 | "basic-auth": { 108 | "version": "2.0.1", 109 | "resolved": "https://registry.npmjs.org/basic-auth/-/basic-auth-2.0.1.tgz", 110 | "integrity": "sha512-NF+epuEdnUYVlGuhaxbbq+dvJttwLnGY+YixlXlME5KpQ5W3CnXA5cVTneY3SPbPDRkcjMbifrwmFYcClgOZeg==", 111 | "requires": { 112 | "safe-buffer": "5.1.2" 113 | } 114 | }, 115 | "bcrypt": { 116 | "version": "3.0.2", 117 | "resolved": "https://registry.npmjs.org/bcrypt/-/bcrypt-3.0.2.tgz", 118 | "integrity": "sha512-kE1IaaRchCgdrmzQX/eBQKcsuL4jRHZ+O11sMvEUrI/HgFTQYAGvxlj9z7kb3zfFuwljQ5y8/NrbnXtgx5oJLg==", 119 | "requires": { 120 | "nan": "2.11.1", 121 | "node-pre-gyp": "0.11.0" 122 | }, 123 | "dependencies": { 124 | "abbrev": { 125 | "version": "1.1.1", 126 | "bundled": true 127 | }, 128 | "ansi-regex": { 129 | "version": "2.1.1", 130 | "bundled": true 131 | }, 132 | "aproba": { 133 | "version": "1.2.0", 134 | "bundled": true 135 | }, 136 | "are-we-there-yet": { 137 | "version": "1.1.5", 138 | "bundled": true, 139 | "requires": { 140 | "delegates": "^1.0.0", 141 | "readable-stream": "^2.0.6" 142 | } 143 | }, 144 | "balanced-match": { 145 | "version": "1.0.0", 146 | "bundled": true 147 | }, 148 | "brace-expansion": { 149 | "version": "1.1.11", 150 | "bundled": true, 151 | "requires": { 152 | "balanced-match": "^1.0.0", 153 | "concat-map": "0.0.1" 154 | } 155 | }, 156 | "chownr": { 157 | "version": "1.1.1", 158 | "bundled": true 159 | }, 160 | "code-point-at": { 161 | "version": "1.1.0", 162 | "bundled": true 163 | }, 164 | "concat-map": { 165 | "version": "0.0.1", 166 | "bundled": true 167 | }, 168 | "console-control-strings": { 169 | "version": "1.1.0", 170 | "bundled": true 171 | }, 172 | "core-util-is": { 173 | "version": "1.0.2", 174 | "bundled": true 175 | }, 176 | "debug": { 177 | "version": "2.6.9", 178 | "bundled": true, 179 | "requires": { 180 | "ms": "2.0.0" 181 | } 182 | }, 183 | "deep-extend": { 184 | "version": "0.6.0", 185 | "bundled": true 186 | }, 187 | "delegates": { 188 | "version": "1.0.0", 189 | "bundled": true 190 | }, 191 | "detect-libc": { 192 | "version": "1.0.3", 193 | "bundled": true 194 | }, 195 | "fs-minipass": { 196 | "version": "1.2.5", 197 | "bundled": true, 198 | "requires": { 199 | "minipass": "^2.2.1" 200 | } 201 | }, 202 | "fs.realpath": { 203 | "version": "1.0.0", 204 | "bundled": true 205 | }, 206 | "gauge": { 207 | "version": "2.7.4", 208 | "bundled": true, 209 | "requires": { 210 | "aproba": "^1.0.3", 211 | "console-control-strings": "^1.0.0", 212 | "has-unicode": "^2.0.0", 213 | "object-assign": "^4.1.0", 214 | "signal-exit": "^3.0.0", 215 | "string-width": "^1.0.1", 216 | "strip-ansi": "^3.0.1", 217 | "wide-align": "^1.1.0" 218 | } 219 | }, 220 | "glob": { 221 | "version": "7.1.2", 222 | "bundled": true, 223 | "requires": { 224 | "fs.realpath": "^1.0.0", 225 | "inflight": "^1.0.4", 226 | "inherits": "2", 227 | "minimatch": "^3.0.4", 228 | "once": "^1.3.0", 229 | "path-is-absolute": "^1.0.0" 230 | } 231 | }, 232 | "has-unicode": { 233 | "version": "2.0.1", 234 | "bundled": true 235 | }, 236 | "iconv-lite": { 237 | "version": "0.4.24", 238 | "bundled": true, 239 | "requires": { 240 | "safer-buffer": ">= 2.1.2 < 3" 241 | } 242 | }, 243 | "ignore-walk": { 244 | "version": "3.0.1", 245 | "bundled": true, 246 | "requires": { 247 | "minimatch": "^3.0.4" 248 | } 249 | }, 250 | "inflight": { 251 | "version": "1.0.6", 252 | "bundled": true, 253 | "requires": { 254 | "once": "^1.3.0", 255 | "wrappy": "1" 256 | } 257 | }, 258 | "inherits": { 259 | "version": "2.0.3", 260 | "bundled": true 261 | }, 262 | "ini": { 263 | "version": "1.3.5", 264 | "bundled": true 265 | }, 266 | "is-fullwidth-code-point": { 267 | "version": "1.0.0", 268 | "bundled": true, 269 | "requires": { 270 | "number-is-nan": "^1.0.0" 271 | } 272 | }, 273 | "isarray": { 274 | "version": "1.0.0", 275 | "bundled": true 276 | }, 277 | "minimatch": { 278 | "version": "3.0.4", 279 | "bundled": true, 280 | "requires": { 281 | "brace-expansion": "^1.1.7" 282 | } 283 | }, 284 | "minimist": { 285 | "version": "0.0.8", 286 | "bundled": true 287 | }, 288 | "minipass": { 289 | "version": "2.3.4", 290 | "bundled": true, 291 | "requires": { 292 | "safe-buffer": "^5.1.2", 293 | "yallist": "^3.0.0" 294 | }, 295 | "dependencies": { 296 | "safe-buffer": { 297 | "version": "5.1.2", 298 | "bundled": true 299 | }, 300 | "yallist": { 301 | "version": "3.0.2", 302 | "bundled": true 303 | } 304 | } 305 | }, 306 | "minizlib": { 307 | "version": "1.1.0", 308 | "bundled": true, 309 | "requires": { 310 | "minipass": "^2.2.1" 311 | } 312 | }, 313 | "mkdirp": { 314 | "version": "0.5.1", 315 | "bundled": true, 316 | "requires": { 317 | "minimist": "0.0.8" 318 | } 319 | }, 320 | "ms": { 321 | "version": "2.0.0", 322 | "bundled": true 323 | }, 324 | "needle": { 325 | "version": "2.2.3", 326 | "bundled": true, 327 | "requires": { 328 | "debug": "^2.1.2", 329 | "iconv-lite": "^0.4.4", 330 | "sax": "^1.2.4" 331 | } 332 | }, 333 | "node-pre-gyp": { 334 | "version": "0.11.0", 335 | "bundled": true, 336 | "requires": { 337 | "detect-libc": "^1.0.2", 338 | "mkdirp": "^0.5.1", 339 | "needle": "^2.2.1", 340 | "nopt": "^4.0.1", 341 | "npm-packlist": "^1.1.6", 342 | "npmlog": "^4.0.2", 343 | "rc": "^1.2.7", 344 | "rimraf": "^2.6.1", 345 | "semver": "^5.3.0", 346 | "tar": "^4" 347 | } 348 | }, 349 | "nopt": { 350 | "version": "4.0.1", 351 | "bundled": true, 352 | "requires": { 353 | "abbrev": "1", 354 | "osenv": "^0.1.4" 355 | } 356 | }, 357 | "npm-bundled": { 358 | "version": "1.0.5", 359 | "bundled": true 360 | }, 361 | "npm-packlist": { 362 | "version": "1.1.11", 363 | "bundled": true, 364 | "requires": { 365 | "ignore-walk": "^3.0.1", 366 | "npm-bundled": "^1.0.1" 367 | } 368 | }, 369 | "npmlog": { 370 | "version": "4.1.2", 371 | "bundled": true, 372 | "requires": { 373 | "are-we-there-yet": "~1.1.2", 374 | "console-control-strings": "~1.1.0", 375 | "gauge": "~2.7.3", 376 | "set-blocking": "~2.0.0" 377 | } 378 | }, 379 | "number-is-nan": { 380 | "version": "1.0.1", 381 | "bundled": true 382 | }, 383 | "object-assign": { 384 | "version": "4.1.1", 385 | "bundled": true 386 | }, 387 | "once": { 388 | "version": "1.4.0", 389 | "bundled": true, 390 | "requires": { 391 | "wrappy": "1" 392 | } 393 | }, 394 | "os-homedir": { 395 | "version": "1.0.2", 396 | "bundled": true 397 | }, 398 | "os-tmpdir": { 399 | "version": "1.0.2", 400 | "bundled": true 401 | }, 402 | "osenv": { 403 | "version": "0.1.5", 404 | "bundled": true, 405 | "requires": { 406 | "os-homedir": "^1.0.0", 407 | "os-tmpdir": "^1.0.0" 408 | } 409 | }, 410 | "path-is-absolute": { 411 | "version": "1.0.1", 412 | "bundled": true 413 | }, 414 | "process-nextick-args": { 415 | "version": "2.0.0", 416 | "bundled": true 417 | }, 418 | "rc": { 419 | "version": "1.2.8", 420 | "bundled": true, 421 | "requires": { 422 | "deep-extend": "^0.6.0", 423 | "ini": "~1.3.0", 424 | "minimist": "^1.2.0", 425 | "strip-json-comments": "~2.0.1" 426 | }, 427 | "dependencies": { 428 | "minimist": { 429 | "version": "1.2.0", 430 | "bundled": true 431 | } 432 | } 433 | }, 434 | "readable-stream": { 435 | "version": "2.3.5", 436 | "bundled": true, 437 | "requires": { 438 | "core-util-is": "~1.0.0", 439 | "inherits": "~2.0.3", 440 | "isarray": "~1.0.0", 441 | "process-nextick-args": "~2.0.0", 442 | "safe-buffer": "~5.1.1", 443 | "string_decoder": "~1.0.3", 444 | "util-deprecate": "~1.0.1" 445 | } 446 | }, 447 | "rimraf": { 448 | "version": "2.6.2", 449 | "bundled": true, 450 | "requires": { 451 | "glob": "^7.0.5" 452 | } 453 | }, 454 | "safe-buffer": { 455 | "version": "5.1.1", 456 | "bundled": true 457 | }, 458 | "safer-buffer": { 459 | "version": "2.1.2", 460 | "bundled": true 461 | }, 462 | "sax": { 463 | "version": "1.2.4", 464 | "bundled": true 465 | }, 466 | "semver": { 467 | "version": "5.5.1", 468 | "bundled": true 469 | }, 470 | "set-blocking": { 471 | "version": "2.0.0", 472 | "bundled": true 473 | }, 474 | "signal-exit": { 475 | "version": "3.0.2", 476 | "bundled": true 477 | }, 478 | "string-width": { 479 | "version": "1.0.2", 480 | "bundled": true, 481 | "requires": { 482 | "code-point-at": "^1.0.0", 483 | "is-fullwidth-code-point": "^1.0.0", 484 | "strip-ansi": "^3.0.0" 485 | } 486 | }, 487 | "string_decoder": { 488 | "version": "1.0.3", 489 | "bundled": true, 490 | "requires": { 491 | "safe-buffer": "~5.1.0" 492 | } 493 | }, 494 | "strip-ansi": { 495 | "version": "3.0.1", 496 | "bundled": true, 497 | "requires": { 498 | "ansi-regex": "^2.0.0" 499 | } 500 | }, 501 | "strip-json-comments": { 502 | "version": "2.0.1", 503 | "bundled": true 504 | }, 505 | "tar": { 506 | "version": "4.4.6", 507 | "bundled": true, 508 | "requires": { 509 | "chownr": "^1.0.1", 510 | "fs-minipass": "^1.2.5", 511 | "minipass": "^2.3.3", 512 | "minizlib": "^1.1.0", 513 | "mkdirp": "^0.5.0", 514 | "safe-buffer": "^5.1.2", 515 | "yallist": "^3.0.2" 516 | }, 517 | "dependencies": { 518 | "safe-buffer": { 519 | "version": "5.1.2", 520 | "bundled": true 521 | }, 522 | "yallist": { 523 | "version": "3.0.2", 524 | "bundled": true 525 | } 526 | } 527 | }, 528 | "util-deprecate": { 529 | "version": "1.0.2", 530 | "bundled": true 531 | }, 532 | "wide-align": { 533 | "version": "1.1.3", 534 | "bundled": true, 535 | "requires": { 536 | "string-width": "^1.0.2 || 2" 537 | } 538 | }, 539 | "wrappy": { 540 | "version": "1.0.2", 541 | "bundled": true 542 | } 543 | } 544 | }, 545 | "bcrypt-pbkdf": { 546 | "version": "1.0.2", 547 | "resolved": "https://registry.npmjs.org/bcrypt-pbkdf/-/bcrypt-pbkdf-1.0.2.tgz", 548 | "integrity": "sha1-pDAdOJtqQ/m2f/PKEaP2Y342Dp4=", 549 | "requires": { 550 | "tweetnacl": "^0.14.3" 551 | } 552 | }, 553 | "bluebird": { 554 | "version": "3.5.3", 555 | "resolved": "https://registry.npmjs.org/bluebird/-/bluebird-3.5.3.tgz", 556 | "integrity": "sha512-/qKPUQlaW1OyR51WeCPBvRnAlnZFUJkCSG5HzGnuIqhgyJtF+T94lFnn33eiazjRm2LAHVy2guNnaq48X9SJuw==" 557 | }, 558 | "body-parser": { 559 | "version": "1.18.3", 560 | "resolved": "https://registry.npmjs.org/body-parser/-/body-parser-1.18.3.tgz", 561 | "integrity": "sha1-WykhmP/dVTs6DyDe0FkrlWlVyLQ=", 562 | "requires": { 563 | "bytes": "3.0.0", 564 | "content-type": "~1.0.4", 565 | "debug": "2.6.9", 566 | "depd": "~1.1.2", 567 | "http-errors": "~1.6.3", 568 | "iconv-lite": "0.4.23", 569 | "on-finished": "~2.3.0", 570 | "qs": "6.5.2", 571 | "raw-body": "2.3.3", 572 | "type-is": "~1.6.16" 573 | }, 574 | "dependencies": { 575 | "iconv-lite": { 576 | "version": "0.4.23", 577 | "resolved": "https://registry.npmjs.org/iconv-lite/-/iconv-lite-0.4.23.tgz", 578 | "integrity": "sha512-neyTUVFtahjf0mB3dZT77u+8O0QB89jFdnBkd5P1JgYPbPaia3gXXOVL2fq8VyU2gMMD7SaN7QukTB/pmXYvDA==", 579 | "requires": { 580 | "safer-buffer": ">= 2.1.2 < 3" 581 | } 582 | } 583 | } 584 | }, 585 | "brace-expansion": { 586 | "version": "1.1.11", 587 | "resolved": "https://registry.npmjs.org/brace-expansion/-/brace-expansion-1.1.11.tgz", 588 | "integrity": "sha512-iCuPHDFgrHX7H2vEI/5xpz07zSHB00TpugqhmYtVmMO6518mCuRMoOYFldEBl0g187ufozdaHgWKcYFb61qGiA==", 589 | "requires": { 590 | "balanced-match": "^1.0.0", 591 | "concat-map": "0.0.1" 592 | } 593 | }, 594 | "buffer-equal-constant-time": { 595 | "version": "1.0.1", 596 | "resolved": "https://registry.npmjs.org/buffer-equal-constant-time/-/buffer-equal-constant-time-1.0.1.tgz", 597 | "integrity": "sha1-+OcRMvf/5uAaXJaXpMbz5I1cyBk=" 598 | }, 599 | "bytes": { 600 | "version": "3.0.0", 601 | "resolved": "https://registry.npmjs.org/bytes/-/bytes-3.0.0.tgz", 602 | "integrity": "sha1-0ygVQE1olpn4Wk6k+odV3ROpYEg=" 603 | }, 604 | "caseless": { 605 | "version": "0.12.0", 606 | "resolved": "https://registry.npmjs.org/caseless/-/caseless-0.12.0.tgz", 607 | "integrity": "sha1-G2gcIf+EAzyCZUMJBolCDRhxUdw=" 608 | }, 609 | "chownr": { 610 | "version": "1.1.1", 611 | "resolved": "https://registry.npmjs.org/chownr/-/chownr-1.1.1.tgz", 612 | "integrity": "sha512-j38EvO5+LHX84jlo6h4UzmOwi0UgW61WRyPtJz4qaadK5eY3BTS5TY/S1Stc3Uk2lIM6TPevAlULiEJwie860g==" 613 | }, 614 | "cls-bluebird": { 615 | "version": "2.1.0", 616 | "resolved": "https://registry.npmjs.org/cls-bluebird/-/cls-bluebird-2.1.0.tgz", 617 | "integrity": "sha1-N+8eCAqP+1XC9BZPU28ZGeeWiu4=", 618 | "requires": { 619 | "is-bluebird": "^1.0.2", 620 | "shimmer": "^1.1.0" 621 | } 622 | }, 623 | "code-point-at": { 624 | "version": "1.1.0", 625 | "resolved": "https://registry.npmjs.org/code-point-at/-/code-point-at-1.1.0.tgz", 626 | "integrity": "sha1-DQcLTQQ6W+ozovGkDi7bPZpMz3c=" 627 | }, 628 | "combined-stream": { 629 | "version": "1.0.7", 630 | "resolved": "https://registry.npmjs.org/combined-stream/-/combined-stream-1.0.7.tgz", 631 | "integrity": "sha512-brWl9y6vOB1xYPZcpZde3N9zDByXTosAeMDo4p1wzo6UMOX4vumB+TP1RZ76sfE6Md68Q0NJSrE/gbezd4Ul+w==", 632 | "requires": { 633 | "delayed-stream": "~1.0.0" 634 | } 635 | }, 636 | "concat-map": { 637 | "version": "0.0.1", 638 | "resolved": "https://registry.npmjs.org/concat-map/-/concat-map-0.0.1.tgz", 639 | "integrity": "sha1-2Klr13/Wjfd5OnMDajug1UBdR3s=" 640 | }, 641 | "console-control-strings": { 642 | "version": "1.1.0", 643 | "resolved": "https://registry.npmjs.org/console-control-strings/-/console-control-strings-1.1.0.tgz", 644 | "integrity": "sha1-PXz0Rk22RG6mRL9LOVB/mFEAjo4=" 645 | }, 646 | "content-disposition": { 647 | "version": "0.5.2", 648 | "resolved": "https://registry.npmjs.org/content-disposition/-/content-disposition-0.5.2.tgz", 649 | "integrity": "sha1-DPaLud318r55YcOoUXjLhdunjLQ=" 650 | }, 651 | "content-type": { 652 | "version": "1.0.4", 653 | "resolved": "https://registry.npmjs.org/content-type/-/content-type-1.0.4.tgz", 654 | "integrity": "sha512-hIP3EEPs8tB9AT1L+NUqtwOAps4mk2Zob89MWXMHjHWg9milF/j4osnnQLXBCBFBk/tvIG/tUc9mOUJiPBhPXA==" 655 | }, 656 | "cookie": { 657 | "version": "0.3.1", 658 | "resolved": "https://registry.npmjs.org/cookie/-/cookie-0.3.1.tgz", 659 | "integrity": "sha1-5+Ch+e9DtMi6klxcWpboBtFoc7s=" 660 | }, 661 | "cookie-parser": { 662 | "version": "1.4.3", 663 | "resolved": "https://registry.npmjs.org/cookie-parser/-/cookie-parser-1.4.3.tgz", 664 | "integrity": "sha1-D+MfoZ0AC5X0qt8fU/3CuKIDuqU=", 665 | "requires": { 666 | "cookie": "0.3.1", 667 | "cookie-signature": "1.0.6" 668 | } 669 | }, 670 | "cookie-signature": { 671 | "version": "1.0.6", 672 | "resolved": "https://registry.npmjs.org/cookie-signature/-/cookie-signature-1.0.6.tgz", 673 | "integrity": "sha1-4wOogrNCzD7oylE6eZmXNNqzriw=" 674 | }, 675 | "core-util-is": { 676 | "version": "1.0.2", 677 | "resolved": "https://registry.npmjs.org/core-util-is/-/core-util-is-1.0.2.tgz", 678 | "integrity": "sha1-tf1UIgqivFq1eqtxQMlAdUUDwac=" 679 | }, 680 | "cors": { 681 | "version": "2.8.5", 682 | "resolved": "https://registry.npmjs.org/cors/-/cors-2.8.5.tgz", 683 | "integrity": "sha512-KIHbLJqu73RGr/hnbrO9uBeixNGuvSQjul/jdFvS/KFSIH1hWVd1ng7zOHx+YrEfInLG7q4n6GHQ9cDtxv/P6g==", 684 | "requires": { 685 | "object-assign": "^4", 686 | "vary": "^1" 687 | } 688 | }, 689 | "dashdash": { 690 | "version": "1.14.1", 691 | "resolved": "https://registry.npmjs.org/dashdash/-/dashdash-1.14.1.tgz", 692 | "integrity": "sha1-hTz6D3y+L+1d4gMmuN1YEDX24vA=", 693 | "requires": { 694 | "assert-plus": "^1.0.0" 695 | } 696 | }, 697 | "debug": { 698 | "version": "2.6.9", 699 | "resolved": "https://registry.npmjs.org/debug/-/debug-2.6.9.tgz", 700 | "integrity": "sha512-bC7ElrdJaJnPbAP+1EotYvqZsb3ecl5wi6Bfi6BJTUcNowp6cvspg0jXznRTKDjm/E7AdgFBVeAPVMNcKGsHMA==", 701 | "requires": { 702 | "ms": "2.0.0" 703 | }, 704 | "dependencies": { 705 | "ms": { 706 | "version": "2.0.0", 707 | "resolved": "https://registry.npmjs.org/ms/-/ms-2.0.0.tgz", 708 | "integrity": "sha1-VgiurfwAvmwpAd9fmGF4jeDVl8g=" 709 | } 710 | } 711 | }, 712 | "deep-extend": { 713 | "version": "0.6.0", 714 | "resolved": "https://registry.npmjs.org/deep-extend/-/deep-extend-0.6.0.tgz", 715 | "integrity": "sha512-LOHxIOaPYdHlJRtCQfDIVZtfw/ufM8+rVj649RIHzcm/vGwQRXFt6OPqIFWsm2XEMrNIEtWR64sY1LEKD2vAOA==" 716 | }, 717 | "delayed-stream": { 718 | "version": "1.0.0", 719 | "resolved": "https://registry.npmjs.org/delayed-stream/-/delayed-stream-1.0.0.tgz", 720 | "integrity": "sha1-3zrhmayt+31ECqrgsp4icrJOxhk=" 721 | }, 722 | "delegates": { 723 | "version": "1.0.0", 724 | "resolved": "https://registry.npmjs.org/delegates/-/delegates-1.0.0.tgz", 725 | "integrity": "sha1-hMbhWbgZBP3KWaDvRM2HDTElD5o=" 726 | }, 727 | "depd": { 728 | "version": "1.1.2", 729 | "resolved": "https://registry.npmjs.org/depd/-/depd-1.1.2.tgz", 730 | "integrity": "sha1-m81S4UwJd2PnSbJ0xDRu0uVgtak=" 731 | }, 732 | "destroy": { 733 | "version": "1.0.4", 734 | "resolved": "https://registry.npmjs.org/destroy/-/destroy-1.0.4.tgz", 735 | "integrity": "sha1-l4hXRCxEdJ5CBmE+N5RiBYJqvYA=" 736 | }, 737 | "detect-libc": { 738 | "version": "1.0.3", 739 | "resolved": "https://registry.npmjs.org/detect-libc/-/detect-libc-1.0.3.tgz", 740 | "integrity": "sha1-+hN8S9aY7fVc1c0CrFWfkaTEups=" 741 | }, 742 | "dottie": { 743 | "version": "2.0.1", 744 | "resolved": "https://registry.npmjs.org/dottie/-/dottie-2.0.1.tgz", 745 | "integrity": "sha512-ch5OQgvGDK2u8pSZeSYAQaV/lczImd7pMJ7BcEPXmnFVjy4yJIzP6CsODJUTH8mg1tyH1Z2abOiuJO3DjZ/GBw==" 746 | }, 747 | "ecc-jsbn": { 748 | "version": "0.1.2", 749 | "resolved": "https://registry.npmjs.org/ecc-jsbn/-/ecc-jsbn-0.1.2.tgz", 750 | "integrity": "sha1-OoOpBOVDUyh4dMVkt1SThoSamMk=", 751 | "requires": { 752 | "jsbn": "~0.1.0", 753 | "safer-buffer": "^2.1.0" 754 | } 755 | }, 756 | "ecdsa-sig-formatter": { 757 | "version": "1.0.10", 758 | "resolved": "https://registry.npmjs.org/ecdsa-sig-formatter/-/ecdsa-sig-formatter-1.0.10.tgz", 759 | "integrity": "sha1-HFlQAPBKiJffuFAAiSoPTDOvhsM=", 760 | "requires": { 761 | "safe-buffer": "^5.0.1" 762 | } 763 | }, 764 | "ee-first": { 765 | "version": "1.1.1", 766 | "resolved": "https://registry.npmjs.org/ee-first/-/ee-first-1.1.1.tgz", 767 | "integrity": "sha1-WQxhFWsK4vTwJVcyoViyZrxWsh0=" 768 | }, 769 | "ejs": { 770 | "version": "2.5.9", 771 | "resolved": "https://registry.npmjs.org/ejs/-/ejs-2.5.9.tgz", 772 | "integrity": "sha512-GJCAeDBKfREgkBtgrYSf9hQy9kTb3helv0zGdzqhM7iAkW8FA/ZF97VQDbwFiwIT8MQLLOe5VlPZOEvZAqtUAQ==" 773 | }, 774 | "encodeurl": { 775 | "version": "1.0.2", 776 | "resolved": "https://registry.npmjs.org/encodeurl/-/encodeurl-1.0.2.tgz", 777 | "integrity": "sha1-rT/0yG7C0CkyL1oCw6mmBslbP1k=" 778 | }, 779 | "escape-html": { 780 | "version": "1.0.3", 781 | "resolved": "https://registry.npmjs.org/escape-html/-/escape-html-1.0.3.tgz", 782 | "integrity": "sha1-Aljq5NPQwJdN4cFpGI7wBR0dGYg=" 783 | }, 784 | "etag": { 785 | "version": "1.8.1", 786 | "resolved": "https://registry.npmjs.org/etag/-/etag-1.8.1.tgz", 787 | "integrity": "sha1-Qa4u62XvpiJorr/qg6x9eSmbCIc=" 788 | }, 789 | "express": { 790 | "version": "4.16.4", 791 | "resolved": "https://registry.npmjs.org/express/-/express-4.16.4.tgz", 792 | "integrity": "sha512-j12Uuyb4FMrd/qQAm6uCHAkPtO8FDTRJZBDd5D2KOL2eLaz1yUNdUB/NOIyq0iU4q4cFarsUCrnFDPBcnksuOg==", 793 | "requires": { 794 | "accepts": "~1.3.5", 795 | "array-flatten": "1.1.1", 796 | "body-parser": "1.18.3", 797 | "content-disposition": "0.5.2", 798 | "content-type": "~1.0.4", 799 | "cookie": "0.3.1", 800 | "cookie-signature": "1.0.6", 801 | "debug": "2.6.9", 802 | "depd": "~1.1.2", 803 | "encodeurl": "~1.0.2", 804 | "escape-html": "~1.0.3", 805 | "etag": "~1.8.1", 806 | "finalhandler": "1.1.1", 807 | "fresh": "0.5.2", 808 | "merge-descriptors": "1.0.1", 809 | "methods": "~1.1.2", 810 | "on-finished": "~2.3.0", 811 | "parseurl": "~1.3.2", 812 | "path-to-regexp": "0.1.7", 813 | "proxy-addr": "~2.0.4", 814 | "qs": "6.5.2", 815 | "range-parser": "~1.2.0", 816 | "safe-buffer": "5.1.2", 817 | "send": "0.16.2", 818 | "serve-static": "1.13.2", 819 | "setprototypeof": "1.1.0", 820 | "statuses": "~1.4.0", 821 | "type-is": "~1.6.16", 822 | "utils-merge": "1.0.1", 823 | "vary": "~1.1.2" 824 | } 825 | }, 826 | "extend": { 827 | "version": "3.0.2", 828 | "resolved": "https://registry.npmjs.org/extend/-/extend-3.0.2.tgz", 829 | "integrity": "sha512-fjquC59cD7CyW6urNXK0FBufkZcoiGG80wTuPujX590cB5Ttln20E2UB4S/WARVqhXffZl2LNgS+gQdPIIim/g==" 830 | }, 831 | "extsprintf": { 832 | "version": "1.3.0", 833 | "resolved": "https://registry.npmjs.org/extsprintf/-/extsprintf-1.3.0.tgz", 834 | "integrity": "sha1-lpGEQOMEGnpBT4xS48V06zw+HgU=" 835 | }, 836 | "fast-deep-equal": { 837 | "version": "2.0.1", 838 | "resolved": "https://registry.npmjs.org/fast-deep-equal/-/fast-deep-equal-2.0.1.tgz", 839 | "integrity": "sha1-ewUhjd+WZ79/Nwv3/bLLFf3Qqkk=" 840 | }, 841 | "fast-json-stable-stringify": { 842 | "version": "2.0.0", 843 | "resolved": "https://registry.npmjs.org/fast-json-stable-stringify/-/fast-json-stable-stringify-2.0.0.tgz", 844 | "integrity": "sha1-1RQsDK7msRifh9OnYREGT4bIu/I=" 845 | }, 846 | "finalhandler": { 847 | "version": "1.1.1", 848 | "resolved": "http://registry.npmjs.org/finalhandler/-/finalhandler-1.1.1.tgz", 849 | "integrity": "sha512-Y1GUDo39ez4aHAw7MysnUD5JzYX+WaIj8I57kO3aEPT1fFRL4sr7mjei97FgnwhAyyzRYmQZaTHb2+9uZ1dPtg==", 850 | "requires": { 851 | "debug": "2.6.9", 852 | "encodeurl": "~1.0.2", 853 | "escape-html": "~1.0.3", 854 | "on-finished": "~2.3.0", 855 | "parseurl": "~1.3.2", 856 | "statuses": "~1.4.0", 857 | "unpipe": "~1.0.0" 858 | } 859 | }, 860 | "forever-agent": { 861 | "version": "0.6.1", 862 | "resolved": "https://registry.npmjs.org/forever-agent/-/forever-agent-0.6.1.tgz", 863 | "integrity": "sha1-+8cfDEGt6zf5bFd60e1C2P2sypE=" 864 | }, 865 | "form-data": { 866 | "version": "2.3.3", 867 | "resolved": "https://registry.npmjs.org/form-data/-/form-data-2.3.3.tgz", 868 | "integrity": "sha512-1lLKB2Mu3aGP1Q/2eCOx0fNbRMe7XdwktwOruhfqqd0rIJWwN4Dh+E3hrPSlDCXnSR7UtZ1N38rVXm+6+MEhJQ==", 869 | "requires": { 870 | "asynckit": "^0.4.0", 871 | "combined-stream": "^1.0.6", 872 | "mime-types": "^2.1.12" 873 | } 874 | }, 875 | "forwarded": { 876 | "version": "0.1.2", 877 | "resolved": "https://registry.npmjs.org/forwarded/-/forwarded-0.1.2.tgz", 878 | "integrity": "sha1-mMI9qxF1ZXuMBXPozszZGw/xjIQ=" 879 | }, 880 | "fresh": { 881 | "version": "0.5.2", 882 | "resolved": "https://registry.npmjs.org/fresh/-/fresh-0.5.2.tgz", 883 | "integrity": "sha1-PYyt2Q2XZWn6g1qx+OSyOhBWBac=" 884 | }, 885 | "fs-minipass": { 886 | "version": "1.2.5", 887 | "resolved": "https://registry.npmjs.org/fs-minipass/-/fs-minipass-1.2.5.tgz", 888 | "integrity": "sha512-JhBl0skXjUPCFH7x6x61gQxrKyXsxB5gcgePLZCwfyCGGsTISMoIeObbrvVeP6Xmyaudw4TT43qV2Gz+iyd2oQ==", 889 | "requires": { 890 | "minipass": "^2.2.1" 891 | } 892 | }, 893 | "fs.realpath": { 894 | "version": "1.0.0", 895 | "resolved": "https://registry.npmjs.org/fs.realpath/-/fs.realpath-1.0.0.tgz", 896 | "integrity": "sha1-FQStJSMVjKpA20onh8sBQRmU6k8=" 897 | }, 898 | "gauge": { 899 | "version": "2.7.4", 900 | "resolved": "https://registry.npmjs.org/gauge/-/gauge-2.7.4.tgz", 901 | "integrity": "sha1-LANAXHU4w51+s3sxcCLjJfsBi/c=", 902 | "requires": { 903 | "aproba": "^1.0.3", 904 | "console-control-strings": "^1.0.0", 905 | "has-unicode": "^2.0.0", 906 | "object-assign": "^4.1.0", 907 | "signal-exit": "^3.0.0", 908 | "string-width": "^1.0.1", 909 | "strip-ansi": "^3.0.1", 910 | "wide-align": "^1.1.0" 911 | } 912 | }, 913 | "generic-pool": { 914 | "version": "3.4.2", 915 | "resolved": "https://registry.npmjs.org/generic-pool/-/generic-pool-3.4.2.tgz", 916 | "integrity": "sha512-H7cUpwCQSiJmAHM4c/aFu6fUfrhWXW1ncyh8ftxEPMu6AiYkHw9K8br720TGPZJbk5eOH2bynjZD1yPvdDAmag==" 917 | }, 918 | "getpass": { 919 | "version": "0.1.7", 920 | "resolved": "https://registry.npmjs.org/getpass/-/getpass-0.1.7.tgz", 921 | "integrity": "sha1-Xv+OPmhNVprkyysSgmBOi6YhSfo=", 922 | "requires": { 923 | "assert-plus": "^1.0.0" 924 | } 925 | }, 926 | "glob": { 927 | "version": "7.1.3", 928 | "resolved": "https://registry.npmjs.org/glob/-/glob-7.1.3.tgz", 929 | "integrity": "sha512-vcfuiIxogLV4DlGBHIUOwI0IbrJ8HWPc4MU7HzviGeNho/UJDfi6B5p3sHeWIQ0KGIU0Jpxi5ZHxemQfLkkAwQ==", 930 | "requires": { 931 | "fs.realpath": "^1.0.0", 932 | "inflight": "^1.0.4", 933 | "inherits": "2", 934 | "minimatch": "^3.0.4", 935 | "once": "^1.3.0", 936 | "path-is-absolute": "^1.0.0" 937 | } 938 | }, 939 | "har-schema": { 940 | "version": "2.0.0", 941 | "resolved": "https://registry.npmjs.org/har-schema/-/har-schema-2.0.0.tgz", 942 | "integrity": "sha1-qUwiJOvKwEeCoNkDVSHyRzW37JI=" 943 | }, 944 | "har-validator": { 945 | "version": "5.1.3", 946 | "resolved": "https://registry.npmjs.org/har-validator/-/har-validator-5.1.3.tgz", 947 | "integrity": "sha512-sNvOCzEQNr/qrvJgc3UG/kD4QtlHycrzwS+6mfTrrSq97BvaYcPZZI1ZSqGSPR73Cxn4LKTD4PttRwfU7jWq5g==", 948 | "requires": { 949 | "ajv": "^6.5.5", 950 | "har-schema": "^2.0.0" 951 | } 952 | }, 953 | "has-unicode": { 954 | "version": "2.0.1", 955 | "resolved": "https://registry.npmjs.org/has-unicode/-/has-unicode-2.0.1.tgz", 956 | "integrity": "sha1-4Ob+aijPUROIVeCG0Wkedx3iqLk=" 957 | }, 958 | "http-errors": { 959 | "version": "1.6.3", 960 | "resolved": "http://registry.npmjs.org/http-errors/-/http-errors-1.6.3.tgz", 961 | "integrity": "sha1-i1VoC7S+KDoLW/TqLjhYC+HZMg0=", 962 | "requires": { 963 | "depd": "~1.1.2", 964 | "inherits": "2.0.3", 965 | "setprototypeof": "1.1.0", 966 | "statuses": ">= 1.4.0 < 2" 967 | } 968 | }, 969 | "http-signature": { 970 | "version": "1.2.0", 971 | "resolved": "https://registry.npmjs.org/http-signature/-/http-signature-1.2.0.tgz", 972 | "integrity": "sha1-muzZJRFHcvPZW2WmCruPfBj7rOE=", 973 | "requires": { 974 | "assert-plus": "^1.0.0", 975 | "jsprim": "^1.2.2", 976 | "sshpk": "^1.7.0" 977 | } 978 | }, 979 | "iconv-lite": { 980 | "version": "0.4.24", 981 | "resolved": "https://registry.npmjs.org/iconv-lite/-/iconv-lite-0.4.24.tgz", 982 | "integrity": "sha512-v3MXnZAcvnywkTUEZomIActle7RXXeedOR31wwl7VlyoXO4Qi9arvSenNQWne1TcRwhCL1HwLI21bEqdpj8/rA==", 983 | "requires": { 984 | "safer-buffer": ">= 2.1.2 < 3" 985 | } 986 | }, 987 | "ignore-walk": { 988 | "version": "3.0.1", 989 | "resolved": "https://registry.npmjs.org/ignore-walk/-/ignore-walk-3.0.1.tgz", 990 | "integrity": "sha512-DTVlMx3IYPe0/JJcYP7Gxg7ttZZu3IInhuEhbchuqneY9wWe5Ojy2mXLBaQFUQmo0AW2r3qG7m1mg86js+gnlQ==", 991 | "requires": { 992 | "minimatch": "^3.0.4" 993 | } 994 | }, 995 | "inflection": { 996 | "version": "1.12.0", 997 | "resolved": "https://registry.npmjs.org/inflection/-/inflection-1.12.0.tgz", 998 | "integrity": "sha1-ogCTVlbW9fa8TcdQLhrstwMihBY=" 999 | }, 1000 | "inflight": { 1001 | "version": "1.0.6", 1002 | "resolved": "https://registry.npmjs.org/inflight/-/inflight-1.0.6.tgz", 1003 | "integrity": "sha1-Sb1jMdfQLQwJvJEKEHW6gWW1bfk=", 1004 | "requires": { 1005 | "once": "^1.3.0", 1006 | "wrappy": "1" 1007 | } 1008 | }, 1009 | "inherits": { 1010 | "version": "2.0.3", 1011 | "resolved": "https://registry.npmjs.org/inherits/-/inherits-2.0.3.tgz", 1012 | "integrity": "sha1-Yzwsg+PaQqUC9SRmAiSA9CCCYd4=" 1013 | }, 1014 | "ini": { 1015 | "version": "1.3.5", 1016 | "resolved": "https://registry.npmjs.org/ini/-/ini-1.3.5.tgz", 1017 | "integrity": "sha512-RZY5huIKCMRWDUqZlEi72f/lmXKMvuszcMBduliQ3nnWbx9X/ZBQO7DijMEYS9EhHBb2qacRUMtC7svLwe0lcw==" 1018 | }, 1019 | "ipaddr.js": { 1020 | "version": "1.8.0", 1021 | "resolved": "https://registry.npmjs.org/ipaddr.js/-/ipaddr.js-1.8.0.tgz", 1022 | "integrity": "sha1-6qM9bd16zo9/b+DJygRA5wZzix4=" 1023 | }, 1024 | "is-bluebird": { 1025 | "version": "1.0.2", 1026 | "resolved": "https://registry.npmjs.org/is-bluebird/-/is-bluebird-1.0.2.tgz", 1027 | "integrity": "sha1-CWQ5Bg9KpBGr7hkUOoTWpVNG1uI=" 1028 | }, 1029 | "is-fullwidth-code-point": { 1030 | "version": "1.0.0", 1031 | "resolved": "https://registry.npmjs.org/is-fullwidth-code-point/-/is-fullwidth-code-point-1.0.0.tgz", 1032 | "integrity": "sha1-754xOG8DGn8NZDr4L95QxFfvAMs=", 1033 | "requires": { 1034 | "number-is-nan": "^1.0.0" 1035 | } 1036 | }, 1037 | "is-typedarray": { 1038 | "version": "1.0.0", 1039 | "resolved": "https://registry.npmjs.org/is-typedarray/-/is-typedarray-1.0.0.tgz", 1040 | "integrity": "sha1-5HnICFjfDBsR3dppQPlgEfzaSpo=" 1041 | }, 1042 | "isarray": { 1043 | "version": "1.0.0", 1044 | "resolved": "https://registry.npmjs.org/isarray/-/isarray-1.0.0.tgz", 1045 | "integrity": "sha1-u5NdSFgsuhaMBoNJV6VKPgcSTxE=" 1046 | }, 1047 | "isstream": { 1048 | "version": "0.1.2", 1049 | "resolved": "https://registry.npmjs.org/isstream/-/isstream-0.1.2.tgz", 1050 | "integrity": "sha1-R+Y/evVa+m+S4VAOaQ64uFKcCZo=" 1051 | }, 1052 | "jsbn": { 1053 | "version": "0.1.1", 1054 | "resolved": "https://registry.npmjs.org/jsbn/-/jsbn-0.1.1.tgz", 1055 | "integrity": "sha1-peZUwuWi3rXyAdls77yoDA7y9RM=" 1056 | }, 1057 | "json-schema": { 1058 | "version": "0.2.3", 1059 | "resolved": "https://registry.npmjs.org/json-schema/-/json-schema-0.2.3.tgz", 1060 | "integrity": "sha1-tIDIkuWaLwWVTOcnvT8qTogvnhM=" 1061 | }, 1062 | "json-schema-traverse": { 1063 | "version": "0.4.1", 1064 | "resolved": "https://registry.npmjs.org/json-schema-traverse/-/json-schema-traverse-0.4.1.tgz", 1065 | "integrity": "sha512-xbbCH5dCYU5T8LcEhhuh7HJ88HXuW3qsI3Y0zOZFKfZEHcpWiHU/Jxzk629Brsab/mMiHQti9wMP+845RPe3Vg==" 1066 | }, 1067 | "json-stringify-safe": { 1068 | "version": "5.0.1", 1069 | "resolved": "https://registry.npmjs.org/json-stringify-safe/-/json-stringify-safe-5.0.1.tgz", 1070 | "integrity": "sha1-Epai1Y/UXxmg9s4B1lcB4sc1tus=" 1071 | }, 1072 | "jsonwebtoken": { 1073 | "version": "8.4.0", 1074 | "resolved": "https://registry.npmjs.org/jsonwebtoken/-/jsonwebtoken-8.4.0.tgz", 1075 | "integrity": "sha512-coyXjRTCy0pw5WYBpMvWOMN+Kjaik2MwTUIq9cna/W7NpO9E+iYbumZONAz3hcr+tXFJECoQVrtmIoC3Oz0gvg==", 1076 | "requires": { 1077 | "jws": "^3.1.5", 1078 | "lodash.includes": "^4.3.0", 1079 | "lodash.isboolean": "^3.0.3", 1080 | "lodash.isinteger": "^4.0.4", 1081 | "lodash.isnumber": "^3.0.3", 1082 | "lodash.isplainobject": "^4.0.6", 1083 | "lodash.isstring": "^4.0.1", 1084 | "lodash.once": "^4.0.0", 1085 | "ms": "^2.1.1" 1086 | } 1087 | }, 1088 | "jsprim": { 1089 | "version": "1.4.1", 1090 | "resolved": "https://registry.npmjs.org/jsprim/-/jsprim-1.4.1.tgz", 1091 | "integrity": "sha1-MT5mvB5cwG5Di8G3SZwuXFastqI=", 1092 | "requires": { 1093 | "assert-plus": "1.0.0", 1094 | "extsprintf": "1.3.0", 1095 | "json-schema": "0.2.3", 1096 | "verror": "1.10.0" 1097 | } 1098 | }, 1099 | "jwa": { 1100 | "version": "1.1.6", 1101 | "resolved": "https://registry.npmjs.org/jwa/-/jwa-1.1.6.tgz", 1102 | "integrity": "sha512-tBO/cf++BUsJkYql/kBbJroKOgHWEigTKBAjjBEmrMGYd1QMBC74Hr4Wo2zCZw6ZrVhlJPvoMrkcOnlWR/DJfw==", 1103 | "requires": { 1104 | "buffer-equal-constant-time": "1.0.1", 1105 | "ecdsa-sig-formatter": "1.0.10", 1106 | "safe-buffer": "^5.0.1" 1107 | } 1108 | }, 1109 | "jws": { 1110 | "version": "3.1.5", 1111 | "resolved": "https://registry.npmjs.org/jws/-/jws-3.1.5.tgz", 1112 | "integrity": "sha512-GsCSexFADNQUr8T5HPJvayTjvPIfoyJPtLQBwn5a4WZQchcrPMPMAWcC1AzJVRDKyD6ZPROPAxgv6rfHViO4uQ==", 1113 | "requires": { 1114 | "jwa": "^1.1.5", 1115 | "safe-buffer": "^5.0.1" 1116 | } 1117 | }, 1118 | "lodash": { 1119 | "version": "4.17.11", 1120 | "resolved": "https://registry.npmjs.org/lodash/-/lodash-4.17.11.tgz", 1121 | "integrity": "sha512-cQKh8igo5QUhZ7lg38DYWAxMvjSAKG0A8wGSVimP07SIUEK2UO+arSRKbRZWtelMtN5V0Hkwh5ryOto/SshYIg==" 1122 | }, 1123 | "lodash.includes": { 1124 | "version": "4.3.0", 1125 | "resolved": "https://registry.npmjs.org/lodash.includes/-/lodash.includes-4.3.0.tgz", 1126 | "integrity": "sha1-YLuYqHy5I8aMoeUTJUgzFISfVT8=" 1127 | }, 1128 | "lodash.isboolean": { 1129 | "version": "3.0.3", 1130 | "resolved": "https://registry.npmjs.org/lodash.isboolean/-/lodash.isboolean-3.0.3.tgz", 1131 | "integrity": "sha1-bC4XHbKiV82WgC/UOwGyDV9YcPY=" 1132 | }, 1133 | "lodash.isinteger": { 1134 | "version": "4.0.4", 1135 | "resolved": "https://registry.npmjs.org/lodash.isinteger/-/lodash.isinteger-4.0.4.tgz", 1136 | "integrity": "sha1-YZwK89A/iwTDH1iChAt3sRzWg0M=" 1137 | }, 1138 | "lodash.isnumber": { 1139 | "version": "3.0.3", 1140 | "resolved": "https://registry.npmjs.org/lodash.isnumber/-/lodash.isnumber-3.0.3.tgz", 1141 | "integrity": "sha1-POdoEMWSjQM1IwGsKHMX8RwLH/w=" 1142 | }, 1143 | "lodash.isplainobject": { 1144 | "version": "4.0.6", 1145 | "resolved": "https://registry.npmjs.org/lodash.isplainobject/-/lodash.isplainobject-4.0.6.tgz", 1146 | "integrity": "sha1-fFJqUtibRcRcxpC4gWO+BJf1UMs=" 1147 | }, 1148 | "lodash.isstring": { 1149 | "version": "4.0.1", 1150 | "resolved": "https://registry.npmjs.org/lodash.isstring/-/lodash.isstring-4.0.1.tgz", 1151 | "integrity": "sha1-1SfftUVuynzJu5XV2ur4i6VKVFE=" 1152 | }, 1153 | "lodash.once": { 1154 | "version": "4.1.1", 1155 | "resolved": "https://registry.npmjs.org/lodash.once/-/lodash.once-4.1.1.tgz", 1156 | "integrity": "sha1-DdOXEhPHxW34gJd9UEyI+0cal6w=" 1157 | }, 1158 | "media-typer": { 1159 | "version": "0.3.0", 1160 | "resolved": "http://registry.npmjs.org/media-typer/-/media-typer-0.3.0.tgz", 1161 | "integrity": "sha1-hxDXrwqmJvj/+hzgAWhUUmMlV0g=" 1162 | }, 1163 | "merge-descriptors": { 1164 | "version": "1.0.1", 1165 | "resolved": "https://registry.npmjs.org/merge-descriptors/-/merge-descriptors-1.0.1.tgz", 1166 | "integrity": "sha1-sAqqVW3YtEVoFQ7J0blT8/kMu2E=" 1167 | }, 1168 | "methods": { 1169 | "version": "1.1.2", 1170 | "resolved": "https://registry.npmjs.org/methods/-/methods-1.1.2.tgz", 1171 | "integrity": "sha1-VSmk1nZUE07cxSZmVoNbD4Ua/O4=" 1172 | }, 1173 | "mime": { 1174 | "version": "1.4.1", 1175 | "resolved": "https://registry.npmjs.org/mime/-/mime-1.4.1.tgz", 1176 | "integrity": "sha512-KI1+qOZu5DcW6wayYHSzR/tXKCDC5Om4s1z2QJjDULzLcmf3DvzS7oluY4HCTrc+9FiKmWUgeNLg7W3uIQvxtQ==" 1177 | }, 1178 | "mime-db": { 1179 | "version": "1.37.0", 1180 | "resolved": "https://registry.npmjs.org/mime-db/-/mime-db-1.37.0.tgz", 1181 | "integrity": "sha512-R3C4db6bgQhlIhPU48fUtdVmKnflq+hRdad7IyKhtFj06VPNVdk2RhiYL3UjQIlso8L+YxAtFkobT0VK+S/ybg==" 1182 | }, 1183 | "mime-types": { 1184 | "version": "2.1.21", 1185 | "resolved": "https://registry.npmjs.org/mime-types/-/mime-types-2.1.21.tgz", 1186 | "integrity": "sha512-3iL6DbwpyLzjR3xHSFNFeb9Nz/M8WDkX33t1GFQnFOllWk8pOrh/LSrB5OXlnlW5P9LH73X6loW/eogc+F5lJg==", 1187 | "requires": { 1188 | "mime-db": "~1.37.0" 1189 | } 1190 | }, 1191 | "minimatch": { 1192 | "version": "3.0.4", 1193 | "resolved": "https://registry.npmjs.org/minimatch/-/minimatch-3.0.4.tgz", 1194 | "integrity": "sha512-yJHVQEhyqPLUTgt9B83PXu6W3rx4MvvHvSUvToogpwoGDOUQ+yDrR0HRot+yOCdCO7u4hX3pWft6kWBBcqh0UA==", 1195 | "requires": { 1196 | "brace-expansion": "^1.1.7" 1197 | } 1198 | }, 1199 | "minimist": { 1200 | "version": "0.0.8", 1201 | "resolved": "http://registry.npmjs.org/minimist/-/minimist-0.0.8.tgz", 1202 | "integrity": "sha1-hX/Kv8M5fSYluCKCYuhqp6ARsF0=" 1203 | }, 1204 | "minipass": { 1205 | "version": "2.3.5", 1206 | "resolved": "https://registry.npmjs.org/minipass/-/minipass-2.3.5.tgz", 1207 | "integrity": "sha512-Gi1W4k059gyRbyVUZQ4mEqLm0YIUiGYfvxhF6SIlk3ui1WVxMTGfGdQ2SInh3PDrRTVvPKgULkpJtT4RH10+VA==", 1208 | "requires": { 1209 | "safe-buffer": "^5.1.2", 1210 | "yallist": "^3.0.0" 1211 | } 1212 | }, 1213 | "minizlib": { 1214 | "version": "1.1.1", 1215 | "resolved": "https://registry.npmjs.org/minizlib/-/minizlib-1.1.1.tgz", 1216 | "integrity": "sha512-TrfjCjk4jLhcJyGMYymBH6oTXcWjYbUAXTHDbtnWHjZC25h0cdajHuPE1zxb4DVmu8crfh+HwH/WMuyLG0nHBg==", 1217 | "requires": { 1218 | "minipass": "^2.2.1" 1219 | } 1220 | }, 1221 | "mkdirp": { 1222 | "version": "0.5.1", 1223 | "resolved": "http://registry.npmjs.org/mkdirp/-/mkdirp-0.5.1.tgz", 1224 | "integrity": "sha1-MAV0OOrGz3+MR2fzhkjWaX11yQM=", 1225 | "requires": { 1226 | "minimist": "0.0.8" 1227 | } 1228 | }, 1229 | "moment": { 1230 | "version": "2.22.2", 1231 | "resolved": "https://registry.npmjs.org/moment/-/moment-2.22.2.tgz", 1232 | "integrity": "sha1-PCV/mDn8DpP/UxSWMiOeuQeD/2Y=" 1233 | }, 1234 | "moment-timezone": { 1235 | "version": "0.5.23", 1236 | "resolved": "https://registry.npmjs.org/moment-timezone/-/moment-timezone-0.5.23.tgz", 1237 | "integrity": "sha512-WHFH85DkCfiNMDX5D3X7hpNH3/PUhjTGcD0U1SgfBGZxJ3qUmJh5FdvaFjcClxOvB3rzdfj4oRffbI38jEnC1w==", 1238 | "requires": { 1239 | "moment": ">= 2.9.0" 1240 | } 1241 | }, 1242 | "morgan": { 1243 | "version": "1.9.1", 1244 | "resolved": "https://registry.npmjs.org/morgan/-/morgan-1.9.1.tgz", 1245 | "integrity": "sha512-HQStPIV4y3afTiCYVxirakhlCfGkI161c76kKFca7Fk1JusM//Qeo1ej2XaMniiNeaZklMVrh3vTtIzpzwbpmA==", 1246 | "requires": { 1247 | "basic-auth": "~2.0.0", 1248 | "debug": "2.6.9", 1249 | "depd": "~1.1.2", 1250 | "on-finished": "~2.3.0", 1251 | "on-headers": "~1.0.1" 1252 | } 1253 | }, 1254 | "ms": { 1255 | "version": "2.1.1", 1256 | "resolved": "https://registry.npmjs.org/ms/-/ms-2.1.1.tgz", 1257 | "integrity": "sha512-tgp+dl5cGk28utYktBsrFqA7HKgrhgPsg6Z/EfhWI4gl1Hwq8B/GmY/0oXZ6nF8hDVesS/FpnYaD/kOWhYQvyg==" 1258 | }, 1259 | "nan": { 1260 | "version": "2.11.1", 1261 | "resolved": "https://registry.npmjs.org/nan/-/nan-2.11.1.tgz", 1262 | "integrity": "sha512-iji6k87OSXa0CcrLl9z+ZiYSuR2o+c0bGuNmXdrhTQTakxytAFsC56SArGYoiHlJlFoHSnvmhpceZJaXkVuOtA==" 1263 | }, 1264 | "needle": { 1265 | "version": "2.2.4", 1266 | "resolved": "https://registry.npmjs.org/needle/-/needle-2.2.4.tgz", 1267 | "integrity": "sha512-HyoqEb4wr/rsoaIDfTH2aVL9nWtQqba2/HvMv+++m8u0dz808MaagKILxtfeSN7QU7nvbQ79zk3vYOJp9zsNEA==", 1268 | "requires": { 1269 | "debug": "^2.1.2", 1270 | "iconv-lite": "^0.4.4", 1271 | "sax": "^1.2.4" 1272 | } 1273 | }, 1274 | "negotiator": { 1275 | "version": "0.6.1", 1276 | "resolved": "https://registry.npmjs.org/negotiator/-/negotiator-0.6.1.tgz", 1277 | "integrity": "sha1-KzJxhOiZIQEXeyhWP7XnECrNDKk=" 1278 | }, 1279 | "node-pre-gyp": { 1280 | "version": "0.10.3", 1281 | "resolved": "https://registry.npmjs.org/node-pre-gyp/-/node-pre-gyp-0.10.3.tgz", 1282 | "integrity": "sha512-d1xFs+C/IPS8Id0qPTZ4bUT8wWryfR/OzzAFxweG+uLN85oPzyo2Iw6bVlLQ/JOdgNonXLCoRyqDzDWq4iw72A==", 1283 | "requires": { 1284 | "detect-libc": "^1.0.2", 1285 | "mkdirp": "^0.5.1", 1286 | "needle": "^2.2.1", 1287 | "nopt": "^4.0.1", 1288 | "npm-packlist": "^1.1.6", 1289 | "npmlog": "^4.0.2", 1290 | "rc": "^1.2.7", 1291 | "rimraf": "^2.6.1", 1292 | "semver": "^5.3.0", 1293 | "tar": "^4" 1294 | } 1295 | }, 1296 | "nopt": { 1297 | "version": "4.0.1", 1298 | "resolved": "https://registry.npmjs.org/nopt/-/nopt-4.0.1.tgz", 1299 | "integrity": "sha1-0NRoWv1UFRk8jHUFYC0NF81kR00=", 1300 | "requires": { 1301 | "abbrev": "1", 1302 | "osenv": "^0.1.4" 1303 | } 1304 | }, 1305 | "npm-bundled": { 1306 | "version": "1.0.5", 1307 | "resolved": "https://registry.npmjs.org/npm-bundled/-/npm-bundled-1.0.5.tgz", 1308 | "integrity": "sha512-m/e6jgWu8/v5niCUKQi9qQl8QdeEduFA96xHDDzFGqly0OOjI7c+60KM/2sppfnUU9JJagf+zs+yGhqSOFj71g==" 1309 | }, 1310 | "npm-packlist": { 1311 | "version": "1.1.12", 1312 | "resolved": "https://registry.npmjs.org/npm-packlist/-/npm-packlist-1.1.12.tgz", 1313 | "integrity": "sha512-WJKFOVMeAlsU/pjXuqVdzU0WfgtIBCupkEVwn+1Y0ERAbUfWw8R4GjgVbaKnUjRoD2FoQbHOCbOyT5Mbs9Lw4g==", 1314 | "requires": { 1315 | "ignore-walk": "^3.0.1", 1316 | "npm-bundled": "^1.0.1" 1317 | } 1318 | }, 1319 | "npmlog": { 1320 | "version": "4.1.2", 1321 | "resolved": "https://registry.npmjs.org/npmlog/-/npmlog-4.1.2.tgz", 1322 | "integrity": "sha512-2uUqazuKlTaSI/dC8AzicUck7+IrEaOnN/e0jd3Xtt1KcGpwx30v50mL7oPyr/h9bL3E4aZccVwpwP+5W9Vjkg==", 1323 | "requires": { 1324 | "are-we-there-yet": "~1.1.2", 1325 | "console-control-strings": "~1.1.0", 1326 | "gauge": "~2.7.3", 1327 | "set-blocking": "~2.0.0" 1328 | } 1329 | }, 1330 | "number-is-nan": { 1331 | "version": "1.0.1", 1332 | "resolved": "https://registry.npmjs.org/number-is-nan/-/number-is-nan-1.0.1.tgz", 1333 | "integrity": "sha1-CXtgK1NCKlIsGvuHkDGDNpQaAR0=" 1334 | }, 1335 | "oauth-sign": { 1336 | "version": "0.9.0", 1337 | "resolved": "https://registry.npmjs.org/oauth-sign/-/oauth-sign-0.9.0.tgz", 1338 | "integrity": "sha512-fexhUFFPTGV8ybAtSIGbV6gOkSv8UtRbDBnAyLQw4QPKkgNlsH2ByPGtMUqdWkos6YCRmAqViwgZrJc/mRDzZQ==" 1339 | }, 1340 | "object-assign": { 1341 | "version": "4.1.1", 1342 | "resolved": "https://registry.npmjs.org/object-assign/-/object-assign-4.1.1.tgz", 1343 | "integrity": "sha1-IQmtx5ZYh8/AXLvUQsrIv7s2CGM=" 1344 | }, 1345 | "on-finished": { 1346 | "version": "2.3.0", 1347 | "resolved": "https://registry.npmjs.org/on-finished/-/on-finished-2.3.0.tgz", 1348 | "integrity": "sha1-IPEzZIGwg811M3mSoWlxqi2QaUc=", 1349 | "requires": { 1350 | "ee-first": "1.1.1" 1351 | } 1352 | }, 1353 | "on-headers": { 1354 | "version": "1.0.1", 1355 | "resolved": "https://registry.npmjs.org/on-headers/-/on-headers-1.0.1.tgz", 1356 | "integrity": "sha1-ko9dD0cNSTQmUepnlLCFfBAGk/c=" 1357 | }, 1358 | "once": { 1359 | "version": "1.4.0", 1360 | "resolved": "https://registry.npmjs.org/once/-/once-1.4.0.tgz", 1361 | "integrity": "sha1-WDsap3WWHUsROsF9nFC6753Xa9E=", 1362 | "requires": { 1363 | "wrappy": "1" 1364 | } 1365 | }, 1366 | "os-homedir": { 1367 | "version": "1.0.2", 1368 | "resolved": "http://registry.npmjs.org/os-homedir/-/os-homedir-1.0.2.tgz", 1369 | "integrity": "sha1-/7xJiDNuDoM94MFox+8VISGqf7M=" 1370 | }, 1371 | "os-tmpdir": { 1372 | "version": "1.0.2", 1373 | "resolved": "http://registry.npmjs.org/os-tmpdir/-/os-tmpdir-1.0.2.tgz", 1374 | "integrity": "sha1-u+Z0BseaqFxc/sdm/lc0VV36EnQ=" 1375 | }, 1376 | "osenv": { 1377 | "version": "0.1.5", 1378 | "resolved": "https://registry.npmjs.org/osenv/-/osenv-0.1.5.tgz", 1379 | "integrity": "sha512-0CWcCECdMVc2Rw3U5w9ZjqX6ga6ubk1xDVKxtBQPK7wis/0F2r9T6k4ydGYhecl7YUBxBVxhL5oisPsNxAPe2g==", 1380 | "requires": { 1381 | "os-homedir": "^1.0.0", 1382 | "os-tmpdir": "^1.0.0" 1383 | } 1384 | }, 1385 | "parseurl": { 1386 | "version": "1.3.2", 1387 | "resolved": "https://registry.npmjs.org/parseurl/-/parseurl-1.3.2.tgz", 1388 | "integrity": "sha1-/CidTtiZMRlGDBViUyYs3I3mW/M=" 1389 | }, 1390 | "path-is-absolute": { 1391 | "version": "1.0.1", 1392 | "resolved": "http://registry.npmjs.org/path-is-absolute/-/path-is-absolute-1.0.1.tgz", 1393 | "integrity": "sha1-F0uSaHNVNP+8es5r9TpanhtcX18=" 1394 | }, 1395 | "path-to-regexp": { 1396 | "version": "0.1.7", 1397 | "resolved": "https://registry.npmjs.org/path-to-regexp/-/path-to-regexp-0.1.7.tgz", 1398 | "integrity": "sha1-32BBeABfUi8V60SQ5yR6G/qmf4w=" 1399 | }, 1400 | "performance-now": { 1401 | "version": "2.1.0", 1402 | "resolved": "https://registry.npmjs.org/performance-now/-/performance-now-2.1.0.tgz", 1403 | "integrity": "sha1-Ywn04OX6kT7BxpMHrjZLSzd8nns=" 1404 | }, 1405 | "process-nextick-args": { 1406 | "version": "2.0.0", 1407 | "resolved": "https://registry.npmjs.org/process-nextick-args/-/process-nextick-args-2.0.0.tgz", 1408 | "integrity": "sha512-MtEC1TqN0EU5nephaJ4rAtThHtC86dNN9qCuEhtshvpVBkAW5ZO7BASN9REnF9eoXGcRub+pFuKEpOHE+HbEMw==" 1409 | }, 1410 | "proxy-addr": { 1411 | "version": "2.0.4", 1412 | "resolved": "https://registry.npmjs.org/proxy-addr/-/proxy-addr-2.0.4.tgz", 1413 | "integrity": "sha512-5erio2h9jp5CHGwcybmxmVqHmnCBZeewlfJ0pex+UW7Qny7OOZXTtH56TGNyBizkgiOwhJtMKrVzDTeKcySZwA==", 1414 | "requires": { 1415 | "forwarded": "~0.1.2", 1416 | "ipaddr.js": "1.8.0" 1417 | } 1418 | }, 1419 | "psl": { 1420 | "version": "1.1.29", 1421 | "resolved": "https://registry.npmjs.org/psl/-/psl-1.1.29.tgz", 1422 | "integrity": "sha512-AeUmQ0oLN02flVHXWh9sSJF7mcdFq0ppid/JkErufc3hGIV/AMa8Fo9VgDo/cT2jFdOWoFvHp90qqBH54W+gjQ==" 1423 | }, 1424 | "punycode": { 1425 | "version": "2.1.1", 1426 | "resolved": "https://registry.npmjs.org/punycode/-/punycode-2.1.1.tgz", 1427 | "integrity": "sha512-XRsRjdf+j5ml+y/6GKHPZbrF/8p2Yga0JPtdqTIY2Xe5ohJPD9saDJJLPvp9+NSBprVvevdXZybnj2cv8OEd0A==" 1428 | }, 1429 | "qs": { 1430 | "version": "6.5.2", 1431 | "resolved": "https://registry.npmjs.org/qs/-/qs-6.5.2.tgz", 1432 | "integrity": "sha512-N5ZAX4/LxJmF+7wN74pUD6qAh9/wnvdQcjq9TZjevvXzSUo7bfmw91saqMjzGS2xq91/odN2dW/WOl7qQHNDGA==" 1433 | }, 1434 | "range-parser": { 1435 | "version": "1.2.0", 1436 | "resolved": "https://registry.npmjs.org/range-parser/-/range-parser-1.2.0.tgz", 1437 | "integrity": "sha1-9JvmtIeJTdxA3MlKMi9hEJLgDV4=" 1438 | }, 1439 | "raw-body": { 1440 | "version": "2.3.3", 1441 | "resolved": "https://registry.npmjs.org/raw-body/-/raw-body-2.3.3.tgz", 1442 | "integrity": "sha512-9esiElv1BrZoI3rCDuOuKCBRbuApGGaDPQfjSflGxdy4oyzqghxu6klEkkVIvBje+FF0BX9coEv8KqW6X/7njw==", 1443 | "requires": { 1444 | "bytes": "3.0.0", 1445 | "http-errors": "1.6.3", 1446 | "iconv-lite": "0.4.23", 1447 | "unpipe": "1.0.0" 1448 | }, 1449 | "dependencies": { 1450 | "iconv-lite": { 1451 | "version": "0.4.23", 1452 | "resolved": "https://registry.npmjs.org/iconv-lite/-/iconv-lite-0.4.23.tgz", 1453 | "integrity": "sha512-neyTUVFtahjf0mB3dZT77u+8O0QB89jFdnBkd5P1JgYPbPaia3gXXOVL2fq8VyU2gMMD7SaN7QukTB/pmXYvDA==", 1454 | "requires": { 1455 | "safer-buffer": ">= 2.1.2 < 3" 1456 | } 1457 | } 1458 | } 1459 | }, 1460 | "rc": { 1461 | "version": "1.2.8", 1462 | "resolved": "https://registry.npmjs.org/rc/-/rc-1.2.8.tgz", 1463 | "integrity": "sha512-y3bGgqKj3QBdxLbLkomlohkvsA8gdAiUQlSBJnBhfn+BPxg4bc62d8TcBW15wavDfgexCgccckhcZvywyQYPOw==", 1464 | "requires": { 1465 | "deep-extend": "^0.6.0", 1466 | "ini": "~1.3.0", 1467 | "minimist": "^1.2.0", 1468 | "strip-json-comments": "~2.0.1" 1469 | }, 1470 | "dependencies": { 1471 | "minimist": { 1472 | "version": "1.2.0", 1473 | "resolved": "http://registry.npmjs.org/minimist/-/minimist-1.2.0.tgz", 1474 | "integrity": "sha1-o1AIsg9BOD7sH7kU9M1d95omQoQ=" 1475 | } 1476 | } 1477 | }, 1478 | "readable-stream": { 1479 | "version": "2.3.6", 1480 | "resolved": "http://registry.npmjs.org/readable-stream/-/readable-stream-2.3.6.tgz", 1481 | "integrity": "sha512-tQtKA9WIAhBF3+VLAseyMqZeBjW0AHJoxOtYqSUZNJxauErmLbVm2FW1y+J/YA9dUrAC39ITejlZWhVIwawkKw==", 1482 | "requires": { 1483 | "core-util-is": "~1.0.0", 1484 | "inherits": "~2.0.3", 1485 | "isarray": "~1.0.0", 1486 | "process-nextick-args": "~2.0.0", 1487 | "safe-buffer": "~5.1.1", 1488 | "string_decoder": "~1.1.1", 1489 | "util-deprecate": "~1.0.1" 1490 | } 1491 | }, 1492 | "request": { 1493 | "version": "2.88.0", 1494 | "resolved": "https://registry.npmjs.org/request/-/request-2.88.0.tgz", 1495 | "integrity": "sha512-NAqBSrijGLZdM0WZNsInLJpkJokL72XYjUpnB0iwsRgxh7dB6COrHnTBNwN0E+lHDAJzu7kLAkDeY08z2/A0hg==", 1496 | "requires": { 1497 | "aws-sign2": "~0.7.0", 1498 | "aws4": "^1.8.0", 1499 | "caseless": "~0.12.0", 1500 | "combined-stream": "~1.0.6", 1501 | "extend": "~3.0.2", 1502 | "forever-agent": "~0.6.1", 1503 | "form-data": "~2.3.2", 1504 | "har-validator": "~5.1.0", 1505 | "http-signature": "~1.2.0", 1506 | "is-typedarray": "~1.0.0", 1507 | "isstream": "~0.1.2", 1508 | "json-stringify-safe": "~5.0.1", 1509 | "mime-types": "~2.1.19", 1510 | "oauth-sign": "~0.9.0", 1511 | "performance-now": "^2.1.0", 1512 | "qs": "~6.5.2", 1513 | "safe-buffer": "^5.1.2", 1514 | "tough-cookie": "~2.4.3", 1515 | "tunnel-agent": "^0.6.0", 1516 | "uuid": "^3.3.2" 1517 | } 1518 | }, 1519 | "retry-as-promised": { 1520 | "version": "2.3.2", 1521 | "resolved": "https://registry.npmjs.org/retry-as-promised/-/retry-as-promised-2.3.2.tgz", 1522 | "integrity": "sha1-zZdO5P2bX+A8vzGHHuSCIcB3N7c=", 1523 | "requires": { 1524 | "bluebird": "^3.4.6", 1525 | "debug": "^2.6.9" 1526 | }, 1527 | "dependencies": { 1528 | "debug": { 1529 | "version": "2.6.9", 1530 | "resolved": "https://registry.npmjs.org/debug/-/debug-2.6.9.tgz", 1531 | "integrity": "sha512-bC7ElrdJaJnPbAP+1EotYvqZsb3ecl5wi6Bfi6BJTUcNowp6cvspg0jXznRTKDjm/E7AdgFBVeAPVMNcKGsHMA==", 1532 | "requires": { 1533 | "ms": "2.0.0" 1534 | } 1535 | }, 1536 | "ms": { 1537 | "version": "2.0.0", 1538 | "resolved": "https://registry.npmjs.org/ms/-/ms-2.0.0.tgz", 1539 | "integrity": "sha1-VgiurfwAvmwpAd9fmGF4jeDVl8g=" 1540 | } 1541 | } 1542 | }, 1543 | "rimraf": { 1544 | "version": "2.6.2", 1545 | "resolved": "https://registry.npmjs.org/rimraf/-/rimraf-2.6.2.tgz", 1546 | "integrity": "sha512-lreewLK/BlghmxtfH36YYVg1i8IAce4TI7oao75I1g245+6BctqTVQiBP3YUJ9C6DQOXJmkYR9X9fCLtCOJc5w==", 1547 | "requires": { 1548 | "glob": "^7.0.5" 1549 | } 1550 | }, 1551 | "safe-buffer": { 1552 | "version": "5.1.2", 1553 | "resolved": "https://registry.npmjs.org/safe-buffer/-/safe-buffer-5.1.2.tgz", 1554 | "integrity": "sha512-Gd2UZBJDkXlY7GbJxfsE8/nvKkUEU1G38c1siN6QP6a9PT9MmHB8GnpscSmMJSoF8LOIrt8ud/wPtojys4G6+g==" 1555 | }, 1556 | "safer-buffer": { 1557 | "version": "2.1.2", 1558 | "resolved": "https://registry.npmjs.org/safer-buffer/-/safer-buffer-2.1.2.tgz", 1559 | "integrity": "sha512-YZo3K82SD7Riyi0E1EQPojLz7kpepnSQI9IyPbHHg1XXXevb5dJI7tpyN2ADxGcQbHG7vcyRHk0cbwqcQriUtg==" 1560 | }, 1561 | "sax": { 1562 | "version": "1.2.4", 1563 | "resolved": "https://registry.npmjs.org/sax/-/sax-1.2.4.tgz", 1564 | "integrity": "sha512-NqVDv9TpANUjFm0N8uM5GxL36UgKi9/atZw+x7YFnQ8ckwFGKrl4xX4yWtrey3UJm5nP1kUbnYgLopqWNSRhWw==" 1565 | }, 1566 | "semver": { 1567 | "version": "5.6.0", 1568 | "resolved": "https://registry.npmjs.org/semver/-/semver-5.6.0.tgz", 1569 | "integrity": "sha512-RS9R6R35NYgQn++fkDWaOmqGoj4Ek9gGs+DPxNUZKuwE183xjJroKvyo1IzVFeXvUrvmALy6FWD5xrdJT25gMg==" 1570 | }, 1571 | "send": { 1572 | "version": "0.16.2", 1573 | "resolved": "https://registry.npmjs.org/send/-/send-0.16.2.tgz", 1574 | "integrity": "sha512-E64YFPUssFHEFBvpbbjr44NCLtI1AohxQ8ZSiJjQLskAdKuriYEP6VyGEsRDH8ScozGpkaX1BGvhanqCwkcEZw==", 1575 | "requires": { 1576 | "debug": "2.6.9", 1577 | "depd": "~1.1.2", 1578 | "destroy": "~1.0.4", 1579 | "encodeurl": "~1.0.2", 1580 | "escape-html": "~1.0.3", 1581 | "etag": "~1.8.1", 1582 | "fresh": "0.5.2", 1583 | "http-errors": "~1.6.2", 1584 | "mime": "1.4.1", 1585 | "ms": "2.0.0", 1586 | "on-finished": "~2.3.0", 1587 | "range-parser": "~1.2.0", 1588 | "statuses": "~1.4.0" 1589 | }, 1590 | "dependencies": { 1591 | "ms": { 1592 | "version": "2.0.0", 1593 | "resolved": "https://registry.npmjs.org/ms/-/ms-2.0.0.tgz", 1594 | "integrity": "sha1-VgiurfwAvmwpAd9fmGF4jeDVl8g=" 1595 | } 1596 | } 1597 | }, 1598 | "sequelize": { 1599 | "version": "4.41.2", 1600 | "resolved": "https://registry.npmjs.org/sequelize/-/sequelize-4.41.2.tgz", 1601 | "integrity": "sha512-8vPf2R0o9iEmtzkqNzwFdblO+0Mu+RNxOdLeYGGqWGlp3cushLpQucAeSGPQgf2hQVZP5yOCM1ouZKTQ5FTlvA==", 1602 | "requires": { 1603 | "bluebird": "^3.5.0", 1604 | "cls-bluebird": "^2.1.0", 1605 | "debug": "^3.1.0", 1606 | "depd": "^1.1.0", 1607 | "dottie": "^2.0.0", 1608 | "generic-pool": "^3.4.0", 1609 | "inflection": "1.12.0", 1610 | "lodash": "^4.17.1", 1611 | "moment": "^2.20.0", 1612 | "moment-timezone": "^0.5.14", 1613 | "retry-as-promised": "^2.3.2", 1614 | "semver": "^5.5.0", 1615 | "terraformer-wkt-parser": "^1.1.2", 1616 | "toposort-class": "^1.0.1", 1617 | "uuid": "^3.2.1", 1618 | "validator": "^10.4.0", 1619 | "wkx": "^0.4.1" 1620 | }, 1621 | "dependencies": { 1622 | "debug": { 1623 | "version": "3.2.6", 1624 | "resolved": "https://registry.npmjs.org/debug/-/debug-3.2.6.tgz", 1625 | "integrity": "sha512-mel+jf7nrtEl5Pn1Qx46zARXKDpBbvzezse7p7LqINmdoIk8PYP5SySaxEmYv6TZ0JyEKA1hsCId6DIhgITtWQ==", 1626 | "requires": { 1627 | "ms": "^2.1.1" 1628 | } 1629 | } 1630 | } 1631 | }, 1632 | "serve-static": { 1633 | "version": "1.13.2", 1634 | "resolved": "https://registry.npmjs.org/serve-static/-/serve-static-1.13.2.tgz", 1635 | "integrity": "sha512-p/tdJrO4U387R9oMjb1oj7qSMaMfmOyd4j9hOFoxZe2baQszgHcSWjuya/CiT5kgZZKRudHNOA0pYXOl8rQ5nw==", 1636 | "requires": { 1637 | "encodeurl": "~1.0.2", 1638 | "escape-html": "~1.0.3", 1639 | "parseurl": "~1.3.2", 1640 | "send": "0.16.2" 1641 | } 1642 | }, 1643 | "set-blocking": { 1644 | "version": "2.0.0", 1645 | "resolved": "https://registry.npmjs.org/set-blocking/-/set-blocking-2.0.0.tgz", 1646 | "integrity": "sha1-BF+XgtARrppoA93TgrJDkrPYkPc=" 1647 | }, 1648 | "setprototypeof": { 1649 | "version": "1.1.0", 1650 | "resolved": "https://registry.npmjs.org/setprototypeof/-/setprototypeof-1.1.0.tgz", 1651 | "integrity": "sha512-BvE/TwpZX4FXExxOxZyRGQQv651MSwmWKZGqvmPcRIjDqWub67kTKuIMx43cZZrS/cBBzwBcNDWoFxt2XEFIpQ==" 1652 | }, 1653 | "shimmer": { 1654 | "version": "1.2.0", 1655 | "resolved": "https://registry.npmjs.org/shimmer/-/shimmer-1.2.0.tgz", 1656 | "integrity": "sha512-xTCx2vohXC2EWWDqY/zb4+5Mu28D+HYNSOuFzsyRDRvI/e1ICb69afwaUwfjr+25ZXldbOLyp+iDUZHq8UnTag==" 1657 | }, 1658 | "signal-exit": { 1659 | "version": "3.0.2", 1660 | "resolved": "https://registry.npmjs.org/signal-exit/-/signal-exit-3.0.2.tgz", 1661 | "integrity": "sha1-tf3AjxKH6hF4Yo5BXiUTK3NkbG0=" 1662 | }, 1663 | "sprintf-js": { 1664 | "version": "1.0.3", 1665 | "resolved": "https://registry.npmjs.org/sprintf-js/-/sprintf-js-1.0.3.tgz", 1666 | "integrity": "sha1-BOaSb2YolTVPPdAVIDYzuFcpfiw=" 1667 | }, 1668 | "sqlite3": { 1669 | "version": "4.0.4", 1670 | "resolved": "https://registry.npmjs.org/sqlite3/-/sqlite3-4.0.4.tgz", 1671 | "integrity": "sha512-CO8vZMyUXBPC+E3iXOCc7Tz2pAdq5BWfLcQmOokCOZW5S5sZ/paijiPOCdvzpdP83RroWHYa5xYlVqCxSqpnQg==", 1672 | "requires": { 1673 | "nan": "~2.10.0", 1674 | "node-pre-gyp": "^0.10.3", 1675 | "request": "^2.87.0" 1676 | }, 1677 | "dependencies": { 1678 | "nan": { 1679 | "version": "2.10.0", 1680 | "resolved": "http://registry.npmjs.org/nan/-/nan-2.10.0.tgz", 1681 | "integrity": "sha512-bAdJv7fBLhWC+/Bls0Oza+mvTaNQtP+1RyhhhvD95pgUJz6XM5IzgmxOkItJ9tkoCiplvAnXI1tNmmUD/eScyA==" 1682 | } 1683 | } 1684 | }, 1685 | "sshpk": { 1686 | "version": "1.15.2", 1687 | "resolved": "https://registry.npmjs.org/sshpk/-/sshpk-1.15.2.tgz", 1688 | "integrity": "sha512-Ra/OXQtuh0/enyl4ETZAfTaeksa6BXks5ZcjpSUNrjBr0DvrJKX+1fsKDPpT9TBXgHAFsa4510aNVgI8g/+SzA==", 1689 | "requires": { 1690 | "asn1": "~0.2.3", 1691 | "assert-plus": "^1.0.0", 1692 | "bcrypt-pbkdf": "^1.0.0", 1693 | "dashdash": "^1.12.0", 1694 | "ecc-jsbn": "~0.1.1", 1695 | "getpass": "^0.1.1", 1696 | "jsbn": "~0.1.0", 1697 | "safer-buffer": "^2.0.2", 1698 | "tweetnacl": "~0.14.0" 1699 | } 1700 | }, 1701 | "statuses": { 1702 | "version": "1.4.0", 1703 | "resolved": "https://registry.npmjs.org/statuses/-/statuses-1.4.0.tgz", 1704 | "integrity": "sha512-zhSCtt8v2NDrRlPQpCNtw/heZLtfUDqxBM1udqikb/Hbk52LK4nQSwr10u77iopCW5LsyHpuXS0GnEc48mLeew==" 1705 | }, 1706 | "string-width": { 1707 | "version": "1.0.2", 1708 | "resolved": "https://registry.npmjs.org/string-width/-/string-width-1.0.2.tgz", 1709 | "integrity": "sha1-EYvfW4zcUaKn5w0hHgfisLmxB9M=", 1710 | "requires": { 1711 | "code-point-at": "^1.0.0", 1712 | "is-fullwidth-code-point": "^1.0.0", 1713 | "strip-ansi": "^3.0.0" 1714 | } 1715 | }, 1716 | "string_decoder": { 1717 | "version": "1.1.1", 1718 | "resolved": "https://registry.npmjs.org/string_decoder/-/string_decoder-1.1.1.tgz", 1719 | "integrity": "sha512-n/ShnvDi6FHbbVfviro+WojiFzv+s8MPMHBczVePfUpDJLwoLT0ht1l4YwBCbi8pJAveEEdnkHyPyTP/mzRfwg==", 1720 | "requires": { 1721 | "safe-buffer": "~5.1.0" 1722 | } 1723 | }, 1724 | "strip-ansi": { 1725 | "version": "3.0.1", 1726 | "resolved": "http://registry.npmjs.org/strip-ansi/-/strip-ansi-3.0.1.tgz", 1727 | "integrity": "sha1-ajhfuIU9lS1f8F0Oiq+UJ43GPc8=", 1728 | "requires": { 1729 | "ansi-regex": "^2.0.0" 1730 | } 1731 | }, 1732 | "strip-json-comments": { 1733 | "version": "2.0.1", 1734 | "resolved": "https://registry.npmjs.org/strip-json-comments/-/strip-json-comments-2.0.1.tgz", 1735 | "integrity": "sha1-PFMZQukIwml8DsNEhYwobHygpgo=" 1736 | }, 1737 | "tar": { 1738 | "version": "4.4.8", 1739 | "resolved": "https://registry.npmjs.org/tar/-/tar-4.4.8.tgz", 1740 | "integrity": "sha512-LzHF64s5chPQQS0IYBn9IN5h3i98c12bo4NCO7e0sGM2llXQ3p2FGC5sdENN4cTW48O915Sh+x+EXx7XW96xYQ==", 1741 | "requires": { 1742 | "chownr": "^1.1.1", 1743 | "fs-minipass": "^1.2.5", 1744 | "minipass": "^2.3.4", 1745 | "minizlib": "^1.1.1", 1746 | "mkdirp": "^0.5.0", 1747 | "safe-buffer": "^5.1.2", 1748 | "yallist": "^3.0.2" 1749 | } 1750 | }, 1751 | "terraformer": { 1752 | "version": "1.0.9", 1753 | "resolved": "https://registry.npmjs.org/terraformer/-/terraformer-1.0.9.tgz", 1754 | "integrity": "sha512-YlmQ1fsMWTkKGDGibCRWgmLzrpDRUr63Q025LJ/taYQ6j1Yb8q9McKF7NBi6ACAyUXO6F/bl9w6v4MY307y5Ag==", 1755 | "requires": { 1756 | "@types/geojson": "^1.0.0" 1757 | } 1758 | }, 1759 | "terraformer-wkt-parser": { 1760 | "version": "1.2.0", 1761 | "resolved": "https://registry.npmjs.org/terraformer-wkt-parser/-/terraformer-wkt-parser-1.2.0.tgz", 1762 | "integrity": "sha512-QU3iA54St5lF8Za1jg1oj4NYc8sn5tCZ08aNSWDeGzrsaV48eZk1iAVWasxhNspYBoCqdHuoot1pUTUrE1AJ4w==", 1763 | "requires": { 1764 | "@types/geojson": "^1.0.0", 1765 | "terraformer": "~1.0.5" 1766 | } 1767 | }, 1768 | "toposort-class": { 1769 | "version": "1.0.1", 1770 | "resolved": "https://registry.npmjs.org/toposort-class/-/toposort-class-1.0.1.tgz", 1771 | "integrity": "sha1-f/0feMi+KMO6Rc1OGj9e4ZO9mYg=" 1772 | }, 1773 | "tough-cookie": { 1774 | "version": "2.4.3", 1775 | "resolved": "https://registry.npmjs.org/tough-cookie/-/tough-cookie-2.4.3.tgz", 1776 | "integrity": "sha512-Q5srk/4vDM54WJsJio3XNn6K2sCG+CQ8G5Wz6bZhRZoAe/+TxjWB/GlFAnYEbkYVlON9FMk/fE3h2RLpPXo4lQ==", 1777 | "requires": { 1778 | "psl": "^1.1.24", 1779 | "punycode": "^1.4.1" 1780 | }, 1781 | "dependencies": { 1782 | "punycode": { 1783 | "version": "1.4.1", 1784 | "resolved": "https://registry.npmjs.org/punycode/-/punycode-1.4.1.tgz", 1785 | "integrity": "sha1-wNWmOycYgArY4esPpSachN1BhF4=" 1786 | } 1787 | } 1788 | }, 1789 | "tunnel-agent": { 1790 | "version": "0.6.0", 1791 | "resolved": "https://registry.npmjs.org/tunnel-agent/-/tunnel-agent-0.6.0.tgz", 1792 | "integrity": "sha1-J6XeoGs2sEoKmWZ3SykIaPD8QP0=", 1793 | "requires": { 1794 | "safe-buffer": "^5.0.1" 1795 | } 1796 | }, 1797 | "tweetnacl": { 1798 | "version": "0.14.5", 1799 | "resolved": "https://registry.npmjs.org/tweetnacl/-/tweetnacl-0.14.5.tgz", 1800 | "integrity": "sha1-WuaBd/GS1EViadEIr6k/+HQ/T2Q=" 1801 | }, 1802 | "type-is": { 1803 | "version": "1.6.16", 1804 | "resolved": "https://registry.npmjs.org/type-is/-/type-is-1.6.16.tgz", 1805 | "integrity": "sha512-HRkVv/5qY2G6I8iab9cI7v1bOIdhm94dVjQCPFElW9W+3GeDOSHmy2EBYe4VTApuzolPcmgFTN3ftVJRKR2J9Q==", 1806 | "requires": { 1807 | "media-typer": "0.3.0", 1808 | "mime-types": "~2.1.18" 1809 | } 1810 | }, 1811 | "unpipe": { 1812 | "version": "1.0.0", 1813 | "resolved": "https://registry.npmjs.org/unpipe/-/unpipe-1.0.0.tgz", 1814 | "integrity": "sha1-sr9O6FFKrmFltIF4KdIbLvSZBOw=" 1815 | }, 1816 | "uri-js": { 1817 | "version": "4.2.2", 1818 | "resolved": "https://registry.npmjs.org/uri-js/-/uri-js-4.2.2.tgz", 1819 | "integrity": "sha512-KY9Frmirql91X2Qgjry0Wd4Y+YTdrdZheS8TFwvkbLWf/G5KNJDCh6pKL5OZctEW4+0Baa5idK2ZQuELRwPznQ==", 1820 | "requires": { 1821 | "punycode": "^2.1.0" 1822 | } 1823 | }, 1824 | "util-deprecate": { 1825 | "version": "1.0.2", 1826 | "resolved": "https://registry.npmjs.org/util-deprecate/-/util-deprecate-1.0.2.tgz", 1827 | "integrity": "sha1-RQ1Nyfpw3nMnYvvS1KKJgUGaDM8=" 1828 | }, 1829 | "utils-merge": { 1830 | "version": "1.0.1", 1831 | "resolved": "https://registry.npmjs.org/utils-merge/-/utils-merge-1.0.1.tgz", 1832 | "integrity": "sha1-n5VxD1CiZ5R7LMwSR0HBAoQn5xM=" 1833 | }, 1834 | "uuid": { 1835 | "version": "3.3.2", 1836 | "resolved": "https://registry.npmjs.org/uuid/-/uuid-3.3.2.tgz", 1837 | "integrity": "sha512-yXJmeNaw3DnnKAOKJE51sL/ZaYfWJRl1pK9dr19YFCu0ObS231AB1/LbqTKRAQ5kw8A90rA6fr4riOUpTZvQZA==" 1838 | }, 1839 | "validator": { 1840 | "version": "10.9.0", 1841 | "resolved": "https://registry.npmjs.org/validator/-/validator-10.9.0.tgz", 1842 | "integrity": "sha512-hZJcZSWz9poXBlAkjjcsNAdrZ6JbjD3kWlNjq/+vE7RLLS/+8PAj3qVVwrwsOz/WL8jPmZ1hYkRvtlUeZAm4ug==" 1843 | }, 1844 | "vary": { 1845 | "version": "1.1.2", 1846 | "resolved": "https://registry.npmjs.org/vary/-/vary-1.1.2.tgz", 1847 | "integrity": "sha1-IpnwLG3tMNSllhsLn3RSShj2NPw=" 1848 | }, 1849 | "verror": { 1850 | "version": "1.10.0", 1851 | "resolved": "https://registry.npmjs.org/verror/-/verror-1.10.0.tgz", 1852 | "integrity": "sha1-OhBcoXBTr1XW4nDB+CiGguGNpAA=", 1853 | "requires": { 1854 | "assert-plus": "^1.0.0", 1855 | "core-util-is": "1.0.2", 1856 | "extsprintf": "^1.2.0" 1857 | } 1858 | }, 1859 | "wide-align": { 1860 | "version": "1.1.3", 1861 | "resolved": "https://registry.npmjs.org/wide-align/-/wide-align-1.1.3.tgz", 1862 | "integrity": "sha512-QGkOQc8XL6Bt5PwnsExKBPuMKBxnGxWWW3fU55Xt4feHozMUhdUMaBCk290qpm/wG5u/RSKzwdAC4i51YigihA==", 1863 | "requires": { 1864 | "string-width": "^1.0.2 || 2" 1865 | } 1866 | }, 1867 | "wkx": { 1868 | "version": "0.4.5", 1869 | "resolved": "https://registry.npmjs.org/wkx/-/wkx-0.4.5.tgz", 1870 | "integrity": "sha512-01dloEcJZAJabLO5XdcRgqdKpmnxS0zIT02LhkdWOZX2Zs2tPM6hlZ4XG9tWaWur1Qd1OO4kJxUbe2+5BofvnA==", 1871 | "requires": { 1872 | "@types/node": "*" 1873 | } 1874 | }, 1875 | "wrappy": { 1876 | "version": "1.0.2", 1877 | "resolved": "https://registry.npmjs.org/wrappy/-/wrappy-1.0.2.tgz", 1878 | "integrity": "sha1-tSQ9jz7BqjXxNkYFvA0QNuMKtp8=" 1879 | }, 1880 | "yallist": { 1881 | "version": "3.0.3", 1882 | "resolved": "https://registry.npmjs.org/yallist/-/yallist-3.0.3.tgz", 1883 | "integrity": "sha512-S+Zk8DEWE6oKpV+vI3qWkaK+jSbIK86pCwe2IF/xwIpQ8jEuxpw9NyaGjmp9+BoJv5FV2piqCDcoCtStppiq2A==" 1884 | }, 1885 | "yamljs": { 1886 | "version": "0.3.0", 1887 | "resolved": "https://registry.npmjs.org/yamljs/-/yamljs-0.3.0.tgz", 1888 | "integrity": "sha512-C/FsVVhht4iPQYXOInoxUM/1ELSf9EsgKH34FofQOp6hwCPrW4vG4w5++TED3xRUo8gD7l0P1J1dLlDYzODsTQ==", 1889 | "requires": { 1890 | "argparse": "^1.0.7", 1891 | "glob": "^7.0.5" 1892 | } 1893 | } 1894 | } 1895 | } 1896 | -------------------------------------------------------------------------------- /package.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "node-express-jwt-example", 3 | "version": "0.0.0", 4 | "private": true, 5 | "scripts": { 6 | "start": "node ./bin/www" 7 | }, 8 | "dependencies": { 9 | "bcrypt": "^3.0.2", 10 | "cookie-parser": "~1.4.3", 11 | "cors": "^2.8.5", 12 | "debug": "~2.6.9", 13 | "ejs": "~2.5.7", 14 | "express": "~4.16.0", 15 | "http-errors": "~1.6.2", 16 | "jsonwebtoken": "^8.4.0", 17 | "morgan": "~1.9.0", 18 | "sequelize": "^4.41.2", 19 | "sqlite3": "^4.0.4", 20 | "yamljs": "^0.3.0" 21 | } 22 | } 23 | -------------------------------------------------------------------------------- /public/stylesheets/style.css: -------------------------------------------------------------------------------- 1 | body { 2 | padding: 50px; 3 | font: 14px "Lucida Grande", Helvetica, Arial, sans-serif; 4 | } 5 | 6 | a { 7 | color: #00B7FF; 8 | } 9 | -------------------------------------------------------------------------------- /server/controllers/api/index.js: -------------------------------------------------------------------------------- 1 | const V1 = require('./v1'); 2 | 3 | module.exports = { 4 | V1 5 | } 6 | -------------------------------------------------------------------------------- /server/controllers/api/v1/index.js: -------------------------------------------------------------------------------- 1 | 'use strict'; 2 | 3 | const RestrictedController = require('./restricted.controller'); 4 | 5 | module.exports = { 6 | RestrictedController 7 | } 8 | -------------------------------------------------------------------------------- /server/controllers/api/v1/restricted.controller.js: -------------------------------------------------------------------------------- 1 | 'use strict'; 2 | 3 | class RestrictedController { 4 | 5 | constructor() {} 6 | 7 | usersOnly(req, res) { 8 | return res.status(200).send({ message: 'Users Only Route' }); 9 | } 10 | 11 | adminOnly(req, res) { 12 | return res.status(200).send({ message: 'Admin Only Route' }); 13 | } 14 | 15 | } 16 | 17 | module.exports = new RestrictedController(); 18 | -------------------------------------------------------------------------------- /server/controllers/authentication.controller.js: -------------------------------------------------------------------------------- 1 | 'use strict'; 2 | 3 | const bcrypt = require('bcrypt'); 4 | const { UsersRepository } = require('../repositories'); 5 | const { JwtService } = require('../services'); 6 | 7 | class AuthenticationController { 8 | 9 | constructor() {} 10 | 11 | authenticate(req, res) { 12 | 13 | const username = req.body.username; 14 | const password = req.body.password; 15 | 16 | let user = UsersRepository.findOne(username); 17 | 18 | let valid = user.then((user) => { 19 | if (user) 20 | return bcrypt.compare(password, user.dataValues.password); 21 | else 22 | throw err; 23 | }); 24 | 25 | return Promise.all([user, valid]) 26 | .then(([user, valid]) => { 27 | if(valid) { 28 | let payload = { user: user.dataValues.username, roles: user.dataValues.roles }; 29 | return res.status(200).send({ token: JwtService.sign(payload) }); 30 | } else { 31 | throw err; 32 | } 33 | }) 34 | .catch((error) => res.status(401).send({ error: 'Invalid username/password' })); 35 | } 36 | 37 | } 38 | 39 | module.exports = new AuthenticationController(UsersRepository); 40 | -------------------------------------------------------------------------------- /server/controllers/index.js: -------------------------------------------------------------------------------- 1 | 'use strict'; 2 | 3 | const AuthenticationController = require('./authentication.controller'); 4 | const UsersController = require('./users.controller'); 5 | const ApiControllers = require('./api'); 6 | 7 | module.exports = { 8 | ApiControllers, 9 | AuthenticationController, 10 | UsersController 11 | }; 12 | -------------------------------------------------------------------------------- /server/controllers/users.controller.js: -------------------------------------------------------------------------------- 1 | 'use strict'; 2 | 3 | const bcrypt = require('bcrypt'); 4 | const { UsersRepository } = require('../repositories'); 5 | 6 | class UsersController { 7 | 8 | constructor() {} 9 | 10 | list(req, res) { 11 | return UsersRepository.findAll() 12 | .then(users => res.status(200).send(users)) 13 | .catch((error) => res.status(400).send({ error: "Error processing your request" })); 14 | } 15 | 16 | create(req, res) { 17 | let user = req.body; 18 | 19 | return UsersRepository.save(user) 20 | .then((user) => UsersRepository.findById(user.dataValues.id)) 21 | .then((user) => res.status(201).send(user)) 22 | .catch((error) => res.status(400).send({ error: 'Error creating new user' })); 23 | } 24 | 25 | exists(req, res) { 26 | const id = req.params.id; 27 | return UsersRepository.exists(id) 28 | .then((count) => { 29 | if(count > 0) return res.status(204).send(); 30 | else throw err; 31 | }) 32 | .catch((error) => res.status(404).send({ error: 'User id not found'})); 33 | } 34 | 35 | find(req, res) { 36 | const id = req.params.id; 37 | return UsersRepository.findById(id) 38 | .then((user) => { 39 | if(user) res.status(200).send(user) 40 | else throw err; 41 | }) 42 | .catch((error) => res.status(404).send({ error: `User with id ${id} not found`})); 43 | } 44 | 45 | replace(req, res) { 46 | const id = req.params.id; 47 | return res.status(200).send({ message: `Replaced user with id ${id}` }); 48 | } 49 | 50 | update(req, res) { 51 | const id = req.params.id; 52 | return res.status(200).send({ message: `Updated user with id ${id}` }); 53 | 54 | } 55 | 56 | delete(req, res) { 57 | const id = req.params.id; 58 | return res.status(200).send({ message: `Deleted user with id ${id}` }); 59 | } 60 | 61 | } 62 | 63 | module.exports = new UsersController(); 64 | -------------------------------------------------------------------------------- /server/index.js: -------------------------------------------------------------------------------- 1 | 'use strict'; 2 | 3 | const createError = require('http-errors'); 4 | const express = require('express'); 5 | const path = require('path'); 6 | const cookieParser = require('cookie-parser'); 7 | const logger = require('morgan'); 8 | const cors = require('cors'); 9 | const fs = require('fs'); 10 | 11 | const router = require('./routes'); 12 | 13 | class Server { 14 | 15 | constructor() { 16 | this.server = express(); 17 | } 18 | 19 | setup(config) { 20 | this.server.set('env', config.env); 21 | this.server.set('hostname', config.hostname); 22 | this.server.set('port', config.port); 23 | 24 | this.server.set('views', config.viewDir); 25 | this.server.set('view engine', 'ejs'); 26 | 27 | this.server.use(logger(config.env)); 28 | this.server.use(cors()); 29 | this.server.use(express.json()); 30 | this.server.use(express.urlencoded({ extended: false })); 31 | this.server.use(cookieParser()); 32 | this.server.use(express.static(config.staticDir)); 33 | 34 | this.server.use('/', router); 35 | 36 | this.server.use((req, res, next) => { 37 | next(createError(404)); 38 | }); 39 | 40 | this.server.use((err, req, res, next) => { 41 | res.locals.message = err.message; 42 | res.locals.error = req.app.get('env') === 'dev' ? err : {}; 43 | res.status(err.status || 500); 44 | res.render('error'); 45 | }); 46 | 47 | } 48 | 49 | start() { 50 | let hostname = this.server.get('hostname'); 51 | let port = this.server.get('port'); 52 | this.server.listen(port, () => { 53 | console.log('Express server listening on - http://' + hostname + ':' + port); 54 | }); 55 | } 56 | 57 | } 58 | 59 | module.exports = new Server(); 60 | -------------------------------------------------------------------------------- /server/middleware/index.js: -------------------------------------------------------------------------------- 1 | const JwtMiddleware = require('./jwt.middleware'); 2 | 3 | module.exports = { 4 | JwtMiddleware 5 | }; 6 | -------------------------------------------------------------------------------- /server/middleware/jwt.middleware.js: -------------------------------------------------------------------------------- 1 | const { JwtService } = require('../services'); 2 | 3 | class JwtMiddleware { 4 | 5 | constructor() {} 6 | 7 | verify(req, res, next) { 8 | const bearer = req.header('Authorization') || ''; 9 | const token = bearer.split(' ')[1]; 10 | const valid = JwtService.verify(token); 11 | return valid ? next() : res.status(401).send({ error: 'Unauthorized' }); 12 | } 13 | 14 | hasRole(role) { 15 | return (req, res, next) => { 16 | const bearer = req.header('Authorization') || ''; 17 | const token = bearer.split(' ')[1]; 18 | const decoded = JwtService.decode(token); 19 | const foundRole = decoded.payload.roles.find(e => e.role === role); 20 | return foundRole ? next() : res.status(403).send({ error: 'Access Denied' }); 21 | } 22 | } 23 | 24 | hasAllRoles(roles) { 25 | return (req, res, next) => { 26 | const bearer = req.header('Authorization') || ''; 27 | const token = bearer.split(' ')[1]; 28 | const decoded = JwtService.decode(token); 29 | const foundAllRole = roles.every(e => decoded.payload.roles.find(i => i.role === e)); 30 | return foundAllRoles ? next() : res.status(403).send({ error: 'Access Denied' }); 31 | } 32 | } 33 | 34 | hasAnyRole(roles) { 35 | return (req, res, next) => { 36 | const bearer = req.header('Authorization') || ''; 37 | const token = bearer.split(' ')[1]; 38 | const decoded = JwtService.decode(token); 39 | const foundAnyRole = roles.some(e => decoded.payload.roles.find(i => i.role === e)); 40 | return foundAnyRole ? next() : res.status(403).send({ error: 'Access Denied' }); 41 | } 42 | } 43 | 44 | } 45 | 46 | module.exports = new JwtMiddleware(); 47 | -------------------------------------------------------------------------------- /server/models/index.js: -------------------------------------------------------------------------------- 1 | 'use strict'; 2 | 3 | const fs = require('fs'); 4 | const path = require('path'); 5 | const Sequelize = require('sequelize'); 6 | const basename = path.basename(__filename); 7 | const env = process.env.NODE_ENV || 'development'; 8 | const config = require(__dirname + '/../../config/config.json')[env]; 9 | const db = {}; 10 | 11 | let sequelize; 12 | if (config.use_env_variable) { 13 | sequelize = new Sequelize(process.env[config.use_env_variable], config); 14 | } else { 15 | sequelize = new Sequelize(config.database, config.username, config.password, config); 16 | } 17 | 18 | fs 19 | .readdirSync(__dirname) 20 | .filter(file => { 21 | return (file.indexOf('.') !== 0) && (file !== basename) && (file.slice(-3) === '.js'); 22 | }) 23 | .forEach(file => { 24 | const model = sequelize['import'](path.join(__dirname, file)); 25 | db[model.name] = model; 26 | }); 27 | 28 | Object.keys(db).forEach(modelName => { 29 | if (db[modelName].associate) { 30 | db[modelName].associate(db); 31 | } 32 | }); 33 | 34 | db.sequelize = sequelize; 35 | db.Sequelize = Sequelize; 36 | 37 | module.exports = db; 38 | -------------------------------------------------------------------------------- /server/models/role.js: -------------------------------------------------------------------------------- 1 | 'use strict'; 2 | module.exports = (sequelize, DataTypes) => { 3 | const Role = sequelize.define('Role', { 4 | role: { 5 | type: DataTypes.STRING, 6 | field: 'role', 7 | allowNull: false, 8 | unique: true 9 | } 10 | }, { 11 | timestamps: false 12 | }); 13 | Role.associate = function(models) { 14 | Role.belongsToMany(models.User, { 15 | through: 'UserRole', 16 | as: 'users', 17 | foreignKey: 'roleId' 18 | }); 19 | }; 20 | return Role; 21 | }; 22 | -------------------------------------------------------------------------------- /server/models/user.js: -------------------------------------------------------------------------------- 1 | 'use strict'; 2 | 3 | const bcrypt = require('bcrypt'); 4 | 5 | module.exports = (sequelize, DataTypes) => { 6 | const User = sequelize.define('User', { 7 | username: { 8 | type: DataTypes.STRING, 9 | field: 'username' 10 | }, 11 | password: { 12 | type: DataTypes.STRING, 13 | field: 'password' 14 | } 15 | }, { 16 | timestamps: false, 17 | hooks: { 18 | beforeCreate: (user, options) => { 19 | user.password = bcrypt.hashSync(user.password, 10); 20 | user.addRole([1]); 21 | } 22 | } 23 | }); 24 | User.associate = function(models) { 25 | User.belongsToMany(models.Role, { 26 | through: 'UserRole', 27 | as: 'roles', 28 | foreignKey: 'userId' 29 | }); 30 | }; 31 | return User; 32 | }; 33 | -------------------------------------------------------------------------------- /server/models/userrole.js: -------------------------------------------------------------------------------- 1 | 'use strict'; 2 | module.exports = (sequelize, DataTypes) => { 3 | const UserRole = sequelize.define('UserRole', { 4 | userId: { 5 | type: DataTypes.INTEGER, 6 | field: 'user_id' 7 | }, 8 | roleId: { 9 | type: DataTypes.INTEGER, 10 | field: 'role_id' 11 | } 12 | }, { 13 | timestamps: false, 14 | tableName: 'user_roles' 15 | }); 16 | UserRole.associate = function(models) { 17 | // associations can be defined here 18 | }; 19 | return UserRole; 20 | }; 21 | -------------------------------------------------------------------------------- /server/repositories/index.js: -------------------------------------------------------------------------------- 1 | 'use strict'; 2 | 3 | const UsersRepository = require('./users.repository'); 4 | 5 | module.exports = { 6 | UsersRepository 7 | }; 8 | -------------------------------------------------------------------------------- /server/repositories/users.repository.js: -------------------------------------------------------------------------------- 1 | 'use strict'; 2 | 3 | const { User, Role } = require('../models'); 4 | 5 | class UsersRepository { 6 | 7 | constructor() {} 8 | 9 | findAll() { 10 | return User.findAll({ 11 | include: [{ 12 | model: Role, 13 | as: 'roles', 14 | attributes: ['role'], 15 | through: { attributes: [] } 16 | }], 17 | attributes: { 18 | exclude: ['id', 'password'] 19 | } 20 | }); 21 | } 22 | 23 | findById(id) { 24 | return User.findByPk(id, { 25 | include: [{ 26 | model: Role, 27 | as: 'roles', 28 | attributes: ['role'], 29 | through: { attributes: [] } 30 | }], 31 | attributes: { 32 | exclude: ['id', 'password'] 33 | } 34 | }); 35 | } 36 | 37 | findOne(username) { 38 | return User.findOne({ 39 | where: { username: username }, 40 | include: [{ 41 | model: Role, as: 'roles', attributes: ['role'], 42 | through: { attributes: [] } 43 | }] 44 | }); 45 | } 46 | 47 | save(user) { 48 | return User.build(user).save() 49 | .then((user) => user) 50 | .catch((error) => new Promise((resolve, reject) => reject(error))); 51 | } 52 | 53 | exists(id) { 54 | return User.count({ 55 | where: { id: id } 56 | }); 57 | } 58 | 59 | } 60 | 61 | module.exports = new UsersRepository(); 62 | -------------------------------------------------------------------------------- /server/routes/api/index.js: -------------------------------------------------------------------------------- 1 | 'use strict'; 2 | 3 | const express = require('express'); 4 | const ApiRouter = express.Router(); 5 | 6 | const ApiV1Router = require('./v1'); 7 | 8 | ApiRouter.use('/v1', ApiV1Router); 9 | 10 | module.exports = ApiRouter; 11 | -------------------------------------------------------------------------------- /server/routes/api/v1/index.js: -------------------------------------------------------------------------------- 1 | 'use strict'; 2 | 3 | const express = require('express'); 4 | const ApiV1Router = express.Router(); 5 | 6 | const RestrictedRouter = require('./restricted.router'); 7 | 8 | ApiV1Router.use('/restricted', RestrictedRouter); 9 | 10 | module.exports = ApiV1Router; 11 | -------------------------------------------------------------------------------- /server/routes/api/v1/restricted.router.js: -------------------------------------------------------------------------------- 1 | 'use strict'; 2 | 3 | const express = require('express'); 4 | const RestrictedRouter = express.Router(); 5 | 6 | const { RestrictedController } = require('../../../controllers').ApiControllers.V1; 7 | const { JwtMiddleware } = require('../../../middleware'); 8 | 9 | RestrictedRouter.get('/adminOnly', JwtMiddleware.hasRole('ADMIN'), RestrictedController.adminOnly); 10 | RestrictedRouter.get('/usersOnly', JwtMiddleware.hasRole('USER'), RestrictedController.usersOnly); 11 | 12 | module.exports = RestrictedRouter; 13 | -------------------------------------------------------------------------------- /server/routes/authentication.router.js: -------------------------------------------------------------------------------- 1 | 'use strict'; 2 | 3 | const express = require('express'); 4 | const AuthenticationRouter = express.Router(); 5 | 6 | const { AuthenticationController } = require('../controllers'); 7 | 8 | AuthenticationRouter.post('/', AuthenticationController.authenticate); 9 | 10 | module.exports = AuthenticationRouter; 11 | -------------------------------------------------------------------------------- /server/routes/index.js: -------------------------------------------------------------------------------- 1 | 'use strict'; 2 | const express = require('express'); 3 | const Router = express.Router(); 4 | 5 | const ApiRouter = require('./api'); 6 | const AuthenticationRouter = require('./authentication.router'); 7 | const UsersRouter = require('./users.router'); 8 | 9 | const { JwtMiddleware } = require('../middleware'); 10 | 11 | Router.use('/api', JwtMiddleware.verify, ApiRouter); 12 | Router.use('/auth', AuthenticationRouter); 13 | Router.use('/users', JwtMiddleware.verify, UsersRouter); 14 | 15 | module.exports = Router; 16 | -------------------------------------------------------------------------------- /server/routes/users.router.js: -------------------------------------------------------------------------------- 1 | 'use strict'; 2 | 3 | const express = require('express'); 4 | const UsersRouter = express.Router(); 5 | 6 | const { UsersController } = require('../controllers'); 7 | const { JwtMiddleware } = require('../middleware'); 8 | 9 | UsersRouter.route('/') 10 | .get(JwtMiddleware.hasRole('ADMIN'), UsersController.list) 11 | .post(JwtMiddleware.hasRole('ADMIN'), UsersController.create); 12 | 13 | UsersRouter.route('/:id') 14 | .head(JwtMiddleware.hasRole('ADMIN'), UsersController.exists) 15 | .get(JwtMiddleware.hasRole('ADMIN'), UsersController.find) 16 | .put(JwtMiddleware.hasRole('ADMIN'), UsersController.replace) 17 | .patch(JwtMiddleware.hasRole('ADMIN'), UsersController.update) 18 | .delete(JwtMiddleware.hasRole('ADMIN'), UsersController.delete); 19 | 20 | module.exports = UsersRouter; 21 | -------------------------------------------------------------------------------- /server/services/index.js: -------------------------------------------------------------------------------- 1 | 'use strict'; 2 | 3 | const JwtService = require('./jwt.service'); 4 | 5 | module.exports = { 6 | JwtService 7 | }; 8 | -------------------------------------------------------------------------------- /server/services/jwt.service.js: -------------------------------------------------------------------------------- 1 | 'use strict'; 2 | 3 | const fs = require('fs'); 4 | const jwt = require('jsonwebtoken'); 5 | 6 | const jwtConfig = require('../../config/jwt'); 7 | 8 | const privateKey = fs.readFileSync('config/private.key', 'utf8'); 9 | const publicKey = fs.readFileSync('config/public.key', 'utf8'); 10 | 11 | class JwtService { 12 | 13 | constructor() {} 14 | 15 | sign(payload) { 16 | return jwt.sign(payload, privateKey, jwtConfig.options); 17 | } 18 | 19 | verify(token) { 20 | try { 21 | return jwt.verify(token, publicKey, jwtConfig.options); 22 | } catch(error) { 23 | return false; 24 | } 25 | } 26 | 27 | decode(token) { 28 | return jwt.decode(token, { complete: true }); 29 | } 30 | 31 | } 32 | 33 | module.exports = new JwtService(); 34 | -------------------------------------------------------------------------------- /views/error.ejs: -------------------------------------------------------------------------------- 1 |
<%= error.stack %>4 | -------------------------------------------------------------------------------- /views/index.ejs: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 |
Welcome to <%= title %>
10 | 11 | 12 | --------------------------------------------------------------------------------