├── .assets ├── bak.sketch ├── logo.webp ├── logo2.png └── logo2.webp ├── .circleci └── config.yml ├── .eslintignore ├── .eslintrc ├── .gitignore ├── LICENSE ├── README.md ├── examples └── helloworld │ ├── bak.config.js │ ├── controllers │ └── api.js │ └── package.json ├── jest.config.js ├── lerna.json ├── package.json ├── packages ├── audit │ ├── CHANGELOG.md │ ├── README.md │ ├── lib │ │ ├── audit.js │ │ └── index.js │ └── package.json ├── auth │ ├── CHANGELOG.md │ ├── README.md │ ├── lib │ │ ├── controller.js │ │ ├── index.js │ │ ├── provider │ │ │ ├── base.js │ │ │ ├── default.js │ │ │ ├── user.js │ │ │ └── utils.js │ │ └── token-scheme.js │ └── package.json ├── bak │ ├── CHANGELOG.md │ ├── README.md │ ├── bin │ │ ├── bak │ │ └── cli │ │ │ ├── commands │ │ │ ├── dev.js │ │ │ └── start.js │ │ │ ├── index.js │ │ │ ├── styles.js │ │ │ └── utils.js │ ├── lib │ │ ├── bak.js │ │ ├── controller.js │ │ ├── index.js │ │ ├── options.js │ │ ├── server.js │ │ └── utils.js │ └── package.json ├── input │ ├── CHANGELOG.md │ ├── README.md │ ├── lib │ │ └── index.js │ └── package.json ├── logging │ ├── CHANGELOG.md │ ├── README.md │ ├── lib │ │ └── index.js │ └── package.json ├── mongo │ ├── CHANGELOG.md │ ├── README.md │ ├── lib │ │ ├── index.js │ │ ├── model.js │ │ └── utils.js │ └── package.json ├── nunjucks │ ├── CHANGELOG.md │ ├── README.md │ ├── lib │ │ ├── index.js │ │ └── nunjucks-engine.js │ └── package.json ├── policy │ ├── CHANGELOG.md │ ├── README.md │ ├── lib │ │ ├── authorize-plugin.js │ │ ├── guard-plugin.js │ │ └── index.js │ └── package.json ├── ratelimit │ ├── CHANGELOG.md │ ├── README.md │ ├── lib │ │ ├── index.js │ │ └── providers │ │ │ ├── memory.js │ │ │ └── redis.js │ └── package.json ├── route-table │ ├── CHANGELOG.md │ ├── README.md │ ├── lib │ │ ├── index.js │ │ └── table.js │ └── package.json └── shortcuts │ ├── CHANGELOG.md │ ├── README.md │ ├── lib │ └── index.js │ └── package.json ├── renovate.json ├── test ├── _setup.js ├── bak.spec.js └── fixtures │ └── basic │ ├── bak.config.js │ ├── controllers │ └── api.js │ └── package.json └── yarn.lock /.assets/bak.sketch: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/bakjs/bak/f69a310f2bad8adb70354761d06794212e1ba4de/.assets/bak.sketch -------------------------------------------------------------------------------- /.assets/logo.webp: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/bakjs/bak/f69a310f2bad8adb70354761d06794212e1ba4de/.assets/logo.webp -------------------------------------------------------------------------------- /.assets/logo2.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/bakjs/bak/f69a310f2bad8adb70354761d06794212e1ba4de/.assets/logo2.png -------------------------------------------------------------------------------- /.assets/logo2.webp: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/bakjs/bak/f69a310f2bad8adb70354761d06794212e1ba4de/.assets/logo2.webp -------------------------------------------------------------------------------- /.circleci/config.yml: -------------------------------------------------------------------------------- 1 | version: 2 2 | jobs: 3 | build: 4 | docker: 5 | - image: circleci/node 6 | steps: 7 | # Checkout repository 8 | - checkout 9 | 10 | # Restore cache 11 | - restore_cache: 12 | key: yarn-cache-{{ checksum "yarn.lock" }} 13 | 14 | # Install dependencies 15 | - run: 16 | name: Install Dependencies 17 | command: NODE_ENV=dev yarn 18 | 19 | # Keep cache 20 | - save_cache: 21 | key: yarn-cache-{{ checksum "yarn.lock" }} 22 | paths: 23 | - "node_modules" 24 | 25 | # Test 26 | - run: 27 | name: Tests 28 | command: yarn test && yarn codecov 29 | -------------------------------------------------------------------------------- /.eslintignore: -------------------------------------------------------------------------------- 1 | node_modules 2 | .git 3 | coverage 4 | -------------------------------------------------------------------------------- /.eslintrc: -------------------------------------------------------------------------------- 1 | { 2 | "extends": "standard", 3 | "rules": { 4 | "camelcase": "off" 5 | }, 6 | "plugins": [ 7 | "jest" 8 | ], 9 | "env": { 10 | "jest/globals": true 11 | } 12 | } -------------------------------------------------------------------------------- /.gitignore: -------------------------------------------------------------------------------- 1 | *.iml 2 | .idea 3 | node_modules 4 | *.log 5 | packages/*/yarn.lock 6 | .vscode 7 | coverage 8 | .DS_STORE -------------------------------------------------------------------------------- /LICENSE: -------------------------------------------------------------------------------- 1 | The MIT License (MIT) 2 | 3 | Copyright (c) 2016-2017 Fandogh - Pooya Parsa 4 | 5 | Permission is hereby granted, free of charge, to any person obtaining a copy 6 | of this software and associated documentation files (the "Software"), to deal 7 | in the Software without restriction, including without limitation the rights 8 | to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 9 | copies of the Software, and to permit persons to whom the Software is 10 | furnished to do so, subject to the following conditions: 11 | 12 | The above copyright notice and this permission notice shall be included in all 13 | copies or substantial portions of the Software. 14 | 15 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 16 | IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 17 | FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 18 | AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 19 | LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 20 | OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE 21 | SOFTWARE. -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 |

2 | 3 | Bak.js 4 | 5 |

6 | 7 |

Delightful modern web applications framework for hapi.js

8 | 9 |

10 | 11 | 12 | 13 | 14 | 15 | 16 | 17 | 18 | 19 | 20 | 21 | 22 | 23 | 24 | 25 | 26 | 27 | 28 | 29 | 30 | 31 |

32 | 33 |

Features

