├── .gitignore ├── .vscode └── settings.json ├── client ├── .gitignore ├── README.md ├── package-lock.json ├── package.json ├── public │ ├── favicon.ico │ ├── index.html │ └── manifest.json ├── src │ ├── App.css │ ├── App.js │ ├── App.test.js │ ├── actions │ │ ├── authActions.js │ │ └── types.js │ ├── components │ │ ├── auth │ │ │ ├── Login.js │ │ │ └── Register.js │ │ ├── guards │ │ │ ├── IsAdmin.js │ │ │ ├── IsAuth.js │ │ │ ├── IsModerator.js │ │ │ └── NotAuth.js │ │ ├── home │ │ │ └── Home.js │ │ ├── layouts │ │ │ ├── Banner.js │ │ │ ├── Navbar.js │ │ │ └── Sidebar.js │ │ ├── notfound │ │ │ └── NotFound.js │ │ └── user │ │ │ ├── admin │ │ │ └── Admin.js │ │ │ ├── dashboard │ │ │ └── Dashboard.js │ │ │ └── moderator │ │ │ └── Moderator.js │ ├── index.js │ ├── reducers │ │ ├── authReducers.js │ │ ├── errorReducers.js │ │ └── index.js │ ├── registerServiceWorker.js │ ├── store.js │ └── utils │ │ └── setAuthToken.js └── yarn.lock ├── config ├── dbSecretKeys.js ├── dbSecretKeysProd.js └── passport.js ├── guards ├── isAdmin.js └── isModerator.js ├── models └── User.js ├── package-lock.json ├── package.json ├── routes └── api │ ├── auth.js │ └── users.js ├── server.js └── utils └── isEmpty.js /.gitignore: -------------------------------------------------------------------------------- 1 | /node_modules 2 | /config/dbSecretKeysDev.js -------------------------------------------------------------------------------- /.vscode/settings.json: -------------------------------------------------------------------------------- 1 | { 2 | } -------------------------------------------------------------------------------- /client/.gitignore: -------------------------------------------------------------------------------- 1 | # See https://help.github.com/ignore-files/ for more about ignoring files. 2 | 3 | # dependencies 4 | /node_modules 5 | 6 | # testing 7 | /coverage 8 | 9 | # production 10 | /build 11 | 12 | # misc 13 | .DS_Store 14 | .env.local 15 | .env.development.local 16 | .env.test.local 17 | .env.production.local 18 | 19 | npm-debug.log* 20 | yarn-debug.log* 21 | yarn-error.log* 22 | -------------------------------------------------------------------------------- /client/package.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "client", 3 | "version": "0.1.0", 4 | "private": true, 5 | "dependencies": { 6 | "axios": "^0.18.0", 7 | "classnames": "^2.2.6", 8 | "jwt-decode": "^2.2.0", 9 | "react": "^16.4.2", 10 | "react-dom": "^16.4.2", 11 | "react-redux": "^5.0.7", 12 | "react-router-dom": "^4.3.1", 13 | "react-scripts": "1.1.4", 14 | "redux": "^4.0.0", 15 | "redux-thunk": "^2.3.0" 16 | }, 17 | "scripts": { 18 | "start": "react-scripts start", 19 | "build": "react-scripts build", 20 | "test": "react-scripts test --env=jsdom", 21 | "eject": "react-scripts eject" 22 | }, 23 | "proxy": "http://localhost:5000" 24 | } 25 | -------------------------------------------------------------------------------- /client/public/favicon.ico: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/nbagonoc/mern-multiple-user/9c3e5e973382247758c741f27ba665e226a05e53/client/public/favicon.ico -------------------------------------------------------------------------------- /client/public/index.html: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | 7 | 8 | 10 | 11 | 12 | 13 | React App 14 | 15 | 16 | 17 | 20 |
21 | 29 | 30 | 31 | -------------------------------------------------------------------------------- /client/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 | -------------------------------------------------------------------------------- /client/src/App.css: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/nbagonoc/mern-multiple-user/9c3e5e973382247758c741f27ba665e226a05e53/client/src/App.css -------------------------------------------------------------------------------- /client/src/App.js: -------------------------------------------------------------------------------- 1 | import React, { Component } from "react"; 2 | import { BrowserRouter as Router, Route, Switch } from "react-router-dom"; 3 | import jwt_decode from "jwt-decode"; 4 | import setAuthToken from "./utils/setAuthToken"; 5 | import { setCurrentUser, logoutUser } from "./actions/authActions"; 6 | import { Provider } from "react-redux"; 7 | import store from "./store"; 8 | 9 | // COMPONENTS 10 | // layout 11 | import Navbar from "./components/layouts/Navbar"; 12 | // pages 13 | import Home from "./components/home/Home"; 14 | import Register from "./components/auth/Register"; 15 | import Login from "./components/auth/Login"; 16 | import Dashboard from "./components/user/dashboard/Dashboard"; 17 | import Moderator from "./components/user/moderator/Moderator"; 18 | import Admin from "./components/user/admin/Admin"; 19 | import NotFound from "./components/notfound/NotFound"; 20 | // guards 21 | import NotAuth from "./components/guards/NotAuth"; 22 | import IsAuth from "./components/guards/IsAuth"; 23 | import IsModerator from "./components/guards/IsModerator"; 24 | import IsAdmin from "./components/guards/IsAdmin"; 25 | 26 | // ASSETS 27 | import "./App.css"; 28 | 29 | // TOKEN CHECKER 30 | // Checks the local storage if there's a token, and if it still valid 31 | if (localStorage.jwtToken) { 32 | // set auth token header auth 33 | setAuthToken(localStorage.jwtToken); 34 | // decode token and get suer info and expiration 35 | const decoded = jwt_decode(localStorage.jwtToken); 36 | // set user and isAuthenticated 37 | store.dispatch(setCurrentUser(decoded)); 38 | 39 | // check for epired token 40 | const currentTime = Date.now() / 1000; 41 | if (decoded.exp < currentTime) { 42 | // logout user 43 | store.dispatch(logoutUser()); 44 | } 45 | } 46 | 47 | class App extends Component { 48 | render() { 49 | return ( 50 | 51 | 52 |
53 | 54 | 55 | 56 | 57 | 58 | 59 | 60 | 61 | 62 | 63 |
64 |
65 |
66 | ); 67 | } 68 | } 69 | 70 | export default App; 71 | -------------------------------------------------------------------------------- /client/src/App.test.js: -------------------------------------------------------------------------------- 1 | import React from 'react'; 2 | import ReactDOM from 'react-dom'; 3 | import App from './App'; 4 | 5 | it('renders without crashing', () => { 6 | const div = document.createElement('div'); 7 | ReactDOM.render(, div); 8 | ReactDOM.unmountComponentAtNode(div); 9 | }); 10 | -------------------------------------------------------------------------------- /client/src/actions/authActions.js: -------------------------------------------------------------------------------- 1 | import axios from "axios"; 2 | import setAuthToken from "../utils/setAuthToken"; 3 | import jwt_decode from "jwt-decode"; 4 | 5 | import { 6 | SET_CURRENT_USER, 7 | CLEAR_CURRENT_USER, 8 | GET_ERRORS, 9 | CLEAR_ERRORS 10 | } from "./types"; 11 | 12 | // register user 13 | export const registerUser = (userData, history) => dispatch => { 14 | axios 15 | .post("/api/auth/register", userData) 16 | .then(res => { 17 | // redirect user to login page 18 | history.push("/login"); 19 | // clear errors 20 | dispatch({ 21 | type: CLEAR_ERRORS, 22 | payload: {} 23 | }); 24 | }) 25 | .catch(err => 26 | dispatch({ 27 | type: GET_ERRORS, 28 | payload: err.response.data 29 | }) 30 | ); 31 | }; 32 | 33 | // login user 34 | export const loginUser = (userData, history) => dispatch => { 35 | axios 36 | .post("/api/auth/login", userData) 37 | .then(res => { 38 | // save to localStorage 39 | const { token } = res.data; 40 | // set token to localStorage 41 | localStorage.setItem("jwtToken", token); 42 | // set token to auth header 43 | setAuthToken(token); 44 | // decode token to get user data 45 | const decoded = jwt_decode(token); 46 | // set current user 47 | dispatch(setCurrentUser(decoded)); 48 | // redirect user 49 | history.push("/dashboard"); 50 | // clear errors 51 | dispatch({ 52 | type: CLEAR_ERRORS, 53 | payload: {} 54 | }); 55 | }) 56 | .catch(err => 57 | dispatch({ 58 | type: GET_ERRORS, 59 | payload: err.response.data 60 | }) 61 | ); 62 | }; 63 | 64 | // set token of logged in user 65 | export const setCurrentUser = decoded => { 66 | return { 67 | type: SET_CURRENT_USER, 68 | payload: decoded 69 | }; 70 | }; 71 | 72 | // log user out 73 | export const logoutUser = () => dispatch => { 74 | // remove token from localStorage 75 | localStorage.removeItem("jwtToken"); 76 | // Clear the current user 77 | dispatch({ 78 | type: CLEAR_CURRENT_USER, 79 | payload: {} 80 | }); 81 | }; 82 | -------------------------------------------------------------------------------- /client/src/actions/types.js: -------------------------------------------------------------------------------- 1 | export const SET_CURRENT_USER = "SET_CURRENT_USER"; 2 | export const CLEAR_CURRENT_USER = "CLEAR_CURRENT_USER"; 3 | export const GET_ERRORS = "GET_ERRORS"; 4 | export const CLEAR_ERRORS = "CLEAR_ERRORS"; 5 | -------------------------------------------------------------------------------- /client/src/components/auth/Login.js: -------------------------------------------------------------------------------- 1 | import React, { Component } from "react"; 2 | import PropTypes from "prop-types"; 3 | import { Link } from "react-router-dom"; 4 | import { connect } from "react-redux"; 5 | import { loginUser } from "../../actions/authActions"; 6 | import classnames from "classnames"; 7 | 8 | class Login extends Component { 9 | state = { 10 | email: "", 11 | password: "", 12 | errors: {} 13 | }; 14 | 15 | onChange = e => { 16 | this.setState({ [e.target.name]: e.target.value }); 17 | }; 18 | 19 | onSubmitHandler = e => { 20 | e.preventDefault(); 21 | 22 | const userData = { 23 | email: this.state.email, 24 | password: this.state.password 25 | }; 26 | 27 | this.props.loginUser(userData, this.props.history); 28 | }; 29 | 30 | componentWillReceiveProps(nextProps) { 31 | if (nextProps.errors) { 32 | this.setState({ errors: nextProps.errors }); 33 | } 34 | } 35 | 36 | render() { 37 | const { email, password, errors } = this.state; 38 | 39 | return ( 40 |
41 |
42 |
43 |
44 |
Sign-in
45 |
46 |
47 | {/* Email */} 48 |
49 | 59 | {errors && ( 60 |
{errors.email}
61 | )} 62 |
63 | {/* password */} 64 |
65 | 75 | {errors && ( 76 |
{errors.password}
77 | )} 78 |
79 | 85 | 86 | Not yet registered yet? 87 | Register now 88 | 89 |
90 |
91 |
92 |
93 |
94 |
95 | ); 96 | } 97 | } 98 | 99 | Login.propTypes = { 100 | loginUser: PropTypes.func.isRequired, 101 | auth: PropTypes.object.isRequired, 102 | errors: PropTypes.object.isRequired 103 | }; 104 | 105 | const mapStateToProps = state => ({ 106 | auth: state.auth, 107 | errors: state.errors 108 | }); 109 | 110 | export default connect( 111 | mapStateToProps, 112 | { loginUser } 113 | )(Login); 114 | -------------------------------------------------------------------------------- /client/src/components/auth/Register.js: -------------------------------------------------------------------------------- 1 | import React, { Component } from "react"; 2 | import PropTypes from "prop-types"; 3 | import { Link } from "react-router-dom"; 4 | import { connect } from "react-redux"; 5 | import { registerUser } from "../../actions/authActions"; 6 | import classnames from "classnames"; 7 | 8 | class Register extends Component { 9 | state = { 10 | name: "", 11 | email: "", 12 | password: "", 13 | password2: "", 14 | errors: {} 15 | }; 16 | 17 | onChange = e => { 18 | this.setState({ [e.target.name]: e.target.value }); 19 | }; 20 | 21 | onSubmitHandler = e => { 22 | e.preventDefault(); 23 | 24 | const newUser = { 25 | name: this.state.name, 26 | email: this.state.email, 27 | password: this.state.password, 28 | password2: this.state.password2 29 | }; 30 | 31 | this.props.registerUser(newUser, this.props.history); 32 | }; 33 | 34 | componentWillReceiveProps(nextProps) { 35 | if (nextProps.errors) { 36 | this.setState({ errors: nextProps.errors }); 37 | } 38 | } 39 | 40 | render() { 41 | const { name, email, password, password2, errors } = this.state; 42 | 43 | return ( 44 |
45 |
46 |
47 |
48 |
Sign-up
49 |
50 |
51 | {/* Name */} 52 |
53 | 63 | {errors && ( 64 |
{errors.name}
65 | )} 66 |
67 | {/* Email */} 68 |
69 | 79 | {errors && ( 80 |
{errors.email}
81 | )} 82 |
83 | {/* password */} 84 |
85 | 95 | {errors && ( 96 |
{errors.password}
97 | )} 98 |
99 | {/* password2 */} 100 |
101 | 111 | {errors && ( 112 |
{errors.password2}
113 | )} 114 |
115 | 128 | 129 | Already have an account? 130 | Login now 131 | 132 |
133 |
134 |
135 |
136 |
137 |
138 | ); 139 | } 140 | } 141 | 142 | Register.propTypes = { 143 | registerUser: PropTypes.func.isRequired, 144 | auth: PropTypes.object.isRequired, 145 | errors: PropTypes.object.isRequired 146 | }; 147 | 148 | const mapStateToProps = state => ({ 149 | auth: state.auth, 150 | errors: state.errors 151 | }); 152 | 153 | export default connect( 154 | mapStateToProps, 155 | { registerUser } 156 | )(Register); 157 | -------------------------------------------------------------------------------- /client/src/components/guards/IsAdmin.js: -------------------------------------------------------------------------------- 1 | import React from "react"; 2 | import { Route, Redirect } from "react-router-dom"; 3 | import { connect } from "react-redux"; 4 | import PropTypes from "prop-types"; 5 | 6 | const IsAdmin = ({ component: Component, auth, ...rest }) => ( 7 | 10 | auth.isAuthenticated === true ? ( 11 | auth.user.role === "admin" ? ( 12 | 13 | ) : ( 14 | 15 | ) 16 | ) : ( 17 | 18 | ) 19 | } 20 | /> 21 | ); 22 | 23 | IsAdmin.propTypes = { 24 | auth: PropTypes.object.isRequired 25 | }; 26 | 27 | const mapStateToProps = state => ({ 28 | auth: state.auth 29 | }); 30 | 31 | export default connect(mapStateToProps)(IsAdmin); 32 | -------------------------------------------------------------------------------- /client/src/components/guards/IsAuth.js: -------------------------------------------------------------------------------- 1 | import React from "react"; 2 | import { Route, Redirect } from "react-router-dom"; 3 | import { connect } from "react-redux"; 4 | import PropTypes from "prop-types"; 5 | 6 | const IsAuth = ({ component: Component, auth, ...rest }) => ( 7 | 10 | auth.isAuthenticated === true ? ( 11 | 12 | ) : ( 13 | 14 | ) 15 | } 16 | /> 17 | ); 18 | 19 | IsAuth.propTypes = { 20 | auth: PropTypes.object.isRequired 21 | }; 22 | 23 | const mapStateToProps = state => ({ 24 | auth: state.auth 25 | }); 26 | 27 | export default connect(mapStateToProps)(IsAuth); 28 | -------------------------------------------------------------------------------- /client/src/components/guards/IsModerator.js: -------------------------------------------------------------------------------- 1 | import React from "react"; 2 | import { Route, Redirect } from "react-router-dom"; 3 | import { connect } from "react-redux"; 4 | import PropTypes from "prop-types"; 5 | 6 | const IsModerator = ({ component: Component, auth, ...rest }) => ( 7 | 10 | auth.isAuthenticated === true ? ( 11 | auth.user.role === "moderator" || auth.user.role === "admin" ? ( 12 | 13 | ) : ( 14 | 15 | ) 16 | ) : ( 17 | 18 | ) 19 | } 20 | /> 21 | ); 22 | 23 | IsModerator.propTypes = { 24 | auth: PropTypes.object.isRequired 25 | }; 26 | 27 | const mapStateToProps = state => ({ 28 | auth: state.auth 29 | }); 30 | 31 | export default connect(mapStateToProps)(IsModerator); 32 | -------------------------------------------------------------------------------- /client/src/components/guards/NotAuth.js: -------------------------------------------------------------------------------- 1 | import React from "react"; 2 | import { Route, Redirect } from "react-router-dom"; 3 | import { connect } from "react-redux"; 4 | import PropTypes from "prop-types"; 5 | 6 | const IsAuth = ({ component: Component, auth, ...rest }) => ( 7 | 10 | auth.isAuthenticated === false ? ( 11 | 12 | ) : ( 13 | 14 | ) 15 | } 16 | /> 17 | ); 18 | 19 | IsAuth.propTypes = { 20 | auth: PropTypes.object.isRequired 21 | }; 22 | 23 | const mapStateToProps = state => ({ 24 | auth: state.auth 25 | }); 26 | 27 | export default connect(mapStateToProps)(IsAuth); 28 | -------------------------------------------------------------------------------- /client/src/components/home/Home.js: -------------------------------------------------------------------------------- 1 | import React from "react"; 2 | 3 | // COMPONENTS 4 | import Banner from "../layouts/Banner"; 5 | 6 | const Home = () => { 7 | return ( 8 |
9 | 10 |
11 | ); 12 | }; 13 | 14 | export default Home; 15 | -------------------------------------------------------------------------------- /client/src/components/layouts/Banner.js: -------------------------------------------------------------------------------- 1 | import React from "react"; 2 | 3 | const Banner = () => { 4 | return ( 5 |
6 |

7 | MERN Stack Boilerplate 8 |

9 |

10 | A MERN Stack boilerplate with multiple auth user roles using JWT 11 |

12 |
13 | ); 14 | }; 15 | 16 | export default Banner; 17 | -------------------------------------------------------------------------------- /client/src/components/layouts/Navbar.js: -------------------------------------------------------------------------------- 1 | import React, { Component } from "react"; 2 | import { Link } from "react-router-dom"; 3 | import PropTypes from "prop-types"; 4 | import { connect } from "react-redux"; 5 | import { logoutUser } from "../../actions/authActions"; 6 | 7 | class Navbar extends Component { 8 | onLogout(e) { 9 | e.preventDefault(); 10 | this.props.logoutUser(); 11 | } 12 | 13 | render() { 14 | const { isAuthenticated, user } = this.props.auth; 15 | 16 | return ( 17 |
18 | 84 |
85 | ); 86 | } 87 | } 88 | 89 | Navbar.propTypes = { 90 | logoutUser: PropTypes.func.isRequired, 91 | auth: PropTypes.object.isRequired 92 | }; 93 | 94 | const mapStateToProps = state => ({ 95 | auth: state.auth 96 | }); 97 | 98 | export default connect( 99 | mapStateToProps, 100 | { logoutUser } 101 | )(Navbar); 102 | -------------------------------------------------------------------------------- /client/src/components/layouts/Sidebar.js: -------------------------------------------------------------------------------- 1 | import React, { Component } from "react"; 2 | import { Link } from "react-router-dom"; 3 | import PropTypes from "prop-types"; 4 | import { connect } from "react-redux"; 5 | 6 | class Sidebar extends Component { 7 | render() { 8 | const { user } = this.props.auth; 9 | 10 | return ( 11 |
12 | {/* moderator lvl */} 13 | {user.role === "moderator" || user.role === "admin" ? ( 14 |
15 | 19 | Dashboard 20 | 21 | 25 | Moderator Page 26 | 27 | {/* Admin lvl only*/} 28 | {user.role === "admin" ? ( 29 | 33 | Admin Page 34 | 35 | ) : null} 36 |
37 | ) : ( 38 |
39 | {/* user lvl */} 40 | 44 | Dashboard 45 | 46 |
47 | )} 48 |
49 | ); 50 | } 51 | } 52 | 53 | Sidebar.proptypes = { 54 | auth: PropTypes.object.isRequired 55 | }; 56 | 57 | const mapStateToProps = state => ({ 58 | auth: state.auth 59 | }); 60 | 61 | export default connect(mapStateToProps)(Sidebar); 62 | -------------------------------------------------------------------------------- /client/src/components/notfound/NotFound.js: -------------------------------------------------------------------------------- 1 | import React from "react"; 2 | import { Link } from "react-router-dom"; 3 | 4 | const NotFound = () => { 5 | return ( 6 |
7 |

Error 404

8 |

9 | The page you're looking for does not exist 10 |

11 | 12 | Back to Home 13 | 14 |
15 | ); 16 | }; 17 | 18 | export default NotFound; 19 | -------------------------------------------------------------------------------- /client/src/components/user/admin/Admin.js: -------------------------------------------------------------------------------- 1 | import React, { Component } from "react"; 2 | import Sidebar from "../../layouts/Sidebar"; 3 | 4 | class Admin extends Component { 5 | render() { 6 | return ( 7 |
8 |
9 |
10 | 11 |
12 |
13 |
14 |
Welcome to the admin page
15 |
16 | Only the Adminstrator can access this page 17 |
18 |
19 |
20 |
21 |
22 | ); 23 | } 24 | } 25 | 26 | export default Admin; 27 | -------------------------------------------------------------------------------- /client/src/components/user/dashboard/Dashboard.js: -------------------------------------------------------------------------------- 1 | import React, { Component } from "react"; 2 | import Sidebar from "../../layouts/Sidebar"; 3 | 4 | class Dashboard extends Component { 5 | render() { 6 | return ( 7 |
8 |
9 |
10 | 11 |
12 |
13 |
14 |
Welcome to the dashboard
15 |
16 | Every user that is authenticated can access the dashboard 17 |
18 |
19 |
20 |
21 |
22 | ); 23 | } 24 | } 25 | 26 | export default Dashboard; 27 | -------------------------------------------------------------------------------- /client/src/components/user/moderator/Moderator.js: -------------------------------------------------------------------------------- 1 | import React, { Component } from "react"; 2 | import Sidebar from "../../layouts/Sidebar"; 3 | 4 | class Moderator extends Component { 5 | render() { 6 | return ( 7 |
8 |
9 |
10 | 11 |
12 |
13 |
14 |
Welcome to the moderator page
15 |
16 | The administrator and moderator can access this page 17 |
18 |
19 |
20 |
21 |
22 | ); 23 | } 24 | } 25 | 26 | export default Moderator; 27 | -------------------------------------------------------------------------------- /client/src/index.js: -------------------------------------------------------------------------------- 1 | import React from "react"; 2 | import ReactDOM from "react-dom"; 3 | import App from "./App"; 4 | import registerServiceWorker from "./registerServiceWorker"; 5 | 6 | ReactDOM.render(, document.getElementById("root")); 7 | registerServiceWorker(); 8 | -------------------------------------------------------------------------------- /client/src/reducers/authReducers.js: -------------------------------------------------------------------------------- 1 | import { SET_CURRENT_USER, CLEAR_CURRENT_USER } from "../actions/types"; 2 | 3 | const initialState = { 4 | isAuthenticated: false, 5 | user: {} 6 | }; 7 | 8 | export default function(state = initialState, action) { 9 | switch (action.type) { 10 | case SET_CURRENT_USER: 11 | return { 12 | ...state, 13 | isAuthenticated: true, 14 | user: action.payload 15 | }; 16 | case CLEAR_CURRENT_USER: 17 | return { 18 | ...state, 19 | isAuthenticated: false, 20 | user: action.payload 21 | }; 22 | default: 23 | return state; 24 | } 25 | } 26 | -------------------------------------------------------------------------------- /client/src/reducers/errorReducers.js: -------------------------------------------------------------------------------- 1 | import { GET_ERRORS, CLEAR_ERRORS } from "../actions/types"; 2 | 3 | const initialState = {}; 4 | 5 | export default function(state = initialState, action) { 6 | switch (action.type) { 7 | case GET_ERRORS: 8 | return action.payload; 9 | case CLEAR_ERRORS: 10 | return {}; 11 | default: 12 | return state; 13 | } 14 | } 15 | -------------------------------------------------------------------------------- /client/src/reducers/index.js: -------------------------------------------------------------------------------- 1 | import { combineReducers } from "redux"; 2 | import authReducers from "./authReducers"; 3 | import errorReducers from "./errorReducers"; 4 | 5 | export default combineReducers({ 6 | auth: authReducers, 7 | errors: errorReducers 8 | }); 9 | -------------------------------------------------------------------------------- /client/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 | const isLocalhost = Boolean( 12 | window.location.hostname === 'localhost' || 13 | // [::1] is the IPv6 localhost address. 14 | window.location.hostname === '[::1]' || 15 | // 127.0.0.1/8 is considered localhost for IPv4. 16 | window.location.hostname.match( 17 | /^127(?:\.(?:25[0-5]|2[0-4][0-9]|[01]?[0-9][0-9]?)){3}$/ 18 | ) 19 | ); 20 | 21 | export default function register() { 22 | if (process.env.NODE_ENV === 'production' && 'serviceWorker' in navigator) { 23 | // The URL constructor is available in all browsers that support SW. 24 | const publicUrl = new URL(process.env.PUBLIC_URL, window.location); 25 | if (publicUrl.origin !== window.location.origin) { 26 | // Our service worker won't work if PUBLIC_URL is on a different origin 27 | // from what our page is served on. This might happen if a CDN is used to 28 | // serve assets; see https://github.com/facebookincubator/create-react-app/issues/2374 29 | return; 30 | } 31 | 32 | window.addEventListener('load', () => { 33 | const swUrl = `${process.env.PUBLIC_URL}/service-worker.js`; 34 | 35 | if (isLocalhost) { 36 | // This is running on localhost. Lets check if a service worker still exists or not. 37 | checkValidServiceWorker(swUrl); 38 | 39 | // Add some additional logging to localhost, pointing developers to the 40 | // service worker/PWA documentation. 41 | navigator.serviceWorker.ready.then(() => { 42 | console.log( 43 | 'This web app is being served cache-first by a service ' + 44 | 'worker. To learn more, visit https://goo.gl/SC7cgQ' 45 | ); 46 | }); 47 | } else { 48 | // Is not local host. Just register service worker 49 | registerValidSW(swUrl); 50 | } 51 | }); 52 | } 53 | } 54 | 55 | function registerValidSW(swUrl) { 56 | navigator.serviceWorker 57 | .register(swUrl) 58 | .then(registration => { 59 | registration.onupdatefound = () => { 60 | const installingWorker = registration.installing; 61 | installingWorker.onstatechange = () => { 62 | if (installingWorker.state === 'installed') { 63 | if (navigator.serviceWorker.controller) { 64 | // At this point, the old content will have been purged and 65 | // the fresh content will have been added to the cache. 66 | // It's the perfect time to display a "New content is 67 | // available; please refresh." message in your web app. 68 | console.log('New content is available; please refresh.'); 69 | } else { 70 | // At this point, everything has been precached. 71 | // It's the perfect time to display a 72 | // "Content is cached for offline use." message. 73 | console.log('Content is cached for offline use.'); 74 | } 75 | } 76 | }; 77 | }; 78 | }) 79 | .catch(error => { 80 | console.error('Error during service worker registration:', error); 81 | }); 82 | } 83 | 84 | function checkValidServiceWorker(swUrl) { 85 | // Check if the service worker can be found. If it can't reload the page. 86 | fetch(swUrl) 87 | .then(response => { 88 | // Ensure service worker exists, and that we really are getting a JS file. 89 | if ( 90 | response.status === 404 || 91 | response.headers.get('content-type').indexOf('javascript') === -1 92 | ) { 93 | // No service worker found. Probably a different app. Reload the page. 94 | navigator.serviceWorker.ready.then(registration => { 95 | registration.unregister().then(() => { 96 | window.location.reload(); 97 | }); 98 | }); 99 | } else { 100 | // Service worker found. Proceed as normal. 101 | registerValidSW(swUrl); 102 | } 103 | }) 104 | .catch(() => { 105 | console.log( 106 | 'No internet connection found. App is running in offline mode.' 107 | ); 108 | }); 109 | } 110 | 111 | export function unregister() { 112 | if ('serviceWorker' in navigator) { 113 | navigator.serviceWorker.ready.then(registration => { 114 | registration.unregister(); 115 | }); 116 | } 117 | } 118 | -------------------------------------------------------------------------------- /client/src/store.js: -------------------------------------------------------------------------------- 1 | import { createStore, applyMiddleware, compose } from "redux"; 2 | import thunk from "redux-thunk"; 3 | import rootReducer from "./reducers"; 4 | 5 | const initialState = {}; 6 | 7 | const middleware = [thunk]; 8 | 9 | const store = createStore( 10 | rootReducer, 11 | initialState, 12 | compose( 13 | applyMiddleware(...middleware), 14 | window.navigator.userAgent.includes("Chrome") 15 | ? window.__REDUX_DEVTOOLS_EXTENSION__ && 16 | window.__REDUX_DEVTOOLS_EXTENSION__() 17 | : compose 18 | ) 19 | ); 20 | 21 | export default store; 22 | -------------------------------------------------------------------------------- /client/src/utils/setAuthToken.js: -------------------------------------------------------------------------------- 1 | import axios from "axios"; 2 | 3 | const setAuthToken = token => { 4 | if (token) { 5 | // apply to every request 6 | axios.defaults.headers.common["Authorization"] = token; 7 | } else { 8 | // delete auth header 9 | delete axios.defaults.headers.common["Authorization"]; 10 | } 11 | }; 12 | 13 | export default setAuthToken; 14 | -------------------------------------------------------------------------------- /config/dbSecretKeys.js: -------------------------------------------------------------------------------- 1 | if (process.env.NODE_ENV === "production") { 2 | module.exports = require("./dbSecretKeysProd"); 3 | } else { 4 | module.exports = require("./dbSecretKeysDev"); 5 | } 6 | -------------------------------------------------------------------------------- /config/dbSecretKeysProd.js: -------------------------------------------------------------------------------- 1 | module.exports = { 2 | mongoURI: process.env.MONGO_URI, 3 | secretOrKey: process.env.SECRET_OR_KEY 4 | }; 5 | -------------------------------------------------------------------------------- /config/passport.js: -------------------------------------------------------------------------------- 1 | const JwtStrategy = require("passport-jwt").Strategy; 2 | const ExtractJwt = require("passport-jwt").ExtractJwt; 3 | const mongoose = require("mongoose"); 4 | const User = mongoose.model("users"); 5 | const keys = require("./dbSecretKeys"); 6 | 7 | let opts = {}; 8 | // opts.jwtFromRequest = ExtractJwt.fromAuthHeaderAsBearerToken(); 9 | opts.jwtFromRequest = ExtractJwt.fromAuthHeaderWithScheme("jwt"); 10 | opts.secretOrKey = keys.secretOrKey; 11 | 12 | module.exports = passport => { 13 | passport.use( 14 | new JwtStrategy(opts, (jwt_payload, done) => { 15 | User.findById(jwt_payload.id) 16 | .then(user => { 17 | if (user) { 18 | return done(null, user); 19 | } 20 | return done(null, false); 21 | }) 22 | .catch(err => console.log(err)); 23 | }) 24 | ); 25 | }; 26 | -------------------------------------------------------------------------------- /guards/isAdmin.js: -------------------------------------------------------------------------------- 1 | module.exports = function(req, res, next) { 2 | if (req.user.role != "admin") return res.status(403).send("Access Denied"); 3 | next(); 4 | }; 5 | -------------------------------------------------------------------------------- /guards/isModerator.js: -------------------------------------------------------------------------------- 1 | module.exports = function(req, res, next) { 2 | if (!(req.user.role == "moderator" || req.user.role == "admin")) 3 | return res.status(403).send("Access Denied"); 4 | next(); 5 | }; 6 | -------------------------------------------------------------------------------- /models/User.js: -------------------------------------------------------------------------------- 1 | const mongoose = require("mongoose"); 2 | const { Schema } = mongoose; 3 | 4 | // Create Schema 5 | const userSchema = new Schema({ 6 | name: { 7 | type: String 8 | }, 9 | email: { 10 | type: String, 11 | required: true 12 | }, 13 | role: { 14 | type: String, 15 | default: "subscriber" 16 | // roles available to this proj: admin, moderator, subscriber 17 | }, 18 | password: { 19 | type: String 20 | } 21 | }); 22 | 23 | // module.exports = User = mongoose.model("users", UserSchema); 24 | mongoose.model("users", userSchema); 25 | -------------------------------------------------------------------------------- /package-lock.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "mern", 3 | "version": "1.0.0", 4 | "lockfileVersion": 1, 5 | "requires": true, 6 | "dependencies": { 7 | "accepts": { 8 | "version": "1.3.5", 9 | "resolved": "https://registry.npmjs.org/accepts/-/accepts-1.3.5.tgz", 10 | "integrity": "sha1-63d99gEXI6OxTopywIBcjoZ0a9I=", 11 | "requires": { 12 | "mime-types": "~2.1.18", 13 | "negotiator": "0.6.1" 14 | } 15 | }, 16 | "acorn": { 17 | "version": "5.7.1", 18 | "resolved": "https://registry.npmjs.org/acorn/-/acorn-5.7.1.tgz", 19 | "integrity": "sha512-d+nbxBUGKg7Arpsvbnlq61mc12ek3EY8EQldM3GPAhWJ1UVxC6TDGbIvUMNU6obBX3i1+ptCIzV4vq0gFPEGVQ==" 20 | }, 21 | "acorn-jsx": { 22 | "version": "3.0.1", 23 | "resolved": "https://registry.npmjs.org/acorn-jsx/-/acorn-jsx-3.0.1.tgz", 24 | "integrity": "sha1-r9+UiPsezvyDSPb7IvRk4ypYs2s=", 25 | "requires": { 26 | "acorn": "^3.0.4" 27 | }, 28 | "dependencies": { 29 | "acorn": { 30 | "version": "3.3.0", 31 | "resolved": "https://registry.npmjs.org/acorn/-/acorn-3.3.0.tgz", 32 | "integrity": "sha1-ReN/s56No/JbruP/U2niu18iAXo=" 33 | } 34 | } 35 | }, 36 | "ajv": { 37 | "version": "5.5.2", 38 | "resolved": "https://registry.npmjs.org/ajv/-/ajv-5.5.2.tgz", 39 | "integrity": "sha1-c7Xuyj+rZT49P5Qis0GtQiBdyWU=", 40 | "requires": { 41 | "co": "^4.6.0", 42 | "fast-deep-equal": "^1.0.0", 43 | "fast-json-stable-stringify": "^2.0.0", 44 | "json-schema-traverse": "^0.3.0" 45 | } 46 | }, 47 | "ajv-keywords": { 48 | "version": "3.2.0", 49 | "resolved": "https://registry.npmjs.org/ajv-keywords/-/ajv-keywords-3.2.0.tgz", 50 | "integrity": "sha1-6GuBnGAs+IIa1jdBNpjx3sAhhHo=" 51 | }, 52 | "ansi-escapes": { 53 | "version": "3.1.0", 54 | "resolved": "https://registry.npmjs.org/ansi-escapes/-/ansi-escapes-3.1.0.tgz", 55 | "integrity": "sha512-UgAb8H9D41AQnu/PbWlCofQVcnV4Gs2bBJi9eZPxfU/hgglFh3SMDMENRIqdr7H6XFnXdoknctFByVsCOotTVw==" 56 | }, 57 | "ansi-regex": { 58 | "version": "2.1.1", 59 | "resolved": "https://registry.npmjs.org/ansi-regex/-/ansi-regex-2.1.1.tgz", 60 | "integrity": "sha1-w7M6te42DYbg5ijwRorn7yfWVN8=" 61 | }, 62 | "ansi-styles": { 63 | "version": "2.2.1", 64 | "resolved": "https://registry.npmjs.org/ansi-styles/-/ansi-styles-2.2.1.tgz", 65 | "integrity": "sha1-tDLdM1i2NM914eRmQ2gkBTPB3b4=" 66 | }, 67 | "argparse": { 68 | "version": "1.0.10", 69 | "resolved": "https://registry.npmjs.org/argparse/-/argparse-1.0.10.tgz", 70 | "integrity": "sha512-o5Roy6tNG4SL/FOkCAN6RzjiakZS25RLYFrcMttJqbdd8BWrnA+fGz57iN5Pb06pvBGvl5gQ0B48dJlslXvoTg==", 71 | "requires": { 72 | "sprintf-js": "~1.0.2" 73 | } 74 | }, 75 | "array-flatten": { 76 | "version": "1.1.1", 77 | "resolved": "https://registry.npmjs.org/array-flatten/-/array-flatten-1.1.1.tgz", 78 | "integrity": "sha1-ml9pkFGx5wczKPKgCJaLZOopVdI=" 79 | }, 80 | "array-union": { 81 | "version": "1.0.2", 82 | "resolved": "https://registry.npmjs.org/array-union/-/array-union-1.0.2.tgz", 83 | "integrity": "sha1-mjRBDk9OPaI96jdb5b5w8kd47Dk=", 84 | "requires": { 85 | "array-uniq": "^1.0.1" 86 | } 87 | }, 88 | "array-uniq": { 89 | "version": "1.0.3", 90 | "resolved": "https://registry.npmjs.org/array-uniq/-/array-uniq-1.0.3.tgz", 91 | "integrity": "sha1-r2rId6Jcx/dOBYiUdThY39sk/bY=" 92 | }, 93 | "arrify": { 94 | "version": "1.0.1", 95 | "resolved": "https://registry.npmjs.org/arrify/-/arrify-1.0.1.tgz", 96 | "integrity": "sha1-iYUI2iIm84DfkEcoRWhJwVAaSw0=" 97 | }, 98 | "async": { 99 | "version": "2.6.1", 100 | "resolved": "https://registry.npmjs.org/async/-/async-2.6.1.tgz", 101 | "integrity": "sha512-fNEiL2+AZt6AlAw/29Cr0UDe4sRAHCpEHh54WMz+Bb7QfNcFw4h3loofyJpLeQs4Yx7yuqu/2dLgM5hKOs6HlQ==", 102 | "requires": { 103 | "lodash": "^4.17.10" 104 | } 105 | }, 106 | "babel-code-frame": { 107 | "version": "6.26.0", 108 | "resolved": "https://registry.npmjs.org/babel-code-frame/-/babel-code-frame-6.26.0.tgz", 109 | "integrity": "sha1-Y/1D99weO7fONZR9uP42mj9Yx0s=", 110 | "requires": { 111 | "chalk": "^1.1.3", 112 | "esutils": "^2.0.2", 113 | "js-tokens": "^3.0.2" 114 | }, 115 | "dependencies": { 116 | "chalk": { 117 | "version": "1.1.3", 118 | "resolved": "https://registry.npmjs.org/chalk/-/chalk-1.1.3.tgz", 119 | "integrity": "sha1-qBFcVeSnAv5NFQq9OHKCKn4J/Jg=", 120 | "requires": { 121 | "ansi-styles": "^2.2.1", 122 | "escape-string-regexp": "^1.0.2", 123 | "has-ansi": "^2.0.0", 124 | "strip-ansi": "^3.0.0", 125 | "supports-color": "^2.0.0" 126 | } 127 | }, 128 | "strip-ansi": { 129 | "version": "3.0.1", 130 | "resolved": "https://registry.npmjs.org/strip-ansi/-/strip-ansi-3.0.1.tgz", 131 | "integrity": "sha1-ajhfuIU9lS1f8F0Oiq+UJ43GPc8=", 132 | "requires": { 133 | "ansi-regex": "^2.0.0" 134 | } 135 | } 136 | } 137 | }, 138 | "balanced-match": { 139 | "version": "1.0.0", 140 | "resolved": "https://registry.npmjs.org/balanced-match/-/balanced-match-1.0.0.tgz", 141 | "integrity": "sha1-ibTRmasr7kneFk6gK4nORi1xt2c=" 142 | }, 143 | "bcryptjs": { 144 | "version": "2.4.3", 145 | "resolved": "https://registry.npmjs.org/bcryptjs/-/bcryptjs-2.4.3.tgz", 146 | "integrity": "sha1-mrVie5PmBiH/fNrF2pczAn3x0Ms=" 147 | }, 148 | "bluebird": { 149 | "version": "3.5.1", 150 | "resolved": "https://registry.npmjs.org/bluebird/-/bluebird-3.5.1.tgz", 151 | "integrity": "sha512-MKiLiV+I1AA596t9w1sQJ8jkiSr5+ZKi0WKrYGUn6d1Fx+Ij4tIj+m2WMQSGczs5jZVxV339chE8iwk6F64wjA==" 152 | }, 153 | "body-parser": { 154 | "version": "1.18.3", 155 | "resolved": "https://registry.npmjs.org/body-parser/-/body-parser-1.18.3.tgz", 156 | "integrity": "sha1-WykhmP/dVTs6DyDe0FkrlWlVyLQ=", 157 | "requires": { 158 | "bytes": "3.0.0", 159 | "content-type": "~1.0.4", 160 | "debug": "2.6.9", 161 | "depd": "~1.1.2", 162 | "http-errors": "~1.6.3", 163 | "iconv-lite": "0.4.23", 164 | "on-finished": "~2.3.0", 165 | "qs": "6.5.2", 166 | "raw-body": "2.3.3", 167 | "type-is": "~1.6.16" 168 | } 169 | }, 170 | "brace-expansion": { 171 | "version": "1.1.11", 172 | "resolved": "https://registry.npmjs.org/brace-expansion/-/brace-expansion-1.1.11.tgz", 173 | "integrity": "sha512-iCuPHDFgrHX7H2vEI/5xpz07zSHB00TpugqhmYtVmMO6518mCuRMoOYFldEBl0g187ufozdaHgWKcYFb61qGiA==", 174 | "requires": { 175 | "balanced-match": "^1.0.0", 176 | "concat-map": "0.0.1" 177 | } 178 | }, 179 | "bson": { 180 | "version": "1.0.9", 181 | "resolved": "https://registry.npmjs.org/bson/-/bson-1.0.9.tgz", 182 | "integrity": "sha512-IQX9/h7WdMBIW/q/++tGd+emQr0XMdeZ6icnT/74Xk9fnabWn+gZgpE+9V+gujL3hhJOoNrnDVY7tWdzc7NUTg==" 183 | }, 184 | "buffer-equal-constant-time": { 185 | "version": "1.0.1", 186 | "resolved": "https://registry.npmjs.org/buffer-equal-constant-time/-/buffer-equal-constant-time-1.0.1.tgz", 187 | "integrity": "sha1-+OcRMvf/5uAaXJaXpMbz5I1cyBk=" 188 | }, 189 | "buffer-from": { 190 | "version": "1.1.1", 191 | "resolved": "https://registry.npmjs.org/buffer-from/-/buffer-from-1.1.1.tgz", 192 | "integrity": "sha512-MQcXEUbCKtEo7bhqEs6560Hyd4XaovZlO/k9V3hjVUF/zwW7KBVdSK4gIt/bzwS9MbR5qob+F5jusZsb0YQK2A==" 193 | }, 194 | "builtin-modules": { 195 | "version": "1.1.1", 196 | "resolved": "https://registry.npmjs.org/builtin-modules/-/builtin-modules-1.1.1.tgz", 197 | "integrity": "sha1-Jw8HbFpywC9bZaR9+Uxf46J4iS8=" 198 | }, 199 | "bytes": { 200 | "version": "3.0.0", 201 | "resolved": "https://registry.npmjs.org/bytes/-/bytes-3.0.0.tgz", 202 | "integrity": "sha1-0ygVQE1olpn4Wk6k+odV3ROpYEg=" 203 | }, 204 | "caller-path": { 205 | "version": "0.1.0", 206 | "resolved": "https://registry.npmjs.org/caller-path/-/caller-path-0.1.0.tgz", 207 | "integrity": "sha1-lAhe9jWB7NPaqSREqP6U6CV3dR8=", 208 | "requires": { 209 | "callsites": "^0.2.0" 210 | } 211 | }, 212 | "callsites": { 213 | "version": "0.2.0", 214 | "resolved": "https://registry.npmjs.org/callsites/-/callsites-0.2.0.tgz", 215 | "integrity": "sha1-r6uWJikQp/M8GaV3WCXGnzTjUMo=" 216 | }, 217 | "chalk": { 218 | "version": "2.4.1", 219 | "resolved": "https://registry.npmjs.org/chalk/-/chalk-2.4.1.tgz", 220 | "integrity": "sha512-ObN6h1v2fTJSmUXoS3nMQ92LbDK9be4TV+6G+omQlGJFdcUX5heKi1LZ1YnRMIgwTLEj3E24bT6tYni50rlCfQ==", 221 | "requires": { 222 | "ansi-styles": "^3.2.1", 223 | "escape-string-regexp": "^1.0.5", 224 | "supports-color": "^5.3.0" 225 | }, 226 | "dependencies": { 227 | "ansi-styles": { 228 | "version": "3.2.1", 229 | "resolved": "https://registry.npmjs.org/ansi-styles/-/ansi-styles-3.2.1.tgz", 230 | "integrity": "sha512-VT0ZI6kZRdTh8YyJw3SMbYm/u+NqfsAxEpWO0Pf9sq8/e94WxxOpPKx9FR1FlyCtOVDNOQ+8ntlqFxiRc+r5qA==", 231 | "requires": { 232 | "color-convert": "^1.9.0" 233 | } 234 | }, 235 | "supports-color": { 236 | "version": "5.4.0", 237 | "resolved": "https://registry.npmjs.org/supports-color/-/supports-color-5.4.0.tgz", 238 | "integrity": "sha512-zjaXglF5nnWpsq470jSv6P9DwPvgLkuapYmfDm3JWOm0vkNTVF2tI4UrN2r6jH1qM/uc/WtxYY1hYoA2dOKj5w==", 239 | "requires": { 240 | "has-flag": "^3.0.0" 241 | } 242 | } 243 | } 244 | }, 245 | "chardet": { 246 | "version": "0.4.2", 247 | "resolved": "https://registry.npmjs.org/chardet/-/chardet-0.4.2.tgz", 248 | "integrity": "sha1-tUc7M9yXxCTl2Y3IfVXU2KKci/I=" 249 | }, 250 | "circular-json": { 251 | "version": "0.3.3", 252 | "resolved": "https://registry.npmjs.org/circular-json/-/circular-json-0.3.3.tgz", 253 | "integrity": "sha512-UZK3NBx2Mca+b5LsG7bY183pHWt5Y1xts4P3Pz7ENTwGVnJOUWbRb3ocjvX7hx9tq/yTAdclXm9sZ38gNuem4A==" 254 | }, 255 | "cli-cursor": { 256 | "version": "2.1.0", 257 | "resolved": "https://registry.npmjs.org/cli-cursor/-/cli-cursor-2.1.0.tgz", 258 | "integrity": "sha1-s12sN2R5+sw+lHR9QdDQ9SOP/LU=", 259 | "requires": { 260 | "restore-cursor": "^2.0.0" 261 | } 262 | }, 263 | "cli-width": { 264 | "version": "2.2.0", 265 | "resolved": "https://registry.npmjs.org/cli-width/-/cli-width-2.2.0.tgz", 266 | "integrity": "sha1-/xnt6Kml5XkyQUewwR8PvLq+1jk=" 267 | }, 268 | "co": { 269 | "version": "4.6.0", 270 | "resolved": "https://registry.npmjs.org/co/-/co-4.6.0.tgz", 271 | "integrity": "sha1-bqa989hTrlTMuOR7+gvz+QMfsYQ=" 272 | }, 273 | "color-convert": { 274 | "version": "1.9.2", 275 | "resolved": "https://registry.npmjs.org/color-convert/-/color-convert-1.9.2.tgz", 276 | "integrity": "sha512-3NUJZdhMhcdPn8vJ9v2UQJoH0qqoGUkYTgFEPZaPjEtwmmKUfNV46zZmgB2M5M4DCEQHMaCfWHCxiBflLm04Tg==", 277 | "requires": { 278 | "color-name": "1.1.1" 279 | } 280 | }, 281 | "color-name": { 282 | "version": "1.1.1", 283 | "resolved": "https://registry.npmjs.org/color-name/-/color-name-1.1.1.tgz", 284 | "integrity": "sha1-SxQVMEz1ACjqgWQ2Q72C6gWANok=" 285 | }, 286 | "commander": { 287 | "version": "2.6.0", 288 | "resolved": "https://registry.npmjs.org/commander/-/commander-2.6.0.tgz", 289 | "integrity": "sha1-nfflL7Kgyw+4kFjugMMQQiXzfh0=" 290 | }, 291 | "concat-map": { 292 | "version": "0.0.1", 293 | "resolved": "https://registry.npmjs.org/concat-map/-/concat-map-0.0.1.tgz", 294 | "integrity": "sha1-2Klr13/Wjfd5OnMDajug1UBdR3s=" 295 | }, 296 | "concat-stream": { 297 | "version": "1.6.2", 298 | "resolved": "https://registry.npmjs.org/concat-stream/-/concat-stream-1.6.2.tgz", 299 | "integrity": "sha512-27HBghJxjiZtIk3Ycvn/4kbJk/1uZuJFfuPEns6LaEvpvG1f0hTea8lilrouyo9mVc2GWdcEZ8OLoGmSADlrCw==", 300 | "requires": { 301 | "buffer-from": "^1.0.0", 302 | "inherits": "^2.0.3", 303 | "readable-stream": "^2.2.2", 304 | "typedarray": "^0.0.6" 305 | } 306 | }, 307 | "concurrently": { 308 | "version": "3.6.1", 309 | "resolved": "https://registry.npmjs.org/concurrently/-/concurrently-3.6.1.tgz", 310 | "integrity": "sha512-/+ugz+gwFSEfTGUxn0KHkY+19XPRTXR8+7oUK/HxgiN1n7FjeJmkrbSiXAJfyQ0zORgJYPaenmymwon51YXH9Q==", 311 | "requires": { 312 | "chalk": "^2.4.1", 313 | "commander": "2.6.0", 314 | "date-fns": "^1.23.0", 315 | "lodash": "^4.5.1", 316 | "read-pkg": "^3.0.0", 317 | "rx": "2.3.24", 318 | "spawn-command": "^0.0.2-1", 319 | "supports-color": "^3.2.3", 320 | "tree-kill": "^1.1.0" 321 | }, 322 | "dependencies": { 323 | "has-flag": { 324 | "version": "1.0.0", 325 | "resolved": "https://registry.npmjs.org/has-flag/-/has-flag-1.0.0.tgz", 326 | "integrity": "sha1-nZ55MWXOAXoA8AQYxD+UKnsdEfo=" 327 | }, 328 | "supports-color": { 329 | "version": "3.2.3", 330 | "resolved": "https://registry.npmjs.org/supports-color/-/supports-color-3.2.3.tgz", 331 | "integrity": "sha1-ZawFBLOVQXHYpklGsq48u4pfVPY=", 332 | "requires": { 333 | "has-flag": "^1.0.0" 334 | } 335 | } 336 | } 337 | }, 338 | "content-disposition": { 339 | "version": "0.5.2", 340 | "resolved": "https://registry.npmjs.org/content-disposition/-/content-disposition-0.5.2.tgz", 341 | "integrity": "sha1-DPaLud318r55YcOoUXjLhdunjLQ=" 342 | }, 343 | "content-type": { 344 | "version": "1.0.4", 345 | "resolved": "https://registry.npmjs.org/content-type/-/content-type-1.0.4.tgz", 346 | "integrity": "sha512-hIP3EEPs8tB9AT1L+NUqtwOAps4mk2Zob89MWXMHjHWg9milF/j4osnnQLXBCBFBk/tvIG/tUc9mOUJiPBhPXA==" 347 | }, 348 | "cookie": { 349 | "version": "0.3.1", 350 | "resolved": "https://registry.npmjs.org/cookie/-/cookie-0.3.1.tgz", 351 | "integrity": "sha1-5+Ch+e9DtMi6klxcWpboBtFoc7s=" 352 | }, 353 | "cookie-signature": { 354 | "version": "1.0.6", 355 | "resolved": "https://registry.npmjs.org/cookie-signature/-/cookie-signature-1.0.6.tgz", 356 | "integrity": "sha1-4wOogrNCzD7oylE6eZmXNNqzriw=" 357 | }, 358 | "core-util-is": { 359 | "version": "1.0.2", 360 | "resolved": "https://registry.npmjs.org/core-util-is/-/core-util-is-1.0.2.tgz", 361 | "integrity": "sha1-tf1UIgqivFq1eqtxQMlAdUUDwac=" 362 | }, 363 | "cors": { 364 | "version": "2.8.4", 365 | "resolved": "https://registry.npmjs.org/cors/-/cors-2.8.4.tgz", 366 | "integrity": "sha1-K9OB8usgECAQXNUOpZ2mMJBpRoY=", 367 | "requires": { 368 | "object-assign": "^4", 369 | "vary": "^1" 370 | } 371 | }, 372 | "cross-spawn": { 373 | "version": "5.1.0", 374 | "resolved": "https://registry.npmjs.org/cross-spawn/-/cross-spawn-5.1.0.tgz", 375 | "integrity": "sha1-6L0O/uWPz/b4+UUQoKVUu/ojVEk=", 376 | "requires": { 377 | "lru-cache": "^4.0.1", 378 | "shebang-command": "^1.2.0", 379 | "which": "^1.2.9" 380 | } 381 | }, 382 | "date-fns": { 383 | "version": "1.29.0", 384 | "resolved": "https://registry.npmjs.org/date-fns/-/date-fns-1.29.0.tgz", 385 | "integrity": "sha512-lbTXWZ6M20cWH8N9S6afb0SBm6tMk+uUg6z3MqHPKE9atmsY3kJkTm8vKe93izJ2B2+q5MV990sM2CHgtAZaOw==" 386 | }, 387 | "debug": { 388 | "version": "2.6.9", 389 | "resolved": "https://registry.npmjs.org/debug/-/debug-2.6.9.tgz", 390 | "integrity": "sha512-bC7ElrdJaJnPbAP+1EotYvqZsb3ecl5wi6Bfi6BJTUcNowp6cvspg0jXznRTKDjm/E7AdgFBVeAPVMNcKGsHMA==", 391 | "requires": { 392 | "ms": "2.0.0" 393 | } 394 | }, 395 | "deep-is": { 396 | "version": "0.1.3", 397 | "resolved": "https://registry.npmjs.org/deep-is/-/deep-is-0.1.3.tgz", 398 | "integrity": "sha1-s2nW+128E+7PUk+RsHD+7cNXzzQ=" 399 | }, 400 | "del": { 401 | "version": "2.2.2", 402 | "resolved": "https://registry.npmjs.org/del/-/del-2.2.2.tgz", 403 | "integrity": "sha1-wSyYHQZ4RshLyvhiz/kw2Qf/0ag=", 404 | "requires": { 405 | "globby": "^5.0.0", 406 | "is-path-cwd": "^1.0.0", 407 | "is-path-in-cwd": "^1.0.0", 408 | "object-assign": "^4.0.1", 409 | "pify": "^2.0.0", 410 | "pinkie-promise": "^2.0.0", 411 | "rimraf": "^2.2.8" 412 | } 413 | }, 414 | "depd": { 415 | "version": "1.1.2", 416 | "resolved": "https://registry.npmjs.org/depd/-/depd-1.1.2.tgz", 417 | "integrity": "sha1-m81S4UwJd2PnSbJ0xDRu0uVgtak=" 418 | }, 419 | "destroy": { 420 | "version": "1.0.4", 421 | "resolved": "https://registry.npmjs.org/destroy/-/destroy-1.0.4.tgz", 422 | "integrity": "sha1-l4hXRCxEdJ5CBmE+N5RiBYJqvYA=" 423 | }, 424 | "doctrine": { 425 | "version": "2.1.0", 426 | "resolved": "https://registry.npmjs.org/doctrine/-/doctrine-2.1.0.tgz", 427 | "integrity": "sha512-35mSku4ZXK0vfCuHEDAwt55dg2jNajHZ1odvF+8SSr82EsZY4QmXfuWso8oEd8zRhVObSN18aM0CjSdoBX7zIw==", 428 | "requires": { 429 | "esutils": "^2.0.2" 430 | } 431 | }, 432 | "ecdsa-sig-formatter": { 433 | "version": "1.0.10", 434 | "resolved": "https://registry.npmjs.org/ecdsa-sig-formatter/-/ecdsa-sig-formatter-1.0.10.tgz", 435 | "integrity": "sha1-HFlQAPBKiJffuFAAiSoPTDOvhsM=", 436 | "requires": { 437 | "safe-buffer": "^5.0.1" 438 | } 439 | }, 440 | "ee-first": { 441 | "version": "1.1.1", 442 | "resolved": "https://registry.npmjs.org/ee-first/-/ee-first-1.1.1.tgz", 443 | "integrity": "sha1-WQxhFWsK4vTwJVcyoViyZrxWsh0=" 444 | }, 445 | "encodeurl": { 446 | "version": "1.0.2", 447 | "resolved": "https://registry.npmjs.org/encodeurl/-/encodeurl-1.0.2.tgz", 448 | "integrity": "sha1-rT/0yG7C0CkyL1oCw6mmBslbP1k=" 449 | }, 450 | "error-ex": { 451 | "version": "1.3.2", 452 | "resolved": "https://registry.npmjs.org/error-ex/-/error-ex-1.3.2.tgz", 453 | "integrity": "sha512-7dFHNmqeFSEt2ZBsCriorKnn3Z2pj+fd9kmI6QoWw4//DL+icEBfc0U7qJCisqrTsKTjw4fNFy2pW9OqStD84g==", 454 | "requires": { 455 | "is-arrayish": "^0.2.1" 456 | } 457 | }, 458 | "escape-html": { 459 | "version": "1.0.3", 460 | "resolved": "https://registry.npmjs.org/escape-html/-/escape-html-1.0.3.tgz", 461 | "integrity": "sha1-Aljq5NPQwJdN4cFpGI7wBR0dGYg=" 462 | }, 463 | "escape-string-regexp": { 464 | "version": "1.0.5", 465 | "resolved": "https://registry.npmjs.org/escape-string-regexp/-/escape-string-regexp-1.0.5.tgz", 466 | "integrity": "sha1-G2HAViGQqN/2rjuyzwIAyhMLhtQ=" 467 | }, 468 | "eslint": { 469 | "version": "4.14.0", 470 | "resolved": "https://registry.npmjs.org/eslint/-/eslint-4.14.0.tgz", 471 | "integrity": "sha512-Ul6CSGRjKscEyg0X/EeNs7o2XdnbTEOD1OM8cTjmx85RPcBJQrEhZLevhuJZNAE/vS2iVl5Uhgiqf3h5uLMCJQ==", 472 | "requires": { 473 | "ajv": "^5.3.0", 474 | "babel-code-frame": "^6.22.0", 475 | "chalk": "^2.1.0", 476 | "concat-stream": "^1.6.0", 477 | "cross-spawn": "^5.1.0", 478 | "debug": "^3.1.0", 479 | "doctrine": "^2.0.2", 480 | "eslint-scope": "^3.7.1", 481 | "eslint-visitor-keys": "^1.0.0", 482 | "espree": "^3.5.2", 483 | "esquery": "^1.0.0", 484 | "esutils": "^2.0.2", 485 | "file-entry-cache": "^2.0.0", 486 | "functional-red-black-tree": "^1.0.1", 487 | "glob": "^7.1.2", 488 | "globals": "^11.0.1", 489 | "ignore": "^3.3.3", 490 | "imurmurhash": "^0.1.4", 491 | "inquirer": "^3.0.6", 492 | "is-resolvable": "^1.0.0", 493 | "js-yaml": "^3.9.1", 494 | "json-stable-stringify-without-jsonify": "^1.0.1", 495 | "levn": "^0.3.0", 496 | "lodash": "^4.17.4", 497 | "minimatch": "^3.0.2", 498 | "mkdirp": "^0.5.1", 499 | "natural-compare": "^1.4.0", 500 | "optionator": "^0.8.2", 501 | "path-is-inside": "^1.0.2", 502 | "pluralize": "^7.0.0", 503 | "progress": "^2.0.0", 504 | "require-uncached": "^1.0.3", 505 | "semver": "^5.3.0", 506 | "strip-ansi": "^4.0.0", 507 | "strip-json-comments": "~2.0.1", 508 | "table": "^4.0.1", 509 | "text-table": "~0.2.0" 510 | }, 511 | "dependencies": { 512 | "debug": { 513 | "version": "3.1.0", 514 | "resolved": "https://registry.npmjs.org/debug/-/debug-3.1.0.tgz", 515 | "integrity": "sha512-OX8XqP7/1a9cqkxYw2yXss15f26NKWBpDXQd0/uK/KPqdQhxbPa994hnzjcE2VqQpDslf55723cKPUOGSmMY3g==", 516 | "requires": { 517 | "ms": "2.0.0" 518 | } 519 | } 520 | } 521 | }, 522 | "eslint-scope": { 523 | "version": "3.7.3", 524 | "resolved": "https://registry.npmjs.org/eslint-scope/-/eslint-scope-3.7.3.tgz", 525 | "integrity": "sha512-W+B0SvF4gamyCTmUc+uITPY0989iXVfKvhwtmJocTaYoc/3khEHmEmvfY/Gn9HA9VV75jrQECsHizkNw1b68FA==", 526 | "requires": { 527 | "esrecurse": "^4.1.0", 528 | "estraverse": "^4.1.1" 529 | } 530 | }, 531 | "eslint-visitor-keys": { 532 | "version": "1.0.0", 533 | "resolved": "https://registry.npmjs.org/eslint-visitor-keys/-/eslint-visitor-keys-1.0.0.tgz", 534 | "integrity": "sha512-qzm/XxIbxm/FHyH341ZrbnMUpe+5Bocte9xkmFMzPMjRaZMcXww+MpBptFvtU+79L362nqiLhekCxCxDPaUMBQ==" 535 | }, 536 | "espree": { 537 | "version": "3.5.4", 538 | "resolved": "https://registry.npmjs.org/espree/-/espree-3.5.4.tgz", 539 | "integrity": "sha512-yAcIQxtmMiB/jL32dzEp2enBeidsB7xWPLNiw3IIkpVds1P+h7qF9YwJq1yUNzp2OKXgAprs4F61ih66UsoD1A==", 540 | "requires": { 541 | "acorn": "^5.5.0", 542 | "acorn-jsx": "^3.0.0" 543 | } 544 | }, 545 | "esprima": { 546 | "version": "4.0.1", 547 | "resolved": "https://registry.npmjs.org/esprima/-/esprima-4.0.1.tgz", 548 | "integrity": "sha512-eGuFFw7Upda+g4p+QHvnW0RyTX/SVeJBDM/gCtMARO0cLuT2HcEKnTPvhjV6aGeqrCB/sbNop0Kszm0jsaWU4A==" 549 | }, 550 | "esquery": { 551 | "version": "1.0.1", 552 | "resolved": "https://registry.npmjs.org/esquery/-/esquery-1.0.1.tgz", 553 | "integrity": "sha512-SmiyZ5zIWH9VM+SRUReLS5Q8a7GxtRdxEBVZpm98rJM7Sb+A9DVCndXfkeFUd3byderg+EbDkfnevfCwynWaNA==", 554 | "requires": { 555 | "estraverse": "^4.0.0" 556 | } 557 | }, 558 | "esrecurse": { 559 | "version": "4.2.1", 560 | "resolved": "https://registry.npmjs.org/esrecurse/-/esrecurse-4.2.1.tgz", 561 | "integrity": "sha512-64RBB++fIOAXPw3P9cy89qfMlvZEXZkqqJkjqqXIvzP5ezRZjW+lPWjw35UX/3EhUPFYbg5ER4JYgDw4007/DQ==", 562 | "requires": { 563 | "estraverse": "^4.1.0" 564 | } 565 | }, 566 | "estraverse": { 567 | "version": "4.2.0", 568 | "resolved": "https://registry.npmjs.org/estraverse/-/estraverse-4.2.0.tgz", 569 | "integrity": "sha1-De4/7TH81GlhjOc0IJn8GvoL2xM=" 570 | }, 571 | "esutils": { 572 | "version": "2.0.2", 573 | "resolved": "https://registry.npmjs.org/esutils/-/esutils-2.0.2.tgz", 574 | "integrity": "sha1-Cr9PHKpbyx96nYrMbepPqqBLrJs=" 575 | }, 576 | "etag": { 577 | "version": "1.8.1", 578 | "resolved": "https://registry.npmjs.org/etag/-/etag-1.8.1.tgz", 579 | "integrity": "sha1-Qa4u62XvpiJorr/qg6x9eSmbCIc=" 580 | }, 581 | "express": { 582 | "version": "4.16.3", 583 | "resolved": "https://registry.npmjs.org/express/-/express-4.16.3.tgz", 584 | "integrity": "sha1-avilAjUNsyRuzEvs9rWjTSL37VM=", 585 | "requires": { 586 | "accepts": "~1.3.5", 587 | "array-flatten": "1.1.1", 588 | "body-parser": "1.18.2", 589 | "content-disposition": "0.5.2", 590 | "content-type": "~1.0.4", 591 | "cookie": "0.3.1", 592 | "cookie-signature": "1.0.6", 593 | "debug": "2.6.9", 594 | "depd": "~1.1.2", 595 | "encodeurl": "~1.0.2", 596 | "escape-html": "~1.0.3", 597 | "etag": "~1.8.1", 598 | "finalhandler": "1.1.1", 599 | "fresh": "0.5.2", 600 | "merge-descriptors": "1.0.1", 601 | "methods": "~1.1.2", 602 | "on-finished": "~2.3.0", 603 | "parseurl": "~1.3.2", 604 | "path-to-regexp": "0.1.7", 605 | "proxy-addr": "~2.0.3", 606 | "qs": "6.5.1", 607 | "range-parser": "~1.2.0", 608 | "safe-buffer": "5.1.1", 609 | "send": "0.16.2", 610 | "serve-static": "1.13.2", 611 | "setprototypeof": "1.1.0", 612 | "statuses": "~1.4.0", 613 | "type-is": "~1.6.16", 614 | "utils-merge": "1.0.1", 615 | "vary": "~1.1.2" 616 | }, 617 | "dependencies": { 618 | "body-parser": { 619 | "version": "1.18.2", 620 | "resolved": "https://registry.npmjs.org/body-parser/-/body-parser-1.18.2.tgz", 621 | "integrity": "sha1-h2eKGdhLR9hZuDGZvVm84iKxBFQ=", 622 | "requires": { 623 | "bytes": "3.0.0", 624 | "content-type": "~1.0.4", 625 | "debug": "2.6.9", 626 | "depd": "~1.1.1", 627 | "http-errors": "~1.6.2", 628 | "iconv-lite": "0.4.19", 629 | "on-finished": "~2.3.0", 630 | "qs": "6.5.1", 631 | "raw-body": "2.3.2", 632 | "type-is": "~1.6.15" 633 | } 634 | }, 635 | "iconv-lite": { 636 | "version": "0.4.19", 637 | "resolved": "https://registry.npmjs.org/iconv-lite/-/iconv-lite-0.4.19.tgz", 638 | "integrity": "sha512-oTZqweIP51xaGPI4uPa56/Pri/480R+mo7SeU+YETByQNhDG55ycFyNLIgta9vXhILrxXDmF7ZGhqZIcuN0gJQ==" 639 | }, 640 | "qs": { 641 | "version": "6.5.1", 642 | "resolved": "https://registry.npmjs.org/qs/-/qs-6.5.1.tgz", 643 | "integrity": "sha512-eRzhrN1WSINYCDCbrz796z37LOe3m5tmW7RQf6oBntukAG1nmovJvhnwHHRMAfeoItc1m2Hk02WER2aQ/iqs+A==" 644 | }, 645 | "raw-body": { 646 | "version": "2.3.2", 647 | "resolved": "https://registry.npmjs.org/raw-body/-/raw-body-2.3.2.tgz", 648 | "integrity": "sha1-vNYMd9Prk83gBQKVw/N5OJvIj4k=", 649 | "requires": { 650 | "bytes": "3.0.0", 651 | "http-errors": "1.6.2", 652 | "iconv-lite": "0.4.19", 653 | "unpipe": "1.0.0" 654 | }, 655 | "dependencies": { 656 | "depd": { 657 | "version": "1.1.1", 658 | "resolved": "https://registry.npmjs.org/depd/-/depd-1.1.1.tgz", 659 | "integrity": "sha1-V4O04cRZ8G+lyif5kfPQbnoxA1k=" 660 | }, 661 | "http-errors": { 662 | "version": "1.6.2", 663 | "resolved": "https://registry.npmjs.org/http-errors/-/http-errors-1.6.2.tgz", 664 | "integrity": "sha1-CgAsyFcHGSp+eUbO7cERVfYOxzY=", 665 | "requires": { 666 | "depd": "1.1.1", 667 | "inherits": "2.0.3", 668 | "setprototypeof": "1.0.3", 669 | "statuses": ">= 1.3.1 < 2" 670 | } 671 | }, 672 | "setprototypeof": { 673 | "version": "1.0.3", 674 | "resolved": "https://registry.npmjs.org/setprototypeof/-/setprototypeof-1.0.3.tgz", 675 | "integrity": "sha1-ZlZ+NwQ+608E2RvWWMDL77VbjgQ=" 676 | } 677 | } 678 | }, 679 | "statuses": { 680 | "version": "1.4.0", 681 | "resolved": "https://registry.npmjs.org/statuses/-/statuses-1.4.0.tgz", 682 | "integrity": "sha512-zhSCtt8v2NDrRlPQpCNtw/heZLtfUDqxBM1udqikb/Hbk52LK4nQSwr10u77iopCW5LsyHpuXS0GnEc48mLeew==" 683 | } 684 | } 685 | }, 686 | "external-editor": { 687 | "version": "2.2.0", 688 | "resolved": "https://registry.npmjs.org/external-editor/-/external-editor-2.2.0.tgz", 689 | "integrity": "sha512-bSn6gvGxKt+b7+6TKEv1ZycHleA7aHhRHyAqJyp5pbUFuYYNIzpZnQDk7AsYckyWdEnTeAnay0aCy2aV6iTk9A==", 690 | "requires": { 691 | "chardet": "^0.4.0", 692 | "iconv-lite": "^0.4.17", 693 | "tmp": "^0.0.33" 694 | } 695 | }, 696 | "fast-deep-equal": { 697 | "version": "1.1.0", 698 | "resolved": "https://registry.npmjs.org/fast-deep-equal/-/fast-deep-equal-1.1.0.tgz", 699 | "integrity": "sha1-wFNHeBfIa1HaqFPIHgWbcz0CNhQ=" 700 | }, 701 | "fast-json-stable-stringify": { 702 | "version": "2.0.0", 703 | "resolved": "https://registry.npmjs.org/fast-json-stable-stringify/-/fast-json-stable-stringify-2.0.0.tgz", 704 | "integrity": "sha1-1RQsDK7msRifh9OnYREGT4bIu/I=" 705 | }, 706 | "fast-levenshtein": { 707 | "version": "2.0.6", 708 | "resolved": "https://registry.npmjs.org/fast-levenshtein/-/fast-levenshtein-2.0.6.tgz", 709 | "integrity": "sha1-PYpcZog6FqMMqGQ+hR8Zuqd5eRc=" 710 | }, 711 | "figures": { 712 | "version": "2.0.0", 713 | "resolved": "https://registry.npmjs.org/figures/-/figures-2.0.0.tgz", 714 | "integrity": "sha1-OrGi0qYsi/tDGgyUy3l6L84nyWI=", 715 | "requires": { 716 | "escape-string-regexp": "^1.0.5" 717 | } 718 | }, 719 | "file-entry-cache": { 720 | "version": "2.0.0", 721 | "resolved": "https://registry.npmjs.org/file-entry-cache/-/file-entry-cache-2.0.0.tgz", 722 | "integrity": "sha1-w5KZDD5oR4PYOLjISkXYoEhFg2E=", 723 | "requires": { 724 | "flat-cache": "^1.2.1", 725 | "object-assign": "^4.0.1" 726 | } 727 | }, 728 | "finalhandler": { 729 | "version": "1.1.1", 730 | "resolved": "https://registry.npmjs.org/finalhandler/-/finalhandler-1.1.1.tgz", 731 | "integrity": "sha512-Y1GUDo39ez4aHAw7MysnUD5JzYX+WaIj8I57kO3aEPT1fFRL4sr7mjei97FgnwhAyyzRYmQZaTHb2+9uZ1dPtg==", 732 | "requires": { 733 | "debug": "2.6.9", 734 | "encodeurl": "~1.0.2", 735 | "escape-html": "~1.0.3", 736 | "on-finished": "~2.3.0", 737 | "parseurl": "~1.3.2", 738 | "statuses": "~1.4.0", 739 | "unpipe": "~1.0.0" 740 | }, 741 | "dependencies": { 742 | "statuses": { 743 | "version": "1.4.0", 744 | "resolved": "https://registry.npmjs.org/statuses/-/statuses-1.4.0.tgz", 745 | "integrity": "sha512-zhSCtt8v2NDrRlPQpCNtw/heZLtfUDqxBM1udqikb/Hbk52LK4nQSwr10u77iopCW5LsyHpuXS0GnEc48mLeew==" 746 | } 747 | } 748 | }, 749 | "flat-cache": { 750 | "version": "1.3.0", 751 | "resolved": "https://registry.npmjs.org/flat-cache/-/flat-cache-1.3.0.tgz", 752 | "integrity": "sha1-0wMLMrOBVPTjt+nHCfSQ9++XxIE=", 753 | "requires": { 754 | "circular-json": "^0.3.1", 755 | "del": "^2.0.2", 756 | "graceful-fs": "^4.1.2", 757 | "write": "^0.2.1" 758 | } 759 | }, 760 | "forwarded": { 761 | "version": "0.1.2", 762 | "resolved": "https://registry.npmjs.org/forwarded/-/forwarded-0.1.2.tgz", 763 | "integrity": "sha1-mMI9qxF1ZXuMBXPozszZGw/xjIQ=" 764 | }, 765 | "fresh": { 766 | "version": "0.5.2", 767 | "resolved": "https://registry.npmjs.org/fresh/-/fresh-0.5.2.tgz", 768 | "integrity": "sha1-PYyt2Q2XZWn6g1qx+OSyOhBWBac=" 769 | }, 770 | "fs.realpath": { 771 | "version": "1.0.0", 772 | "resolved": "https://registry.npmjs.org/fs.realpath/-/fs.realpath-1.0.0.tgz", 773 | "integrity": "sha1-FQStJSMVjKpA20onh8sBQRmU6k8=" 774 | }, 775 | "functional-red-black-tree": { 776 | "version": "1.0.1", 777 | "resolved": "https://registry.npmjs.org/functional-red-black-tree/-/functional-red-black-tree-1.0.1.tgz", 778 | "integrity": "sha1-GwqzvVU7Kg1jmdKcDj6gslIHgyc=" 779 | }, 780 | "glob": { 781 | "version": "7.1.2", 782 | "resolved": "https://registry.npmjs.org/glob/-/glob-7.1.2.tgz", 783 | "integrity": "sha512-MJTUg1kjuLeQCJ+ccE4Vpa6kKVXkPYJ2mOCQyUuKLcLQsdrMCpBPUi8qVE6+YuaJkozeA9NusTAw3hLr8Xe5EQ==", 784 | "requires": { 785 | "fs.realpath": "^1.0.0", 786 | "inflight": "^1.0.4", 787 | "inherits": "2", 788 | "minimatch": "^3.0.4", 789 | "once": "^1.3.0", 790 | "path-is-absolute": "^1.0.0" 791 | } 792 | }, 793 | "globals": { 794 | "version": "11.7.0", 795 | "resolved": "https://registry.npmjs.org/globals/-/globals-11.7.0.tgz", 796 | "integrity": "sha512-K8BNSPySfeShBQXsahYB/AbbWruVOTyVpgoIDnl8odPpeSfP2J5QO2oLFFdl2j7GfDCtZj2bMKar2T49itTPCg==" 797 | }, 798 | "globby": { 799 | "version": "5.0.0", 800 | "resolved": "https://registry.npmjs.org/globby/-/globby-5.0.0.tgz", 801 | "integrity": "sha1-69hGZ8oNuzMLmbz8aOrCvFQ3Dg0=", 802 | "requires": { 803 | "array-union": "^1.0.1", 804 | "arrify": "^1.0.0", 805 | "glob": "^7.0.3", 806 | "object-assign": "^4.0.1", 807 | "pify": "^2.0.0", 808 | "pinkie-promise": "^2.0.0" 809 | } 810 | }, 811 | "graceful-fs": { 812 | "version": "4.1.11", 813 | "resolved": "https://registry.npmjs.org/graceful-fs/-/graceful-fs-4.1.11.tgz", 814 | "integrity": "sha1-Dovf5NHduIVNZOBOp8AOKgJuVlg=" 815 | }, 816 | "has-ansi": { 817 | "version": "2.0.0", 818 | "resolved": "https://registry.npmjs.org/has-ansi/-/has-ansi-2.0.0.tgz", 819 | "integrity": "sha1-NPUEnOHs3ysGSa8+8k5F7TVBbZE=", 820 | "requires": { 821 | "ansi-regex": "^2.0.0" 822 | } 823 | }, 824 | "has-flag": { 825 | "version": "3.0.0", 826 | "resolved": "https://registry.npmjs.org/has-flag/-/has-flag-3.0.0.tgz", 827 | "integrity": "sha1-tdRU3CGZriJWmfNGfloH87lVuv0=" 828 | }, 829 | "hosted-git-info": { 830 | "version": "2.7.1", 831 | "resolved": "https://registry.npmjs.org/hosted-git-info/-/hosted-git-info-2.7.1.tgz", 832 | "integrity": "sha512-7T/BxH19zbcCTa8XkMlbK5lTo1WtgkFi3GvdWEyNuc4Vex7/9Dqbnpsf4JMydcfj9HCg4zUWFTL3Za6lapg5/w==" 833 | }, 834 | "http-errors": { 835 | "version": "1.6.3", 836 | "resolved": "https://registry.npmjs.org/http-errors/-/http-errors-1.6.3.tgz", 837 | "integrity": "sha1-i1VoC7S+KDoLW/TqLjhYC+HZMg0=", 838 | "requires": { 839 | "depd": "~1.1.2", 840 | "inherits": "2.0.3", 841 | "setprototypeof": "1.1.0", 842 | "statuses": ">= 1.4.0 < 2" 843 | } 844 | }, 845 | "iconv-lite": { 846 | "version": "0.4.23", 847 | "resolved": "https://registry.npmjs.org/iconv-lite/-/iconv-lite-0.4.23.tgz", 848 | "integrity": "sha512-neyTUVFtahjf0mB3dZT77u+8O0QB89jFdnBkd5P1JgYPbPaia3gXXOVL2fq8VyU2gMMD7SaN7QukTB/pmXYvDA==", 849 | "requires": { 850 | "safer-buffer": ">= 2.1.2 < 3" 851 | } 852 | }, 853 | "ignore": { 854 | "version": "3.3.10", 855 | "resolved": "https://registry.npmjs.org/ignore/-/ignore-3.3.10.tgz", 856 | "integrity": "sha512-Pgs951kaMm5GXP7MOvxERINe3gsaVjUWFm+UZPSq9xYriQAksyhg0csnS0KXSNRD5NmNdapXEpjxG49+AKh/ug==" 857 | }, 858 | "imurmurhash": { 859 | "version": "0.1.4", 860 | "resolved": "https://registry.npmjs.org/imurmurhash/-/imurmurhash-0.1.4.tgz", 861 | "integrity": "sha1-khi5srkoojixPcT7a21XbyMUU+o=" 862 | }, 863 | "inflight": { 864 | "version": "1.0.6", 865 | "resolved": "https://registry.npmjs.org/inflight/-/inflight-1.0.6.tgz", 866 | "integrity": "sha1-Sb1jMdfQLQwJvJEKEHW6gWW1bfk=", 867 | "requires": { 868 | "once": "^1.3.0", 869 | "wrappy": "1" 870 | } 871 | }, 872 | "inherits": { 873 | "version": "2.0.3", 874 | "resolved": "https://registry.npmjs.org/inherits/-/inherits-2.0.3.tgz", 875 | "integrity": "sha1-Yzwsg+PaQqUC9SRmAiSA9CCCYd4=" 876 | }, 877 | "inquirer": { 878 | "version": "3.3.0", 879 | "resolved": "https://registry.npmjs.org/inquirer/-/inquirer-3.3.0.tgz", 880 | "integrity": "sha512-h+xtnyk4EwKvFWHrUYsWErEVR+igKtLdchu+o0Z1RL7VU/jVMFbYir2bp6bAj8efFNxWqHX0dIss6fJQ+/+qeQ==", 881 | "requires": { 882 | "ansi-escapes": "^3.0.0", 883 | "chalk": "^2.0.0", 884 | "cli-cursor": "^2.1.0", 885 | "cli-width": "^2.0.0", 886 | "external-editor": "^2.0.4", 887 | "figures": "^2.0.0", 888 | "lodash": "^4.3.0", 889 | "mute-stream": "0.0.7", 890 | "run-async": "^2.2.0", 891 | "rx-lite": "^4.0.8", 892 | "rx-lite-aggregates": "^4.0.8", 893 | "string-width": "^2.1.0", 894 | "strip-ansi": "^4.0.0", 895 | "through": "^2.3.6" 896 | } 897 | }, 898 | "ipaddr.js": { 899 | "version": "1.8.0", 900 | "resolved": "https://registry.npmjs.org/ipaddr.js/-/ipaddr.js-1.8.0.tgz", 901 | "integrity": "sha1-6qM9bd16zo9/b+DJygRA5wZzix4=" 902 | }, 903 | "is-arrayish": { 904 | "version": "0.2.1", 905 | "resolved": "https://registry.npmjs.org/is-arrayish/-/is-arrayish-0.2.1.tgz", 906 | "integrity": "sha1-d8mYQFJ6qOyxqLppe4BkWnqSap0=" 907 | }, 908 | "is-builtin-module": { 909 | "version": "1.0.0", 910 | "resolved": "https://registry.npmjs.org/is-builtin-module/-/is-builtin-module-1.0.0.tgz", 911 | "integrity": "sha1-VAVy0096wxGfj3bDDLwbHgN6/74=", 912 | "requires": { 913 | "builtin-modules": "^1.0.0" 914 | } 915 | }, 916 | "is-fullwidth-code-point": { 917 | "version": "2.0.0", 918 | "resolved": "https://registry.npmjs.org/is-fullwidth-code-point/-/is-fullwidth-code-point-2.0.0.tgz", 919 | "integrity": "sha1-o7MKXE8ZkYMWeqq5O+764937ZU8=" 920 | }, 921 | "is-path-cwd": { 922 | "version": "1.0.0", 923 | "resolved": "https://registry.npmjs.org/is-path-cwd/-/is-path-cwd-1.0.0.tgz", 924 | "integrity": "sha1-0iXsIxMuie3Tj9p2dHLmLmXxEG0=" 925 | }, 926 | "is-path-in-cwd": { 927 | "version": "1.0.1", 928 | "resolved": "https://registry.npmjs.org/is-path-in-cwd/-/is-path-in-cwd-1.0.1.tgz", 929 | "integrity": "sha512-FjV1RTW48E7CWM7eE/J2NJvAEEVektecDBVBE5Hh3nM1Jd0kvhHtX68Pr3xsDf857xt3Y4AkwVULK1Vku62aaQ==", 930 | "requires": { 931 | "is-path-inside": "^1.0.0" 932 | } 933 | }, 934 | "is-path-inside": { 935 | "version": "1.0.1", 936 | "resolved": "https://registry.npmjs.org/is-path-inside/-/is-path-inside-1.0.1.tgz", 937 | "integrity": "sha1-jvW33lBDej/cprToZe96pVy0gDY=", 938 | "requires": { 939 | "path-is-inside": "^1.0.1" 940 | } 941 | }, 942 | "is-promise": { 943 | "version": "2.1.0", 944 | "resolved": "https://registry.npmjs.org/is-promise/-/is-promise-2.1.0.tgz", 945 | "integrity": "sha1-eaKp7OfwlugPNtKy87wWwf9L8/o=" 946 | }, 947 | "is-resolvable": { 948 | "version": "1.1.0", 949 | "resolved": "https://registry.npmjs.org/is-resolvable/-/is-resolvable-1.1.0.tgz", 950 | "integrity": "sha512-qgDYXFSR5WvEfuS5dMj6oTMEbrrSaM0CrFk2Yiq/gXnBvD9pMa2jGXxyhGLfvhZpuMZe18CJpFxAt3CRs42NMg==" 951 | }, 952 | "isarray": { 953 | "version": "1.0.0", 954 | "resolved": "https://registry.npmjs.org/isarray/-/isarray-1.0.0.tgz", 955 | "integrity": "sha1-u5NdSFgsuhaMBoNJV6VKPgcSTxE=" 956 | }, 957 | "isexe": { 958 | "version": "2.0.0", 959 | "resolved": "https://registry.npmjs.org/isexe/-/isexe-2.0.0.tgz", 960 | "integrity": "sha1-6PvzdNxVb/iUehDcsFctYz8s+hA=" 961 | }, 962 | "js-tokens": { 963 | "version": "3.0.2", 964 | "resolved": "https://registry.npmjs.org/js-tokens/-/js-tokens-3.0.2.tgz", 965 | "integrity": "sha1-mGbfOVECEw449/mWvOtlRDIJwls=" 966 | }, 967 | "js-yaml": { 968 | "version": "3.12.0", 969 | "resolved": "https://registry.npmjs.org/js-yaml/-/js-yaml-3.12.0.tgz", 970 | "integrity": "sha512-PIt2cnwmPfL4hKNwqeiuz4bKfnzHTBv6HyVgjahA6mPLwPDzjDWrplJBMjHUFxku/N3FlmrbyPclad+I+4mJ3A==", 971 | "requires": { 972 | "argparse": "^1.0.7", 973 | "esprima": "^4.0.0" 974 | } 975 | }, 976 | "json-parse-better-errors": { 977 | "version": "1.0.2", 978 | "resolved": "https://registry.npmjs.org/json-parse-better-errors/-/json-parse-better-errors-1.0.2.tgz", 979 | "integrity": "sha512-mrqyZKfX5EhL7hvqcV6WG1yYjnjeuYDzDhhcAAUrq8Po85NBQBJP+ZDUT75qZQ98IkUoBqdkExkukOU7Ts2wrw==" 980 | }, 981 | "json-schema-traverse": { 982 | "version": "0.3.1", 983 | "resolved": "https://registry.npmjs.org/json-schema-traverse/-/json-schema-traverse-0.3.1.tgz", 984 | "integrity": "sha1-NJptRMU6Ud6JtAgFxdXlm0F9M0A=" 985 | }, 986 | "json-stable-stringify-without-jsonify": { 987 | "version": "1.0.1", 988 | "resolved": "https://registry.npmjs.org/json-stable-stringify-without-jsonify/-/json-stable-stringify-without-jsonify-1.0.1.tgz", 989 | "integrity": "sha1-nbe1lJatPzz+8wp1FC0tkwrXJlE=" 990 | }, 991 | "jsonwebtoken": { 992 | "version": "8.3.0", 993 | "resolved": "https://registry.npmjs.org/jsonwebtoken/-/jsonwebtoken-8.3.0.tgz", 994 | "integrity": "sha512-oge/hvlmeJCH+iIz1DwcO7vKPkNGJHhgkspk8OH3VKlw+mbi42WtD4ig1+VXRln765vxptAv+xT26Fd3cteqag==", 995 | "requires": { 996 | "jws": "^3.1.5", 997 | "lodash.includes": "^4.3.0", 998 | "lodash.isboolean": "^3.0.3", 999 | "lodash.isinteger": "^4.0.4", 1000 | "lodash.isnumber": "^3.0.3", 1001 | "lodash.isplainobject": "^4.0.6", 1002 | "lodash.isstring": "^4.0.1", 1003 | "lodash.once": "^4.0.0", 1004 | "ms": "^2.1.1" 1005 | }, 1006 | "dependencies": { 1007 | "ms": { 1008 | "version": "2.1.1", 1009 | "resolved": "https://registry.npmjs.org/ms/-/ms-2.1.1.tgz", 1010 | "integrity": "sha512-tgp+dl5cGk28utYktBsrFqA7HKgrhgPsg6Z/EfhWI4gl1Hwq8B/GmY/0oXZ6nF8hDVesS/FpnYaD/kOWhYQvyg==" 1011 | } 1012 | } 1013 | }, 1014 | "jwa": { 1015 | "version": "1.1.6", 1016 | "resolved": "https://registry.npmjs.org/jwa/-/jwa-1.1.6.tgz", 1017 | "integrity": "sha512-tBO/cf++BUsJkYql/kBbJroKOgHWEigTKBAjjBEmrMGYd1QMBC74Hr4Wo2zCZw6ZrVhlJPvoMrkcOnlWR/DJfw==", 1018 | "requires": { 1019 | "buffer-equal-constant-time": "1.0.1", 1020 | "ecdsa-sig-formatter": "1.0.10", 1021 | "safe-buffer": "^5.0.1" 1022 | } 1023 | }, 1024 | "jws": { 1025 | "version": "3.1.5", 1026 | "resolved": "https://registry.npmjs.org/jws/-/jws-3.1.5.tgz", 1027 | "integrity": "sha512-GsCSexFADNQUr8T5HPJvayTjvPIfoyJPtLQBwn5a4WZQchcrPMPMAWcC1AzJVRDKyD6ZPROPAxgv6rfHViO4uQ==", 1028 | "requires": { 1029 | "jwa": "^1.1.5", 1030 | "safe-buffer": "^5.0.1" 1031 | } 1032 | }, 1033 | "kareem": { 1034 | "version": "2.2.1", 1035 | "resolved": "https://registry.npmjs.org/kareem/-/kareem-2.2.1.tgz", 1036 | "integrity": "sha512-xpDFy8OxkFM+vK6pXy6JmH92ibeEFUuDWzas5M9L7MzVmHW3jzwAHxodCPV/BYkf4A31bVDLyonrMfp9RXb/oA==" 1037 | }, 1038 | "levn": { 1039 | "version": "0.3.0", 1040 | "resolved": "https://registry.npmjs.org/levn/-/levn-0.3.0.tgz", 1041 | "integrity": "sha1-OwmSTt+fCDwEkP3UwLxEIeBHZO4=", 1042 | "requires": { 1043 | "prelude-ls": "~1.1.2", 1044 | "type-check": "~0.3.2" 1045 | } 1046 | }, 1047 | "load-json-file": { 1048 | "version": "4.0.0", 1049 | "resolved": "https://registry.npmjs.org/load-json-file/-/load-json-file-4.0.0.tgz", 1050 | "integrity": "sha1-L19Fq5HjMhYjT9U62rZo607AmTs=", 1051 | "requires": { 1052 | "graceful-fs": "^4.1.2", 1053 | "parse-json": "^4.0.0", 1054 | "pify": "^3.0.0", 1055 | "strip-bom": "^3.0.0" 1056 | }, 1057 | "dependencies": { 1058 | "pify": { 1059 | "version": "3.0.0", 1060 | "resolved": "https://registry.npmjs.org/pify/-/pify-3.0.0.tgz", 1061 | "integrity": "sha1-5aSs0sEB/fPZpNB/DbxNtJ3SgXY=" 1062 | } 1063 | } 1064 | }, 1065 | "lodash": { 1066 | "version": "4.17.10", 1067 | "resolved": "https://registry.npmjs.org/lodash/-/lodash-4.17.10.tgz", 1068 | "integrity": "sha512-UejweD1pDoXu+AD825lWwp4ZGtSwgnpZxb3JDViD7StjQz+Nb/6l093lx4OQ0foGWNRoc19mWy7BzL+UAK2iVg==" 1069 | }, 1070 | "lodash.get": { 1071 | "version": "4.4.2", 1072 | "resolved": "https://registry.npmjs.org/lodash.get/-/lodash.get-4.4.2.tgz", 1073 | "integrity": "sha1-LRd/ZS+jHpObRDjVNBSZ36OCXpk=" 1074 | }, 1075 | "lodash.includes": { 1076 | "version": "4.3.0", 1077 | "resolved": "https://registry.npmjs.org/lodash.includes/-/lodash.includes-4.3.0.tgz", 1078 | "integrity": "sha1-YLuYqHy5I8aMoeUTJUgzFISfVT8=" 1079 | }, 1080 | "lodash.isboolean": { 1081 | "version": "3.0.3", 1082 | "resolved": "https://registry.npmjs.org/lodash.isboolean/-/lodash.isboolean-3.0.3.tgz", 1083 | "integrity": "sha1-bC4XHbKiV82WgC/UOwGyDV9YcPY=" 1084 | }, 1085 | "lodash.isinteger": { 1086 | "version": "4.0.4", 1087 | "resolved": "https://registry.npmjs.org/lodash.isinteger/-/lodash.isinteger-4.0.4.tgz", 1088 | "integrity": "sha1-YZwK89A/iwTDH1iChAt3sRzWg0M=" 1089 | }, 1090 | "lodash.isnumber": { 1091 | "version": "3.0.3", 1092 | "resolved": "https://registry.npmjs.org/lodash.isnumber/-/lodash.isnumber-3.0.3.tgz", 1093 | "integrity": "sha1-POdoEMWSjQM1IwGsKHMX8RwLH/w=" 1094 | }, 1095 | "lodash.isplainobject": { 1096 | "version": "4.0.6", 1097 | "resolved": "https://registry.npmjs.org/lodash.isplainobject/-/lodash.isplainobject-4.0.6.tgz", 1098 | "integrity": "sha1-fFJqUtibRcRcxpC4gWO+BJf1UMs=" 1099 | }, 1100 | "lodash.isstring": { 1101 | "version": "4.0.1", 1102 | "resolved": "https://registry.npmjs.org/lodash.isstring/-/lodash.isstring-4.0.1.tgz", 1103 | "integrity": "sha1-1SfftUVuynzJu5XV2ur4i6VKVFE=" 1104 | }, 1105 | "lodash.once": { 1106 | "version": "4.1.1", 1107 | "resolved": "https://registry.npmjs.org/lodash.once/-/lodash.once-4.1.1.tgz", 1108 | "integrity": "sha1-DdOXEhPHxW34gJd9UEyI+0cal6w=" 1109 | }, 1110 | "lru-cache": { 1111 | "version": "4.1.3", 1112 | "resolved": "https://registry.npmjs.org/lru-cache/-/lru-cache-4.1.3.tgz", 1113 | "integrity": "sha512-fFEhvcgzuIoJVUF8fYr5KR0YqxD238zgObTps31YdADwPPAp82a4M8TrckkWyx7ekNlf9aBcVn81cFwwXngrJA==", 1114 | "requires": { 1115 | "pseudomap": "^1.0.2", 1116 | "yallist": "^2.1.2" 1117 | } 1118 | }, 1119 | "media-typer": { 1120 | "version": "0.3.0", 1121 | "resolved": "https://registry.npmjs.org/media-typer/-/media-typer-0.3.0.tgz", 1122 | "integrity": "sha1-hxDXrwqmJvj/+hzgAWhUUmMlV0g=" 1123 | }, 1124 | "merge-descriptors": { 1125 | "version": "1.0.1", 1126 | "resolved": "https://registry.npmjs.org/merge-descriptors/-/merge-descriptors-1.0.1.tgz", 1127 | "integrity": "sha1-sAqqVW3YtEVoFQ7J0blT8/kMu2E=" 1128 | }, 1129 | "methods": { 1130 | "version": "1.1.2", 1131 | "resolved": "https://registry.npmjs.org/methods/-/methods-1.1.2.tgz", 1132 | "integrity": "sha1-VSmk1nZUE07cxSZmVoNbD4Ua/O4=" 1133 | }, 1134 | "mime": { 1135 | "version": "1.4.1", 1136 | "resolved": "https://registry.npmjs.org/mime/-/mime-1.4.1.tgz", 1137 | "integrity": "sha512-KI1+qOZu5DcW6wayYHSzR/tXKCDC5Om4s1z2QJjDULzLcmf3DvzS7oluY4HCTrc+9FiKmWUgeNLg7W3uIQvxtQ==" 1138 | }, 1139 | "mime-db": { 1140 | "version": "1.35.0", 1141 | "resolved": "https://registry.npmjs.org/mime-db/-/mime-db-1.35.0.tgz", 1142 | "integrity": "sha512-JWT/IcCTsB0Io3AhWUMjRqucrHSPsSf2xKLaRldJVULioggvkJvggZ3VXNNSRkCddE6D+BUI4HEIZIA2OjwIvg==" 1143 | }, 1144 | "mime-types": { 1145 | "version": "2.1.19", 1146 | "resolved": "https://registry.npmjs.org/mime-types/-/mime-types-2.1.19.tgz", 1147 | "integrity": "sha512-P1tKYHVSZ6uFo26mtnve4HQFE3koh1UWVkp8YUC+ESBHe945xWSoXuHHiGarDqcEZ+whpCDnlNw5LON0kLo+sw==", 1148 | "requires": { 1149 | "mime-db": "~1.35.0" 1150 | } 1151 | }, 1152 | "mimic-fn": { 1153 | "version": "1.2.0", 1154 | "resolved": "https://registry.npmjs.org/mimic-fn/-/mimic-fn-1.2.0.tgz", 1155 | "integrity": "sha512-jf84uxzwiuiIVKiOLpfYk7N46TSy8ubTonmneY9vrpHNAnp0QBt2BxWV9dO3/j+BoVAb+a5G6YDPW3M5HOdMWQ==" 1156 | }, 1157 | "minimatch": { 1158 | "version": "3.0.4", 1159 | "resolved": "https://registry.npmjs.org/minimatch/-/minimatch-3.0.4.tgz", 1160 | "integrity": "sha512-yJHVQEhyqPLUTgt9B83PXu6W3rx4MvvHvSUvToogpwoGDOUQ+yDrR0HRot+yOCdCO7u4hX3pWft6kWBBcqh0UA==", 1161 | "requires": { 1162 | "brace-expansion": "^1.1.7" 1163 | } 1164 | }, 1165 | "minimist": { 1166 | "version": "0.0.8", 1167 | "resolved": "https://registry.npmjs.org/minimist/-/minimist-0.0.8.tgz", 1168 | "integrity": "sha1-hX/Kv8M5fSYluCKCYuhqp6ARsF0=" 1169 | }, 1170 | "mkdirp": { 1171 | "version": "0.5.1", 1172 | "resolved": "https://registry.npmjs.org/mkdirp/-/mkdirp-0.5.1.tgz", 1173 | "integrity": "sha1-MAV0OOrGz3+MR2fzhkjWaX11yQM=", 1174 | "requires": { 1175 | "minimist": "0.0.8" 1176 | } 1177 | }, 1178 | "mongodb": { 1179 | "version": "3.1.1", 1180 | "resolved": "https://registry.npmjs.org/mongodb/-/mongodb-3.1.1.tgz", 1181 | "integrity": "sha512-GU9oWK4pi8PC7NyGiwjFMwZyMqwGWoMEMvM0LZh7UKW/FFAqgmZKjjriD+5MEOCDUJE2dtHX93/K5UtDxO0otg==", 1182 | "requires": { 1183 | "mongodb-core": "3.1.0" 1184 | } 1185 | }, 1186 | "mongodb-core": { 1187 | "version": "3.1.0", 1188 | "resolved": "https://registry.npmjs.org/mongodb-core/-/mongodb-core-3.1.0.tgz", 1189 | "integrity": "sha512-qRjG62Fu//CZhkgn0jA/k8jh5MhACIq8cOJUryH6sck87pgt+C222MSD02tsCq5zNo/B6ZFHtNodZ2qpf8E86g==", 1190 | "requires": { 1191 | "bson": "~1.0.4", 1192 | "require_optional": "^1.0.1", 1193 | "saslprep": "^1.0.0" 1194 | } 1195 | }, 1196 | "mongoose": { 1197 | "version": "5.2.6", 1198 | "resolved": "https://registry.npmjs.org/mongoose/-/mongoose-5.2.6.tgz", 1199 | "integrity": "sha512-0W37haYSdeaD2oRT+T8A94HD9vpPvB4PptnzfnB09PTqA55aH0ASM+MZZRN81kZI72vYRqXqEwdU6gBR6R/vqw==", 1200 | "requires": { 1201 | "async": "2.6.1", 1202 | "bson": "~1.0.5", 1203 | "kareem": "2.2.1", 1204 | "lodash.get": "4.4.2", 1205 | "mongodb": "3.1.1", 1206 | "mongodb-core": "3.1.0", 1207 | "mongoose-legacy-pluralize": "1.0.2", 1208 | "mpath": "0.4.1", 1209 | "mquery": "3.1.1", 1210 | "ms": "2.0.0", 1211 | "regexp-clone": "0.0.1", 1212 | "sliced": "1.0.1" 1213 | } 1214 | }, 1215 | "mongoose-legacy-pluralize": { 1216 | "version": "1.0.2", 1217 | "resolved": "https://registry.npmjs.org/mongoose-legacy-pluralize/-/mongoose-legacy-pluralize-1.0.2.tgz", 1218 | "integrity": "sha512-Yo/7qQU4/EyIS8YDFSeenIvXxZN+ld7YdV9LqFVQJzTLye8unujAWPZ4NWKfFA+RNjh+wvTWKY9Z3E5XM6ZZiQ==" 1219 | }, 1220 | "mpath": { 1221 | "version": "0.4.1", 1222 | "resolved": "https://registry.npmjs.org/mpath/-/mpath-0.4.1.tgz", 1223 | "integrity": "sha512-NNY/MpBkALb9jJmjpBlIi6GRoLveLUM0pJzgbp9vY9F7IQEb/HREC/nxrixechcQwd1NevOhJnWWV8QQQRE+OA==" 1224 | }, 1225 | "mquery": { 1226 | "version": "3.1.1", 1227 | "resolved": "https://registry.npmjs.org/mquery/-/mquery-3.1.1.tgz", 1228 | "integrity": "sha512-RC8BMQJizE20bYaAiZ5uQIvuRLNo6eH6wFPoOwQbBR69dDti8Cj6d3f7pRyvbLv7WMjaQIJYo3P2kM4RUabdFg==", 1229 | "requires": { 1230 | "bluebird": "3.5.1", 1231 | "debug": "3.1.0", 1232 | "eslint": "4.14.0", 1233 | "regexp-clone": "0.0.1", 1234 | "sliced": "1.0.1" 1235 | }, 1236 | "dependencies": { 1237 | "debug": { 1238 | "version": "3.1.0", 1239 | "resolved": "https://registry.npmjs.org/debug/-/debug-3.1.0.tgz", 1240 | "integrity": "sha512-OX8XqP7/1a9cqkxYw2yXss15f26NKWBpDXQd0/uK/KPqdQhxbPa994hnzjcE2VqQpDslf55723cKPUOGSmMY3g==", 1241 | "requires": { 1242 | "ms": "2.0.0" 1243 | } 1244 | } 1245 | } 1246 | }, 1247 | "ms": { 1248 | "version": "2.0.0", 1249 | "resolved": "https://registry.npmjs.org/ms/-/ms-2.0.0.tgz", 1250 | "integrity": "sha1-VgiurfwAvmwpAd9fmGF4jeDVl8g=" 1251 | }, 1252 | "mute-stream": { 1253 | "version": "0.0.7", 1254 | "resolved": "https://registry.npmjs.org/mute-stream/-/mute-stream-0.0.7.tgz", 1255 | "integrity": "sha1-MHXOk7whuPq0PhvE2n6BFe0ee6s=" 1256 | }, 1257 | "natural-compare": { 1258 | "version": "1.4.0", 1259 | "resolved": "https://registry.npmjs.org/natural-compare/-/natural-compare-1.4.0.tgz", 1260 | "integrity": "sha1-Sr6/7tdUHywnrPspvbvRXI1bpPc=" 1261 | }, 1262 | "negotiator": { 1263 | "version": "0.6.1", 1264 | "resolved": "https://registry.npmjs.org/negotiator/-/negotiator-0.6.1.tgz", 1265 | "integrity": "sha1-KzJxhOiZIQEXeyhWP7XnECrNDKk=" 1266 | }, 1267 | "normalize-package-data": { 1268 | "version": "2.4.0", 1269 | "resolved": "https://registry.npmjs.org/normalize-package-data/-/normalize-package-data-2.4.0.tgz", 1270 | "integrity": "sha512-9jjUFbTPfEy3R/ad/2oNbKtW9Hgovl5O1FvFWKkKblNXoN/Oou6+9+KKohPK13Yc3/TyunyWhJp6gvRNR/PPAw==", 1271 | "requires": { 1272 | "hosted-git-info": "^2.1.4", 1273 | "is-builtin-module": "^1.0.0", 1274 | "semver": "2 || 3 || 4 || 5", 1275 | "validate-npm-package-license": "^3.0.1" 1276 | } 1277 | }, 1278 | "object-assign": { 1279 | "version": "4.1.1", 1280 | "resolved": "https://registry.npmjs.org/object-assign/-/object-assign-4.1.1.tgz", 1281 | "integrity": "sha1-IQmtx5ZYh8/AXLvUQsrIv7s2CGM=" 1282 | }, 1283 | "on-finished": { 1284 | "version": "2.3.0", 1285 | "resolved": "https://registry.npmjs.org/on-finished/-/on-finished-2.3.0.tgz", 1286 | "integrity": "sha1-IPEzZIGwg811M3mSoWlxqi2QaUc=", 1287 | "requires": { 1288 | "ee-first": "1.1.1" 1289 | } 1290 | }, 1291 | "once": { 1292 | "version": "1.4.0", 1293 | "resolved": "https://registry.npmjs.org/once/-/once-1.4.0.tgz", 1294 | "integrity": "sha1-WDsap3WWHUsROsF9nFC6753Xa9E=", 1295 | "requires": { 1296 | "wrappy": "1" 1297 | } 1298 | }, 1299 | "onetime": { 1300 | "version": "2.0.1", 1301 | "resolved": "https://registry.npmjs.org/onetime/-/onetime-2.0.1.tgz", 1302 | "integrity": "sha1-BnQoIw/WdEOyeUsiu6UotoZ5YtQ=", 1303 | "requires": { 1304 | "mimic-fn": "^1.0.0" 1305 | } 1306 | }, 1307 | "optionator": { 1308 | "version": "0.8.2", 1309 | "resolved": "https://registry.npmjs.org/optionator/-/optionator-0.8.2.tgz", 1310 | "integrity": "sha1-NkxeQJ0/TWMB1sC0wFu6UBgK62Q=", 1311 | "requires": { 1312 | "deep-is": "~0.1.3", 1313 | "fast-levenshtein": "~2.0.4", 1314 | "levn": "~0.3.0", 1315 | "prelude-ls": "~1.1.2", 1316 | "type-check": "~0.3.2", 1317 | "wordwrap": "~1.0.0" 1318 | } 1319 | }, 1320 | "os-tmpdir": { 1321 | "version": "1.0.2", 1322 | "resolved": "https://registry.npmjs.org/os-tmpdir/-/os-tmpdir-1.0.2.tgz", 1323 | "integrity": "sha1-u+Z0BseaqFxc/sdm/lc0VV36EnQ=" 1324 | }, 1325 | "parse-json": { 1326 | "version": "4.0.0", 1327 | "resolved": "https://registry.npmjs.org/parse-json/-/parse-json-4.0.0.tgz", 1328 | "integrity": "sha1-vjX1Qlvh9/bHRxhPmKeIy5lHfuA=", 1329 | "requires": { 1330 | "error-ex": "^1.3.1", 1331 | "json-parse-better-errors": "^1.0.1" 1332 | } 1333 | }, 1334 | "parseurl": { 1335 | "version": "1.3.2", 1336 | "resolved": "https://registry.npmjs.org/parseurl/-/parseurl-1.3.2.tgz", 1337 | "integrity": "sha1-/CidTtiZMRlGDBViUyYs3I3mW/M=" 1338 | }, 1339 | "passport": { 1340 | "version": "0.4.0", 1341 | "resolved": "https://registry.npmjs.org/passport/-/passport-0.4.0.tgz", 1342 | "integrity": "sha1-xQlWkTR71a07XhgCOMORTRbwWBE=", 1343 | "requires": { 1344 | "passport-strategy": "1.x.x", 1345 | "pause": "0.0.1" 1346 | } 1347 | }, 1348 | "passport-jwt": { 1349 | "version": "4.0.0", 1350 | "resolved": "https://registry.npmjs.org/passport-jwt/-/passport-jwt-4.0.0.tgz", 1351 | "integrity": "sha512-BwC0n2GP/1hMVjR4QpnvqA61TxenUMlmfNjYNgK0ZAs0HK4SOQkHcSv4L328blNTLtHq7DbmvyNJiH+bn6C5Mg==", 1352 | "requires": { 1353 | "jsonwebtoken": "^8.2.0", 1354 | "passport-strategy": "^1.0.0" 1355 | } 1356 | }, 1357 | "passport-strategy": { 1358 | "version": "1.0.0", 1359 | "resolved": "https://registry.npmjs.org/passport-strategy/-/passport-strategy-1.0.0.tgz", 1360 | "integrity": "sha1-tVOaqPwiWj0a0XlHbd8ja0QPUuQ=" 1361 | }, 1362 | "path-is-absolute": { 1363 | "version": "1.0.1", 1364 | "resolved": "https://registry.npmjs.org/path-is-absolute/-/path-is-absolute-1.0.1.tgz", 1365 | "integrity": "sha1-F0uSaHNVNP+8es5r9TpanhtcX18=" 1366 | }, 1367 | "path-is-inside": { 1368 | "version": "1.0.2", 1369 | "resolved": "https://registry.npmjs.org/path-is-inside/-/path-is-inside-1.0.2.tgz", 1370 | "integrity": "sha1-NlQX3t5EQw0cEa9hAn+s8HS9/FM=" 1371 | }, 1372 | "path-to-regexp": { 1373 | "version": "0.1.7", 1374 | "resolved": "https://registry.npmjs.org/path-to-regexp/-/path-to-regexp-0.1.7.tgz", 1375 | "integrity": "sha1-32BBeABfUi8V60SQ5yR6G/qmf4w=" 1376 | }, 1377 | "path-type": { 1378 | "version": "3.0.0", 1379 | "resolved": "https://registry.npmjs.org/path-type/-/path-type-3.0.0.tgz", 1380 | "integrity": "sha512-T2ZUsdZFHgA3u4e5PfPbjd7HDDpxPnQb5jN0SrDsjNSuVXHJqtwTnWqG0B1jZrgmJ/7lj1EmVIByWt1gxGkWvg==", 1381 | "requires": { 1382 | "pify": "^3.0.0" 1383 | }, 1384 | "dependencies": { 1385 | "pify": { 1386 | "version": "3.0.0", 1387 | "resolved": "https://registry.npmjs.org/pify/-/pify-3.0.0.tgz", 1388 | "integrity": "sha1-5aSs0sEB/fPZpNB/DbxNtJ3SgXY=" 1389 | } 1390 | } 1391 | }, 1392 | "pause": { 1393 | "version": "0.0.1", 1394 | "resolved": "https://registry.npmjs.org/pause/-/pause-0.0.1.tgz", 1395 | "integrity": "sha1-HUCLP9t2kjuVQ9lvtMnf1TXZy10=" 1396 | }, 1397 | "pify": { 1398 | "version": "2.3.0", 1399 | "resolved": "https://registry.npmjs.org/pify/-/pify-2.3.0.tgz", 1400 | "integrity": "sha1-7RQaasBDqEnqWISY59yosVMw6Qw=" 1401 | }, 1402 | "pinkie": { 1403 | "version": "2.0.4", 1404 | "resolved": "https://registry.npmjs.org/pinkie/-/pinkie-2.0.4.tgz", 1405 | "integrity": "sha1-clVrgM+g1IqXToDnckjoDtT3+HA=" 1406 | }, 1407 | "pinkie-promise": { 1408 | "version": "2.0.1", 1409 | "resolved": "https://registry.npmjs.org/pinkie-promise/-/pinkie-promise-2.0.1.tgz", 1410 | "integrity": "sha1-ITXW36ejWMBprJsXh3YogihFD/o=", 1411 | "requires": { 1412 | "pinkie": "^2.0.0" 1413 | } 1414 | }, 1415 | "pluralize": { 1416 | "version": "7.0.0", 1417 | "resolved": "https://registry.npmjs.org/pluralize/-/pluralize-7.0.0.tgz", 1418 | "integrity": "sha512-ARhBOdzS3e41FbkW/XWrTEtukqqLoK5+Z/4UeDaLuSW+39JPeFgs4gCGqsrJHVZX0fUrx//4OF0K1CUGwlIFow==" 1419 | }, 1420 | "prelude-ls": { 1421 | "version": "1.1.2", 1422 | "resolved": "https://registry.npmjs.org/prelude-ls/-/prelude-ls-1.1.2.tgz", 1423 | "integrity": "sha1-IZMqVJ9eUv/ZqCf1cOBL5iqX2lQ=" 1424 | }, 1425 | "process-nextick-args": { 1426 | "version": "2.0.0", 1427 | "resolved": "https://registry.npmjs.org/process-nextick-args/-/process-nextick-args-2.0.0.tgz", 1428 | "integrity": "sha512-MtEC1TqN0EU5nephaJ4rAtThHtC86dNN9qCuEhtshvpVBkAW5ZO7BASN9REnF9eoXGcRub+pFuKEpOHE+HbEMw==" 1429 | }, 1430 | "progress": { 1431 | "version": "2.0.0", 1432 | "resolved": "https://registry.npmjs.org/progress/-/progress-2.0.0.tgz", 1433 | "integrity": "sha1-ihvjZr+Pwj2yvSPxDG/pILQ4nR8=" 1434 | }, 1435 | "proxy-addr": { 1436 | "version": "2.0.4", 1437 | "resolved": "https://registry.npmjs.org/proxy-addr/-/proxy-addr-2.0.4.tgz", 1438 | "integrity": "sha512-5erio2h9jp5CHGwcybmxmVqHmnCBZeewlfJ0pex+UW7Qny7OOZXTtH56TGNyBizkgiOwhJtMKrVzDTeKcySZwA==", 1439 | "requires": { 1440 | "forwarded": "~0.1.2", 1441 | "ipaddr.js": "1.8.0" 1442 | } 1443 | }, 1444 | "pseudomap": { 1445 | "version": "1.0.2", 1446 | "resolved": "https://registry.npmjs.org/pseudomap/-/pseudomap-1.0.2.tgz", 1447 | "integrity": "sha1-8FKijacOYYkX7wqKw0wa5aaChrM=" 1448 | }, 1449 | "punycode": { 1450 | "version": "2.1.1", 1451 | "resolved": "https://registry.npmjs.org/punycode/-/punycode-2.1.1.tgz", 1452 | "integrity": "sha512-XRsRjdf+j5ml+y/6GKHPZbrF/8p2Yga0JPtdqTIY2Xe5ohJPD9saDJJLPvp9+NSBprVvevdXZybnj2cv8OEd0A==" 1453 | }, 1454 | "qs": { 1455 | "version": "6.5.2", 1456 | "resolved": "https://registry.npmjs.org/qs/-/qs-6.5.2.tgz", 1457 | "integrity": "sha512-N5ZAX4/LxJmF+7wN74pUD6qAh9/wnvdQcjq9TZjevvXzSUo7bfmw91saqMjzGS2xq91/odN2dW/WOl7qQHNDGA==" 1458 | }, 1459 | "range-parser": { 1460 | "version": "1.2.0", 1461 | "resolved": "https://registry.npmjs.org/range-parser/-/range-parser-1.2.0.tgz", 1462 | "integrity": "sha1-9JvmtIeJTdxA3MlKMi9hEJLgDV4=" 1463 | }, 1464 | "raw-body": { 1465 | "version": "2.3.3", 1466 | "resolved": "https://registry.npmjs.org/raw-body/-/raw-body-2.3.3.tgz", 1467 | "integrity": "sha512-9esiElv1BrZoI3rCDuOuKCBRbuApGGaDPQfjSflGxdy4oyzqghxu6klEkkVIvBje+FF0BX9coEv8KqW6X/7njw==", 1468 | "requires": { 1469 | "bytes": "3.0.0", 1470 | "http-errors": "1.6.3", 1471 | "iconv-lite": "0.4.23", 1472 | "unpipe": "1.0.0" 1473 | } 1474 | }, 1475 | "read-pkg": { 1476 | "version": "3.0.0", 1477 | "resolved": "https://registry.npmjs.org/read-pkg/-/read-pkg-3.0.0.tgz", 1478 | "integrity": "sha1-nLxoaXj+5l0WwA4rGcI3/Pbjg4k=", 1479 | "requires": { 1480 | "load-json-file": "^4.0.0", 1481 | "normalize-package-data": "^2.3.2", 1482 | "path-type": "^3.0.0" 1483 | } 1484 | }, 1485 | "readable-stream": { 1486 | "version": "2.3.6", 1487 | "resolved": "https://registry.npmjs.org/readable-stream/-/readable-stream-2.3.6.tgz", 1488 | "integrity": "sha512-tQtKA9WIAhBF3+VLAseyMqZeBjW0AHJoxOtYqSUZNJxauErmLbVm2FW1y+J/YA9dUrAC39ITejlZWhVIwawkKw==", 1489 | "requires": { 1490 | "core-util-is": "~1.0.0", 1491 | "inherits": "~2.0.3", 1492 | "isarray": "~1.0.0", 1493 | "process-nextick-args": "~2.0.0", 1494 | "safe-buffer": "~5.1.1", 1495 | "string_decoder": "~1.1.1", 1496 | "util-deprecate": "~1.0.1" 1497 | } 1498 | }, 1499 | "regexp-clone": { 1500 | "version": "0.0.1", 1501 | "resolved": "https://registry.npmjs.org/regexp-clone/-/regexp-clone-0.0.1.tgz", 1502 | "integrity": "sha1-p8LgmJH9vzj7sQ03b7cwA+aKxYk=" 1503 | }, 1504 | "require-uncached": { 1505 | "version": "1.0.3", 1506 | "resolved": "https://registry.npmjs.org/require-uncached/-/require-uncached-1.0.3.tgz", 1507 | "integrity": "sha1-Tg1W1slmL9MeQwEcS5WqSZVUIdM=", 1508 | "requires": { 1509 | "caller-path": "^0.1.0", 1510 | "resolve-from": "^1.0.0" 1511 | }, 1512 | "dependencies": { 1513 | "resolve-from": { 1514 | "version": "1.0.1", 1515 | "resolved": "https://registry.npmjs.org/resolve-from/-/resolve-from-1.0.1.tgz", 1516 | "integrity": "sha1-Jsv+k10a7uq7Kbw/5a6wHpPUQiY=" 1517 | } 1518 | } 1519 | }, 1520 | "require_optional": { 1521 | "version": "1.0.1", 1522 | "resolved": "https://registry.npmjs.org/require_optional/-/require_optional-1.0.1.tgz", 1523 | "integrity": "sha512-qhM/y57enGWHAe3v/NcwML6a3/vfESLe/sGM2dII+gEO0BpKRUkWZow/tyloNqJyN6kXSl3RyyM8Ll5D/sJP8g==", 1524 | "requires": { 1525 | "resolve-from": "^2.0.0", 1526 | "semver": "^5.1.0" 1527 | } 1528 | }, 1529 | "resolve-from": { 1530 | "version": "2.0.0", 1531 | "resolved": "https://registry.npmjs.org/resolve-from/-/resolve-from-2.0.0.tgz", 1532 | "integrity": "sha1-lICrIOlP+h2egKgEx+oUdhGWa1c=" 1533 | }, 1534 | "restore-cursor": { 1535 | "version": "2.0.0", 1536 | "resolved": "https://registry.npmjs.org/restore-cursor/-/restore-cursor-2.0.0.tgz", 1537 | "integrity": "sha1-n37ih/gv0ybU/RYpI9YhKe7g368=", 1538 | "requires": { 1539 | "onetime": "^2.0.0", 1540 | "signal-exit": "^3.0.2" 1541 | } 1542 | }, 1543 | "rimraf": { 1544 | "version": "2.6.2", 1545 | "resolved": "https://registry.npmjs.org/rimraf/-/rimraf-2.6.2.tgz", 1546 | "integrity": "sha512-lreewLK/BlghmxtfH36YYVg1i8IAce4TI7oao75I1g245+6BctqTVQiBP3YUJ9C6DQOXJmkYR9X9fCLtCOJc5w==", 1547 | "requires": { 1548 | "glob": "^7.0.5" 1549 | } 1550 | }, 1551 | "run-async": { 1552 | "version": "2.3.0", 1553 | "resolved": "https://registry.npmjs.org/run-async/-/run-async-2.3.0.tgz", 1554 | "integrity": "sha1-A3GrSuC91yDUFm19/aZP96RFpsA=", 1555 | "requires": { 1556 | "is-promise": "^2.1.0" 1557 | } 1558 | }, 1559 | "rx": { 1560 | "version": "2.3.24", 1561 | "resolved": "https://registry.npmjs.org/rx/-/rx-2.3.24.tgz", 1562 | "integrity": "sha1-FPlQpCF9fjXapxu8vljv9o6ksrc=" 1563 | }, 1564 | "rx-lite": { 1565 | "version": "4.0.8", 1566 | "resolved": "https://registry.npmjs.org/rx-lite/-/rx-lite-4.0.8.tgz", 1567 | "integrity": "sha1-Cx4Rr4vESDbwSmQH6S2kJGe3lEQ=" 1568 | }, 1569 | "rx-lite-aggregates": { 1570 | "version": "4.0.8", 1571 | "resolved": "https://registry.npmjs.org/rx-lite-aggregates/-/rx-lite-aggregates-4.0.8.tgz", 1572 | "integrity": "sha1-dTuHqJoRyVRnxKwWJsTvxOBcZ74=", 1573 | "requires": { 1574 | "rx-lite": "*" 1575 | } 1576 | }, 1577 | "safe-buffer": { 1578 | "version": "5.1.1", 1579 | "resolved": "https://registry.npmjs.org/safe-buffer/-/safe-buffer-5.1.1.tgz", 1580 | "integrity": "sha512-kKvNJn6Mm93gAczWVJg7wH+wGYWNrDHdWvpUmHyEsgCtIwwo3bqPtV4tR5tuPaUhTOo/kvhVwd8XwwOllGYkbg==" 1581 | }, 1582 | "safer-buffer": { 1583 | "version": "2.1.2", 1584 | "resolved": "https://registry.npmjs.org/safer-buffer/-/safer-buffer-2.1.2.tgz", 1585 | "integrity": "sha512-YZo3K82SD7Riyi0E1EQPojLz7kpepnSQI9IyPbHHg1XXXevb5dJI7tpyN2ADxGcQbHG7vcyRHk0cbwqcQriUtg==" 1586 | }, 1587 | "saslprep": { 1588 | "version": "1.0.1", 1589 | "resolved": "https://registry.npmjs.org/saslprep/-/saslprep-1.0.1.tgz", 1590 | "integrity": "sha512-ntN6SbE3hRqd45PKKadRPgA+xHPWg5lPSj2JWJdJvjTwXDDfkPVtXWvP8jJojvnm+rAsZ2b299C5NwZqq818EA==", 1591 | "optional": true 1592 | }, 1593 | "semver": { 1594 | "version": "5.5.0", 1595 | "resolved": "https://registry.npmjs.org/semver/-/semver-5.5.0.tgz", 1596 | "integrity": "sha512-4SJ3dm0WAwWy/NVeioZh5AntkdJoWKxHxcmyP622fOkgHa4z3R0TdBJICINyaSDE6uNwVc8gZr+ZinwZAH4xIA==" 1597 | }, 1598 | "send": { 1599 | "version": "0.16.2", 1600 | "resolved": "https://registry.npmjs.org/send/-/send-0.16.2.tgz", 1601 | "integrity": "sha512-E64YFPUssFHEFBvpbbjr44NCLtI1AohxQ8ZSiJjQLskAdKuriYEP6VyGEsRDH8ScozGpkaX1BGvhanqCwkcEZw==", 1602 | "requires": { 1603 | "debug": "2.6.9", 1604 | "depd": "~1.1.2", 1605 | "destroy": "~1.0.4", 1606 | "encodeurl": "~1.0.2", 1607 | "escape-html": "~1.0.3", 1608 | "etag": "~1.8.1", 1609 | "fresh": "0.5.2", 1610 | "http-errors": "~1.6.2", 1611 | "mime": "1.4.1", 1612 | "ms": "2.0.0", 1613 | "on-finished": "~2.3.0", 1614 | "range-parser": "~1.2.0", 1615 | "statuses": "~1.4.0" 1616 | }, 1617 | "dependencies": { 1618 | "statuses": { 1619 | "version": "1.4.0", 1620 | "resolved": "https://registry.npmjs.org/statuses/-/statuses-1.4.0.tgz", 1621 | "integrity": "sha512-zhSCtt8v2NDrRlPQpCNtw/heZLtfUDqxBM1udqikb/Hbk52LK4nQSwr10u77iopCW5LsyHpuXS0GnEc48mLeew==" 1622 | } 1623 | } 1624 | }, 1625 | "serve-static": { 1626 | "version": "1.13.2", 1627 | "resolved": "https://registry.npmjs.org/serve-static/-/serve-static-1.13.2.tgz", 1628 | "integrity": "sha512-p/tdJrO4U387R9oMjb1oj7qSMaMfmOyd4j9hOFoxZe2baQszgHcSWjuya/CiT5kgZZKRudHNOA0pYXOl8rQ5nw==", 1629 | "requires": { 1630 | "encodeurl": "~1.0.2", 1631 | "escape-html": "~1.0.3", 1632 | "parseurl": "~1.3.2", 1633 | "send": "0.16.2" 1634 | } 1635 | }, 1636 | "setprototypeof": { 1637 | "version": "1.1.0", 1638 | "resolved": "https://registry.npmjs.org/setprototypeof/-/setprototypeof-1.1.0.tgz", 1639 | "integrity": "sha512-BvE/TwpZX4FXExxOxZyRGQQv651MSwmWKZGqvmPcRIjDqWub67kTKuIMx43cZZrS/cBBzwBcNDWoFxt2XEFIpQ==" 1640 | }, 1641 | "shebang-command": { 1642 | "version": "1.2.0", 1643 | "resolved": "https://registry.npmjs.org/shebang-command/-/shebang-command-1.2.0.tgz", 1644 | "integrity": "sha1-RKrGW2lbAzmJaMOfNj/uXer98eo=", 1645 | "requires": { 1646 | "shebang-regex": "^1.0.0" 1647 | } 1648 | }, 1649 | "shebang-regex": { 1650 | "version": "1.0.0", 1651 | "resolved": "https://registry.npmjs.org/shebang-regex/-/shebang-regex-1.0.0.tgz", 1652 | "integrity": "sha1-2kL0l0DAtC2yypcoVxyxkMmO/qM=" 1653 | }, 1654 | "signal-exit": { 1655 | "version": "3.0.2", 1656 | "resolved": "https://registry.npmjs.org/signal-exit/-/signal-exit-3.0.2.tgz", 1657 | "integrity": "sha1-tf3AjxKH6hF4Yo5BXiUTK3NkbG0=" 1658 | }, 1659 | "slice-ansi": { 1660 | "version": "1.0.0", 1661 | "resolved": "https://registry.npmjs.org/slice-ansi/-/slice-ansi-1.0.0.tgz", 1662 | "integrity": "sha512-POqxBK6Lb3q6s047D/XsDVNPnF9Dl8JSaqe9h9lURl0OdNqy/ujDrOiIHtsqXMGbWWTIomRzAMaTyawAU//Reg==", 1663 | "requires": { 1664 | "is-fullwidth-code-point": "^2.0.0" 1665 | } 1666 | }, 1667 | "sliced": { 1668 | "version": "1.0.1", 1669 | "resolved": "https://registry.npmjs.org/sliced/-/sliced-1.0.1.tgz", 1670 | "integrity": "sha1-CzpmK10Ewxd7GSa+qCsD+Dei70E=" 1671 | }, 1672 | "spawn-command": { 1673 | "version": "0.0.2-1", 1674 | "resolved": "https://registry.npmjs.org/spawn-command/-/spawn-command-0.0.2-1.tgz", 1675 | "integrity": "sha1-YvXpRmmBwbeW3Fkpk34RycaSG9A=" 1676 | }, 1677 | "spdx-correct": { 1678 | "version": "3.0.0", 1679 | "resolved": "https://registry.npmjs.org/spdx-correct/-/spdx-correct-3.0.0.tgz", 1680 | "integrity": "sha512-N19o9z5cEyc8yQQPukRCZ9EUmb4HUpnrmaL/fxS2pBo2jbfcFRVuFZ/oFC+vZz0MNNk0h80iMn5/S6qGZOL5+g==", 1681 | "requires": { 1682 | "spdx-expression-parse": "^3.0.0", 1683 | "spdx-license-ids": "^3.0.0" 1684 | } 1685 | }, 1686 | "spdx-exceptions": { 1687 | "version": "2.1.0", 1688 | "resolved": "https://registry.npmjs.org/spdx-exceptions/-/spdx-exceptions-2.1.0.tgz", 1689 | "integrity": "sha512-4K1NsmrlCU1JJgUrtgEeTVyfx8VaYea9J9LvARxhbHtVtohPs/gFGG5yy49beySjlIMhhXZ4QqujIZEfS4l6Cg==" 1690 | }, 1691 | "spdx-expression-parse": { 1692 | "version": "3.0.0", 1693 | "resolved": "https://registry.npmjs.org/spdx-expression-parse/-/spdx-expression-parse-3.0.0.tgz", 1694 | "integrity": "sha512-Yg6D3XpRD4kkOmTpdgbUiEJFKghJH03fiC1OPll5h/0sO6neh2jqRDVHOQ4o/LMea0tgCkbMgea5ip/e+MkWyg==", 1695 | "requires": { 1696 | "spdx-exceptions": "^2.1.0", 1697 | "spdx-license-ids": "^3.0.0" 1698 | } 1699 | }, 1700 | "spdx-license-ids": { 1701 | "version": "3.0.0", 1702 | "resolved": "https://registry.npmjs.org/spdx-license-ids/-/spdx-license-ids-3.0.0.tgz", 1703 | "integrity": "sha512-2+EPwgbnmOIl8HjGBXXMd9NAu02vLjOO1nWw4kmeRDFyHn+M/ETfHxQUK0oXg8ctgVnl9t3rosNVsZ1jG61nDA==" 1704 | }, 1705 | "sprintf-js": { 1706 | "version": "1.0.3", 1707 | "resolved": "https://registry.npmjs.org/sprintf-js/-/sprintf-js-1.0.3.tgz", 1708 | "integrity": "sha1-BOaSb2YolTVPPdAVIDYzuFcpfiw=" 1709 | }, 1710 | "statuses": { 1711 | "version": "1.5.0", 1712 | "resolved": "https://registry.npmjs.org/statuses/-/statuses-1.5.0.tgz", 1713 | "integrity": "sha1-Fhx9rBd2Wf2YEfQ3cfqZOBR4Yow=" 1714 | }, 1715 | "string-width": { 1716 | "version": "2.1.1", 1717 | "resolved": "https://registry.npmjs.org/string-width/-/string-width-2.1.1.tgz", 1718 | "integrity": "sha512-nOqH59deCq9SRHlxq1Aw85Jnt4w6KvLKqWVik6oA9ZklXLNIOlqg4F2yrT1MVaTjAqvVwdfeZ7w7aCvJD7ugkw==", 1719 | "requires": { 1720 | "is-fullwidth-code-point": "^2.0.0", 1721 | "strip-ansi": "^4.0.0" 1722 | } 1723 | }, 1724 | "string_decoder": { 1725 | "version": "1.1.1", 1726 | "resolved": "https://registry.npmjs.org/string_decoder/-/string_decoder-1.1.1.tgz", 1727 | "integrity": "sha512-n/ShnvDi6FHbbVfviro+WojiFzv+s8MPMHBczVePfUpDJLwoLT0ht1l4YwBCbi8pJAveEEdnkHyPyTP/mzRfwg==", 1728 | "requires": { 1729 | "safe-buffer": "~5.1.0" 1730 | } 1731 | }, 1732 | "strip-ansi": { 1733 | "version": "4.0.0", 1734 | "resolved": "https://registry.npmjs.org/strip-ansi/-/strip-ansi-4.0.0.tgz", 1735 | "integrity": "sha1-qEeQIusaw2iocTibY1JixQXuNo8=", 1736 | "requires": { 1737 | "ansi-regex": "^3.0.0" 1738 | }, 1739 | "dependencies": { 1740 | "ansi-regex": { 1741 | "version": "3.0.0", 1742 | "resolved": "https://registry.npmjs.org/ansi-regex/-/ansi-regex-3.0.0.tgz", 1743 | "integrity": "sha1-7QMXwyIGT3lGbAKWa922Bas32Zg=" 1744 | } 1745 | } 1746 | }, 1747 | "strip-bom": { 1748 | "version": "3.0.0", 1749 | "resolved": "https://registry.npmjs.org/strip-bom/-/strip-bom-3.0.0.tgz", 1750 | "integrity": "sha1-IzTBjpx1n3vdVv3vfprj1YjmjtM=" 1751 | }, 1752 | "strip-json-comments": { 1753 | "version": "2.0.1", 1754 | "resolved": "https://registry.npmjs.org/strip-json-comments/-/strip-json-comments-2.0.1.tgz", 1755 | "integrity": "sha1-PFMZQukIwml8DsNEhYwobHygpgo=" 1756 | }, 1757 | "supports-color": { 1758 | "version": "2.0.0", 1759 | "resolved": "https://registry.npmjs.org/supports-color/-/supports-color-2.0.0.tgz", 1760 | "integrity": "sha1-U10EXOa2Nj+kARcIRimZXp3zJMc=" 1761 | }, 1762 | "table": { 1763 | "version": "4.0.3", 1764 | "resolved": "https://registry.npmjs.org/table/-/table-4.0.3.tgz", 1765 | "integrity": "sha512-S7rnFITmBH1EnyKcvxBh1LjYeQMmnZtCXSEbHcH6S0NoKit24ZuFO/T1vDcLdYsLQkM188PVVhQmzKIuThNkKg==", 1766 | "requires": { 1767 | "ajv": "^6.0.1", 1768 | "ajv-keywords": "^3.0.0", 1769 | "chalk": "^2.1.0", 1770 | "lodash": "^4.17.4", 1771 | "slice-ansi": "1.0.0", 1772 | "string-width": "^2.1.1" 1773 | }, 1774 | "dependencies": { 1775 | "ajv": { 1776 | "version": "6.5.2", 1777 | "resolved": "https://registry.npmjs.org/ajv/-/ajv-6.5.2.tgz", 1778 | "integrity": "sha512-hOs7GfvI6tUI1LfZddH82ky6mOMyTuY0mk7kE2pWpmhhUSkumzaTO5vbVwij39MdwPQWCV4Zv57Eo06NtL/GVA==", 1779 | "requires": { 1780 | "fast-deep-equal": "^2.0.1", 1781 | "fast-json-stable-stringify": "^2.0.0", 1782 | "json-schema-traverse": "^0.4.1", 1783 | "uri-js": "^4.2.1" 1784 | } 1785 | }, 1786 | "fast-deep-equal": { 1787 | "version": "2.0.1", 1788 | "resolved": "https://registry.npmjs.org/fast-deep-equal/-/fast-deep-equal-2.0.1.tgz", 1789 | "integrity": "sha1-ewUhjd+WZ79/Nwv3/bLLFf3Qqkk=" 1790 | }, 1791 | "json-schema-traverse": { 1792 | "version": "0.4.1", 1793 | "resolved": "https://registry.npmjs.org/json-schema-traverse/-/json-schema-traverse-0.4.1.tgz", 1794 | "integrity": "sha512-xbbCH5dCYU5T8LcEhhuh7HJ88HXuW3qsI3Y0zOZFKfZEHcpWiHU/Jxzk629Brsab/mMiHQti9wMP+845RPe3Vg==" 1795 | } 1796 | } 1797 | }, 1798 | "text-table": { 1799 | "version": "0.2.0", 1800 | "resolved": "https://registry.npmjs.org/text-table/-/text-table-0.2.0.tgz", 1801 | "integrity": "sha1-f17oI66AUgfACvLfSoTsP8+lcLQ=" 1802 | }, 1803 | "through": { 1804 | "version": "2.3.8", 1805 | "resolved": "https://registry.npmjs.org/through/-/through-2.3.8.tgz", 1806 | "integrity": "sha1-DdTJ/6q8NXlgsbckEV1+Doai4fU=" 1807 | }, 1808 | "tmp": { 1809 | "version": "0.0.33", 1810 | "resolved": "https://registry.npmjs.org/tmp/-/tmp-0.0.33.tgz", 1811 | "integrity": "sha512-jRCJlojKnZ3addtTOjdIqoRuPEKBvNXcGYqzO6zWZX8KfKEpnGY5jfggJQ3EjKuu8D4bJRr0y+cYJFmYbImXGw==", 1812 | "requires": { 1813 | "os-tmpdir": "~1.0.2" 1814 | } 1815 | }, 1816 | "tree-kill": { 1817 | "version": "1.2.0", 1818 | "resolved": "https://registry.npmjs.org/tree-kill/-/tree-kill-1.2.0.tgz", 1819 | "integrity": "sha512-DlX6dR0lOIRDFxI0mjL9IYg6OTncLm/Zt+JiBhE5OlFcAR8yc9S7FFXU9so0oda47frdM/JFsk7UjNt9vscKcg==" 1820 | }, 1821 | "type-check": { 1822 | "version": "0.3.2", 1823 | "resolved": "https://registry.npmjs.org/type-check/-/type-check-0.3.2.tgz", 1824 | "integrity": "sha1-WITKtRLPHTVeP7eE8wgEsrUg23I=", 1825 | "requires": { 1826 | "prelude-ls": "~1.1.2" 1827 | } 1828 | }, 1829 | "type-is": { 1830 | "version": "1.6.16", 1831 | "resolved": "https://registry.npmjs.org/type-is/-/type-is-1.6.16.tgz", 1832 | "integrity": "sha512-HRkVv/5qY2G6I8iab9cI7v1bOIdhm94dVjQCPFElW9W+3GeDOSHmy2EBYe4VTApuzolPcmgFTN3ftVJRKR2J9Q==", 1833 | "requires": { 1834 | "media-typer": "0.3.0", 1835 | "mime-types": "~2.1.18" 1836 | } 1837 | }, 1838 | "typedarray": { 1839 | "version": "0.0.6", 1840 | "resolved": "https://registry.npmjs.org/typedarray/-/typedarray-0.0.6.tgz", 1841 | "integrity": "sha1-hnrHTjhkGHsdPUfZlqeOxciDB3c=" 1842 | }, 1843 | "unpipe": { 1844 | "version": "1.0.0", 1845 | "resolved": "https://registry.npmjs.org/unpipe/-/unpipe-1.0.0.tgz", 1846 | "integrity": "sha1-sr9O6FFKrmFltIF4KdIbLvSZBOw=" 1847 | }, 1848 | "uri-js": { 1849 | "version": "4.2.2", 1850 | "resolved": "https://registry.npmjs.org/uri-js/-/uri-js-4.2.2.tgz", 1851 | "integrity": "sha512-KY9Frmirql91X2Qgjry0Wd4Y+YTdrdZheS8TFwvkbLWf/G5KNJDCh6pKL5OZctEW4+0Baa5idK2ZQuELRwPznQ==", 1852 | "requires": { 1853 | "punycode": "^2.1.0" 1854 | } 1855 | }, 1856 | "util-deprecate": { 1857 | "version": "1.0.2", 1858 | "resolved": "https://registry.npmjs.org/util-deprecate/-/util-deprecate-1.0.2.tgz", 1859 | "integrity": "sha1-RQ1Nyfpw3nMnYvvS1KKJgUGaDM8=" 1860 | }, 1861 | "utils-merge": { 1862 | "version": "1.0.1", 1863 | "resolved": "https://registry.npmjs.org/utils-merge/-/utils-merge-1.0.1.tgz", 1864 | "integrity": "sha1-n5VxD1CiZ5R7LMwSR0HBAoQn5xM=" 1865 | }, 1866 | "validate-npm-package-license": { 1867 | "version": "3.0.4", 1868 | "resolved": "https://registry.npmjs.org/validate-npm-package-license/-/validate-npm-package-license-3.0.4.tgz", 1869 | "integrity": "sha512-DpKm2Ui/xN7/HQKCtpZxoRWBhZ9Z0kqtygG8XCgNQ8ZlDnxuQmWhj566j8fN4Cu3/JmbhsDo7fcAJq4s9h27Ew==", 1870 | "requires": { 1871 | "spdx-correct": "^3.0.0", 1872 | "spdx-expression-parse": "^3.0.0" 1873 | } 1874 | }, 1875 | "validator": { 1876 | "version": "10.5.0", 1877 | "resolved": "https://registry.npmjs.org/validator/-/validator-10.5.0.tgz", 1878 | "integrity": "sha512-6OOi+eV2mOxCFLq0f2cJDrdB6lrtLXEUxabhNRGjgOLT/l3SSll9J49Cl+LIloUqkWWTPraK/mucEQ3dc2jStQ==" 1879 | }, 1880 | "vary": { 1881 | "version": "1.1.2", 1882 | "resolved": "https://registry.npmjs.org/vary/-/vary-1.1.2.tgz", 1883 | "integrity": "sha1-IpnwLG3tMNSllhsLn3RSShj2NPw=" 1884 | }, 1885 | "which": { 1886 | "version": "1.3.1", 1887 | "resolved": "https://registry.npmjs.org/which/-/which-1.3.1.tgz", 1888 | "integrity": "sha512-HxJdYWq1MTIQbJ3nw0cqssHoTNU267KlrDuGZ1WYlxDStUtKUhOaJmh112/TZmHxxUfuJqPXSOm7tDyas0OSIQ==", 1889 | "requires": { 1890 | "isexe": "^2.0.0" 1891 | } 1892 | }, 1893 | "wordwrap": { 1894 | "version": "1.0.0", 1895 | "resolved": "https://registry.npmjs.org/wordwrap/-/wordwrap-1.0.0.tgz", 1896 | "integrity": "sha1-J1hIEIkUVqQXHI0CJkQa3pDLyus=" 1897 | }, 1898 | "wrappy": { 1899 | "version": "1.0.2", 1900 | "resolved": "https://registry.npmjs.org/wrappy/-/wrappy-1.0.2.tgz", 1901 | "integrity": "sha1-tSQ9jz7BqjXxNkYFvA0QNuMKtp8=" 1902 | }, 1903 | "write": { 1904 | "version": "0.2.1", 1905 | "resolved": "https://registry.npmjs.org/write/-/write-0.2.1.tgz", 1906 | "integrity": "sha1-X8A4KOJkzqP+kUVUdvejxWbLB1c=", 1907 | "requires": { 1908 | "mkdirp": "^0.5.1" 1909 | } 1910 | }, 1911 | "yallist": { 1912 | "version": "2.1.2", 1913 | "resolved": "https://registry.npmjs.org/yallist/-/yallist-2.1.2.tgz", 1914 | "integrity": "sha1-HBH5IY8HYImkfdUS+TxmmaaoHVI=" 1915 | } 1916 | } 1917 | } 1918 | -------------------------------------------------------------------------------- /package.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "mern", 3 | "version": "1.0.0", 4 | "description": "", 5 | "main": "server.js", 6 | "scripts": { 7 | "client-install": "npm install --prefix client", 8 | "start": "node server.js", 9 | "server": "nodemon server", 10 | "client": "npm start --prefix client", 11 | "dev": "concurrently \"npm run server\" \"npm run client\"" 12 | }, 13 | "author": "Niccu Bagonoc", 14 | "license": "ISC", 15 | "dependencies": { 16 | "bcryptjs": "^2.4.3", 17 | "body-parser": "^1.18.3", 18 | "concurrently": "^3.6.1", 19 | "cors": "^2.8.4", 20 | "express": "^4.16.3", 21 | "jsonwebtoken": "^8.3.0", 22 | "mongoose": "^5.1.5", 23 | "passport": "^0.4.0", 24 | "passport-jwt": "^4.0.0", 25 | "validator": "^10.5.0" 26 | } 27 | } 28 | -------------------------------------------------------------------------------- /routes/api/auth.js: -------------------------------------------------------------------------------- 1 | const express = require("express"); 2 | const router = express.Router(); 3 | const mongoose = require("mongoose"); 4 | const bcrypt = require("bcryptjs"); 5 | const jwt = require("jsonwebtoken"); 6 | // const passport = require("passport"); 7 | const validator = require("validator"); 8 | const isEmpty = require("../../utils/isEmpty"); 9 | const key = require("../../config/dbSecretKeys"); 10 | 11 | // bring in user model 12 | require("../../models/User"); 13 | const User = mongoose.model("users"); 14 | 15 | // POST | api/auth/register 16 | // register process 17 | router.post("/register", (req, res, next) => { 18 | const { errors, isValid } = validateRegister(req.body); 19 | 20 | // Register validation 21 | function validateRegister(data) { 22 | let errors = {}; 23 | 24 | // check if data send is "" 25 | data.name = !isEmpty(data.name) ? data.name : ""; 26 | data.email = !isEmpty(data.email) ? data.email : ""; 27 | data.password = !isEmpty(data.password) ? data.password : ""; 28 | data.password2 = !isEmpty(data.password2) ? data.password2 : ""; 29 | 30 | // validation using validator 31 | if (validator.isEmpty(data.name)) { 32 | errors.name = "name is required"; 33 | } 34 | if (validator.isEmpty(data.email)) { 35 | errors.email = "email is required"; 36 | } 37 | if (!validator.isEmail(data.email)) { 38 | errors.email = "email is not valid"; 39 | } 40 | if (validator.isEmpty(data.password)) { 41 | errors.password = "password is required"; 42 | } 43 | if (validator.isEmpty(data.password2)) { 44 | errors.password2 = "confirm password is required"; 45 | } 46 | if (!validator.equals(data.password, data.password2)) { 47 | errors.password2 = "confirm password did not match"; 48 | } 49 | return { 50 | errors, 51 | isValid: isEmpty(errors) 52 | }; 53 | } 54 | 55 | if (!isValid) { 56 | return res.status(400).json(errors); 57 | } else { 58 | User.findOne({ email: req.body.email }) 59 | .then(user => { 60 | if (user) { 61 | errors.email = "Email already exists"; 62 | return res.status(400).json(errors); 63 | } else { 64 | const newUser = new User({ 65 | name: req.body.name, 66 | email: req.body.email, 67 | password: req.body.password 68 | }); 69 | bcrypt.genSalt(10, (err, salt) => { 70 | bcrypt.hash(newUser.password, salt, (err, hash) => { 71 | if (err) { 72 | res.json({ success: false, msg: "Failed to register user" }); 73 | } else { 74 | newUser.password = hash; 75 | newUser 76 | .save() 77 | .then(user => { 78 | res.json({ success: true, msg: "User registered" }); 79 | }) 80 | .catch(ex => { 81 | return res.status(500).send("Something went wrong"); 82 | }); 83 | } 84 | }); 85 | }); 86 | } 87 | }) 88 | .catch(ex => { 89 | return res.status(500).send("Something went wrong"); 90 | }); 91 | } 92 | }); 93 | 94 | // POST | api/auth/login 95 | // Login process 96 | router.post("/login", (req, res, next) => { 97 | const { errors, isValid } = validateLogin(req.body); 98 | 99 | // Login validation 100 | function validateLogin(data) { 101 | let errors = {}; 102 | 103 | if (validator.isEmpty(data.email)) { 104 | errors.email = "email is required"; 105 | } 106 | if (!validator.isEmail(data.email)) { 107 | errors.email = "email is not valid"; 108 | } 109 | if (validator.isEmpty(data.password)) { 110 | errors.password = "password is required"; 111 | } 112 | return { 113 | errors, 114 | isValid: isEmpty(errors) 115 | }; 116 | } 117 | 118 | if (!isValid) { 119 | return res.status(400).json(errors); 120 | } else { 121 | const email = req.body.email; 122 | const password = req.body.password; 123 | 124 | User.findOne({ email }) 125 | .then(user => { 126 | // check for user 127 | if (!user) { 128 | errors.email = "User does not exist"; 129 | return res.status(400).json(errors); 130 | } else { 131 | // check password 132 | bcrypt 133 | .compare(password, user.password) 134 | .then(isMatch => { 135 | if (isMatch) { 136 | // user matched 137 | const payload = { 138 | id: user.id, 139 | name: user.name, 140 | role: user.role 141 | }; 142 | // create JWT payload 143 | // sign token 144 | jwt.sign( 145 | payload, 146 | key.secretOrKey, 147 | { expiresIn: 86400 }, 148 | (err, token) => { 149 | res.json({ 150 | success: true, 151 | token: "JWT " + token 152 | }); 153 | } 154 | ); 155 | } else { 156 | errors.password = "Password incorrect"; 157 | return res.status(400).json(errors); 158 | } 159 | }) 160 | .catch(ex => { 161 | return res.status(500).send("Something went wrong"); 162 | }); 163 | } 164 | }) 165 | .catch(ex => { 166 | return res.status(500).send("Something went wrong"); 167 | }); 168 | } 169 | }); 170 | 171 | // test if the backend is secured 172 | // router.get( 173 | // "/test", 174 | // passport.authenticate("jwt", { session: false }), 175 | // (req, res) => { 176 | // res.json({ message: "you are authorized" }); 177 | // } 178 | // ); 179 | 180 | module.exports = router; 181 | -------------------------------------------------------------------------------- /routes/api/users.js: -------------------------------------------------------------------------------- 1 | const express = require("express"); 2 | const router = express.Router(); 3 | const mongoose = require("mongoose"); 4 | const passport = require("passport"); 5 | const isAdmin = require("../../guards/isAdmin"); 6 | const isModerator = require("../../guards/isModerator"); 7 | 8 | // bring in user model 9 | require("../../models/User"); 10 | const User = mongoose.model("users"); 11 | 12 | // GET | api/users/profile 13 | // view current user profile 14 | router.get( 15 | "/profile", 16 | passport.authenticate("jwt", { session: false }), 17 | (req, res) => { 18 | User.findOne({ _id: req.user.id }) 19 | .then(user => { 20 | if (user) { 21 | res.json({ success: true, user }); 22 | } else { 23 | res.json({ success: false, message: "User not found" }); 24 | } 25 | }) 26 | .catch(ex => { 27 | return res.status(500).send("Something went wrong"); 28 | }); 29 | } 30 | ); 31 | 32 | // GET | api/users 33 | // view users list 34 | router.get( 35 | "/", 36 | [passport.authenticate("jwt", { session: false }), isAdmin], 37 | (req, res) => { 38 | User.find({ role: "subscriber" }) 39 | .then(users => { 40 | if (users) { 41 | res.json({ success: true, users }); 42 | } else { 43 | res.json({ success: false, message: "Users not found" }); 44 | } 45 | }) 46 | .catch(ex => { 47 | return res.status(500).send("Something went wrong"); 48 | }); 49 | } 50 | ); 51 | 52 | // GET | api/users/view/:id 53 | // get user 54 | router.get( 55 | "/show/:id", 56 | [passport.authenticate("jwt", { session: false }), isAdmin], 57 | (req, res) => { 58 | User.findOne({ _id: req.params.id }) 59 | .then(user => { 60 | if (user) { 61 | res.json({ success: true, user }); 62 | } else { 63 | res.json({ success: false, message: "User not found" }); 64 | } 65 | }) 66 | .catch(ex => { 67 | return res.status(500).send("Something went wrong"); 68 | }); 69 | } 70 | ); 71 | 72 | // PUT | api/users/update 73 | // update user 74 | router.put( 75 | "/update/:id", 76 | [passport.authenticate("jwt", { session: false }), isAdmin], 77 | (req, res) => { 78 | if (!req.body.name) { 79 | res.json({ success: false, msg: "name is required" }); 80 | } else { 81 | User.findOne({ _id: req.params.id }) 82 | .then(user => { 83 | if (user) { 84 | user.name = req.body.name; 85 | user 86 | .save() 87 | .then(userUpdated => { 88 | if (userUpdated) { 89 | res.json({ success: true, message: "User updated!" }); 90 | } else { 91 | res.json({ 92 | success: false, 93 | message: "User was not updated. Please try again" 94 | }); 95 | } 96 | }) 97 | .catch(ex => { 98 | return res.status(500).send("Something went wrong"); 99 | }); 100 | } else { 101 | res.json({ success: false, message: "User not found" }); 102 | } 103 | }) 104 | .catch(ex => { 105 | return res.status(500).send("Something went wrong"); 106 | }); 107 | } 108 | } 109 | ); 110 | 111 | // DELETE | api/users/delete/:id 112 | // delete user 113 | router.delete( 114 | "/delete/:id", 115 | [passport.authenticate("jwt", { session: false }), isAdmin], 116 | (req, res) => { 117 | User.findOne({ _id: req.params.id }) 118 | .then(user => { 119 | if (user) { 120 | user 121 | .remove() 122 | .then(userDeleted => { 123 | if (userDeleted) { 124 | res.json({ success: true, message: "User deleted" }); 125 | } else { 126 | res.json({ success: false, message: "User was not deleted" }); 127 | } 128 | }) 129 | .catch(ex => { 130 | return res.status(500).send("Something went wrong"); 131 | }); 132 | } else { 133 | res.json({ success: false, message: "User not found" }); 134 | } 135 | }) 136 | .catch(ex => { 137 | return res.status(500).send("Something went wrong"); 138 | }); 139 | } 140 | ); 141 | 142 | module.exports = router; 143 | -------------------------------------------------------------------------------- /server.js: -------------------------------------------------------------------------------- 1 | const express = require("express"); 2 | const mongoose = require("mongoose"); 3 | const passport = require("passport"); 4 | const bodyParser = require("body-parser"); 5 | const path = require("path"); 6 | const cors = require("cors"); 7 | 8 | // INITIALIZE APP 9 | const app = express(); 10 | 11 | // MODELS 12 | require("./models/User"); 13 | 14 | // MIDDLEWARES 15 | // cors 16 | app.use(cors()); 17 | // body parser middleware 18 | app.use(bodyParser.urlencoded({ extended: false })); 19 | app.use(bodyParser.json()); 20 | // passport middleware 21 | app.use(passport.initialize()); 22 | app.use(passport.session()); 23 | 24 | // CONFIGS 25 | // mongoDB; 26 | const db = require("./config/dbSecretKeys").mongoURI; 27 | // Passport config 28 | require("./config/passport")(passport); 29 | 30 | // CONNECT TO DB 31 | mongoose 32 | .connect(db) 33 | .then(() => console.log("we are connected to our DB")) 34 | .catch(err => console.log(err)); 35 | 36 | // ROUTES 37 | const auth = require("./routes/api/auth"); 38 | const users = require("./routes/api/users"); 39 | 40 | // USE ROUTES 41 | app.use("/api/auth", auth); 42 | app.use("/api/users", users); 43 | 44 | // STATIC FOLDER 45 | app.use(express.static(path.join(__dirname, "public"))); 46 | 47 | // USE ANGULAR AS FRONTEND 48 | app.get("*", (req, res) => { 49 | res.sendFile(path.join(__dirname, "public", "index.html")); 50 | }); 51 | 52 | // SET PORT 53 | const port = process.env.PORT || 5000; 54 | app.listen(port, () => { 55 | console.log(`we are live at ${port}`); 56 | }); 57 | -------------------------------------------------------------------------------- /utils/isEmpty.js: -------------------------------------------------------------------------------- 1 | const isEmpty = value => 2 | value === undefined || 3 | value === null || 4 | (typeof value === "object" && Object.keys(value).length === 0) || 5 | (typeof value === "string" && value.trim().length === 0); 6 | module.exports = isEmpty; 7 | --------------------------------------------------------------------------------