├── .gitignore ├── README.md ├── docker-compose.yaml ├── front-end ├── .dockerignore ├── .gitattributes ├── .gitignore ├── Dockerfile ├── LICENSE ├── k8s │ ├── front-end-deployment.yml │ └── front-end-service.yml ├── nginx.conf ├── package-lock.json ├── package.json ├── public │ ├── favicon.ico │ ├── icons │ │ ├── 192.png │ │ ├── 48.png │ │ ├── 512.png │ │ └── 96.png │ ├── index.html │ └── manifest.json ├── src │ ├── App.js │ ├── Login.jsx │ ├── NavBar.jsx │ ├── Notification.js │ ├── Product.jsx │ ├── ProductEditor.jsx │ ├── ProductList.jsx │ ├── api.js │ ├── appStore.js │ ├── autoBlur.js │ ├── index.css │ ├── index.js │ └── registerServiceWorker.js └── yarn.lock ├── gateway ├── .dockerignore ├── .env.example ├── .gitignore ├── Dockerfile ├── LICENSE ├── api.js ├── auth.js ├── config.js ├── index.js ├── k8s │ ├── gateway-deployment.yml │ ├── gateway-secret.yml │ ├── gateway-service.yml │ └── ingress.yml ├── package-lock.json └── package.json ├── product ├── .dockerignore ├── .gitignore ├── Dockerfile ├── api │ ├── index.js │ └── products │ │ ├── delete.js │ │ ├── get.js │ │ ├── index.js │ │ ├── post.js │ │ └── put.js ├── config │ ├── db.js │ └── server.js ├── dal │ └── productService.js ├── db.js ├── index.js ├── k8s │ ├── product-deployment.yml │ ├── product-secret.yml │ └── product-service.yml ├── knexfile.js ├── migrate.js ├── migrations │ └── 20180518140440_create-products-table.js ├── package-lock.json ├── package.json ├── scripts │ ├── build.sh │ ├── patch.json │ └── patch.sh ├── seeds │ └── dummy-products.js ├── server.js └── web.js ├── scripts ├── build ├── migrate └── up └── user ├── .dockerignore ├── .gitignore ├── Dockerfile ├── api ├── index.js ├── login.js └── register.js ├── config ├── db.js └── server.js ├── dal └── userService.js ├── db.js ├── index.js ├── k8s ├── user-deployment.yml ├── user-secret.yml └── user-service.yml ├── knexfile.js ├── migrate.js ├── migrations └── 20180523144152_create-users-table.js ├── package-lock.json ├── package.json ├── server.js ├── utils ├── db-helper.js ├── hashPass.js └── retry.js └── web.js /.gitignore: -------------------------------------------------------------------------------- 1 | # Logs 2 | logs 3 | *.log 4 | npm-debug.log* 5 | yarn-debug.log* 6 | yarn-error.log* 7 | 8 | # Runtime data 9 | pids 10 | *.pid 11 | *.seed 12 | *.pid.lock 13 | 14 | # Directory for instrumented libs generated by jscoverage/JSCover 15 | lib-cov 16 | 17 | # Coverage directory used by tools like istanbul 18 | coverage 19 | 20 | # nyc test coverage 21 | .nyc_output 22 | 23 | # Grunt intermediate storage (http://gruntjs.com/creating-plugins#storing-task-files) 24 | .grunt 25 | 26 | # Bower dependency directory (https://bower.io/) 27 | bower_components 28 | 29 | # node-waf configuration 30 | .lock-wscript 31 | 32 | # Compiled binary addons (http://nodejs.org/api/addons.html) 33 | build/Release 34 | build/ 35 | 36 | # Dependency directories 37 | node_modules/ 38 | jspm_packages/ 39 | 40 | # Typescript v1 declaration files 41 | typings/ 42 | 43 | # Optional npm cache directory 44 | .npm 45 | 46 | # Optional eslint cache 47 | .eslintcache 48 | 49 | # Optional REPL history 50 | .node_repl_history 51 | 52 | # Output of 'npm pack' 53 | *.tgz 54 | 55 | # Yarn Integrity file 56 | .yarn-integrity 57 | 58 | # dotenv environment variables file 59 | .env 60 | 61 | img-version-history -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | # Microservice Training 2 | 3 | ## Prerequisites 4 | 5 | - [docker](https://www.docker.com/) 6 | - [docker-compose](https://docs.docker.com/compose/) 7 | - [kubectl](https://kubernetes.io/docs/tasks/tools/install-kubectl/) - kubernetes manager CLI 8 | - [minikube](https://github.com/kubernetes/minikube) - local virtualized kubernetes 9 | 10 | ```bash 11 | $ minikube start # start local kubernetes 12 | $ eval $(minikube docker-env) # connect docker to minikube machine 13 | $ minikube addons enable ingress # we will need to use ingress resources 14 | $ docker-compose up -d # start databases 15 | ``` 16 | 17 | Before you begin, stop all docker daemon processes on your local machine to avoid collision. We are going to use a VirtualBox virtual 18 | machine to run docker. 19 | 20 | Running databases inside of the "cluster" is not part of the current training session. To emulate hosted database 21 | environments we created a docker-comopse script that runs a postgresql database on the minikube machine but outside of 22 | kubernetes. 23 | 24 | Easy mode: 25 | 26 | ```sh 27 | ./scripts/up 28 | ``` 29 | 30 | If you encounter any problems: follow the guide below. 31 | 32 | Navigate to http://localhost:5433 to view the database browser (pgweb). By default it will create a database for the 33 | one of the services. Navigate to the query tab and run the following query: 34 | 35 | ```sql 36 | CREATE DATABASE "test-users-db"; 37 | ``` 38 | 39 | ## User service 40 | 41 | ```bash 42 | $ cd user 43 | $ npm run build # build the image 44 | $ kubectl create -f ./k8s/ # create all the necessary kubernetes resources 45 | $ kubectl get pods # verify that they are running 46 | ``` 47 | 48 | ## Product service 49 | 50 | ```bash 51 | $ cd product 52 | $ npm run build # build the image 53 | $ kubectl create -f ./k8s/ # create all the necessary kubernetes resources 54 | $ kubectl get pods # verify that they are running 55 | ``` 56 | 57 | ### Migrate db 58 | 59 | ```bash 60 | ./scripts/migrate 61 | ``` 62 | 63 | ## Front-end 64 | 65 | ```bash 66 | $ cd front-end 67 | $ npm run docker-build 68 | ``` 69 | 70 | Be patient that can take 30 minutes even. 71 | 72 | ## Gateway 73 | 74 | ```bash 75 | $ cd gateway 76 | $ npm run build # builds current image 77 | $ kubectl create -f ./k8s/` 78 | ``` 79 | 80 | In case of image pull error, your docker environment is not pointed to kubernetes, so kubernetes cannot pull it from 81 | there. Try rebuilding the container from the previous step and the edit the kubernetes deployment with: 82 | `kubectl edit gateway` 83 | 84 | ## Windows users notice 85 | 86 | Docker for windows includes kubernetes, so instead of using minikube we can start using the kubernetes service provided by docker. 87 | 88 | Ingress is not included by default, we have to include the following command: 89 | https://github.com/docker/for-win/issues/1901 90 | -------------------------------------------------------------------------------- /docker-compose.yaml: -------------------------------------------------------------------------------- 1 | version: '3.3' 2 | services: 3 | db: 4 | image: postgres:10.4-alpine 5 | restart: always 6 | environment: 7 | POSTGRES_USER: root 8 | POSTGRES_PASSWORD: root 9 | POSTGRES_DB: test-products-db 10 | ports: 11 | - 5432:5432 12 | pgweb: 13 | image: sosedoff/pgweb 14 | restart: always 15 | environment: 16 | - DATABASE_URL=postgres://root:root@db:5432/test-products-db?sslmode=disable 17 | ports: 18 | - 5433:8081 19 | links: 20 | - db 21 | -------------------------------------------------------------------------------- /front-end/.dockerignore: -------------------------------------------------------------------------------- 1 | node_modules/ 2 | -------------------------------------------------------------------------------- /front-end/.gitattributes: -------------------------------------------------------------------------------- 1 | package-lock.json binary 2 | -------------------------------------------------------------------------------- /front-end/.gitignore: -------------------------------------------------------------------------------- 1 | # Logs 2 | logs 3 | *.log 4 | npm-debug.log* 5 | yarn-debug.log* 6 | yarn-error.log* 7 | 8 | # Production build 9 | /build 10 | 11 | # Runtime data 12 | pids 13 | *.pid 14 | *.seed 15 | *.pid.lock 16 | 17 | # Directory for instrumented libs generated by jscoverage/JSCover 18 | lib-cov 19 | 20 | # Coverage directory used by tools like istanbul 21 | coverage 22 | 23 | # nyc test coverage 24 | .nyc_output 25 | 26 | # Grunt intermediate storage (http://gruntjs.com/creating-plugins#storing-task-files) 27 | .grunt 28 | 29 | # Bower dependency directory (https://bower.io/) 30 | bower_components 31 | 32 | # node-waf configuration 33 | .lock-wscript 34 | 35 | # Compiled binary addons (http://nodejs.org/api/addons.html) 36 | build/Release 37 | 38 | # Dependency directories 39 | node_modules/ 40 | jspm_packages/ 41 | 42 | # Typescript v1 declaration files 43 | typings/ 44 | 45 | # Optional npm cache directory 46 | .npm 47 | 48 | # Optional eslint cache 49 | .eslintcache 50 | 51 | # Optional REPL history 52 | .node_repl_history 53 | 54 | # Output of 'npm pack' 55 | *.tgz 56 | 57 | # Yarn Integrity file 58 | .yarn-integrity 59 | 60 | # dotenv environment variables file 61 | .env 62 | 63 | -------------------------------------------------------------------------------- /front-end/Dockerfile: -------------------------------------------------------------------------------- 1 | FROM node:boron-stretch as builder 2 | COPY package.json package.json 3 | RUN npm install 4 | COPY . . 5 | RUN npm run build 6 | 7 | FROM nginx 8 | COPY --from=builder build/ /usr/share/nginx/www/ 9 | COPY nginx.conf /etc/nginx/nginx.conf 10 | -------------------------------------------------------------------------------- /front-end/LICENSE: -------------------------------------------------------------------------------- 1 | MIT License 2 | 3 | Copyright (c) 2018 Miklos Bertalan 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. 22 | -------------------------------------------------------------------------------- /front-end/k8s/front-end-deployment.yml: -------------------------------------------------------------------------------- 1 | apiVersion: apps/v1beta2 2 | kind: Deployment 3 | metadata: 4 | labels: 5 | app: front-end 6 | name: front-end 7 | spec: 8 | selector: 9 | matchLabels: 10 | app: front-end 11 | template: 12 | metadata: 13 | labels: 14 | app: front-end 15 | spec: 16 | containers: 17 | - image: rs-ms/front-end 18 | name: front-end 19 | imagePullPolicy: IfNotPresent 20 | -------------------------------------------------------------------------------- /front-end/k8s/front-end-service.yml: -------------------------------------------------------------------------------- 1 | apiVersion: v1 2 | kind: Service 3 | metadata: 4 | labels: 5 | app: front-end 6 | name: front-end 7 | spec: 8 | ports: 9 | - port: 3000 10 | protocol: TCP 11 | targetPort: 3000 12 | selector: 13 | app: front-end 14 | type: ClusterIP 15 | -------------------------------------------------------------------------------- /front-end/nginx.conf: -------------------------------------------------------------------------------- 1 | events { } 2 | 3 | http { 4 | include mime.types; 5 | sendfile on; 6 | gzip on; 7 | 8 | server { 9 | root /usr/share/nginx/www; 10 | listen 3000; 11 | server_name localhost; 12 | index index.html index.html; 13 | location / { 14 | try_files $uri /index.html; 15 | } 16 | } 17 | 18 | include servers/*; 19 | } 20 | -------------------------------------------------------------------------------- /front-end/package.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "generic-concrete-salad", 3 | "version": "0.1.0", 4 | "private": true, 5 | "dependencies": { 6 | "axios": "0.18.0", 7 | "lodash": "4.17.5", 8 | "material-ui": "1.0.0-beta.4", 9 | "material-ui-icons": "1.0.0-beta.35", 10 | "material-ui-search-bar": "1.0.0-beta.4", 11 | "now-purge": "0.0.15", 12 | "react": "16.2.0", 13 | "react-dom": "16.2.0", 14 | "react-easy-stack": "0.1.0-alpha.10", 15 | "react-scripts": "1.1.1", 16 | "serve": "6.5.0", 17 | "typeface-roboto": "0.0.54" 18 | }, 19 | "scripts": { 20 | "start": "react-scripts start", 21 | "build": "react-scripts build", 22 | "test": "react-scripts test --env=jsdom", 23 | "eject": "react-scripts eject", 24 | "now-start": "serve build --single --cache 0", 25 | "deploy": "git add -A && git commit -m 'add code' && git push origin master && now && now alias", 26 | "purge-deployments": "now-purge -n generic-concrete-salad", 27 | "docker-build": "../scripts/build \"front-end\"" 28 | }, 29 | "now": { 30 | "alias": "generic-concrete-salad" 31 | } 32 | } 33 | -------------------------------------------------------------------------------- /front-end/public/favicon.ico: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/RisingStack/training-microservices-v3/bc8970e9a4dfeee53490e7769eb02dfafc56f081/front-end/public/favicon.ico -------------------------------------------------------------------------------- /front-end/public/icons/192.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/RisingStack/training-microservices-v3/bc8970e9a4dfeee53490e7769eb02dfafc56f081/front-end/public/icons/192.png -------------------------------------------------------------------------------- /front-end/public/icons/48.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/RisingStack/training-microservices-v3/bc8970e9a4dfeee53490e7769eb02dfafc56f081/front-end/public/icons/48.png -------------------------------------------------------------------------------- /front-end/public/icons/512.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/RisingStack/training-microservices-v3/bc8970e9a4dfeee53490e7769eb02dfafc56f081/front-end/public/icons/512.png -------------------------------------------------------------------------------- /front-end/public/icons/96.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/RisingStack/training-microservices-v3/bc8970e9a4dfeee53490e7769eb02dfafc56f081/front-end/public/icons/96.png -------------------------------------------------------------------------------- /front-end/public/index.html: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | 7 | 11 | 12 | 13 | 22 | React App 23 | 24 | 25 | 28 |
29 |
30 | 40 | 41 | 42 | -------------------------------------------------------------------------------- /front-end/public/manifest.json: -------------------------------------------------------------------------------- 1 | { 2 | "short_name": "React App", 3 | "name": "Create React App Sample", 4 | "icons": [ 5 | { 6 | "src": "favicon.ico", 7 | "sizes": "64x64 32x32 24x24 16x16", 8 | "type": "image/x-icon" 9 | } 10 | ], 11 | "start_url": "./index.html", 12 | "display": "standalone", 13 | "theme_color": "#000000", 14 | "background_color": "#ffffff" 15 | } 16 | -------------------------------------------------------------------------------- /front-end/src/App.js: -------------------------------------------------------------------------------- 1 | import React, { Component, Fragment } from 'react'; 2 | import { Router, view, params } from 'react-easy-stack'; 3 | import Button from 'material-ui/Button'; 4 | import NavBar from './NavBar'; 5 | import ProductList from './ProductList'; 6 | import ProductEditor from './ProductEditor'; 7 | import Login from './Login'; 8 | import appStore, * as app from './appStore'; 9 | import Notification, { notify } from './Notification'; 10 | 11 | const appStyle = { 12 | maxWidth: 800, 13 | margin: '50px auto', 14 | padding: 20 15 | }; 16 | 17 | const enterAnimation = { 18 | keyframes: { 19 | opacity: [0, 1], 20 | transform: ['translateX(-10px)', 'translateX(5)', 'none'] 21 | }, 22 | duration: 150 23 | }; 24 | 25 | const leaveAnimation = { 26 | keyframes: { 27 | opacity: [1, 0], 28 | transform: ['none', 'translateX(10px)'] 29 | }, 30 | duration: 50 31 | }; 32 | 33 | class App extends Component { 34 | searchProducts = async () => { 35 | await app.search(params.search); 36 | }; 37 | 38 | onRoute = ({ toPage, preventDefault }) => { 39 | if (toPage === 'product' && !appStore.isLoggedIn) { 40 | preventDefault({ to: '/login' }); 41 | notify('Please log in to see the product page'); 42 | } 43 | }; 44 | 45 | render() { 46 | return ( 47 | 48 | 49 | 57 | 62 | 63 | 64 | 65 | 66 | 67 | ); 68 | } 69 | } 70 | 71 | export default view(App); 72 | -------------------------------------------------------------------------------- /front-end/src/Login.jsx: -------------------------------------------------------------------------------- 1 | import React, { Component } from 'react'; 2 | import { view, store, route } from 'react-easy-stack'; 3 | import { FormGroup } from 'material-ui/Form'; 4 | import Button from 'material-ui/Button'; 5 | import TextField from 'material-ui/TextField'; 6 | import * as app from './appStore'; 7 | 8 | const buttonStyle = { 9 | marginTop: 10 10 | }; 11 | 12 | class Login extends Component { 13 | store = store(); 14 | 15 | onChange = ev => { 16 | this.store[ev.target.name] = ev.target.value; 17 | }; 18 | 19 | onLogin = async () => { 20 | await app.login(this.store); 21 | route({ to: '/' }); 22 | }; 23 | 24 | onRegister = async () => { 25 | await app.register(this.store); 26 | route({ to: '/' }); 27 | }; 28 | 29 | render() { 30 | return ( 31 | 32 | 40 | 48 | 55 | 63 | 66 | 67 | ); 68 | } 69 | } 70 | 71 | export default view(Login); 72 | -------------------------------------------------------------------------------- /front-end/src/NavBar.jsx: -------------------------------------------------------------------------------- 1 | import React, { Component } from 'react'; 2 | import AppBar from 'material-ui/AppBar'; 3 | import Toolbar from 'material-ui/Toolbar'; 4 | import SearchBar from 'material-ui-search-bar'; 5 | import { LinearProgress } from 'material-ui/Progress'; 6 | import Button from 'material-ui/Button'; 7 | import { view, params, path, route, Link } from 'react-easy-stack'; 8 | import { notify } from './Notification'; 9 | import appStore, * as app from './appStore'; 10 | 11 | const toolbarStyle = { 12 | width: 800, 13 | maxWidth: '100%', 14 | margin: '0 auto', 15 | padding: '3 10px', 16 | display: 'flex', 17 | justifyContent: 'space-between' 18 | }; 19 | 20 | const searchStyle = { 21 | width: '50%', 22 | minWidth: '180px' 23 | }; 24 | 25 | class NavBar extends Component { 26 | onSearch = search => { 27 | route({ 28 | to: 'products', 29 | params: { search }, 30 | options: { history: true, animate: search !== params.search } 31 | }); 32 | }; 33 | 34 | onLogout = () => { 35 | app.logout(); 36 | route({ to: '/login' }); 37 | if (path[0] === 'product') { 38 | notify('Please log in to see the product page'); 39 | } 40 | }; 41 | 42 | render() { 43 | const { isLoggedIn, isLoading } = appStore; 44 | 45 | return ( 46 | 47 | 48 | 53 | 60 | 61 | {isLoading && } 62 | 63 | ); 64 | } 65 | } 66 | 67 | export default view(NavBar); 68 | -------------------------------------------------------------------------------- /front-end/src/Notification.js: -------------------------------------------------------------------------------- 1 | import React from 'react'; 2 | import { view, store } from 'react-easy-stack'; 3 | import Snackbar from 'material-ui/Snackbar'; 4 | 5 | const notificationStore = store({ 6 | message: '', 7 | action: undefined, 8 | isOpen: false 9 | }); 10 | 11 | export function notify(message, action) { 12 | notificationStore.message = message; 13 | notificationStore.action = action; 14 | notificationStore.isOpen = true; 15 | } 16 | 17 | function closeNotification() { 18 | notificationStore.message = ''; 19 | notificationStore.action = undefined; 20 | notificationStore.isOpen = false; 21 | } 22 | 23 | export default view(() => ( 24 | 31 | )); 32 | -------------------------------------------------------------------------------- /front-end/src/Product.jsx: -------------------------------------------------------------------------------- 1 | import React, { Component } from 'react'; 2 | import { view, Link } from 'react-easy-stack'; 3 | import Card, { CardContent } from 'material-ui/Card'; 4 | 5 | const productStyle = { 6 | width: 350, 7 | maxWidth: '95%', 8 | margin: '10px 20px' 9 | }; 10 | 11 | class Product extends Component { 12 | render() { 13 | const { name, description, id } = this.props.product; 14 | return ( 15 | 16 | 17 | 18 |

{name}

19 |

{description}

20 |
21 | 22 |
23 | ); 24 | } 25 | } 26 | 27 | export default view(Product); 28 | -------------------------------------------------------------------------------- /front-end/src/ProductEditor.jsx: -------------------------------------------------------------------------------- 1 | import React, { Component } from 'react'; 2 | import { view, store, params, route } from 'react-easy-stack'; 3 | import { FormGroup, FormControlLabel } from 'material-ui/Form'; 4 | import Button from 'material-ui/Button'; 5 | import TextField from 'material-ui/TextField'; 6 | import Checkbox from 'material-ui/Checkbox'; 7 | import appStore, * as app from './appStore'; 8 | 9 | const productShell = { 10 | name: '', 11 | description: '', 12 | price: 0, 13 | currency: 'USD', 14 | available: undefined 15 | }; 16 | 17 | class ProductEditor extends Component { 18 | store = store({ 19 | changes: params.id ? {} : productShell 20 | }); 21 | 22 | onChange = ev => { 23 | this.store.changes[ev.target.name] = ev.target.value; 24 | }; 25 | 26 | onCheckChange = ev => { 27 | this.store.changes[ev.target.name] = ev.target.checked; 28 | }; 29 | 30 | onSave = async () => { 31 | if (params.id) { 32 | await app.editProduct(params.id, this.store.changes); 33 | } else { 34 | const product = await app.saveProduct(this.store.changes); 35 | } 36 | route({ to: '/products' }); 37 | }; 38 | 39 | render() { 40 | const { product } = this.props; 41 | const { changes } = this.store; 42 | const { name, description, price, available } = Object.assign( 43 | {}, 44 | product, 45 | changes 46 | ); 47 | const label = params.id ? 'Edit Product' : 'Add Product'; 48 | 49 | return ( 50 | 51 | 58 | 66 | 74 | 82 | } 83 | label="Avaliable" 84 | /> 85 | 88 | 89 | ); 90 | } 91 | } 92 | 93 | export default view(ProductEditor); 94 | -------------------------------------------------------------------------------- /front-end/src/ProductList.jsx: -------------------------------------------------------------------------------- 1 | import React, { Component, Fragment } from 'react'; 2 | import { view, storage, path, params, Link } from 'react-easy-stack'; 3 | import ReactDOM from 'react-dom'; 4 | import Button from 'material-ui/Button'; 5 | import AddIcon from 'material-ui-icons/Add'; 6 | import appStore from './appStore'; 7 | import Product from './Product'; 8 | 9 | const listStyle = { 10 | display: 'flex', 11 | alignItems: 'stretch', 12 | justifyContent: 'space-around', 13 | flexWrap: 'wrap', 14 | margin: '0 -20px' 15 | }; 16 | 17 | const addButtonStyle = { 18 | position: 'fixed', 19 | right: 20, 20 | bottom: 20 21 | }; 22 | 23 | function ProductList({ pageResolved }) { 24 | const products = pageResolved 25 | ? appStore.products 26 | : storage.cache[params.search] || []; 27 | 28 | return ( 29 | 30 |
31 | {products.map(product => ( 32 | 33 | ))} 34 |
35 | {appStore.isLoggedIn && 36 | ReactDOM.createPortal( 37 | 38 | 41 | , 42 | document.getElementById('action-button') 43 | )} 44 |
45 | ); 46 | } 47 | 48 | export default view(ProductList); 49 | -------------------------------------------------------------------------------- /front-end/src/api.js: -------------------------------------------------------------------------------- 1 | import axios from 'axios'; 2 | import appStore, * as app from './appStore'; 3 | import { pick, defaults } from 'lodash'; 4 | import { storage } from 'react-easy-stack'; 5 | import { notify } from './Notification'; 6 | 7 | defaults(storage, { 8 | cache: {} 9 | }); 10 | 11 | const api = axios.create({ 12 | baseURL: '/api/', 13 | headers: { 14 | Authorization: `Bearer ${storage.token}`, 15 | 'Content-Type': 'application/json' 16 | } 17 | }); 18 | 19 | // TODO: it should handle errors, replace this with a counter 20 | api.interceptors.request.use(config => { 21 | appStore.isLoading = true; 22 | return config; 23 | }); 24 | 25 | api.interceptors.response.use( 26 | response => { 27 | appStore.isLoading = false; 28 | return response; 29 | }, 30 | error => { 31 | appStore.isLoading = false; 32 | throw error; 33 | } 34 | ); 35 | 36 | export async function search(filter) { 37 | const { data } = await api.get('/products', { 38 | params: { search: filter } 39 | }); 40 | storage.cache[filter] = data.products; 41 | return data.products; 42 | } 43 | 44 | export async function fetchProduct(id) { 45 | const { data } = await api.get(`/products/${id}`); 46 | return data; 47 | } 48 | 49 | export async function saveProduct(product) { 50 | const { data } = await api.post('/products', product); 51 | return data; 52 | } 53 | 54 | export async function editProduct(id, product) { 55 | const { data } = await api.put(`/products/${id}`, product); 56 | return data; 57 | } 58 | 59 | export async function login(loginData) { 60 | loginData = pick(loginData, ['email', 'pass']); 61 | const { data } = await api.post('/users/login', loginData); 62 | api.defaults.headers.token = data.token; 63 | storage.token = data.token; 64 | return data.user; 65 | } 66 | 67 | export async function register(registerData) { 68 | await api.post('/users/register', registerData); 69 | return login(registerData); 70 | } 71 | 72 | export function logout() { 73 | delete api.defaults.headers.token; 74 | delete storage.token; 75 | } 76 | 77 | export function isLoggedIn() { 78 | return 'token' in storage; 79 | } 80 | -------------------------------------------------------------------------------- /front-end/src/appStore.js: -------------------------------------------------------------------------------- 1 | import { store, params } from 'react-easy-stack'; 2 | import * as api from './api'; 3 | 4 | const appStore = store({ 5 | products: [], 6 | isLoading: false, 7 | isLoggedIn: api.isLoggedIn() 8 | }); 9 | 10 | export async function search(filter) { 11 | appStore.products = await api.search(filter); 12 | } 13 | 14 | export async function resolveProduct() { 15 | if (!params.id) { 16 | return { product: {} }; 17 | } 18 | return { product: await api.fetchProduct(params.id) }; 19 | } 20 | 21 | export async function saveProduct(product) { 22 | return api.saveProduct(product); 23 | } 24 | 25 | export async function editProduct(id, data) { 26 | return api.editProduct(id, data); 27 | } 28 | 29 | export async function login(loginData) { 30 | appStore.user = await api.login(loginData); 31 | appStore.isLoggedIn = true; 32 | } 33 | 34 | export async function logout() { 35 | await api.logout(); 36 | appStore.isLoggedIn = false; 37 | } 38 | 39 | export async function register(registerData) { 40 | appStore.user = await api.register(registerData); 41 | } 42 | 43 | export default appStore; 44 | -------------------------------------------------------------------------------- /front-end/src/autoBlur.js: -------------------------------------------------------------------------------- 1 | export default function autoBlur () { 2 | window.addEventListener('keypress', ev => { 3 | const element = ev.target 4 | const isEnter = ev.charCode === 13 || ev.which === 13 || ev.keyCode === 13 5 | const isInput = element.matches('input, select') 6 | if (isInput && isEnter) { 7 | if (element.form) { 8 | const inputs = Array.from( 9 | element.form.querySelectorAll('input, textarea, select') 10 | ) 11 | const inputIndex = inputs.indexOf(element) 12 | const nextInput = inputs[inputIndex + 1] 13 | if (nextInput) { 14 | nextInput.focus() 15 | nextInput.select() 16 | } else { 17 | element.blur() 18 | } 19 | } else { 20 | element.blur() 21 | } 22 | } 23 | }) 24 | } 25 | -------------------------------------------------------------------------------- /front-end/src/index.css: -------------------------------------------------------------------------------- 1 | body { 2 | margin: 0; 3 | padding: 0; 4 | } 5 | 6 | * { 7 | font-family: 'Roboto', sans-serif; 8 | box-sizing: border-box; 9 | } 10 | 11 | a { 12 | color: inherit; 13 | text-decoration: none; 14 | } 15 | -------------------------------------------------------------------------------- /front-end/src/index.js: -------------------------------------------------------------------------------- 1 | import React from 'react' 2 | import ReactDOM from 'react-dom' 3 | import './index.css' 4 | import App from './App' 5 | import registerServiceWorker from './registerServiceWorker' 6 | import autoBlur from './autoBlur' 7 | 8 | ReactDOM.render(, document.getElementById('root')) 9 | registerServiceWorker() 10 | // autoBlur(); 11 | -------------------------------------------------------------------------------- /front-end/src/registerServiceWorker.js: -------------------------------------------------------------------------------- 1 | // In production, we register a service worker to serve assets from local cache. 2 | 3 | // This lets the app load faster on subsequent visits in production, and gives 4 | // it offline capabilities. However, it also means that developers (and users) 5 | // will only see deployed updates on the "N+1" visit to a page, since previously 6 | // cached resources are updated in the background. 7 | 8 | // To learn more about the benefits of this model, read https://goo.gl/KwvDNy. 9 | // This link also includes instructions on opting out of this behavior. 10 | 11 | import React from 'react'; 12 | import Button from 'material-ui/Button'; 13 | import { notify } from './Notification'; 14 | 15 | const isLocalhost = Boolean( 16 | window.location.hostname === 'localhost' || 17 | // [::1] is the IPv6 localhost address. 18 | window.location.hostname === '[::1]' || 19 | // 127.0.0.1/8 is considered localhost for IPv4. 20 | window.location.hostname.match( 21 | /^127(?:\.(?:25[0-5]|2[0-4][0-9]|[01]?[0-9][0-9]?)){3}$/ 22 | ) 23 | ); 24 | 25 | export default function register() { 26 | if (process.env.NODE_ENV === 'production' && 'serviceWorker' in navigator) { 27 | // The URL constructor is available in all browsers that support SW. 28 | const publicUrl = new URL(process.env.PUBLIC_URL, window.location); 29 | if (publicUrl.origin !== window.location.origin) { 30 | // Our service worker won't work if PUBLIC_URL is on a different origin 31 | // from what our page is served on. This might happen if a CDN is used to 32 | // serve assets; see https://github.com/facebookincubator/create-react-app/issues/2374 33 | return; 34 | } 35 | 36 | window.addEventListener('load', () => { 37 | const swUrl = `${process.env.PUBLIC_URL}/service-worker.js`; 38 | 39 | if (isLocalhost) { 40 | // This is running on localhost. Lets check if a service worker still exists or not. 41 | checkValidServiceWorker(swUrl); 42 | 43 | // Add some additional logging to localhost, pointing developers to the 44 | // service worker/PWA documentation. 45 | navigator.serviceWorker.ready.then(() => { 46 | console.log( 47 | 'This web app is being served cache-first by a service ' + 48 | 'worker. To learn more, visit https://goo.gl/SC7cgQ' 49 | ); 50 | }); 51 | } else { 52 | // Is not local host. Just register service worker 53 | registerValidSW(swUrl); 54 | } 55 | }); 56 | } 57 | } 58 | 59 | function registerValidSW(swUrl) { 60 | navigator.serviceWorker 61 | .register(swUrl) 62 | .then(registration => { 63 | registration.onupdatefound = () => { 64 | const installingWorker = registration.installing; 65 | installingWorker.onstatechange = () => { 66 | if (installingWorker.state === 'installed') { 67 | if (navigator.serviceWorker.controller) { 68 | // At this point, the old content will have been purged and 69 | // the fresh content will have been added to the cache. 70 | // It's the perfect time to display a "New content is 71 | // available; please refresh." message in your web app. 72 | notify( 73 | 'New content is available, please refresh.', 74 | 80 | ); 81 | } else { 82 | // At this point, everything has been precached. 83 | // It's the perfect time to display a 84 | // "Content is cached for offline use." message. 85 | notify('Content is cached for offline use.'); 86 | } 87 | } 88 | }; 89 | }; 90 | }) 91 | .catch(error => { 92 | console.error('Error during service worker registration:', error); 93 | }); 94 | } 95 | 96 | function checkValidServiceWorker(swUrl) { 97 | // Check if the service worker can be found. If it can't reload the page. 98 | fetch(swUrl) 99 | .then(response => { 100 | // Ensure service worker exists, and that we really are getting a JS file. 101 | if ( 102 | response.status === 404 || 103 | response.headers.get('content-type').indexOf('javascript') === -1 104 | ) { 105 | // No service worker found. Probably a different app. Reload the page. 106 | navigator.serviceWorker.ready.then(registration => { 107 | registration.unregister().then(() => { 108 | window.location.reload(); 109 | }); 110 | }); 111 | } else { 112 | // Service worker found. Proceed as normal. 113 | registerValidSW(swUrl); 114 | } 115 | }) 116 | .catch(() => { 117 | notify('No internet connection found. App is running in offline mode.'); 118 | }); 119 | } 120 | 121 | export function unregister() { 122 | if ('serviceWorker' in navigator) { 123 | navigator.serviceWorker.ready.then(registration => { 124 | registration.unregister(); 125 | }); 126 | } 127 | } 128 | 129 | window.addEventListener('online', () => notify('You are online again.')); 130 | window.addEventListener('offline', () => 131 | notify('No internet connection found. App is running in offline mode.') 132 | ); 133 | -------------------------------------------------------------------------------- /gateway/.dockerignore: -------------------------------------------------------------------------------- 1 | node_modules -------------------------------------------------------------------------------- /gateway/.env.example: -------------------------------------------------------------------------------- 1 | PORT=3000 2 | JWT_SECRET=secret 3 | 4 | # Upstream services 5 | PRODUCTS_API_URL= 6 | USERS_API_URL= 7 | -------------------------------------------------------------------------------- /gateway/.gitignore: -------------------------------------------------------------------------------- 1 | # Logs 2 | logs 3 | *.log 4 | npm-debug.log* 5 | yarn-debug.log* 6 | yarn-error.log* 7 | 8 | # Runtime data 9 | pids 10 | *.pid 11 | *.seed 12 | *.pid.lock 13 | 14 | # Directory for instrumented libs generated by jscoverage/JSCover 15 | lib-cov 16 | 17 | # Coverage directory used by tools like istanbul 18 | coverage 19 | 20 | # nyc test coverage 21 | .nyc_output 22 | 23 | # Grunt intermediate storage (http://gruntjs.com/creating-plugins#storing-task-files) 24 | .grunt 25 | 26 | # Bower dependency directory (https://bower.io/) 27 | bower_components 28 | 29 | # node-waf configuration 30 | .lock-wscript 31 | 32 | # Compiled binary addons (http://nodejs.org/api/addons.html) 33 | build/Release 34 | 35 | # Dependency directories 36 | node_modules/ 37 | jspm_packages/ 38 | 39 | # Typescript v1 declaration files 40 | typings/ 41 | 42 | # Optional npm cache directory 43 | .npm 44 | 45 | # Optional eslint cache 46 | .eslintcache 47 | 48 | # Optional REPL history 49 | .node_repl_history 50 | 51 | # Output of 'npm pack' 52 | *.tgz 53 | 54 | # Yarn Integrity file 55 | .yarn-integrity 56 | 57 | # dotenv environment variables file 58 | .env 59 | 60 | img-version-history -------------------------------------------------------------------------------- /gateway/Dockerfile: -------------------------------------------------------------------------------- 1 | FROM node:8.9.4 2 | 3 | COPY package.json package.json 4 | RUN npm install 5 | 6 | # Add your source files 7 | COPY . . 8 | 9 | ENV NODE_ENV production 10 | ENV PORT 3000 11 | 12 | EXPOSE 3000 13 | 14 | CMD ["node", "index.js", "--use-strict"] -------------------------------------------------------------------------------- /gateway/LICENSE: -------------------------------------------------------------------------------- 1 | BSD 3-Clause License 2 | 3 | Copyright (c) 2018, RisingStack 4 | All rights reserved. 5 | 6 | Redistribution and use in source and binary forms, with or without 7 | modification, are permitted provided that the following conditions are met: 8 | 9 | * Redistributions of source code must retain the above copyright notice, this 10 | list of conditions and the following disclaimer. 11 | 12 | * Redistributions in binary form must reproduce the above copyright notice, 13 | this list of conditions and the following disclaimer in the documentation 14 | and/or other materials provided with the distribution. 15 | 16 | * Neither the name of the copyright holder nor the names of its 17 | contributors may be used to endorse or promote products derived from 18 | this software without specific prior written permission. 19 | 20 | THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" 21 | AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE 22 | IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE 23 | DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE 24 | FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL 25 | DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR 26 | SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER 27 | CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, 28 | OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE 29 | OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. 30 | -------------------------------------------------------------------------------- /gateway/api.js: -------------------------------------------------------------------------------- 1 | const url = require('url') 2 | const express = require('express') 3 | const axios = require('axios') 4 | const proxy = require('express-http-proxy') 5 | const cors = require('cors') 6 | 7 | const { productsAPIURL, usersAPIURL } = require('./config') 8 | const auth = require('./auth') 9 | 10 | const api = express() 11 | 12 | // api.use(cors()) 13 | 14 | // TODO use route versioning 15 | api.post('/users/register', proxy(usersAPIURL)) 16 | api.post( 17 | '/users/login', 18 | proxy(usersAPIURL, { 19 | proxyReqPathResolver: function (req) { 20 | return '/login' 21 | }, 22 | userResDecorator: function (proxyRes, proxyResData, userReq, userRes) { 23 | if (proxyRes.statusCode === 200) { 24 | console.log(proxyResData.toString('UTF-8')) 25 | const { id: userId, username, email } = JSON.parse( 26 | proxyResData.toString('UTF-8') 27 | ) 28 | 29 | const isAdmin = true 30 | 31 | return JSON.stringify({ 32 | message: 'Successful login', 33 | token: auth.sign({ userId, isAdmin }), 34 | user: { 35 | username, 36 | email, 37 | isAdmin 38 | } 39 | }) 40 | } 41 | return proxyResData 42 | } 43 | }) 44 | ) 45 | 46 | api.get('/products', proxy(productsAPIURL)) 47 | api.post('/products', auth.middleware, proxy(productsAPIURL)) 48 | api.get('/products/:id', auth.middleware, proxy(productsAPIURL)) 49 | api.delete('/products/:id', auth.middleware, proxy(productsAPIURL)) 50 | api.put('/products/:id', auth.middleware, proxy(productsAPIURL)) 51 | 52 | module.exports = api 53 | -------------------------------------------------------------------------------- /gateway/auth.js: -------------------------------------------------------------------------------- 1 | const jwtMiddleware = require('express-jwt') 2 | const jwtSigner = require('jsonwebtoken') 3 | const axios = require('axios') 4 | 5 | const { jwtSecret: secret } = require('./config') 6 | 7 | const middleware = jwtMiddleware({ secret }) 8 | 9 | function sign (payload) { 10 | return jwtSigner.sign(payload, secret) 11 | } 12 | 13 | module.exports = { 14 | middleware, 15 | sign 16 | } 17 | -------------------------------------------------------------------------------- /gateway/config.js: -------------------------------------------------------------------------------- 1 | const { 2 | PORT: serverPort, 3 | PRODUCT_WEB_SERVICE_HOST, 4 | PRODUCT_WEB_SERVICE_PORT, 5 | USER_WEB_SERVICE_HOST, 6 | USER_WEB_SERVICE_PORT, 7 | JWT_SECRET: jwtSecret 8 | } = process.env 9 | 10 | const productsAPIURL = `${PRODUCT_WEB_SERVICE_HOST}:${PRODUCT_WEB_SERVICE_PORT}` 11 | const usersAPIURL = `${USER_WEB_SERVICE_HOST}:${USER_WEB_SERVICE_PORT}` 12 | 13 | module.exports = { 14 | serverPort, 15 | productsAPIURL, 16 | usersAPIURL, 17 | jwtSecret 18 | } 19 | -------------------------------------------------------------------------------- /gateway/index.js: -------------------------------------------------------------------------------- 1 | const express = require('express') 2 | 3 | const api = require('./api') 4 | const { serverPort, productsAPIURL, usersAPIURL } = require('./config') 5 | 6 | const app = express() 7 | 8 | app.get('/', (req, res) => res.send('Hello, World!')) 9 | app.use('/api', api) 10 | 11 | const listener = app.listen(serverPort, err => { 12 | if (err) { 13 | console.error(err) 14 | process.exit(1) 15 | } 16 | 17 | console.log( 18 | 'Server is listening on port http://localhost:%d', 19 | listener.address().port 20 | ) 21 | console.log('Server will be looking for productService at %s', productsAPIURL) 22 | console.log('Server will be looking for userService at %s', usersAPIURL) 23 | }) 24 | -------------------------------------------------------------------------------- /gateway/k8s/gateway-deployment.yml: -------------------------------------------------------------------------------- 1 | apiVersion: apps/v1beta2 2 | kind: Deployment 3 | metadata: 4 | labels: 5 | app: gateway 6 | name: gateway 7 | spec: 8 | selector: 9 | matchLabels: 10 | app: gateway 11 | template: 12 | metadata: 13 | labels: 14 | app: gateway 15 | spec: 16 | containers: 17 | - image: rs-ms/gateway 18 | name: gateway 19 | imagePullPolicy: IfNotPresent 20 | env: 21 | - name: JWT_SECRET 22 | valueFrom: 23 | secretKeyRef: 24 | name: gateway 25 | key: jwtSecret 26 | -------------------------------------------------------------------------------- /gateway/k8s/gateway-secret.yml: -------------------------------------------------------------------------------- 1 | apiVersion: v1 2 | data: 3 | jwtSecret: bXktc2VjcmV0 4 | kind: Secret 5 | metadata: 6 | name: gateway -------------------------------------------------------------------------------- /gateway/k8s/gateway-service.yml: -------------------------------------------------------------------------------- 1 | apiVersion: v1 2 | kind: Service 3 | metadata: 4 | labels: 5 | app: gateway 6 | name: gateway 7 | spec: 8 | ports: 9 | - port: 3000 10 | protocol: TCP 11 | targetPort: 3000 12 | selector: 13 | app: gateway 14 | type: ClusterIP 15 | -------------------------------------------------------------------------------- /gateway/k8s/ingress.yml: -------------------------------------------------------------------------------- 1 | apiVersion: extensions/v1beta1 2 | kind: Ingress 3 | metadata: 4 | name: rs-ingress 5 | spec: 6 | rules: 7 | - http: 8 | paths: 9 | - backend: 10 | serviceName: gateway 11 | servicePort: 3000 12 | path: /api 13 | - backend: 14 | serviceName: front-end 15 | servicePort: 3000 16 | path: / 17 | 18 | -------------------------------------------------------------------------------- /gateway/package-lock.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "freebie-api-server-api-gateway", 3 | "version": "1.0.0", 4 | "lockfileVersion": 1, 5 | "requires": true, 6 | "dependencies": { 7 | "abbrev": { 8 | "version": "1.1.1", 9 | "resolved": "https://registry.npmjs.org/abbrev/-/abbrev-1.1.1.tgz", 10 | "integrity": "sha512-nne9/IiQ/hzIhY6pdDnbBtz7DjPTKrY00P/zvPSm5pOFkl6xuGrGnXn/VtTNNfNtAfZ9/1RtehkszU9qcTii0Q==", 11 | "dev": true 12 | }, 13 | "accepts": { 14 | "version": "1.3.5", 15 | "resolved": "https://registry.npmjs.org/accepts/-/accepts-1.3.5.tgz", 16 | "integrity": "sha1-63d99gEXI6OxTopywIBcjoZ0a9I=", 17 | "requires": { 18 | "mime-types": "2.1.18", 19 | "negotiator": "0.6.1" 20 | } 21 | }, 22 | "ansi-align": { 23 | "version": "2.0.0", 24 | "resolved": "https://registry.npmjs.org/ansi-align/-/ansi-align-2.0.0.tgz", 25 | "integrity": "sha1-w2rsy6VjuJzrVW82kPCx2eNUf38=", 26 | "dev": true, 27 | "requires": { 28 | "string-width": "2.1.1" 29 | } 30 | }, 31 | "ansi-regex": { 32 | "version": "3.0.0", 33 | "resolved": "https://registry.npmjs.org/ansi-regex/-/ansi-regex-3.0.0.tgz", 34 | "integrity": "sha1-7QMXwyIGT3lGbAKWa922Bas32Zg=", 35 | "dev": true 36 | }, 37 | "ansi-styles": { 38 | "version": "3.2.1", 39 | "resolved": "https://registry.npmjs.org/ansi-styles/-/ansi-styles-3.2.1.tgz", 40 | "integrity": "sha512-VT0ZI6kZRdTh8YyJw3SMbYm/u+NqfsAxEpWO0Pf9sq8/e94WxxOpPKx9FR1FlyCtOVDNOQ+8ntlqFxiRc+r5qA==", 41 | "dev": true, 42 | "requires": { 43 | "color-convert": "1.9.1" 44 | } 45 | }, 46 | "anymatch": { 47 | "version": "2.0.0", 48 | "resolved": "https://registry.npmjs.org/anymatch/-/anymatch-2.0.0.tgz", 49 | "integrity": "sha512-5teOsQWABXHHBFP9y3skS5P3d/WfWXpv3FUpy+LorMrNYaT9pI4oLMQX7jzQ2KklNpGpWHzdCXTDT2Y3XGlZBw==", 50 | "dev": true, 51 | "requires": { 52 | "micromatch": "3.1.10", 53 | "normalize-path": "2.1.1" 54 | } 55 | }, 56 | "arr-diff": { 57 | "version": "4.0.0", 58 | "resolved": "https://registry.npmjs.org/arr-diff/-/arr-diff-4.0.0.tgz", 59 | "integrity": "sha1-1kYQdP6/7HHn4VI1dhoyml3HxSA=", 60 | "dev": true 61 | }, 62 | "arr-flatten": { 63 | "version": "1.1.0", 64 | "resolved": "https://registry.npmjs.org/arr-flatten/-/arr-flatten-1.1.0.tgz", 65 | "integrity": "sha512-L3hKV5R/p5o81R7O02IGnwpDmkp6E982XhtbuwSe3O4qOtMMMtodicASA1Cny2U+aCXcNpml+m4dPsvsJ3jatg==", 66 | "dev": true 67 | }, 68 | "arr-union": { 69 | "version": "3.1.0", 70 | "resolved": "https://registry.npmjs.org/arr-union/-/arr-union-3.1.0.tgz", 71 | "integrity": "sha1-45sJrqne+Gao8gbiiK9jkZuuOcQ=", 72 | "dev": true 73 | }, 74 | "array-flatten": { 75 | "version": "1.1.1", 76 | "resolved": "https://registry.npmjs.org/array-flatten/-/array-flatten-1.1.1.tgz", 77 | "integrity": "sha1-ml9pkFGx5wczKPKgCJaLZOopVdI=" 78 | }, 79 | "array-unique": { 80 | "version": "0.3.2", 81 | "resolved": "https://registry.npmjs.org/array-unique/-/array-unique-0.3.2.tgz", 82 | "integrity": "sha1-qJS3XUvE9s1nnvMkSp/Y9Gri1Cg=", 83 | "dev": true 84 | }, 85 | "assign-symbols": { 86 | "version": "1.0.0", 87 | "resolved": "https://registry.npmjs.org/assign-symbols/-/assign-symbols-1.0.0.tgz", 88 | "integrity": "sha1-WWZ/QfrdTyDMvCu5a41Pf3jsA2c=", 89 | "dev": true 90 | }, 91 | "async": { 92 | "version": "1.5.2", 93 | "resolved": "https://registry.npmjs.org/async/-/async-1.5.2.tgz", 94 | "integrity": "sha1-7GphrlZIDAw8skHJVhjiCJL5Zyo=" 95 | }, 96 | "async-each": { 97 | "version": "1.0.1", 98 | "resolved": "https://registry.npmjs.org/async-each/-/async-each-1.0.1.tgz", 99 | "integrity": "sha1-GdOGodntxufByF04iu28xW0zYC0=", 100 | "dev": true 101 | }, 102 | "atob": { 103 | "version": "2.1.0", 104 | "resolved": "https://registry.npmjs.org/atob/-/atob-2.1.0.tgz", 105 | "integrity": "sha512-SuiKH8vbsOyCALjA/+EINmt/Kdl+TQPrtFgW7XZZcwtryFu9e5kQoX3bjCW6mIvGH1fbeAZZuvwGR5IlBRznGw==", 106 | "dev": true 107 | }, 108 | "axios": { 109 | "version": "0.18.0", 110 | "resolved": "https://registry.npmjs.org/axios/-/axios-0.18.0.tgz", 111 | "integrity": "sha1-MtU+SFHv3AoRmTts0AB4nXDAUQI=", 112 | "requires": { 113 | "follow-redirects": "1.4.1", 114 | "is-buffer": "1.1.6" 115 | } 116 | }, 117 | "balanced-match": { 118 | "version": "1.0.0", 119 | "resolved": "https://registry.npmjs.org/balanced-match/-/balanced-match-1.0.0.tgz", 120 | "integrity": "sha1-ibTRmasr7kneFk6gK4nORi1xt2c=", 121 | "dev": true 122 | }, 123 | "base": { 124 | "version": "0.11.2", 125 | "resolved": "https://registry.npmjs.org/base/-/base-0.11.2.tgz", 126 | "integrity": "sha512-5T6P4xPgpp0YDFvSWwEZ4NoE3aM4QBQXDzmVbraCkFj8zHM+mba8SyqB5DbZWyR7mYHo6Y7BdQo3MoA4m0TeQg==", 127 | "dev": true, 128 | "requires": { 129 | "cache-base": "1.0.1", 130 | "class-utils": "0.3.6", 131 | "component-emitter": "1.2.1", 132 | "define-property": "1.0.0", 133 | "isobject": "3.0.1", 134 | "mixin-deep": "1.3.1", 135 | "pascalcase": "0.1.1" 136 | }, 137 | "dependencies": { 138 | "define-property": { 139 | "version": "1.0.0", 140 | "resolved": "https://registry.npmjs.org/define-property/-/define-property-1.0.0.tgz", 141 | "integrity": "sha1-dp66rz9KY6rTr56NMEybvnm/sOY=", 142 | "dev": true, 143 | "requires": { 144 | "is-descriptor": "1.0.2" 145 | } 146 | }, 147 | "is-accessor-descriptor": { 148 | "version": "1.0.0", 149 | "resolved": "https://registry.npmjs.org/is-accessor-descriptor/-/is-accessor-descriptor-1.0.0.tgz", 150 | "integrity": "sha512-m5hnHTkcVsPfqx3AKlyttIPb7J+XykHvJP2B9bZDjlhLIoEq4XoK64Vg7boZlVWYK6LUY94dYPEE7Lh0ZkZKcQ==", 151 | "dev": true, 152 | "requires": { 153 | "kind-of": "6.0.2" 154 | } 155 | }, 156 | "is-data-descriptor": { 157 | "version": "1.0.0", 158 | "resolved": "https://registry.npmjs.org/is-data-descriptor/-/is-data-descriptor-1.0.0.tgz", 159 | "integrity": "sha512-jbRXy1FmtAoCjQkVmIVYwuuqDFUbaOeDjmed1tOGPrsMhtJA4rD9tkgA0F1qJ3gRFRXcHYVkdeaP50Q5rE/jLQ==", 160 | "dev": true, 161 | "requires": { 162 | "kind-of": "6.0.2" 163 | } 164 | }, 165 | "is-descriptor": { 166 | "version": "1.0.2", 167 | "resolved": "https://registry.npmjs.org/is-descriptor/-/is-descriptor-1.0.2.tgz", 168 | "integrity": "sha512-2eis5WqQGV7peooDyLmNEPUrps9+SXX5c9pL3xEB+4e9HnGuDa7mB7kHxHw4CbqS9k1T2hOH3miL8n8WtiYVtg==", 169 | "dev": true, 170 | "requires": { 171 | "is-accessor-descriptor": "1.0.0", 172 | "is-data-descriptor": "1.0.0", 173 | "kind-of": "6.0.2" 174 | } 175 | } 176 | } 177 | }, 178 | "base64url": { 179 | "version": "2.0.0", 180 | "resolved": "https://registry.npmjs.org/base64url/-/base64url-2.0.0.tgz", 181 | "integrity": "sha1-6sFuA+oUOO/5Qj1puqNiYu0fcLs=" 182 | }, 183 | "binary-extensions": { 184 | "version": "1.11.0", 185 | "resolved": "https://registry.npmjs.org/binary-extensions/-/binary-extensions-1.11.0.tgz", 186 | "integrity": "sha1-RqoXUftqL5PuXmibsQh9SxTGwgU=", 187 | "dev": true 188 | }, 189 | "body-parser": { 190 | "version": "1.18.2", 191 | "resolved": "https://registry.npmjs.org/body-parser/-/body-parser-1.18.2.tgz", 192 | "integrity": "sha1-h2eKGdhLR9hZuDGZvVm84iKxBFQ=", 193 | "requires": { 194 | "bytes": "3.0.0", 195 | "content-type": "1.0.4", 196 | "debug": "2.6.9", 197 | "depd": "1.1.2", 198 | "http-errors": "1.6.3", 199 | "iconv-lite": "0.4.19", 200 | "on-finished": "2.3.0", 201 | "qs": "6.5.1", 202 | "raw-body": "2.3.2", 203 | "type-is": "1.6.16" 204 | } 205 | }, 206 | "boxen": { 207 | "version": "1.3.0", 208 | "resolved": "https://registry.npmjs.org/boxen/-/boxen-1.3.0.tgz", 209 | "integrity": "sha512-TNPjfTr432qx7yOjQyaXm3dSR0MH9vXp7eT1BFSl/C51g+EFnOR9hTg1IreahGBmDNCehscshe45f+C1TBZbLw==", 210 | "dev": true, 211 | "requires": { 212 | "ansi-align": "2.0.0", 213 | "camelcase": "4.1.0", 214 | "chalk": "2.3.2", 215 | "cli-boxes": "1.0.0", 216 | "string-width": "2.1.1", 217 | "term-size": "1.2.0", 218 | "widest-line": "2.0.0" 219 | } 220 | }, 221 | "brace-expansion": { 222 | "version": "1.1.11", 223 | "resolved": "https://registry.npmjs.org/brace-expansion/-/brace-expansion-1.1.11.tgz", 224 | "integrity": "sha512-iCuPHDFgrHX7H2vEI/5xpz07zSHB00TpugqhmYtVmMO6518mCuRMoOYFldEBl0g187ufozdaHgWKcYFb61qGiA==", 225 | "dev": true, 226 | "requires": { 227 | "balanced-match": "1.0.0", 228 | "concat-map": "0.0.1" 229 | } 230 | }, 231 | "braces": { 232 | "version": "2.3.2", 233 | "resolved": "https://registry.npmjs.org/braces/-/braces-2.3.2.tgz", 234 | "integrity": "sha512-aNdbnj9P8PjdXU4ybaWLK2IF3jc/EoDYbC7AazW6to3TRsfXxscC9UXOB5iDiEQrkyIbWp2SLQda4+QAa7nc3w==", 235 | "dev": true, 236 | "requires": { 237 | "arr-flatten": "1.1.0", 238 | "array-unique": "0.3.2", 239 | "extend-shallow": "2.0.1", 240 | "fill-range": "4.0.0", 241 | "isobject": "3.0.1", 242 | "repeat-element": "1.1.2", 243 | "snapdragon": "0.8.2", 244 | "snapdragon-node": "2.1.1", 245 | "split-string": "3.1.0", 246 | "to-regex": "3.0.2" 247 | }, 248 | "dependencies": { 249 | "extend-shallow": { 250 | "version": "2.0.1", 251 | "resolved": "https://registry.npmjs.org/extend-shallow/-/extend-shallow-2.0.1.tgz", 252 | "integrity": "sha1-Ua99YUrZqfYQ6huvu5idaxxWiQ8=", 253 | "dev": true, 254 | "requires": { 255 | "is-extendable": "0.1.1" 256 | } 257 | } 258 | } 259 | }, 260 | "buffer-equal-constant-time": { 261 | "version": "1.0.1", 262 | "resolved": "https://registry.npmjs.org/buffer-equal-constant-time/-/buffer-equal-constant-time-1.0.1.tgz", 263 | "integrity": "sha1-+OcRMvf/5uAaXJaXpMbz5I1cyBk=" 264 | }, 265 | "bytes": { 266 | "version": "3.0.0", 267 | "resolved": "https://registry.npmjs.org/bytes/-/bytes-3.0.0.tgz", 268 | "integrity": "sha1-0ygVQE1olpn4Wk6k+odV3ROpYEg=" 269 | }, 270 | "cache-base": { 271 | "version": "1.0.1", 272 | "resolved": "https://registry.npmjs.org/cache-base/-/cache-base-1.0.1.tgz", 273 | "integrity": "sha512-AKcdTnFSWATd5/GCPRxr2ChwIJ85CeyrEyjRHlKxQ56d4XJMGym0uAiKn0xbLOGOl3+yRpOTi484dVCEc5AUzQ==", 274 | "dev": true, 275 | "requires": { 276 | "collection-visit": "1.0.0", 277 | "component-emitter": "1.2.1", 278 | "get-value": "2.0.6", 279 | "has-value": "1.0.0", 280 | "isobject": "3.0.1", 281 | "set-value": "2.0.0", 282 | "to-object-path": "0.3.0", 283 | "union-value": "1.0.0", 284 | "unset-value": "1.0.0" 285 | } 286 | }, 287 | "camelcase": { 288 | "version": "4.1.0", 289 | "resolved": "https://registry.npmjs.org/camelcase/-/camelcase-4.1.0.tgz", 290 | "integrity": "sha1-1UVjW+HjPFQmScaRc+Xeas+uNN0=", 291 | "dev": true 292 | }, 293 | "capture-stack-trace": { 294 | "version": "1.0.0", 295 | "resolved": "https://registry.npmjs.org/capture-stack-trace/-/capture-stack-trace-1.0.0.tgz", 296 | "integrity": "sha1-Sm+gc5nCa7pH8LJJa00PtAjFVQ0=", 297 | "dev": true 298 | }, 299 | "chalk": { 300 | "version": "2.3.2", 301 | "resolved": "https://registry.npmjs.org/chalk/-/chalk-2.3.2.tgz", 302 | "integrity": "sha512-ZM4j2/ld/YZDc3Ma8PgN7gyAk+kHMMMyzLNryCPGhWrsfAuDVeuid5bpRFTDgMH9JBK2lA4dyyAkkZYF/WcqDQ==", 303 | "dev": true, 304 | "requires": { 305 | "ansi-styles": "3.2.1", 306 | "escape-string-regexp": "1.0.5", 307 | "supports-color": "5.3.0" 308 | } 309 | }, 310 | "chokidar": { 311 | "version": "2.0.3", 312 | "resolved": "https://registry.npmjs.org/chokidar/-/chokidar-2.0.3.tgz", 313 | "integrity": "sha512-zW8iXYZtXMx4kux/nuZVXjkLP+CyIK5Al5FHnj1OgTKGZfp4Oy6/ymtMSKFv3GD8DviEmUPmJg9eFdJ/JzudMg==", 314 | "dev": true, 315 | "requires": { 316 | "anymatch": "2.0.0", 317 | "async-each": "1.0.1", 318 | "braces": "2.3.2", 319 | "fsevents": "1.1.3", 320 | "glob-parent": "3.1.0", 321 | "inherits": "2.0.3", 322 | "is-binary-path": "1.0.1", 323 | "is-glob": "4.0.0", 324 | "normalize-path": "2.1.1", 325 | "path-is-absolute": "1.0.1", 326 | "readdirp": "2.1.0", 327 | "upath": "1.0.4" 328 | } 329 | }, 330 | "ci-info": { 331 | "version": "1.1.3", 332 | "resolved": "https://registry.npmjs.org/ci-info/-/ci-info-1.1.3.tgz", 333 | "integrity": "sha512-SK/846h/Rcy8q9Z9CAwGBLfCJ6EkjJWdpelWDufQpqVDYq2Wnnv8zlSO6AMQap02jvhVruKKpEtQOufo3pFhLg==", 334 | "dev": true 335 | }, 336 | "class-utils": { 337 | "version": "0.3.6", 338 | "resolved": "https://registry.npmjs.org/class-utils/-/class-utils-0.3.6.tgz", 339 | "integrity": "sha512-qOhPa/Fj7s6TY8H8esGu5QNpMMQxz79h+urzrNYN6mn+9BnxlDGf5QZ+XeCDsxSjPqsSR56XOZOJmpeurnLMeg==", 340 | "dev": true, 341 | "requires": { 342 | "arr-union": "3.1.0", 343 | "define-property": "0.2.5", 344 | "isobject": "3.0.1", 345 | "static-extend": "0.1.2" 346 | }, 347 | "dependencies": { 348 | "define-property": { 349 | "version": "0.2.5", 350 | "resolved": "https://registry.npmjs.org/define-property/-/define-property-0.2.5.tgz", 351 | "integrity": "sha1-w1se+RjsPJkPmlvFe+BKrOxcgRY=", 352 | "dev": true, 353 | "requires": { 354 | "is-descriptor": "0.1.6" 355 | } 356 | } 357 | } 358 | }, 359 | "cli-boxes": { 360 | "version": "1.0.0", 361 | "resolved": "https://registry.npmjs.org/cli-boxes/-/cli-boxes-1.0.0.tgz", 362 | "integrity": "sha1-T6kXw+WclKAEzWH47lCdplFocUM=", 363 | "dev": true 364 | }, 365 | "collection-visit": { 366 | "version": "1.0.0", 367 | "resolved": "https://registry.npmjs.org/collection-visit/-/collection-visit-1.0.0.tgz", 368 | "integrity": "sha1-S8A3PBZLwykbTTaMgpzxqApZ3KA=", 369 | "dev": true, 370 | "requires": { 371 | "map-visit": "1.0.0", 372 | "object-visit": "1.0.1" 373 | } 374 | }, 375 | "color-convert": { 376 | "version": "1.9.1", 377 | "resolved": "https://registry.npmjs.org/color-convert/-/color-convert-1.9.1.tgz", 378 | "integrity": "sha512-mjGanIiwQJskCC18rPR6OmrZ6fm2Lc7PeGFYwCmy5J34wC6F1PzdGL6xeMfmgicfYcNLGuVFA3WzXtIDCQSZxQ==", 379 | "dev": true, 380 | "requires": { 381 | "color-name": "1.1.3" 382 | } 383 | }, 384 | "color-name": { 385 | "version": "1.1.3", 386 | "resolved": "https://registry.npmjs.org/color-name/-/color-name-1.1.3.tgz", 387 | "integrity": "sha1-p9BVi9icQveV3UIyj3QIMcpTvCU=", 388 | "dev": true 389 | }, 390 | "component-emitter": { 391 | "version": "1.2.1", 392 | "resolved": "https://registry.npmjs.org/component-emitter/-/component-emitter-1.2.1.tgz", 393 | "integrity": "sha1-E3kY1teCg/ffemt8WmPhQOaUJeY=", 394 | "dev": true 395 | }, 396 | "concat-map": { 397 | "version": "0.0.1", 398 | "resolved": "https://registry.npmjs.org/concat-map/-/concat-map-0.0.1.tgz", 399 | "integrity": "sha1-2Klr13/Wjfd5OnMDajug1UBdR3s=", 400 | "dev": true 401 | }, 402 | "configstore": { 403 | "version": "3.1.2", 404 | "resolved": "https://registry.npmjs.org/configstore/-/configstore-3.1.2.tgz", 405 | "integrity": "sha512-vtv5HtGjcYUgFrXc6Kx747B83MRRVS5R1VTEQoXvuP+kMI+if6uywV0nDGoiydJRy4yk7h9od5Og0kxx4zUXmw==", 406 | "dev": true, 407 | "requires": { 408 | "dot-prop": "4.2.0", 409 | "graceful-fs": "4.1.11", 410 | "make-dir": "1.2.0", 411 | "unique-string": "1.0.0", 412 | "write-file-atomic": "2.3.0", 413 | "xdg-basedir": "3.0.0" 414 | } 415 | }, 416 | "content-disposition": { 417 | "version": "0.5.2", 418 | "resolved": "https://registry.npmjs.org/content-disposition/-/content-disposition-0.5.2.tgz", 419 | "integrity": "sha1-DPaLud318r55YcOoUXjLhdunjLQ=" 420 | }, 421 | "content-type": { 422 | "version": "1.0.4", 423 | "resolved": "https://registry.npmjs.org/content-type/-/content-type-1.0.4.tgz", 424 | "integrity": "sha512-hIP3EEPs8tB9AT1L+NUqtwOAps4mk2Zob89MWXMHjHWg9milF/j4osnnQLXBCBFBk/tvIG/tUc9mOUJiPBhPXA==" 425 | }, 426 | "cookie": { 427 | "version": "0.3.1", 428 | "resolved": "https://registry.npmjs.org/cookie/-/cookie-0.3.1.tgz", 429 | "integrity": "sha1-5+Ch+e9DtMi6klxcWpboBtFoc7s=" 430 | }, 431 | "cookie-signature": { 432 | "version": "1.0.6", 433 | "resolved": "https://registry.npmjs.org/cookie-signature/-/cookie-signature-1.0.6.tgz", 434 | "integrity": "sha1-4wOogrNCzD7oylE6eZmXNNqzriw=" 435 | }, 436 | "copy-descriptor": { 437 | "version": "0.1.1", 438 | "resolved": "https://registry.npmjs.org/copy-descriptor/-/copy-descriptor-0.1.1.tgz", 439 | "integrity": "sha1-Z29us8OZl8LuGsOpJP1hJHSPV40=", 440 | "dev": true 441 | }, 442 | "core-util-is": { 443 | "version": "1.0.2", 444 | "resolved": "https://registry.npmjs.org/core-util-is/-/core-util-is-1.0.2.tgz", 445 | "integrity": "sha1-tf1UIgqivFq1eqtxQMlAdUUDwac=", 446 | "dev": true 447 | }, 448 | "cors": { 449 | "version": "2.8.4", 450 | "resolved": "https://registry.npmjs.org/cors/-/cors-2.8.4.tgz", 451 | "integrity": "sha1-K9OB8usgECAQXNUOpZ2mMJBpRoY=", 452 | "requires": { 453 | "object-assign": "4.1.1", 454 | "vary": "1.1.2" 455 | } 456 | }, 457 | "create-error-class": { 458 | "version": "3.0.2", 459 | "resolved": "https://registry.npmjs.org/create-error-class/-/create-error-class-3.0.2.tgz", 460 | "integrity": "sha1-Br56vvlHo/FKMP1hBnHUAbyot7Y=", 461 | "dev": true, 462 | "requires": { 463 | "capture-stack-trace": "1.0.0" 464 | } 465 | }, 466 | "cross-spawn": { 467 | "version": "5.1.0", 468 | "resolved": "https://registry.npmjs.org/cross-spawn/-/cross-spawn-5.1.0.tgz", 469 | "integrity": "sha1-6L0O/uWPz/b4+UUQoKVUu/ojVEk=", 470 | "dev": true, 471 | "requires": { 472 | "lru-cache": "4.1.2", 473 | "shebang-command": "1.2.0", 474 | "which": "1.3.0" 475 | } 476 | }, 477 | "crypto-random-string": { 478 | "version": "1.0.0", 479 | "resolved": "https://registry.npmjs.org/crypto-random-string/-/crypto-random-string-1.0.0.tgz", 480 | "integrity": "sha1-ojD2T1aDEOFJgAmUB5DsmVRbyn4=", 481 | "dev": true 482 | }, 483 | "debug": { 484 | "version": "2.6.9", 485 | "resolved": "https://registry.npmjs.org/debug/-/debug-2.6.9.tgz", 486 | "integrity": "sha512-bC7ElrdJaJnPbAP+1EotYvqZsb3ecl5wi6Bfi6BJTUcNowp6cvspg0jXznRTKDjm/E7AdgFBVeAPVMNcKGsHMA==", 487 | "requires": { 488 | "ms": "2.0.0" 489 | } 490 | }, 491 | "decode-uri-component": { 492 | "version": "0.2.0", 493 | "resolved": "https://registry.npmjs.org/decode-uri-component/-/decode-uri-component-0.2.0.tgz", 494 | "integrity": "sha1-6zkTMzRYd1y4TNGh+uBiEGu4dUU=", 495 | "dev": true 496 | }, 497 | "deep-extend": { 498 | "version": "0.4.2", 499 | "resolved": "https://registry.npmjs.org/deep-extend/-/deep-extend-0.4.2.tgz", 500 | "integrity": "sha1-SLaZwn4zS/ifEIkr5DL25MfTSn8=", 501 | "dev": true 502 | }, 503 | "define-property": { 504 | "version": "2.0.2", 505 | "resolved": "https://registry.npmjs.org/define-property/-/define-property-2.0.2.tgz", 506 | "integrity": "sha512-jwK2UV4cnPpbcG7+VRARKTZPUWowwXA8bzH5NP6ud0oeAxyYPuGZUAC7hMugpCdz4BeSZl2Dl9k66CHJ/46ZYQ==", 507 | "dev": true, 508 | "requires": { 509 | "is-descriptor": "1.0.2", 510 | "isobject": "3.0.1" 511 | }, 512 | "dependencies": { 513 | "is-accessor-descriptor": { 514 | "version": "1.0.0", 515 | "resolved": "https://registry.npmjs.org/is-accessor-descriptor/-/is-accessor-descriptor-1.0.0.tgz", 516 | "integrity": "sha512-m5hnHTkcVsPfqx3AKlyttIPb7J+XykHvJP2B9bZDjlhLIoEq4XoK64Vg7boZlVWYK6LUY94dYPEE7Lh0ZkZKcQ==", 517 | "dev": true, 518 | "requires": { 519 | "kind-of": "6.0.2" 520 | } 521 | }, 522 | "is-data-descriptor": { 523 | "version": "1.0.0", 524 | "resolved": "https://registry.npmjs.org/is-data-descriptor/-/is-data-descriptor-1.0.0.tgz", 525 | "integrity": "sha512-jbRXy1FmtAoCjQkVmIVYwuuqDFUbaOeDjmed1tOGPrsMhtJA4rD9tkgA0F1qJ3gRFRXcHYVkdeaP50Q5rE/jLQ==", 526 | "dev": true, 527 | "requires": { 528 | "kind-of": "6.0.2" 529 | } 530 | }, 531 | "is-descriptor": { 532 | "version": "1.0.2", 533 | "resolved": "https://registry.npmjs.org/is-descriptor/-/is-descriptor-1.0.2.tgz", 534 | "integrity": "sha512-2eis5WqQGV7peooDyLmNEPUrps9+SXX5c9pL3xEB+4e9HnGuDa7mB7kHxHw4CbqS9k1T2hOH3miL8n8WtiYVtg==", 535 | "dev": true, 536 | "requires": { 537 | "is-accessor-descriptor": "1.0.0", 538 | "is-data-descriptor": "1.0.0", 539 | "kind-of": "6.0.2" 540 | } 541 | } 542 | } 543 | }, 544 | "depd": { 545 | "version": "1.1.2", 546 | "resolved": "https://registry.npmjs.org/depd/-/depd-1.1.2.tgz", 547 | "integrity": "sha1-m81S4UwJd2PnSbJ0xDRu0uVgtak=" 548 | }, 549 | "destroy": { 550 | "version": "1.0.4", 551 | "resolved": "https://registry.npmjs.org/destroy/-/destroy-1.0.4.tgz", 552 | "integrity": "sha1-l4hXRCxEdJ5CBmE+N5RiBYJqvYA=" 553 | }, 554 | "dot-prop": { 555 | "version": "4.2.0", 556 | "resolved": "https://registry.npmjs.org/dot-prop/-/dot-prop-4.2.0.tgz", 557 | "integrity": "sha512-tUMXrxlExSW6U2EXiiKGSBVdYgtV8qlHL+C10TsW4PURY/ic+eaysnSkwB4kA/mBlCyy/IKDJ+Lc3wbWeaXtuQ==", 558 | "dev": true, 559 | "requires": { 560 | "is-obj": "1.0.1" 561 | } 562 | }, 563 | "dotenv": { 564 | "version": "5.0.1", 565 | "resolved": "https://registry.npmjs.org/dotenv/-/dotenv-5.0.1.tgz", 566 | "integrity": "sha512-4As8uPrjfwb7VXC+WnLCbXK7y+Ueb2B3zgNCePYfhxS1PYeaO1YTeplffTEcbfLhvFNGLAz90VvJs9yomG7bow==" 567 | }, 568 | "duplexer": { 569 | "version": "0.1.1", 570 | "resolved": "https://registry.npmjs.org/duplexer/-/duplexer-0.1.1.tgz", 571 | "integrity": "sha1-rOb/gIwc5mtX0ev5eXessCM0z8E=", 572 | "dev": true 573 | }, 574 | "duplexer3": { 575 | "version": "0.1.4", 576 | "resolved": "https://registry.npmjs.org/duplexer3/-/duplexer3-0.1.4.tgz", 577 | "integrity": "sha1-7gHdHKwO08vH/b6jfcCo8c4ALOI=", 578 | "dev": true 579 | }, 580 | "ecdsa-sig-formatter": { 581 | "version": "1.0.9", 582 | "resolved": "https://registry.npmjs.org/ecdsa-sig-formatter/-/ecdsa-sig-formatter-1.0.9.tgz", 583 | "integrity": "sha1-S8kmJ07Dtau1AW5+HWCSGsJisqE=", 584 | "requires": { 585 | "base64url": "2.0.0", 586 | "safe-buffer": "5.1.1" 587 | } 588 | }, 589 | "ee-first": { 590 | "version": "1.1.1", 591 | "resolved": "https://registry.npmjs.org/ee-first/-/ee-first-1.1.1.tgz", 592 | "integrity": "sha1-WQxhFWsK4vTwJVcyoViyZrxWsh0=" 593 | }, 594 | "encodeurl": { 595 | "version": "1.0.2", 596 | "resolved": "https://registry.npmjs.org/encodeurl/-/encodeurl-1.0.2.tgz", 597 | "integrity": "sha1-rT/0yG7C0CkyL1oCw6mmBslbP1k=" 598 | }, 599 | "es6-promise": { 600 | "version": "4.2.4", 601 | "resolved": "https://registry.npmjs.org/es6-promise/-/es6-promise-4.2.4.tgz", 602 | "integrity": "sha512-/NdNZVJg+uZgtm9eS3O6lrOLYmQag2DjdEXuPaHlZ6RuVqgqaVZfgYCepEIKsLqwdQArOPtC3XzRLqGGfT8KQQ==" 603 | }, 604 | "escape-html": { 605 | "version": "1.0.3", 606 | "resolved": "https://registry.npmjs.org/escape-html/-/escape-html-1.0.3.tgz", 607 | "integrity": "sha1-Aljq5NPQwJdN4cFpGI7wBR0dGYg=" 608 | }, 609 | "escape-string-regexp": { 610 | "version": "1.0.5", 611 | "resolved": "https://registry.npmjs.org/escape-string-regexp/-/escape-string-regexp-1.0.5.tgz", 612 | "integrity": "sha1-G2HAViGQqN/2rjuyzwIAyhMLhtQ=", 613 | "dev": true 614 | }, 615 | "etag": { 616 | "version": "1.8.1", 617 | "resolved": "https://registry.npmjs.org/etag/-/etag-1.8.1.tgz", 618 | "integrity": "sha1-Qa4u62XvpiJorr/qg6x9eSmbCIc=" 619 | }, 620 | "event-stream": { 621 | "version": "3.3.4", 622 | "resolved": "http://registry.npmjs.org/event-stream/-/event-stream-3.3.4.tgz", 623 | "integrity": "sha1-SrTJoPWlTbkzi0w02Gv86PSzVXE=", 624 | "dev": true, 625 | "requires": { 626 | "duplexer": "0.1.1", 627 | "from": "0.1.7", 628 | "map-stream": "0.1.0", 629 | "pause-stream": "0.0.11", 630 | "split": "0.3.3", 631 | "stream-combiner": "0.0.4", 632 | "through": "2.3.8" 633 | } 634 | }, 635 | "execa": { 636 | "version": "0.7.0", 637 | "resolved": "https://registry.npmjs.org/execa/-/execa-0.7.0.tgz", 638 | "integrity": "sha1-lEvs00zEHuMqY6n68nrVpl/Fl3c=", 639 | "dev": true, 640 | "requires": { 641 | "cross-spawn": "5.1.0", 642 | "get-stream": "3.0.0", 643 | "is-stream": "1.1.0", 644 | "npm-run-path": "2.0.2", 645 | "p-finally": "1.0.0", 646 | "signal-exit": "3.0.2", 647 | "strip-eof": "1.0.0" 648 | } 649 | }, 650 | "expand-brackets": { 651 | "version": "2.1.4", 652 | "resolved": "https://registry.npmjs.org/expand-brackets/-/expand-brackets-2.1.4.tgz", 653 | "integrity": "sha1-t3c14xXOMPa27/D4OwQVGiJEliI=", 654 | "dev": true, 655 | "requires": { 656 | "debug": "2.6.9", 657 | "define-property": "0.2.5", 658 | "extend-shallow": "2.0.1", 659 | "posix-character-classes": "0.1.1", 660 | "regex-not": "1.0.2", 661 | "snapdragon": "0.8.2", 662 | "to-regex": "3.0.2" 663 | }, 664 | "dependencies": { 665 | "define-property": { 666 | "version": "0.2.5", 667 | "resolved": "https://registry.npmjs.org/define-property/-/define-property-0.2.5.tgz", 668 | "integrity": "sha1-w1se+RjsPJkPmlvFe+BKrOxcgRY=", 669 | "dev": true, 670 | "requires": { 671 | "is-descriptor": "0.1.6" 672 | } 673 | }, 674 | "extend-shallow": { 675 | "version": "2.0.1", 676 | "resolved": "https://registry.npmjs.org/extend-shallow/-/extend-shallow-2.0.1.tgz", 677 | "integrity": "sha1-Ua99YUrZqfYQ6huvu5idaxxWiQ8=", 678 | "dev": true, 679 | "requires": { 680 | "is-extendable": "0.1.1" 681 | } 682 | } 683 | } 684 | }, 685 | "express": { 686 | "version": "4.16.3", 687 | "resolved": "https://registry.npmjs.org/express/-/express-4.16.3.tgz", 688 | "integrity": "sha1-avilAjUNsyRuzEvs9rWjTSL37VM=", 689 | "requires": { 690 | "accepts": "1.3.5", 691 | "array-flatten": "1.1.1", 692 | "body-parser": "1.18.2", 693 | "content-disposition": "0.5.2", 694 | "content-type": "1.0.4", 695 | "cookie": "0.3.1", 696 | "cookie-signature": "1.0.6", 697 | "debug": "2.6.9", 698 | "depd": "1.1.2", 699 | "encodeurl": "1.0.2", 700 | "escape-html": "1.0.3", 701 | "etag": "1.8.1", 702 | "finalhandler": "1.1.1", 703 | "fresh": "0.5.2", 704 | "merge-descriptors": "1.0.1", 705 | "methods": "1.1.2", 706 | "on-finished": "2.3.0", 707 | "parseurl": "1.3.2", 708 | "path-to-regexp": "0.1.7", 709 | "proxy-addr": "2.0.3", 710 | "qs": "6.5.1", 711 | "range-parser": "1.2.0", 712 | "safe-buffer": "5.1.1", 713 | "send": "0.16.2", 714 | "serve-static": "1.13.2", 715 | "setprototypeof": "1.1.0", 716 | "statuses": "1.4.0", 717 | "type-is": "1.6.16", 718 | "utils-merge": "1.0.1", 719 | "vary": "1.1.2" 720 | } 721 | }, 722 | "express-http-proxy": { 723 | "version": "1.1.0", 724 | "resolved": "https://registry.npmjs.org/express-http-proxy/-/express-http-proxy-1.1.0.tgz", 725 | "integrity": "sha1-tTOEKt0VtwIdeNJIyAtTV4p7a6A=", 726 | "requires": { 727 | "debug": "3.1.0", 728 | "es6-promise": "4.2.4", 729 | "raw-body": "2.3.2" 730 | }, 731 | "dependencies": { 732 | "debug": { 733 | "version": "3.1.0", 734 | "resolved": "https://registry.npmjs.org/debug/-/debug-3.1.0.tgz", 735 | "integrity": "sha512-OX8XqP7/1a9cqkxYw2yXss15f26NKWBpDXQd0/uK/KPqdQhxbPa994hnzjcE2VqQpDslf55723cKPUOGSmMY3g==", 736 | "requires": { 737 | "ms": "2.0.0" 738 | } 739 | } 740 | } 741 | }, 742 | "express-jwt": { 743 | "version": "5.3.1", 744 | "resolved": "https://registry.npmjs.org/express-jwt/-/express-jwt-5.3.1.tgz", 745 | "integrity": "sha512-1C9RNq0wMp/JvsH/qZMlg3SIPvKu14YkZ4YYv7gJQ1Vq+Dv8LH9tLKenS5vMNth45gTlEUGx+ycp9IHIlaHP/g==", 746 | "requires": { 747 | "async": "1.5.2", 748 | "express-unless": "0.3.1", 749 | "jsonwebtoken": "8.2.1", 750 | "lodash.set": "4.3.2" 751 | } 752 | }, 753 | "express-unless": { 754 | "version": "0.3.1", 755 | "resolved": "https://registry.npmjs.org/express-unless/-/express-unless-0.3.1.tgz", 756 | "integrity": "sha1-JVfBRudb65A+LSR/m1ugFFJpbiA=" 757 | }, 758 | "extend-shallow": { 759 | "version": "3.0.2", 760 | "resolved": "https://registry.npmjs.org/extend-shallow/-/extend-shallow-3.0.2.tgz", 761 | "integrity": "sha1-Jqcarwc7OfshJxcnRhMcJwQCjbg=", 762 | "dev": true, 763 | "requires": { 764 | "assign-symbols": "1.0.0", 765 | "is-extendable": "1.0.1" 766 | }, 767 | "dependencies": { 768 | "is-extendable": { 769 | "version": "1.0.1", 770 | "resolved": "https://registry.npmjs.org/is-extendable/-/is-extendable-1.0.1.tgz", 771 | "integrity": "sha512-arnXMxT1hhoKo9k1LZdmlNyJdDDfy2v0fXjFlmok4+i8ul/6WlbVge9bhM74OpNPQPMGUToDtz+KXa1PneJxOA==", 772 | "dev": true, 773 | "requires": { 774 | "is-plain-object": "2.0.4" 775 | } 776 | } 777 | } 778 | }, 779 | "extglob": { 780 | "version": "2.0.4", 781 | "resolved": "https://registry.npmjs.org/extglob/-/extglob-2.0.4.tgz", 782 | "integrity": "sha512-Nmb6QXkELsuBr24CJSkilo6UHHgbekK5UiZgfE6UHD3Eb27YC6oD+bhcT+tJ6cl8dmsgdQxnWlcry8ksBIBLpw==", 783 | "dev": true, 784 | "requires": { 785 | "array-unique": "0.3.2", 786 | "define-property": "1.0.0", 787 | "expand-brackets": "2.1.4", 788 | "extend-shallow": "2.0.1", 789 | "fragment-cache": "0.2.1", 790 | "regex-not": "1.0.2", 791 | "snapdragon": "0.8.2", 792 | "to-regex": "3.0.2" 793 | }, 794 | "dependencies": { 795 | "define-property": { 796 | "version": "1.0.0", 797 | "resolved": "https://registry.npmjs.org/define-property/-/define-property-1.0.0.tgz", 798 | "integrity": "sha1-dp66rz9KY6rTr56NMEybvnm/sOY=", 799 | "dev": true, 800 | "requires": { 801 | "is-descriptor": "1.0.2" 802 | } 803 | }, 804 | "extend-shallow": { 805 | "version": "2.0.1", 806 | "resolved": "https://registry.npmjs.org/extend-shallow/-/extend-shallow-2.0.1.tgz", 807 | "integrity": "sha1-Ua99YUrZqfYQ6huvu5idaxxWiQ8=", 808 | "dev": true, 809 | "requires": { 810 | "is-extendable": "0.1.1" 811 | } 812 | }, 813 | "is-accessor-descriptor": { 814 | "version": "1.0.0", 815 | "resolved": "https://registry.npmjs.org/is-accessor-descriptor/-/is-accessor-descriptor-1.0.0.tgz", 816 | "integrity": "sha512-m5hnHTkcVsPfqx3AKlyttIPb7J+XykHvJP2B9bZDjlhLIoEq4XoK64Vg7boZlVWYK6LUY94dYPEE7Lh0ZkZKcQ==", 817 | "dev": true, 818 | "requires": { 819 | "kind-of": "6.0.2" 820 | } 821 | }, 822 | "is-data-descriptor": { 823 | "version": "1.0.0", 824 | "resolved": "https://registry.npmjs.org/is-data-descriptor/-/is-data-descriptor-1.0.0.tgz", 825 | "integrity": "sha512-jbRXy1FmtAoCjQkVmIVYwuuqDFUbaOeDjmed1tOGPrsMhtJA4rD9tkgA0F1qJ3gRFRXcHYVkdeaP50Q5rE/jLQ==", 826 | "dev": true, 827 | "requires": { 828 | "kind-of": "6.0.2" 829 | } 830 | }, 831 | "is-descriptor": { 832 | "version": "1.0.2", 833 | "resolved": "https://registry.npmjs.org/is-descriptor/-/is-descriptor-1.0.2.tgz", 834 | "integrity": "sha512-2eis5WqQGV7peooDyLmNEPUrps9+SXX5c9pL3xEB+4e9HnGuDa7mB7kHxHw4CbqS9k1T2hOH3miL8n8WtiYVtg==", 835 | "dev": true, 836 | "requires": { 837 | "is-accessor-descriptor": "1.0.0", 838 | "is-data-descriptor": "1.0.0", 839 | "kind-of": "6.0.2" 840 | } 841 | } 842 | } 843 | }, 844 | "fill-range": { 845 | "version": "4.0.0", 846 | "resolved": "https://registry.npmjs.org/fill-range/-/fill-range-4.0.0.tgz", 847 | "integrity": "sha1-1USBHUKPmOsGpj3EAtJAPDKMOPc=", 848 | "dev": true, 849 | "requires": { 850 | "extend-shallow": "2.0.1", 851 | "is-number": "3.0.0", 852 | "repeat-string": "1.6.1", 853 | "to-regex-range": "2.1.1" 854 | }, 855 | "dependencies": { 856 | "extend-shallow": { 857 | "version": "2.0.1", 858 | "resolved": "https://registry.npmjs.org/extend-shallow/-/extend-shallow-2.0.1.tgz", 859 | "integrity": "sha1-Ua99YUrZqfYQ6huvu5idaxxWiQ8=", 860 | "dev": true, 861 | "requires": { 862 | "is-extendable": "0.1.1" 863 | } 864 | } 865 | } 866 | }, 867 | "finalhandler": { 868 | "version": "1.1.1", 869 | "resolved": "https://registry.npmjs.org/finalhandler/-/finalhandler-1.1.1.tgz", 870 | "integrity": "sha512-Y1GUDo39ez4aHAw7MysnUD5JzYX+WaIj8I57kO3aEPT1fFRL4sr7mjei97FgnwhAyyzRYmQZaTHb2+9uZ1dPtg==", 871 | "requires": { 872 | "debug": "2.6.9", 873 | "encodeurl": "1.0.2", 874 | "escape-html": "1.0.3", 875 | "on-finished": "2.3.0", 876 | "parseurl": "1.3.2", 877 | "statuses": "1.4.0", 878 | "unpipe": "1.0.0" 879 | } 880 | }, 881 | "follow-redirects": { 882 | "version": "1.4.1", 883 | "resolved": "https://registry.npmjs.org/follow-redirects/-/follow-redirects-1.4.1.tgz", 884 | "integrity": "sha512-uxYePVPogtya1ktGnAAXOacnbIuRMB4dkvqeNz2qTtTQsuzSfbDolV+wMMKxAmCx0bLgAKLbBOkjItMbbkR1vg==", 885 | "requires": { 886 | "debug": "3.1.0" 887 | }, 888 | "dependencies": { 889 | "debug": { 890 | "version": "3.1.0", 891 | "resolved": "https://registry.npmjs.org/debug/-/debug-3.1.0.tgz", 892 | "integrity": "sha512-OX8XqP7/1a9cqkxYw2yXss15f26NKWBpDXQd0/uK/KPqdQhxbPa994hnzjcE2VqQpDslf55723cKPUOGSmMY3g==", 893 | "requires": { 894 | "ms": "2.0.0" 895 | } 896 | } 897 | } 898 | }, 899 | "for-in": { 900 | "version": "1.0.2", 901 | "resolved": "https://registry.npmjs.org/for-in/-/for-in-1.0.2.tgz", 902 | "integrity": "sha1-gQaNKVqBQuwKxybG4iAMMPttXoA=", 903 | "dev": true 904 | }, 905 | "forwarded": { 906 | "version": "0.1.2", 907 | "resolved": "https://registry.npmjs.org/forwarded/-/forwarded-0.1.2.tgz", 908 | "integrity": "sha1-mMI9qxF1ZXuMBXPozszZGw/xjIQ=" 909 | }, 910 | "fragment-cache": { 911 | "version": "0.2.1", 912 | "resolved": "https://registry.npmjs.org/fragment-cache/-/fragment-cache-0.2.1.tgz", 913 | "integrity": "sha1-QpD60n8T6Jvn8zeZxrxaCr//DRk=", 914 | "dev": true, 915 | "requires": { 916 | "map-cache": "0.2.2" 917 | } 918 | }, 919 | "fresh": { 920 | "version": "0.5.2", 921 | "resolved": "https://registry.npmjs.org/fresh/-/fresh-0.5.2.tgz", 922 | "integrity": "sha1-PYyt2Q2XZWn6g1qx+OSyOhBWBac=" 923 | }, 924 | "from": { 925 | "version": "0.1.7", 926 | "resolved": "https://registry.npmjs.org/from/-/from-0.1.7.tgz", 927 | "integrity": "sha1-g8YK/Fi5xWmXAH7Rp2izqzA6RP4=", 928 | "dev": true 929 | }, 930 | "fsevents": { 931 | "version": "1.1.3", 932 | "resolved": "https://registry.npmjs.org/fsevents/-/fsevents-1.1.3.tgz", 933 | "integrity": "sha512-WIr7iDkdmdbxu/Gh6eKEZJL6KPE74/5MEsf2whTOFNxbIoIixogroLdKYqB6FDav4Wavh/lZdzzd3b2KxIXC5Q==", 934 | "dev": true, 935 | "optional": true, 936 | "requires": { 937 | "nan": "2.10.0", 938 | "node-pre-gyp": "0.6.39" 939 | }, 940 | "dependencies": { 941 | "abbrev": { 942 | "version": "1.1.0", 943 | "bundled": true, 944 | "dev": true, 945 | "optional": true 946 | }, 947 | "ajv": { 948 | "version": "4.11.8", 949 | "bundled": true, 950 | "dev": true, 951 | "optional": true, 952 | "requires": { 953 | "co": "4.6.0", 954 | "json-stable-stringify": "1.0.1" 955 | } 956 | }, 957 | "ansi-regex": { 958 | "version": "2.1.1", 959 | "bundled": true, 960 | "dev": true 961 | }, 962 | "aproba": { 963 | "version": "1.1.1", 964 | "bundled": true, 965 | "dev": true, 966 | "optional": true 967 | }, 968 | "are-we-there-yet": { 969 | "version": "1.1.4", 970 | "bundled": true, 971 | "dev": true, 972 | "optional": true, 973 | "requires": { 974 | "delegates": "1.0.0", 975 | "readable-stream": "2.2.9" 976 | } 977 | }, 978 | "asn1": { 979 | "version": "0.2.3", 980 | "bundled": true, 981 | "dev": true, 982 | "optional": true 983 | }, 984 | "assert-plus": { 985 | "version": "0.2.0", 986 | "bundled": true, 987 | "dev": true, 988 | "optional": true 989 | }, 990 | "asynckit": { 991 | "version": "0.4.0", 992 | "bundled": true, 993 | "dev": true, 994 | "optional": true 995 | }, 996 | "aws-sign2": { 997 | "version": "0.6.0", 998 | "bundled": true, 999 | "dev": true, 1000 | "optional": true 1001 | }, 1002 | "aws4": { 1003 | "version": "1.6.0", 1004 | "bundled": true, 1005 | "dev": true, 1006 | "optional": true 1007 | }, 1008 | "balanced-match": { 1009 | "version": "0.4.2", 1010 | "bundled": true, 1011 | "dev": true 1012 | }, 1013 | "bcrypt-pbkdf": { 1014 | "version": "1.0.1", 1015 | "bundled": true, 1016 | "dev": true, 1017 | "optional": true, 1018 | "requires": { 1019 | "tweetnacl": "0.14.5" 1020 | } 1021 | }, 1022 | "block-stream": { 1023 | "version": "0.0.9", 1024 | "bundled": true, 1025 | "dev": true, 1026 | "requires": { 1027 | "inherits": "2.0.3" 1028 | } 1029 | }, 1030 | "boom": { 1031 | "version": "2.10.1", 1032 | "bundled": true, 1033 | "dev": true, 1034 | "requires": { 1035 | "hoek": "2.16.3" 1036 | } 1037 | }, 1038 | "brace-expansion": { 1039 | "version": "1.1.7", 1040 | "bundled": true, 1041 | "dev": true, 1042 | "requires": { 1043 | "balanced-match": "0.4.2", 1044 | "concat-map": "0.0.1" 1045 | } 1046 | }, 1047 | "buffer-shims": { 1048 | "version": "1.0.0", 1049 | "bundled": true, 1050 | "dev": true 1051 | }, 1052 | "caseless": { 1053 | "version": "0.12.0", 1054 | "bundled": true, 1055 | "dev": true, 1056 | "optional": true 1057 | }, 1058 | "co": { 1059 | "version": "4.6.0", 1060 | "bundled": true, 1061 | "dev": true, 1062 | "optional": true 1063 | }, 1064 | "code-point-at": { 1065 | "version": "1.1.0", 1066 | "bundled": true, 1067 | "dev": true 1068 | }, 1069 | "combined-stream": { 1070 | "version": "1.0.5", 1071 | "bundled": true, 1072 | "dev": true, 1073 | "requires": { 1074 | "delayed-stream": "1.0.0" 1075 | } 1076 | }, 1077 | "concat-map": { 1078 | "version": "0.0.1", 1079 | "bundled": true, 1080 | "dev": true 1081 | }, 1082 | "console-control-strings": { 1083 | "version": "1.1.0", 1084 | "bundled": true, 1085 | "dev": true 1086 | }, 1087 | "core-util-is": { 1088 | "version": "1.0.2", 1089 | "bundled": true, 1090 | "dev": true 1091 | }, 1092 | "cryptiles": { 1093 | "version": "2.0.5", 1094 | "bundled": true, 1095 | "dev": true, 1096 | "requires": { 1097 | "boom": "2.10.1" 1098 | } 1099 | }, 1100 | "dashdash": { 1101 | "version": "1.14.1", 1102 | "bundled": true, 1103 | "dev": true, 1104 | "optional": true, 1105 | "requires": { 1106 | "assert-plus": "1.0.0" 1107 | }, 1108 | "dependencies": { 1109 | "assert-plus": { 1110 | "version": "1.0.0", 1111 | "bundled": true, 1112 | "dev": true, 1113 | "optional": true 1114 | } 1115 | } 1116 | }, 1117 | "debug": { 1118 | "version": "2.6.8", 1119 | "bundled": true, 1120 | "dev": true, 1121 | "optional": true, 1122 | "requires": { 1123 | "ms": "2.0.0" 1124 | } 1125 | }, 1126 | "deep-extend": { 1127 | "version": "0.4.2", 1128 | "bundled": true, 1129 | "dev": true, 1130 | "optional": true 1131 | }, 1132 | "delayed-stream": { 1133 | "version": "1.0.0", 1134 | "bundled": true, 1135 | "dev": true 1136 | }, 1137 | "delegates": { 1138 | "version": "1.0.0", 1139 | "bundled": true, 1140 | "dev": true, 1141 | "optional": true 1142 | }, 1143 | "detect-libc": { 1144 | "version": "1.0.2", 1145 | "bundled": true, 1146 | "dev": true, 1147 | "optional": true 1148 | }, 1149 | "ecc-jsbn": { 1150 | "version": "0.1.1", 1151 | "bundled": true, 1152 | "dev": true, 1153 | "optional": true, 1154 | "requires": { 1155 | "jsbn": "0.1.1" 1156 | } 1157 | }, 1158 | "extend": { 1159 | "version": "3.0.1", 1160 | "bundled": true, 1161 | "dev": true, 1162 | "optional": true 1163 | }, 1164 | "extsprintf": { 1165 | "version": "1.0.2", 1166 | "bundled": true, 1167 | "dev": true 1168 | }, 1169 | "forever-agent": { 1170 | "version": "0.6.1", 1171 | "bundled": true, 1172 | "dev": true, 1173 | "optional": true 1174 | }, 1175 | "form-data": { 1176 | "version": "2.1.4", 1177 | "bundled": true, 1178 | "dev": true, 1179 | "optional": true, 1180 | "requires": { 1181 | "asynckit": "0.4.0", 1182 | "combined-stream": "1.0.5", 1183 | "mime-types": "2.1.15" 1184 | } 1185 | }, 1186 | "fs.realpath": { 1187 | "version": "1.0.0", 1188 | "bundled": true, 1189 | "dev": true 1190 | }, 1191 | "fstream": { 1192 | "version": "1.0.11", 1193 | "bundled": true, 1194 | "dev": true, 1195 | "requires": { 1196 | "graceful-fs": "4.1.11", 1197 | "inherits": "2.0.3", 1198 | "mkdirp": "0.5.1", 1199 | "rimraf": "2.6.1" 1200 | } 1201 | }, 1202 | "fstream-ignore": { 1203 | "version": "1.0.5", 1204 | "bundled": true, 1205 | "dev": true, 1206 | "optional": true, 1207 | "requires": { 1208 | "fstream": "1.0.11", 1209 | "inherits": "2.0.3", 1210 | "minimatch": "3.0.4" 1211 | } 1212 | }, 1213 | "gauge": { 1214 | "version": "2.7.4", 1215 | "bundled": true, 1216 | "dev": true, 1217 | "optional": true, 1218 | "requires": { 1219 | "aproba": "1.1.1", 1220 | "console-control-strings": "1.1.0", 1221 | "has-unicode": "2.0.1", 1222 | "object-assign": "4.1.1", 1223 | "signal-exit": "3.0.2", 1224 | "string-width": "1.0.2", 1225 | "strip-ansi": "3.0.1", 1226 | "wide-align": "1.1.2" 1227 | } 1228 | }, 1229 | "getpass": { 1230 | "version": "0.1.7", 1231 | "bundled": true, 1232 | "dev": true, 1233 | "optional": true, 1234 | "requires": { 1235 | "assert-plus": "1.0.0" 1236 | }, 1237 | "dependencies": { 1238 | "assert-plus": { 1239 | "version": "1.0.0", 1240 | "bundled": true, 1241 | "dev": true, 1242 | "optional": true 1243 | } 1244 | } 1245 | }, 1246 | "glob": { 1247 | "version": "7.1.2", 1248 | "bundled": true, 1249 | "dev": true, 1250 | "requires": { 1251 | "fs.realpath": "1.0.0", 1252 | "inflight": "1.0.6", 1253 | "inherits": "2.0.3", 1254 | "minimatch": "3.0.4", 1255 | "once": "1.4.0", 1256 | "path-is-absolute": "1.0.1" 1257 | } 1258 | }, 1259 | "graceful-fs": { 1260 | "version": "4.1.11", 1261 | "bundled": true, 1262 | "dev": true 1263 | }, 1264 | "har-schema": { 1265 | "version": "1.0.5", 1266 | "bundled": true, 1267 | "dev": true, 1268 | "optional": true 1269 | }, 1270 | "har-validator": { 1271 | "version": "4.2.1", 1272 | "bundled": true, 1273 | "dev": true, 1274 | "optional": true, 1275 | "requires": { 1276 | "ajv": "4.11.8", 1277 | "har-schema": "1.0.5" 1278 | } 1279 | }, 1280 | "has-unicode": { 1281 | "version": "2.0.1", 1282 | "bundled": true, 1283 | "dev": true, 1284 | "optional": true 1285 | }, 1286 | "hawk": { 1287 | "version": "3.1.3", 1288 | "bundled": true, 1289 | "dev": true, 1290 | "requires": { 1291 | "boom": "2.10.1", 1292 | "cryptiles": "2.0.5", 1293 | "hoek": "2.16.3", 1294 | "sntp": "1.0.9" 1295 | } 1296 | }, 1297 | "hoek": { 1298 | "version": "2.16.3", 1299 | "bundled": true, 1300 | "dev": true 1301 | }, 1302 | "http-signature": { 1303 | "version": "1.1.1", 1304 | "bundled": true, 1305 | "dev": true, 1306 | "optional": true, 1307 | "requires": { 1308 | "assert-plus": "0.2.0", 1309 | "jsprim": "1.4.0", 1310 | "sshpk": "1.13.0" 1311 | } 1312 | }, 1313 | "inflight": { 1314 | "version": "1.0.6", 1315 | "bundled": true, 1316 | "dev": true, 1317 | "requires": { 1318 | "once": "1.4.0", 1319 | "wrappy": "1.0.2" 1320 | } 1321 | }, 1322 | "inherits": { 1323 | "version": "2.0.3", 1324 | "bundled": true, 1325 | "dev": true 1326 | }, 1327 | "ini": { 1328 | "version": "1.3.4", 1329 | "bundled": true, 1330 | "dev": true, 1331 | "optional": true 1332 | }, 1333 | "is-fullwidth-code-point": { 1334 | "version": "1.0.0", 1335 | "bundled": true, 1336 | "dev": true, 1337 | "requires": { 1338 | "number-is-nan": "1.0.1" 1339 | } 1340 | }, 1341 | "is-typedarray": { 1342 | "version": "1.0.0", 1343 | "bundled": true, 1344 | "dev": true, 1345 | "optional": true 1346 | }, 1347 | "isarray": { 1348 | "version": "1.0.0", 1349 | "bundled": true, 1350 | "dev": true 1351 | }, 1352 | "isstream": { 1353 | "version": "0.1.2", 1354 | "bundled": true, 1355 | "dev": true, 1356 | "optional": true 1357 | }, 1358 | "jodid25519": { 1359 | "version": "1.0.2", 1360 | "bundled": true, 1361 | "dev": true, 1362 | "optional": true, 1363 | "requires": { 1364 | "jsbn": "0.1.1" 1365 | } 1366 | }, 1367 | "jsbn": { 1368 | "version": "0.1.1", 1369 | "bundled": true, 1370 | "dev": true, 1371 | "optional": true 1372 | }, 1373 | "json-schema": { 1374 | "version": "0.2.3", 1375 | "bundled": true, 1376 | "dev": true, 1377 | "optional": true 1378 | }, 1379 | "json-stable-stringify": { 1380 | "version": "1.0.1", 1381 | "bundled": true, 1382 | "dev": true, 1383 | "optional": true, 1384 | "requires": { 1385 | "jsonify": "0.0.0" 1386 | } 1387 | }, 1388 | "json-stringify-safe": { 1389 | "version": "5.0.1", 1390 | "bundled": true, 1391 | "dev": true, 1392 | "optional": true 1393 | }, 1394 | "jsonify": { 1395 | "version": "0.0.0", 1396 | "bundled": true, 1397 | "dev": true, 1398 | "optional": true 1399 | }, 1400 | "jsprim": { 1401 | "version": "1.4.0", 1402 | "bundled": true, 1403 | "dev": true, 1404 | "optional": true, 1405 | "requires": { 1406 | "assert-plus": "1.0.0", 1407 | "extsprintf": "1.0.2", 1408 | "json-schema": "0.2.3", 1409 | "verror": "1.3.6" 1410 | }, 1411 | "dependencies": { 1412 | "assert-plus": { 1413 | "version": "1.0.0", 1414 | "bundled": true, 1415 | "dev": true, 1416 | "optional": true 1417 | } 1418 | } 1419 | }, 1420 | "mime-db": { 1421 | "version": "1.27.0", 1422 | "bundled": true, 1423 | "dev": true 1424 | }, 1425 | "mime-types": { 1426 | "version": "2.1.15", 1427 | "bundled": true, 1428 | "dev": true, 1429 | "requires": { 1430 | "mime-db": "1.27.0" 1431 | } 1432 | }, 1433 | "minimatch": { 1434 | "version": "3.0.4", 1435 | "bundled": true, 1436 | "dev": true, 1437 | "requires": { 1438 | "brace-expansion": "1.1.7" 1439 | } 1440 | }, 1441 | "minimist": { 1442 | "version": "0.0.8", 1443 | "bundled": true, 1444 | "dev": true 1445 | }, 1446 | "mkdirp": { 1447 | "version": "0.5.1", 1448 | "bundled": true, 1449 | "dev": true, 1450 | "requires": { 1451 | "minimist": "0.0.8" 1452 | } 1453 | }, 1454 | "ms": { 1455 | "version": "2.0.0", 1456 | "bundled": true, 1457 | "dev": true, 1458 | "optional": true 1459 | }, 1460 | "node-pre-gyp": { 1461 | "version": "0.6.39", 1462 | "bundled": true, 1463 | "dev": true, 1464 | "optional": true, 1465 | "requires": { 1466 | "detect-libc": "1.0.2", 1467 | "hawk": "3.1.3", 1468 | "mkdirp": "0.5.1", 1469 | "nopt": "4.0.1", 1470 | "npmlog": "4.1.0", 1471 | "rc": "1.2.1", 1472 | "request": "2.81.0", 1473 | "rimraf": "2.6.1", 1474 | "semver": "5.3.0", 1475 | "tar": "2.2.1", 1476 | "tar-pack": "3.4.0" 1477 | } 1478 | }, 1479 | "nopt": { 1480 | "version": "4.0.1", 1481 | "bundled": true, 1482 | "dev": true, 1483 | "optional": true, 1484 | "requires": { 1485 | "abbrev": "1.1.0", 1486 | "osenv": "0.1.4" 1487 | } 1488 | }, 1489 | "npmlog": { 1490 | "version": "4.1.0", 1491 | "bundled": true, 1492 | "dev": true, 1493 | "optional": true, 1494 | "requires": { 1495 | "are-we-there-yet": "1.1.4", 1496 | "console-control-strings": "1.1.0", 1497 | "gauge": "2.7.4", 1498 | "set-blocking": "2.0.0" 1499 | } 1500 | }, 1501 | "number-is-nan": { 1502 | "version": "1.0.1", 1503 | "bundled": true, 1504 | "dev": true 1505 | }, 1506 | "oauth-sign": { 1507 | "version": "0.8.2", 1508 | "bundled": true, 1509 | "dev": true, 1510 | "optional": true 1511 | }, 1512 | "object-assign": { 1513 | "version": "4.1.1", 1514 | "bundled": true, 1515 | "dev": true, 1516 | "optional": true 1517 | }, 1518 | "once": { 1519 | "version": "1.4.0", 1520 | "bundled": true, 1521 | "dev": true, 1522 | "requires": { 1523 | "wrappy": "1.0.2" 1524 | } 1525 | }, 1526 | "os-homedir": { 1527 | "version": "1.0.2", 1528 | "bundled": true, 1529 | "dev": true, 1530 | "optional": true 1531 | }, 1532 | "os-tmpdir": { 1533 | "version": "1.0.2", 1534 | "bundled": true, 1535 | "dev": true, 1536 | "optional": true 1537 | }, 1538 | "osenv": { 1539 | "version": "0.1.4", 1540 | "bundled": true, 1541 | "dev": true, 1542 | "optional": true, 1543 | "requires": { 1544 | "os-homedir": "1.0.2", 1545 | "os-tmpdir": "1.0.2" 1546 | } 1547 | }, 1548 | "path-is-absolute": { 1549 | "version": "1.0.1", 1550 | "bundled": true, 1551 | "dev": true 1552 | }, 1553 | "performance-now": { 1554 | "version": "0.2.0", 1555 | "bundled": true, 1556 | "dev": true, 1557 | "optional": true 1558 | }, 1559 | "process-nextick-args": { 1560 | "version": "1.0.7", 1561 | "bundled": true, 1562 | "dev": true 1563 | }, 1564 | "punycode": { 1565 | "version": "1.4.1", 1566 | "bundled": true, 1567 | "dev": true, 1568 | "optional": true 1569 | }, 1570 | "qs": { 1571 | "version": "6.4.0", 1572 | "bundled": true, 1573 | "dev": true, 1574 | "optional": true 1575 | }, 1576 | "rc": { 1577 | "version": "1.2.1", 1578 | "bundled": true, 1579 | "dev": true, 1580 | "optional": true, 1581 | "requires": { 1582 | "deep-extend": "0.4.2", 1583 | "ini": "1.3.4", 1584 | "minimist": "1.2.0", 1585 | "strip-json-comments": "2.0.1" 1586 | }, 1587 | "dependencies": { 1588 | "minimist": { 1589 | "version": "1.2.0", 1590 | "bundled": true, 1591 | "dev": true, 1592 | "optional": true 1593 | } 1594 | } 1595 | }, 1596 | "readable-stream": { 1597 | "version": "2.2.9", 1598 | "bundled": true, 1599 | "dev": true, 1600 | "requires": { 1601 | "buffer-shims": "1.0.0", 1602 | "core-util-is": "1.0.2", 1603 | "inherits": "2.0.3", 1604 | "isarray": "1.0.0", 1605 | "process-nextick-args": "1.0.7", 1606 | "string_decoder": "1.0.1", 1607 | "util-deprecate": "1.0.2" 1608 | } 1609 | }, 1610 | "request": { 1611 | "version": "2.81.0", 1612 | "bundled": true, 1613 | "dev": true, 1614 | "optional": true, 1615 | "requires": { 1616 | "aws-sign2": "0.6.0", 1617 | "aws4": "1.6.0", 1618 | "caseless": "0.12.0", 1619 | "combined-stream": "1.0.5", 1620 | "extend": "3.0.1", 1621 | "forever-agent": "0.6.1", 1622 | "form-data": "2.1.4", 1623 | "har-validator": "4.2.1", 1624 | "hawk": "3.1.3", 1625 | "http-signature": "1.1.1", 1626 | "is-typedarray": "1.0.0", 1627 | "isstream": "0.1.2", 1628 | "json-stringify-safe": "5.0.1", 1629 | "mime-types": "2.1.15", 1630 | "oauth-sign": "0.8.2", 1631 | "performance-now": "0.2.0", 1632 | "qs": "6.4.0", 1633 | "safe-buffer": "5.0.1", 1634 | "stringstream": "0.0.5", 1635 | "tough-cookie": "2.3.2", 1636 | "tunnel-agent": "0.6.0", 1637 | "uuid": "3.0.1" 1638 | } 1639 | }, 1640 | "rimraf": { 1641 | "version": "2.6.1", 1642 | "bundled": true, 1643 | "dev": true, 1644 | "requires": { 1645 | "glob": "7.1.2" 1646 | } 1647 | }, 1648 | "safe-buffer": { 1649 | "version": "5.0.1", 1650 | "bundled": true, 1651 | "dev": true 1652 | }, 1653 | "semver": { 1654 | "version": "5.3.0", 1655 | "bundled": true, 1656 | "dev": true, 1657 | "optional": true 1658 | }, 1659 | "set-blocking": { 1660 | "version": "2.0.0", 1661 | "bundled": true, 1662 | "dev": true, 1663 | "optional": true 1664 | }, 1665 | "signal-exit": { 1666 | "version": "3.0.2", 1667 | "bundled": true, 1668 | "dev": true, 1669 | "optional": true 1670 | }, 1671 | "sntp": { 1672 | "version": "1.0.9", 1673 | "bundled": true, 1674 | "dev": true, 1675 | "requires": { 1676 | "hoek": "2.16.3" 1677 | } 1678 | }, 1679 | "sshpk": { 1680 | "version": "1.13.0", 1681 | "bundled": true, 1682 | "dev": true, 1683 | "optional": true, 1684 | "requires": { 1685 | "asn1": "0.2.3", 1686 | "assert-plus": "1.0.0", 1687 | "bcrypt-pbkdf": "1.0.1", 1688 | "dashdash": "1.14.1", 1689 | "ecc-jsbn": "0.1.1", 1690 | "getpass": "0.1.7", 1691 | "jodid25519": "1.0.2", 1692 | "jsbn": "0.1.1", 1693 | "tweetnacl": "0.14.5" 1694 | }, 1695 | "dependencies": { 1696 | "assert-plus": { 1697 | "version": "1.0.0", 1698 | "bundled": true, 1699 | "dev": true, 1700 | "optional": true 1701 | } 1702 | } 1703 | }, 1704 | "string-width": { 1705 | "version": "1.0.2", 1706 | "bundled": true, 1707 | "dev": true, 1708 | "requires": { 1709 | "code-point-at": "1.1.0", 1710 | "is-fullwidth-code-point": "1.0.0", 1711 | "strip-ansi": "3.0.1" 1712 | } 1713 | }, 1714 | "string_decoder": { 1715 | "version": "1.0.1", 1716 | "bundled": true, 1717 | "dev": true, 1718 | "requires": { 1719 | "safe-buffer": "5.0.1" 1720 | } 1721 | }, 1722 | "stringstream": { 1723 | "version": "0.0.5", 1724 | "bundled": true, 1725 | "dev": true, 1726 | "optional": true 1727 | }, 1728 | "strip-ansi": { 1729 | "version": "3.0.1", 1730 | "bundled": true, 1731 | "dev": true, 1732 | "requires": { 1733 | "ansi-regex": "2.1.1" 1734 | } 1735 | }, 1736 | "strip-json-comments": { 1737 | "version": "2.0.1", 1738 | "bundled": true, 1739 | "dev": true, 1740 | "optional": true 1741 | }, 1742 | "tar": { 1743 | "version": "2.2.1", 1744 | "bundled": true, 1745 | "dev": true, 1746 | "requires": { 1747 | "block-stream": "0.0.9", 1748 | "fstream": "1.0.11", 1749 | "inherits": "2.0.3" 1750 | } 1751 | }, 1752 | "tar-pack": { 1753 | "version": "3.4.0", 1754 | "bundled": true, 1755 | "dev": true, 1756 | "optional": true, 1757 | "requires": { 1758 | "debug": "2.6.8", 1759 | "fstream": "1.0.11", 1760 | "fstream-ignore": "1.0.5", 1761 | "once": "1.4.0", 1762 | "readable-stream": "2.2.9", 1763 | "rimraf": "2.6.1", 1764 | "tar": "2.2.1", 1765 | "uid-number": "0.0.6" 1766 | } 1767 | }, 1768 | "tough-cookie": { 1769 | "version": "2.3.2", 1770 | "bundled": true, 1771 | "dev": true, 1772 | "optional": true, 1773 | "requires": { 1774 | "punycode": "1.4.1" 1775 | } 1776 | }, 1777 | "tunnel-agent": { 1778 | "version": "0.6.0", 1779 | "bundled": true, 1780 | "dev": true, 1781 | "optional": true, 1782 | "requires": { 1783 | "safe-buffer": "5.0.1" 1784 | } 1785 | }, 1786 | "tweetnacl": { 1787 | "version": "0.14.5", 1788 | "bundled": true, 1789 | "dev": true, 1790 | "optional": true 1791 | }, 1792 | "uid-number": { 1793 | "version": "0.0.6", 1794 | "bundled": true, 1795 | "dev": true, 1796 | "optional": true 1797 | }, 1798 | "util-deprecate": { 1799 | "version": "1.0.2", 1800 | "bundled": true, 1801 | "dev": true 1802 | }, 1803 | "uuid": { 1804 | "version": "3.0.1", 1805 | "bundled": true, 1806 | "dev": true, 1807 | "optional": true 1808 | }, 1809 | "verror": { 1810 | "version": "1.3.6", 1811 | "bundled": true, 1812 | "dev": true, 1813 | "optional": true, 1814 | "requires": { 1815 | "extsprintf": "1.0.2" 1816 | } 1817 | }, 1818 | "wide-align": { 1819 | "version": "1.1.2", 1820 | "bundled": true, 1821 | "dev": true, 1822 | "optional": true, 1823 | "requires": { 1824 | "string-width": "1.0.2" 1825 | } 1826 | }, 1827 | "wrappy": { 1828 | "version": "1.0.2", 1829 | "bundled": true, 1830 | "dev": true 1831 | } 1832 | } 1833 | }, 1834 | "get-stream": { 1835 | "version": "3.0.0", 1836 | "resolved": "https://registry.npmjs.org/get-stream/-/get-stream-3.0.0.tgz", 1837 | "integrity": "sha1-jpQ9E1jcN1VQVOy+LtsFqhdO3hQ=", 1838 | "dev": true 1839 | }, 1840 | "get-value": { 1841 | "version": "2.0.6", 1842 | "resolved": "https://registry.npmjs.org/get-value/-/get-value-2.0.6.tgz", 1843 | "integrity": "sha1-3BXKHGcjh8p2vTesCjlbogQqLCg=", 1844 | "dev": true 1845 | }, 1846 | "glob-parent": { 1847 | "version": "3.1.0", 1848 | "resolved": "https://registry.npmjs.org/glob-parent/-/glob-parent-3.1.0.tgz", 1849 | "integrity": "sha1-nmr2KZ2NO9K9QEMIMr0RPfkGxa4=", 1850 | "dev": true, 1851 | "requires": { 1852 | "is-glob": "3.1.0", 1853 | "path-dirname": "1.0.2" 1854 | }, 1855 | "dependencies": { 1856 | "is-glob": { 1857 | "version": "3.1.0", 1858 | "resolved": "https://registry.npmjs.org/is-glob/-/is-glob-3.1.0.tgz", 1859 | "integrity": "sha1-e6WuJCF4BKxwcHuWkiVnSGzD6Eo=", 1860 | "dev": true, 1861 | "requires": { 1862 | "is-extglob": "2.1.1" 1863 | } 1864 | } 1865 | } 1866 | }, 1867 | "global-dirs": { 1868 | "version": "0.1.1", 1869 | "resolved": "https://registry.npmjs.org/global-dirs/-/global-dirs-0.1.1.tgz", 1870 | "integrity": "sha1-sxnA3UYH81PzvpzKTHL8FIxJ9EU=", 1871 | "dev": true, 1872 | "requires": { 1873 | "ini": "1.3.5" 1874 | } 1875 | }, 1876 | "got": { 1877 | "version": "6.7.1", 1878 | "resolved": "https://registry.npmjs.org/got/-/got-6.7.1.tgz", 1879 | "integrity": "sha1-JAzQV4WpoY5WHcG0S0HHY+8ejbA=", 1880 | "dev": true, 1881 | "requires": { 1882 | "create-error-class": "3.0.2", 1883 | "duplexer3": "0.1.4", 1884 | "get-stream": "3.0.0", 1885 | "is-redirect": "1.0.0", 1886 | "is-retry-allowed": "1.1.0", 1887 | "is-stream": "1.1.0", 1888 | "lowercase-keys": "1.0.1", 1889 | "safe-buffer": "5.1.1", 1890 | "timed-out": "4.0.1", 1891 | "unzip-response": "2.0.1", 1892 | "url-parse-lax": "1.0.0" 1893 | } 1894 | }, 1895 | "graceful-fs": { 1896 | "version": "4.1.11", 1897 | "resolved": "https://registry.npmjs.org/graceful-fs/-/graceful-fs-4.1.11.tgz", 1898 | "integrity": "sha1-Dovf5NHduIVNZOBOp8AOKgJuVlg=", 1899 | "dev": true 1900 | }, 1901 | "has-flag": { 1902 | "version": "3.0.0", 1903 | "resolved": "https://registry.npmjs.org/has-flag/-/has-flag-3.0.0.tgz", 1904 | "integrity": "sha1-tdRU3CGZriJWmfNGfloH87lVuv0=", 1905 | "dev": true 1906 | }, 1907 | "has-value": { 1908 | "version": "1.0.0", 1909 | "resolved": "https://registry.npmjs.org/has-value/-/has-value-1.0.0.tgz", 1910 | "integrity": "sha1-GLKB2lhbHFxR3vJMkw7SmgvmsXc=", 1911 | "dev": true, 1912 | "requires": { 1913 | "get-value": "2.0.6", 1914 | "has-values": "1.0.0", 1915 | "isobject": "3.0.1" 1916 | } 1917 | }, 1918 | "has-values": { 1919 | "version": "1.0.0", 1920 | "resolved": "https://registry.npmjs.org/has-values/-/has-values-1.0.0.tgz", 1921 | "integrity": "sha1-lbC2P+whRmGab+V/51Yo1aOe/k8=", 1922 | "dev": true, 1923 | "requires": { 1924 | "is-number": "3.0.0", 1925 | "kind-of": "4.0.0" 1926 | }, 1927 | "dependencies": { 1928 | "kind-of": { 1929 | "version": "4.0.0", 1930 | "resolved": "https://registry.npmjs.org/kind-of/-/kind-of-4.0.0.tgz", 1931 | "integrity": "sha1-IIE989cSkosgc3hpGkUGb65y3Vc=", 1932 | "dev": true, 1933 | "requires": { 1934 | "is-buffer": "1.1.6" 1935 | } 1936 | } 1937 | } 1938 | }, 1939 | "http-errors": { 1940 | "version": "1.6.3", 1941 | "resolved": "https://registry.npmjs.org/http-errors/-/http-errors-1.6.3.tgz", 1942 | "integrity": "sha1-i1VoC7S+KDoLW/TqLjhYC+HZMg0=", 1943 | "requires": { 1944 | "depd": "1.1.2", 1945 | "inherits": "2.0.3", 1946 | "setprototypeof": "1.1.0", 1947 | "statuses": "1.4.0" 1948 | } 1949 | }, 1950 | "iconv-lite": { 1951 | "version": "0.4.19", 1952 | "resolved": "https://registry.npmjs.org/iconv-lite/-/iconv-lite-0.4.19.tgz", 1953 | "integrity": "sha512-oTZqweIP51xaGPI4uPa56/Pri/480R+mo7SeU+YETByQNhDG55ycFyNLIgta9vXhILrxXDmF7ZGhqZIcuN0gJQ==" 1954 | }, 1955 | "ignore-by-default": { 1956 | "version": "1.0.1", 1957 | "resolved": "https://registry.npmjs.org/ignore-by-default/-/ignore-by-default-1.0.1.tgz", 1958 | "integrity": "sha1-SMptcvbGo68Aqa1K5odr44ieKwk=", 1959 | "dev": true 1960 | }, 1961 | "import-lazy": { 1962 | "version": "2.1.0", 1963 | "resolved": "https://registry.npmjs.org/import-lazy/-/import-lazy-2.1.0.tgz", 1964 | "integrity": "sha1-BWmOPUXIjo1+nZLLBYTnfwlvPkM=", 1965 | "dev": true 1966 | }, 1967 | "imurmurhash": { 1968 | "version": "0.1.4", 1969 | "resolved": "https://registry.npmjs.org/imurmurhash/-/imurmurhash-0.1.4.tgz", 1970 | "integrity": "sha1-khi5srkoojixPcT7a21XbyMUU+o=", 1971 | "dev": true 1972 | }, 1973 | "inherits": { 1974 | "version": "2.0.3", 1975 | "resolved": "https://registry.npmjs.org/inherits/-/inherits-2.0.3.tgz", 1976 | "integrity": "sha1-Yzwsg+PaQqUC9SRmAiSA9CCCYd4=" 1977 | }, 1978 | "ini": { 1979 | "version": "1.3.5", 1980 | "resolved": "https://registry.npmjs.org/ini/-/ini-1.3.5.tgz", 1981 | "integrity": "sha512-RZY5huIKCMRWDUqZlEi72f/lmXKMvuszcMBduliQ3nnWbx9X/ZBQO7DijMEYS9EhHBb2qacRUMtC7svLwe0lcw==", 1982 | "dev": true 1983 | }, 1984 | "ipaddr.js": { 1985 | "version": "1.6.0", 1986 | "resolved": "https://registry.npmjs.org/ipaddr.js/-/ipaddr.js-1.6.0.tgz", 1987 | "integrity": "sha1-4/o1e3c9phnybpXwSdBVxyeW+Gs=" 1988 | }, 1989 | "is-accessor-descriptor": { 1990 | "version": "0.1.6", 1991 | "resolved": "https://registry.npmjs.org/is-accessor-descriptor/-/is-accessor-descriptor-0.1.6.tgz", 1992 | "integrity": "sha1-qeEss66Nh2cn7u84Q/igiXtcmNY=", 1993 | "dev": true, 1994 | "requires": { 1995 | "kind-of": "3.2.2" 1996 | }, 1997 | "dependencies": { 1998 | "kind-of": { 1999 | "version": "3.2.2", 2000 | "resolved": "https://registry.npmjs.org/kind-of/-/kind-of-3.2.2.tgz", 2001 | "integrity": "sha1-MeohpzS6ubuw8yRm2JOupR5KPGQ=", 2002 | "dev": true, 2003 | "requires": { 2004 | "is-buffer": "1.1.6" 2005 | } 2006 | } 2007 | } 2008 | }, 2009 | "is-binary-path": { 2010 | "version": "1.0.1", 2011 | "resolved": "https://registry.npmjs.org/is-binary-path/-/is-binary-path-1.0.1.tgz", 2012 | "integrity": "sha1-dfFmQrSA8YenEcgUFh/TpKdlWJg=", 2013 | "dev": true, 2014 | "requires": { 2015 | "binary-extensions": "1.11.0" 2016 | } 2017 | }, 2018 | "is-buffer": { 2019 | "version": "1.1.6", 2020 | "resolved": "https://registry.npmjs.org/is-buffer/-/is-buffer-1.1.6.tgz", 2021 | "integrity": "sha512-NcdALwpXkTm5Zvvbk7owOUSvVvBKDgKP5/ewfXEznmQFfs4ZRmanOeKBTjRVjka3QFoN6XJ+9F3USqfHqTaU5w==" 2022 | }, 2023 | "is-ci": { 2024 | "version": "1.1.0", 2025 | "resolved": "https://registry.npmjs.org/is-ci/-/is-ci-1.1.0.tgz", 2026 | "integrity": "sha512-c7TnwxLePuqIlxHgr7xtxzycJPegNHFuIrBkwbf8hc58//+Op1CqFkyS+xnIMkwn9UsJIwc174BIjkyBmSpjKg==", 2027 | "dev": true, 2028 | "requires": { 2029 | "ci-info": "1.1.3" 2030 | } 2031 | }, 2032 | "is-data-descriptor": { 2033 | "version": "0.1.4", 2034 | "resolved": "https://registry.npmjs.org/is-data-descriptor/-/is-data-descriptor-0.1.4.tgz", 2035 | "integrity": "sha1-C17mSDiOLIYCgueT8YVv7D8wG1Y=", 2036 | "dev": true, 2037 | "requires": { 2038 | "kind-of": "3.2.2" 2039 | }, 2040 | "dependencies": { 2041 | "kind-of": { 2042 | "version": "3.2.2", 2043 | "resolved": "https://registry.npmjs.org/kind-of/-/kind-of-3.2.2.tgz", 2044 | "integrity": "sha1-MeohpzS6ubuw8yRm2JOupR5KPGQ=", 2045 | "dev": true, 2046 | "requires": { 2047 | "is-buffer": "1.1.6" 2048 | } 2049 | } 2050 | } 2051 | }, 2052 | "is-descriptor": { 2053 | "version": "0.1.6", 2054 | "resolved": "https://registry.npmjs.org/is-descriptor/-/is-descriptor-0.1.6.tgz", 2055 | "integrity": "sha512-avDYr0SB3DwO9zsMov0gKCESFYqCnE4hq/4z3TdUlukEy5t9C0YRq7HLrsN52NAcqXKaepeCD0n+B0arnVG3Hg==", 2056 | "dev": true, 2057 | "requires": { 2058 | "is-accessor-descriptor": "0.1.6", 2059 | "is-data-descriptor": "0.1.4", 2060 | "kind-of": "5.1.0" 2061 | }, 2062 | "dependencies": { 2063 | "kind-of": { 2064 | "version": "5.1.0", 2065 | "resolved": "https://registry.npmjs.org/kind-of/-/kind-of-5.1.0.tgz", 2066 | "integrity": "sha512-NGEErnH6F2vUuXDh+OlbcKW7/wOcfdRHaZ7VWtqCztfHri/++YKmP51OdWeGPuqCOba6kk2OTe5d02VmTB80Pw==", 2067 | "dev": true 2068 | } 2069 | } 2070 | }, 2071 | "is-extendable": { 2072 | "version": "0.1.1", 2073 | "resolved": "https://registry.npmjs.org/is-extendable/-/is-extendable-0.1.1.tgz", 2074 | "integrity": "sha1-YrEQ4omkcUGOPsNqYX1HLjAd/Ik=", 2075 | "dev": true 2076 | }, 2077 | "is-extglob": { 2078 | "version": "2.1.1", 2079 | "resolved": "https://registry.npmjs.org/is-extglob/-/is-extglob-2.1.1.tgz", 2080 | "integrity": "sha1-qIwCU1eR8C7TfHahueqXc8gz+MI=", 2081 | "dev": true 2082 | }, 2083 | "is-fullwidth-code-point": { 2084 | "version": "2.0.0", 2085 | "resolved": "https://registry.npmjs.org/is-fullwidth-code-point/-/is-fullwidth-code-point-2.0.0.tgz", 2086 | "integrity": "sha1-o7MKXE8ZkYMWeqq5O+764937ZU8=", 2087 | "dev": true 2088 | }, 2089 | "is-glob": { 2090 | "version": "4.0.0", 2091 | "resolved": "https://registry.npmjs.org/is-glob/-/is-glob-4.0.0.tgz", 2092 | "integrity": "sha1-lSHHaEXMJhCoUgPd8ICpWML/q8A=", 2093 | "dev": true, 2094 | "requires": { 2095 | "is-extglob": "2.1.1" 2096 | } 2097 | }, 2098 | "is-installed-globally": { 2099 | "version": "0.1.0", 2100 | "resolved": "https://registry.npmjs.org/is-installed-globally/-/is-installed-globally-0.1.0.tgz", 2101 | "integrity": "sha1-Df2Y9akRFxbdU13aZJL2e/PSWoA=", 2102 | "dev": true, 2103 | "requires": { 2104 | "global-dirs": "0.1.1", 2105 | "is-path-inside": "1.0.1" 2106 | } 2107 | }, 2108 | "is-npm": { 2109 | "version": "1.0.0", 2110 | "resolved": "https://registry.npmjs.org/is-npm/-/is-npm-1.0.0.tgz", 2111 | "integrity": "sha1-8vtjpl5JBbQGyGBydloaTceTufQ=", 2112 | "dev": true 2113 | }, 2114 | "is-number": { 2115 | "version": "3.0.0", 2116 | "resolved": "https://registry.npmjs.org/is-number/-/is-number-3.0.0.tgz", 2117 | "integrity": "sha1-JP1iAaR4LPUFYcgQJ2r8fRLXEZU=", 2118 | "dev": true, 2119 | "requires": { 2120 | "kind-of": "3.2.2" 2121 | }, 2122 | "dependencies": { 2123 | "kind-of": { 2124 | "version": "3.2.2", 2125 | "resolved": "https://registry.npmjs.org/kind-of/-/kind-of-3.2.2.tgz", 2126 | "integrity": "sha1-MeohpzS6ubuw8yRm2JOupR5KPGQ=", 2127 | "dev": true, 2128 | "requires": { 2129 | "is-buffer": "1.1.6" 2130 | } 2131 | } 2132 | } 2133 | }, 2134 | "is-obj": { 2135 | "version": "1.0.1", 2136 | "resolved": "https://registry.npmjs.org/is-obj/-/is-obj-1.0.1.tgz", 2137 | "integrity": "sha1-PkcprB9f3gJc19g6iW2rn09n2w8=", 2138 | "dev": true 2139 | }, 2140 | "is-odd": { 2141 | "version": "2.0.0", 2142 | "resolved": "https://registry.npmjs.org/is-odd/-/is-odd-2.0.0.tgz", 2143 | "integrity": "sha512-OTiixgpZAT1M4NHgS5IguFp/Vz2VI3U7Goh4/HA1adtwyLtSBrxYlcSYkhpAE07s4fKEcjrFxyvtQBND4vFQyQ==", 2144 | "dev": true, 2145 | "requires": { 2146 | "is-number": "4.0.0" 2147 | }, 2148 | "dependencies": { 2149 | "is-number": { 2150 | "version": "4.0.0", 2151 | "resolved": "https://registry.npmjs.org/is-number/-/is-number-4.0.0.tgz", 2152 | "integrity": "sha512-rSklcAIlf1OmFdyAqbnWTLVelsQ58uvZ66S/ZyawjWqIviTWCjg2PzVGw8WUA+nNuPTqb4wgA+NszrJ+08LlgQ==", 2153 | "dev": true 2154 | } 2155 | } 2156 | }, 2157 | "is-path-inside": { 2158 | "version": "1.0.1", 2159 | "resolved": "https://registry.npmjs.org/is-path-inside/-/is-path-inside-1.0.1.tgz", 2160 | "integrity": "sha1-jvW33lBDej/cprToZe96pVy0gDY=", 2161 | "dev": true, 2162 | "requires": { 2163 | "path-is-inside": "1.0.2" 2164 | } 2165 | }, 2166 | "is-plain-object": { 2167 | "version": "2.0.4", 2168 | "resolved": "https://registry.npmjs.org/is-plain-object/-/is-plain-object-2.0.4.tgz", 2169 | "integrity": "sha512-h5PpgXkWitc38BBMYawTYMWJHFZJVnBquFE57xFpjB8pJFiF6gZ+bU+WyI/yqXiFR5mdLsgYNaPe8uao6Uv9Og==", 2170 | "dev": true, 2171 | "requires": { 2172 | "isobject": "3.0.1" 2173 | } 2174 | }, 2175 | "is-redirect": { 2176 | "version": "1.0.0", 2177 | "resolved": "https://registry.npmjs.org/is-redirect/-/is-redirect-1.0.0.tgz", 2178 | "integrity": "sha1-HQPd7VO9jbDzDCbk+V02/HyH3CQ=", 2179 | "dev": true 2180 | }, 2181 | "is-retry-allowed": { 2182 | "version": "1.1.0", 2183 | "resolved": "https://registry.npmjs.org/is-retry-allowed/-/is-retry-allowed-1.1.0.tgz", 2184 | "integrity": "sha1-EaBgVotnM5REAz0BJaYaINVk+zQ=", 2185 | "dev": true 2186 | }, 2187 | "is-stream": { 2188 | "version": "1.1.0", 2189 | "resolved": "https://registry.npmjs.org/is-stream/-/is-stream-1.1.0.tgz", 2190 | "integrity": "sha1-EtSj3U5o4Lec6428hBc66A2RykQ=", 2191 | "dev": true 2192 | }, 2193 | "is-windows": { 2194 | "version": "1.0.2", 2195 | "resolved": "https://registry.npmjs.org/is-windows/-/is-windows-1.0.2.tgz", 2196 | "integrity": "sha512-eXK1UInq2bPmjyX6e3VHIzMLobc4J94i4AWn+Hpq3OU5KkrRC96OAcR3PRJ/pGu6m8TRnBHP9dkXQVsT/COVIA==", 2197 | "dev": true 2198 | }, 2199 | "isarray": { 2200 | "version": "1.0.0", 2201 | "resolved": "https://registry.npmjs.org/isarray/-/isarray-1.0.0.tgz", 2202 | "integrity": "sha1-u5NdSFgsuhaMBoNJV6VKPgcSTxE=", 2203 | "dev": true 2204 | }, 2205 | "isexe": { 2206 | "version": "2.0.0", 2207 | "resolved": "https://registry.npmjs.org/isexe/-/isexe-2.0.0.tgz", 2208 | "integrity": "sha1-6PvzdNxVb/iUehDcsFctYz8s+hA=", 2209 | "dev": true 2210 | }, 2211 | "isobject": { 2212 | "version": "3.0.1", 2213 | "resolved": "https://registry.npmjs.org/isobject/-/isobject-3.0.1.tgz", 2214 | "integrity": "sha1-TkMekrEalzFjaqH5yNHMvP2reN8=", 2215 | "dev": true 2216 | }, 2217 | "jsonwebtoken": { 2218 | "version": "8.2.1", 2219 | "resolved": "https://registry.npmjs.org/jsonwebtoken/-/jsonwebtoken-8.2.1.tgz", 2220 | "integrity": "sha512-l8rUBr0fqYYwPc8/ZGrue7GiW7vWdZtZqelxo4Sd5lMvuEeCK8/wS54sEo6tJhdZ6hqfutsj6COgC0d1XdbHGw==", 2221 | "requires": { 2222 | "jws": "3.1.4", 2223 | "lodash.includes": "4.3.0", 2224 | "lodash.isboolean": "3.0.3", 2225 | "lodash.isinteger": "4.0.4", 2226 | "lodash.isnumber": "3.0.3", 2227 | "lodash.isplainobject": "4.0.6", 2228 | "lodash.isstring": "4.0.1", 2229 | "lodash.once": "4.1.1", 2230 | "ms": "2.1.1", 2231 | "xtend": "4.0.1" 2232 | }, 2233 | "dependencies": { 2234 | "ms": { 2235 | "version": "2.1.1", 2236 | "resolved": "https://registry.npmjs.org/ms/-/ms-2.1.1.tgz", 2237 | "integrity": "sha512-tgp+dl5cGk28utYktBsrFqA7HKgrhgPsg6Z/EfhWI4gl1Hwq8B/GmY/0oXZ6nF8hDVesS/FpnYaD/kOWhYQvyg==" 2238 | } 2239 | } 2240 | }, 2241 | "jwa": { 2242 | "version": "1.1.5", 2243 | "resolved": "https://registry.npmjs.org/jwa/-/jwa-1.1.5.tgz", 2244 | "integrity": "sha1-oFUs4CIHQs1S4VN3SjKQXDDnVuU=", 2245 | "requires": { 2246 | "base64url": "2.0.0", 2247 | "buffer-equal-constant-time": "1.0.1", 2248 | "ecdsa-sig-formatter": "1.0.9", 2249 | "safe-buffer": "5.1.1" 2250 | } 2251 | }, 2252 | "jws": { 2253 | "version": "3.1.4", 2254 | "resolved": "https://registry.npmjs.org/jws/-/jws-3.1.4.tgz", 2255 | "integrity": "sha1-+ei5M46KhHJ31kRLFGT2GIDgUKI=", 2256 | "requires": { 2257 | "base64url": "2.0.0", 2258 | "jwa": "1.1.5", 2259 | "safe-buffer": "5.1.1" 2260 | } 2261 | }, 2262 | "kind-of": { 2263 | "version": "6.0.2", 2264 | "resolved": "https://registry.npmjs.org/kind-of/-/kind-of-6.0.2.tgz", 2265 | "integrity": "sha512-s5kLOcnH0XqDO+FvuaLX8DDjZ18CGFk7VygH40QoKPUQhW4e2rvM0rwUq0t8IQDOwYSeLK01U90OjzBTme2QqA==", 2266 | "dev": true 2267 | }, 2268 | "latest-version": { 2269 | "version": "3.1.0", 2270 | "resolved": "https://registry.npmjs.org/latest-version/-/latest-version-3.1.0.tgz", 2271 | "integrity": "sha1-ogU4P+oyKzO1rjsYq+4NwvNW7hU=", 2272 | "dev": true, 2273 | "requires": { 2274 | "package-json": "4.0.1" 2275 | } 2276 | }, 2277 | "lodash.includes": { 2278 | "version": "4.3.0", 2279 | "resolved": "https://registry.npmjs.org/lodash.includes/-/lodash.includes-4.3.0.tgz", 2280 | "integrity": "sha1-YLuYqHy5I8aMoeUTJUgzFISfVT8=" 2281 | }, 2282 | "lodash.isboolean": { 2283 | "version": "3.0.3", 2284 | "resolved": "https://registry.npmjs.org/lodash.isboolean/-/lodash.isboolean-3.0.3.tgz", 2285 | "integrity": "sha1-bC4XHbKiV82WgC/UOwGyDV9YcPY=" 2286 | }, 2287 | "lodash.isinteger": { 2288 | "version": "4.0.4", 2289 | "resolved": "https://registry.npmjs.org/lodash.isinteger/-/lodash.isinteger-4.0.4.tgz", 2290 | "integrity": "sha1-YZwK89A/iwTDH1iChAt3sRzWg0M=" 2291 | }, 2292 | "lodash.isnumber": { 2293 | "version": "3.0.3", 2294 | "resolved": "https://registry.npmjs.org/lodash.isnumber/-/lodash.isnumber-3.0.3.tgz", 2295 | "integrity": "sha1-POdoEMWSjQM1IwGsKHMX8RwLH/w=" 2296 | }, 2297 | "lodash.isplainobject": { 2298 | "version": "4.0.6", 2299 | "resolved": "https://registry.npmjs.org/lodash.isplainobject/-/lodash.isplainobject-4.0.6.tgz", 2300 | "integrity": "sha1-fFJqUtibRcRcxpC4gWO+BJf1UMs=" 2301 | }, 2302 | "lodash.isstring": { 2303 | "version": "4.0.1", 2304 | "resolved": "https://registry.npmjs.org/lodash.isstring/-/lodash.isstring-4.0.1.tgz", 2305 | "integrity": "sha1-1SfftUVuynzJu5XV2ur4i6VKVFE=" 2306 | }, 2307 | "lodash.once": { 2308 | "version": "4.1.1", 2309 | "resolved": "https://registry.npmjs.org/lodash.once/-/lodash.once-4.1.1.tgz", 2310 | "integrity": "sha1-DdOXEhPHxW34gJd9UEyI+0cal6w=" 2311 | }, 2312 | "lodash.set": { 2313 | "version": "4.3.2", 2314 | "resolved": "https://registry.npmjs.org/lodash.set/-/lodash.set-4.3.2.tgz", 2315 | "integrity": "sha1-2HV7HagH3eJIFrDWqEvqGnYjCyM=" 2316 | }, 2317 | "lowercase-keys": { 2318 | "version": "1.0.1", 2319 | "resolved": "https://registry.npmjs.org/lowercase-keys/-/lowercase-keys-1.0.1.tgz", 2320 | "integrity": "sha512-G2Lj61tXDnVFFOi8VZds+SoQjtQC3dgokKdDG2mTm1tx4m50NUHBOZSBwQQHyy0V12A0JTG4icfZQH+xPyh8VA==", 2321 | "dev": true 2322 | }, 2323 | "lru-cache": { 2324 | "version": "4.1.2", 2325 | "resolved": "https://registry.npmjs.org/lru-cache/-/lru-cache-4.1.2.tgz", 2326 | "integrity": "sha512-wgeVXhrDwAWnIF/yZARsFnMBtdFXOg1b8RIrhilp+0iDYN4mdQcNZElDZ0e4B64BhaxeQ5zN7PMyvu7we1kPeQ==", 2327 | "dev": true, 2328 | "requires": { 2329 | "pseudomap": "1.0.2", 2330 | "yallist": "2.1.2" 2331 | } 2332 | }, 2333 | "make-dir": { 2334 | "version": "1.2.0", 2335 | "resolved": "https://registry.npmjs.org/make-dir/-/make-dir-1.2.0.tgz", 2336 | "integrity": "sha512-aNUAa4UMg/UougV25bbrU4ZaaKNjJ/3/xnvg/twpmKROPdKZPZ9wGgI0opdZzO8q/zUFawoUuixuOv33eZ61Iw==", 2337 | "dev": true, 2338 | "requires": { 2339 | "pify": "3.0.0" 2340 | } 2341 | }, 2342 | "map-cache": { 2343 | "version": "0.2.2", 2344 | "resolved": "https://registry.npmjs.org/map-cache/-/map-cache-0.2.2.tgz", 2345 | "integrity": "sha1-wyq9C9ZSXZsFFkW7TyasXcmKDb8=", 2346 | "dev": true 2347 | }, 2348 | "map-stream": { 2349 | "version": "0.1.0", 2350 | "resolved": "https://registry.npmjs.org/map-stream/-/map-stream-0.1.0.tgz", 2351 | "integrity": "sha1-5WqpTEyAVaFkBKBnS3jyFffI4ZQ=", 2352 | "dev": true 2353 | }, 2354 | "map-visit": { 2355 | "version": "1.0.0", 2356 | "resolved": "https://registry.npmjs.org/map-visit/-/map-visit-1.0.0.tgz", 2357 | "integrity": "sha1-7Nyo8TFE5mDxtb1B8S80edmN+48=", 2358 | "dev": true, 2359 | "requires": { 2360 | "object-visit": "1.0.1" 2361 | } 2362 | }, 2363 | "media-typer": { 2364 | "version": "0.3.0", 2365 | "resolved": "https://registry.npmjs.org/media-typer/-/media-typer-0.3.0.tgz", 2366 | "integrity": "sha1-hxDXrwqmJvj/+hzgAWhUUmMlV0g=" 2367 | }, 2368 | "merge-descriptors": { 2369 | "version": "1.0.1", 2370 | "resolved": "https://registry.npmjs.org/merge-descriptors/-/merge-descriptors-1.0.1.tgz", 2371 | "integrity": "sha1-sAqqVW3YtEVoFQ7J0blT8/kMu2E=" 2372 | }, 2373 | "methods": { 2374 | "version": "1.1.2", 2375 | "resolved": "https://registry.npmjs.org/methods/-/methods-1.1.2.tgz", 2376 | "integrity": "sha1-VSmk1nZUE07cxSZmVoNbD4Ua/O4=" 2377 | }, 2378 | "micromatch": { 2379 | "version": "3.1.10", 2380 | "resolved": "https://registry.npmjs.org/micromatch/-/micromatch-3.1.10.tgz", 2381 | "integrity": "sha512-MWikgl9n9M3w+bpsY3He8L+w9eF9338xRl8IAO5viDizwSzziFEyUzo2xrrloB64ADbTf8uA8vRqqttDTOmccg==", 2382 | "dev": true, 2383 | "requires": { 2384 | "arr-diff": "4.0.0", 2385 | "array-unique": "0.3.2", 2386 | "braces": "2.3.2", 2387 | "define-property": "2.0.2", 2388 | "extend-shallow": "3.0.2", 2389 | "extglob": "2.0.4", 2390 | "fragment-cache": "0.2.1", 2391 | "kind-of": "6.0.2", 2392 | "nanomatch": "1.2.9", 2393 | "object.pick": "1.3.0", 2394 | "regex-not": "1.0.2", 2395 | "snapdragon": "0.8.2", 2396 | "to-regex": "3.0.2" 2397 | } 2398 | }, 2399 | "mime": { 2400 | "version": "1.4.1", 2401 | "resolved": "https://registry.npmjs.org/mime/-/mime-1.4.1.tgz", 2402 | "integrity": "sha512-KI1+qOZu5DcW6wayYHSzR/tXKCDC5Om4s1z2QJjDULzLcmf3DvzS7oluY4HCTrc+9FiKmWUgeNLg7W3uIQvxtQ==" 2403 | }, 2404 | "mime-db": { 2405 | "version": "1.33.0", 2406 | "resolved": "https://registry.npmjs.org/mime-db/-/mime-db-1.33.0.tgz", 2407 | "integrity": "sha512-BHJ/EKruNIqJf/QahvxwQZXKygOQ256myeN/Ew+THcAa5q+PjyTTMMeNQC4DZw5AwfvelsUrA6B67NKMqXDbzQ==" 2408 | }, 2409 | "mime-types": { 2410 | "version": "2.1.18", 2411 | "resolved": "https://registry.npmjs.org/mime-types/-/mime-types-2.1.18.tgz", 2412 | "integrity": "sha512-lc/aahn+t4/SWV/qcmumYjymLsWfN3ELhpmVuUFjgsORruuZPVSwAQryq+HHGvO/SI2KVX26bx+En+zhM8g8hQ==", 2413 | "requires": { 2414 | "mime-db": "1.33.0" 2415 | } 2416 | }, 2417 | "minimatch": { 2418 | "version": "3.0.4", 2419 | "resolved": "https://registry.npmjs.org/minimatch/-/minimatch-3.0.4.tgz", 2420 | "integrity": "sha512-yJHVQEhyqPLUTgt9B83PXu6W3rx4MvvHvSUvToogpwoGDOUQ+yDrR0HRot+yOCdCO7u4hX3pWft6kWBBcqh0UA==", 2421 | "dev": true, 2422 | "requires": { 2423 | "brace-expansion": "1.1.11" 2424 | } 2425 | }, 2426 | "minimist": { 2427 | "version": "1.2.0", 2428 | "resolved": "https://registry.npmjs.org/minimist/-/minimist-1.2.0.tgz", 2429 | "integrity": "sha1-o1AIsg9BOD7sH7kU9M1d95omQoQ=", 2430 | "dev": true 2431 | }, 2432 | "mixin-deep": { 2433 | "version": "1.3.1", 2434 | "resolved": "https://registry.npmjs.org/mixin-deep/-/mixin-deep-1.3.1.tgz", 2435 | "integrity": "sha512-8ZItLHeEgaqEvd5lYBXfm4EZSFCX29Jb9K+lAHhDKzReKBQKj3R+7NOF6tjqYi9t4oI8VUfaWITJQm86wnXGNQ==", 2436 | "dev": true, 2437 | "requires": { 2438 | "for-in": "1.0.2", 2439 | "is-extendable": "1.0.1" 2440 | }, 2441 | "dependencies": { 2442 | "is-extendable": { 2443 | "version": "1.0.1", 2444 | "resolved": "https://registry.npmjs.org/is-extendable/-/is-extendable-1.0.1.tgz", 2445 | "integrity": "sha512-arnXMxT1hhoKo9k1LZdmlNyJdDDfy2v0fXjFlmok4+i8ul/6WlbVge9bhM74OpNPQPMGUToDtz+KXa1PneJxOA==", 2446 | "dev": true, 2447 | "requires": { 2448 | "is-plain-object": "2.0.4" 2449 | } 2450 | } 2451 | } 2452 | }, 2453 | "ms": { 2454 | "version": "2.0.0", 2455 | "resolved": "https://registry.npmjs.org/ms/-/ms-2.0.0.tgz", 2456 | "integrity": "sha1-VgiurfwAvmwpAd9fmGF4jeDVl8g=" 2457 | }, 2458 | "nan": { 2459 | "version": "2.10.0", 2460 | "resolved": "https://registry.npmjs.org/nan/-/nan-2.10.0.tgz", 2461 | "integrity": "sha512-bAdJv7fBLhWC+/Bls0Oza+mvTaNQtP+1RyhhhvD95pgUJz6XM5IzgmxOkItJ9tkoCiplvAnXI1tNmmUD/eScyA==", 2462 | "dev": true, 2463 | "optional": true 2464 | }, 2465 | "nanomatch": { 2466 | "version": "1.2.9", 2467 | "resolved": "https://registry.npmjs.org/nanomatch/-/nanomatch-1.2.9.tgz", 2468 | "integrity": "sha512-n8R9bS8yQ6eSXaV6jHUpKzD8gLsin02w1HSFiegwrs9E098Ylhw5jdyKPaYqvHknHaSCKTPp7C8dGCQ0q9koXA==", 2469 | "dev": true, 2470 | "requires": { 2471 | "arr-diff": "4.0.0", 2472 | "array-unique": "0.3.2", 2473 | "define-property": "2.0.2", 2474 | "extend-shallow": "3.0.2", 2475 | "fragment-cache": "0.2.1", 2476 | "is-odd": "2.0.0", 2477 | "is-windows": "1.0.2", 2478 | "kind-of": "6.0.2", 2479 | "object.pick": "1.3.0", 2480 | "regex-not": "1.0.2", 2481 | "snapdragon": "0.8.2", 2482 | "to-regex": "3.0.2" 2483 | } 2484 | }, 2485 | "negotiator": { 2486 | "version": "0.6.1", 2487 | "resolved": "https://registry.npmjs.org/negotiator/-/negotiator-0.6.1.tgz", 2488 | "integrity": "sha1-KzJxhOiZIQEXeyhWP7XnECrNDKk=" 2489 | }, 2490 | "nodemon": { 2491 | "version": "1.17.3", 2492 | "resolved": "https://registry.npmjs.org/nodemon/-/nodemon-1.17.3.tgz", 2493 | "integrity": "sha512-8AtS+wA5u6qoE12LONjqOzUzxAI5ObzSw6U5LgqpaO/0y6wwId4l5dN0ZulYyYdpLZD1MbkBp7GjG1hqaoRqYg==", 2494 | "dev": true, 2495 | "requires": { 2496 | "chokidar": "2.0.3", 2497 | "debug": "3.1.0", 2498 | "ignore-by-default": "1.0.1", 2499 | "minimatch": "3.0.4", 2500 | "pstree.remy": "1.1.0", 2501 | "semver": "5.5.0", 2502 | "supports-color": "5.3.0", 2503 | "touch": "3.1.0", 2504 | "undefsafe": "2.0.2", 2505 | "update-notifier": "2.4.0" 2506 | }, 2507 | "dependencies": { 2508 | "debug": { 2509 | "version": "3.1.0", 2510 | "resolved": "https://registry.npmjs.org/debug/-/debug-3.1.0.tgz", 2511 | "integrity": "sha512-OX8XqP7/1a9cqkxYw2yXss15f26NKWBpDXQd0/uK/KPqdQhxbPa994hnzjcE2VqQpDslf55723cKPUOGSmMY3g==", 2512 | "dev": true, 2513 | "requires": { 2514 | "ms": "2.0.0" 2515 | } 2516 | } 2517 | } 2518 | }, 2519 | "nopt": { 2520 | "version": "1.0.10", 2521 | "resolved": "https://registry.npmjs.org/nopt/-/nopt-1.0.10.tgz", 2522 | "integrity": "sha1-bd0hvSoxQXuScn3Vhfim83YI6+4=", 2523 | "dev": true, 2524 | "requires": { 2525 | "abbrev": "1.1.1" 2526 | } 2527 | }, 2528 | "normalize-path": { 2529 | "version": "2.1.1", 2530 | "resolved": "https://registry.npmjs.org/normalize-path/-/normalize-path-2.1.1.tgz", 2531 | "integrity": "sha1-GrKLVW4Zg2Oowab35vogE3/mrtk=", 2532 | "dev": true, 2533 | "requires": { 2534 | "remove-trailing-separator": "1.1.0" 2535 | } 2536 | }, 2537 | "npm-run-path": { 2538 | "version": "2.0.2", 2539 | "resolved": "https://registry.npmjs.org/npm-run-path/-/npm-run-path-2.0.2.tgz", 2540 | "integrity": "sha1-NakjLfo11wZ7TLLd8jV7GHFTbF8=", 2541 | "dev": true, 2542 | "requires": { 2543 | "path-key": "2.0.1" 2544 | } 2545 | }, 2546 | "object-assign": { 2547 | "version": "4.1.1", 2548 | "resolved": "https://registry.npmjs.org/object-assign/-/object-assign-4.1.1.tgz", 2549 | "integrity": "sha1-IQmtx5ZYh8/AXLvUQsrIv7s2CGM=" 2550 | }, 2551 | "object-copy": { 2552 | "version": "0.1.0", 2553 | "resolved": "https://registry.npmjs.org/object-copy/-/object-copy-0.1.0.tgz", 2554 | "integrity": "sha1-fn2Fi3gb18mRpBupde04EnVOmYw=", 2555 | "dev": true, 2556 | "requires": { 2557 | "copy-descriptor": "0.1.1", 2558 | "define-property": "0.2.5", 2559 | "kind-of": "3.2.2" 2560 | }, 2561 | "dependencies": { 2562 | "define-property": { 2563 | "version": "0.2.5", 2564 | "resolved": "https://registry.npmjs.org/define-property/-/define-property-0.2.5.tgz", 2565 | "integrity": "sha1-w1se+RjsPJkPmlvFe+BKrOxcgRY=", 2566 | "dev": true, 2567 | "requires": { 2568 | "is-descriptor": "0.1.6" 2569 | } 2570 | }, 2571 | "kind-of": { 2572 | "version": "3.2.2", 2573 | "resolved": "https://registry.npmjs.org/kind-of/-/kind-of-3.2.2.tgz", 2574 | "integrity": "sha1-MeohpzS6ubuw8yRm2JOupR5KPGQ=", 2575 | "dev": true, 2576 | "requires": { 2577 | "is-buffer": "1.1.6" 2578 | } 2579 | } 2580 | } 2581 | }, 2582 | "object-visit": { 2583 | "version": "1.0.1", 2584 | "resolved": "https://registry.npmjs.org/object-visit/-/object-visit-1.0.1.tgz", 2585 | "integrity": "sha1-95xEk68MU3e1n+OdOV5BBC3QRbs=", 2586 | "dev": true, 2587 | "requires": { 2588 | "isobject": "3.0.1" 2589 | } 2590 | }, 2591 | "object.pick": { 2592 | "version": "1.3.0", 2593 | "resolved": "https://registry.npmjs.org/object.pick/-/object.pick-1.3.0.tgz", 2594 | "integrity": "sha1-h6EKxMFpS9Lhy/U1kaZhQftd10c=", 2595 | "dev": true, 2596 | "requires": { 2597 | "isobject": "3.0.1" 2598 | } 2599 | }, 2600 | "on-finished": { 2601 | "version": "2.3.0", 2602 | "resolved": "https://registry.npmjs.org/on-finished/-/on-finished-2.3.0.tgz", 2603 | "integrity": "sha1-IPEzZIGwg811M3mSoWlxqi2QaUc=", 2604 | "requires": { 2605 | "ee-first": "1.1.1" 2606 | } 2607 | }, 2608 | "p-finally": { 2609 | "version": "1.0.0", 2610 | "resolved": "https://registry.npmjs.org/p-finally/-/p-finally-1.0.0.tgz", 2611 | "integrity": "sha1-P7z7FbiZpEEjs0ttzBi3JDNqLK4=", 2612 | "dev": true 2613 | }, 2614 | "package-json": { 2615 | "version": "4.0.1", 2616 | "resolved": "https://registry.npmjs.org/package-json/-/package-json-4.0.1.tgz", 2617 | "integrity": "sha1-iGmgQBJTZhxMTKPabCEh7VVfXu0=", 2618 | "dev": true, 2619 | "requires": { 2620 | "got": "6.7.1", 2621 | "registry-auth-token": "3.3.2", 2622 | "registry-url": "3.1.0", 2623 | "semver": "5.5.0" 2624 | } 2625 | }, 2626 | "parseurl": { 2627 | "version": "1.3.2", 2628 | "resolved": "https://registry.npmjs.org/parseurl/-/parseurl-1.3.2.tgz", 2629 | "integrity": "sha1-/CidTtiZMRlGDBViUyYs3I3mW/M=" 2630 | }, 2631 | "pascalcase": { 2632 | "version": "0.1.1", 2633 | "resolved": "https://registry.npmjs.org/pascalcase/-/pascalcase-0.1.1.tgz", 2634 | "integrity": "sha1-s2PlXoAGym/iF4TS2yK9FdeRfxQ=", 2635 | "dev": true 2636 | }, 2637 | "path-dirname": { 2638 | "version": "1.0.2", 2639 | "resolved": "https://registry.npmjs.org/path-dirname/-/path-dirname-1.0.2.tgz", 2640 | "integrity": "sha1-zDPSTVJeCZpTiMAzbG4yuRYGCeA=", 2641 | "dev": true 2642 | }, 2643 | "path-is-absolute": { 2644 | "version": "1.0.1", 2645 | "resolved": "https://registry.npmjs.org/path-is-absolute/-/path-is-absolute-1.0.1.tgz", 2646 | "integrity": "sha1-F0uSaHNVNP+8es5r9TpanhtcX18=", 2647 | "dev": true 2648 | }, 2649 | "path-is-inside": { 2650 | "version": "1.0.2", 2651 | "resolved": "https://registry.npmjs.org/path-is-inside/-/path-is-inside-1.0.2.tgz", 2652 | "integrity": "sha1-NlQX3t5EQw0cEa9hAn+s8HS9/FM=", 2653 | "dev": true 2654 | }, 2655 | "path-key": { 2656 | "version": "2.0.1", 2657 | "resolved": "https://registry.npmjs.org/path-key/-/path-key-2.0.1.tgz", 2658 | "integrity": "sha1-QRyttXTFoUDTpLGRDUDYDMn0C0A=", 2659 | "dev": true 2660 | }, 2661 | "path-to-regexp": { 2662 | "version": "0.1.7", 2663 | "resolved": "https://registry.npmjs.org/path-to-regexp/-/path-to-regexp-0.1.7.tgz", 2664 | "integrity": "sha1-32BBeABfUi8V60SQ5yR6G/qmf4w=" 2665 | }, 2666 | "pause-stream": { 2667 | "version": "0.0.11", 2668 | "resolved": "https://registry.npmjs.org/pause-stream/-/pause-stream-0.0.11.tgz", 2669 | "integrity": "sha1-/lo0sMvOErWqaitAPuLnO2AvFEU=", 2670 | "dev": true, 2671 | "requires": { 2672 | "through": "2.3.8" 2673 | } 2674 | }, 2675 | "pify": { 2676 | "version": "3.0.0", 2677 | "resolved": "https://registry.npmjs.org/pify/-/pify-3.0.0.tgz", 2678 | "integrity": "sha1-5aSs0sEB/fPZpNB/DbxNtJ3SgXY=", 2679 | "dev": true 2680 | }, 2681 | "posix-character-classes": { 2682 | "version": "0.1.1", 2683 | "resolved": "https://registry.npmjs.org/posix-character-classes/-/posix-character-classes-0.1.1.tgz", 2684 | "integrity": "sha1-AerA/jta9xoqbAL+q7jB/vfgDqs=", 2685 | "dev": true 2686 | }, 2687 | "prepend-http": { 2688 | "version": "1.0.4", 2689 | "resolved": "https://registry.npmjs.org/prepend-http/-/prepend-http-1.0.4.tgz", 2690 | "integrity": "sha1-1PRWKwzjaW5BrFLQ4ALlemNdxtw=", 2691 | "dev": true 2692 | }, 2693 | "process-nextick-args": { 2694 | "version": "2.0.0", 2695 | "resolved": "https://registry.npmjs.org/process-nextick-args/-/process-nextick-args-2.0.0.tgz", 2696 | "integrity": "sha512-MtEC1TqN0EU5nephaJ4rAtThHtC86dNN9qCuEhtshvpVBkAW5ZO7BASN9REnF9eoXGcRub+pFuKEpOHE+HbEMw==", 2697 | "dev": true 2698 | }, 2699 | "proxy-addr": { 2700 | "version": "2.0.3", 2701 | "resolved": "https://registry.npmjs.org/proxy-addr/-/proxy-addr-2.0.3.tgz", 2702 | "integrity": "sha512-jQTChiCJteusULxjBp8+jftSQE5Obdl3k4cnmLA6WXtK6XFuWRnvVL7aCiBqaLPM8c4ph0S4tKna8XvmIwEnXQ==", 2703 | "requires": { 2704 | "forwarded": "0.1.2", 2705 | "ipaddr.js": "1.6.0" 2706 | } 2707 | }, 2708 | "ps-tree": { 2709 | "version": "1.1.0", 2710 | "resolved": "https://registry.npmjs.org/ps-tree/-/ps-tree-1.1.0.tgz", 2711 | "integrity": "sha1-tCGyQUDWID8e08dplrRCewjowBQ=", 2712 | "dev": true, 2713 | "requires": { 2714 | "event-stream": "3.3.4" 2715 | } 2716 | }, 2717 | "pseudomap": { 2718 | "version": "1.0.2", 2719 | "resolved": "https://registry.npmjs.org/pseudomap/-/pseudomap-1.0.2.tgz", 2720 | "integrity": "sha1-8FKijacOYYkX7wqKw0wa5aaChrM=", 2721 | "dev": true 2722 | }, 2723 | "pstree.remy": { 2724 | "version": "1.1.0", 2725 | "resolved": "https://registry.npmjs.org/pstree.remy/-/pstree.remy-1.1.0.tgz", 2726 | "integrity": "sha512-q5I5vLRMVtdWa8n/3UEzZX7Lfghzrg9eG2IKk2ENLSofKRCXVqMvMUHxCKgXNaqH/8ebhBxrqftHWnyTFweJ5Q==", 2727 | "dev": true, 2728 | "requires": { 2729 | "ps-tree": "1.1.0" 2730 | } 2731 | }, 2732 | "qs": { 2733 | "version": "6.5.1", 2734 | "resolved": "https://registry.npmjs.org/qs/-/qs-6.5.1.tgz", 2735 | "integrity": "sha512-eRzhrN1WSINYCDCbrz796z37LOe3m5tmW7RQf6oBntukAG1nmovJvhnwHHRMAfeoItc1m2Hk02WER2aQ/iqs+A==" 2736 | }, 2737 | "range-parser": { 2738 | "version": "1.2.0", 2739 | "resolved": "https://registry.npmjs.org/range-parser/-/range-parser-1.2.0.tgz", 2740 | "integrity": "sha1-9JvmtIeJTdxA3MlKMi9hEJLgDV4=" 2741 | }, 2742 | "raw-body": { 2743 | "version": "2.3.2", 2744 | "resolved": "https://registry.npmjs.org/raw-body/-/raw-body-2.3.2.tgz", 2745 | "integrity": "sha1-vNYMd9Prk83gBQKVw/N5OJvIj4k=", 2746 | "requires": { 2747 | "bytes": "3.0.0", 2748 | "http-errors": "1.6.2", 2749 | "iconv-lite": "0.4.19", 2750 | "unpipe": "1.0.0" 2751 | }, 2752 | "dependencies": { 2753 | "depd": { 2754 | "version": "1.1.1", 2755 | "resolved": "https://registry.npmjs.org/depd/-/depd-1.1.1.tgz", 2756 | "integrity": "sha1-V4O04cRZ8G+lyif5kfPQbnoxA1k=" 2757 | }, 2758 | "http-errors": { 2759 | "version": "1.6.2", 2760 | "resolved": "https://registry.npmjs.org/http-errors/-/http-errors-1.6.2.tgz", 2761 | "integrity": "sha1-CgAsyFcHGSp+eUbO7cERVfYOxzY=", 2762 | "requires": { 2763 | "depd": "1.1.1", 2764 | "inherits": "2.0.3", 2765 | "setprototypeof": "1.0.3", 2766 | "statuses": "1.4.0" 2767 | } 2768 | }, 2769 | "setprototypeof": { 2770 | "version": "1.0.3", 2771 | "resolved": "https://registry.npmjs.org/setprototypeof/-/setprototypeof-1.0.3.tgz", 2772 | "integrity": "sha1-ZlZ+NwQ+608E2RvWWMDL77VbjgQ=" 2773 | } 2774 | } 2775 | }, 2776 | "rc": { 2777 | "version": "1.2.6", 2778 | "resolved": "https://registry.npmjs.org/rc/-/rc-1.2.6.tgz", 2779 | "integrity": "sha1-6xiYnG1PTxYsOZ953dKfODVWgJI=", 2780 | "dev": true, 2781 | "requires": { 2782 | "deep-extend": "0.4.2", 2783 | "ini": "1.3.5", 2784 | "minimist": "1.2.0", 2785 | "strip-json-comments": "2.0.1" 2786 | } 2787 | }, 2788 | "readable-stream": { 2789 | "version": "2.3.6", 2790 | "resolved": "https://registry.npmjs.org/readable-stream/-/readable-stream-2.3.6.tgz", 2791 | "integrity": "sha512-tQtKA9WIAhBF3+VLAseyMqZeBjW0AHJoxOtYqSUZNJxauErmLbVm2FW1y+J/YA9dUrAC39ITejlZWhVIwawkKw==", 2792 | "dev": true, 2793 | "requires": { 2794 | "core-util-is": "1.0.2", 2795 | "inherits": "2.0.3", 2796 | "isarray": "1.0.0", 2797 | "process-nextick-args": "2.0.0", 2798 | "safe-buffer": "5.1.1", 2799 | "string_decoder": "1.1.1", 2800 | "util-deprecate": "1.0.2" 2801 | } 2802 | }, 2803 | "readdirp": { 2804 | "version": "2.1.0", 2805 | "resolved": "https://registry.npmjs.org/readdirp/-/readdirp-2.1.0.tgz", 2806 | "integrity": "sha1-TtCtBg3zBzMAxIRANz9y0cxkLXg=", 2807 | "dev": true, 2808 | "requires": { 2809 | "graceful-fs": "4.1.11", 2810 | "minimatch": "3.0.4", 2811 | "readable-stream": "2.3.6", 2812 | "set-immediate-shim": "1.0.1" 2813 | } 2814 | }, 2815 | "regex-not": { 2816 | "version": "1.0.2", 2817 | "resolved": "https://registry.npmjs.org/regex-not/-/regex-not-1.0.2.tgz", 2818 | "integrity": "sha512-J6SDjUgDxQj5NusnOtdFxDwN/+HWykR8GELwctJ7mdqhcyy1xEc4SRFHUXvxTp661YaVKAjfRLZ9cCqS6tn32A==", 2819 | "dev": true, 2820 | "requires": { 2821 | "extend-shallow": "3.0.2", 2822 | "safe-regex": "1.1.0" 2823 | } 2824 | }, 2825 | "registry-auth-token": { 2826 | "version": "3.3.2", 2827 | "resolved": "https://registry.npmjs.org/registry-auth-token/-/registry-auth-token-3.3.2.tgz", 2828 | "integrity": "sha512-JL39c60XlzCVgNrO+qq68FoNb56w/m7JYvGR2jT5iR1xBrUA3Mfx5Twk5rqTThPmQKMWydGmq8oFtDlxfrmxnQ==", 2829 | "dev": true, 2830 | "requires": { 2831 | "rc": "1.2.6", 2832 | "safe-buffer": "5.1.1" 2833 | } 2834 | }, 2835 | "registry-url": { 2836 | "version": "3.1.0", 2837 | "resolved": "https://registry.npmjs.org/registry-url/-/registry-url-3.1.0.tgz", 2838 | "integrity": "sha1-PU74cPc93h138M+aOBQyRE4XSUI=", 2839 | "dev": true, 2840 | "requires": { 2841 | "rc": "1.2.6" 2842 | } 2843 | }, 2844 | "remove-trailing-separator": { 2845 | "version": "1.1.0", 2846 | "resolved": "https://registry.npmjs.org/remove-trailing-separator/-/remove-trailing-separator-1.1.0.tgz", 2847 | "integrity": "sha1-wkvOKig62tW8P1jg1IJJuSN52O8=", 2848 | "dev": true 2849 | }, 2850 | "repeat-element": { 2851 | "version": "1.1.2", 2852 | "resolved": "https://registry.npmjs.org/repeat-element/-/repeat-element-1.1.2.tgz", 2853 | "integrity": "sha1-7wiaF40Ug7quTZPrmLT55OEdmQo=", 2854 | "dev": true 2855 | }, 2856 | "repeat-string": { 2857 | "version": "1.6.1", 2858 | "resolved": "https://registry.npmjs.org/repeat-string/-/repeat-string-1.6.1.tgz", 2859 | "integrity": "sha1-jcrkcOHIirwtYA//Sndihtp15jc=", 2860 | "dev": true 2861 | }, 2862 | "resolve-url": { 2863 | "version": "0.2.1", 2864 | "resolved": "https://registry.npmjs.org/resolve-url/-/resolve-url-0.2.1.tgz", 2865 | "integrity": "sha1-LGN/53yJOv0qZj/iGqkIAGjiBSo=", 2866 | "dev": true 2867 | }, 2868 | "ret": { 2869 | "version": "0.1.15", 2870 | "resolved": "https://registry.npmjs.org/ret/-/ret-0.1.15.tgz", 2871 | "integrity": "sha512-TTlYpa+OL+vMMNG24xSlQGEJ3B/RzEfUlLct7b5G/ytav+wPrplCpVMFuwzXbkecJrb6IYo1iFb0S9v37754mg==", 2872 | "dev": true 2873 | }, 2874 | "safe-buffer": { 2875 | "version": "5.1.1", 2876 | "resolved": "https://registry.npmjs.org/safe-buffer/-/safe-buffer-5.1.1.tgz", 2877 | "integrity": "sha512-kKvNJn6Mm93gAczWVJg7wH+wGYWNrDHdWvpUmHyEsgCtIwwo3bqPtV4tR5tuPaUhTOo/kvhVwd8XwwOllGYkbg==" 2878 | }, 2879 | "safe-regex": { 2880 | "version": "1.1.0", 2881 | "resolved": "https://registry.npmjs.org/safe-regex/-/safe-regex-1.1.0.tgz", 2882 | "integrity": "sha1-QKNmnzsHfR6UPURinhV91IAjvy4=", 2883 | "dev": true, 2884 | "requires": { 2885 | "ret": "0.1.15" 2886 | } 2887 | }, 2888 | "semver": { 2889 | "version": "5.5.0", 2890 | "resolved": "https://registry.npmjs.org/semver/-/semver-5.5.0.tgz", 2891 | "integrity": "sha512-4SJ3dm0WAwWy/NVeioZh5AntkdJoWKxHxcmyP622fOkgHa4z3R0TdBJICINyaSDE6uNwVc8gZr+ZinwZAH4xIA==", 2892 | "dev": true 2893 | }, 2894 | "semver-diff": { 2895 | "version": "2.1.0", 2896 | "resolved": "https://registry.npmjs.org/semver-diff/-/semver-diff-2.1.0.tgz", 2897 | "integrity": "sha1-S7uEN8jTfksM8aaP1ybsbWRdbTY=", 2898 | "dev": true, 2899 | "requires": { 2900 | "semver": "5.5.0" 2901 | } 2902 | }, 2903 | "send": { 2904 | "version": "0.16.2", 2905 | "resolved": "https://registry.npmjs.org/send/-/send-0.16.2.tgz", 2906 | "integrity": "sha512-E64YFPUssFHEFBvpbbjr44NCLtI1AohxQ8ZSiJjQLskAdKuriYEP6VyGEsRDH8ScozGpkaX1BGvhanqCwkcEZw==", 2907 | "requires": { 2908 | "debug": "2.6.9", 2909 | "depd": "1.1.2", 2910 | "destroy": "1.0.4", 2911 | "encodeurl": "1.0.2", 2912 | "escape-html": "1.0.3", 2913 | "etag": "1.8.1", 2914 | "fresh": "0.5.2", 2915 | "http-errors": "1.6.3", 2916 | "mime": "1.4.1", 2917 | "ms": "2.0.0", 2918 | "on-finished": "2.3.0", 2919 | "range-parser": "1.2.0", 2920 | "statuses": "1.4.0" 2921 | } 2922 | }, 2923 | "serve-static": { 2924 | "version": "1.13.2", 2925 | "resolved": "https://registry.npmjs.org/serve-static/-/serve-static-1.13.2.tgz", 2926 | "integrity": "sha512-p/tdJrO4U387R9oMjb1oj7qSMaMfmOyd4j9hOFoxZe2baQszgHcSWjuya/CiT5kgZZKRudHNOA0pYXOl8rQ5nw==", 2927 | "requires": { 2928 | "encodeurl": "1.0.2", 2929 | "escape-html": "1.0.3", 2930 | "parseurl": "1.3.2", 2931 | "send": "0.16.2" 2932 | } 2933 | }, 2934 | "set-immediate-shim": { 2935 | "version": "1.0.1", 2936 | "resolved": "https://registry.npmjs.org/set-immediate-shim/-/set-immediate-shim-1.0.1.tgz", 2937 | "integrity": "sha1-SysbJ+uAip+NzEgaWOXlb1mfP2E=", 2938 | "dev": true 2939 | }, 2940 | "set-value": { 2941 | "version": "2.0.0", 2942 | "resolved": "https://registry.npmjs.org/set-value/-/set-value-2.0.0.tgz", 2943 | "integrity": "sha512-hw0yxk9GT/Hr5yJEYnHNKYXkIA8mVJgd9ditYZCe16ZczcaELYYcfvaXesNACk2O8O0nTiPQcQhGUQj8JLzeeg==", 2944 | "dev": true, 2945 | "requires": { 2946 | "extend-shallow": "2.0.1", 2947 | "is-extendable": "0.1.1", 2948 | "is-plain-object": "2.0.4", 2949 | "split-string": "3.1.0" 2950 | }, 2951 | "dependencies": { 2952 | "extend-shallow": { 2953 | "version": "2.0.1", 2954 | "resolved": "https://registry.npmjs.org/extend-shallow/-/extend-shallow-2.0.1.tgz", 2955 | "integrity": "sha1-Ua99YUrZqfYQ6huvu5idaxxWiQ8=", 2956 | "dev": true, 2957 | "requires": { 2958 | "is-extendable": "0.1.1" 2959 | } 2960 | } 2961 | } 2962 | }, 2963 | "setprototypeof": { 2964 | "version": "1.1.0", 2965 | "resolved": "https://registry.npmjs.org/setprototypeof/-/setprototypeof-1.1.0.tgz", 2966 | "integrity": "sha512-BvE/TwpZX4FXExxOxZyRGQQv651MSwmWKZGqvmPcRIjDqWub67kTKuIMx43cZZrS/cBBzwBcNDWoFxt2XEFIpQ==" 2967 | }, 2968 | "shebang-command": { 2969 | "version": "1.2.0", 2970 | "resolved": "https://registry.npmjs.org/shebang-command/-/shebang-command-1.2.0.tgz", 2971 | "integrity": "sha1-RKrGW2lbAzmJaMOfNj/uXer98eo=", 2972 | "dev": true, 2973 | "requires": { 2974 | "shebang-regex": "1.0.0" 2975 | } 2976 | }, 2977 | "shebang-regex": { 2978 | "version": "1.0.0", 2979 | "resolved": "https://registry.npmjs.org/shebang-regex/-/shebang-regex-1.0.0.tgz", 2980 | "integrity": "sha1-2kL0l0DAtC2yypcoVxyxkMmO/qM=", 2981 | "dev": true 2982 | }, 2983 | "signal-exit": { 2984 | "version": "3.0.2", 2985 | "resolved": "https://registry.npmjs.org/signal-exit/-/signal-exit-3.0.2.tgz", 2986 | "integrity": "sha1-tf3AjxKH6hF4Yo5BXiUTK3NkbG0=", 2987 | "dev": true 2988 | }, 2989 | "snapdragon": { 2990 | "version": "0.8.2", 2991 | "resolved": "https://registry.npmjs.org/snapdragon/-/snapdragon-0.8.2.tgz", 2992 | "integrity": "sha512-FtyOnWN/wCHTVXOMwvSv26d+ko5vWlIDD6zoUJ7LW8vh+ZBC8QdljveRP+crNrtBwioEUWy/4dMtbBjA4ioNlg==", 2993 | "dev": true, 2994 | "requires": { 2995 | "base": "0.11.2", 2996 | "debug": "2.6.9", 2997 | "define-property": "0.2.5", 2998 | "extend-shallow": "2.0.1", 2999 | "map-cache": "0.2.2", 3000 | "source-map": "0.5.7", 3001 | "source-map-resolve": "0.5.1", 3002 | "use": "3.1.0" 3003 | }, 3004 | "dependencies": { 3005 | "define-property": { 3006 | "version": "0.2.5", 3007 | "resolved": "https://registry.npmjs.org/define-property/-/define-property-0.2.5.tgz", 3008 | "integrity": "sha1-w1se+RjsPJkPmlvFe+BKrOxcgRY=", 3009 | "dev": true, 3010 | "requires": { 3011 | "is-descriptor": "0.1.6" 3012 | } 3013 | }, 3014 | "extend-shallow": { 3015 | "version": "2.0.1", 3016 | "resolved": "https://registry.npmjs.org/extend-shallow/-/extend-shallow-2.0.1.tgz", 3017 | "integrity": "sha1-Ua99YUrZqfYQ6huvu5idaxxWiQ8=", 3018 | "dev": true, 3019 | "requires": { 3020 | "is-extendable": "0.1.1" 3021 | } 3022 | } 3023 | } 3024 | }, 3025 | "snapdragon-node": { 3026 | "version": "2.1.1", 3027 | "resolved": "https://registry.npmjs.org/snapdragon-node/-/snapdragon-node-2.1.1.tgz", 3028 | "integrity": "sha512-O27l4xaMYt/RSQ5TR3vpWCAB5Kb/czIcqUFOM/C4fYcLnbZUc1PkjTAMjof2pBWaSTwOUd6qUHcFGVGj7aIwnw==", 3029 | "dev": true, 3030 | "requires": { 3031 | "define-property": "1.0.0", 3032 | "isobject": "3.0.1", 3033 | "snapdragon-util": "3.0.1" 3034 | }, 3035 | "dependencies": { 3036 | "define-property": { 3037 | "version": "1.0.0", 3038 | "resolved": "https://registry.npmjs.org/define-property/-/define-property-1.0.0.tgz", 3039 | "integrity": "sha1-dp66rz9KY6rTr56NMEybvnm/sOY=", 3040 | "dev": true, 3041 | "requires": { 3042 | "is-descriptor": "1.0.2" 3043 | } 3044 | }, 3045 | "is-accessor-descriptor": { 3046 | "version": "1.0.0", 3047 | "resolved": "https://registry.npmjs.org/is-accessor-descriptor/-/is-accessor-descriptor-1.0.0.tgz", 3048 | "integrity": "sha512-m5hnHTkcVsPfqx3AKlyttIPb7J+XykHvJP2B9bZDjlhLIoEq4XoK64Vg7boZlVWYK6LUY94dYPEE7Lh0ZkZKcQ==", 3049 | "dev": true, 3050 | "requires": { 3051 | "kind-of": "6.0.2" 3052 | } 3053 | }, 3054 | "is-data-descriptor": { 3055 | "version": "1.0.0", 3056 | "resolved": "https://registry.npmjs.org/is-data-descriptor/-/is-data-descriptor-1.0.0.tgz", 3057 | "integrity": "sha512-jbRXy1FmtAoCjQkVmIVYwuuqDFUbaOeDjmed1tOGPrsMhtJA4rD9tkgA0F1qJ3gRFRXcHYVkdeaP50Q5rE/jLQ==", 3058 | "dev": true, 3059 | "requires": { 3060 | "kind-of": "6.0.2" 3061 | } 3062 | }, 3063 | "is-descriptor": { 3064 | "version": "1.0.2", 3065 | "resolved": "https://registry.npmjs.org/is-descriptor/-/is-descriptor-1.0.2.tgz", 3066 | "integrity": "sha512-2eis5WqQGV7peooDyLmNEPUrps9+SXX5c9pL3xEB+4e9HnGuDa7mB7kHxHw4CbqS9k1T2hOH3miL8n8WtiYVtg==", 3067 | "dev": true, 3068 | "requires": { 3069 | "is-accessor-descriptor": "1.0.0", 3070 | "is-data-descriptor": "1.0.0", 3071 | "kind-of": "6.0.2" 3072 | } 3073 | } 3074 | } 3075 | }, 3076 | "snapdragon-util": { 3077 | "version": "3.0.1", 3078 | "resolved": "https://registry.npmjs.org/snapdragon-util/-/snapdragon-util-3.0.1.tgz", 3079 | "integrity": "sha512-mbKkMdQKsjX4BAL4bRYTj21edOf8cN7XHdYUJEe+Zn99hVEYcMvKPct1IqNe7+AZPirn8BCDOQBHQZknqmKlZQ==", 3080 | "dev": true, 3081 | "requires": { 3082 | "kind-of": "3.2.2" 3083 | }, 3084 | "dependencies": { 3085 | "kind-of": { 3086 | "version": "3.2.2", 3087 | "resolved": "https://registry.npmjs.org/kind-of/-/kind-of-3.2.2.tgz", 3088 | "integrity": "sha1-MeohpzS6ubuw8yRm2JOupR5KPGQ=", 3089 | "dev": true, 3090 | "requires": { 3091 | "is-buffer": "1.1.6" 3092 | } 3093 | } 3094 | } 3095 | }, 3096 | "source-map": { 3097 | "version": "0.5.7", 3098 | "resolved": "https://registry.npmjs.org/source-map/-/source-map-0.5.7.tgz", 3099 | "integrity": "sha1-igOdLRAh0i0eoUyA2OpGi6LvP8w=", 3100 | "dev": true 3101 | }, 3102 | "source-map-resolve": { 3103 | "version": "0.5.1", 3104 | "resolved": "https://registry.npmjs.org/source-map-resolve/-/source-map-resolve-0.5.1.tgz", 3105 | "integrity": "sha512-0KW2wvzfxm8NCTb30z0LMNyPqWCdDGE2viwzUaucqJdkTRXtZiSY3I+2A6nVAjmdOy0I4gU8DwnVVGsk9jvP2A==", 3106 | "dev": true, 3107 | "requires": { 3108 | "atob": "2.1.0", 3109 | "decode-uri-component": "0.2.0", 3110 | "resolve-url": "0.2.1", 3111 | "source-map-url": "0.4.0", 3112 | "urix": "0.1.0" 3113 | } 3114 | }, 3115 | "source-map-url": { 3116 | "version": "0.4.0", 3117 | "resolved": "https://registry.npmjs.org/source-map-url/-/source-map-url-0.4.0.tgz", 3118 | "integrity": "sha1-PpNdfd1zYxuXZZlW1VEo6HtQhKM=", 3119 | "dev": true 3120 | }, 3121 | "split": { 3122 | "version": "0.3.3", 3123 | "resolved": "https://registry.npmjs.org/split/-/split-0.3.3.tgz", 3124 | "integrity": "sha1-zQ7qXmOiEd//frDwkcQTPi0N0o8=", 3125 | "dev": true, 3126 | "requires": { 3127 | "through": "2.3.8" 3128 | } 3129 | }, 3130 | "split-string": { 3131 | "version": "3.1.0", 3132 | "resolved": "https://registry.npmjs.org/split-string/-/split-string-3.1.0.tgz", 3133 | "integrity": "sha512-NzNVhJDYpwceVVii8/Hu6DKfD2G+NrQHlS/V/qgv763EYudVwEcMQNxd2lh+0VrUByXN/oJkl5grOhYWvQUYiw==", 3134 | "dev": true, 3135 | "requires": { 3136 | "extend-shallow": "3.0.2" 3137 | } 3138 | }, 3139 | "static-extend": { 3140 | "version": "0.1.2", 3141 | "resolved": "https://registry.npmjs.org/static-extend/-/static-extend-0.1.2.tgz", 3142 | "integrity": "sha1-YICcOcv/VTNyJv1eC1IPNB8ftcY=", 3143 | "dev": true, 3144 | "requires": { 3145 | "define-property": "0.2.5", 3146 | "object-copy": "0.1.0" 3147 | }, 3148 | "dependencies": { 3149 | "define-property": { 3150 | "version": "0.2.5", 3151 | "resolved": "https://registry.npmjs.org/define-property/-/define-property-0.2.5.tgz", 3152 | "integrity": "sha1-w1se+RjsPJkPmlvFe+BKrOxcgRY=", 3153 | "dev": true, 3154 | "requires": { 3155 | "is-descriptor": "0.1.6" 3156 | } 3157 | } 3158 | } 3159 | }, 3160 | "statuses": { 3161 | "version": "1.4.0", 3162 | "resolved": "https://registry.npmjs.org/statuses/-/statuses-1.4.0.tgz", 3163 | "integrity": "sha512-zhSCtt8v2NDrRlPQpCNtw/heZLtfUDqxBM1udqikb/Hbk52LK4nQSwr10u77iopCW5LsyHpuXS0GnEc48mLeew==" 3164 | }, 3165 | "stream-combiner": { 3166 | "version": "0.0.4", 3167 | "resolved": "https://registry.npmjs.org/stream-combiner/-/stream-combiner-0.0.4.tgz", 3168 | "integrity": "sha1-TV5DPBhSYd3mI8o/RMWGvPXErRQ=", 3169 | "dev": true, 3170 | "requires": { 3171 | "duplexer": "0.1.1" 3172 | } 3173 | }, 3174 | "string-width": { 3175 | "version": "2.1.1", 3176 | "resolved": "https://registry.npmjs.org/string-width/-/string-width-2.1.1.tgz", 3177 | "integrity": "sha512-nOqH59deCq9SRHlxq1Aw85Jnt4w6KvLKqWVik6oA9ZklXLNIOlqg4F2yrT1MVaTjAqvVwdfeZ7w7aCvJD7ugkw==", 3178 | "dev": true, 3179 | "requires": { 3180 | "is-fullwidth-code-point": "2.0.0", 3181 | "strip-ansi": "4.0.0" 3182 | } 3183 | }, 3184 | "string_decoder": { 3185 | "version": "1.1.1", 3186 | "resolved": "https://registry.npmjs.org/string_decoder/-/string_decoder-1.1.1.tgz", 3187 | "integrity": "sha512-n/ShnvDi6FHbbVfviro+WojiFzv+s8MPMHBczVePfUpDJLwoLT0ht1l4YwBCbi8pJAveEEdnkHyPyTP/mzRfwg==", 3188 | "dev": true, 3189 | "requires": { 3190 | "safe-buffer": "5.1.1" 3191 | } 3192 | }, 3193 | "strip-ansi": { 3194 | "version": "4.0.0", 3195 | "resolved": "https://registry.npmjs.org/strip-ansi/-/strip-ansi-4.0.0.tgz", 3196 | "integrity": "sha1-qEeQIusaw2iocTibY1JixQXuNo8=", 3197 | "dev": true, 3198 | "requires": { 3199 | "ansi-regex": "3.0.0" 3200 | } 3201 | }, 3202 | "strip-eof": { 3203 | "version": "1.0.0", 3204 | "resolved": "https://registry.npmjs.org/strip-eof/-/strip-eof-1.0.0.tgz", 3205 | "integrity": "sha1-u0P/VZim6wXYm1n80SnJgzE2Br8=", 3206 | "dev": true 3207 | }, 3208 | "strip-json-comments": { 3209 | "version": "2.0.1", 3210 | "resolved": "https://registry.npmjs.org/strip-json-comments/-/strip-json-comments-2.0.1.tgz", 3211 | "integrity": "sha1-PFMZQukIwml8DsNEhYwobHygpgo=", 3212 | "dev": true 3213 | }, 3214 | "supports-color": { 3215 | "version": "5.3.0", 3216 | "resolved": "https://registry.npmjs.org/supports-color/-/supports-color-5.3.0.tgz", 3217 | "integrity": "sha512-0aP01LLIskjKs3lq52EC0aGBAJhLq7B2Rd8HC/DR/PtNNpcLilNmHC12O+hu0usQpo7wtHNRqtrhBwtDb0+dNg==", 3218 | "dev": true, 3219 | "requires": { 3220 | "has-flag": "3.0.0" 3221 | } 3222 | }, 3223 | "term-size": { 3224 | "version": "1.2.0", 3225 | "resolved": "https://registry.npmjs.org/term-size/-/term-size-1.2.0.tgz", 3226 | "integrity": "sha1-RYuDiH8oj8Vtb/+/rSYuJmOO+mk=", 3227 | "dev": true, 3228 | "requires": { 3229 | "execa": "0.7.0" 3230 | } 3231 | }, 3232 | "through": { 3233 | "version": "2.3.8", 3234 | "resolved": "https://registry.npmjs.org/through/-/through-2.3.8.tgz", 3235 | "integrity": "sha1-DdTJ/6q8NXlgsbckEV1+Doai4fU=", 3236 | "dev": true 3237 | }, 3238 | "timed-out": { 3239 | "version": "4.0.1", 3240 | "resolved": "https://registry.npmjs.org/timed-out/-/timed-out-4.0.1.tgz", 3241 | "integrity": "sha1-8y6srFoXW+ol1/q1Zas+2HQe9W8=", 3242 | "dev": true 3243 | }, 3244 | "to-object-path": { 3245 | "version": "0.3.0", 3246 | "resolved": "https://registry.npmjs.org/to-object-path/-/to-object-path-0.3.0.tgz", 3247 | "integrity": "sha1-KXWIt7Dn4KwI4E5nL4XB9JmeF68=", 3248 | "dev": true, 3249 | "requires": { 3250 | "kind-of": "3.2.2" 3251 | }, 3252 | "dependencies": { 3253 | "kind-of": { 3254 | "version": "3.2.2", 3255 | "resolved": "https://registry.npmjs.org/kind-of/-/kind-of-3.2.2.tgz", 3256 | "integrity": "sha1-MeohpzS6ubuw8yRm2JOupR5KPGQ=", 3257 | "dev": true, 3258 | "requires": { 3259 | "is-buffer": "1.1.6" 3260 | } 3261 | } 3262 | } 3263 | }, 3264 | "to-regex": { 3265 | "version": "3.0.2", 3266 | "resolved": "https://registry.npmjs.org/to-regex/-/to-regex-3.0.2.tgz", 3267 | "integrity": "sha512-FWtleNAtZ/Ki2qtqej2CXTOayOH9bHDQF+Q48VpWyDXjbYxA4Yz8iDB31zXOBUlOHHKidDbqGVrTUvQMPmBGBw==", 3268 | "dev": true, 3269 | "requires": { 3270 | "define-property": "2.0.2", 3271 | "extend-shallow": "3.0.2", 3272 | "regex-not": "1.0.2", 3273 | "safe-regex": "1.1.0" 3274 | } 3275 | }, 3276 | "to-regex-range": { 3277 | "version": "2.1.1", 3278 | "resolved": "https://registry.npmjs.org/to-regex-range/-/to-regex-range-2.1.1.tgz", 3279 | "integrity": "sha1-fIDBe53+vlmeJzZ+DU3VWQFB2zg=", 3280 | "dev": true, 3281 | "requires": { 3282 | "is-number": "3.0.0", 3283 | "repeat-string": "1.6.1" 3284 | } 3285 | }, 3286 | "touch": { 3287 | "version": "3.1.0", 3288 | "resolved": "https://registry.npmjs.org/touch/-/touch-3.1.0.tgz", 3289 | "integrity": "sha512-WBx8Uy5TLtOSRtIq+M03/sKDrXCLHxwDcquSP2c43Le03/9serjQBIztjRz6FkJez9D/hleyAXTBGLwwZUw9lA==", 3290 | "dev": true, 3291 | "requires": { 3292 | "nopt": "1.0.10" 3293 | } 3294 | }, 3295 | "type-is": { 3296 | "version": "1.6.16", 3297 | "resolved": "https://registry.npmjs.org/type-is/-/type-is-1.6.16.tgz", 3298 | "integrity": "sha512-HRkVv/5qY2G6I8iab9cI7v1bOIdhm94dVjQCPFElW9W+3GeDOSHmy2EBYe4VTApuzolPcmgFTN3ftVJRKR2J9Q==", 3299 | "requires": { 3300 | "media-typer": "0.3.0", 3301 | "mime-types": "2.1.18" 3302 | } 3303 | }, 3304 | "undefsafe": { 3305 | "version": "2.0.2", 3306 | "resolved": "https://registry.npmjs.org/undefsafe/-/undefsafe-2.0.2.tgz", 3307 | "integrity": "sha1-Il9rngM3Zj4Njnz9aG/Cg2zKznY=", 3308 | "dev": true, 3309 | "requires": { 3310 | "debug": "2.6.9" 3311 | } 3312 | }, 3313 | "union-value": { 3314 | "version": "1.0.0", 3315 | "resolved": "https://registry.npmjs.org/union-value/-/union-value-1.0.0.tgz", 3316 | "integrity": "sha1-XHHDTLW61dzr4+oM0IIHulqhrqQ=", 3317 | "dev": true, 3318 | "requires": { 3319 | "arr-union": "3.1.0", 3320 | "get-value": "2.0.6", 3321 | "is-extendable": "0.1.1", 3322 | "set-value": "0.4.3" 3323 | }, 3324 | "dependencies": { 3325 | "extend-shallow": { 3326 | "version": "2.0.1", 3327 | "resolved": "https://registry.npmjs.org/extend-shallow/-/extend-shallow-2.0.1.tgz", 3328 | "integrity": "sha1-Ua99YUrZqfYQ6huvu5idaxxWiQ8=", 3329 | "dev": true, 3330 | "requires": { 3331 | "is-extendable": "0.1.1" 3332 | } 3333 | }, 3334 | "set-value": { 3335 | "version": "0.4.3", 3336 | "resolved": "https://registry.npmjs.org/set-value/-/set-value-0.4.3.tgz", 3337 | "integrity": "sha1-fbCPnT0i3H945Trzw79GZuzfzPE=", 3338 | "dev": true, 3339 | "requires": { 3340 | "extend-shallow": "2.0.1", 3341 | "is-extendable": "0.1.1", 3342 | "is-plain-object": "2.0.4", 3343 | "to-object-path": "0.3.0" 3344 | } 3345 | } 3346 | } 3347 | }, 3348 | "unique-string": { 3349 | "version": "1.0.0", 3350 | "resolved": "https://registry.npmjs.org/unique-string/-/unique-string-1.0.0.tgz", 3351 | "integrity": "sha1-nhBXzKhRq7kzmPizOuGHuZyuwRo=", 3352 | "dev": true, 3353 | "requires": { 3354 | "crypto-random-string": "1.0.0" 3355 | } 3356 | }, 3357 | "unpipe": { 3358 | "version": "1.0.0", 3359 | "resolved": "https://registry.npmjs.org/unpipe/-/unpipe-1.0.0.tgz", 3360 | "integrity": "sha1-sr9O6FFKrmFltIF4KdIbLvSZBOw=" 3361 | }, 3362 | "unset-value": { 3363 | "version": "1.0.0", 3364 | "resolved": "https://registry.npmjs.org/unset-value/-/unset-value-1.0.0.tgz", 3365 | "integrity": "sha1-g3aHP30jNRef+x5vw6jtDfyKtVk=", 3366 | "dev": true, 3367 | "requires": { 3368 | "has-value": "0.3.1", 3369 | "isobject": "3.0.1" 3370 | }, 3371 | "dependencies": { 3372 | "has-value": { 3373 | "version": "0.3.1", 3374 | "resolved": "https://registry.npmjs.org/has-value/-/has-value-0.3.1.tgz", 3375 | "integrity": "sha1-ex9YutpiyoJ+wKIHgCVlSEWZXh8=", 3376 | "dev": true, 3377 | "requires": { 3378 | "get-value": "2.0.6", 3379 | "has-values": "0.1.4", 3380 | "isobject": "2.1.0" 3381 | }, 3382 | "dependencies": { 3383 | "isobject": { 3384 | "version": "2.1.0", 3385 | "resolved": "https://registry.npmjs.org/isobject/-/isobject-2.1.0.tgz", 3386 | "integrity": "sha1-8GVWEJaj8dou9GJy+BXIQNh+DIk=", 3387 | "dev": true, 3388 | "requires": { 3389 | "isarray": "1.0.0" 3390 | } 3391 | } 3392 | } 3393 | }, 3394 | "has-values": { 3395 | "version": "0.1.4", 3396 | "resolved": "https://registry.npmjs.org/has-values/-/has-values-0.1.4.tgz", 3397 | "integrity": "sha1-bWHeldkd/Km5oCCJrThL/49it3E=", 3398 | "dev": true 3399 | } 3400 | } 3401 | }, 3402 | "unzip-response": { 3403 | "version": "2.0.1", 3404 | "resolved": "https://registry.npmjs.org/unzip-response/-/unzip-response-2.0.1.tgz", 3405 | "integrity": "sha1-0vD3N9FrBhXnKmk17QQhRXLVb5c=", 3406 | "dev": true 3407 | }, 3408 | "upath": { 3409 | "version": "1.0.4", 3410 | "resolved": "https://registry.npmjs.org/upath/-/upath-1.0.4.tgz", 3411 | "integrity": "sha512-d4SJySNBXDaQp+DPrziv3xGS6w3d2Xt69FijJr86zMPBy23JEloMCEOUBBzuN7xCtjLCnmB9tI/z7SBCahHBOw==", 3412 | "dev": true 3413 | }, 3414 | "update-notifier": { 3415 | "version": "2.4.0", 3416 | "resolved": "https://registry.npmjs.org/update-notifier/-/update-notifier-2.4.0.tgz", 3417 | "integrity": "sha1-+bTHAPv9TsEsgRWHJYd31WPYyGY=", 3418 | "dev": true, 3419 | "requires": { 3420 | "boxen": "1.3.0", 3421 | "chalk": "2.3.2", 3422 | "configstore": "3.1.2", 3423 | "import-lazy": "2.1.0", 3424 | "is-ci": "1.1.0", 3425 | "is-installed-globally": "0.1.0", 3426 | "is-npm": "1.0.0", 3427 | "latest-version": "3.1.0", 3428 | "semver-diff": "2.1.0", 3429 | "xdg-basedir": "3.0.0" 3430 | } 3431 | }, 3432 | "urix": { 3433 | "version": "0.1.0", 3434 | "resolved": "https://registry.npmjs.org/urix/-/urix-0.1.0.tgz", 3435 | "integrity": "sha1-2pN/emLiH+wf0Y1Js1wpNQZ6bHI=", 3436 | "dev": true 3437 | }, 3438 | "url-parse-lax": { 3439 | "version": "1.0.0", 3440 | "resolved": "https://registry.npmjs.org/url-parse-lax/-/url-parse-lax-1.0.0.tgz", 3441 | "integrity": "sha1-evjzA2Rem9eaJy56FKxovAYJ2nM=", 3442 | "dev": true, 3443 | "requires": { 3444 | "prepend-http": "1.0.4" 3445 | } 3446 | }, 3447 | "use": { 3448 | "version": "3.1.0", 3449 | "resolved": "https://registry.npmjs.org/use/-/use-3.1.0.tgz", 3450 | "integrity": "sha512-6UJEQM/L+mzC3ZJNM56Q4DFGLX/evKGRg15UJHGB9X5j5Z3AFbgZvjUh2yq/UJUY4U5dh7Fal++XbNg1uzpRAw==", 3451 | "dev": true, 3452 | "requires": { 3453 | "kind-of": "6.0.2" 3454 | } 3455 | }, 3456 | "util-deprecate": { 3457 | "version": "1.0.2", 3458 | "resolved": "https://registry.npmjs.org/util-deprecate/-/util-deprecate-1.0.2.tgz", 3459 | "integrity": "sha1-RQ1Nyfpw3nMnYvvS1KKJgUGaDM8=", 3460 | "dev": true 3461 | }, 3462 | "utils-merge": { 3463 | "version": "1.0.1", 3464 | "resolved": "https://registry.npmjs.org/utils-merge/-/utils-merge-1.0.1.tgz", 3465 | "integrity": "sha1-n5VxD1CiZ5R7LMwSR0HBAoQn5xM=" 3466 | }, 3467 | "vary": { 3468 | "version": "1.1.2", 3469 | "resolved": "https://registry.npmjs.org/vary/-/vary-1.1.2.tgz", 3470 | "integrity": "sha1-IpnwLG3tMNSllhsLn3RSShj2NPw=" 3471 | }, 3472 | "which": { 3473 | "version": "1.3.0", 3474 | "resolved": "https://registry.npmjs.org/which/-/which-1.3.0.tgz", 3475 | "integrity": "sha512-xcJpopdamTuY5duC/KnTTNBraPK54YwpenP4lzxU8H91GudWpFv38u0CKjclE1Wi2EH2EDz5LRcHcKbCIzqGyg==", 3476 | "dev": true, 3477 | "requires": { 3478 | "isexe": "2.0.0" 3479 | } 3480 | }, 3481 | "widest-line": { 3482 | "version": "2.0.0", 3483 | "resolved": "https://registry.npmjs.org/widest-line/-/widest-line-2.0.0.tgz", 3484 | "integrity": "sha1-AUKk6KJD+IgsAjOqDgKBqnYVInM=", 3485 | "dev": true, 3486 | "requires": { 3487 | "string-width": "2.1.1" 3488 | } 3489 | }, 3490 | "write-file-atomic": { 3491 | "version": "2.3.0", 3492 | "resolved": "https://registry.npmjs.org/write-file-atomic/-/write-file-atomic-2.3.0.tgz", 3493 | "integrity": "sha512-xuPeK4OdjWqtfi59ylvVL0Yn35SF3zgcAcv7rBPFHVaEapaDr4GdGgm3j7ckTwH9wHL7fGmgfAnb0+THrHb8tA==", 3494 | "dev": true, 3495 | "requires": { 3496 | "graceful-fs": "4.1.11", 3497 | "imurmurhash": "0.1.4", 3498 | "signal-exit": "3.0.2" 3499 | } 3500 | }, 3501 | "xdg-basedir": { 3502 | "version": "3.0.0", 3503 | "resolved": "https://registry.npmjs.org/xdg-basedir/-/xdg-basedir-3.0.0.tgz", 3504 | "integrity": "sha1-SWsswQnsqNus/i3HK2A8F8WHCtQ=", 3505 | "dev": true 3506 | }, 3507 | "xtend": { 3508 | "version": "4.0.1", 3509 | "resolved": "https://registry.npmjs.org/xtend/-/xtend-4.0.1.tgz", 3510 | "integrity": "sha1-pcbVMr5lbiPbgg77lDofBJmNY68=" 3511 | }, 3512 | "yallist": { 3513 | "version": "2.1.2", 3514 | "resolved": "https://registry.npmjs.org/yallist/-/yallist-2.1.2.tgz", 3515 | "integrity": "sha1-HBH5IY8HYImkfdUS+TxmmaaoHVI=", 3516 | "dev": true 3517 | } 3518 | } 3519 | } 3520 | -------------------------------------------------------------------------------- /gateway/package.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "freebie-api-server-api-gateway", 3 | "version": "1.0.0", 4 | "description": "API Gateway for the freebie-api-server web client", 5 | "main": "index.js", 6 | "scripts": { 7 | "start": "nodemon --use_strict -r dotenv/config index.js", 8 | "test": "echo \"Error: no test specified\" && exit 1", 9 | "docker-build": "../scripts/build gateway" 10 | }, 11 | "dependencies": { 12 | "axios": "0.18.0", 13 | "cors": "2.8.4", 14 | "dotenv": "5.0.1", 15 | "express": "4.16.3", 16 | "express-http-proxy": "1.1.0", 17 | "express-jwt": "5.3.1", 18 | "jsonwebtoken": "8.2.1" 19 | }, 20 | "devDependencies": { 21 | "nodemon": "1.17.3" 22 | } 23 | } 24 | -------------------------------------------------------------------------------- /product/.dockerignore: -------------------------------------------------------------------------------- 1 | node_modules -------------------------------------------------------------------------------- /product/.gitignore: -------------------------------------------------------------------------------- 1 | node_modules 2 | 3 | img-version-history -------------------------------------------------------------------------------- /product/Dockerfile: -------------------------------------------------------------------------------- 1 | FROM node:8.9.4 2 | 3 | COPY package.json package.json 4 | RUN npm install 5 | 6 | # Add your source files 7 | COPY . . 8 | 9 | ENV NODE_ENV production 10 | ENV PORT 3000 11 | 12 | EXPOSE 3000 13 | 14 | CMD ["node", "index.js", "--use-strict"] -------------------------------------------------------------------------------- /product/api/index.js: -------------------------------------------------------------------------------- 1 | 'use strict' 2 | 3 | const Router = require('koa-router') 4 | const bodyParser = require('koa-bodyparser') 5 | const productController = require('./products') 6 | 7 | const router = new Router() 8 | 9 | router.use(bodyParser()) 10 | 11 | router.get('/healthcheck', (ctx) => { 12 | ctx.body = 'Products service is up!' 13 | }) 14 | 15 | // PRODUCTS 16 | router.get('/products', productController.listAllProducts) 17 | router.get('/products/:id', productController.listProductByID) 18 | 19 | router.put('/products/:id', productController.editProduct) 20 | router.post('/products', productController.addNewProduct) 21 | router.delete('/products/:id', productController.deleteProduct) 22 | 23 | module.exports = router 24 | -------------------------------------------------------------------------------- /product/api/products/delete.js: -------------------------------------------------------------------------------- 1 | 'use strict' 2 | 3 | const ProductService = require('../../dal/productService') 4 | const logger = require('winston') 5 | 6 | async function deleteProduct(ctx) { 7 | try { 8 | const result = await ProductService.delete(ctx.params.id) 9 | if (result === 1) { 10 | ctx.status = 200 11 | } else { 12 | ctx.status = 404 13 | } 14 | } catch (err) { 15 | logger.error(err) 16 | ctx.status = 500 17 | } 18 | } 19 | 20 | 21 | module.exports = { 22 | deleteProduct 23 | } 24 | -------------------------------------------------------------------------------- /product/api/products/get.js: -------------------------------------------------------------------------------- 1 | 'use strict' 2 | 3 | const ProductService = require('../../dal/productService') 4 | const logger = require('winston') 5 | 6 | const GetProducts = { 7 | async productByID(ctx) { 8 | try { 9 | const [result] = await ProductService.getProductByID(ctx.params.id) 10 | if (!result) { 11 | ctx.status = 404 12 | return 13 | } 14 | ctx.body = result 15 | ctx.status = 200 16 | } catch (err) { 17 | logger.error(err) 18 | ctx.status = 500 19 | } 20 | }, 21 | 22 | async listAllProducts(ctx) { 23 | try { 24 | if (ctx.query.search) { 25 | ctx.body = Object.assign({}, { products: await ProductService.getProductsBySearch(ctx.query.search) }) 26 | } else { 27 | ctx.body = Object.assign({}, { products: await ProductService.getAllProducts() }) 28 | } 29 | ctx.status = 200 30 | } catch (err) { 31 | logger.error(err) 32 | ctx.status = 500 33 | } 34 | } 35 | } 36 | 37 | 38 | module.exports = { 39 | listAllProducts: GetProducts.listAllProducts, 40 | listProductByID: GetProducts.productByID, 41 | GetProducts 42 | } 43 | -------------------------------------------------------------------------------- /product/api/products/index.js: -------------------------------------------------------------------------------- 1 | 'use strict' 2 | 3 | const { listAllProducts, listProductByID } = require('./get') 4 | const { addNewProduct } = require('./post') 5 | const { editProduct } = require('./put') 6 | const { deleteProduct } = require('./delete') 7 | 8 | const ProductController = { 9 | listAllProducts, 10 | listProductByID, 11 | addNewProduct, 12 | editProduct, 13 | deleteProduct 14 | } 15 | 16 | module.exports = ProductController 17 | -------------------------------------------------------------------------------- /product/api/products/post.js: -------------------------------------------------------------------------------- 1 | 'use strict' 2 | 3 | const ProductService = require('../../dal/productService') 4 | const logger = require('winston') 5 | 6 | const AddProduct = { 7 | async addNewProduct(ctx) { 8 | try { 9 | ctx.body = await ProductService.insertProduct(ctx.request.body) 10 | ctx.status = 201 11 | } catch (err) { 12 | logger.error(err) 13 | ctx.status = 500 14 | } 15 | } 16 | } 17 | 18 | module.exports = { 19 | addNewProduct: AddProduct.addNewProduct, 20 | AddProduct 21 | } 22 | -------------------------------------------------------------------------------- /product/api/products/put.js: -------------------------------------------------------------------------------- 1 | 'use strict' 2 | 3 | const ProductService = require('../../dal/productService') 4 | const logger = require('winston') 5 | 6 | const EditProduct = { 7 | async editProduct(ctx) { 8 | try { 9 | ctx.body = await ProductService.edit(ctx.params.id, ctx.request.body) 10 | ctx.status = 200 11 | } catch (err) { 12 | logger.error(err) 13 | ctx.status = 500 14 | } 15 | } 16 | } 17 | 18 | 19 | module.exports = { 20 | editProduct: EditProduct.editProduct, 21 | EditProduct 22 | } 23 | -------------------------------------------------------------------------------- /product/config/db.js: -------------------------------------------------------------------------------- 1 | 'use strict' 2 | 3 | const path = require('path') 4 | const joi = require('joi') 5 | 6 | const envVarsSchema = joi.object({ 7 | DB_HOST: joi.string().required(), 8 | DB_USER: joi.string().required(), 9 | DB_PASSWORD: joi.string().allow('').required(), 10 | DB_DATABASE: joi.string().required(), 11 | DB_PORT: joi.number().required(), 12 | DB_POOL_MIN: joi.number().default(1), 13 | DB_POOL_MAX: joi.number().default(20), 14 | DB_DEBUG: joi.bool().default(false) 15 | }).unknown() 16 | .required() 17 | 18 | const envVars = joi.attempt(process.env, envVarsSchema) 19 | 20 | const config = { 21 | client: 'pg', 22 | connection: { 23 | host: process.env.DB_HOST, 24 | user: process.env.DB_USER, 25 | password: process.env.DB_PASSWORD, 26 | database: process.env.DB_DATABASE, 27 | port: process.env.DB_PORT 28 | }, 29 | pool: { 30 | min: envVars.DB_POOL_MIN, 31 | max: envVars.DB_POOL_MAX 32 | }, 33 | migrations: { 34 | directory: path.join(__dirname, '../migrations') 35 | }, 36 | debug: envVars.DB_DEBUG 37 | } 38 | 39 | module.exports = config 40 | -------------------------------------------------------------------------------- /product/config/server.js: -------------------------------------------------------------------------------- 1 | 'use strict' 2 | 3 | const joi = require('joi') 4 | 5 | const envVarsSchema = joi.object({ 6 | PORT: joi.number().integer().min(0).required(), 7 | LOGGER_LEVEL: joi.string().default('info') 8 | }).unknown() 9 | .required() 10 | 11 | const envVars = joi.attempt(process.env, envVarsSchema) 12 | 13 | const config = { 14 | port: envVars.PORT, 15 | logger: { 16 | level: envVars.LOGGER_LEVEL 17 | } 18 | } 19 | 20 | module.exports = config 21 | -------------------------------------------------------------------------------- /product/dal/productService.js: -------------------------------------------------------------------------------- 1 | 'use strict' 2 | 3 | const db = require('../db') 4 | 5 | const tableName = 'products' 6 | 7 | const ProductService = { 8 | getAllProducts() { 9 | return db(tableName) 10 | .select() 11 | .orderBy('id', 'asc') 12 | .then((result) => result) 13 | }, 14 | 15 | getProductByID(id) { 16 | return db(tableName) 17 | .where({ id }) 18 | .then((result) => result) 19 | }, 20 | 21 | getProductsBySearch(searchString) { 22 | return db(tableName) 23 | .where('name', 'ILIKE', `%${searchString}%`) 24 | .orWhere('description', 'ILIKE', `%${searchString}%`) 25 | .orWhere('ingredients', 'ILIKE', `%${searchString}%`) 26 | .then((result) => result) 27 | }, 28 | 29 | insertProduct(newProduct) { 30 | return db(tableName) 31 | .insert(newProduct) 32 | .returning('*') 33 | .then((result) => result[0]) 34 | }, 35 | 36 | edit(id, update) { 37 | return db(tableName) 38 | .where({ id }) 39 | .update(update) 40 | .returning('*') 41 | .then((result) => result[0]) 42 | }, 43 | 44 | delete(id) { 45 | return db(tableName) 46 | .where({ id }) 47 | .delete() 48 | } 49 | } 50 | 51 | module.exports = ProductService 52 | -------------------------------------------------------------------------------- /product/db.js: -------------------------------------------------------------------------------- 1 | 'use strict' 2 | 3 | const config = require('./config/db') 4 | const knex = require('knex') 5 | 6 | module.exports = knex(config) 7 | -------------------------------------------------------------------------------- /product/index.js: -------------------------------------------------------------------------------- 1 | 'use strict' 2 | 3 | const processType = process.env.PROCESS_TYPE 4 | 5 | switch (processType) { 6 | case 'web': 7 | require('./web') 8 | break 9 | case 'script': 10 | require('./migrate') 11 | break 12 | default: 13 | throw new Error( 14 | `Invalid process type: ${processType}. It should be one of: 'web', 'script'.` 15 | ) 16 | } 17 | -------------------------------------------------------------------------------- /product/k8s/product-deployment.yml: -------------------------------------------------------------------------------- 1 | apiVersion: apps/v1beta2 2 | kind: Deployment 3 | metadata: 4 | labels: 5 | app: product 6 | type: web 7 | name: product-web 8 | spec: 9 | selector: 10 | matchLabels: 11 | app: product 12 | type: web 13 | template: 14 | metadata: 15 | labels: 16 | app: product 17 | type: web 18 | spec: 19 | containers: 20 | - image: rs-ms/product 21 | name: product 22 | imagePullPolicy: IfNotPresent 23 | env: 24 | - name: DB_USER 25 | valueFrom: 26 | secretKeyRef: 27 | name: product 28 | key: db_user 29 | - name: DB_PASSWORD 30 | valueFrom: 31 | secretKeyRef: 32 | name: product 33 | key: db_password 34 | - name: DB_HOST 35 | valueFrom: 36 | secretKeyRef: 37 | name: product 38 | key: db_host 39 | - name: DB_DATABASE 40 | valueFrom: 41 | secretKeyRef: 42 | name: product 43 | key: db_database 44 | - name: DB_PORT 45 | valueFrom: 46 | secretKeyRef: 47 | name: product 48 | key: db_port 49 | - name: PROCESS_TYPE 50 | value: web 51 | -------------------------------------------------------------------------------- /product/k8s/product-secret.yml: -------------------------------------------------------------------------------- 1 | apiVersion: v1 2 | kind: Secret 3 | metadata: 4 | name: product 5 | data: 6 | db_user: cm9vdA== 7 | db_password: cm9vdA== 8 | db_database: dGVzdC1wcm9kdWN0cy1kYg== 9 | db_host: aG9zdC5kb2NrZXIuaW50ZXJuYWw= 10 | db_port: NTQzMg== 11 | -------------------------------------------------------------------------------- /product/k8s/product-service.yml: -------------------------------------------------------------------------------- 1 | apiVersion: v1 2 | kind: Service 3 | metadata: 4 | labels: 5 | app: product 6 | type: web 7 | name: product-web 8 | spec: 9 | ports: 10 | - port: 3000 11 | protocol: TCP 12 | targetPort: 3000 13 | selector: 14 | app: product 15 | type: web 16 | type: ClusterIP -------------------------------------------------------------------------------- /product/knexfile.js: -------------------------------------------------------------------------------- 1 | 'use strict' 2 | 3 | const dbConfig = require('./config/db') 4 | 5 | module.exports = { 6 | development: dbConfig, 7 | staging: dbConfig, 8 | production: dbConfig 9 | } 10 | -------------------------------------------------------------------------------- /product/migrate.js: -------------------------------------------------------------------------------- 1 | const { exec } = require('child_process') 2 | const logger = require('winston') 3 | 4 | exec('./node_modules/.bin/knex migrate:latest', 5 | (err, stdout, stderr) => { 6 | if (err) throw err 7 | logger.info(stdout) 8 | }) 9 | -------------------------------------------------------------------------------- /product/migrations/20180518140440_create-products-table.js: -------------------------------------------------------------------------------- 1 | 'use strict' 2 | 3 | const tableName = 'products' 4 | 5 | function up(knex) { 6 | return knex.schema.createTable(tableName, (table) => { 7 | table.increments('id') 8 | table.string('name').notNullable() 9 | table.text('description') 10 | table.text('ingredients') 11 | table.decimal('price').unsigned().notNullable() 12 | table.string('currency').notNullable() 13 | table.boolean('available').notNullable().defaultTo('false') 14 | }) 15 | } 16 | 17 | function down(knex) { 18 | return knex.schema.dropTableIfExists(tableName) 19 | } 20 | 21 | module.exports = { 22 | up, 23 | down 24 | } 25 | -------------------------------------------------------------------------------- /product/package.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "product", 3 | "version": "1.0.0", 4 | "description": "", 5 | "main": "index.js", 6 | "scripts": { 7 | "test": "echo \"Error: no test specified\" && exit 1", 8 | "db-start": "docker-compose up -d", 9 | "db-stop": "docker-compose down -d", 10 | "db-migrate": "./node_modules/.bin/knex migrate:latest", 11 | "db-seed": "./node_modules/.bin/knex seed:run", 12 | "start": "node index.js", 13 | "start:dev": "NODE_ENV=development PROCESS_TYPE=web ./node_modules/.bin/nodemon ./index.js", 14 | "docker-build": "../scripts/build product" 15 | }, 16 | "author": "", 17 | "license": "ISC", 18 | "dependencies": { 19 | "faker": "4.1.0", 20 | "joi": "^13.3.0", 21 | "knex": "0.14.2", 22 | "koa": "2.4.1", 23 | "koa-bodyparser": "4.2.0", 24 | "koa-compose": "4.0.0", 25 | "koa-logger": "^3.2.0", 26 | "koa-router": "7.3.0", 27 | "koa2-cors": "2.0.5", 28 | "lodash": "4.17.4", 29 | "npm-run-all": "4.1.3", 30 | "pg": "7.4.1", 31 | "winston": "2.4.0" 32 | }, 33 | "devDependencies": { 34 | "eslint": "4.15.0", 35 | "eslint-config-airbnb-base": "12.1.0", 36 | "eslint-plugin-import": "2.8.0", 37 | "dotenv": "4.0.0", 38 | "nodemon": "1.14.9" 39 | } 40 | } 41 | -------------------------------------------------------------------------------- /product/scripts/build.sh: -------------------------------------------------------------------------------- 1 | #!/usr/bin/env bash 2 | set -e 3 | 4 | VERSION_FILE="scripts/img-version-history" 5 | touch $VERSION_FILE 6 | 7 | version_history=$(cat $VERSION_FILE) 8 | 9 | if [ -z version_history ]; then 10 | version_history=0 11 | fi 12 | 13 | version=$((version_history += 1)) 14 | 15 | echo $version > $VERSION_FILE 16 | 17 | docker build . -t rs-ms/product:v$version -------------------------------------------------------------------------------- /product/scripts/patch.json: -------------------------------------------------------------------------------- 1 | { 2 | "spec": { 3 | "template": { 4 | "spec": { 5 | "containers": [ 6 | { 7 | "image": "rs-ms/product:v{{VERSION}}", 8 | "name": "product-web" 9 | } 10 | ] 11 | } 12 | } 13 | } 14 | } -------------------------------------------------------------------------------- /product/scripts/patch.sh: -------------------------------------------------------------------------------- 1 | #!/usr/bin/env bash 2 | set -e 3 | 4 | VERSION_FILE="scripts/img-version-history" 5 | PATCH_FILE="scripts/patch.json" 6 | VERSION_PLACEHOLDER="{{VERSION}}" 7 | touch $VERSION_FILE 8 | 9 | version=$(cat $VERSION_FILE) 10 | 11 | if [ -z version ]; then 12 | echo "No version history, exiting" 13 | exit 1 14 | fi 15 | 16 | PATCH=$(cat scripts/patch.json | tr "\n" " " | sed 's/ //g' | sed "s/${VERSION_PLACEHOLDER}/${version}/g") 17 | 18 | kubectl patch deployment gateway -p $PATCH -------------------------------------------------------------------------------- /product/seeds/dummy-products.js: -------------------------------------------------------------------------------- 1 | 2 | 'use strict' 3 | 4 | const faker = require('faker') 5 | 6 | const generateProducts = (quantity) => { 7 | const products = [] 8 | 9 | for (let i = 0; i < quantity; i += 1) { 10 | products.push({ 11 | name: faker.commerce.productName(), 12 | description: faker.lorem.sentences(3), 13 | ingredients: faker.lorem.words(5).split(' ').join(', '), 14 | price: faker.finance.amount(0.5, 10, 2), 15 | currency: 'USD', 16 | available: faker.random.boolean() 17 | }) 18 | } 19 | 20 | return products 21 | } 22 | 23 | function seed(knex) { 24 | // Deletes ALL existing entries 25 | return knex('products').del() 26 | .then(() => knex('products').insert(generateProducts(50))) 27 | } 28 | 29 | module.exports = { 30 | seed 31 | } 32 | -------------------------------------------------------------------------------- /product/server.js: -------------------------------------------------------------------------------- 1 | 'use strict' 2 | 3 | const Koa = require('koa') 4 | const logger = require('winston') 5 | const api = require('./api') 6 | const koaLogger = require('koa-logger') 7 | const cors = require('koa2-cors') 8 | 9 | const app = new Koa() 10 | 11 | app.proxy = true 12 | 13 | app 14 | .use(cors({ 15 | origin: '*' 16 | })) 17 | .use(koaLogger()) 18 | .use(api.routes()) 19 | .use(api.allowedMethods()) 20 | 21 | app.on('error', (err) => { 22 | logger.error('Server error', { error: err.message }) 23 | throw err 24 | }) 25 | 26 | module.exports = app 27 | -------------------------------------------------------------------------------- /product/web.js: -------------------------------------------------------------------------------- 1 | 'use strict' 2 | 3 | const dotenv = require('dotenv') 4 | 5 | if (process.env.NODE_ENV === 'development') { 6 | dotenv.config({ silent: true }) 7 | } 8 | 9 | const { promisify } = require('util') 10 | const http = require('http') 11 | const logger = require('winston') 12 | const app = require('./server') 13 | const config = require('./config/server') 14 | 15 | logger.default.transports.console.colorize = true 16 | logger.level = config.logger.level 17 | 18 | const server = http.createServer(app.callback()) 19 | const serverListen = promisify(server.listen).bind(server) 20 | 21 | serverListen(config.port) 22 | .then(() => { 23 | logger.info(`Products service is up and running on localhost:${config.port}`) 24 | }) 25 | .catch((err) => { 26 | logger.error(err) 27 | process.exit(1) 28 | }) 29 | -------------------------------------------------------------------------------- /scripts/build: -------------------------------------------------------------------------------- 1 | #!/usr/bin/env bash 2 | set -e 3 | 4 | app_name=$1 5 | 6 | image_name=rs-ms/$app_name 7 | current_version=$(docker images $image_name --format "{{.Tag}}" | head -2 | tail -1 | grep -o "[0-9]\+" || echo 0) 8 | next_version=$(( $current_version + 1)) 9 | docker build -t $image_name:v$next_version -t $image_name:latest . 10 | 11 | kubectl delete pod -l app=$app_name 12 | -------------------------------------------------------------------------------- /scripts/migrate: -------------------------------------------------------------------------------- 1 | #!/bin/bash 2 | 3 | function get_pod () { 4 | kubectl get pods -l app="$1" -o go-template --template '{{range .items}}{{.metadata.name}}{{"\n"}}{{end}}' | head -1 5 | } 6 | 7 | product_pod_name=$(get_pod product) 8 | user_pod_name=$(get_pod user) 9 | 10 | for pod_name in $user_pod_name $product_pod_name; do 11 | kubectl exec $pod_name npm run db-migrate 12 | done 13 | 14 | kubectl exec $product_pod_name npm run db-seed 15 | 16 | -------------------------------------------------------------------------------- /scripts/up: -------------------------------------------------------------------------------- 1 | #!/bin/bash 2 | 3 | eval $(minikube docker-env) 4 | 5 | script_directory="$( cd "$( dirname "${BASH_SOURCE[0]}" )" && pwd )" 6 | 7 | for app in "user" "product" "gateway" "front-end"; do 8 | echo "cding into: $script_directory/$app" 9 | cd "$script_directory/../$app" 10 | npm run docker-build 11 | kubectl create -f ./k8s 12 | done 13 | 14 | docker-compose up -d 15 | 16 | echo 'waiting 30 seconds for postgresql to start...' 17 | sleep 30 18 | 19 | docker exec $(docker ps --format '{{.ID}}' -f name=trainingmicroservicesv3_db) psql -d test-products-db -c "CREATE DATABASE \"test-users-db\"" 20 | 21 | $script_directory/migrate 22 | -------------------------------------------------------------------------------- /user/.dockerignore: -------------------------------------------------------------------------------- 1 | node_modules -------------------------------------------------------------------------------- /user/.gitignore: -------------------------------------------------------------------------------- 1 | node_modules 2 | 3 | img-version-history -------------------------------------------------------------------------------- /user/Dockerfile: -------------------------------------------------------------------------------- 1 | FROM node:8.9.4 2 | 3 | COPY package.json package.json 4 | RUN npm install 5 | # RUN npm rebuild bcrypt --build-from-source 6 | 7 | # Add your source files 8 | COPY . . 9 | 10 | ENV NODE_ENV production 11 | ENV PORT 3000 12 | 13 | EXPOSE 3000 14 | 15 | CMD ["node", "index.js", "--use-strict"] -------------------------------------------------------------------------------- /user/api/index.js: -------------------------------------------------------------------------------- 1 | 'use strict' 2 | 3 | const { login } = require('./login') 4 | const { register } = require('./register') 5 | const Router = require('koa-router') 6 | const bodyParser = require('koa-bodyparser') 7 | 8 | const router = new Router() 9 | 10 | router.use(bodyParser()) 11 | 12 | 13 | router.get('/healthcheck', (ctx) => { 14 | ctx.body = 'Users Service is up!' 15 | }) 16 | 17 | // USER MANAGEMENT 18 | router.post('/users/register', register) 19 | router.post('/login', login) 20 | 21 | module.exports = router 22 | -------------------------------------------------------------------------------- /user/api/login.js: -------------------------------------------------------------------------------- 1 | 'use strict' 2 | 3 | const bcrypt = require('bcrypt') 4 | const jwt = require('jsonwebtoken') 5 | const logger = require('winston') 6 | const UserService = require('../dal/userService') 7 | const config = require('../config/server') 8 | 9 | const UserLogin = { 10 | async login(ctx) { 11 | const loginCredentials = { 12 | email: ctx.request.body.email, 13 | password: ctx.request.body.pass 14 | } 15 | 16 | try { 17 | const result = await UserService.getLogin(loginCredentials.email) 18 | if (!result) { 19 | ctx.status = 401 20 | return 21 | } 22 | const isPasswordValid = await bcrypt.compare(loginCredentials.password, result.pass) 23 | if (!isPasswordValid) { 24 | ctx.status = 401 25 | return 26 | } 27 | const token = jwt.sign({ id: result.id, isAdmin: result.isAdmin }, config.tokenSecret, { 28 | expiresIn: config.tokenExpiration 29 | }) 30 | ctx.body = { 31 | token, 32 | message: 'Successfully logged in', 33 | user: { 34 | username: result.username, 35 | email: result.email, 36 | admin: result.isAdmin 37 | } 38 | } 39 | return 40 | } catch (err) { 41 | logger.error(err) 42 | ctx.status = 500 43 | } 44 | } 45 | } 46 | 47 | module.exports = { 48 | login: UserLogin.login 49 | } 50 | -------------------------------------------------------------------------------- /user/api/register.js: -------------------------------------------------------------------------------- 1 | 'use strict' 2 | 3 | const logger = require('winston') 4 | const UserService = require('../dal/userService') 5 | const { hashPass } = require('../utils/hashPass') 6 | 7 | const UserRegister = { 8 | async register(ctx) { 9 | try { 10 | const credentials = { 11 | username: ctx.request.body.username, 12 | email: ctx.request.body.email, 13 | pass: await hashPass(ctx.request.body.pass) 14 | } 15 | await UserService.insert(credentials) 16 | ctx.body = { 17 | message: 'User successfully registered' 18 | } 19 | ctx.status = 201 20 | } catch (err) { 21 | logger.error(err) 22 | ctx.status = 500 23 | } 24 | } 25 | } 26 | 27 | module.exports = { 28 | register: UserRegister.register 29 | } 30 | -------------------------------------------------------------------------------- /user/config/db.js: -------------------------------------------------------------------------------- 1 | 'use strict' 2 | 3 | const path = require('path') 4 | const joi = require('joi') 5 | 6 | const envVarsSchema = joi.object({ 7 | DB_HOST: joi.string().required(), 8 | DB_USER: joi.string().required(), 9 | DB_PASSWORD: joi.string().allow('').required(), 10 | DB_DATABASE: joi.string().required(), 11 | DB_PORT: joi.number().required(), 12 | DB_POOL_MIN: joi.number().default(1), 13 | DB_POOL_MAX: joi.number().default(20), 14 | DB_DEBUG: joi.bool().default(false) 15 | }).unknown() 16 | .required() 17 | 18 | const envVars = joi.attempt(process.env, envVarsSchema) 19 | 20 | const config = { 21 | client: 'pg', 22 | connection: { 23 | host: process.env.DB_HOST, 24 | user: process.env.DB_USER, 25 | password: process.env.DB_PASSWORD, 26 | database: process.env.DB_DATABASE, 27 | port: process.env.DB_PORT 28 | }, 29 | pool: { 30 | min: envVars.DB_POOL_MIN, 31 | max: envVars.DB_POOL_MAX 32 | }, 33 | migrations: { 34 | directory: path.join(__dirname, '../migrations') 35 | }, 36 | debug: envVars.DB_DEBUG 37 | } 38 | 39 | module.exports = config 40 | -------------------------------------------------------------------------------- /user/config/server.js: -------------------------------------------------------------------------------- 1 | 'use strict' 2 | 3 | const joi = require('joi') 4 | 5 | const envVarsSchema = joi.object({ 6 | PORT: joi.number().integer().min(0).required(), 7 | TOKEN_SECRET: joi.string().default('my_secret_token'), 8 | TOKEN_EXPIRATION: joi.string().default('1h'), 9 | LOGGER_LEVEL: joi.string() 10 | }).unknown() 11 | .required() 12 | 13 | const envVars = joi.attempt(process.env, envVarsSchema) 14 | 15 | const config = { 16 | port: envVars.PORT, 17 | tokenSecret: envVars.TOKEN_SECRET, 18 | tokenExpiration: envVars.TOKEN_EXPIRATION, 19 | logger: { 20 | level: envVars.LOGGER_LEVEL 21 | } 22 | } 23 | 24 | module.exports = config 25 | -------------------------------------------------------------------------------- /user/dal/userService.js: -------------------------------------------------------------------------------- 1 | 'use strict' 2 | 3 | const db = require('../db') 4 | 5 | const tableName = 'users' 6 | 7 | const UserService = { 8 | insert(credentials) { 9 | return db(tableName) 10 | .insert(credentials) 11 | .returning('id') 12 | .then((result) => result[0]) 13 | }, 14 | 15 | getLogin(email) { 16 | return db(tableName) 17 | .where({ email }) 18 | .returning('*') 19 | .then((result) => result[0]) 20 | }, 21 | } 22 | 23 | module.exports = UserService 24 | -------------------------------------------------------------------------------- /user/db.js: -------------------------------------------------------------------------------- 1 | 'use strict' 2 | 3 | const config = require('./config/db') 4 | const knex = require('knex') 5 | 6 | module.exports = knex(config) 7 | -------------------------------------------------------------------------------- /user/index.js: -------------------------------------------------------------------------------- 1 | 'use strict' 2 | 3 | const processType = process.env.PROCESS_TYPE 4 | 5 | switch (processType) { 6 | case 'web': 7 | require('./web') 8 | break 9 | case 'script': 10 | require('./migrate') 11 | break 12 | default: 13 | throw new Error( 14 | `Invalid process type: ${processType}. It should be one of: 'web', 'script'.` 15 | ) 16 | } 17 | -------------------------------------------------------------------------------- /user/k8s/user-deployment.yml: -------------------------------------------------------------------------------- 1 | apiVersion: apps/v1beta2 2 | kind: Deployment 3 | metadata: 4 | labels: 5 | app: user 6 | type: web 7 | name: user-web 8 | spec: 9 | selector: 10 | matchLabels: 11 | app: user 12 | type: web 13 | template: 14 | metadata: 15 | labels: 16 | app: user 17 | type: web 18 | spec: 19 | containers: 20 | - image: rs-ms/user 21 | name: user 22 | imagePullPolicy: IfNotPresent 23 | env: 24 | - name: DB_USER 25 | valueFrom: 26 | secretKeyRef: 27 | name: user 28 | key: db_user 29 | - name: DB_PASSWORD 30 | valueFrom: 31 | secretKeyRef: 32 | name: user 33 | key: db_password 34 | - name: DB_HOST 35 | valueFrom: 36 | secretKeyRef: 37 | name: user 38 | key: db_host 39 | - name: DB_DATABASE 40 | valueFrom: 41 | secretKeyRef: 42 | name: user 43 | key: db_database 44 | - name: DB_PORT 45 | valueFrom: 46 | secretKeyRef: 47 | name: user 48 | key: db_port 49 | - name: TOKEN_SECRET 50 | valueFrom: 51 | secretKeyRef: 52 | name: user 53 | key: token_secret 54 | - name: PROCESS_TYPE 55 | value: web 56 | -------------------------------------------------------------------------------- /user/k8s/user-secret.yml: -------------------------------------------------------------------------------- 1 | apiVersion: v1 2 | kind: Secret 3 | metadata: 4 | name: user 5 | data: 6 | db_user: cm9vdA== 7 | db_password: cm9vdA== 8 | db_database: dGVzdC11c2Vycy1kYg== 9 | db_host: aG9zdC5kb2NrZXIuaW50ZXJuYWw= 10 | db_port: NTQzMg== 11 | token_secret: dGhpcy1pcy1teS1zZWNyZXQ= 12 | -------------------------------------------------------------------------------- /user/k8s/user-service.yml: -------------------------------------------------------------------------------- 1 | apiVersion: v1 2 | kind: Service 3 | metadata: 4 | labels: 5 | app: user 6 | type: web 7 | name: user-web 8 | spec: 9 | ports: 10 | - port: 3000 11 | protocol: TCP 12 | targetPort: 3000 13 | selector: 14 | app: user 15 | type: web 16 | type: ClusterIP -------------------------------------------------------------------------------- /user/knexfile.js: -------------------------------------------------------------------------------- 1 | 'use strict' 2 | 3 | const dbConfig = require('./config/db') 4 | 5 | module.exports = { 6 | development: dbConfig, 7 | staging: dbConfig, 8 | production: dbConfig 9 | } 10 | -------------------------------------------------------------------------------- /user/migrate.js: -------------------------------------------------------------------------------- 1 | const { exec } = require('child_process') 2 | const logger = require('winston') 3 | 4 | exec('./node_modules/.bin/knex migrate:latest', 5 | (err, stdout, stderr) => { 6 | if (err) throw err 7 | logger.info(stdout) 8 | }) 9 | -------------------------------------------------------------------------------- /user/migrations/20180523144152_create-users-table.js: -------------------------------------------------------------------------------- 1 | 'use strict' 2 | 3 | const tableName = 'users' 4 | 5 | function up(knex) { 6 | return knex.schema.createTable(tableName, (table) => { 7 | table.increments('id') 8 | table.string('username').notNullable() 9 | table.string('email').notNullable() 10 | table.string('pass').notNullable() 11 | table.boolean('isAdmin').defaultTo('false') 12 | }) 13 | } 14 | 15 | function down(knex) { 16 | return knex.schema.dropTableIfExists(tableName) 17 | } 18 | 19 | module.exports = { 20 | up, 21 | down 22 | } 23 | -------------------------------------------------------------------------------- /user/package.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "users", 3 | "version": "1.0.0", 4 | "description": "", 5 | "main": "index.js", 6 | "scripts": { 7 | "test": "echo \"Error: no test specified\" && exit 1", 8 | "db-start": "docker run --rm --name freebie-users-local -e POSTGRES_PASSWORD=root -e POSTGRES_USER=root -e POSTGRES_DB=test-users-db -p 3001:5432 -d postgres:10", 9 | "db-stop": "docker rm -f freebie-users-local || true", 10 | "db-migrate": "./node_modules/.bin/knex migrate:latest", 11 | "start": "node index.js", 12 | "start:dev": "NODE_ENV=development PROCESS_TYPE=web ./node_modules/.bin/nodemon ./index.js", 13 | "docker-build": "../scripts/build \"user\"" 14 | }, 15 | "author": "", 16 | "license": "ISC", 17 | "dependencies": { 18 | "bcrypt": "^2.0.1", 19 | "joi": "^13.3.0", 20 | "jsonwebtoken": "^8.2.1", 21 | "knex": "^0.14.6", 22 | "koa": "^2.5.1", 23 | "koa-bodyparser": "^4.2.1", 24 | "koa-logger": "^3.2.0", 25 | "koa-router": "^7.4.0", 26 | "koa2-cors": "^2.0.5", 27 | "winston": "^2.4.2", 28 | "pg": "7.4.1" 29 | }, 30 | "devDependencies": { 31 | "dotenv": "^5.0.1", 32 | "nodemon": "^1.17.5" 33 | } 34 | } 35 | -------------------------------------------------------------------------------- /user/server.js: -------------------------------------------------------------------------------- 1 | 'use strict' 2 | 3 | const Koa = require('koa') 4 | const logger = require('winston') 5 | const koaLogger = require('koa-logger') 6 | const router = require('./api/') 7 | 8 | const app = new Koa() 9 | const cors = require('koa2-cors') 10 | 11 | app.proxy = true 12 | 13 | app 14 | .use(cors({ 15 | origin: '*' 16 | })) 17 | .use(koaLogger()) 18 | .use(router.routes()) 19 | .use(router.allowedMethods()) 20 | 21 | app.on('error', (err) => { 22 | logger.error('Server error', { error: err.message }) 23 | throw err 24 | }) 25 | 26 | module.exports = app 27 | -------------------------------------------------------------------------------- /user/utils/db-helper.js: -------------------------------------------------------------------------------- 1 | // 'use strict' 2 | 3 | // const logger = require('winston') 4 | // // const knex = require('../models/db/') 5 | // // const { generateProducts } = require('./generate-seed') 6 | // const retry = require('./retry') 7 | // // const { hashPass } = require('./hashPass') 8 | 9 | // async function checkConnection(knex) { 10 | // try { 11 | // await retry(10, knex.select.bind(knex.select), { criteria: 1 }) 12 | // logger.info('DB connection is ready') 13 | // return 14 | // } catch (err) { 15 | // logger.error(err) 16 | // throw err 17 | // } 18 | // } 19 | 20 | // async function migrateDB(knex) { 21 | // await knex.migrate.latest() 22 | // .then(() => { 23 | // // eslint-disable-next-line no-console 24 | // logger.info('DB is synched') 25 | // }) 26 | // .catch((err) => { 27 | // // eslint-disable-next-line no-console 28 | // logger.error(err) 29 | // throw err 30 | // }) 31 | // } 32 | 33 | // async function clearDB(knex) { 34 | // try { 35 | // await knex.migrate.rollback() 36 | // logger.debug('Database is cleared') 37 | // return 38 | // } catch (err) { 39 | // logger.error('Cannot drop the base') 40 | // throw err 41 | // } 42 | // } 43 | 44 | // // async function seedDB() { 45 | // // try { 46 | // // await seedProducts(20) 47 | // // await seedAdmin() 48 | // // await seedUser() 49 | // // return 50 | // // } catch (err) { 51 | // // logger.error(err) 52 | // // throw err 53 | // // } 54 | // // } 55 | 56 | // // async function checkDBContent() { 57 | // // try { 58 | // // const result = await knex('products').first() 59 | // // if (result) { 60 | // // return false 61 | // // } 62 | // // return true 63 | // // } catch (err) { 64 | // // throw err 65 | // // } 66 | // // } 67 | 68 | // module.exports = { 69 | // checkConnection, 70 | // migrateDB, 71 | // // seedDB, 72 | // clearDB, 73 | // // checkDBContent 74 | // } 75 | -------------------------------------------------------------------------------- /user/utils/hashPass.js: -------------------------------------------------------------------------------- 1 | 'use strict' 2 | 3 | const bcrypt = require('bcrypt') 4 | const logger = require('winston') 5 | 6 | async function hashPass(valueToHash) { 7 | const rounds = 10 8 | 9 | try { 10 | const hashedValue = await bcrypt.hash(valueToHash, rounds) 11 | return hashedValue 12 | } catch (err) { 13 | logger.error(err) 14 | throw err 15 | } 16 | } 17 | 18 | module.exports = { 19 | hashPass 20 | } 21 | -------------------------------------------------------------------------------- /user/utils/retry.js: -------------------------------------------------------------------------------- 1 | // 'use strict' 2 | 3 | // const logger = require('winston') 4 | 5 | // function sleep(timeout) { 6 | // return new Promise((resolve) => { 7 | // setTimeout(() => { 8 | // resolve() 9 | // }, timeout) 10 | // }) 11 | // } 12 | 13 | // async function retry(times, subject, ...subjectArgs) { 14 | // let counter = 0 15 | // let err 16 | 17 | // while (counter < times) { 18 | // try { 19 | // logger.debug(`Trying connection: ${counter}`) 20 | // // eslint-disable-next-line 21 | // return await subject(...subjectArgs) 22 | // } catch (ex) { 23 | // err = ex 24 | // counter += 1 25 | // // eslint-disable-next-line 26 | // await sleep(counter * 1000) 27 | // } 28 | // } 29 | 30 | // throw err 31 | // } 32 | 33 | // module.exports = retry 34 | -------------------------------------------------------------------------------- /user/web.js: -------------------------------------------------------------------------------- 1 | 'use strict' 2 | 3 | const dotenv = require('dotenv') 4 | 5 | if (process.env.NODE_ENV === 'development') { 6 | dotenv.config({ silent: true }) 7 | } 8 | 9 | const { promisify } = require('util') 10 | const http = require('http') 11 | const logger = require('winston') 12 | const app = require('./server') 13 | const config = require('./config/server') 14 | 15 | logger.default.transports.console.colorize = true 16 | logger.level = config.logger.level 17 | 18 | const server = http.createServer(app.callback()) 19 | const serverListen = promisify(server.listen).bind(server) 20 | 21 | serverListen(config.port) 22 | .then(() => { 23 | logger.info(`Users service is up and running on localhost:${config.port}`) 24 | }) 25 | .catch((err) => { 26 | logger.error(err) 27 | process.exit(1) 28 | }) 29 | --------------------------------------------------------------------------------