├── public ├── favicon.ico ├── manifest.json └── index.html ├── src ├── index.js ├── App.test.js ├── index.css ├── App.css ├── components │ ├── navbar.component.js │ ├── create-user.component.js │ ├── exercises-list.component.js │ ├── create-exercise.component.js │ └── edit-exercise.component.js ├── App.js ├── logo.svg └── serviceWorker.js ├── README.md ├── backend ├── models │ ├── user.model.js │ └── exercise.model.js ├── package.json ├── routes │ ├── users.js │ └── exercises.js ├── server.js └── package-lock.json ├── .gitignore └── package.json /public/favicon.ico: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/beaucarnes/mern-exercise-tracker-mongodb/HEAD/public/favicon.ico -------------------------------------------------------------------------------- /src/index.js: -------------------------------------------------------------------------------- 1 | import React from 'react'; 2 | import ReactDOM from 'react-dom'; 3 | import App from './App'; 4 | 5 | ReactDOM.render(, document.getElementById('root')); -------------------------------------------------------------------------------- /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 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | Code for MERN tutorial. 2 | 3 | Video Tutorial: https://www.youtube.com/watch?v=7CqJlxBYj-M 4 | 5 | Article Tutorial: https://medium.com/@beaucarnes/learn-the-mern-stack-by-building-an-exercise-tracker-mern-tutorial-59c13c1237a1 6 | 7 | This project was bootstrapped with [Create React App](https://github.com/facebook/create-react-app). 8 | -------------------------------------------------------------------------------- /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": ".", 12 | "display": "standalone", 13 | "theme_color": "#000000", 14 | "background_color": "#ffffff" 15 | } 16 | -------------------------------------------------------------------------------- /backend/models/user.model.js: -------------------------------------------------------------------------------- 1 | const mongoose = require('mongoose'); 2 | 3 | const Schema = mongoose.Schema; 4 | 5 | const userSchema = new Schema({ 6 | username: { 7 | type: String, 8 | required: true, 9 | unique: true, 10 | trim: true, 11 | minlength: 3 12 | }, 13 | }, { 14 | timestamps: true, 15 | }); 16 | 17 | const User = mongoose.model('User', userSchema); 18 | 19 | module.exports = User; -------------------------------------------------------------------------------- /src/index.css: -------------------------------------------------------------------------------- 1 | body { 2 | margin: 0; 3 | font-family: -apple-system, BlinkMacSystemFont, "Segoe UI", "Roboto", "Oxygen", 4 | "Ubuntu", "Cantarell", "Fira Sans", "Droid Sans", "Helvetica Neue", 5 | sans-serif; 6 | -webkit-font-smoothing: antialiased; 7 | -moz-osx-font-smoothing: grayscale; 8 | } 9 | 10 | code { 11 | font-family: source-code-pro, Menlo, Monaco, Consolas, "Courier New", 12 | monospace; 13 | } 14 | -------------------------------------------------------------------------------- /.gitignore: -------------------------------------------------------------------------------- 1 | # See https://help.github.com/articles/ignoring-files/ for more about ignoring files. 2 | 3 | # dependencies 4 | /node_modules 5 | backend/node_modules 6 | /.pnp 7 | .pnp.js 8 | 9 | # testing 10 | /coverage 11 | 12 | # production 13 | /build 14 | 15 | # misc 16 | .DS_Store 17 | .env.local 18 | .env.development.local 19 | .env.test.local 20 | .env.production.local 21 | 22 | npm-debug.log* 23 | yarn-debug.log* 24 | yarn-error.log* 25 | -------------------------------------------------------------------------------- /backend/package.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "backend", 3 | "version": "1.0.0", 4 | "description": "", 5 | "main": "index.js", 6 | "scripts": { 7 | "test": "echo \"Error: no test specified\" && exit 1" 8 | }, 9 | "keywords": [], 10 | "author": "", 11 | "license": "ISC", 12 | "dependencies": { 13 | "body-parser": "^1.19.0", 14 | "cors": "^2.8.5", 15 | "dotenv": "^8.0.0", 16 | "express": "^4.16.4", 17 | "mongoose": "^5.5.7" 18 | } 19 | } 20 | -------------------------------------------------------------------------------- /backend/models/exercise.model.js: -------------------------------------------------------------------------------- 1 | const mongoose = require('mongoose'); 2 | 3 | const Schema = mongoose.Schema; 4 | 5 | const exerciseSchema = new Schema({ 6 | username: { type: String, required: true }, 7 | description: { type: String, required: true }, 8 | duration: { type: Number, required: true }, 9 | date: { type: Date, required: true }, 10 | }, { 11 | timestamps: true, 12 | }); 13 | 14 | const Exercise = mongoose.model('Exercise', exerciseSchema); 15 | 16 | module.exports = Exercise; -------------------------------------------------------------------------------- /public/index.html: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | 7 | 8 | 9 | 10 | Exercise Tracker 11 | 12 | 13 | 14 |
15 | 16 | 17 | 18 | -------------------------------------------------------------------------------- /backend/routes/users.js: -------------------------------------------------------------------------------- 1 | const router = require('express').Router(); 2 | let User = require('../models/user.model'); 3 | 4 | router.route('/').get((req, res) => { 5 | User.find() 6 | .then(users => res.json(users)) 7 | .catch(err => res.status(400).json('Error: ' + err)); 8 | }); 9 | 10 | router.route('/add').post((req, res) => { 11 | const username = req.body.username; 12 | 13 | const newUser = new User({username}); 14 | 15 | newUser.save() 16 | .then(() => res.json('User added!')) 17 | .catch(err => res.status(400).json('Error: ' + err)); 18 | }); 19 | 20 | module.exports = router; -------------------------------------------------------------------------------- /src/App.css: -------------------------------------------------------------------------------- 1 | .App { 2 | text-align: center; 3 | } 4 | 5 | .App-logo { 6 | animation: App-logo-spin infinite 20s linear; 7 | height: 40vmin; 8 | pointer-events: none; 9 | } 10 | 11 | .App-header { 12 | background-color: #282c34; 13 | min-height: 100vh; 14 | display: flex; 15 | flex-direction: column; 16 | align-items: center; 17 | justify-content: center; 18 | font-size: calc(10px + 2vmin); 19 | color: white; 20 | } 21 | 22 | .App-link { 23 | color: #61dafb; 24 | } 25 | 26 | @keyframes App-logo-spin { 27 | from { 28 | transform: rotate(0deg); 29 | } 30 | to { 31 | transform: rotate(360deg); 32 | } 33 | } 34 | -------------------------------------------------------------------------------- /backend/server.js: -------------------------------------------------------------------------------- 1 | const express = require('express'); 2 | const cors = require('cors'); 3 | const mongoose = require('mongoose'); 4 | 5 | require('dotenv').config(); 6 | 7 | const app = express(); 8 | const port = process.env.PORT || 5000; 9 | 10 | app.use(cors()); 11 | app.use(express.json()); 12 | 13 | const uri = process.env.ATLAS_URI; 14 | mongoose.connect(uri, { useNewUrlParser: true, useCreateIndex: true } 15 | ); 16 | const connection = mongoose.connection; 17 | connection.once('open', () => { 18 | console.log("MongoDB database connection established successfully"); 19 | }) 20 | 21 | const exercisesRouter = require('./routes/exercises'); 22 | const usersRouter = require('./routes/users'); 23 | 24 | app.use('/exercises', exercisesRouter); 25 | app.use('/users', usersRouter); 26 | 27 | app.listen(port, () => { 28 | console.log(`Server is running on port: ${port}`); 29 | }); 30 | -------------------------------------------------------------------------------- /package.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "mern-tracker", 3 | "version": "0.1.0", 4 | "private": true, 5 | "dependencies": { 6 | "axios": "^0.18.0", 7 | "bootstrap": "^4.3.1", 8 | "react": "^16.8.6", 9 | "react-datepicker": "^2.5.0", 10 | "react-dom": "^16.8.6", 11 | "react-router-dom": "^5.0.0", 12 | "react-scripts": "3.0.1" 13 | }, 14 | "scripts": { 15 | "start": "react-scripts start", 16 | "build": "react-scripts build", 17 | "test": "react-scripts test", 18 | "eject": "react-scripts eject" 19 | }, 20 | "eslintConfig": { 21 | "extends": "react-app" 22 | }, 23 | "browserslist": { 24 | "production": [ 25 | ">0.2%", 26 | "not dead", 27 | "not op_mini all" 28 | ], 29 | "development": [ 30 | "last 1 chrome version", 31 | "last 1 firefox version", 32 | "last 1 safari version" 33 | ] 34 | } 35 | } 36 | -------------------------------------------------------------------------------- /src/components/navbar.component.js: -------------------------------------------------------------------------------- 1 | import React, { Component } from 'react'; 2 | import { Link } from 'react-router-dom'; 3 | 4 | export default class Navbar extends Component { 5 | 6 | render() { 7 | return ( 8 | 24 | ); 25 | } 26 | } -------------------------------------------------------------------------------- /src/App.js: -------------------------------------------------------------------------------- 1 | import React from 'react'; 2 | import "bootstrap/dist/css/bootstrap.min.css"; 3 | import { BrowserRouter as Router, Route} from "react-router-dom"; 4 | 5 | import Navbar from "./components/navbar.component" 6 | import ExercisesList from "./components/exercises-list.component"; 7 | import EditExercise from "./components/edit-exercise.component"; 8 | import CreateExercise from "./components/create-exercise.component"; 9 | import CreateUser from "./components/create-user.component"; 10 | 11 | function App() { 12 | return ( 13 | 14 |
15 | 16 |
17 | 18 | 19 | 20 | 21 |
22 |
23 | ); 24 | } 25 | 26 | export default App; 27 | -------------------------------------------------------------------------------- /src/components/create-user.component.js: -------------------------------------------------------------------------------- 1 | import React, { Component } from 'react'; 2 | import axios from 'axios'; 3 | 4 | export default class CreateUser extends Component { 5 | constructor(props) { 6 | super(props); 7 | 8 | this.onChangeUsername = this.onChangeUsername.bind(this); 9 | this.onSubmit = this.onSubmit.bind(this); 10 | 11 | this.state = { 12 | username: '' 13 | } 14 | } 15 | 16 | onChangeUsername(e) { 17 | this.setState({ 18 | username: e.target.value 19 | }) 20 | } 21 | 22 | onSubmit(e) { 23 | e.preventDefault(); 24 | 25 | const user = { 26 | username: this.state.username 27 | } 28 | 29 | console.log(user); 30 | 31 | axios.post('http://localhost:5000/users/add', user) 32 | .then(res => console.log(res.data)); 33 | 34 | this.setState({ 35 | username: '' 36 | }) 37 | } 38 | 39 | render() { 40 | return ( 41 |
42 |

Create New User

43 |
44 |
45 | 46 | 52 |
53 |
54 | 55 |
56 |
57 |
58 | ) 59 | } 60 | } -------------------------------------------------------------------------------- /backend/routes/exercises.js: -------------------------------------------------------------------------------- 1 | const router = require('express').Router(); 2 | let Exercise = require('../models/exercise.model'); 3 | 4 | router.route('/').get((req, res) => { 5 | Exercise.find() 6 | .then(exercises => res.json(exercises)) 7 | .catch(err => res.status(400).json('Error: ' + err)); 8 | }); 9 | 10 | router.route('/add').post((req, res) => { 11 | const username = req.body.username; 12 | const description = req.body.description; 13 | const duration = Number(req.body.duration); 14 | const date = Date.parse(req.body.date); 15 | 16 | const newExercise = new Exercise({ 17 | username, 18 | description, 19 | duration, 20 | date, 21 | }); 22 | 23 | newExercise.save() 24 | .then(() => res.json('Exercise added!')) 25 | .catch(err => res.status(400).json('Error: ' + err)); 26 | }); 27 | 28 | router.route('/:id').get((req, res) => { 29 | Exercise.findById(req.params.id) 30 | .then(exercise => res.json(exercise)) 31 | .catch(err => res.status(400).json('Error: ' + err)); 32 | }); 33 | 34 | router.route('/:id').delete((req, res) => { 35 | Exercise.findByIdAndDelete(req.params.id) 36 | .then(() => res.json('Exercise deleted.')) 37 | .catch(err => res.status(400).json('Error: ' + err)); 38 | }); 39 | 40 | router.route('/update/:id').post((req, res) => { 41 | Exercise.findById(req.params.id) 42 | .then(exercise => { 43 | exercise.username = req.body.username; 44 | exercise.description = req.body.description; 45 | exercise.duration = Number(req.body.duration); 46 | exercise.date = Date.parse(req.body.date); 47 | 48 | exercise.save() 49 | .then(() => res.json('Exercise updated!')) 50 | .catch(err => res.status(400).json('Error: ' + err)); 51 | }) 52 | .catch(err => res.status(400).json('Error: ' + err)); 53 | }); 54 | 55 | module.exports = router; -------------------------------------------------------------------------------- /src/components/exercises-list.component.js: -------------------------------------------------------------------------------- 1 | import React, { Component } from 'react'; 2 | import { Link } from 'react-router-dom'; 3 | import axios from 'axios'; 4 | 5 | const Exercise = props => ( 6 | 7 | {props.exercise.username} 8 | {props.exercise.description} 9 | {props.exercise.duration} 10 | {props.exercise.date.substring(0,10)} 11 | 12 | edit | { props.deleteExercise(props.exercise._id) }}>delete 13 | 14 | 15 | ) 16 | 17 | export default class ExercisesList extends Component { 18 | constructor(props) { 19 | super(props); 20 | 21 | this.deleteExercise = this.deleteExercise.bind(this) 22 | 23 | this.state = {exercises: []}; 24 | } 25 | 26 | componentDidMount() { 27 | axios.get('http://localhost:5000/exercises/') 28 | .then(response => { 29 | this.setState({ exercises: response.data }) 30 | }) 31 | .catch((error) => { 32 | console.log(error); 33 | }) 34 | } 35 | 36 | deleteExercise(id) { 37 | axios.delete('http://localhost:5000/exercises/'+id) 38 | .then(response => { console.log(response.data)}); 39 | 40 | this.setState({ 41 | exercises: this.state.exercises.filter(el => el._id !== id) 42 | }) 43 | } 44 | 45 | exerciseList() { 46 | return this.state.exercises.map(currentexercise => { 47 | return ; 48 | }) 49 | } 50 | 51 | render() { 52 | return ( 53 |
54 |

Logged Exercises

55 | 56 | 57 | 58 | 59 | 60 | 61 | 62 | 63 | 64 | 65 | 66 | { this.exerciseList() } 67 | 68 |
UsernameDescriptionDurationDateActions
69 |
70 | ) 71 | } 72 | } -------------------------------------------------------------------------------- /src/logo.svg: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | 7 | 8 | -------------------------------------------------------------------------------- /src/components/create-exercise.component.js: -------------------------------------------------------------------------------- 1 | import React, { Component } from 'react'; 2 | import axios from 'axios'; 3 | import DatePicker from 'react-datepicker'; 4 | import "react-datepicker/dist/react-datepicker.css"; 5 | 6 | export default class CreateExercise extends Component { 7 | constructor(props) { 8 | super(props); 9 | 10 | this.onChangeUsername = this.onChangeUsername.bind(this); 11 | this.onChangeDescription = this.onChangeDescription.bind(this); 12 | this.onChangeDuration = this.onChangeDuration.bind(this); 13 | this.onChangeDate = this.onChangeDate.bind(this); 14 | this.onSubmit = this.onSubmit.bind(this); 15 | 16 | this.state = { 17 | username: '', 18 | description: '', 19 | duration: 0, 20 | date: new Date(), 21 | users: [] 22 | } 23 | } 24 | 25 | componentDidMount() { 26 | axios.get('http://localhost:5000/users/') 27 | .then(response => { 28 | if (response.data.length > 0) { 29 | this.setState({ 30 | users: response.data.map(user => user.username), 31 | username: response.data[0].username 32 | }) 33 | } 34 | }) 35 | .catch((error) => { 36 | console.log(error); 37 | }) 38 | 39 | } 40 | 41 | onChangeUsername(e) { 42 | this.setState({ 43 | username: e.target.value 44 | }) 45 | } 46 | 47 | onChangeDescription(e) { 48 | this.setState({ 49 | description: e.target.value 50 | }) 51 | } 52 | 53 | onChangeDuration(e) { 54 | this.setState({ 55 | duration: e.target.value 56 | }) 57 | } 58 | 59 | onChangeDate(date) { 60 | this.setState({ 61 | date: date 62 | }) 63 | } 64 | 65 | onSubmit(e) { 66 | e.preventDefault(); 67 | 68 | const exercise = { 69 | username: this.state.username, 70 | description: this.state.description, 71 | duration: this.state.duration, 72 | date: this.state.date 73 | } 74 | 75 | console.log(exercise); 76 | 77 | axios.post('http://localhost:5000/exercises/add', exercise) 78 | .then(res => console.log(res.data)); 79 | 80 | window.location = '/'; 81 | } 82 | 83 | render() { 84 | return ( 85 |
86 |

Create New Exercise Log

87 |
88 |
89 | 90 | 104 |
105 |
106 | 107 | 113 |
114 |
115 | 116 | 122 |
123 |
124 | 125 |
126 | 130 |
131 |
132 | 133 |
134 | 135 |
136 |
137 |
138 | ) 139 | } 140 | } -------------------------------------------------------------------------------- /src/components/edit-exercise.component.js: -------------------------------------------------------------------------------- 1 | import React, { Component } from 'react'; 2 | import axios from 'axios'; 3 | import DatePicker from 'react-datepicker'; 4 | import "react-datepicker/dist/react-datepicker.css"; 5 | 6 | export default class EditExercise extends Component { 7 | constructor(props) { 8 | super(props); 9 | 10 | this.onChangeUsername = this.onChangeUsername.bind(this); 11 | this.onChangeDescription = this.onChangeDescription.bind(this); 12 | this.onChangeDuration = this.onChangeDuration.bind(this); 13 | this.onChangeDate = this.onChangeDate.bind(this); 14 | this.onSubmit = this.onSubmit.bind(this); 15 | 16 | this.state = { 17 | username: '', 18 | description: '', 19 | duration: 0, 20 | date: new Date(), 21 | users: [] 22 | } 23 | } 24 | 25 | componentDidMount() { 26 | axios.get('http://localhost:5000/exercises/'+this.props.match.params.id) 27 | .then(response => { 28 | this.setState({ 29 | username: response.data.username, 30 | description: response.data.description, 31 | duration: response.data.duration, 32 | date: new Date(response.data.date) 33 | }) 34 | }) 35 | .catch(function (error) { 36 | console.log(error); 37 | }) 38 | 39 | axios.get('http://localhost:5000/users/') 40 | .then(response => { 41 | if (response.data.length > 0) { 42 | this.setState({ 43 | users: response.data.map(user => user.username), 44 | }) 45 | } 46 | }) 47 | .catch((error) => { 48 | console.log(error); 49 | }) 50 | 51 | } 52 | 53 | onChangeUsername(e) { 54 | this.setState({ 55 | username: e.target.value 56 | }) 57 | } 58 | 59 | onChangeDescription(e) { 60 | this.setState({ 61 | description: e.target.value 62 | }) 63 | } 64 | 65 | onChangeDuration(e) { 66 | this.setState({ 67 | duration: e.target.value 68 | }) 69 | } 70 | 71 | onChangeDate(date) { 72 | this.setState({ 73 | date: date 74 | }) 75 | } 76 | 77 | onSubmit(e) { 78 | e.preventDefault(); 79 | 80 | const exercise = { 81 | username: this.state.username, 82 | description: this.state.description, 83 | duration: this.state.duration, 84 | date: this.state.date 85 | } 86 | 87 | console.log(exercise); 88 | 89 | axios.post('http://localhost:5000/exercises/update/' + this.props.match.params.id, exercise) 90 | .then(res => console.log(res.data)); 91 | 92 | window.location = '/'; 93 | } 94 | 95 | render() { 96 | return ( 97 |
98 |

Edit Exercise Log

99 |
100 |
101 | 102 | 116 |
117 |
118 | 119 | 125 |
126 |
127 | 128 | 134 |
135 |
136 | 137 |
138 | 142 |
143 |
144 | 145 |
146 | 147 |
148 |
149 |
150 | ) 151 | } 152 | } -------------------------------------------------------------------------------- /src/serviceWorker.js: -------------------------------------------------------------------------------- 1 | // This optional code is used to register a service worker. 2 | // register() is not called by default. 3 | 4 | // This lets the app load faster on subsequent visits in production, and gives 5 | // it offline capabilities. However, it also means that developers (and users) 6 | // will only see deployed updates on subsequent visits to a page, after all the 7 | // existing tabs open on the page have been closed, since previously cached 8 | // resources are updated in the background. 9 | 10 | // To learn more about the benefits of this model and instructions on how to 11 | // opt-in, read https://bit.ly/CRA-PWA 12 | 13 | const isLocalhost = Boolean( 14 | window.location.hostname === 'localhost' || 15 | // [::1] is the IPv6 localhost address. 16 | window.location.hostname === '[::1]' || 17 | // 127.0.0.1/8 is considered localhost for IPv4. 18 | window.location.hostname.match( 19 | /^127(?:\.(?:25[0-5]|2[0-4][0-9]|[01]?[0-9][0-9]?)){3}$/ 20 | ) 21 | ); 22 | 23 | export function register(config) { 24 | if (process.env.NODE_ENV === 'production' && 'serviceWorker' in navigator) { 25 | // The URL constructor is available in all browsers that support SW. 26 | const publicUrl = new URL(process.env.PUBLIC_URL, window.location.href); 27 | if (publicUrl.origin !== window.location.origin) { 28 | // Our service worker won't work if PUBLIC_URL is on a different origin 29 | // from what our page is served on. This might happen if a CDN is used to 30 | // serve assets; see https://github.com/facebook/create-react-app/issues/2374 31 | return; 32 | } 33 | 34 | window.addEventListener('load', () => { 35 | const swUrl = `${process.env.PUBLIC_URL}/service-worker.js`; 36 | 37 | if (isLocalhost) { 38 | // This is running on localhost. Let's check if a service worker still exists or not. 39 | checkValidServiceWorker(swUrl, config); 40 | 41 | // Add some additional logging to localhost, pointing developers to the 42 | // service worker/PWA documentation. 43 | navigator.serviceWorker.ready.then(() => { 44 | console.log( 45 | 'This web app is being served cache-first by a service ' + 46 | 'worker. To learn more, visit https://bit.ly/CRA-PWA' 47 | ); 48 | }); 49 | } else { 50 | // Is not localhost. Just register service worker 51 | registerValidSW(swUrl, config); 52 | } 53 | }); 54 | } 55 | } 56 | 57 | function registerValidSW(swUrl, config) { 58 | navigator.serviceWorker 59 | .register(swUrl) 60 | .then(registration => { 61 | registration.onupdatefound = () => { 62 | const installingWorker = registration.installing; 63 | if (installingWorker == null) { 64 | return; 65 | } 66 | installingWorker.onstatechange = () => { 67 | if (installingWorker.state === 'installed') { 68 | if (navigator.serviceWorker.controller) { 69 | // At this point, the updated precached content has been fetched, 70 | // but the previous service worker will still serve the older 71 | // content until all client tabs are closed. 72 | console.log( 73 | 'New content is available and will be used when all ' + 74 | 'tabs for this page are closed. See https://bit.ly/CRA-PWA.' 75 | ); 76 | 77 | // Execute callback 78 | if (config && config.onUpdate) { 79 | config.onUpdate(registration); 80 | } 81 | } else { 82 | // At this point, everything has been precached. 83 | // It's the perfect time to display a 84 | // "Content is cached for offline use." message. 85 | console.log('Content is cached for offline use.'); 86 | 87 | // Execute callback 88 | if (config && config.onSuccess) { 89 | config.onSuccess(registration); 90 | } 91 | } 92 | } 93 | }; 94 | }; 95 | }) 96 | .catch(error => { 97 | console.error('Error during service worker registration:', error); 98 | }); 99 | } 100 | 101 | function checkValidServiceWorker(swUrl, config) { 102 | // Check if the service worker can be found. If it can't reload the page. 103 | fetch(swUrl) 104 | .then(response => { 105 | // Ensure service worker exists, and that we really are getting a JS file. 106 | const contentType = response.headers.get('content-type'); 107 | if ( 108 | response.status === 404 || 109 | (contentType != null && contentType.indexOf('javascript') === -1) 110 | ) { 111 | // No service worker found. Probably a different app. Reload the page. 112 | navigator.serviceWorker.ready.then(registration => { 113 | registration.unregister().then(() => { 114 | window.location.reload(); 115 | }); 116 | }); 117 | } else { 118 | // Service worker found. Proceed as normal. 119 | registerValidSW(swUrl, config); 120 | } 121 | }) 122 | .catch(() => { 123 | console.log( 124 | 'No internet connection found. App is running in offline mode.' 125 | ); 126 | }); 127 | } 128 | 129 | export function unregister() { 130 | if ('serviceWorker' in navigator) { 131 | navigator.serviceWorker.ready.then(registration => { 132 | registration.unregister(); 133 | }); 134 | } 135 | } 136 | -------------------------------------------------------------------------------- /backend/package-lock.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "backend", 3 | "version": "1.0.0", 4 | "lockfileVersion": 1, 5 | "requires": true, 6 | "dependencies": { 7 | "accepts": { 8 | "version": "1.3.7", 9 | "resolved": "https://registry.npmjs.org/accepts/-/accepts-1.3.7.tgz", 10 | "integrity": "sha512-Il80Qs2WjYlJIBNzNkK6KYqlVMTbZLXgHx2oT0pU/fjRHyEp+PEfEPY0R3WCwAGVOtauxh1hOxNgIf5bv7dQpA==", 11 | "requires": { 12 | "mime-types": "~2.1.24", 13 | "negotiator": "0.6.2" 14 | } 15 | }, 16 | "array-flatten": { 17 | "version": "1.1.1", 18 | "resolved": "https://registry.npmjs.org/array-flatten/-/array-flatten-1.1.1.tgz", 19 | "integrity": "sha1-ml9pkFGx5wczKPKgCJaLZOopVdI=" 20 | }, 21 | "async": { 22 | "version": "2.6.1", 23 | "resolved": "https://registry.npmjs.org/async/-/async-2.6.1.tgz", 24 | "integrity": "sha512-fNEiL2+AZt6AlAw/29Cr0UDe4sRAHCpEHh54WMz+Bb7QfNcFw4h3loofyJpLeQs4Yx7yuqu/2dLgM5hKOs6HlQ==", 25 | "requires": { 26 | "lodash": "^4.17.10" 27 | } 28 | }, 29 | "bluebird": { 30 | "version": "3.5.1", 31 | "resolved": "https://registry.npmjs.org/bluebird/-/bluebird-3.5.1.tgz", 32 | "integrity": "sha512-MKiLiV+I1AA596t9w1sQJ8jkiSr5+ZKi0WKrYGUn6d1Fx+Ij4tIj+m2WMQSGczs5jZVxV339chE8iwk6F64wjA==" 33 | }, 34 | "body-parser": { 35 | "version": "1.19.0", 36 | "resolved": "https://registry.npmjs.org/body-parser/-/body-parser-1.19.0.tgz", 37 | "integrity": "sha512-dhEPs72UPbDnAQJ9ZKMNTP6ptJaionhP5cBb541nXPlW60Jepo9RV/a4fX4XWW9CuFNK22krhrj1+rgzifNCsw==", 38 | "requires": { 39 | "bytes": "3.1.0", 40 | "content-type": "~1.0.4", 41 | "debug": "2.6.9", 42 | "depd": "~1.1.2", 43 | "http-errors": "1.7.2", 44 | "iconv-lite": "0.4.24", 45 | "on-finished": "~2.3.0", 46 | "qs": "6.7.0", 47 | "raw-body": "2.4.0", 48 | "type-is": "~1.6.17" 49 | } 50 | }, 51 | "bson": { 52 | "version": "1.1.1", 53 | "resolved": "https://registry.npmjs.org/bson/-/bson-1.1.1.tgz", 54 | "integrity": "sha512-jCGVYLoYMHDkOsbwJZBCqwMHyH4c+wzgI9hG7Z6SZJRXWr+x58pdIbm2i9a/jFGCkRJqRUr8eoI7lDWa0hTkxg==" 55 | }, 56 | "bytes": { 57 | "version": "3.1.0", 58 | "resolved": "https://registry.npmjs.org/bytes/-/bytes-3.1.0.tgz", 59 | "integrity": "sha512-zauLjrfCG+xvoyaqLoV8bLVXXNGC4JqlxFCutSDWA6fJrTo2ZuvLYTqZ7aHBLZSMOopbzwv8f+wZcVzfVTI2Dg==" 60 | }, 61 | "content-disposition": { 62 | "version": "0.5.2", 63 | "resolved": "https://registry.npmjs.org/content-disposition/-/content-disposition-0.5.2.tgz", 64 | "integrity": "sha1-DPaLud318r55YcOoUXjLhdunjLQ=" 65 | }, 66 | "content-type": { 67 | "version": "1.0.4", 68 | "resolved": "https://registry.npmjs.org/content-type/-/content-type-1.0.4.tgz", 69 | "integrity": "sha512-hIP3EEPs8tB9AT1L+NUqtwOAps4mk2Zob89MWXMHjHWg9milF/j4osnnQLXBCBFBk/tvIG/tUc9mOUJiPBhPXA==" 70 | }, 71 | "cookie": { 72 | "version": "0.3.1", 73 | "resolved": "https://registry.npmjs.org/cookie/-/cookie-0.3.1.tgz", 74 | "integrity": "sha1-5+Ch+e9DtMi6klxcWpboBtFoc7s=" 75 | }, 76 | "cookie-signature": { 77 | "version": "1.0.6", 78 | "resolved": "https://registry.npmjs.org/cookie-signature/-/cookie-signature-1.0.6.tgz", 79 | "integrity": "sha1-4wOogrNCzD7oylE6eZmXNNqzriw=" 80 | }, 81 | "cors": { 82 | "version": "2.8.5", 83 | "resolved": "https://registry.npmjs.org/cors/-/cors-2.8.5.tgz", 84 | "integrity": "sha512-KIHbLJqu73RGr/hnbrO9uBeixNGuvSQjul/jdFvS/KFSIH1hWVd1ng7zOHx+YrEfInLG7q4n6GHQ9cDtxv/P6g==", 85 | "requires": { 86 | "object-assign": "^4", 87 | "vary": "^1" 88 | } 89 | }, 90 | "debug": { 91 | "version": "2.6.9", 92 | "resolved": "https://registry.npmjs.org/debug/-/debug-2.6.9.tgz", 93 | "integrity": "sha512-bC7ElrdJaJnPbAP+1EotYvqZsb3ecl5wi6Bfi6BJTUcNowp6cvspg0jXznRTKDjm/E7AdgFBVeAPVMNcKGsHMA==", 94 | "requires": { 95 | "ms": "2.0.0" 96 | } 97 | }, 98 | "depd": { 99 | "version": "1.1.2", 100 | "resolved": "https://registry.npmjs.org/depd/-/depd-1.1.2.tgz", 101 | "integrity": "sha1-m81S4UwJd2PnSbJ0xDRu0uVgtak=" 102 | }, 103 | "destroy": { 104 | "version": "1.0.4", 105 | "resolved": "https://registry.npmjs.org/destroy/-/destroy-1.0.4.tgz", 106 | "integrity": "sha1-l4hXRCxEdJ5CBmE+N5RiBYJqvYA=" 107 | }, 108 | "dotenv": { 109 | "version": "8.0.0", 110 | "resolved": "https://registry.npmjs.org/dotenv/-/dotenv-8.0.0.tgz", 111 | "integrity": "sha512-30xVGqjLjiUOArT4+M5q9sYdvuR4riM6yK9wMcas9Vbp6zZa+ocC9dp6QoftuhTPhFAiLK/0C5Ni2nou/Bk8lg==" 112 | }, 113 | "ee-first": { 114 | "version": "1.1.1", 115 | "resolved": "https://registry.npmjs.org/ee-first/-/ee-first-1.1.1.tgz", 116 | "integrity": "sha1-WQxhFWsK4vTwJVcyoViyZrxWsh0=" 117 | }, 118 | "encodeurl": { 119 | "version": "1.0.2", 120 | "resolved": "https://registry.npmjs.org/encodeurl/-/encodeurl-1.0.2.tgz", 121 | "integrity": "sha1-rT/0yG7C0CkyL1oCw6mmBslbP1k=" 122 | }, 123 | "escape-html": { 124 | "version": "1.0.3", 125 | "resolved": "https://registry.npmjs.org/escape-html/-/escape-html-1.0.3.tgz", 126 | "integrity": "sha1-Aljq5NPQwJdN4cFpGI7wBR0dGYg=" 127 | }, 128 | "etag": { 129 | "version": "1.8.1", 130 | "resolved": "https://registry.npmjs.org/etag/-/etag-1.8.1.tgz", 131 | "integrity": "sha1-Qa4u62XvpiJorr/qg6x9eSmbCIc=" 132 | }, 133 | "express": { 134 | "version": "4.16.4", 135 | "resolved": "https://registry.npmjs.org/express/-/express-4.16.4.tgz", 136 | "integrity": "sha512-j12Uuyb4FMrd/qQAm6uCHAkPtO8FDTRJZBDd5D2KOL2eLaz1yUNdUB/NOIyq0iU4q4cFarsUCrnFDPBcnksuOg==", 137 | "requires": { 138 | "accepts": "~1.3.5", 139 | "array-flatten": "1.1.1", 140 | "body-parser": "1.18.3", 141 | "content-disposition": "0.5.2", 142 | "content-type": "~1.0.4", 143 | "cookie": "0.3.1", 144 | "cookie-signature": "1.0.6", 145 | "debug": "2.6.9", 146 | "depd": "~1.1.2", 147 | "encodeurl": "~1.0.2", 148 | "escape-html": "~1.0.3", 149 | "etag": "~1.8.1", 150 | "finalhandler": "1.1.1", 151 | "fresh": "0.5.2", 152 | "merge-descriptors": "1.0.1", 153 | "methods": "~1.1.2", 154 | "on-finished": "~2.3.0", 155 | "parseurl": "~1.3.2", 156 | "path-to-regexp": "0.1.7", 157 | "proxy-addr": "~2.0.4", 158 | "qs": "6.5.2", 159 | "range-parser": "~1.2.0", 160 | "safe-buffer": "5.1.2", 161 | "send": "0.16.2", 162 | "serve-static": "1.13.2", 163 | "setprototypeof": "1.1.0", 164 | "statuses": "~1.4.0", 165 | "type-is": "~1.6.16", 166 | "utils-merge": "1.0.1", 167 | "vary": "~1.1.2" 168 | }, 169 | "dependencies": { 170 | "body-parser": { 171 | "version": "1.18.3", 172 | "resolved": "https://registry.npmjs.org/body-parser/-/body-parser-1.18.3.tgz", 173 | "integrity": "sha1-WykhmP/dVTs6DyDe0FkrlWlVyLQ=", 174 | "requires": { 175 | "bytes": "3.0.0", 176 | "content-type": "~1.0.4", 177 | "debug": "2.6.9", 178 | "depd": "~1.1.2", 179 | "http-errors": "~1.6.3", 180 | "iconv-lite": "0.4.23", 181 | "on-finished": "~2.3.0", 182 | "qs": "6.5.2", 183 | "raw-body": "2.3.3", 184 | "type-is": "~1.6.16" 185 | } 186 | }, 187 | "bytes": { 188 | "version": "3.0.0", 189 | "resolved": "https://registry.npmjs.org/bytes/-/bytes-3.0.0.tgz", 190 | "integrity": "sha1-0ygVQE1olpn4Wk6k+odV3ROpYEg=" 191 | }, 192 | "http-errors": { 193 | "version": "1.6.3", 194 | "resolved": "https://registry.npmjs.org/http-errors/-/http-errors-1.6.3.tgz", 195 | "integrity": "sha1-i1VoC7S+KDoLW/TqLjhYC+HZMg0=", 196 | "requires": { 197 | "depd": "~1.1.2", 198 | "inherits": "2.0.3", 199 | "setprototypeof": "1.1.0", 200 | "statuses": ">= 1.4.0 < 2" 201 | } 202 | }, 203 | "iconv-lite": { 204 | "version": "0.4.23", 205 | "resolved": "https://registry.npmjs.org/iconv-lite/-/iconv-lite-0.4.23.tgz", 206 | "integrity": "sha512-neyTUVFtahjf0mB3dZT77u+8O0QB89jFdnBkd5P1JgYPbPaia3gXXOVL2fq8VyU2gMMD7SaN7QukTB/pmXYvDA==", 207 | "requires": { 208 | "safer-buffer": ">= 2.1.2 < 3" 209 | } 210 | }, 211 | "qs": { 212 | "version": "6.5.2", 213 | "resolved": "https://registry.npmjs.org/qs/-/qs-6.5.2.tgz", 214 | "integrity": "sha512-N5ZAX4/LxJmF+7wN74pUD6qAh9/wnvdQcjq9TZjevvXzSUo7bfmw91saqMjzGS2xq91/odN2dW/WOl7qQHNDGA==" 215 | }, 216 | "raw-body": { 217 | "version": "2.3.3", 218 | "resolved": "https://registry.npmjs.org/raw-body/-/raw-body-2.3.3.tgz", 219 | "integrity": "sha512-9esiElv1BrZoI3rCDuOuKCBRbuApGGaDPQfjSflGxdy4oyzqghxu6klEkkVIvBje+FF0BX9coEv8KqW6X/7njw==", 220 | "requires": { 221 | "bytes": "3.0.0", 222 | "http-errors": "1.6.3", 223 | "iconv-lite": "0.4.23", 224 | "unpipe": "1.0.0" 225 | } 226 | }, 227 | "setprototypeof": { 228 | "version": "1.1.0", 229 | "resolved": "https://registry.npmjs.org/setprototypeof/-/setprototypeof-1.1.0.tgz", 230 | "integrity": "sha512-BvE/TwpZX4FXExxOxZyRGQQv651MSwmWKZGqvmPcRIjDqWub67kTKuIMx43cZZrS/cBBzwBcNDWoFxt2XEFIpQ==" 231 | }, 232 | "statuses": { 233 | "version": "1.4.0", 234 | "resolved": "https://registry.npmjs.org/statuses/-/statuses-1.4.0.tgz", 235 | "integrity": "sha512-zhSCtt8v2NDrRlPQpCNtw/heZLtfUDqxBM1udqikb/Hbk52LK4nQSwr10u77iopCW5LsyHpuXS0GnEc48mLeew==" 236 | } 237 | } 238 | }, 239 | "finalhandler": { 240 | "version": "1.1.1", 241 | "resolved": "https://registry.npmjs.org/finalhandler/-/finalhandler-1.1.1.tgz", 242 | "integrity": "sha512-Y1GUDo39ez4aHAw7MysnUD5JzYX+WaIj8I57kO3aEPT1fFRL4sr7mjei97FgnwhAyyzRYmQZaTHb2+9uZ1dPtg==", 243 | "requires": { 244 | "debug": "2.6.9", 245 | "encodeurl": "~1.0.2", 246 | "escape-html": "~1.0.3", 247 | "on-finished": "~2.3.0", 248 | "parseurl": "~1.3.2", 249 | "statuses": "~1.4.0", 250 | "unpipe": "~1.0.0" 251 | }, 252 | "dependencies": { 253 | "statuses": { 254 | "version": "1.4.0", 255 | "resolved": "https://registry.npmjs.org/statuses/-/statuses-1.4.0.tgz", 256 | "integrity": "sha512-zhSCtt8v2NDrRlPQpCNtw/heZLtfUDqxBM1udqikb/Hbk52LK4nQSwr10u77iopCW5LsyHpuXS0GnEc48mLeew==" 257 | } 258 | } 259 | }, 260 | "forwarded": { 261 | "version": "0.1.2", 262 | "resolved": "https://registry.npmjs.org/forwarded/-/forwarded-0.1.2.tgz", 263 | "integrity": "sha1-mMI9qxF1ZXuMBXPozszZGw/xjIQ=" 264 | }, 265 | "fresh": { 266 | "version": "0.5.2", 267 | "resolved": "https://registry.npmjs.org/fresh/-/fresh-0.5.2.tgz", 268 | "integrity": "sha1-PYyt2Q2XZWn6g1qx+OSyOhBWBac=" 269 | }, 270 | "http-errors": { 271 | "version": "1.7.2", 272 | "resolved": "https://registry.npmjs.org/http-errors/-/http-errors-1.7.2.tgz", 273 | "integrity": "sha512-uUQBt3H/cSIVfch6i1EuPNy/YsRSOUBXTVfZ+yR7Zjez3qjBz6i9+i4zjNaoqcoFVI4lQJ5plg63TvGfRSDCRg==", 274 | "requires": { 275 | "depd": "~1.1.2", 276 | "inherits": "2.0.3", 277 | "setprototypeof": "1.1.1", 278 | "statuses": ">= 1.5.0 < 2", 279 | "toidentifier": "1.0.0" 280 | } 281 | }, 282 | "iconv-lite": { 283 | "version": "0.4.24", 284 | "resolved": "https://registry.npmjs.org/iconv-lite/-/iconv-lite-0.4.24.tgz", 285 | "integrity": "sha512-v3MXnZAcvnywkTUEZomIActle7RXXeedOR31wwl7VlyoXO4Qi9arvSenNQWne1TcRwhCL1HwLI21bEqdpj8/rA==", 286 | "requires": { 287 | "safer-buffer": ">= 2.1.2 < 3" 288 | } 289 | }, 290 | "inherits": { 291 | "version": "2.0.3", 292 | "resolved": "https://registry.npmjs.org/inherits/-/inherits-2.0.3.tgz", 293 | "integrity": "sha1-Yzwsg+PaQqUC9SRmAiSA9CCCYd4=" 294 | }, 295 | "ipaddr.js": { 296 | "version": "1.9.0", 297 | "resolved": "https://registry.npmjs.org/ipaddr.js/-/ipaddr.js-1.9.0.tgz", 298 | "integrity": "sha512-M4Sjn6N/+O6/IXSJseKqHoFc+5FdGJ22sXqnjTpdZweHK64MzEPAyQZyEU3R/KRv2GLoa7nNtg/C2Ev6m7z+eA==" 299 | }, 300 | "kareem": { 301 | "version": "2.3.0", 302 | "resolved": "https://registry.npmjs.org/kareem/-/kareem-2.3.0.tgz", 303 | "integrity": "sha512-6hHxsp9e6zQU8nXsP+02HGWXwTkOEw6IROhF2ZA28cYbUk4eJ6QbtZvdqZOdD9YPKghG3apk5eOCvs+tLl3lRg==" 304 | }, 305 | "lodash": { 306 | "version": "4.17.11", 307 | "resolved": "https://registry.npmjs.org/lodash/-/lodash-4.17.11.tgz", 308 | "integrity": "sha512-cQKh8igo5QUhZ7lg38DYWAxMvjSAKG0A8wGSVimP07SIUEK2UO+arSRKbRZWtelMtN5V0Hkwh5ryOto/SshYIg==" 309 | }, 310 | "media-typer": { 311 | "version": "0.3.0", 312 | "resolved": "https://registry.npmjs.org/media-typer/-/media-typer-0.3.0.tgz", 313 | "integrity": "sha1-hxDXrwqmJvj/+hzgAWhUUmMlV0g=" 314 | }, 315 | "memory-pager": { 316 | "version": "1.5.0", 317 | "resolved": "https://registry.npmjs.org/memory-pager/-/memory-pager-1.5.0.tgz", 318 | "integrity": "sha512-ZS4Bp4r/Zoeq6+NLJpP+0Zzm0pR8whtGPf1XExKLJBAczGMnSi3It14OiNCStjQjM6NU1okjQGSxgEZN8eBYKg==", 319 | "optional": true 320 | }, 321 | "merge-descriptors": { 322 | "version": "1.0.1", 323 | "resolved": "https://registry.npmjs.org/merge-descriptors/-/merge-descriptors-1.0.1.tgz", 324 | "integrity": "sha1-sAqqVW3YtEVoFQ7J0blT8/kMu2E=" 325 | }, 326 | "methods": { 327 | "version": "1.1.2", 328 | "resolved": "https://registry.npmjs.org/methods/-/methods-1.1.2.tgz", 329 | "integrity": "sha1-VSmk1nZUE07cxSZmVoNbD4Ua/O4=" 330 | }, 331 | "mime": { 332 | "version": "1.4.1", 333 | "resolved": "https://registry.npmjs.org/mime/-/mime-1.4.1.tgz", 334 | "integrity": "sha512-KI1+qOZu5DcW6wayYHSzR/tXKCDC5Om4s1z2QJjDULzLcmf3DvzS7oluY4HCTrc+9FiKmWUgeNLg7W3uIQvxtQ==" 335 | }, 336 | "mime-db": { 337 | "version": "1.40.0", 338 | "resolved": "https://registry.npmjs.org/mime-db/-/mime-db-1.40.0.tgz", 339 | "integrity": "sha512-jYdeOMPy9vnxEqFRRo6ZvTZ8d9oPb+k18PKoYNYUe2stVEBPPwsln/qWzdbmaIvnhZ9v2P+CuecK+fpUfsV2mA==" 340 | }, 341 | "mime-types": { 342 | "version": "2.1.24", 343 | "resolved": "https://registry.npmjs.org/mime-types/-/mime-types-2.1.24.tgz", 344 | "integrity": "sha512-WaFHS3MCl5fapm3oLxU4eYDw77IQM2ACcxQ9RIxfaC3ooc6PFuBMGZZsYpvoXS5D5QTWPieo1jjLdAm3TBP3cQ==", 345 | "requires": { 346 | "mime-db": "1.40.0" 347 | } 348 | }, 349 | "mongodb": { 350 | "version": "3.2.3", 351 | "resolved": "https://registry.npmjs.org/mongodb/-/mongodb-3.2.3.tgz", 352 | "integrity": "sha512-jw8UyPsq4QleZ9z+t/pIVy3L++51vKdaJ2Q/XXeYxk/3cnKioAH8H6f5tkkDivrQL4PUgUOHe9uZzkpRFH1XtQ==", 353 | "requires": { 354 | "mongodb-core": "^3.2.3", 355 | "safe-buffer": "^5.1.2" 356 | } 357 | }, 358 | "mongodb-core": { 359 | "version": "3.2.3", 360 | "resolved": "https://registry.npmjs.org/mongodb-core/-/mongodb-core-3.2.3.tgz", 361 | "integrity": "sha512-UyI0rmvPPkjOJV8XGWa9VCTq7R4hBVipimhnAXeSXnuAPjuTqbyfA5Ec9RcYJ1Hhu+ISnc8bJ1KfGZd4ZkYARQ==", 362 | "requires": { 363 | "bson": "^1.1.1", 364 | "require_optional": "^1.0.1", 365 | "safe-buffer": "^5.1.2", 366 | "saslprep": "^1.0.0" 367 | } 368 | }, 369 | "mongoose": { 370 | "version": "5.5.7", 371 | "resolved": "https://registry.npmjs.org/mongoose/-/mongoose-5.5.7.tgz", 372 | "integrity": "sha512-Xs4SVuMGkMRdmKZ6ATGVvbp2pjI+KENCNhPZMnksjU3ZerkJHGhWKrUE1A6e/5iZ2lTu6Oz9AOPcfrDgp6BCUw==", 373 | "requires": { 374 | "async": "2.6.1", 375 | "bson": "~1.1.1", 376 | "kareem": "2.3.0", 377 | "mongodb": "3.2.3", 378 | "mongodb-core": "3.2.3", 379 | "mongoose-legacy-pluralize": "1.0.2", 380 | "mpath": "0.6.0", 381 | "mquery": "3.2.0", 382 | "ms": "2.1.1", 383 | "regexp-clone": "0.0.1", 384 | "safe-buffer": "5.1.2", 385 | "sift": "7.0.1", 386 | "sliced": "1.0.1" 387 | }, 388 | "dependencies": { 389 | "ms": { 390 | "version": "2.1.1", 391 | "resolved": "https://registry.npmjs.org/ms/-/ms-2.1.1.tgz", 392 | "integrity": "sha512-tgp+dl5cGk28utYktBsrFqA7HKgrhgPsg6Z/EfhWI4gl1Hwq8B/GmY/0oXZ6nF8hDVesS/FpnYaD/kOWhYQvyg==" 393 | } 394 | } 395 | }, 396 | "mongoose-legacy-pluralize": { 397 | "version": "1.0.2", 398 | "resolved": "https://registry.npmjs.org/mongoose-legacy-pluralize/-/mongoose-legacy-pluralize-1.0.2.tgz", 399 | "integrity": "sha512-Yo/7qQU4/EyIS8YDFSeenIvXxZN+ld7YdV9LqFVQJzTLye8unujAWPZ4NWKfFA+RNjh+wvTWKY9Z3E5XM6ZZiQ==" 400 | }, 401 | "mpath": { 402 | "version": "0.6.0", 403 | "resolved": "https://registry.npmjs.org/mpath/-/mpath-0.6.0.tgz", 404 | "integrity": "sha512-i75qh79MJ5Xo/sbhxrDrPSEG0H/mr1kcZXJ8dH6URU5jD/knFxCVqVC/gVSW7GIXL/9hHWlT9haLbCXWOll3qw==" 405 | }, 406 | "mquery": { 407 | "version": "3.2.0", 408 | "resolved": "https://registry.npmjs.org/mquery/-/mquery-3.2.0.tgz", 409 | "integrity": "sha512-qPJcdK/yqcbQiKoemAt62Y0BAc0fTEKo1IThodBD+O5meQRJT/2HSe5QpBNwaa4CjskoGrYWsEyjkqgiE0qjhg==", 410 | "requires": { 411 | "bluebird": "3.5.1", 412 | "debug": "3.1.0", 413 | "regexp-clone": "0.0.1", 414 | "safe-buffer": "5.1.2", 415 | "sliced": "1.0.1" 416 | }, 417 | "dependencies": { 418 | "debug": { 419 | "version": "3.1.0", 420 | "resolved": "https://registry.npmjs.org/debug/-/debug-3.1.0.tgz", 421 | "integrity": "sha512-OX8XqP7/1a9cqkxYw2yXss15f26NKWBpDXQd0/uK/KPqdQhxbPa994hnzjcE2VqQpDslf55723cKPUOGSmMY3g==", 422 | "requires": { 423 | "ms": "2.0.0" 424 | } 425 | } 426 | } 427 | }, 428 | "ms": { 429 | "version": "2.0.0", 430 | "resolved": "https://registry.npmjs.org/ms/-/ms-2.0.0.tgz", 431 | "integrity": "sha1-VgiurfwAvmwpAd9fmGF4jeDVl8g=" 432 | }, 433 | "negotiator": { 434 | "version": "0.6.2", 435 | "resolved": "https://registry.npmjs.org/negotiator/-/negotiator-0.6.2.tgz", 436 | "integrity": "sha512-hZXc7K2e+PgeI1eDBe/10Ard4ekbfrrqG8Ep+8Jmf4JID2bNg7NvCPOZN+kfF574pFQI7mum2AUqDidoKqcTOw==" 437 | }, 438 | "object-assign": { 439 | "version": "4.1.1", 440 | "resolved": "https://registry.npmjs.org/object-assign/-/object-assign-4.1.1.tgz", 441 | "integrity": "sha1-IQmtx5ZYh8/AXLvUQsrIv7s2CGM=" 442 | }, 443 | "on-finished": { 444 | "version": "2.3.0", 445 | "resolved": "https://registry.npmjs.org/on-finished/-/on-finished-2.3.0.tgz", 446 | "integrity": "sha1-IPEzZIGwg811M3mSoWlxqi2QaUc=", 447 | "requires": { 448 | "ee-first": "1.1.1" 449 | } 450 | }, 451 | "parseurl": { 452 | "version": "1.3.3", 453 | "resolved": "https://registry.npmjs.org/parseurl/-/parseurl-1.3.3.tgz", 454 | "integrity": "sha512-CiyeOxFT/JZyN5m0z9PfXw4SCBJ6Sygz1Dpl0wqjlhDEGGBP1GnsUVEL0p63hoG1fcj3fHynXi9NYO4nWOL+qQ==" 455 | }, 456 | "path-to-regexp": { 457 | "version": "0.1.7", 458 | "resolved": "https://registry.npmjs.org/path-to-regexp/-/path-to-regexp-0.1.7.tgz", 459 | "integrity": "sha1-32BBeABfUi8V60SQ5yR6G/qmf4w=" 460 | }, 461 | "proxy-addr": { 462 | "version": "2.0.5", 463 | "resolved": "https://registry.npmjs.org/proxy-addr/-/proxy-addr-2.0.5.tgz", 464 | "integrity": "sha512-t/7RxHXPH6cJtP0pRG6smSr9QJidhB+3kXu0KgXnbGYMgzEnUxRQ4/LDdfOwZEMyIh3/xHb8PX3t+lfL9z+YVQ==", 465 | "requires": { 466 | "forwarded": "~0.1.2", 467 | "ipaddr.js": "1.9.0" 468 | } 469 | }, 470 | "qs": { 471 | "version": "6.7.0", 472 | "resolved": "https://registry.npmjs.org/qs/-/qs-6.7.0.tgz", 473 | "integrity": "sha512-VCdBRNFTX1fyE7Nb6FYoURo/SPe62QCaAyzJvUjwRaIsc+NePBEniHlvxFmmX56+HZphIGtV0XeCirBtpDrTyQ==" 474 | }, 475 | "range-parser": { 476 | "version": "1.2.0", 477 | "resolved": "https://registry.npmjs.org/range-parser/-/range-parser-1.2.0.tgz", 478 | "integrity": "sha1-9JvmtIeJTdxA3MlKMi9hEJLgDV4=" 479 | }, 480 | "raw-body": { 481 | "version": "2.4.0", 482 | "resolved": "https://registry.npmjs.org/raw-body/-/raw-body-2.4.0.tgz", 483 | "integrity": "sha512-4Oz8DUIwdvoa5qMJelxipzi/iJIi40O5cGV1wNYp5hvZP8ZN0T+jiNkL0QepXs+EsQ9XJ8ipEDoiH70ySUJP3Q==", 484 | "requires": { 485 | "bytes": "3.1.0", 486 | "http-errors": "1.7.2", 487 | "iconv-lite": "0.4.24", 488 | "unpipe": "1.0.0" 489 | } 490 | }, 491 | "regexp-clone": { 492 | "version": "0.0.1", 493 | "resolved": "https://registry.npmjs.org/regexp-clone/-/regexp-clone-0.0.1.tgz", 494 | "integrity": "sha1-p8LgmJH9vzj7sQ03b7cwA+aKxYk=" 495 | }, 496 | "require_optional": { 497 | "version": "1.0.1", 498 | "resolved": "https://registry.npmjs.org/require_optional/-/require_optional-1.0.1.tgz", 499 | "integrity": "sha512-qhM/y57enGWHAe3v/NcwML6a3/vfESLe/sGM2dII+gEO0BpKRUkWZow/tyloNqJyN6kXSl3RyyM8Ll5D/sJP8g==", 500 | "requires": { 501 | "resolve-from": "^2.0.0", 502 | "semver": "^5.1.0" 503 | } 504 | }, 505 | "resolve-from": { 506 | "version": "2.0.0", 507 | "resolved": "https://registry.npmjs.org/resolve-from/-/resolve-from-2.0.0.tgz", 508 | "integrity": "sha1-lICrIOlP+h2egKgEx+oUdhGWa1c=" 509 | }, 510 | "safe-buffer": { 511 | "version": "5.1.2", 512 | "resolved": "https://registry.npmjs.org/safe-buffer/-/safe-buffer-5.1.2.tgz", 513 | "integrity": "sha512-Gd2UZBJDkXlY7GbJxfsE8/nvKkUEU1G38c1siN6QP6a9PT9MmHB8GnpscSmMJSoF8LOIrt8ud/wPtojys4G6+g==" 514 | }, 515 | "safer-buffer": { 516 | "version": "2.1.2", 517 | "resolved": "https://registry.npmjs.org/safer-buffer/-/safer-buffer-2.1.2.tgz", 518 | "integrity": "sha512-YZo3K82SD7Riyi0E1EQPojLz7kpepnSQI9IyPbHHg1XXXevb5dJI7tpyN2ADxGcQbHG7vcyRHk0cbwqcQriUtg==" 519 | }, 520 | "saslprep": { 521 | "version": "1.0.3", 522 | "resolved": "https://registry.npmjs.org/saslprep/-/saslprep-1.0.3.tgz", 523 | "integrity": "sha512-/MY/PEMbk2SuY5sScONwhUDsV2p77Znkb/q3nSVstq/yQzYJOH/Azh29p9oJLsl3LnQwSvZDKagDGBsBwSooag==", 524 | "optional": true, 525 | "requires": { 526 | "sparse-bitfield": "^3.0.3" 527 | } 528 | }, 529 | "semver": { 530 | "version": "5.7.0", 531 | "resolved": "https://registry.npmjs.org/semver/-/semver-5.7.0.tgz", 532 | "integrity": "sha512-Ya52jSX2u7QKghxeoFGpLwCtGlt7j0oY9DYb5apt9nPlJ42ID+ulTXESnt/qAQcoSERyZ5sl3LDIOw0nAn/5DA==" 533 | }, 534 | "send": { 535 | "version": "0.16.2", 536 | "resolved": "https://registry.npmjs.org/send/-/send-0.16.2.tgz", 537 | "integrity": "sha512-E64YFPUssFHEFBvpbbjr44NCLtI1AohxQ8ZSiJjQLskAdKuriYEP6VyGEsRDH8ScozGpkaX1BGvhanqCwkcEZw==", 538 | "requires": { 539 | "debug": "2.6.9", 540 | "depd": "~1.1.2", 541 | "destroy": "~1.0.4", 542 | "encodeurl": "~1.0.2", 543 | "escape-html": "~1.0.3", 544 | "etag": "~1.8.1", 545 | "fresh": "0.5.2", 546 | "http-errors": "~1.6.2", 547 | "mime": "1.4.1", 548 | "ms": "2.0.0", 549 | "on-finished": "~2.3.0", 550 | "range-parser": "~1.2.0", 551 | "statuses": "~1.4.0" 552 | }, 553 | "dependencies": { 554 | "http-errors": { 555 | "version": "1.6.3", 556 | "resolved": "https://registry.npmjs.org/http-errors/-/http-errors-1.6.3.tgz", 557 | "integrity": "sha1-i1VoC7S+KDoLW/TqLjhYC+HZMg0=", 558 | "requires": { 559 | "depd": "~1.1.2", 560 | "inherits": "2.0.3", 561 | "setprototypeof": "1.1.0", 562 | "statuses": ">= 1.4.0 < 2" 563 | } 564 | }, 565 | "setprototypeof": { 566 | "version": "1.1.0", 567 | "resolved": "https://registry.npmjs.org/setprototypeof/-/setprototypeof-1.1.0.tgz", 568 | "integrity": "sha512-BvE/TwpZX4FXExxOxZyRGQQv651MSwmWKZGqvmPcRIjDqWub67kTKuIMx43cZZrS/cBBzwBcNDWoFxt2XEFIpQ==" 569 | }, 570 | "statuses": { 571 | "version": "1.4.0", 572 | "resolved": "https://registry.npmjs.org/statuses/-/statuses-1.4.0.tgz", 573 | "integrity": "sha512-zhSCtt8v2NDrRlPQpCNtw/heZLtfUDqxBM1udqikb/Hbk52LK4nQSwr10u77iopCW5LsyHpuXS0GnEc48mLeew==" 574 | } 575 | } 576 | }, 577 | "serve-static": { 578 | "version": "1.13.2", 579 | "resolved": "https://registry.npmjs.org/serve-static/-/serve-static-1.13.2.tgz", 580 | "integrity": "sha512-p/tdJrO4U387R9oMjb1oj7qSMaMfmOyd4j9hOFoxZe2baQszgHcSWjuya/CiT5kgZZKRudHNOA0pYXOl8rQ5nw==", 581 | "requires": { 582 | "encodeurl": "~1.0.2", 583 | "escape-html": "~1.0.3", 584 | "parseurl": "~1.3.2", 585 | "send": "0.16.2" 586 | } 587 | }, 588 | "setprototypeof": { 589 | "version": "1.1.1", 590 | "resolved": "https://registry.npmjs.org/setprototypeof/-/setprototypeof-1.1.1.tgz", 591 | "integrity": "sha512-JvdAWfbXeIGaZ9cILp38HntZSFSo3mWg6xGcJJsd+d4aRMOqauag1C63dJfDw7OaMYwEbHMOxEZ1lqVRYP2OAw==" 592 | }, 593 | "sift": { 594 | "version": "7.0.1", 595 | "resolved": "https://registry.npmjs.org/sift/-/sift-7.0.1.tgz", 596 | "integrity": "sha512-oqD7PMJ+uO6jV9EQCl0LrRw1OwsiPsiFQR5AR30heR+4Dl7jBBbDLnNvWiak20tzZlSE1H7RB30SX/1j/YYT7g==" 597 | }, 598 | "sliced": { 599 | "version": "1.0.1", 600 | "resolved": "https://registry.npmjs.org/sliced/-/sliced-1.0.1.tgz", 601 | "integrity": "sha1-CzpmK10Ewxd7GSa+qCsD+Dei70E=" 602 | }, 603 | "sparse-bitfield": { 604 | "version": "3.0.3", 605 | "resolved": "https://registry.npmjs.org/sparse-bitfield/-/sparse-bitfield-3.0.3.tgz", 606 | "integrity": "sha1-/0rm5oZWBWuks+eSqzM004JzyhE=", 607 | "optional": true, 608 | "requires": { 609 | "memory-pager": "^1.0.2" 610 | } 611 | }, 612 | "statuses": { 613 | "version": "1.5.0", 614 | "resolved": "https://registry.npmjs.org/statuses/-/statuses-1.5.0.tgz", 615 | "integrity": "sha1-Fhx9rBd2Wf2YEfQ3cfqZOBR4Yow=" 616 | }, 617 | "toidentifier": { 618 | "version": "1.0.0", 619 | "resolved": "https://registry.npmjs.org/toidentifier/-/toidentifier-1.0.0.tgz", 620 | "integrity": "sha512-yaOH/Pk/VEhBWWTlhI+qXxDFXlejDGcQipMlyxda9nthulaxLZUNcUqFxokp0vcYnvteJln5FNQDRrxj3YcbVw==" 621 | }, 622 | "type-is": { 623 | "version": "1.6.18", 624 | "resolved": "https://registry.npmjs.org/type-is/-/type-is-1.6.18.tgz", 625 | "integrity": "sha512-TkRKr9sUTxEH8MdfuCSP7VizJyzRNMjj2J2do2Jr3Kym598JVdEksuzPQCnlFPW4ky9Q+iA+ma9BGm06XQBy8g==", 626 | "requires": { 627 | "media-typer": "0.3.0", 628 | "mime-types": "~2.1.24" 629 | } 630 | }, 631 | "unpipe": { 632 | "version": "1.0.0", 633 | "resolved": "https://registry.npmjs.org/unpipe/-/unpipe-1.0.0.tgz", 634 | "integrity": "sha1-sr9O6FFKrmFltIF4KdIbLvSZBOw=" 635 | }, 636 | "utils-merge": { 637 | "version": "1.0.1", 638 | "resolved": "https://registry.npmjs.org/utils-merge/-/utils-merge-1.0.1.tgz", 639 | "integrity": "sha1-n5VxD1CiZ5R7LMwSR0HBAoQn5xM=" 640 | }, 641 | "vary": { 642 | "version": "1.1.2", 643 | "resolved": "https://registry.npmjs.org/vary/-/vary-1.1.2.tgz", 644 | "integrity": "sha1-IpnwLG3tMNSllhsLn3RSShj2NPw=" 645 | } 646 | } 647 | } 648 | --------------------------------------------------------------------------------