34 | 35 | ✓ Built on top of [hapi.js](https://hapijs.com), A rich framework for building applications and services 36 | 37 | ✓ Optionally using Controllers for routing 38 | 39 | ✓ Single file configuration without need to extra boilerplate 40 | 41 | ✓ CLI & Dev friendly tools 42 | 43 | ✓ Stable and rich echo-system of hapi compatible [plugins](https://github.com/bakjs/plugins) 44 | 45 | ✓ Modular design without modifying core 46 | 47 |

Getting started

48 | 49 | Install `bak` package: 50 | 51 | ```bash 52 | yarn add bak 53 | ``` 54 | 55 | Create `bak.config.js`; 56 | 57 | ```js 58 | export default { 59 | prefix: '/api', 60 | routes: [ 61 | './controllers/api' 62 | ] 63 | } 64 | ``` 65 | 66 | Create `controllers/api.js`: 67 | 68 | ```js 69 | import { Controller } from 'bak' 70 | 71 | export default class APIController extends Controller { 72 | init () { 73 | this.get('/hello/{name}', this.hello) 74 | } 75 | 76 | hello (request, reply) { 77 | return 'Hello ' + request.params.name 78 | } 79 | } 80 | ``` 81 | 82 | Start server: 83 | 84 | ```bash 85 | yarn bak dev # Use `yarn bak start` for production mode 86 | ``` 87 | 88 | Your API is up! Now you can visit [http://localhost:3000/api/hello/world](http://localhost:3000/api/hello/world) for the results. 89 | 90 | 91 | Inspect mode: 92 | 93 | You can pass Node.js supported args after `--` arg: 94 | 95 | ```bash 96 | yarn bak dev -- --inspect 97 | ``` 98 | 99 |

License

100 | 101 | Released under The MIT [LICENSE](./LICENSE) 102 | -------------------------------------------------------------------------------- /examples/helloworld/bak.config.js: -------------------------------------------------------------------------------- 1 | export default { 2 | prefix: '/api', 3 | routes: [ 4 | './controllers/api' 5 | ] 6 | } 7 | -------------------------------------------------------------------------------- /examples/helloworld/controllers/api.js: -------------------------------------------------------------------------------- 1 | import Joi from 'joi' 2 | import { Controller } from 'bak' 3 | 4 | export default class APIController extends Controller { 5 | init () { 6 | this.defaults = { 7 | validate: { 8 | payload: { 9 | foo: Joi.string() 10 | } 11 | } 12 | } 13 | 14 | this.get('/hello/{name}', this.hello) 15 | this.get('/error', this.error) 16 | 17 | this.get('/validate', this.validate, { 18 | validate: { 19 | query: { 20 | name: Joi.string().required() 21 | } 22 | } 23 | }) 24 | } 25 | 26 | validate () { 27 | return 'OK' 28 | } 29 | 30 | hello (request, h) { 31 | return 'Hello ' + request.params.name 32 | } 33 | 34 | error (request, h) { 35 | return h('foo') 36 | } 37 | } 38 | -------------------------------------------------------------------------------- /examples/helloworld/package.json: -------------------------------------------------------------------------------- 1 | { 2 | "private": true, 3 | "dependencies": { 4 | "bak": "latest" 5 | } 6 | } 7 | -------------------------------------------------------------------------------- /jest.config.js: -------------------------------------------------------------------------------- 1 | module.exports = { 2 | testEnvironment: 'node', 3 | coverageDirectory: './coverage/', 4 | collectCoverage: true 5 | } 6 | -------------------------------------------------------------------------------- /lerna.json: -------------------------------------------------------------------------------- 1 | { 2 | "version": "independent", 3 | "npmClient": "yarn", 4 | "useWorkspaces": true, 5 | "conventionalCommits": true, 6 | "exact": true, 7 | "packages": [ 8 | "packages/*" 9 | ], 10 | "command": { 11 | "publish": { 12 | "npmClient": "npm" 13 | } 14 | }, 15 | "changelog": { 16 | "labels": { 17 | "feat": "New Feature", 18 | "fix": "Bug Fix", 19 | "docs": "Documentation", 20 | "perf": "Performance", 21 | "refactor": "Code Refactoring" 22 | } 23 | } 24 | } 25 | -------------------------------------------------------------------------------- /package.json: -------------------------------------------------------------------------------- 1 | { 2 | "private": true, 3 | "workspaces": [ 4 | "packages/*" 5 | ], 6 | "contributors": [ 7 | "Pooya Parsa " 8 | ], 9 | "scripts": { 10 | "lint": "eslint .", 11 | "test": "yarn lint && jest", 12 | "dev": "yarn bak dev examples/helloworld", 13 | "release": "lerna publish" 14 | }, 15 | "devDependencies": { 16 | "axios": "^0.18.0", 17 | "codecov": "^3.5.0", 18 | "eslint": "^5.16.0", 19 | "eslint-config-standard": "^12.0.0", 20 | "eslint-plugin-html": "^5.0.5", 21 | "eslint-plugin-import": "^2.17.2", 22 | "eslint-plugin-jest": "^22.6.3", 23 | "eslint-plugin-node": "^9.0.1", 24 | "eslint-plugin-promise": "^4.1.1", 25 | "eslint-plugin-standard": "^4.0.0", 26 | "jest": "^24.8.0", 27 | "lerna": "^3.14.1", 28 | "standard-version": "^6.0.1" 29 | } 30 | } 31 | -------------------------------------------------------------------------------- /packages/audit/CHANGELOG.md: -------------------------------------------------------------------------------- 1 | # Change Log 2 | 3 | All notable changes to this project will be documented in this file. 4 | See [Conventional Commits](https://conventionalcommits.org) for commit guidelines. 5 | 6 | ## 3.0.1 (2018-11-19) 7 | 8 | **Note:** Version bump only for package @bakjs/audit 9 | 10 | 11 | 12 | 13 | 14 | 15 | # [3.0.0](https://github.com/bakjs/plugins/compare/@bakjs/audit@2.2.0...@bakjs/audit@3.0.0) (2018-02-08) 16 | 17 | 18 | ### Features 19 | 20 | * **audit:** depricate good-audit and make DB storage working ([1f3f569](https://github.com/bakjs/plugins/commit/1f3f569)) 21 | 22 | 23 | ### BREAKING CHANGES 24 | 25 | * **audit:** usage changes 26 | 27 | 28 | 29 | 30 | 31 | # [2.2.0](https://github.com/bakjs/plugins/compare/@bakjs/audit@2.1.1...@bakjs/audit@2.2.0) (2018-02-08) 32 | 33 | 34 | ### Features 35 | 36 | * export explicit configKey ([add9dfb](https://github.com/bakjs/plugins/commit/add9dfb)) 37 | 38 | 39 | 40 | 41 | 42 | ## [2.1.1](https://github.com/bakjs/plugins/compare/@bakjs/audit@2.1.0...@bakjs/audit@2.1.1) (2017-11-09) 43 | 44 | 45 | ### Bug Fixes 46 | 47 | * **audit:** minor fixes ([5fcd2c8](https://github.com/bakjs/plugins/commit/5fcd2c8)) 48 | 49 | 50 | 51 | 52 | 53 | # 2.1.0 (2017-10-24) 54 | 55 | 56 | ### Features 57 | 58 | * audit plugin ([cf01f9a](https://github.com/bakjs/plugins/commit/cf01f9a)) 59 | 60 | 61 | 62 | 63 | 64 | # 2.1.0 (2017-10-24) 65 | 66 | 67 | ### Features 68 | 69 | * audit plugin ([cf01f9a](https://github.com/bakjs/plugins/commit/cf01f9a)) 70 | -------------------------------------------------------------------------------- /packages/audit/README.md: -------------------------------------------------------------------------------- 1 | Please refer to [bakjs/hapi-plugins](https://github.com/bakjs/hapi-plugins) for more info and docs. -------------------------------------------------------------------------------- /packages/audit/lib/audit.js: -------------------------------------------------------------------------------- 1 | const { Model, Schema } = require('@bakjs/mongo') 2 | 3 | class Audit extends Model { 4 | static get $options () { 5 | return { 6 | strict: false, 7 | timestamps: false // We don't need updated_at 8 | } 9 | } 10 | 11 | static get $schema () { 12 | return { 13 | // Request User 14 | user: { type: Schema.Types.ObjectId, ref: 'User', index: true }, 15 | 16 | // Request IP 17 | ip: { type: String }, 18 | 19 | // Resource 20 | resource: { type: Schema.Types.ObjectId, refPath: 'kind', index: true }, 21 | 22 | // Resource kind 23 | kind: { type: String, index: true }, 24 | 25 | // Action const 26 | action: { type: String, index: true }, 27 | 28 | // Log Entry Create Time 29 | created_at: { type: Date, default: Date.now }, 30 | 31 | // Tags 32 | tags: [ String ] 33 | } 34 | } 35 | } 36 | 37 | module.exports = Audit.$model 38 | -------------------------------------------------------------------------------- /packages/audit/lib/index.js: -------------------------------------------------------------------------------- 1 | const Audit = require('./audit') 2 | 3 | exports.register = function (server, config) { 4 | server.decorate('request', 'audit', function audit (action, resource, opts) { 5 | // Clone object 6 | const audit = Object.assign({}, opts || {}) 7 | 8 | // Action 9 | audit.action = action 10 | 11 | // Resource & Kind 12 | if (resource) { 13 | audit.resource = resource._id || resource 14 | 15 | if (!audit.kind && resource.constructor && resource.constructor.modelName) { 16 | audit.kind = resource.constructor.modelName 17 | } 18 | } 19 | 20 | // User 21 | if (!audit.user && this.auth.credentials && this.auth.credentials.user) { 22 | audit.user = this.auth.credentials.user._id || this.auth.credentials.user 23 | } 24 | 25 | // IP 26 | if (!audit.ip) { 27 | audit.ip = this.ip 28 | } 29 | 30 | // Tags 31 | audit.tags = ['audit'].concat(audit.tags || []) 32 | 33 | // Emit log event 34 | this.log(audit.tags, audit) 35 | 36 | // Store in DB 37 | return new Audit(audit).save() 38 | }) 39 | } 40 | 41 | exports.pkg = require('../package.json') 42 | exports.once = true 43 | exports.configKey = 'audit' 44 | 45 | exports.Audit = Audit 46 | -------------------------------------------------------------------------------- /packages/audit/package.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "@bakjs/audit", 3 | "version": "3.0.1", 4 | "repository": "bakjs/bak", 5 | "license": "MIT", 6 | "main": "lib/index.js", 7 | "publishConfig": { 8 | "access": "public" 9 | }, 10 | "contributors": [ 11 | "Pooya Parsa " 12 | ], 13 | "files": [ 14 | "lib" 15 | ], 16 | "peerDependencies": { 17 | "@bakjs/mongo": "*" 18 | } 19 | } 20 | -------------------------------------------------------------------------------- /packages/auth/CHANGELOG.md: -------------------------------------------------------------------------------- 1 | # Change Log 2 | 3 | All notable changes to this project will be documented in this file. 4 | See [Conventional Commits](https://conventionalcommits.org) for commit guidelines. 5 | 6 | ## [4.0.1](https://github.com/bakjs/bak/compare/@bakjs/auth@4.0.0...@bakjs/auth@4.0.1) (2019-05-22) 7 | 8 | **Note:** Version bump only for package @bakjs/auth 9 | 10 | 11 | 12 | 13 | 14 | # [4.0.0](https://github.com/bakjs/bak/compare/@bakjs/auth@3.8.5...@bakjs/auth@4.0.0) (2019-05-22) 15 | 16 | 17 | ### Features 18 | 19 | * migrate to `@hapi/` ([adf4db1](https://github.com/bakjs/bak/commit/adf4db1)) 20 | 21 | 22 | ### BREAKING CHANGES 23 | 24 | * projects should also update using `@hapi/ ` for: hapi, hoek, joi, boom, vision, inert 25 | 26 | 27 | 28 | 29 | 30 | ## [3.8.5](https://github.com/bakjs/bak/compare/@bakjs/auth@3.8.4...@bakjs/auth@3.8.5) (2019-04-16) 31 | 32 | **Note:** Version bump only for package @bakjs/auth 33 | 34 | 35 | 36 | 37 | 38 | ## [3.8.4](https://github.com/bakjs/bak/compare/@bakjs/auth@3.8.3...@bakjs/auth@3.8.4) (2019-03-09) 39 | 40 | **Note:** Version bump only for package @bakjs/auth 41 | 42 | 43 | 44 | 45 | 46 | ## [3.8.3](https://github.com/bakjs/bak/compare/@bakjs/auth@3.8.2...@bakjs/auth@3.8.3) (2019-01-01) 47 | 48 | **Note:** Version bump only for package @bakjs/auth 49 | 50 | 51 | 52 | 53 | 54 | ## [3.8.2](https://github.com/bakjs/bak/compare/@bakjs/auth@3.8.1...@bakjs/auth@3.8.2) (2018-12-03) 55 | 56 | **Note:** Version bump only for package @bakjs/auth 57 | 58 | 59 | 60 | 61 | 62 | ## [3.8.1](https://github.com/bakjs/bak/compare/@bakjs/auth@0.1.7...@bakjs/auth@3.8.1) (2018-11-19) 63 | 64 | **Note:** Version bump only for package @bakjs/auth 65 | 66 | 67 | 68 | 69 | 70 | # [3.8.0](https://github.com/bakjs/plugins/compare/@bakjs/auth@3.7.0...@bakjs/auth@3.8.0) (2018-11-04) 71 | 72 | 73 | ### Bug Fixes 74 | 75 | * **deps:** update all non-major dependencies ([#5](https://github.com/bakjs/plugins/issues/5)) ([fbfa4ad](https://github.com/bakjs/plugins/commit/fbfa4ad)) 76 | 77 | 78 | ### Features 79 | 80 | * update major dependencies ([0d75f39](https://github.com/bakjs/plugins/commit/0d75f39)) 81 | 82 | 83 | 84 | 85 | 86 | 87 | # [3.7.0](https://github.com/bakjs/plugins/compare/@bakjs/auth@3.6.0...@bakjs/auth@3.7.0) (2018-08-26) 88 | 89 | 90 | ### Bug Fixes 91 | 92 | * **auth, AuthController:** pass authOptions to the constructor as second parameter ([#3](https://github.com/bakjs/plugins/issues/3)) ([888b231](https://github.com/bakjs/plugins/commit/888b231)) 93 | 94 | 95 | ### Features 96 | 97 | * **auth:** ability to store token in cookie ([#2](https://github.com/bakjs/plugins/issues/2)) ([5318989](https://github.com/bakjs/plugins/commit/5318989)) 98 | 99 | 100 | 101 | 102 | 103 | # [3.6.0](https://github.com/bakjs/plugins/compare/@bakjs/auth@3.5.0...@bakjs/auth@3.6.0) (2018-08-22) 104 | 105 | 106 | ### Features 107 | 108 | * **auth/controller:** swagger compatibility ([4d18a28](https://github.com/bakjs/plugins/commit/4d18a28)) 109 | 110 | 111 | 112 | 113 | 114 | # [3.5.0](https://github.com/bakjs/plugins/compare/@bakjs/auth@3.4.0...@bakjs/auth@3.5.0) (2018-07-17) 115 | 116 | 117 | ### Features 118 | 119 | * **auth:** keep user access_token and allow customizing provider's id ([283dcf7](https://github.com/bakjs/plugins/commit/283dcf7)) 120 | 121 | 122 | 123 | 124 | 125 | # [3.4.0](https://github.com/bakjs/plugins/compare/@bakjs/auth@3.3.0...@bakjs/auth@3.4.0) (2018-07-16) 126 | 127 | 128 | ### Features 129 | 130 | * **auth:** improve oauth user fetching ([3125b46](https://github.com/bakjs/plugins/commit/3125b46)) 131 | 132 | 133 | 134 | 135 | 136 | # [3.3.0](https://github.com/bakjs/plugins/compare/@bakjs/auth@3.2.0...@bakjs/auth@3.3.0) (2018-07-16) 137 | 138 | 139 | ### Features 140 | 141 | * **auth:** send grant_type and redirect_uri to oauth2 token endpoint ([38f6363](https://github.com/bakjs/plugins/commit/38f6363)) 142 | 143 | 144 | 145 | 146 | 147 | # [3.2.0](https://github.com/bakjs/plugins/compare/@bakjs/auth@3.1.1...@bakjs/auth@3.2.0) (2018-07-16) 148 | 149 | 150 | ### Features 151 | 152 | * **auth:** allow customizing oauth2 routes ([ff6f89f](https://github.com/bakjs/plugins/commit/ff6f89f)) 153 | 154 | 155 | 156 | 157 | 158 | ## [3.1.1](https://github.com/bakjs/plugins/compare/@bakjs/auth@3.1.0...@bakjs/auth@3.1.1) (2018-04-12) 159 | 160 | 161 | 162 | 163 | **Note:** Version bump only for package @bakjs/auth 164 | 165 | 166 | # [3.1.0](https://github.com/bakjs/plugins/compare/@bakjs/auth@3.0.4...@bakjs/auth@3.1.0) (2018-02-08) 167 | 168 | 169 | ### Features 170 | 171 | * export explicit configKey ([add9dfb](https://github.com/bakjs/plugins/commit/add9dfb)) 172 | 173 | 174 | 175 | 176 | 177 | ## [3.0.4](https://github.com/bakjs/plugins/compare/@bakjs/auth@3.0.3...@bakjs/auth@3.0.4) (2018-01-26) 178 | 179 | 180 | 181 | 182 | **Note:** Version bump only for package @bakjs/auth 183 | 184 | 185 | ## [3.0.3](https://github.com/bakjs/plugins/compare/@bakjs/auth@3.0.2...@bakjs/auth@3.0.3) (2017-12-21) 186 | 187 | 188 | 189 | 190 | **Note:** Version bump only for package @bakjs/auth 191 | 192 | 193 | ## [3.0.2](https://github.com/bakjs/plugins/compare/@bakjs/auth@3.0.1...@bakjs/auth@3.0.2) (2017-12-21) 194 | 195 | 196 | ### Bug Fixes 197 | 198 | * **auth:** logout with any method ([5537db0](https://github.com/bakjs/plugins/commit/5537db0)) 199 | 200 | 201 | 202 | 203 | 204 | ## [3.0.1](https://github.com/bakjs/plugins/compare/@bakjs/auth@3.0.0...@bakjs/auth@3.0.1) (2017-12-18) 205 | 206 | 207 | ### Bug Fixes 208 | 209 | * **auth:** initialize controller ([f62701e](https://github.com/bakjs/plugins/commit/f62701e)) 210 | 211 | 212 | 213 | 214 | 215 | # [3.0.0](https://github.com/bakjs/plugins/compare/@bakjs/auth@2.1.3...@bakjs/auth@3.0.0) (2017-12-18) 216 | 217 | 218 | ### Features 219 | 220 | * **auth:** Rewrite for bak 3.x ([30573ba](https://github.com/bakjs/plugins/commit/30573ba)) 221 | 222 | 223 | ### BREAKING CHANGES 224 | 225 | * **auth:** requires bak >= 3 226 | 227 | 228 | 229 | 230 | 231 | ## [2.1.3](https://github.com/bakjs/plugins/compare/@bakjs/auth@2.1.2...@bakjs/auth@2.1.3) (2017-11-29) 232 | 233 | 234 | 235 | 236 | **Note:** Version bump only for package @bakjs/auth 237 | 238 | 239 | ## [2.1.2](https://github.com/bakjs/plugins/compare/@bakjs/auth@2.1.1...@bakjs/auth@2.1.2) (2017-11-09) 240 | 241 | 242 | 243 | 244 | **Note:** Version bump only for package @bakjs/auth 245 | 246 | 247 | ## [2.1.1](https://github.com/bakjs/plugins/compare/@bakjs/auth@2.1.0...@bakjs/auth@2.1.1) (2017-10-24) 248 | 249 | 250 | 251 | 252 | **Note:** Version bump only for package @bakjs/auth 253 | 254 | 255 | # [2.1.0](https://github.com/bakjs/plugins/compare/@bakjs/auth@0.1.8...@bakjs/auth@2.1.0) (2017-10-24) 256 | 257 | 258 | ### Bug Fixes 259 | 260 | * **auth:** invalid require ([5e4ca00](https://github.com/bakjs/plugins/commit/5e4ca00)) 261 | 262 | 263 | ### Features 264 | 265 | * **logging:** lightweight rewrite ([6727e29](https://github.com/bakjs/plugins/commit/6727e29)) 266 | 267 | 268 | 269 | 270 | 271 | ## 0.1.8 (2017-10-07) 272 | 273 | 274 | 275 | 276 | **Note:** Version bump only for package @bakjs/auth 277 | 278 | 279 | ## [0.1.7](https://github.com/bakjs/bak/compare/@bakjs/auth@0.1.6...@bakjs/auth@0.1.7) (2017-09-15) 280 | 281 | 282 | ### Bug Fixes 283 | 284 | * **auth:** missing async keyword ([39f159c](https://github.com/bakjs/bak/commit/39f159c)) 285 | 286 | 287 | 288 | 289 | 290 | ## [0.1.6](https://github.com/bakjs/bak/compare/@bakjs/auth@0.1.5...@bakjs/auth@0.1.6) (2017-09-15) 291 | 292 | 293 | ### Bug Fixes 294 | 295 | * **auth:** await on afterLogin and afterLogout ([1195e82](https://github.com/bakjs/bak/commit/1195e82)) 296 | 297 | 298 | 299 | 300 | 301 | ## [0.1.5](https://github.com/bakjs/bak/compare/@bakjs/auth@0.1.4...@bakjs/auth@0.1.5) (2017-09-06) 302 | 303 | 304 | ### Bug Fixes 305 | 306 | * make [@bakjs](https://github.com/bakjs)/mongo a peer dependency ([f7ec5c4](https://github.com/bakjs/bak/commit/f7ec5c4)) 307 | 308 | 309 | 310 | 311 | 312 | ## [0.1.4](https://github.com/bakjs/bak/compare/@bakjs/auth@0.1.3...@bakjs/auth@0.1.4) (2017-09-06) 313 | 314 | 315 | ### Bug Fixes 316 | 317 | * require bak as peer dependency ([3336177](https://github.com/bakjs/bak/commit/3336177)) 318 | 319 | 320 | 321 | 322 | 323 | ## 0.1.3 (2017-09-05) 324 | 325 | 326 | ### Bug Fixes 327 | 328 | * **controller:** handle empty payload ([8f300c3](https://github.com/bakjs/bak/commit/8f300c3)) 329 | 330 | 331 | 332 | 333 | 334 | ## 0.1.2 (2017-09-04) 335 | 336 | 337 | ### Bug Fixes 338 | 339 | * **controller:** handle empty payload ([8f300c3](https://github.com/bakjs/bak/commit/8f300c3)) 340 | 341 | 342 | 343 | 344 | 345 | ## [0.1.1](https://github.com/bakjs/bak/compare/@bakjs/auth@0.1.0...@bakjs/auth@0.1.1) (2017-09-03) 346 | 347 | 348 | 349 | 350 | **Note:** Version bump only for package @bakjs/auth 351 | 352 | 353 | # 0.1.0 (2017-09-03) 354 | 355 | 356 | 357 | 358 | **Note:** Version bump only for package @bakjs/auth 359 | -------------------------------------------------------------------------------- /packages/auth/README.md: -------------------------------------------------------------------------------- 1 | Please refer to [bakjs/hapi-plugins](https://github.com/bakjs/hapi-plugins) for more info and docs. -------------------------------------------------------------------------------- /packages/auth/lib/controller.js: -------------------------------------------------------------------------------- 1 | const { Controller } = require('bak') 2 | const Joi = require('@hapi/joi') 3 | 4 | class $AuthController extends Controller { 5 | constructor (authProvider, authOptions) { 6 | super() 7 | this.authProvider = authProvider 8 | this.authOptions = authOptions 9 | } 10 | 11 | init () { 12 | this.prefix = '/api' 13 | this.defaults.auth = false 14 | this.defaults.tags = ['api', 'Authentication'] 15 | 16 | this.get('/auth/user', this.user, { 17 | description: 'Get the currently authenticated user', 18 | auth: { 19 | mode: 'required' 20 | } 21 | }) 22 | 23 | if (this.authProvider.loginSupported) { 24 | this.post('/auth/login', this.login, { 25 | description: 'Login user with the provided credentials', 26 | validate: { 27 | payload: { 28 | username: Joi.string().description('Username or Email').example('test@example.com'), 29 | password: Joi.string().description('Password').example('123456') 30 | } 31 | } 32 | }) 33 | 34 | this.post('/auth/logout', this.logout, { 35 | description: 'Logout the currently authenticated user', 36 | auth: { 37 | mode: 'required' 38 | } 39 | }) 40 | } 41 | 42 | if (this.authProvider.oauthSupported) { 43 | this.get('/oauth/{clientID}/login', this.oauthLogin) 44 | this.post('/oauth/{clientID}/authorize', this.oauthAuthorize) 45 | } 46 | } 47 | 48 | async login (request, h) { 49 | let { username, password } = request.payload || {} 50 | let { token } = await this.authProvider.login({ username, password, request }) 51 | 52 | if (this.authOptions.state) { 53 | h.state(this.authOptions.accessTokenName || 'token', token, Object.assign({ 54 | isSecure: false, 55 | ttl: null, 56 | path: '/' 57 | }, this.authOptions.state)) 58 | } 59 | 60 | return h.response({ token }) 61 | } 62 | 63 | async logout (request, h) { 64 | let { session, user } = request 65 | await this.authProvider.logout({ user, session }) 66 | return 'LOGGED_OUT' 67 | } 68 | 69 | async user (request, h) { 70 | return { user: request.user } 71 | } 72 | 73 | async oauthLogin (request, h) { 74 | let redirect_uri = await this.authProvider.oauthLogin(request.params.clientID) 75 | return { redirect_uri } 76 | } 77 | 78 | async oauthAuthorize (request, h) { 79 | let { token, user } = await this.authProvider.oauthAuthorize(request.params.clientID, request) 80 | return { token, user } 81 | } 82 | } 83 | 84 | module.exports = $AuthController 85 | -------------------------------------------------------------------------------- /packages/auth/lib/index.js: -------------------------------------------------------------------------------- 1 | const AuthController = require('./controller') 2 | const TokenScheme = require('./token-scheme') 3 | const User = require('./provider/user') 4 | 5 | exports.register = async (server, authOptions) => { 6 | // Create Auth provider instance 7 | const Provider = authOptions.provider || require('./provider/default') 8 | let authProvider = new Provider(authOptions) 9 | 10 | // Expose provider 11 | server.expose('auth', authProvider) 12 | 13 | // Register token scheme 14 | server.auth.scheme('token', TokenScheme) 15 | 16 | // Register token strategy as default 17 | server.auth.strategy('default', 'token', { authOptions, authProvider }) 18 | 19 | // Set default as the default strategy 20 | server.auth.default('default') 21 | 22 | // Register Auth Controller 23 | const authController = new AuthController(authProvider, authOptions) 24 | await authController.init() 25 | server.route(await authController.routes()) 26 | } 27 | 28 | exports.pkg = require('../package.json') 29 | exports.once = true 30 | exports.configKey = 'auth' 31 | 32 | exports.User = User 33 | -------------------------------------------------------------------------------- /packages/auth/lib/provider/base.js: -------------------------------------------------------------------------------- 1 | const Boom = require('@hapi/boom') 2 | 3 | class AuthBaseProvider { 4 | constructor (options = {}) { 5 | this.options = options 6 | } 7 | 8 | // ============================== 9 | // Authentication methods 10 | // ============================== 11 | authToken (token) { 12 | throw Boom.notImplemented('authToken not implemented') 13 | } 14 | 15 | // ============================== 16 | // Create Token & Session 17 | // ============================== 18 | getToken (user, request, client) { 19 | throw Boom.notImplemented('getToken not implemented') 20 | } 21 | 22 | // ============================== 23 | // Methods to work with user 24 | // ============================== 25 | findByUsername (username) { 26 | throw Boom.notImplemented('findByUsername not implemented') 27 | } 28 | 29 | validateUser (user) { 30 | // Check user is not null! 31 | if (!user) { 32 | throw Boom.unauthorized('USER_NOT_FOUND') 33 | } 34 | 35 | // Check if user disabled 36 | if (user.is_disabled) { 37 | throw Boom.unauthorized('USER_DISABLED') 38 | } 39 | } 40 | 41 | async validatePassword (user, password) { 42 | throw Boom.notImplemented('validatePassword not implemented') 43 | } 44 | 45 | // ============================== 46 | // Login/Logout 47 | // ============================== 48 | get loginSupported () { 49 | return false 50 | } 51 | 52 | login ({ username, password, request }) { 53 | throw Boom.notImplemented('login not implemented') 54 | } 55 | 56 | logout ({ user, session, request }) { 57 | throw Boom.notImplemented('logout not implemented') 58 | } 59 | 60 | // ============================== 61 | // Oauth Client 62 | // ============================== 63 | get oauthSupported () { 64 | return false 65 | } 66 | 67 | oauthLogin (client_id) { 68 | throw Boom.notImplemented('oauthLogin not implemented') 69 | } 70 | 71 | oauthAuthorize (client_id, request) { 72 | throw Boom.notImplemented('oauthAuthorize not implemented') 73 | } 74 | } 75 | 76 | module.exports = AuthBaseProvider 77 | -------------------------------------------------------------------------------- /packages/auth/lib/provider/default.js: -------------------------------------------------------------------------------- 1 | const Boom = require('@hapi/boom') 2 | const Axios = require('axios') 3 | const _ = require('lodash') 4 | const AuthBaseProvider = require('./base') 5 | const { jwt_verify, jwt_decode, hash_verify, jwt_sign, uid } = require('./utils') 6 | 7 | class AuthDefaultProvider extends AuthBaseProvider { 8 | constructor (options = {}) { 9 | super(options) 10 | 11 | // User Model 12 | if (!this.options.user_model) { 13 | this.options.user_model = require('./user').$model 14 | } 15 | } 16 | 17 | // ============================== 18 | // Authentication methods 19 | // ============================== 20 | async authToken (token) { 21 | let token_decoded = jwt_decode(token) 22 | if (!token_decoded) throw new Error('INVALID_TOKEN') 23 | 24 | let user_id = token_decoded.u 25 | let session_id = token_decoded.s 26 | 27 | // Find user 28 | let user = await this.options.user_model.findById(user_id) 29 | if (!user) { 30 | throw new Error('USER_NOT_FOUND') 31 | } 32 | 33 | // Check if user disabled 34 | if (user.is_disabled) { 35 | throw new Error('USER_DISABLED') 36 | } 37 | 38 | // Find session 39 | let session = user.sessions.id(session_id) 40 | if (!session) { 41 | throw new Error('SESSION_EXPIRED') 42 | } 43 | 44 | // Validate token 45 | let validated = await this.validateToken(token, session, user) 46 | 47 | if (!validated) { 48 | throw new Error('INVALID_TOKEN') 49 | } 50 | 51 | return { 52 | credentials: { user, scope: user.scope }, 53 | artifacts: session 54 | } 55 | } 56 | 57 | validateToken (token) { 58 | return jwt_verify(token, this.options.secret) 59 | } 60 | 61 | // ============================== 62 | // Create Token & Session 63 | // ============================== 64 | async getToken (user, request = { headers: [], info: [] }, client) { 65 | // Create new session 66 | let session = user.sessions.create({ 67 | agent: request.headers['user-agent'], 68 | ip: realIP(request) 69 | }) 70 | 71 | // Revoke any older sessions of user with same user-agent and ip 72 | if (this.options.auto_logout === true) { 73 | user.sessions = user.sessions.filter(s => (s.agent !== session.agent || s.ip !== session.ip)) 74 | } 75 | 76 | // Apply max sessions 77 | let { max_sessions } = this.options 78 | if (max_sessions !== false) { 79 | max_sessions = max_sessions || 3 80 | user.sessions = _.sortBy(user.sessions, ['created_at', '_id']).reverse().slice(0, max_sessions - 1) 81 | } 82 | 83 | // Add new session 84 | user.sessions.unshift(session) 85 | await user.save() 86 | 87 | // Notify user model 88 | await user.afterLogin({ request, session }) 89 | 90 | // Sign session token 91 | let secret = client ? client.secret : this.options.secret 92 | let token = await jwt_sign({ u: user._id, s: session._id }, secret) 93 | return { session, token } 94 | } 95 | 96 | // ============================== 97 | // Methods to work with user 98 | // ============================== 99 | findByUsername (username) { 100 | let username_fields = this.options.username_fields || ['username', 'email'] 101 | 102 | return this.options.user_model.findOne({ 103 | $or: username_fields.map((field) => { 104 | let obj = {} 105 | obj[field] = username 106 | return obj 107 | }) 108 | }) 109 | } 110 | 111 | validateUser (user) { 112 | // Check user is not null! 113 | if (!user) { 114 | throw Boom.unauthorized('USER_NOT_FOUND') 115 | } 116 | 117 | // Check if user disabled 118 | if (user.is_disabled) { 119 | throw Boom.unauthorized('USER_DISABLED') 120 | } 121 | } 122 | 123 | async validatePassword (user, password) { 124 | // Check password 125 | let verified = await hash_verify(password, user.get('password')) 126 | if (verified !== true) { 127 | throw Boom.unauthorized('INVALID_PASSWORD') 128 | } 129 | } 130 | 131 | // ============================== 132 | // Login/Logout 133 | // ============================== 134 | get loginSupported () { 135 | return true 136 | } 137 | 138 | async login ({ username, password, request }) { 139 | // Find user 140 | let user = await this.findByUsername(username) 141 | 142 | // Validate user 143 | await this.validateUser(user) 144 | 145 | // Ensure password is not empty 146 | if (!password || !password.length || typeof password !== 'string') { 147 | throw Boom.unauthorized('INVALID_PASSWORD') 148 | } 149 | 150 | // Validate password 151 | await this.validatePassword(user, password) 152 | 153 | // Issue token 154 | const { token } = await this.getToken(user, request) 155 | 156 | return { token, user } 157 | } 158 | 159 | async logout ({ user, session, request }) { 160 | if (session) user.sessions.remove(session) 161 | else user.sessions = [] 162 | 163 | // Notify user model 164 | await user.afterLogout({ session, request }) 165 | 166 | return user.save() 167 | } 168 | 169 | // ============================== 170 | // Oauth Client 171 | // ============================== 172 | get oauthSupported () { 173 | return this.options.oauth 174 | } 175 | 176 | async oauthLogin (client_id) { 177 | // Find client 178 | let client = this.options.oauth[client_id] 179 | if (!client) { 180 | throw Boom.notFound() 181 | } 182 | 183 | // Scopes 184 | let scope = client.scope || ['oauth'] 185 | if (Array.isArray(scope)) { 186 | scope = scope.join(',') 187 | } 188 | 189 | // State 190 | // An unguessable random string. It is used to protect against CSRF attacks. 191 | let state = await uid(6) 192 | 193 | // Generate url 194 | return `${client.url}/${client.authorize || 'authorize'}?` + 195 | `state=${encodeURIComponent(state)}` + 196 | `&client_id=${encodeURIComponent(client.client_id)}` + 197 | `&scope=${encodeURIComponent(scope)}` + 198 | `&redirect_uri=${encodeURIComponent(client.redirect_uri)}` 199 | } 200 | 201 | async oauthAuthorize (client_id, request) { 202 | // Request params 203 | let { code, state } = request.payload || request.query 204 | 205 | // Find client 206 | let client = this.options.oauth[client_id] 207 | if (!client) { 208 | throw Boom.notFound() 209 | } 210 | 211 | // Request for access_token 212 | let url = `${client.url_internal || client.url}/${client.token || 'access_token'}` 213 | 214 | let data = { 215 | code: code, 216 | state: state, 217 | grant_type: client.grant_type || 'authorization_code', 218 | client_id: client.client_id, 219 | client_secret: client.client_secret, 220 | redirect_uri: client.redirect_uri 221 | } 222 | 223 | let access_token 224 | 225 | try { 226 | let res = (await Axios.post(url, data)).data || {} 227 | access_token = res.access_token 228 | } catch (e) { 229 | throw Boom.unauthorized((e.response && e.response.data) ? e.response.data.message : '') 230 | } 231 | 232 | if (!access_token || access_token === '') { 233 | throw Boom.unauthorized('OAUTH_NO_TOKEN') 234 | } 235 | 236 | // Get user profile 237 | let user 238 | 239 | try { 240 | const { data } = await Axios.get(`${client.url_internal || client.url}/${client.user || 'user'}`, { 241 | headers: { 242 | Authorization: `Bearer ${access_token}` 243 | } 244 | }) 245 | user = data.user || data 246 | } catch (e) { 247 | throw Boom.unauthorized('OAUTH_USER') 248 | } 249 | 250 | if (!user || !user.email) { 251 | throw Boom.unauthorized('OAUTH_INVALID_USER') 252 | } 253 | 254 | // Keep access_token 255 | user.access_token = access_token 256 | 257 | // Find or update local user record 258 | const idKey = [client.id || '_id'] 259 | let local_user = await this.options.user_model.findOne({ [idKey]: user[idKey] }) /* eslont-disable-line new-cap */ 260 | 261 | if (!local_user) { 262 | // Create local user if not found 263 | local_user = new this.options.user_model(user) // eslint-disable-line new-cap 264 | } else { 265 | // Update exiting user 266 | Object.keys(user).forEach(key => { 267 | local_user.set(key, user[key]) 268 | }) 269 | } 270 | 271 | // Save changes 272 | await local_user.save() 273 | 274 | // Issue new token 275 | let { token } = await this.getToken(local_user, request) 276 | 277 | return { token, user } 278 | } 279 | } 280 | 281 | module.exports = AuthDefaultProvider 282 | 283 | function realIP (request) { 284 | return request.ip || request.headers['x-real-ip'] || request.headers['x-forwarded-for'] || request.info['remoteAddress'] 285 | } 286 | -------------------------------------------------------------------------------- /packages/auth/lib/provider/user.js: -------------------------------------------------------------------------------- 1 | const { Model, Schema } = require('@bakjs/mongo') 2 | 3 | class User extends Model { 4 | static get $options () { 5 | return { strict: false } 6 | } 7 | 8 | static get $visible () { 9 | return ['_id', 'name', 'username', 'email', 'avatar', 'scope'] 10 | } 11 | 12 | static get $schema () { 13 | return { 14 | username: { type: String, index: true, sparse: true, unique: true }, 15 | email: { type: String, index: true, sparse: true, unique: true }, 16 | password: { type: String }, 17 | avatar_etag: { type: String }, 18 | name: { type: String }, 19 | is_banned: { type: Boolean }, 20 | meta: { type: Object }, 21 | scope: { type: Array }, 22 | sessions: [ 23 | { 24 | _agent: { type: String }, 25 | _ip: { type: String }, 26 | _client: { type: Schema.Types.ObjectId }, 27 | created_at: { type: Date, default: Date.now } 28 | } 29 | ] 30 | } 31 | } 32 | 33 | get is_disabled () { 34 | return this.get('enabled') === false || this.get('blocked') === true 35 | } 36 | 37 | afterLogin ({ request, session }) { 38 | 39 | } 40 | 41 | afterLogout ({ request, session }) { 42 | 43 | } 44 | } 45 | 46 | module.exports = User 47 | -------------------------------------------------------------------------------- /packages/auth/lib/provider/utils.js: -------------------------------------------------------------------------------- 1 | const Bcrypt = require('bcryptjs') 2 | const JWT = require('jsonwebtoken') 3 | const Crypto = require('crypto') 4 | 5 | // ------------------------- 6 | // bcrypt 7 | // ------------------------- 8 | 9 | /** 10 | * 11 | * @param data 12 | * @returns {Promise} 13 | */ 14 | function bcrypt_hash (data) { 15 | return new Promise((resolve, reject) => { 16 | Bcrypt.genSalt(10, function (err, salt) { 17 | if (err) return reject(err) 18 | Bcrypt.hash(data, salt, function (err, hash) { 19 | if (err) return reject(err) 20 | resolve(hash) 21 | }) 22 | }) 23 | }) 24 | } 25 | 26 | /** 27 | * 28 | * @param data 29 | * @param hash 30 | * @returns {Promise} 31 | */ 32 | function bcrypt_compare (data, hash) { 33 | return new Promise((resolve, reject) => { 34 | Bcrypt.compare(data, hash, (err, isValid) => { 35 | if (err) return reject(err) 36 | return resolve(isValid) 37 | }) 38 | }) 39 | } 40 | 41 | // ------------------------- 42 | // jwt 43 | // ------------------------- 44 | 45 | /** 46 | * 47 | * @param message 48 | * @param key 49 | * @returns {*} 50 | */ 51 | function jwt_sign (message, key) { 52 | return JWT.sign(message, key) 53 | } 54 | 55 | /** 56 | * 57 | * @param message 58 | * @returns {*} 59 | */ 60 | function jwt_decode (message) { 61 | return JWT.decode(message) 62 | } 63 | 64 | /** 65 | * 66 | * @param message 67 | * @param key 68 | * @returns {Promise} 69 | */ 70 | function jwt_verify (message, key) { 71 | return new Promise((resolve, reject) => { 72 | JWT.verify(message, key, (err, data) => { 73 | if (err) return resolve(false) 74 | resolve(data) 75 | }) 76 | }) 77 | } 78 | 79 | // ------------------------- 80 | // General hashing 81 | // ------------------------- 82 | 83 | /** 84 | * 85 | * @param password 86 | * @param _hash in this format: password_hash[|hash_method] 87 | * @returns {Promise} 88 | */ 89 | function hash_verify (password, _hash) { 90 | let hash = _hash.split('|') 91 | switch (hash[1]) { 92 | case 'bcrypt': 93 | return bcrypt_compare(password, hash[0]) 94 | default: 95 | return Promise.resolve(password === hash[0]) 96 | } 97 | } 98 | 99 | // ------------------------- 100 | // Unique ID 101 | // ------------------------- 102 | 103 | function uid (length) { 104 | return new Promise((resolve, reject) => { 105 | Crypto.randomBytes(length * 2, (err, bytes) => { 106 | if (err) return reject(err) 107 | resolve(bytes.toString('base64').replace(/\+/g, '').replace(/\//g, '').replace(/=/g, '').substring(0, length)) 108 | }) 109 | }) 110 | } 111 | 112 | // ------------------------- 113 | // *Exports 114 | // ------------------------- 115 | module.exports = { 116 | bcrypt_hash, 117 | bcrypt_compare, 118 | jwt_sign, 119 | jwt_verify, 120 | jwt_decode, 121 | hash_verify, 122 | uid 123 | } 124 | -------------------------------------------------------------------------------- /packages/auth/lib/token-scheme.js: -------------------------------------------------------------------------------- 1 | const Boom = require('@hapi/boom') 2 | const Hoek = require('@hapi/hoek') 3 | const Joi = require('@hapi/joi') 4 | 5 | const tokenScheme = function (server, { authOptions, authProvider }) { 6 | Hoek.assert(authOptions, 'Missing authOptions') 7 | Hoek.assert(authProvider, 'Missing authProvider') 8 | 9 | const options = Hoek.applyToDefaults(defaults, authOptions) 10 | Joi.assert(options, optionsSchema) 11 | 12 | // https://github.com/hapijs/hapi/blob/master/API.md#authentication-scheme 13 | return { 14 | async authenticate (request, h) { 15 | // Use headers by default 16 | let authorization = request.raw.req.headers.authorization 17 | 18 | // Fallback 1 : Check for cookies 19 | if (options.allowCookieToken && 20 | !authorization && 21 | request.state[options.accessTokenName]) { 22 | authorization = options.tokenType + ' ' + request.state[options.accessTokenName] 23 | } 24 | 25 | // Fallback 2 : URL Query 26 | if (options.allowQueryToken && !authorization && request.query[options.accessTokenName]) { 27 | authorization = options.tokenType + ' ' + request.query[options.accessTokenName] 28 | delete request.query[options.accessTokenName] 29 | } 30 | 31 | // Fallback 3 : Throw Error 32 | if (!authorization) { 33 | throw Boom.unauthorized(null, options.tokenType) 34 | } 35 | 36 | // Try to parse headers 37 | const parts = authorization.split(/\s+/) 38 | 39 | // Ensure correct token type 40 | if (parts[0].toLowerCase() !== options.tokenType.toLowerCase()) { 41 | throw Boom.unauthorized(null, options.tokenType) 42 | } 43 | 44 | // Try to login 45 | const token = parts[1] 46 | const { credentials, artifacts } = await authProvider.authToken(token) 47 | 48 | // OK 49 | return h.authenticated({ credentials, artifacts }) 50 | } 51 | } 52 | } 53 | 54 | module.exports = tokenScheme 55 | 56 | const defaults = { 57 | accessTokenName: 'token', 58 | allowQueryToken: true, 59 | allowCookieToken: true, 60 | tokenType: 'Bearer' 61 | } 62 | 63 | const optionsSchema = Joi.object().keys({ 64 | accessTokenName: Joi.string().required(), 65 | allowQueryToken: Joi.boolean(), 66 | allowCookieToken: Joi.boolean(), 67 | tokenType: Joi.string().required() 68 | }).unknown(true) 69 | -------------------------------------------------------------------------------- /packages/auth/package.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "@bakjs/auth", 3 | "version": "4.0.1", 4 | "repository": "bakjs/bak", 5 | "license": "MIT", 6 | "main": "lib/index.js", 7 | "publishConfig": { 8 | "access": "public" 9 | }, 10 | "contributors": [ 11 | "Pooya Parsa " 12 | ], 13 | "files": [ 14 | "lib" 15 | ], 16 | "peerDependencies": { 17 | "@bakjs/mongo": "*", 18 | "bak": "*" 19 | }, 20 | "dependencies": { 21 | "@hapi/boom": "^7.4.2", 22 | "@hapi/hoek": "^6.2.3", 23 | "@hapi/joi": "^15.0.3", 24 | "axios": "^0.18.0", 25 | "bcryptjs": "^2.4.3", 26 | "jsonwebtoken": "^8.5.1", 27 | "lodash": "^4.17.11" 28 | } 29 | } 30 | -------------------------------------------------------------------------------- /packages/bak/CHANGELOG.md: -------------------------------------------------------------------------------- 1 | # Change Log 2 | 3 | All notable changes to this project will be documented in this file. 4 | See [Conventional Commits](https://conventionalcommits.org) for commit guidelines. 5 | 6 | ## [5.0.1](https://github.com/bakjs/bak/compare/bak@5.0.0...bak@5.0.1) (2019-05-22) 7 | 8 | **Note:** Version bump only for package bak 9 | 10 | 11 | 12 | 13 | 14 | # [5.0.0](https://github.com/bakjs/bak/compare/bak@4.7.6...bak@5.0.0) (2019-05-22) 15 | 16 | 17 | ### Bug Fixes 18 | 19 | * use exact [@bakjs](https://github.com/bakjs) dependencies ([a21ee23](https://github.com/bakjs/bak/commit/a21ee23)) 20 | 21 | 22 | ### Features 23 | 24 | * migrate to `@hapi/` ([adf4db1](https://github.com/bakjs/bak/commit/adf4db1)) 25 | 26 | 27 | ### BREAKING CHANGES 28 | 29 | * projects should also update using `@hapi/ ` for: hapi, hoek, joi, boom, vision, inert 30 | 31 | 32 | 33 | 34 | 35 | ## [4.7.6](https://github.com/bakjs/bak/compare/bak@4.7.5...bak@4.7.6) (2019-04-16) 36 | 37 | 38 | ### Bug Fixes 39 | 40 | * dedefault config file ([cc605f2](https://github.com/bakjs/bak/commit/cc605f2)) 41 | 42 | 43 | 44 | 45 | 46 | ## [4.7.5](https://github.com/bakjs/bak/compare/bak@4.7.4...bak@4.7.5) (2019-04-16) 47 | 48 | 49 | ### Bug Fixes 50 | 51 | * pin esm to 3.2.20 ([aa407b9](https://github.com/bakjs/bak/commit/aa407b9)) 52 | 53 | 54 | 55 | 56 | 57 | ## [4.7.4](https://github.com/bakjs/bak/compare/bak@4.7.3...bak@4.7.4) (2019-04-16) 58 | 59 | 60 | ### Bug Fixes 61 | 62 | * **cli:** use cjs dedefault ([829294e](https://github.com/bakjs/bak/commit/829294e)) 63 | 64 | 65 | 66 | 67 | 68 | ## [4.7.3](https://github.com/bakjs/bak/compare/bak@4.7.2...bak@4.7.3) (2019-03-09) 69 | 70 | 71 | ### Bug Fixes 72 | 73 | * **cli:** workaround for esm[#740](https://github.com/bakjs/bak/issues/740) ([54e6622](https://github.com/bakjs/bak/commit/54e6622)) 74 | 75 | 76 | 77 | 78 | 79 | ## [4.7.2](https://github.com/bakjs/bak/compare/bak@4.7.1...bak@4.7.2) (2019-03-09) 80 | 81 | **Note:** Version bump only for package bak 82 | 83 | 84 | 85 | 86 | 87 | ## [4.7.1](https://github.com/bakjs/bak/compare/bak@4.7.0...bak@4.7.1) (2018-12-16) 88 | 89 | **Note:** Version bump only for package bak 90 | 91 | 92 | 93 | 94 | 95 | # [4.7.0](https://github.com/bakjs/bak/compare/bak@4.6.2...bak@4.7.0) (2018-12-05) 96 | 97 | 98 | ### Features 99 | 100 | * listerner.keepAliveTimeout (fixes [#28](https://github.com/bakjs/bak/issues/28)) ([70e2042](https://github.com/bakjs/bak/commit/70e2042)) 101 | 102 | 103 | 104 | 105 | 106 | ## [4.6.2](https://github.com/bakjs/bak/compare/bak@4.6.1...bak@4.6.2) (2018-12-03) 107 | 108 | **Note:** Version bump only for package bak 109 | 110 | 111 | 112 | 113 | 114 | ## [4.6.1](https://github.com/bakjs/bak/compare/bak@4.6.0...bak@4.6.1) (2018-11-19) 115 | 116 | **Note:** Version bump only for package bak 117 | 118 | 119 | 120 | 121 | 122 | # [4.6.0](https://github.com/bakjs/bak/compare/bak@1.1.4...bak@4.6.0) (2018-11-19) 123 | 124 | 125 | ### Features 126 | 127 | * improve DX ([3efae66](https://github.com/bakjs/bak/commit/3efae66)) 128 | * improve logging plugin ([94d64f2](https://github.com/bakjs/bak/commit/94d64f2)) 129 | * log and send validation errors by default ([6ad26a1](https://github.com/bakjs/bak/commit/6ad26a1)) 130 | 131 | 132 | 133 | 134 | 135 | # Change Log 136 | 137 | All notable changes to this project will be documented in this file. See [standard-version](https://github.com/conventional-changelog/standard-version) for commit guidelines. 138 | 139 | 140 | # [4.5.0](https://github.com/bakjs/bak/compare/v4.4.0...v4.5.0) (2018-11-05) 141 | 142 | 143 | ### Features 144 | 145 | * use consola.wrapConsle ([73c0964](https://github.com/bakjs/bak/commit/73c0964)) 146 | 147 | 148 | 149 | 150 | # [4.4.0](https://github.com/bakjs/bak/compare/v4.3.0...v4.4.0) (2018-11-05) 151 | 152 | 153 | 154 | 155 | # [4.3.0](https://github.com/bakjs/bak/compare/v4.2.1...v4.3.0) (2018-09-15) 156 | 157 | 158 | ### Bug Fixes 159 | 160 | * **cli/dev:** listen on nodemon events after stating ([27642ed](https://github.com/bakjs/bak/commit/27642ed)) 161 | 162 | 163 | ### Features 164 | 165 | * **cli/dev:** restart server using r ([5fe6c29](https://github.com/bakjs/bak/commit/5fe6c29)) 166 | 167 | 168 | 169 | 170 | ## [4.2.1](https://github.com/bakjs/bak/compare/v4.2.0...v4.2.1) (2018-09-14) 171 | 172 | 173 | ### Bug Fixes 174 | 175 | * **controller:** inert directory support ([67c4d73](https://github.com/bakjs/bak/commit/67c4d73)) 176 | 177 | 178 | 179 | 180 | # [4.2.0](https://github.com/bakjs/bak/compare/v4.1.2...v4.2.0) (2018-08-26) 181 | 182 | 183 | ### Bug Fixes 184 | 185 | * **controller:** remove validate.payload for GET and HEAD requests ([04f35a9](https://github.com/bakjs/bak/commit/04f35a9)) 186 | 187 | 188 | ### Features 189 | 190 | * **cli:** support extra args ([2c8398e](https://github.com/bakjs/bak/commit/2c8398e)) 191 | 192 | 193 | 194 | 195 | ## [4.1.2](https://github.com/bakjs/bak/compare/v4.1.1...v4.1.2) (2018-07-03) 196 | 197 | 198 | ### Bug Fixes 199 | 200 | * **server.js:** check for nested plugin props ([#14](https://github.com/bakjs/bak/issues/14)) ([ce3a3ca](https://github.com/bakjs/bak/commit/ce3a3ca)) 201 | 202 | 203 | 204 | 205 | ## [4.1.1](https://github.com/bakjs/bak/compare/v4.1.0...v4.1.1) (2018-05-31) 206 | 207 | 208 | ### Bug Fixes 209 | 210 | * **server:** support array as options for plugin registration ([056edb8](https://github.com/bakjs/bak/commit/056edb8)) 211 | 212 | 213 | 214 | 215 | # [4.1.0](https://github.com/bakjs/bak/compare/v4.0.2...v4.1.0) (2018-05-27) 216 | 217 | 218 | ### Bug Fixes 219 | 220 | * default config value to 'bak.config.js' ([30d0388](https://github.com/bakjs/bak/commit/30d0388)) 221 | * don't replace 0.0.0.0 with hostname ([2addad5](https://github.com/bakjs/bak/commit/2addad5)) 222 | * **bak:** disable relative resolve by default ([aa2fd4e](https://github.com/bakjs/bak/commit/aa2fd4e)) 223 | * use simpler type checkings ([260d756](https://github.com/bakjs/bak/commit/260d756)) 224 | 225 | 226 | ### Features 227 | 228 | * **cli:** development mode support with `bak dev` ([d90c729](https://github.com/bakjs/bak/commit/d90c729)) 229 | * fully support esm for route, controllers and plugins ([3cd74d7](https://github.com/bakjs/bak/commit/3cd74d7)) 230 | * use [@bakjs](https://github.com/bakjs)/dev-errors ([0869214](https://github.com/bakjs/bak/commit/0869214)) 231 | * use consola ([eccbdcd](https://github.com/bakjs/bak/commit/eccbdcd)) 232 | * use std-env to detect dev mode by default ([d260238](https://github.com/bakjs/bak/commit/d260238)) 233 | * **cli:** support esm in bak.config.js ([023f9c6](https://github.com/bakjs/bak/commit/023f9c6)) 234 | * **cli:** use rootdir/bak.config.js as default value ([a3d8986](https://github.com/bakjs/bak/commit/a3d8986)) 235 | * **server:** support esm export default for plugins and routes ([345bbc0](https://github.com/bakjs/bak/commit/345bbc0)) 236 | 237 | 238 | 239 | 240 | ## [4.0.2](https://github.com/bakjs/bak/compare/v4.0.1...v4.0.2) (2018-04-12) 241 | 242 | 243 | 244 | 245 | ## [4.0.1](https://github.com/bakjs/bak/compare/v4.0.0...v4.0.1) (2018-02-08) 246 | 247 | 248 | 249 | 250 | # [4.0.0](https://github.com/bakjs/bak/compare/v3.1.0...v4.0.0) (2018-02-08) 251 | 252 | 253 | ### Features 254 | 255 | * devErrors ([73b2ea7](https://github.com/bakjs/bak/commit/73b2ea7)) 256 | 257 | 258 | ### misc 259 | 260 | * **server:** remove implicit config option handling ([8ea4a5a](https://github.com/bakjs/bak/commit/8ea4a5a)) 261 | 262 | 263 | ### BREAKING CHANGES 264 | 265 | * **server:** plugin options should be explicitly defined in bak.config.js 266 | 267 | 268 | 269 | 270 | # [3.1.0](https://github.com/bakjs/bak/compare/v3.0.7...v3.1.0) (2018-02-08) 271 | 272 | 273 | ### Features 274 | 275 | * upgrade [@bakjs](https://github.com/bakjs)/logging ([ef23430](https://github.com/bakjs/bak/commit/ef23430)) 276 | 277 | 278 | 279 | 280 | ## [3.0.7](https://github.com/bakjs/bak/compare/v3.0.6...v3.0.7) (2018-01-28) 281 | 282 | 283 | 284 | 285 | ## [3.0.6](https://github.com/bakjs/bak/compare/v3.0.5...v3.0.6) (2018-01-26) 286 | 287 | 288 | 289 | 290 | ## [3.0.5](https://github.com/bakjs/bak/compare/v3.0.4...v3.0.5) (2017-12-19) 291 | 292 | 293 | 294 | 295 | ## [3.0.4](https://github.com/bakjs/bak/compare/v3.0.3...v3.0.4) (2017-12-18) 296 | 297 | 298 | ### Bug Fixes 299 | 300 | * **controller:** use defaultsDeep for route.config ([80d0cde](https://github.com/bakjs/bak/commit/80d0cde)) 301 | 302 | 303 | 304 | 305 | ## [3.0.3](https://github.com/bakjs/bak/compare/v3.0.2...v3.0.3) (2017-12-18) 306 | 307 | 308 | ### Bug Fixes 309 | 310 | * **server:** await on routes ([e58d3e3](https://github.com/bakjs/bak/commit/e58d3e3)) 311 | 312 | 313 | 314 | 315 | ## [3.0.2](https://github.com/bakjs/bak/compare/v3.0.1...v3.0.2) (2017-12-18) 316 | 317 | 318 | ### Bug Fixes 319 | 320 | * **controller:** minor fixes ([f9a2566](https://github.com/bakjs/bak/commit/f9a2566)) 321 | 322 | 323 | 324 | 325 | ## [3.0.1](https://github.com/bakjs/bak/compare/v3.0.0...v3.0.1) (2017-12-18) 326 | 327 | 328 | ### Bug Fixes 329 | 330 | * **controller:** call to init() after contructor() ([5ad8c72](https://github.com/bakjs/bak/commit/5ad8c72)) 331 | 332 | 333 | ### Performance Improvements 334 | 335 | * **controller:** routes are no longer async ([7b8bc11](https://github.com/bakjs/bak/commit/7b8bc11)) 336 | 337 | 338 | 339 | 340 | # [3.0.0](https://github.com/bakjs/bak/compare/v2.0.3...v3.0.0) (2017-12-18) 341 | 342 | 343 | ### Features 344 | 345 | * new Controller API ([0c4a663](https://github.com/bakjs/bak/commit/0c4a663)) 346 | 347 | 348 | ### BREAKING CHANGES 349 | 350 | * every project relies on Current auto routing should now defind routes in init() method. See readme and examples. 351 | 352 | 353 | 354 | 355 | ## [2.0.3](https://github.com/bakjs/bak/compare/v2.0.2...v2.0.3) (2017-11-29) 356 | 357 | 358 | ### Bug Fixes 359 | 360 | * **controller:** support using hapi syntax for route paths ([835fec7](https://github.com/bakjs/bak/commit/835fec7)) 361 | 362 | 363 | 364 | 365 | ## [2.0.2](https://github.com/bakjs/bak/compare/v2.0.1...v2.0.2) (2017-11-29) 366 | 367 | 368 | 369 | 370 | ## [2.0.1](https://github.com/bakjs/bak/compare/2.0.0...2.0.1) (2017-11-09) 371 | 372 | 373 | 374 | 375 | ## [1.1.4](https://github.com/bakjs/bak/compare/bak@1.1.3...bak@1.1.4) (2017-09-12) 376 | 377 | 378 | 379 | 380 | **Note:** Version bump only for package bak 381 | 382 | 383 | ## [1.1.3](https://github.com/bakjs/bak/compare/bak@1.1.2...bak@1.1.3) (2017-09-06) 384 | 385 | 386 | ### Bug Fixes 387 | 388 | * install mongo by default for core ([b82810c](https://github.com/bakjs/bak/commit/b82810c)) 389 | 390 | 391 | 392 | 393 | 394 | ## [1.1.2](https://github.com/bakjs/bak/compare/bak@1.1.1...bak@1.1.2) (2017-09-06) 395 | 396 | 397 | ### Bug Fixes 398 | 399 | * make [@bakjs](https://github.com/bakjs)/mongo a peer dependency ([f7ec5c4](https://github.com/bakjs/bak/commit/f7ec5c4)) 400 | 401 | 402 | 403 | 404 | 405 | ## [1.1.1](https://github.com/bakjs/bak/compare/bak@1.1.0...bak@1.1.1) (2017-09-06) 406 | 407 | 408 | ### Bug Fixes 409 | 410 | * routes constructor ([a92a626](https://github.com/bakjs/bak/commit/a92a626)) 411 | 412 | 413 | 414 | 415 | 416 | # [1.1.0](https://github.com/bakjs/bak/compare/bak@1.0.1...bak@1.1.0) (2017-09-06) 417 | 418 | 419 | ### Features 420 | 421 | * use process.env PORT and HOST if provided ([cedaa97](https://github.com/bakjs/bak/commit/cedaa97)) 422 | 423 | 424 | 425 | 426 | 427 | ## 1.0.1 (2017-09-05) 428 | 429 | 430 | 431 | 432 | **Note:** Version bump only for package bak 433 | 434 | 435 | # 1.0.0 (2017-09-04) 436 | 437 | 438 | 439 | 440 | **Note:** Version bump only for package bak 441 | -------------------------------------------------------------------------------- /packages/bak/README.md: -------------------------------------------------------------------------------- 1 | ../../README.md -------------------------------------------------------------------------------- /packages/bak/bin/bak: -------------------------------------------------------------------------------- 1 | #!/usr/bin/env node 2 | 3 | const consola = require('consola') 4 | 5 | consola.wrapConsole() 6 | 7 | require('./cli').parseAndExit().catch(consola.fatal) 8 | -------------------------------------------------------------------------------- /packages/bak/bin/cli/commands/dev.js: -------------------------------------------------------------------------------- 1 | const path = require('path') 2 | const consola = require('consola') 3 | 4 | const { parseArgs } = require('../utils') 5 | 6 | module.exports = { 7 | flags: 'dev [dir]', 8 | desc: 'Start development server with auto-reload', 9 | paramsDesc: 'Root directory of app, containing bak.config.js config file', 10 | async run (argv) { 11 | const { rootDir, config, extraArgs } = parseArgs(argv) 12 | 13 | const nodemon = require('nodemon') 14 | 15 | // https://github.com/remy/nodemon/blob/master/doc/requireable.md 16 | const nodemonConfig = Object.assign({ 17 | cwd: rootDir, 18 | script: path.resolve(__dirname, '../../bak'), 19 | restartable: 'rs', 20 | args: [ 21 | 'start', 22 | rootDir 23 | ], 24 | nodeArgs: extraArgs 25 | }, config.nodemon) 26 | 27 | // Start nodemon before listening for events 28 | nodemon(nodemonConfig) 29 | 30 | // https://github.com/remy/nodemon/blob/master/doc/events.md 31 | nodemon.on('start', () => { 32 | consola.info('Starting server...') 33 | consola.info('Working directory: ' + rootDir) 34 | }) 35 | 36 | nodemon.on('crash', () => { 37 | consola.fatal('Server has crashed!') 38 | consola.info('Fix code or use `rs` command to restart server immediately.') 39 | }) 40 | 41 | nodemon.on('quit', () => { 42 | consola.info('Server gracefully stopped!') 43 | process.exit(0) 44 | }) 45 | 46 | nodemon.on('restart', (files = []) => { 47 | if (files.length) { 48 | consola.info('Reloading server due to file changes...') 49 | } else { 50 | consola.info('Restarting server...') 51 | } 52 | }) 53 | } 54 | } 55 | -------------------------------------------------------------------------------- /packages/bak/bin/cli/commands/start.js: -------------------------------------------------------------------------------- 1 | const chalk = require('chalk') 2 | const consola = require('consola') 3 | const { parseArgs } = require('../utils') 4 | 5 | module.exports = { 6 | flags: 'start [dir]', 7 | desc: 'Start server', 8 | paramsDesc: 'Root directory of app, containing bak.config.js config file', 9 | async run (argv) { 10 | const { config } = parseArgs(argv) 11 | 12 | // Delay loading framework until command is actually run 13 | const { Bak } = require('../../..') 14 | 15 | // Create server instance 16 | const bak = new Bak(config) 17 | 18 | // Start server 19 | await bak.init() 20 | 21 | const { uri } = bak.server.hapi.info 22 | 23 | consola.ready({ 24 | message: `Listening on ${chalk.blue.underline(uri.replace('0.0.0.0', 'localhost'))}` + 25 | (uri.includes('0.0.0.0') ? (chalk.grey(' (all interfaces)')) : ''), 26 | badge: true 27 | }) 28 | } 29 | } 30 | -------------------------------------------------------------------------------- /packages/bak/bin/cli/index.js: -------------------------------------------------------------------------------- 1 | const cli = require('sywac') 2 | const styles = require('./styles') 3 | const consola = require('consola') 4 | 5 | // Console error unhandled promises 6 | process.on('unhandledRejection', err => { 7 | consola.error(err) 8 | }) 9 | 10 | // Commands 11 | cli.commandDirectory('commands') 12 | 13 | // Global options 14 | cli.file('-c, --config ', { 15 | desc: 'Config file', 16 | defaultValue: 'bak.config.js' 17 | }) 18 | 19 | // Help text stuff 20 | cli.style(styles) 21 | cli.help('-h, --help') 22 | cli.version('-v, --version') 23 | cli.showHelpByDefault() 24 | cli.outputSettings({ maxWidth: 75 }) 25 | 26 | // Export cli config 27 | // Call parseAndExit() in bin/bak 28 | // Or call parse(args) in tests 29 | module.exports = cli 30 | -------------------------------------------------------------------------------- /packages/bak/bin/cli/styles.js: -------------------------------------------------------------------------------- 1 | const chalk = require('chalk') 2 | 3 | module.exports = { 4 | // Style usage components 5 | usagePrefix: str => chalk.white(str.slice(0, 6)) + ' ' + chalk.magenta(str.slice(7)), 6 | usageCommandPlaceholder: str => chalk.magenta(str), 7 | usagePositionals: str => chalk.green(str), 8 | usageArgsPlaceholder: str => chalk.green(str), 9 | usageOptionsPlaceholder: str => chalk.green.dim(str), 10 | 11 | // Style normal help text 12 | group: str => chalk.white(str), 13 | flags: (str, type) => { 14 | // Magenta for commands 15 | // Green for args 16 | // Dim green for options 17 | if (type.datatype === 'command') { 18 | return str.split(' ').map(flag => flag.startsWith('[') || flag.startsWith('<') ? chalk.green(flag) : chalk.magenta(flag)).join(' ') 19 | } 20 | let style = chalk.green 21 | if (str.startsWith('-')) style = style.dim 22 | return style(str) 23 | }, 24 | desc: str => chalk.cyan(str), 25 | hints: str => chalk.dim(str), 26 | example: str => chalk.yellow(str.slice(0, 2)) + 27 | str.slice(2).split(' ').map(word => word.startsWith('-') ? chalk.green.dim(word) : chalk.gray(word)).join(' '), 28 | 29 | // Use different style when a type is invalid 30 | groupError: str => chalk.red(str), 31 | flagsError: str => chalk.red(str), 32 | descError: str => chalk.yellow(str), 33 | hintsError: str => chalk.red(str), 34 | 35 | // Style error messages 36 | messages: str => chalk.red(str) 37 | } 38 | -------------------------------------------------------------------------------- /packages/bak/bin/cli/utils.js: -------------------------------------------------------------------------------- 1 | const path = require('path') 2 | const esm = require('esm') 3 | 4 | const _requireESM = esm(module, { 5 | cjs: { 6 | dedefault: true 7 | } 8 | }) 9 | 10 | const requireESM = (path) => { 11 | const m = _requireESM(path) 12 | return m.default || m 13 | } 14 | 15 | module.exports.parseArgs = (argv) => { 16 | const rootDir = path.resolve(process.cwd(), argv.dir || '') 17 | const configPath = path.resolve(rootDir, argv.config) 18 | const config = requireESM(configPath) 19 | 20 | // Extra args 21 | const extraArgs = [] 22 | let extra = false 23 | for (let arg of process.argv) { 24 | if (arg === '--') { 25 | extra = true 26 | continue 27 | } 28 | if (extra) { 29 | extraArgs.push(arg) 30 | } 31 | } 32 | 33 | // Root Dir 34 | if (!config.relativeTo) { 35 | config.relativeTo = rootDir 36 | } 37 | 38 | return { 39 | rootDir, 40 | configPath, 41 | config, 42 | extraArgs 43 | } 44 | } 45 | -------------------------------------------------------------------------------- /packages/bak/lib/bak.js: -------------------------------------------------------------------------------- 1 | const esm = require('esm') 2 | const path = require('path') 3 | 4 | const Options = require('./options') 5 | const Server = require('./server') 6 | 7 | module.exports = class Bak { 8 | constructor (_options) { 9 | this.options = Options.from(_options) 10 | 11 | this._requireESM = esm(module, this.options.esm) 12 | 13 | this.server = new Server(this) 14 | } 15 | 16 | require (modulePath, relative = false) { 17 | if (relative) { 18 | modulePath = path.resolve(this.options.relativeTo, modulePath) 19 | } 20 | 21 | const m = this._requireESM(modulePath) 22 | 23 | return m.default || m 24 | } 25 | 26 | async init () { 27 | await this.server.init() 28 | await this.server.start() 29 | } 30 | } 31 | -------------------------------------------------------------------------------- /packages/bak/lib/controller.js: -------------------------------------------------------------------------------- 1 | const consola = require('consola') 2 | const { defaultsDeep } = require('lodash') 3 | const { normalizePath, VALID_METHODS } = require('./utils') 4 | 5 | class Controller { 6 | constructor () { 7 | this.prefix = '' 8 | this.defaults = {} 9 | this._routes = [] 10 | } 11 | 12 | async routes () { 13 | return this._routes 14 | } 15 | 16 | async init () { 17 | // STUB 18 | } 19 | 20 | get controllerName () { 21 | return this.constructor.name 22 | } 23 | 24 | route (method, path, handler, config) { 25 | this._route({ 26 | method, 27 | path, 28 | handler, 29 | config 30 | }) 31 | } 32 | 33 | _route (route) { 34 | // -- Method -- 35 | // any ~> * 36 | if (route.method === 'any') { 37 | route.method = '*' 38 | } 39 | 40 | // Convert to upper case for consistency 41 | route.method = route.method.toUpperCase() 42 | 43 | // -- Path -- 44 | // Prepend prefix to path 45 | if (this.prefix && this.prefix.length) { 46 | route.path = normalizePath(this.prefix + route.path) 47 | } 48 | 49 | // -- Handler -- 50 | // Validate handler 51 | if (!route.handler) { 52 | consola.fatal('Handler not defined for route: ' + JSON.stringify(route)) 53 | throw Error() 54 | } 55 | 56 | if (!route.handler.name && !route.handler._name) { 57 | consola.fatal('Handler should have nome: ' + JSON.stringify(route)) 58 | throw Error() 59 | } 60 | 61 | // Bind route handler to instance of class 62 | if (typeof route.handler === 'function') { 63 | route.handler = route.handler.bind(this) 64 | } 65 | 66 | // -- Config -- 67 | // Compute route name 68 | const name = (route.handler.name || route.handler._name).replace('bound ', '') 69 | delete route.handler._name 70 | 71 | // Extend route config by defaults 72 | route.config = defaultsDeep({ 73 | id: this.controllerName + '.' + name 74 | }, route.config, this.defaults) 75 | 76 | // Remove payload validation for GET and HEAD requests 77 | if (route.method === 'GET' || route.method === 'HEAD') { 78 | if (route.config.validate) { 79 | delete route.config.validate.payload 80 | } 81 | } 82 | 83 | // Add to _routes 84 | this._routes.push(route) 85 | } 86 | } 87 | 88 | // Append route helpers to Controller prototype 89 | VALID_METHODS.forEach(method => { 90 | const fn = function () { 91 | this.route(method, ...arguments) 92 | } 93 | fn.name = method 94 | Controller.prototype[method] = fn 95 | }) 96 | 97 | module.exports = Controller 98 | -------------------------------------------------------------------------------- /packages/bak/lib/index.js: -------------------------------------------------------------------------------- 1 | const Controller = require('./controller') 2 | const Server = require('./server') 3 | const Bak = require('./bak') 4 | const Utils = require('./utils') 5 | 6 | module.exports = { 7 | Controller, 8 | Server, 9 | Bak, 10 | Utils 11 | } 12 | -------------------------------------------------------------------------------- /packages/bak/lib/options.js: -------------------------------------------------------------------------------- 1 | const env = require('std-env') 2 | const { defaultsDeep } = require('lodash') 3 | const consola = require('consola') 4 | 5 | const Options = module.exports = {} 6 | 7 | Options.from = function (_options) { 8 | const options = defaultsDeep({}, _options, Options.defaults) 9 | 10 | // relativeTo 11 | if (!options.relativeTo) { 12 | options.relativeTo = process.cwd() 13 | } 14 | 15 | // Enable CORS on dev 16 | let routeOptions = options.routes 17 | if (options.dev && routeOptions.cors === undefined) { 18 | routeOptions.cors = { credentials: true } 19 | } 20 | 21 | // routeTable 22 | if (options.routeTable === undefined) { 23 | options.routeTable = options.dev 24 | } 25 | 26 | // Errors 27 | if (options.devErrors) { 28 | if (options.devErrors.showErrors === undefined) { 29 | options.devErrors.showErrors = options.dev 30 | } 31 | } 32 | 33 | // Validation errors 34 | const { validate } = options.server.routes 35 | if (validate.failAction === undefined) { 36 | validate.failAction = async (_, __, err) => { 37 | if (options.dev) { 38 | consola.error(err + '') 39 | } 40 | throw err 41 | } 42 | } 43 | 44 | return options 45 | } 46 | 47 | Options.defaults = { 48 | dev: !env.production && !env.test && !env.ci, 49 | relativeTo: '', 50 | routeTable: undefined, 51 | registrations: [], 52 | routes: [], 53 | esm: {}, 54 | prefix: '/', 55 | nodemon: {}, 56 | devErrors: { 57 | showErrors: undefined, 58 | useYouch: true, 59 | toTerminal: false 60 | }, 61 | server: { 62 | cache: null, 63 | port: process.env.PORT || 3000, 64 | host: process.env.HOST || '0.0.0.0', 65 | router: { 66 | stripTrailingSlash: true 67 | }, 68 | routes: { 69 | cors: undefined, 70 | validate: { 71 | failAction: undefined 72 | } 73 | } 74 | }, 75 | listener: { 76 | keepAliveTimeout: undefined 77 | } 78 | } 79 | -------------------------------------------------------------------------------- /packages/bak/lib/server.js: -------------------------------------------------------------------------------- 1 | const Hapi = require('@hapi/hapi') 2 | const { resolve } = require('path') 3 | const { serial, parallel } = require('items-promise') 4 | const { normalizePath } = require('./utils') 5 | const { camelCase } = require('lodash') 6 | const DevErrors = require('hapi-dev-errors') 7 | const RouteTablePlugin = require('@bakjs/route-table') 8 | const ShortcutsPlugin = require('@bakjs/shortcuts') 9 | const InputPlugin = require('@bakjs/input') 10 | const LoggingPlugin = require('@bakjs/logging') 11 | 12 | module.exports = class Server { 13 | /** 14 | * 15 | * @param bak 16 | */ 17 | constructor (bak) { 18 | this.bak = bak 19 | this.options = this.bak.options 20 | } 21 | 22 | /** 23 | * 24 | * @returns {Promise.} 25 | */ 26 | async init () { 27 | // Create server 28 | this.hapi = new Hapi.Server(this.options.server) 29 | 30 | // Listener options 31 | const { keepAliveTimeout } = this.options.listener 32 | if (keepAliveTimeout !== undefined) { 33 | this.hapi.listener.keepAliveTimeout = keepAliveTimeout 34 | } 35 | 36 | // Register core plugins 37 | const corePlugins = [ 38 | LoggingPlugin, 39 | ShortcutsPlugin, 40 | InputPlugin 41 | ] 42 | 43 | if (this.options.routeTable) { 44 | corePlugins.push(RouteTablePlugin) 45 | } 46 | 47 | if (this.options.devErrors) { 48 | corePlugins.push({ 49 | plugin: DevErrors, 50 | options: this.options.devErrors 51 | }) 52 | } 53 | 54 | await serial(corePlugins, this.register.bind(this)) 55 | 56 | // Register plugins 57 | await serial(this.options.registrations, this.register.bind(this)) 58 | 59 | // Register routes 60 | await this.registerRoutes(this.options.routes) 61 | 62 | return this 63 | } 64 | 65 | /** 66 | * Start server 67 | * @returns {Promise} 68 | * @private 69 | */ 70 | async start () { 71 | await this.hapi.initialize() 72 | await this.hapi.start() 73 | return this 74 | } 75 | 76 | /** 77 | * 78 | * @param routes 79 | * @returns {Promise} 80 | * @private 81 | */ 82 | async _loadRoutes (routes, dirty = false) { 83 | if (typeof routes === 'string') { 84 | dirty = true 85 | routes = this.bak.require(routes, true) 86 | } 87 | 88 | if (typeof routes === 'function') { 89 | dirty = true 90 | routes = new routes() // eslint-disable-line new-cap 91 | routes.server = this.hapi 92 | } 93 | 94 | if (typeof routes.init === 'function') { 95 | await Promise.resolve(routes.init()) 96 | } 97 | 98 | if (typeof routes.routes === 'function') { 99 | dirty = true 100 | routes = await Promise.resolve(routes.routes()) 101 | } 102 | 103 | if (!routes) { 104 | routes = [] 105 | } 106 | 107 | if (!Array.isArray(routes)) { 108 | routes = [routes] 109 | } 110 | 111 | if (dirty) { 112 | routes = await parallel(routes, this._loadRoutes.bind(this)) 113 | routes = Array.prototype.concat.apply([], routes) 114 | } 115 | 116 | return routes 117 | } 118 | 119 | /** 120 | * 121 | * @param routes 122 | * @param prefix 123 | * @returns {Promise} 124 | * @private 125 | */ 126 | async _resolveRoutes (routes, prefix = '') { 127 | routes = await this._loadRoutes(routes, true) 128 | prefix = ((prefix && prefix.length) ? prefix : '') + '/' 129 | 130 | return Array.prototype.concat.apply([], await parallel(routes, async route => { 131 | // Simple nested routes 132 | if (Array.isArray(route)) { 133 | return this._resolveRoutes(route, prefix) 134 | } 135 | 136 | // Prefixed nested routes 137 | if (route && route.prefix) { 138 | const nestedPrefix = prefix + route.prefix 139 | return this._resolveRoutes(route.routes, nestedPrefix) 140 | } 141 | 142 | // Same level routes 143 | if (prefix.length) { 144 | route.path = normalizePath(prefix + (route.path || '')) 145 | } 146 | if (!route.path || !route.path.length) { 147 | route.path = '/' 148 | } 149 | if (!route.method) { 150 | route.method = 'GET' 151 | } 152 | 153 | return route 154 | })) 155 | } 156 | 157 | /** 158 | * 159 | * @param routes 160 | * @returns {Promise} 161 | */ 162 | async registerRoutes (routes) { 163 | // Resolve 164 | routes = await this._resolveRoutes(routes, this.options.prefix) 165 | 166 | // Register 167 | return this.hapi.route(routes) 168 | } 169 | 170 | /** 171 | * 172 | * @param registration 173 | * @param registrationOptions 174 | * @returns {Promise} 175 | */ 176 | register (registration, registrationOptions) { 177 | // Normalize registration into { plugin, [options], ... } 178 | if (!registration.plugin) { 179 | registration = { 180 | plugin: registration 181 | } 182 | } 183 | 184 | // Resolve plugin 185 | if (typeof registration.plugin === 'string') { 186 | let path = registration.plugin 187 | if (this.options.relativeTo && path[0] === '.') { 188 | path = resolve(this.options.relativeTo, path) 189 | } 190 | registration.plugin = this.bak.require(path) 191 | } 192 | 193 | // Resolve and merge configs for plugin 194 | const plugin = registration.plugin.plugin || registration.plugin 195 | const configKey = camelCase(plugin.configKey || plugin.name || (plugin.pkg && plugin.pkg.name) || '') 196 | 197 | if (configKey.length && this.options[configKey] && registration.options === undefined) { 198 | registration.options = this.options[configKey] 199 | } 200 | 201 | return this.hapi.register(registration, registrationOptions || {}) 202 | } 203 | 204 | /** 205 | * Log error to hapi 206 | * @param {*} error 207 | * @param {*} request 208 | */ 209 | logError (error, request = {}) { 210 | // Ignore Boom or null exceptions 211 | if (!error || error.isBoom) { 212 | return 213 | } 214 | 215 | const tags = [ 216 | 'error' 217 | ] 218 | 219 | const context = { 220 | error, 221 | payload: request.payload, 222 | query: request.query, 223 | user: request.auth.credentials 224 | } 225 | 226 | this.hapi.log(tags, context) 227 | } 228 | } 229 | -------------------------------------------------------------------------------- /packages/bak/lib/utils.js: -------------------------------------------------------------------------------- 1 | const consola = require('consola') 2 | 3 | function trimSlash (s) { 4 | return s[s.length - 1] === '/' ? s.slice(0, s.length - 1) : s 5 | } 6 | 7 | function normalizePath (path) { 8 | return trimSlash(path.replace(/\/+/g, '/')) 9 | } 10 | 11 | function fatal (msg, error, additional) { 12 | consola.fatal(additional + '\r\n', msg, error) 13 | process.exit(1) 14 | } 15 | 16 | const VALID_METHODS = ['get', 'post', 'put', 'delete', 'patch', '*', 'any'] 17 | function is_valid_method (method) { 18 | if (!method) return false 19 | if (Array.isArray(method)) { 20 | for (let i = 0; i < method.length; i++) { 21 | if (!is_valid_method(method[i])) { return false } 22 | } 23 | } else { 24 | if (VALID_METHODS.indexOf(method.toLowerCase()) === -1) { return false } 25 | } 26 | return true 27 | } 28 | 29 | /** 30 | * 31 | * @param func {Function} 32 | * @returns {Array} 33 | */ 34 | function getParamNames (func) { 35 | let func_str = func.toString() 36 | let func_def = func_str.slice(func_str.indexOf('(') + 1, func_str.indexOf(')')) 37 | return func_def.match(/([^\s,]+)/g) 38 | } 39 | 40 | /** 41 | * Try to detect request real ip 42 | * @param request 43 | * @returns string 44 | */ 45 | function realIP (request) { 46 | return request.ip || request.headers['x-real-ip'] || request.headers['x-forwarded-for'] || request.info['remoteAddress'] 47 | } 48 | 49 | // Exports 50 | module.exports = { 51 | trimSlash, 52 | normalizePath, 53 | fatal, 54 | is_valid_method, 55 | getParamNames, 56 | realIP, 57 | VALID_METHODS 58 | } 59 | -------------------------------------------------------------------------------- /packages/bak/package.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "bak", 3 | "version": "5.0.1", 4 | "description": "Delightful modern web applications framework for hapi.js", 5 | "repository": "https://github.com/bakjs/bak", 6 | "main": "lib/index.js", 7 | "bin": "bin/bak", 8 | "files": [ 9 | "lib", 10 | "bin" 11 | ], 12 | "dependencies": { 13 | "@bakjs/input": "2.2.1", 14 | "@bakjs/logging": "2.8.1", 15 | "@bakjs/route-table": "2.4.2", 16 | "@bakjs/shortcuts": "2.2.1", 17 | "@hapi/boom": "^7.4.2", 18 | "@hapi/hapi": "^18.3.1", 19 | "chalk": "^2.4.2", 20 | "consola": "^2.6.2", 21 | "esm": "3.2.25", 22 | "hapi-dev-errors": "^3.2.5", 23 | "items-promise": "^1.0.0", 24 | "lodash": "^4.17.11", 25 | "nodemon": "^1.19.0", 26 | "std-env": "^2.2.1", 27 | "sywac": "^1.2.1" 28 | } 29 | } 30 | -------------------------------------------------------------------------------- /packages/input/CHANGELOG.md: -------------------------------------------------------------------------------- 1 | # Change Log 2 | 3 | All notable changes to this project will be documented in this file. 4 | See [Conventional Commits](https://conventionalcommits.org) for commit guidelines. 5 | 6 | ## 2.2.1 (2018-11-19) 7 | 8 | **Note:** Version bump only for package @bakjs/input 9 | 10 | 11 | 12 | 13 | 14 | 15 | # [2.2.0](https://github.com/bakjs/plugins/compare/@bakjs/input@2.1.0...@bakjs/input@2.2.0) (2018-02-08) 16 | 17 | 18 | ### Features 19 | 20 | * export explicit configKey ([add9dfb](https://github.com/bakjs/plugins/commit/add9dfb)) 21 | 22 | 23 | 24 | 25 | 26 | # 2.1.0 (2017-10-24) 27 | 28 | 29 | 30 | 31 | # 2.0.0 (2017-10-19) 32 | 33 | 34 | ### Features 35 | 36 | * new plugins from core ([d9cc60d](https://github.com/bakjs/plugins/commit/d9cc60d)) 37 | -------------------------------------------------------------------------------- /packages/input/README.md: -------------------------------------------------------------------------------- 1 | Please refer to [bakjs/hapi-plugins](https://github.com/bakjs/hapi-plugins) for more info and docs. -------------------------------------------------------------------------------- /packages/input/lib/index.js: -------------------------------------------------------------------------------- 1 | const { capitalize } = require('lodash') 2 | 3 | exports.register = function bakInput (server, config) { 4 | config = Object.assign({ models: [] }, config) 5 | 6 | server.decorate('request', 'input', function (paramNames) { 7 | const request = this 8 | let queue = [] 9 | let resolvedParams = {} 10 | 11 | paramNames.forEach((paramName) => { 12 | // Optional params 13 | const isOptional = paramName.endsWith('?') 14 | if (isOptional) { 15 | paramName = paramName.substr(0, paramName.length - 2) 16 | } 17 | 18 | // Get user input 19 | let requestParam = request.params[paramName] 20 | 21 | // Skip optional params if are not provided 22 | if (isOptional && !requestParam) { 23 | resolvedParams[paramName] = null 24 | return 25 | } 26 | 27 | // Find param model 28 | let model = config.models[capitalize(paramName)] 29 | 30 | // Directly resolve params without model 31 | if (!model) { 32 | resolvedParams[paramName] = requestParam 33 | return 34 | } 35 | 36 | // Prepare query 37 | let routeKey = model.$routeKey || '_id' 38 | if (routeKey instanceof Function) routeKey = routeKey() 39 | let query = {} 40 | query[routeKey] = requestParam.toString() 41 | 42 | // Push find job to queue 43 | queue.push(model.findOne(query).then((item) => { 44 | if (!item) { 45 | throw new Error('no records found for ' + paramName) 46 | } 47 | resolvedParams[paramName] = item 48 | })) 49 | }) 50 | 51 | return Promise.all(queue) 52 | }) 53 | } 54 | 55 | exports.pkg = require('../package.json') 56 | exports.once = true 57 | exports.configKey = 'input' 58 | -------------------------------------------------------------------------------- /packages/input/package.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "@bakjs/input", 3 | "version": "2.2.1", 4 | "repository": "bakjs/bak", 5 | "license": "MIT", 6 | "main": "lib/index.js", 7 | "publishConfig": { 8 | "access": "public" 9 | }, 10 | "contributors": [ 11 | "Pooya Parsa " 12 | ], 13 | "files": [ 14 | "lib" 15 | ] 16 | } 17 | -------------------------------------------------------------------------------- /packages/logging/CHANGELOG.md: -------------------------------------------------------------------------------- 1 | # Change Log 2 | 3 | All notable changes to this project will be documented in this file. 4 | See [Conventional Commits](https://conventionalcommits.org) for commit guidelines. 5 | 6 | ## [2.8.1](https://github.com/bakjs/bak/compare/@bakjs/logging@2.8.0...@bakjs/logging@2.8.1) (2019-05-22) 7 | 8 | **Note:** Version bump only for package @bakjs/logging 9 | 10 | 11 | 12 | 13 | 14 | # [2.8.0](https://github.com/bakjs/bak/compare/@bakjs/logging@2.7.2...@bakjs/logging@2.8.0) (2019-04-16) 15 | 16 | 17 | ### Features 18 | 19 | * **logging:** allow `options.filter` ([ce6f867](https://github.com/bakjs/bak/commit/ce6f867)) 20 | 21 | 22 | 23 | 24 | 25 | ## [2.7.2](https://github.com/bakjs/bak/compare/@bakjs/logging@2.7.1...@bakjs/logging@2.7.2) (2019-03-09) 26 | 27 | **Note:** Version bump only for package @bakjs/logging 28 | 29 | 30 | 31 | 32 | 33 | ## [2.7.1](https://github.com/bakjs/bak/compare/@bakjs/logging@2.7.0...@bakjs/logging@2.7.1) (2018-11-19) 34 | 35 | 36 | ### Bug Fixes 37 | 38 | * hade errors with unauthenticated tag ([b56130c](https://github.com/bakjs/bak/commit/b56130c)) 39 | 40 | 41 | 42 | 43 | 44 | # [2.7.0](https://github.com/bakjs/bak/compare/@bakjs/logging@0.3.1...@bakjs/logging@2.7.0) (2018-11-19) 45 | 46 | 47 | ### Features 48 | 49 | * improve logging plugin ([94d64f2](https://github.com/bakjs/bak/commit/94d64f2)) 50 | 51 | 52 | 53 | 54 | 55 | ## [2.6.1](https://github.com/bakjs/plugins/compare/@bakjs/logging@2.6.0...@bakjs/logging@2.6.1) (2018-11-05) 56 | 57 | 58 | ### Bug Fixes 59 | 60 | * ignore when there is no error ([ee2f050](https://github.com/bakjs/plugins/commit/ee2f050)) 61 | 62 | 63 | 64 | 65 | 66 | # [2.6.0](https://github.com/bakjs/plugins/compare/@bakjs/logging@2.5.1...@bakjs/logging@2.6.0) (2018-11-05) 67 | 68 | 69 | ### Features 70 | 71 | * use consola ([34b8dd0](https://github.com/bakjs/plugins/commit/34b8dd0)) 72 | 73 | 74 | 75 | 76 | 77 | 78 | ## [2.5.1](https://github.com/bakjs/plugins/compare/@bakjs/logging@2.5.0...@bakjs/logging@2.5.1) (2018-09-05) 79 | 80 | 81 | ### Bug Fixes 82 | 83 | * **logging:** log internal errors ([6187ad3](https://github.com/bakjs/plugins/commit/6187ad3)) 84 | 85 | 86 | 87 | 88 | 89 | # [2.5.0](https://github.com/bakjs/plugins/compare/@bakjs/logging@2.4.0...@bakjs/logging@2.5.0) (2018-02-08) 90 | 91 | 92 | ### Features 93 | 94 | * export explicit configKey ([add9dfb](https://github.com/bakjs/plugins/commit/add9dfb)) 95 | 96 | 97 | 98 | 99 | 100 | # [2.4.0](https://github.com/bakjs/plugins/compare/@bakjs/logging@2.3.0...@bakjs/logging@2.4.0) (2018-02-08) 101 | 102 | 103 | ### Features 104 | 105 | * **logging:** rewrite with better output for dev and prod ([77dfea3](https://github.com/bakjs/plugins/commit/77dfea3)) 106 | 107 | 108 | 109 | 110 | 111 | # [2.3.0](https://github.com/bakjs/plugins/compare/@bakjs/logging@2.2.4...@bakjs/logging@2.3.0) (2018-01-28) 112 | 113 | 114 | ### Features 115 | 116 | * **logging:** better Boom error handling ([eafe467](https://github.com/bakjs/plugins/commit/eafe467)) 117 | 118 | 119 | 120 | 121 | 122 | ## [2.2.4](https://github.com/bakjs/plugins/compare/@bakjs/logging@2.2.3...@bakjs/logging@2.2.4) (2017-12-21) 123 | 124 | 125 | ### Bug Fixes 126 | 127 | * **logging:** hide Boom errors in prodction environments ([1abdcce](https://github.com/bakjs/plugins/commit/1abdcce)) 128 | 129 | 130 | 131 | 132 | 133 | ## [2.2.3](https://github.com/bakjs/plugins/compare/@bakjs/logging@2.2.2...@bakjs/logging@2.2.3) (2017-12-19) 134 | 135 | 136 | ### Bug Fixes 137 | 138 | * **logging:** show all errors ([ce14994](https://github.com/bakjs/plugins/commit/ce14994)) 139 | 140 | 141 | 142 | 143 | 144 | ## [2.2.2](https://github.com/bakjs/plugins/compare/@bakjs/logging@2.2.1...@bakjs/logging@2.2.2) (2017-11-09) 145 | 146 | 147 | ### Bug Fixes 148 | 149 | * **audit:** error conflict ([c855449](https://github.com/bakjs/plugins/commit/c855449)) 150 | 151 | 152 | 153 | 154 | 155 | ## [2.2.1](https://github.com/bakjs/plugins/compare/@bakjs/logging@2.2.0...@bakjs/logging@2.2.1) (2017-10-24) 156 | 157 | 158 | 159 | 160 | **Note:** Version bump only for package @bakjs/logging 161 | 162 | 163 | # [2.2.0](https://github.com/bakjs/plugins/compare/@bakjs/logging@2.1.0...@bakjs/logging@2.2.0) (2017-10-24) 164 | 165 | 166 | ### Features 167 | 168 | * **logging:** remvoe dependency from bak ([c0f2d72](https://github.com/bakjs/plugins/commit/c0f2d72)) 169 | 170 | 171 | 172 | 173 | 174 | # [2.1.0](https://github.com/bakjs/plugins/compare/@bakjs/logging@0.3.2...@bakjs/logging@2.1.0) (2017-10-24) 175 | 176 | 177 | ### Features 178 | 179 | * **logging:** lightweight rewrite ([6727e29](https://github.com/bakjs/plugins/commit/6727e29)) 180 | 181 | 182 | 183 | 184 | 185 | ## 0.3.2 (2017-10-07) 186 | 187 | 188 | 189 | 190 | **Note:** Version bump only for package @bakjs/logging 191 | 192 | 193 | ## [0.3.1](https://github.com/bakjs/bak/compare/@bakjs/logging@0.3.0...@bakjs/logging@0.3.1) (2017-09-06) 194 | 195 | 196 | ### Bug Fixes 197 | 198 | * make [@bakjs](https://github.com/bakjs)/mongo a peer dependency ([f7ec5c4](https://github.com/bakjs/bak/commit/f7ec5c4)) 199 | 200 | 201 | 202 | 203 | 204 | # [0.3.0](https://github.com/bakjs/bak/compare/@bakjs/logging@0.2.0...@bakjs/logging@0.3.0) (2017-09-06) 205 | 206 | 207 | ### Bug Fixes 208 | 209 | * add name attr to root ([1a04fea](https://github.com/bakjs/bak/commit/1a04fea)) 210 | * corrct model require ([668dc9d](https://github.com/bakjs/bak/commit/668dc9d)) 211 | 212 | 213 | ### Features 214 | 215 | * always enable console reporter ([4293981](https://github.com/bakjs/bak/commit/4293981)) 216 | 217 | 218 | 219 | 220 | 221 | # 0.2.0 (2017-09-05) 222 | 223 | 224 | ### Features 225 | 226 | * expose Audit model ([5bde1f0](https://github.com/bakjs/bak/commit/5bde1f0)) 227 | 228 | 229 | 230 | 231 | 232 | ## 0.1.1 (2017-09-04) 233 | 234 | 235 | 236 | 237 | **Note:** Version bump only for package @bakjs/logging 238 | 239 | 240 | # 0.1.0 (2017-09-03) 241 | 242 | 243 | 244 | 245 | **Note:** Version bump only for package @bakjs/logging 246 | -------------------------------------------------------------------------------- /packages/logging/README.md: -------------------------------------------------------------------------------- 1 | Please refer to [bakjs/hapi-plugins](https://github.com/bakjs/hapi-plugins) for more info and docs. -------------------------------------------------------------------------------- /packages/logging/lib/index.js: -------------------------------------------------------------------------------- 1 | const consola = require('consola') 2 | 3 | exports.register = function (server, options) { 4 | server.events.on( 5 | { name: 'request', channels: ['error', 'internal'] }, (_, { error, timestamp }, tags) => { 6 | if ( 7 | (!error) || 8 | (tags.unauthenticated) || 9 | (error.output && error.output.statusCode === 404) || 10 | (typeof options.filter === 'function' && !options.filter(error, tags)) 11 | ) { 12 | return 13 | } 14 | 15 | consola.error({ 16 | message: error, 17 | tag: Object.keys(tags), 18 | time: timestamp 19 | }) 20 | } 21 | ) 22 | } 23 | 24 | exports.pkg = require('../package.json') 25 | exports.once = true 26 | exports.configKey = 'logging' 27 | -------------------------------------------------------------------------------- /packages/logging/package.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "@bakjs/logging", 3 | "version": "2.8.1", 4 | "repository": "bakjs/bak", 5 | "license": "MIT", 6 | "main": "lib/index.js", 7 | "publishConfig": { 8 | "access": "public" 9 | }, 10 | "contributors": [ 11 | "Pooya Parsa " 12 | ], 13 | "files": [ 14 | "lib" 15 | ], 16 | "dependencies": { 17 | "consola": "^2.6.2" 18 | } 19 | } 20 | -------------------------------------------------------------------------------- /packages/mongo/CHANGELOG.md: -------------------------------------------------------------------------------- 1 | # Change Log 2 | 3 | All notable changes to this project will be documented in this file. 4 | See [Conventional Commits](https://conventionalcommits.org) for commit guidelines. 5 | 6 | # [4.6.0](https://github.com/bakjs/bak/compare/@bakjs/mongo@4.5.4...@bakjs/mongo@4.6.0) (2019-11-10) 7 | 8 | 9 | ### Features 10 | 11 | * **mongo:** enable useUnifiedTopology ([2bfd1c0](https://github.com/bakjs/bak/commit/2bfd1c0)) 12 | 13 | 14 | 15 | 16 | 17 | ## [4.5.4](https://github.com/bakjs/bak/compare/@bakjs/mongo@4.5.3...@bakjs/mongo@4.5.4) (2019-05-22) 18 | 19 | **Note:** Version bump only for package @bakjs/mongo 20 | 21 | 22 | 23 | 24 | 25 | ## [4.5.3](https://github.com/bakjs/bak/compare/@bakjs/mongo@4.5.2...@bakjs/mongo@4.5.3) (2019-04-16) 26 | 27 | **Note:** Version bump only for package @bakjs/mongo 28 | 29 | 30 | 31 | 32 | 33 | ## [4.5.2](https://github.com/bakjs/bak/compare/@bakjs/mongo@4.5.1...@bakjs/mongo@4.5.2) (2019-04-16) 34 | 35 | **Note:** Version bump only for package @bakjs/mongo 36 | 37 | 38 | 39 | 40 | 41 | ## [4.5.1](https://github.com/bakjs/bak/compare/@bakjs/mongo@4.5.0...@bakjs/mongo@4.5.1) (2019-03-09) 42 | 43 | **Note:** Version bump only for package @bakjs/mongo 44 | 45 | 46 | 47 | 48 | 49 | # [4.5.0](https://github.com/bakjs/bak/compare/@bakjs/mongo@4.4.0...@bakjs/mongo@4.5.0) (2019-01-02) 50 | 51 | 52 | ### Bug Fixes 53 | 54 | * **mongo:** fix default connection setup ([95e72f6](https://github.com/bakjs/bak/commit/95e72f6)) 55 | 56 | 57 | ### Features 58 | 59 | * **mongo:** add h.mongoose ([798f58a](https://github.com/bakjs/bak/commit/798f58a)) 60 | * **mongo:** rework connection handling ([903e43e](https://github.com/bakjs/bak/commit/903e43e)) 61 | 62 | 63 | 64 | 65 | 66 | # [4.4.0](https://github.com/bakjs/bak/compare/@bakjs/mongo@4.3.0...@bakjs/mongo@4.4.0) (2019-01-01) 67 | 68 | 69 | ### Bug Fixes 70 | 71 | * **mongo:** add missing consola dependency ([546b7ec](https://github.com/bakjs/bak/commit/546b7ec)) 72 | 73 | 74 | ### Features 75 | 76 | * **mongo:** logger and forceReconnect ([3407718](https://github.com/bakjs/bak/commit/3407718)) 77 | * **mongo:** fixes and improvements ([97a8dd7](https://github.com/bakjs/bak/commit/97a8dd7)) 78 | 79 | 80 | 81 | 82 | 83 | # [4.3.0](https://github.com/bakjs/bak/compare/@bakjs/mongo@4.2.3...@bakjs/mongo@4.3.0) (2018-12-16) 84 | 85 | 86 | ### Bug Fixes 87 | 88 | * **mongo:** support connection.options ([e053f5b](https://github.com/bakjs/bak/commit/e053f5b)) 89 | 90 | 91 | ### Features 92 | 93 | * **mongo:** pass supported configs for Mongoose.prototype.set() ([1d63341](https://github.com/bakjs/bak/commit/1d63341)), closes [#29](https://github.com/bakjs/bak/issues/29) 94 | 95 | 96 | 97 | 98 | 99 | ## [4.2.3](https://github.com/bakjs/bak/compare/@bakjs/mongo@4.2.2...@bakjs/mongo@4.2.3) (2018-12-03) 100 | 101 | **Note:** Version bump only for package @bakjs/mongo 102 | 103 | 104 | 105 | 106 | 107 | ## [4.2.2](https://github.com/bakjs/bak/compare/@bakjs/mongo@4.2.1...@bakjs/mongo@4.2.2) (2018-11-19) 108 | 109 | 110 | ### Bug Fixes 111 | 112 | * **mongo:** enable useCreateIndex by default ([22ee6b0](https://github.com/bakjs/bak/commit/22ee6b0)) 113 | 114 | 115 | 116 | 117 | 118 | ## [4.2.1](https://github.com/bakjs/bak/compare/@bakjs/mongo@0.2.0...@bakjs/mongo@4.2.1) (2018-11-19) 119 | 120 | **Note:** Version bump only for package @bakjs/mongo 121 | 122 | 123 | 124 | 125 | 126 | # [4.2.0](https://github.com/bakjs/plugins/compare/@bakjs/mongo@4.1.0...@bakjs/mongo@4.2.0) (2018-11-04) 127 | 128 | 129 | ### Bug Fixes 130 | 131 | * **deps:** update all non-major dependencies ([#5](https://github.com/bakjs/plugins/issues/5)) ([fbfa4ad](https://github.com/bakjs/plugins/commit/fbfa4ad)) 132 | 133 | 134 | ### Features 135 | 136 | * update major dependencies ([0d75f39](https://github.com/bakjs/plugins/commit/0d75f39)) 137 | 138 | 139 | 140 | 141 | 142 | 143 | # [4.1.0](https://github.com/bakjs/plugins/compare/@bakjs/mongo@4.0.0...@bakjs/mongo@4.1.0) (2018-09-14) 144 | 145 | 146 | ### Bug Fixes 147 | 148 | * **mongo:** use useNewUrlParser ([2046cb9](https://github.com/bakjs/plugins/commit/2046cb9)) 149 | 150 | 151 | ### Features 152 | 153 | * **mongo:** support custom version of Mongoose ([ff47769](https://github.com/bakjs/plugins/commit/ff47769)) 154 | 155 | 156 | 157 | 158 | 159 | # [4.0.0](https://github.com/bakjs/plugins/compare/@bakjs/mongo@3.1.0...@bakjs/mongo@4.0.0) (2018-04-12) 160 | 161 | 162 | ### misc 163 | 164 | * update to cachegoose 6.0.1 ([26dab17](https://github.com/bakjs/plugins/commit/26dab17)) 165 | 166 | 167 | ### BREAKING CHANGES 168 | 169 | * https://github.com/boblauer/cachegoose 170 | 171 | 172 | 173 | 174 | 175 | # [3.1.0](https://github.com/bakjs/plugins/compare/@bakjs/mongo@3.0.2...@bakjs/mongo@3.1.0) (2018-02-08) 176 | 177 | 178 | ### Features 179 | 180 | * export explicit configKey ([add9dfb](https://github.com/bakjs/plugins/commit/add9dfb)) 181 | 182 | 183 | 184 | 185 | 186 | ## [3.0.2](https://github.com/bakjs/plugins/compare/@bakjs/mongo@3.0.1...@bakjs/mongo@3.0.2) (2018-01-26) 187 | 188 | 189 | 190 | 191 | **Note:** Version bump only for package @bakjs/mongo 192 | 193 | 194 | ## [3.0.1](https://github.com/bakjs/plugins/compare/@bakjs/mongo@3.0.0...@bakjs/mongo@3.0.1) (2018-01-26) 195 | 196 | 197 | 198 | 199 | **Note:** Version bump only for package @bakjs/mongo 200 | 201 | 202 | # [3.0.0](https://github.com/bakjs/plugins/compare/@bakjs/mongo@2.0.4...@bakjs/mongo@3.0.0) (2018-01-26) 203 | 204 | 205 | ### misc 206 | 207 | * **mongo:** update to mongoose 5.x ([3b56e90](https://github.com/bakjs/plugins/commit/3b56e90)) 208 | 209 | 210 | ### BREAKING CHANGES 211 | 212 | * **mongo:** https://github.com/Automattic/mongoose/blob/master/migrating_to_5.md 213 | 214 | 215 | 216 | 217 | 218 | ## [2.0.4](https://github.com/bakjs/plugins/compare/@bakjs/mongo@2.0.3...@bakjs/mongo@2.0.4) (2017-12-13) 219 | 220 | 221 | 222 | 223 | **Note:** Version bump only for package @bakjs/mongo 224 | 225 | 226 | ## [2.0.3](https://github.com/bakjs/plugins/compare/@bakjs/mongo@2.0.2...@bakjs/mongo@2.0.3) (2017-11-29) 227 | 228 | 229 | 230 | 231 | **Note:** Version bump only for package @bakjs/mongo 232 | 233 | 234 | ## [2.0.2](https://github.com/bakjs/plugins/compare/@bakjs/mongo@2.0.1...@bakjs/mongo@2.0.2) (2017-11-09) 235 | 236 | 237 | 238 | 239 | **Note:** Version bump only for package @bakjs/mongo 240 | 241 | 242 | ## [2.0.1](https://github.com/bakjs/plugins/compare/@bakjs/mongo@0.2.1...@bakjs/mongo@2.0.1) (2017-10-24) 243 | 244 | 245 | 246 | 247 | **Note:** Version bump only for package @bakjs/mongo 248 | 249 | 250 | ## 0.2.1 (2017-10-07) 251 | 252 | 253 | 254 | 255 | **Note:** Version bump only for package @bakjs/mongo 256 | 257 | 258 | # 0.2.0 (2017-09-05) 259 | 260 | 261 | ### Bug Fixes 262 | 263 | * **mongo:** fix promises ([5695a0d](https://github.com/bakjs/bak/commit/5695a0d)) 264 | 265 | 266 | ### Features 267 | 268 | * ensure model is registered once ([a93cc1a](https://github.com/bakjs/bak/commit/a93cc1a)) 269 | 270 | 271 | 272 | 273 | 274 | ## 0.1.1 (2017-09-04) 275 | 276 | 277 | ### Bug Fixes 278 | 279 | * **mongo:** fix promises ([5695a0d](https://github.com/bakjs/bak/commit/5695a0d)) 280 | 281 | 282 | 283 | 284 | 285 | # 0.1.0 (2017-09-03) 286 | 287 | 288 | 289 | 290 | **Note:** Version bump only for package @bakjs/mongo 291 | -------------------------------------------------------------------------------- /packages/mongo/README.md: -------------------------------------------------------------------------------- 1 | Please refer to [bakjs/hapi-plugins](https://github.com/bakjs/hapi-plugins) for more info and docs. -------------------------------------------------------------------------------- /packages/mongo/lib/index.js: -------------------------------------------------------------------------------- 1 | const Mongoose = require('mongoose') 2 | const Model = require('./model') 3 | const { connect } = require('./utils') 4 | 5 | exports.register = async function (server, config = {}) { 6 | const _Mongoose = config.Mongoose || Mongoose 7 | 8 | // https://mongoosejs.com/docs/api.html#mongoose_Mongoose-set 9 | const supportedSetKeys = [ 10 | 'debug', 11 | 'bufferCommands', 12 | 'useCreateIndex', 13 | 'useFindAndModify', 14 | 'useNewUrlParser', 15 | 'cloneSchemas', 16 | 'applyPluginsToDiscriminators', 17 | 'objectIdGetter', 18 | 'runValidators', 19 | 'toObject', 20 | 'toJSON', 21 | 'strict', 22 | 'selectPopulatedPaths' 23 | ] 24 | for (const key of supportedSetKeys) { 25 | if (config[key] !== undefined) { 26 | _Mongoose.set(key, config[key]) 27 | } 28 | } 29 | 30 | // Register cachegoose 31 | // @see https://github.com/boblauer/cachegoose 32 | if (config.cache !== false) { 33 | const cachegoose = require('cachegoose') 34 | cachegoose(_Mongoose, config.cache) 35 | } 36 | 37 | // @see https://github.com/whitecolor/mongoose-fill 38 | if (config.fill !== false) { 39 | require('mongoose-fill') 40 | } 41 | 42 | // Connect to all connections 43 | const connectionNames = Object.keys(config.connections) 44 | for (const connectionName of connectionNames) { 45 | await connect(_Mongoose, connectionName, config.connections[connectionName]) 46 | } 47 | 48 | // Add h.mongoose 49 | server.decorate('toolkit', 'mongoose', _Mongoose) 50 | } 51 | 52 | exports.pkg = require('../package.json') 53 | exports.once = true 54 | exports.configKey = 'mongo' 55 | 56 | exports.Model = Model 57 | exports.Schema = Mongoose.Schema 58 | exports.Mongoose = Mongoose 59 | -------------------------------------------------------------------------------- /packages/mongo/lib/model.js: -------------------------------------------------------------------------------- 1 | const Mongoose = require('mongoose') 2 | const _ = require('lodash') 3 | 4 | module.exports = class MongooseModel { 5 | static $make_model (name, collection, connection) { 6 | let _collection = collection 7 | let _name = name || this.name 8 | let _connection = connection || Mongoose 9 | 10 | let _schema = this.$schema || {} 11 | 12 | let _options = _.defaultsDeep({}, this.$options, { 13 | toObject: { 14 | virtuals: true, 15 | getters: true, 16 | transform: transform.bind(this) 17 | }, 18 | toJSON: { 19 | getters: true, 20 | transform: transform.bind(this) 21 | }, 22 | id: false, 23 | versionKey: false, 24 | timestamps: { 25 | createdAt: 'created_at', 26 | updatedAt: 'updated_at' 27 | } 28 | }) 29 | 30 | // Create mongoose schema 31 | let schema = new Mongoose.Schema(_schema, _options) 32 | if (this.$wrap_schema) schema = this.$wrap_schema(schema) || schema 33 | 34 | // Add methods from class to schema 35 | schema.plugin(wrap, this) 36 | 37 | // Create and return a mongoose model using connection 38 | let model = _connection.model(_name, schema, _collection/* optional */, false/* skipInit */) 39 | model.$schema = schema 40 | 41 | return model 42 | } 43 | 44 | static get $model () { 45 | this.__model = this.__model || this.$make_model(this.$name, this.$collection, this.$connection) 46 | return this.__model 47 | } 48 | 49 | static $transform () { 50 | 51 | } 52 | } 53 | 54 | function transform (doc, ret, options) { 55 | if (this.$visible) { 56 | return _.pickBy(ret, (value, key) => this.$visible.indexOf(key) !== -1) 57 | } else if (this.$hidden) { 58 | return _.omitBy(ret, (value, key) => this.$hidden.indexOf(key) !== -1) 59 | } 60 | return ret 61 | } 62 | 63 | function wrap (schema, target, hooks = []) { 64 | // Based on https://github.com/aksyonov/mongoose-class-wrapper (MIT) 65 | 66 | let proto = target.prototype 67 | let parent = Object.getPrototypeOf(target) 68 | let staticProps = Object.getOwnPropertyNames(target) 69 | let prototypeProps = Object.getOwnPropertyNames(proto) 70 | let instanceProps = prototypeProps.filter(name => name !== 'constructor') 71 | 72 | // Wrap parent first 73 | if (parent.name) wrap(schema, parent, hooks) 74 | 75 | // Add middleware hooks 76 | if (target.hooks && typeof target.hooks === 'object') { 77 | for (let hookType in target.hooks) { 78 | for (let hookAction in target.hooks[hookType]) { 79 | let hook = target.hooks[hookType][hookAction] 80 | let index = hooks.indexOf(hook) 81 | if (index < 0) { 82 | hooks.push(hook) 83 | schema[hookType](hookAction, hook) 84 | } 85 | } 86 | } 87 | } 88 | 89 | // Add static methods 90 | staticProps.forEach(name => { 91 | let method = Object.getOwnPropertyDescriptor(target, name) 92 | if (typeof method.value === 'function') { 93 | schema.static(name, method.value) 94 | } else if (typeof method.get === 'function') { 95 | schema.static(name, method.get) 96 | } 97 | }) 98 | 99 | // Add methods and virtual 100 | instanceProps.forEach(name => { 101 | let method = Object.getOwnPropertyDescriptor(proto, name) 102 | if (typeof method.get === 'function') schema.virtual(name).get(method.get) 103 | if (typeof method.set === 'function') schema.virtual(name).set(method.set) 104 | if (typeof method.value === 'function') { 105 | let x = /^(get|set)_(.+)$/.exec(name) 106 | if (x && x[1]) { 107 | switch (x[1]) { 108 | case 'get': 109 | schema.path(x[2]).get(method.value) 110 | break 111 | case 'set': 112 | schema.path(x[2]).set(method.value) 113 | break 114 | } 115 | } else { 116 | schema.method(name, method.value) 117 | } 118 | } 119 | }) 120 | } 121 | -------------------------------------------------------------------------------- /packages/mongo/lib/utils.js: -------------------------------------------------------------------------------- 1 | const consola = require('consola').withTag('MongoDB') 2 | 3 | // Utiliy function to setup logger on db 4 | function logConnectionEvents (conn) { 5 | // Emitted after getting disconnected from the db. 6 | // _readyState: 0 7 | conn.on('disconnected', () => { 8 | conn.$logger.debug('Disconnected!') 9 | }) 10 | 11 | // Emitted when this connection successfully connects to the db. 12 | // May be emitted multiple times in reconnected scenarios. 13 | // _readyState: 1 14 | conn.on('connected', () => { 15 | conn.$logger.success('Connected!') 16 | }) 17 | 18 | // Emitted when connection.{open,openSet}() is executed on this connection. 19 | // _readyState: 2 20 | conn.on('connecting', () => { 21 | conn.$logger.info('Connecting...') 22 | }) 23 | 24 | // Emitted when connection.close() was executed. 25 | // _readyState: 3 26 | conn.on('disconnecting', () => { 27 | conn.$logger.debug('Disconnecting...') 28 | }) 29 | 30 | // Emitted when an error occurs on this connection. 31 | conn.on('error', (error) => { 32 | conn.$logger.error(error) 33 | }) 34 | 35 | // Emitted after we connected and onOpen is executed 36 | // on all of this connections models. 37 | conn.on('open', () => { 38 | conn.$logger.debug('Connection opened!') 39 | }) 40 | 41 | // Emitted after we connected and subsequently disconnected, 42 | // followed by successfully another successfull connection. 43 | conn.on('reconnected', () => { 44 | conn.$logger.debug('Reconnected!') 45 | }) 46 | 47 | // Emitted in a replica-set scenario, when all nodes specified 48 | // in the connection string are connected. 49 | conn.on('fullsetup', () => { 50 | conn.$logger.debug('ReplicaSet ready!') 51 | }) 52 | } 53 | 54 | // Utility function to setup a connection 55 | async function connect (mongoose, connectionName, connectionOpts) { 56 | const isDefault = connectionName === 'default' 57 | 58 | // Normalize and destructure connection options 59 | if (typeof connectionOpts === 'string') { 60 | connectionOpts = { uri: connectionOpts } 61 | } 62 | let { uri, options, forceReconnect } = connectionOpts 63 | 64 | // Apply default options 65 | // https://mongoosejs.com/docs/connections.html#options 66 | options = { 67 | useNewUrlParser: true, 68 | useCreateIndex: true, 69 | useUnifiedTopology: true, 70 | ...options 71 | } 72 | 73 | // Manualy create connection 74 | let conn 75 | if (isDefault) { 76 | conn = mongoose.connection 77 | } else { 78 | conn = new mongoose.Connection(mongoose) 79 | mongoose.connections.push(conn) 80 | } 81 | 82 | // $logger helper 83 | conn.$logger = isDefault 84 | ? consola 85 | : consola.withTag(connectionName || 'unnamed') 86 | 87 | // Log connection events 88 | logConnectionEvents(conn) 89 | 90 | // $connect helper 91 | conn.$connect = () => { 92 | return new Promise((resolve, reject) => { 93 | conn.openUri(uri, options, (error) => { 94 | if (error) { 95 | reject(error) 96 | } else { 97 | resolve(conn) 98 | } 99 | }) 100 | }) 101 | } 102 | 103 | // Make accessable via mongoose.$connectionName 104 | mongoose['$' + connectionName] = conn 105 | 106 | // Setup force reconnect 107 | if (forceReconnect) { 108 | const timeout = forceReconnect === true ? 1000 : forceReconnect 109 | conn.on('error', () => { 110 | conn.close() 111 | }) 112 | conn.on('disconnected', () => { 113 | setTimeout(() => { conn.$connect() }, timeout) 114 | }) 115 | } 116 | 117 | // Connect 118 | await conn.$connect() 119 | await conn.$initialConnection 120 | } 121 | 122 | module.exports = { 123 | connect, 124 | consola 125 | } 126 | -------------------------------------------------------------------------------- /packages/mongo/package.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "@bakjs/mongo", 3 | "version": "4.6.0", 4 | "repository": "bakjs/bak", 5 | "license": "MIT", 6 | "main": "lib/index.js", 7 | "publishConfig": { 8 | "access": "public" 9 | }, 10 | "contributors": [ 11 | "Pooya Parsa " 12 | ], 13 | "files": [ 14 | "lib", 15 | "index.js" 16 | ], 17 | "dependencies": { 18 | "cachegoose": "^8.0.0", 19 | "consola": "^2.6.2", 20 | "lodash": "^4.17.11", 21 | "mongoose": "^5.5.10", 22 | "mongoose-fill": "^1.7.0" 23 | } 24 | } 25 | -------------------------------------------------------------------------------- /packages/nunjucks/CHANGELOG.md: -------------------------------------------------------------------------------- 1 | # Change Log 2 | 3 | All notable changes to this project will be documented in this file. 4 | See [Conventional Commits](https://conventionalcommits.org) for commit guidelines. 5 | 6 | ## [3.0.1](https://github.com/bakjs/bak/compare/@bakjs/nunjucks@3.0.0...@bakjs/nunjucks@3.0.1) (2019-05-22) 7 | 8 | **Note:** Version bump only for package @bakjs/nunjucks 9 | 10 | 11 | 12 | 13 | 14 | # [3.0.0](https://github.com/bakjs/bak/compare/@bakjs/nunjucks@2.1.5...@bakjs/nunjucks@3.0.0) (2019-05-22) 15 | 16 | 17 | ### Features 18 | 19 | * migrate to `@hapi/` ([adf4db1](https://github.com/bakjs/bak/commit/adf4db1)) 20 | 21 | 22 | ### BREAKING CHANGES 23 | 24 | * projects should also update using `@hapi/ ` for: hapi, hoek, joi, boom, vision, inert 25 | 26 | 27 | 28 | 29 | 30 | ## [2.1.5](https://github.com/bakjs/bak/compare/@bakjs/nunjucks@2.1.4...@bakjs/nunjucks@2.1.5) (2019-04-16) 31 | 32 | **Note:** Version bump only for package @bakjs/nunjucks 33 | 34 | 35 | 36 | 37 | 38 | ## [2.1.4](https://github.com/bakjs/bak/compare/@bakjs/nunjucks@2.1.3...@bakjs/nunjucks@2.1.4) (2019-03-09) 39 | 40 | **Note:** Version bump only for package @bakjs/nunjucks 41 | 42 | 43 | 44 | 45 | 46 | ## [2.1.3](https://github.com/bakjs/bak/compare/@bakjs/nunjucks@2.1.2...@bakjs/nunjucks@2.1.3) (2018-12-16) 47 | 48 | **Note:** Version bump only for package @bakjs/nunjucks 49 | 50 | 51 | 52 | 53 | 54 | ## [2.1.2](https://github.com/bakjs/bak/compare/@bakjs/nunjucks@0.1.3...@bakjs/nunjucks@2.1.2) (2018-11-19) 55 | 56 | **Note:** Version bump only for package @bakjs/nunjucks 57 | 58 | 59 | 60 | 61 | 62 | ## [2.1.1](https://github.com/bakjs/plugins/compare/@bakjs/nunjucks@2.1.0...@bakjs/nunjucks@2.1.1) (2018-11-04) 63 | 64 | 65 | ### Bug Fixes 66 | 67 | * **deps:** update all non-major dependencies ([#5](https://github.com/bakjs/plugins/issues/5)) ([fbfa4ad](https://github.com/bakjs/plugins/commit/fbfa4ad)) 68 | 69 | 70 | 71 | 72 | 73 | 74 | # [2.1.0](https://github.com/bakjs/plugins/compare/@bakjs/nunjucks@2.0.4...@bakjs/nunjucks@2.1.0) (2018-02-08) 75 | 76 | 77 | ### Features 78 | 79 | * export explicit configKey ([add9dfb](https://github.com/bakjs/plugins/commit/add9dfb)) 80 | 81 | 82 | 83 | 84 | 85 | ## [2.0.4](https://github.com/bakjs/plugins/compare/@bakjs/nunjucks@2.0.3...@bakjs/nunjucks@2.0.4) (2018-01-26) 86 | 87 | 88 | 89 | 90 | **Note:** Version bump only for package @bakjs/nunjucks 91 | 92 | 93 | ## [2.0.3](https://github.com/bakjs/plugins/compare/@bakjs/nunjucks@2.0.2...@bakjs/nunjucks@2.0.3) (2017-11-29) 94 | 95 | 96 | 97 | 98 | **Note:** Version bump only for package @bakjs/nunjucks 99 | 100 | 101 | ## [2.0.2](https://github.com/bakjs/plugins/compare/@bakjs/nunjucks@2.0.1...@bakjs/nunjucks@2.0.2) (2017-11-09) 102 | 103 | 104 | 105 | 106 | **Note:** Version bump only for package @bakjs/nunjucks 107 | 108 | 109 | ## [2.0.1](https://github.com/bakjs/plugins/compare/@bakjs/nunjucks@0.1.4...@bakjs/nunjucks@2.0.1) (2017-10-24) 110 | 111 | 112 | 113 | 114 | **Note:** Version bump only for package @bakjs/nunjucks 115 | 116 | 117 | ## 0.1.4 (2017-10-07) 118 | 119 | 120 | 121 | 122 | **Note:** Version bump only for package @bakjs/nunjucks 123 | 124 | 125 | ## [0.1.3](https://github.com/bakjs/bak/compare/@bakjs/nunjucks@0.1.2...@bakjs/nunjucks@0.1.3) (2017-09-16) 126 | 127 | 128 | ### Bug Fixes 129 | 130 | * **nunjucks:** add main field ([2b7eaf6](https://github.com/bakjs/bak/commit/2b7eaf6)) 131 | 132 | 133 | 134 | 135 | 136 | ## 0.1.2 (2017-09-05) 137 | 138 | 139 | 140 | 141 | **Note:** Version bump only for package @bakjs/nunjucks 142 | 143 | 144 | ## 0.1.1 (2017-09-04) 145 | 146 | 147 | 148 | 149 | **Note:** Version bump only for package @bakjs/nunjucks 150 | 151 | 152 | # 0.1.0 (2017-09-03) 153 | 154 | 155 | 156 | 157 | **Note:** Version bump only for package @bakjs/nunjucks 158 | -------------------------------------------------------------------------------- /packages/nunjucks/README.md: -------------------------------------------------------------------------------- 1 | Please refer to [bakjs/hapi-plugins](https://github.com/bakjs/hapi-plugins) for more info and docs. -------------------------------------------------------------------------------- /packages/nunjucks/lib/index.js: -------------------------------------------------------------------------------- 1 | const Vision = require('@hapi/vision') 2 | const path = require('path') 3 | const Inert = require('@hapi/inert') 4 | const NunjuksEngine = require('./nunjucks-engine') 5 | 6 | exports.register = (server, options) => { 7 | const baseDir = options.baseDir || 'resources' 8 | 9 | server.register(Vision, () => { 10 | server.root.views({ 11 | engines: Object.assign({ 12 | njk: NunjuksEngine, 13 | jinja2: NunjuksEngine 14 | }, options.engines), 15 | isCached: process.env.NODE_ENV === 'production', 16 | path: options.viewsDir || path.join(baseDir, 'views'), 17 | defaultExtension: options.defaultExtension || 'njk' 18 | }) 19 | }) 20 | 21 | if (options.serveStatic !== false) { 22 | server.register({ register: Inert }, () => { 23 | server.route({ 24 | method: 'GET', 25 | path: `${options.staticRoute || '/static'}/{param*}`, 26 | config: { 27 | auth: false, 28 | cache: { 29 | expiresIn: options.staticCache || 30 * 60 * 1000 30 | } 31 | }, 32 | handler: { 33 | directory: { 34 | path: options.staticDir || path.join(baseDir, 'static') 35 | } 36 | } 37 | }) 38 | }) 39 | } 40 | } 41 | 42 | exports.pkg = require('../package.json') 43 | exports.once = true 44 | exports.configKey = 'nunjucks' 45 | -------------------------------------------------------------------------------- /packages/nunjucks/lib/nunjucks-engine.js: -------------------------------------------------------------------------------- 1 | const Nunjucks = require('nunjucks') 2 | 3 | module.exports = { 4 | compile: (src, options) => { 5 | const template = Nunjucks.compile(src, options.environment) 6 | return function (context) { 7 | return template.render(context) 8 | } 9 | }, 10 | prepare: (options, next) => { 11 | options.compileOptions.environment = Nunjucks.configure(options.path, { watch: false }) 12 | return next() 13 | } 14 | } 15 | -------------------------------------------------------------------------------- /packages/nunjucks/package.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "@bakjs/nunjucks", 3 | "version": "3.0.1", 4 | "repository": "bakjs/bak", 5 | "license": "MIT", 6 | "main": "lib/index.js", 7 | "publishConfig": { 8 | "access": "public" 9 | }, 10 | "contributors": [ 11 | "Pooya Parsa " 12 | ], 13 | "files": [ 14 | "lib" 15 | ], 16 | "dependencies": { 17 | "@hapi/inert": "^5.1.3", 18 | "@hapi/vision": "^5.5.2", 19 | "nunjucks": "^3.2.0", 20 | "path": "^0.12.7" 21 | } 22 | } 23 | -------------------------------------------------------------------------------- /packages/policy/CHANGELOG.md: -------------------------------------------------------------------------------- 1 | # Change Log 2 | 3 | All notable changes to this project will be documented in this file. 4 | See [Conventional Commits](https://conventionalcommits.org) for commit guidelines. 5 | 6 | ## [3.0.1](https://github.com/bakjs/bak/compare/@bakjs/policy@3.0.0...@bakjs/policy@3.0.1) (2019-05-22) 7 | 8 | **Note:** Version bump only for package @bakjs/policy 9 | 10 | 11 | 12 | 13 | 14 | # [3.0.0](https://github.com/bakjs/bak/compare/@bakjs/policy@2.1.3...@bakjs/policy@3.0.0) (2019-05-22) 15 | 16 | 17 | ### Features 18 | 19 | * migrate to `@hapi/` ([adf4db1](https://github.com/bakjs/bak/commit/adf4db1)) 20 | 21 | 22 | ### BREAKING CHANGES 23 | 24 | * projects should also update using `@hapi/ ` for: hapi, hoek, joi, boom, vision, inert 25 | 26 | 27 | 28 | 29 | 30 | ## [2.1.3](https://github.com/bakjs/bak/compare/@bakjs/policy@2.1.2...@bakjs/policy@2.1.3) (2018-12-03) 31 | 32 | **Note:** Version bump only for package @bakjs/policy 33 | 34 | 35 | 36 | 37 | 38 | ## [2.1.2](https://github.com/bakjs/bak/compare/@bakjs/policy@0.1.2...@bakjs/policy@2.1.2) (2018-11-19) 39 | 40 | **Note:** Version bump only for package @bakjs/policy 41 | 42 | 43 | 44 | 45 | 46 | ## [2.1.1](https://github.com/bakjs/plugins/compare/@bakjs/policy@2.1.0...@bakjs/policy@2.1.1) (2018-11-04) 47 | 48 | 49 | ### Bug Fixes 50 | 51 | * **deps:** update all non-major dependencies ([#5](https://github.com/bakjs/plugins/issues/5)) ([fbfa4ad](https://github.com/bakjs/plugins/commit/fbfa4ad)) 52 | 53 | 54 | 55 | 56 | 57 | 58 | # [2.1.0](https://github.com/bakjs/plugins/compare/@bakjs/policy@2.0.2...@bakjs/policy@2.1.0) (2018-02-08) 59 | 60 | 61 | ### Features 62 | 63 | * export explicit configKey ([add9dfb](https://github.com/bakjs/plugins/commit/add9dfb)) 64 | 65 | 66 | 67 | 68 | 69 | ## [2.0.2](https://github.com/bakjs/plugins/compare/@bakjs/policy@2.0.1...@bakjs/policy@2.0.2) (2017-11-09) 70 | 71 | 72 | 73 | 74 | **Note:** Version bump only for package @bakjs/policy 75 | 76 | 77 | ## [2.0.1](https://github.com/bakjs/plugins/compare/@bakjs/policy@0.1.3...@bakjs/policy@2.0.1) (2017-10-24) 78 | 79 | 80 | 81 | 82 | **Note:** Version bump only for package @bakjs/policy 83 | 84 | 85 | ## 0.1.3 (2017-10-07) 86 | 87 | 88 | 89 | 90 | **Note:** Version bump only for package @bakjs/policy 91 | 92 | 93 | ## 0.1.2 (2017-09-05) 94 | 95 | 96 | 97 | 98 | **Note:** Version bump only for package @bakjs/policy 99 | 100 | 101 | ## 0.1.1 (2017-09-04) 102 | 103 | 104 | 105 | 106 | **Note:** Version bump only for package @bakjs/policy 107 | 108 | 109 | # 0.1.0 (2017-09-03) 110 | 111 | 112 | 113 | 114 | **Note:** Version bump only for package @bakjs/policy 115 | -------------------------------------------------------------------------------- /packages/policy/README.md: -------------------------------------------------------------------------------- 1 | Please refer to [bakjs/hapi-plugins](https://github.com/bakjs/hapi-plugins) for more info and docs. -------------------------------------------------------------------------------- /packages/policy/lib/authorize-plugin.js: -------------------------------------------------------------------------------- 1 | const Boom = require('@hapi/boom') 2 | 3 | exports.register = function authorizePlugin (server, options) { 4 | if (!options) options = {} 5 | const policies = options.policies || [] 6 | 7 | // General check function 8 | const check = async (user, action, target) => { 9 | if (!user || !action) return false 10 | 11 | try { 12 | // Resolve check function 13 | let check_fn = action 14 | if (!(check_fn instanceof Function)) check_fn = policies[action] 15 | if (!(check_fn instanceof Function)) { 16 | server.log(['warning', 'authorize'], { message: 'no policy defined for ' + action }) 17 | return false 18 | } 19 | 20 | // Test against check_fn 21 | let result = check_fn(user, target) 22 | 23 | // Support async policies 24 | if (result instanceof Promise) result = await result 25 | 26 | // ensure result is true or false only 27 | return !!result 28 | } catch (error) { 29 | // Log and reject unhandled errors 30 | server.log(['error', 'authorize'], { action, target, error }) 31 | return false 32 | } 33 | } 34 | 35 | // Adds request.can() decorator 36 | function can (action, target) { 37 | return check(this.auth.credentials && this.auth.credentials.user, action, target) 38 | } 39 | 40 | server.decorate('request', 'can', can) 41 | 42 | // Adds request.authorize() decorator 43 | async function authorize (action, target) { 44 | let result = await check(this.auth.credentials && this.auth.credentials.user, action, target) 45 | if (result !== true) throw Boom.unauthorized(action) 46 | return true 47 | } 48 | 49 | server.decorate('request', 'authorize', authorize) 50 | } 51 | 52 | exports.register.attributes = { 53 | name: 'bak-policy-authorize' 54 | } 55 | 56 | exports.pkg = require('../package.json') 57 | exports.name = '@bakjs/policy/authorize' 58 | exports.once = true 59 | -------------------------------------------------------------------------------- /packages/policy/lib/guard-plugin.js: -------------------------------------------------------------------------------- 1 | const Boom = require('@hapi/boom') 2 | 3 | exports.register = function (server, options) { 4 | if (!options) options = {} 5 | 6 | server.ext('onPreHandler', async (request, h) => { 7 | const route_guards = request.route.settings.plugins.guards 8 | if (!route_guards) return h.continue 9 | 10 | try { 11 | for (let i = 0; i < route_guards.length; i++) { 12 | if (!(await request.can(route_guards[i]))) { 13 | throw Boom.unauthorized(route_guards[i].name || route_guards[i]) 14 | } 15 | } 16 | } catch (error) { 17 | // Log and reject unhandled errors 18 | server.log(['error', 'authorize', 'guard'], { error }) 19 | throw Boom.unauthorized() 20 | } 21 | 22 | return h.continue 23 | }) 24 | } 25 | 26 | exports.pkg = require('../package.json') 27 | exports.name = '@bakjs/policy/guard' 28 | exports.once = true 29 | -------------------------------------------------------------------------------- /packages/policy/lib/index.js: -------------------------------------------------------------------------------- 1 | const GuardPlugin = require('./guard-plugin') 2 | const AuthorizePlugin = require('./authorize-plugin') 3 | 4 | exports.register = function (server, options) { 5 | if (!options) options = {} 6 | 7 | server.register({ plugin: GuardPlugin, options }) 8 | 9 | server.register({ plugin: AuthorizePlugin, options }) 10 | } 11 | 12 | exports.pkg = require('../package.json') 13 | exports.once = true 14 | exports.configKey = 'policy' 15 | -------------------------------------------------------------------------------- /packages/policy/package.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "@bakjs/policy", 3 | "version": "3.0.1", 4 | "repository": "bakjs/bak", 5 | "license": "MIT", 6 | "main": "lib/index.js", 7 | "publishConfig": { 8 | "access": "public" 9 | }, 10 | "contributors": [ 11 | "Pooya Parsa " 12 | ], 13 | "files": [ 14 | "lib" 15 | ], 16 | "dependencies": { 17 | "@hapi/boom": "^7.4.2" 18 | } 19 | } 20 | -------------------------------------------------------------------------------- /packages/ratelimit/CHANGELOG.md: -------------------------------------------------------------------------------- 1 | # Change Log 2 | 3 | All notable changes to this project will be documented in this file. 4 | See [Conventional Commits](https://conventionalcommits.org) for commit guidelines. 5 | 6 | ## [3.0.1](https://github.com/bakjs/bak/compare/@bakjs/ratelimit@3.0.0...@bakjs/ratelimit@3.0.1) (2019-05-22) 7 | 8 | **Note:** Version bump only for package @bakjs/ratelimit 9 | 10 | 11 | 12 | 13 | 14 | # [3.0.0](https://github.com/bakjs/bak/compare/@bakjs/ratelimit@2.1.1...@bakjs/ratelimit@3.0.0) (2019-05-22) 15 | 16 | 17 | ### Features 18 | 19 | * migrate to `@hapi/` ([adf4db1](https://github.com/bakjs/bak/commit/adf4db1)) 20 | 21 | 22 | ### BREAKING CHANGES 23 | 24 | * projects should also update using `@hapi/ ` for: hapi, hoek, joi, boom, vision, inert 25 | 26 | 27 | 28 | 29 | 30 | ## [2.1.1](https://github.com/bakjs/bak/compare/@bakjs/ratelimit@0.1.4...@bakjs/ratelimit@2.1.1) (2018-11-19) 31 | 32 | **Note:** Version bump only for package @bakjs/ratelimit 33 | 34 | 35 | 36 | 37 | 38 | 39 | # [2.1.0](https://github.com/bakjs/plugins/compare/@bakjs/ratelimit@2.0.1...@bakjs/ratelimit@2.1.0) (2018-02-08) 40 | 41 | 42 | ### Features 43 | 44 | * export explicit configKey ([add9dfb](https://github.com/bakjs/plugins/commit/add9dfb)) 45 | 46 | 47 | 48 | 49 | 50 | ## [2.0.1](https://github.com/bakjs/plugins/compare/@bakjs/ratelimit@0.1.5...@bakjs/ratelimit@2.0.1) (2017-10-24) 51 | 52 | 53 | 54 | 55 | **Note:** Version bump only for package @bakjs/ratelimit 56 | 57 | 58 | ## 0.1.5 (2017-10-07) 59 | 60 | 61 | 62 | 63 | **Note:** Version bump only for package @bakjs/ratelimit 64 | 65 | 66 | ## [0.1.4](https://github.com/bakjs/bak/compare/@bakjs/ratelimit@0.1.3...@bakjs/ratelimit@0.1.4) (2017-09-06) 67 | 68 | 69 | ### Bug Fixes 70 | 71 | * require bak as peer dependency ([3336177](https://github.com/bakjs/bak/commit/3336177)) 72 | 73 | 74 | 75 | 76 | 77 | ## 0.1.3 (2017-09-05) 78 | 79 | 80 | 81 | 82 | **Note:** Version bump only for package @bakjs/ratelimit 83 | 84 | 85 | ## 0.1.2 (2017-09-04) 86 | 87 | 88 | 89 | 90 | **Note:** Version bump only for package @bakjs/ratelimit 91 | 92 | 93 | ## [0.1.1](https://github.com/bakjs/bak/compare/@bakjs/ratelimit@0.1.0...@bakjs/ratelimit@0.1.1) (2017-09-03) 94 | 95 | 96 | 97 | 98 | **Note:** Version bump only for package @bakjs/ratelimit 99 | 100 | 101 | # 0.1.0 (2017-09-03) 102 | 103 | 104 | 105 | 106 | **Note:** Version bump only for package @bakjs/ratelimit 107 | -------------------------------------------------------------------------------- /packages/ratelimit/README.md: -------------------------------------------------------------------------------- 1 | Please refer to [bakjs/hapi-plugins](https://github.com/bakjs/hapi-plugins) for more info and docs. -------------------------------------------------------------------------------- /packages/ratelimit/lib/index.js: -------------------------------------------------------------------------------- 1 | const Boom = require('@hapi/boom') 2 | const Hoek = require('@hapi/hoek') 3 | 4 | exports.register = function HapiRateLimit (plugin, _options) { 5 | // Apply default options 6 | const options = Hoek.applyToDefaults(defaults, _options) 7 | 8 | // Create limiter instance 9 | // const limiter = new RedisRateLimit(options) 10 | let Driver = options.driver 11 | if (typeof Driver === 'string') { 12 | Driver = require('./providers/' + Driver) 13 | } 14 | const limiter = new Driver(options) 15 | 16 | const handleLimits = async (request, h) => { 17 | const route = request.route 18 | 19 | // Get route-specific limits 20 | let routeLimit = route.settings.plugins && route.settings.plugins.ratelimit 21 | 22 | // Try to apply global if no options 23 | if (options.global) { 24 | routeLimit = Object.assign({}, options.global, routeLimit) 25 | } 26 | 27 | // If no limits on route 28 | if (!routeLimit) { 29 | return h.continue 30 | } 31 | 32 | // Check limits on route 33 | const rateLimit = await limiter.check( 34 | options.namespace + ':' + realIP(request) + ':' + (request.route.id || request.route.path), 35 | routeLimit.limit, 36 | routeLimit.duration 37 | ) 38 | 39 | request.plugins.ratelimit = { 40 | limit: rateLimit.limit, 41 | remaining: rateLimit.remaining - 1, 42 | reset: rateLimit.reset 43 | } 44 | 45 | if (rateLimit.remaining > 0) { 46 | return h.continue 47 | } 48 | 49 | const error = Boom.tooManyRequests('RATE_LIMIT_EXCEEDED') 50 | setHeaders(error.output.headers, request.plugins.ratelimit, options.XHeaders) 51 | error.reformat() 52 | return error 53 | } 54 | 55 | const responseLimits = (request, h) => { 56 | if (request.plugins.ratelimit) { 57 | const response = request.response 58 | if (!response.isBoom) { 59 | setHeaders(response.headers, request.plugins.ratelimit, options.XHeaders) 60 | } 61 | } 62 | return h.continue 63 | } 64 | 65 | // Ext 66 | plugin.ext('onPreAuth', handleLimits) 67 | plugin.ext('onPostHandler', responseLimits) 68 | } 69 | 70 | // Set rate-limit headers 71 | function setHeaders (headers, ratelimit, XHeaders = false) { 72 | if (XHeaders) { 73 | headers['X-Rate-Limit-Limit'] = ratelimit.limit 74 | headers['X-Rate-Limit-Remaining'] = ratelimit.remaining > 0 ? ratelimit.remaining : 0 75 | headers['X-Rate-Limit-Reset'] = ratelimit.reset 76 | } 77 | 78 | // https://developer.mozilla.org/en-US/docs/Web/HTTP/Headers/Retry-After 79 | headers['Retry-After'] = Math.ceil(ratelimit.reset / 1000) 80 | } 81 | 82 | // Default options 83 | const defaults = { 84 | namespace: 'ratelimit', 85 | driver: 'memory', 86 | XHeaders: false, 87 | global: { 88 | limit: 60, 89 | duration: 60000 90 | } 91 | } 92 | 93 | function realIP (request) { 94 | return request.ip || request.headers['x-real-ip'] || request.headers['x-forwarded-for'] || request.info['remoteAddress'] 95 | } 96 | 97 | exports.pkg = require('../package.json') 98 | exports.once = true 99 | exports.configKey = 'ratelimit' 100 | -------------------------------------------------------------------------------- /packages/ratelimit/lib/providers/memory.js: -------------------------------------------------------------------------------- 1 | class MemoryRateLimit { 2 | constructor (options = {}) { 3 | this.options = options 4 | this.limits = {} 5 | } 6 | 7 | check (id, limit, duration) { 8 | const now = Date.now() 9 | 10 | // If first time 11 | if (!this.limits[id]) { 12 | this.limits[id] = { used: 0, time: now } 13 | } 14 | 15 | // Get status 16 | const status = this.limits[id] 17 | 18 | // Calculate reset time 19 | const reset = (status.time + duration) - now 20 | 21 | if (reset <= 0) { 22 | // Time to reset 23 | status.used = 0 24 | status.time = now 25 | return this.check(id, limit, duration) 26 | } 27 | 28 | // Calculate remaining 29 | const remaining = limit - status.used 30 | 31 | // +1 used 32 | status.used++ 33 | 34 | return { limit, remaining, reset } 35 | } 36 | } 37 | 38 | module.exports = MemoryRateLimit 39 | -------------------------------------------------------------------------------- /packages/ratelimit/lib/providers/redis.js: -------------------------------------------------------------------------------- 1 | const Redis = require('redis') 2 | const Limiter = require('ratelimiter') 3 | 4 | class RedisRateLimit { 5 | constructor (options = { redis: {} }) { 6 | this.options = options 7 | 8 | this.redisClient = Redis.createClient( 9 | options.redis.port, 10 | options.redis.host, 11 | options.redis.options 12 | ) 13 | } 14 | 15 | check (id, limit, duration) { 16 | const routeLimiter = new Limiter({ id, max: limit, duration }) 17 | 18 | return new Promise((resolve, reject) => { 19 | routeLimiter.get((err, rateLimit) => { 20 | if (err) { 21 | return reject(err) 22 | } 23 | resolve({ 24 | limit: rateLimit.total, 25 | remaining: rateLimit.remaining, 26 | reset: rateLimit.reset 27 | }) 28 | }) 29 | }) 30 | } 31 | } 32 | 33 | module.exports = RedisRateLimit 34 | -------------------------------------------------------------------------------- /packages/ratelimit/package.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "@bakjs/ratelimit", 3 | "version": "3.0.1", 4 | "repository": "bakjs/bak", 5 | "license": "MIT", 6 | "main": "lib/index.js", 7 | "publishConfig": { 8 | "access": "public" 9 | }, 10 | "contributors": [ 11 | "Pooya Parsa " 12 | ], 13 | "files": [ 14 | "lib" 15 | ] 16 | } 17 | -------------------------------------------------------------------------------- /packages/route-table/CHANGELOG.md: -------------------------------------------------------------------------------- 1 | # Change Log 2 | 3 | All notable changes to this project will be documented in this file. 4 | See [Conventional Commits](https://conventionalcommits.org) for commit guidelines. 5 | 6 | ## [2.4.2](https://github.com/bakjs/bak/compare/@bakjs/route-table@2.4.1...@bakjs/route-table@2.4.2) (2019-04-16) 7 | 8 | **Note:** Version bump only for package @bakjs/route-table 9 | 10 | 11 | 12 | 13 | 14 | ## [2.4.1](https://github.com/bakjs/bak/compare/@bakjs/route-table@2.4.0...@bakjs/route-table@2.4.1) (2019-03-09) 15 | 16 | **Note:** Version bump only for package @bakjs/route-table 17 | 18 | 19 | 20 | 21 | 22 | # 2.4.0 (2018-11-19) 23 | 24 | 25 | ### Features 26 | 27 | * hide scope from route-table ([e958efa](https://github.com/bakjs/bak/commit/e958efa)) 28 | 29 | 30 | 31 | 32 | 33 | # [2.3.0](https://github.com/bakjs/plugins/compare/@bakjs/route-table@2.2.0...@bakjs/route-table@2.3.0) (2018-11-04) 34 | 35 | 36 | ### Bug Fixes 37 | 38 | * **deps:** update all non-major dependencies ([#5](https://github.com/bakjs/plugins/issues/5)) ([fbfa4ad](https://github.com/bakjs/plugins/commit/fbfa4ad)) 39 | 40 | 41 | ### Features 42 | 43 | * update major dependencies ([0d75f39](https://github.com/bakjs/plugins/commit/0d75f39)) 44 | 45 | 46 | 47 | 48 | 49 | 50 | # [2.2.0](https://github.com/bakjs/plugins/compare/@bakjs/route-table@2.1.1...@bakjs/route-table@2.2.0) (2018-02-08) 51 | 52 | 53 | ### Features 54 | 55 | * export explicit configKey ([add9dfb](https://github.com/bakjs/plugins/commit/add9dfb)) 56 | 57 | 58 | 59 | 60 | 61 | ## [2.1.1](https://github.com/bakjs/plugins/compare/@bakjs/route-table@2.1.0...@bakjs/route-table@2.1.1) (2017-11-09) 62 | 63 | 64 | 65 | 66 | **Note:** Version bump only for package @bakjs/route-table 67 | 68 | 69 | # 2.1.0 (2017-10-24) 70 | 71 | 72 | 73 | 74 | # 2.0.0 (2017-10-19) 75 | 76 | 77 | ### Features 78 | 79 | * new plugins from core ([d9cc60d](https://github.com/bakjs/plugins/commit/d9cc60d)) 80 | -------------------------------------------------------------------------------- /packages/route-table/README.md: -------------------------------------------------------------------------------- 1 | Please refer to [bakjs/hapi-plugins](https://github.com/bakjs/hapi-plugins) for more info and docs. -------------------------------------------------------------------------------- /packages/route-table/lib/index.js: -------------------------------------------------------------------------------- 1 | const Chalk = require('chalk') 2 | const _ = require('lodash') 3 | const Table = require('./table') 4 | 5 | const NONE = Chalk.grey('▪') 6 | const COLORS = ['yellow', 'green', 'magenta', 'red', 'blue', 'cyan'] 7 | const METHODS = ['GET', 'POST', 'DELETE', 'PUT', 'PATCH'] 8 | const AUTH_MAP = { 9 | required: Chalk.green('✔'), 10 | optional: Chalk.yellow('OPTIONAL'), 11 | try: Chalk.red('TRY'), 12 | '': NONE 13 | } 14 | 15 | function colorize (str, collection, shift = 0, alt) { 16 | let color = COLORS[((collection.indexOf(str) + 1) + shift) % COLORS.length] 17 | if (!Chalk[color]) return colorize(str, collection, shift + 1, alt) 18 | return Chalk[color](alt || str) 19 | } 20 | 21 | function method_color (method, alt) { 22 | return colorize(method, METHODS, 0, alt) 23 | } 24 | 25 | function printRouteTable (routes) { 26 | // Extract routes 27 | let _routes = routes.map((route) => { 28 | let controller, fn 29 | let id = route.settings.id || '' 30 | let s = id.split('.') 31 | 32 | controller = (s[0] && s[0].length) ? s[0] : 'Default' 33 | s.shift() 34 | fn = s.length ? s.join('_') : NONE 35 | 36 | // Auth 37 | let auth = NONE 38 | if (route.settings.auth) { 39 | auth = route.settings.auth.mode 40 | } 41 | 42 | return ({ 43 | Method: route.method.toUpperCase(), 44 | Path: route.path, 45 | Auth: AUTH_MAP[auth] || AUTH_MAP[''], 46 | Controller: controller, 47 | Function: fn 48 | }) 49 | }) 50 | 51 | // Sort before formatting 52 | _routes.sort((a, b) => { 53 | if (a.Controller > b.Controller) return 1 54 | if (a.Controller < b.Controller) return -1 55 | 56 | if (a.Path > b.Path) return 1 57 | if (a.Path < b.Path) return -1 58 | 59 | let aI = METHODS.indexOf(a.Method) 60 | let bI = METHODS.indexOf(b.Method) 61 | if (aI > bI) return 1 62 | if (aI < bI) return -1 63 | 64 | return 0 65 | }) 66 | 67 | // Colorize 68 | _routes.forEach((route) => { 69 | const m = route.Method 70 | route.Method = `[ ${Chalk(method_color(m))} ]` 71 | route.Path = method_color(m, route.Path) 72 | route.Function = Chalk.grey(route.Function) 73 | }) 74 | 75 | // Separate by controller 76 | let groupedRoutes = _.groupBy(_routes, 'Controller') 77 | _routes.forEach(r => { 78 | delete r.Controller 79 | }) 80 | 81 | let groupedArray = [] 82 | Object.keys(groupedRoutes).forEach(controller => { 83 | if (controller[0] === '_') { 84 | return 85 | } 86 | groupedArray.push(null) 87 | groupedArray.push(controller) 88 | groupedArray.push(null) 89 | groupedArray = groupedArray.concat(groupedRoutes[controller]) 90 | }) 91 | 92 | Table.printTable(groupedArray, 'llcl', null, { indent: 0, rowSpace: 1 }) 93 | 94 | console.log() 95 | } 96 | 97 | // Export plugin 98 | exports.register = function bakRouteTable (server, config) { 99 | server.ext('onPostStart', (_server) => { 100 | printRouteTable(_server.table()) 101 | }) 102 | } 103 | 104 | exports.pkg = require('../package.json') 105 | exports.once = true 106 | exports.configKey = 'routeTable' 107 | -------------------------------------------------------------------------------- /packages/route-table/lib/table.js: -------------------------------------------------------------------------------- 1 | /** 2 | * The MIT License (MIT) 3 | * 4 | * Copyright (c) 2015 Sebastian Ullrich 5 | * 6 | * Permission is hereby granted, free of charge, to any person obtaining a copy 7 | * of this software and associated documentation files (the "Software"), to deal 8 | * in the Software without restriction, including without limitation the rights 9 | * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 10 | * copies of the Software, and to permit persons to whom the Software is 11 | * furnished to do so, subject to the following conditions: 12 | * 13 | * The above copyright notice and this permission notice shall be included in all 14 | * copies or substantial portions of the Software. 15 | * 16 | * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 17 | * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 18 | * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 19 | * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 20 | * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 21 | * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE 22 | * SOFTWARE. 23 | */ 24 | 25 | // Based on table-master 26 | 27 | const _ = require('lodash') 28 | const chalk = require('chalk') 29 | const stripAnsi = require('strip-ansi') 30 | 31 | /** 32 | * Default (white)space constant. 33 | * 34 | * @type {string} 35 | */ 36 | const SPACE = '\u0020' 37 | 38 | /** 39 | * The default settings. 40 | * 41 | * @type {{indent: number, rowSpace: number}} 42 | */ 43 | const defaultSettings = { 44 | indent: 3, 45 | rowSpace: 1 46 | } 47 | 48 | /** 49 | * Function to overload the default settings. 50 | * 51 | * @param newDefaults The new default settings. 52 | */ 53 | const setDefaults = function (newDefaults) { 54 | Object.assign(defaultSettings, newDefaults) 55 | } 56 | 57 | /** 58 | * Creates an empty string with the specified length. 59 | * 60 | * @param {number} length The length of the string. 61 | * @returns {string} The string with the specified length. 62 | */ 63 | function emptyString (length) { 64 | return new Array(++length).join(SPACE) 65 | } 66 | 67 | /** 68 | * Removes all colour commands from a given string. 69 | * 70 | * @param str The string to be cleaned. 71 | * @returns {string} The cleaned string. 72 | */ 73 | function cleanString (str) { 74 | return stripAnsi(str.toString()) 75 | } 76 | 77 | /** 78 | * Returns all keys for a given object. 79 | * 80 | * @param objArray Object to get the keys of. 81 | * @returns {*} Array, containing all keys as string values. 82 | */ 83 | function getAllKeys (objArray) { 84 | let keys = [] 85 | _.forEach(objArray, function (row) { 86 | if (!row || typeof row === 'string') return 87 | keys = keys.concat(Object.keys(row)) 88 | }) 89 | return _.union(keys) 90 | } 91 | 92 | /** 93 | * Determines the longest value for each key. 94 | * 95 | * @param keys The keys of the objects within the array. 96 | * @param objArray The object array. 97 | * @returns {Object} JSON object containing the max length for each key. 98 | */ 99 | function getMaxLength (keys, objArray) { 100 | const maxRowLength = {} 101 | _.forEach(keys, function (key) { 102 | maxRowLength[key] = cleanString(key).length 103 | }) 104 | 105 | _.forEach(objArray, function (objRow) { 106 | _.forEach(objRow, function (val, key) { 107 | const rowLength = cleanString(val).length 108 | if (maxRowLength[key] < rowLength) { 109 | maxRowLength[key] = rowLength 110 | } 111 | }) 112 | }) 113 | 114 | return maxRowLength 115 | } 116 | 117 | /** 118 | * Trims/extends a given string to the specified length. 119 | * If string is too long it will be trimmed with elepsies. 120 | * 121 | * @param str The string to be trimmed/extended 122 | * @param length The desired length of the string 123 | * @param format The align of the string, whether (l)eft, (r)ight or (c)enter aligned. 124 | * @returns {string} The trimmed/extended and aligned string. 125 | */ 126 | function toLength (str, length, format) { 127 | if (!str) { 128 | return emptyString(length) 129 | } 130 | let newStr = str.toString() 131 | let diff = cleanString(str).length - length 132 | if (diff < 0) { 133 | diff *= -1 134 | } 135 | if (!format || format === 'l') { 136 | newStr = newStr + emptyString(diff) 137 | } else if (format === 'r') { 138 | newStr = emptyString(diff) + newStr 139 | } else { // (format === "c") 140 | const s = diff / 2 141 | newStr = emptyString(Math.ceil(s)) + newStr + emptyString(Math.floor(s)) 142 | } 143 | return newStr 144 | } 145 | 146 | /** 147 | * Prints a given array of json objects. 148 | * @param printArray The array of json objects to be printed. 149 | * @param format 150 | * @param preProcessor 151 | * @param settings 152 | */ 153 | const printTable = function (printArray, format, preProcessor, settings) { 154 | format = format || '' 155 | preProcessor = preProcessor || [] 156 | settings = settings || defaultSettings 157 | 158 | const INDENT = emptyString(settings.indent) 159 | const ROW_SPACER = emptyString(settings.rowSpace) 160 | 161 | const headings = getAllKeys(printArray) 162 | const maxLength = getMaxLength(headings, printArray) 163 | const maxLengths = Object.keys(maxLength).reduce((s, k) => s + maxLength[k], 0) 164 | 165 | // print headline 166 | const headline = [] 167 | const seperator = [] 168 | 169 | _.forEach(headings, function (header, i) { 170 | headline.push(toLength(header, maxLength[header], 'c')) 171 | seperator.push(new Array(maxLength[header] + 1).join('-')) 172 | }) 173 | 174 | console.log(INDENT + seperator.join(ROW_SPACER)) 175 | console.log(INDENT + headline.join(ROW_SPACER)) 176 | console.log(INDENT + seperator.join(ROW_SPACER)) 177 | 178 | // print rows 179 | _.forEach(printArray, function (row) { 180 | const line = [] 181 | 182 | if (row === null || typeof row === 'string') { 183 | if (row === null || row === '') { 184 | return console.log('') 185 | } 186 | return console.log(chalk.grey.bold(toLength(row || '', maxLengths, 'l'))) 187 | } 188 | 189 | _.forEach(headings, function (header, i) { 190 | let str = row[header] || '' 191 | 192 | if (_.isFunction(preProcessor[i])) { 193 | str = preProcessor[i](str) || str 194 | } 195 | 196 | line.push(toLength(str, maxLength[header], format.substr(i, 1))) 197 | }) 198 | console.log(INDENT + line.join(ROW_SPACER)) 199 | }) 200 | } 201 | 202 | /** 203 | * Exports the printTable function via module export. 204 | * @type {Function} The printTable function. 205 | */ 206 | exports.printTable = printTable 207 | 208 | /** 209 | * Exports the setDefaults function via module export. 210 | * @type {Function} The setDefaults function. 211 | */ 212 | exports.setDefaults = setDefaults 213 | -------------------------------------------------------------------------------- /packages/route-table/package.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "@bakjs/route-table", 3 | "version": "2.4.2", 4 | "repository": "bakjs/bak", 5 | "license": "MIT", 6 | "main": "lib/index.js", 7 | "publishConfig": { 8 | "access": "public" 9 | }, 10 | "contributors": [ 11 | "Pooya Parsa " 12 | ], 13 | "files": [ 14 | "lib" 15 | ], 16 | "dependencies": { 17 | "chalk": "^2.4.2", 18 | "lodash": "^4.17.11", 19 | "strip-ansi": "^5.2.0" 20 | } 21 | } 22 | -------------------------------------------------------------------------------- /packages/shortcuts/CHANGELOG.md: -------------------------------------------------------------------------------- 1 | # Change Log 2 | 3 | All notable changes to this project will be documented in this file. 4 | See [Conventional Commits](https://conventionalcommits.org) for commit guidelines. 5 | 6 | ## 2.2.1 (2018-11-19) 7 | 8 | **Note:** Version bump only for package @bakjs/shortcuts 9 | 10 | 11 | 12 | 13 | 14 | 15 | # [2.2.0](https://github.com/bakjs/plugins/compare/@bakjs/shortcuts@2.1.0...@bakjs/shortcuts@2.2.0) (2018-02-08) 16 | 17 | 18 | ### Features 19 | 20 | * export explicit configKey ([add9dfb](https://github.com/bakjs/plugins/commit/add9dfb)) 21 | 22 | 23 | 24 | 25 | 26 | # 2.1.0 (2017-10-24) 27 | 28 | 29 | 30 | 31 | # 2.0.0 (2017-10-19) 32 | 33 | 34 | ### Features 35 | 36 | * new plugins from core ([d9cc60d](https://github.com/bakjs/plugins/commit/d9cc60d)) 37 | -------------------------------------------------------------------------------- /packages/shortcuts/README.md: -------------------------------------------------------------------------------- 1 | Please refer to [bakjs/hapi-plugins](https://github.com/bakjs/hapi-plugins) for more info and docs. -------------------------------------------------------------------------------- /packages/shortcuts/lib/index.js: -------------------------------------------------------------------------------- 1 | exports.register = function bakShortcuts (server, config) { 2 | server.ext('onPreHandler', (request, h) => { 3 | request.user = request.auth.credentials ? request.auth.credentials.user : null 4 | request.session = request.auth.artifacts 5 | request.ip = realIP(request) 6 | 7 | return h.continue 8 | }) 9 | } 10 | 11 | exports.pkg = require('../package.json') 12 | exports.once = true 13 | exports.configKey = 'shortcuts' 14 | 15 | function realIP (request) { 16 | return request.ip || request.headers['x-real-ip'] || request.headers['x-forwarded-for'] || request.info['remoteAddress'] 17 | } 18 | -------------------------------------------------------------------------------- /packages/shortcuts/package.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "@bakjs/shortcuts", 3 | "version": "2.2.1", 4 | "repository": "bakjs/bak", 5 | "license": "MIT", 6 | "main": "lib/index.js", 7 | "publishConfig": { 8 | "access": "public" 9 | }, 10 | "contributors": [ 11 | "Pooya Parsa " 12 | ], 13 | "files": [ 14 | "lib" 15 | ] 16 | } 17 | -------------------------------------------------------------------------------- /renovate.json: -------------------------------------------------------------------------------- 1 | { 2 | "extends": [ 3 | "@nuxtjs" 4 | ] 5 | } 6 | -------------------------------------------------------------------------------- /test/_setup.js: -------------------------------------------------------------------------------- 1 | jasmine.DEFAULT_TIMEOUT_INTERVAL = 20000 2 | process.env.SUPPRESS_NO_CONFIG_WARNING = true 3 | 4 | const axios = require('axios') 5 | const { Bak } = require('bak') 6 | 7 | function setup (name) { 8 | // Load bak.config.js for fixture 9 | const config = require(`./fixtures/${name}/bak.config`) 10 | 11 | // Create new Bak instance 12 | this.bak = new Bak(config) 13 | 14 | // Helpers 15 | this.url = path => `http://localhost:${config.server.port}${path}` 16 | this.get = (url, ...args) => axios.get(this.url(url), ...args).then(r => r.data) 17 | 18 | this.init = () => test('init server', async () => { 19 | await this.bak.init() 20 | }) 21 | 22 | this.stop = () => test('stop server', async () => { 23 | await this.bak.server.hapi.stop() 24 | }) 25 | } 26 | 27 | module.exports = setup 28 | -------------------------------------------------------------------------------- /test/bak.spec.js: -------------------------------------------------------------------------------- 1 | const setup = require('./_setup') 2 | 3 | describe('bak', () => { 4 | setup.call(this, 'basic') 5 | 6 | this.init() 7 | 8 | test('/api/hello/world', async () => { 9 | const res = await this.get('/api/hello/world!') 10 | expect(res).toBe('Hello world!') 11 | }) 12 | 13 | this.stop() 14 | }) 15 | -------------------------------------------------------------------------------- /test/fixtures/basic/bak.config.js: -------------------------------------------------------------------------------- 1 | module.exports = { 2 | relativeTo: __dirname, 3 | prefix: '/api', 4 | routes: [ 5 | './controllers/api' 6 | ], 7 | server: { 8 | port: '6565' 9 | } 10 | } 11 | -------------------------------------------------------------------------------- /test/fixtures/basic/controllers/api.js: -------------------------------------------------------------------------------- 1 | import Joi from '@hapi/joi' 2 | import { Controller } from 'bak' 3 | 4 | export default class APIController extends Controller { 5 | init () { 6 | this.defaults = { 7 | validate: { 8 | payload: { 9 | foo: Joi.string() 10 | } 11 | } 12 | } 13 | 14 | this.get('/hello/{name}', this.hello) 15 | } 16 | 17 | hello (request, reply) { 18 | return 'Hello ' + request.params.name 19 | } 20 | } 21 | -------------------------------------------------------------------------------- /test/fixtures/basic/package.json: -------------------------------------------------------------------------------- 1 | { 2 | "private": true, 3 | "dependencies": { 4 | "bak": "latest" 5 | } 6 | } 7 | --------------------------------------------------------------------------------