├── .babelrc ├── .env.example ├── .eslintrc ├── .gitignore ├── .travis.yml ├── LICENSE.txt ├── README.md ├── app ├── Food │ ├── controller.js │ ├── index.js │ └── model.js ├── Home │ ├── controller.js │ └── index.js ├── Orders │ ├── controller.js │ ├── index.js │ └── model.js ├── Restaurant │ ├── controller.js │ ├── index.js │ └── model.js ├── User │ ├── controller.js │ ├── index.js │ └── model.js └── utils │ ├── crypto.js │ ├── getErrorResponse.js │ └── roleAuthorization.js ├── config ├── env │ ├── development.js │ ├── production.js │ └── test.js ├── express.js ├── index.js ├── passport │ └── local.js └── routes.js ├── package.json ├── server.js └── test └── test-home.js /.babelrc: -------------------------------------------------------------------------------- 1 | { 2 | "presets": ["stage-0"] 3 | } 4 | -------------------------------------------------------------------------------- /.env.example: -------------------------------------------------------------------------------- 1 | SOME_ENV_ID=abc 2 | -------------------------------------------------------------------------------- /.eslintrc: -------------------------------------------------------------------------------- 1 | { 2 | "extends": "eslint:recommended", 3 | "env": { 4 | "browser": false, 5 | "es6": true, 6 | "node": true 7 | }, 8 | "parser": "babel-eslint", 9 | "rules": { 10 | "object-curly-spacing": [2, "always"], 11 | "strict": 0, 12 | "quotes": [2, "single", "avoid-escape"], 13 | "semi": [2, "always"], 14 | "space-before-function-paren": [2, "always"], 15 | "keyword-spacing": [2, {"before": true, "after": true}], 16 | "space-infix-ops": 2, 17 | "spaced-comment": [2, "always"], 18 | "arrow-spacing": 2, 19 | "no-console": 0, 20 | "no-compare-neg-zero": 0 21 | }, 22 | "globals": { 23 | } 24 | } 25 | -------------------------------------------------------------------------------- /.gitignore: -------------------------------------------------------------------------------- 1 | node_modules 2 | npm-debug.log 3 | .env 4 | .DS_Store -------------------------------------------------------------------------------- /.travis.yml: -------------------------------------------------------------------------------- 1 | services: 2 | - mongodb 3 | language: node_js 4 | node_js: 5 | - "6.0.0" 6 | - "6.1.0" 7 | -------------------------------------------------------------------------------- /LICENSE.txt: -------------------------------------------------------------------------------- 1 | The MIT License (MIT) 2 | 3 | Copyright (c) 2018 Codebrahma 4 | 5 | Permission is hereby granted, free of charge, to any person obtaining a copy 6 | of this software and associated documentation files (the "Software"), to deal 7 | in the Software without restriction, including without limitation the rights 8 | to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 9 | copies of the Software, and to permit persons to whom the Software is 10 | furnished to do so, subject to the following conditions: 11 | 12 | The above copyright notice and this permission notice shall be included in all 13 | copies or substantial portions of the Software. 14 | 15 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 16 | IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 17 | FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 18 | AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 19 | LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 20 | OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE 21 | SOFTWARE. 22 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | A Boiler plate for REST API using node mongoose express 2 | 3 | ## Usage 4 | $ npm install 5 | $ cp .env.example .env 6 | $ npm start 7 | 8 | -------------------------------------------------------------------------------- /app/Food/controller.js: -------------------------------------------------------------------------------- 1 | const Food = require('./model'); 2 | 3 | // Creates a food 4 | const createFood = function (req, res, next) { 5 | 6 | const { 7 | name, 8 | type 9 | } = req.body; 10 | 11 | const food = new Food({ 12 | name, 13 | type, 14 | }); 15 | 16 | // TODO: This should trigger restaurant's type 17 | food 18 | .save() 19 | .then(food => res.json(food)) 20 | .catch(err => next(err)); 21 | }; 22 | 23 | const getAllFood = function (req, res, next) { 24 | const { 25 | id, 26 | } = req.query; 27 | 28 | // If id is present get Particular item 29 | if (id) { 30 | Food 31 | .findById(id) 32 | .exec() 33 | .then(food => res.json(food)) 34 | .catch(err => next(err)); 35 | } else { 36 | // If no id then get all 37 | Food 38 | .find() 39 | .exec() 40 | .then(food => res.json(food)) 41 | .catch(err => next(err)); 42 | } 43 | }; 44 | 45 | // Deletes Food By id 46 | const deleteFoodById = function (req, res, next) { 47 | 48 | const { 49 | id 50 | } = req.query; 51 | 52 | // Remove the element by Id 53 | Food 54 | .findByIdAndRemove(id) 55 | .exec() 56 | .then(() => res.json({ 57 | success: true, 58 | })) 59 | .catch(err => next(err)); 60 | 61 | }; 62 | 63 | // Updates Food By id 64 | const updateFoodById = function (req, res, next) { 65 | const { 66 | foodId, 67 | } = req.params; 68 | 69 | Food 70 | .findByIdAndUpdate(foodId, req.body) 71 | .exec() 72 | .then(food => res.json(food)) 73 | .catch(err => next(err)); 74 | 75 | }; 76 | 77 | const getFoodType = function (req, res, next) { 78 | Food 79 | .find() 80 | .distinct('type') 81 | .exec() 82 | .then(types => res.json(types)) 83 | .catch(err => next(err)); 84 | }; 85 | 86 | module.exports = { 87 | createFood, 88 | getAllFood, 89 | deleteFoodById, 90 | updateFoodById, 91 | getFoodType, 92 | }; -------------------------------------------------------------------------------- /app/Food/index.js: -------------------------------------------------------------------------------- 1 | var router = require('express').Router(); 2 | var FoodController = require('./controller'); 3 | const Authorization = require('../utils/roleAuthorization'); 4 | 5 | module.exports = function (passport) { 6 | /* GET all Food or get food by id*/ 7 | router.get('/', 8 | passport.authenticate('jwt', { session: false }), 9 | Authorization.roleAuthorization(['admin', 'user']), 10 | FoodController.getAllFood 11 | ); 12 | 13 | /* POST New food */ 14 | router.post('/', 15 | passport.authenticate('jwt', { session: false }), 16 | Authorization.roleAuthorization(['admin']), 17 | FoodController.createFood 18 | ); 19 | 20 | /* Deletes a Food By ID */ 21 | router.delete('/', 22 | passport.authenticate('jwt', { session: false }), 23 | Authorization.roleAuthorization(['admin']), 24 | FoodController.deleteFoodById 25 | ); 26 | 27 | /* Updates a Food */ 28 | router.patch('/:foodId', 29 | passport.authenticate('jwt', { session: false }), 30 | Authorization.roleAuthorization(['admin']), 31 | FoodController.updateFoodById 32 | ); 33 | 34 | /* GET all Food or get food by id*/ 35 | router.get('/types', 36 | passport.authenticate('jwt', { session: false }), 37 | Authorization.roleAuthorization(['user']), 38 | FoodController.getFoodType 39 | ); 40 | 41 | return router; 42 | }; -------------------------------------------------------------------------------- /app/Food/model.js: -------------------------------------------------------------------------------- 1 | const mongoose = require('mongoose'); 2 | 3 | const Food = new mongoose.Schema({ 4 | name: { 5 | required: true, 6 | type: String, 7 | }, 8 | type: { 9 | require: true, 10 | type: String, 11 | }, 12 | }); 13 | 14 | module.exports = mongoose.model('Food', Food); -------------------------------------------------------------------------------- /app/Home/controller.js: -------------------------------------------------------------------------------- 1 | 2 | /*! 3 | * Module dependencies. 4 | */ 5 | 6 | module.exports.index = function (req, res) { 7 | console.log(req.user); 8 | res.send('SUCCESS') 9 | }; 10 | -------------------------------------------------------------------------------- /app/Home/index.js: -------------------------------------------------------------------------------- 1 | const controller = require('./controller'); 2 | const router = require('express').Router(); 3 | const Authorization = require('../utils/roleAuthorization'); 4 | 5 | module.exports = function (passport) { 6 | 7 | router.get('/health', 8 | passport.authenticate('jwt', { session: false }), 9 | Authorization.roleAuthorization(['admin']), 10 | controller.index 11 | ); 12 | 13 | return router; 14 | }; 15 | -------------------------------------------------------------------------------- /app/Orders/controller.js: -------------------------------------------------------------------------------- 1 | const Order = require('./model'); 2 | 3 | const createOrder = function (req, res, next) { 4 | const { 5 | items, 6 | totalCost 7 | } = req.body; 8 | 9 | const userId = req.user._id; 10 | 11 | const order = new Order({ 12 | userId, 13 | items, 14 | totalCost 15 | }); 16 | 17 | order 18 | .save() 19 | .then(order => { 20 | res.json({ 21 | success: true, 22 | order, 23 | }); 24 | }); 25 | }; 26 | 27 | const getOrderByUser = function (req, res, next) { 28 | const { 29 | userId, 30 | } = req.query; 31 | 32 | Order 33 | .find({ 34 | userId, 35 | }) 36 | .exec() 37 | .then((orders) => { 38 | res.json({ 39 | orders 40 | }); 41 | }) 42 | .catch(e => next(e)); 43 | }; 44 | 45 | module.exports = { 46 | createOrder, 47 | getOrderByUser, 48 | }; 49 | -------------------------------------------------------------------------------- /app/Orders/index.js: -------------------------------------------------------------------------------- 1 | var Router = require('express').Router(); 2 | var OrderController = require('./controller'); 3 | const Authorization = require('../utils/roleAuthorization'); 4 | 5 | module.exports = function (passport) { 6 | Router 7 | .post( 8 | '/', 9 | passport.authenticate('jwt', { session: false }), 10 | Authorization.roleAuthorization(['user']), 11 | OrderController.createOrder 12 | ); 13 | 14 | Router 15 | .get( 16 | '/', 17 | passport.authenticate('jwt', { session: false }), 18 | Authorization.roleAuthorization(['user']), 19 | OrderController.getOrderByUser 20 | ); 21 | 22 | return Router; 23 | }; 24 | -------------------------------------------------------------------------------- /app/Orders/model.js: -------------------------------------------------------------------------------- 1 | const mongoose = require('mongoose'); 2 | 3 | const Orders = new mongoose.Schema({ 4 | userId: { 5 | type: mongoose.Schema.Types.ObjectId, 6 | ref: 'User', 7 | required: true, 8 | }, 9 | items: { 10 | type: 'Array', 11 | require: true, 12 | }, 13 | totalCost: { 14 | type: Number, 15 | required: true, 16 | } 17 | }); 18 | 19 | module.exports = mongoose.model('Orders', Orders); -------------------------------------------------------------------------------- /app/Restaurant/controller.js: -------------------------------------------------------------------------------- 1 | const Restaurant = require('./model'); 2 | const filter = require('lodash/filter'); 3 | const findIndex = require('lodash/findIndex'); 4 | const pick = require('lodash/pick'); 5 | const map = require('lodash/map'); 6 | 7 | // Get Restaurants by type 8 | const getRestaurantsByType = function (req, res, next) { 9 | const { 10 | type, 11 | } = req.query; 12 | 13 | Restaurant 14 | .find() 15 | .populate('foods.food') 16 | .exec() 17 | .then(restaurants => { 18 | // returns all restaurants id, name and details 19 | res.json(map(restaurants, restaurant => pick(restaurant, ['_id', 'name', 'details', 'foods']))); 20 | }) 21 | .catch(e => next(e)); 22 | }; 23 | 24 | // Gets all restaurants with food or gets restaurant food 25 | const getAllRestaurants = function (req, res, next) { 26 | const { 27 | id, 28 | } = req.query; 29 | 30 | if (id) { 31 | Restaurant 32 | .findById(id) 33 | .populate('foods.food') 34 | .exec() 35 | .then(restaurants => res.json(restaurants)) 36 | .catch(e => next(e)); 37 | } else { 38 | Restaurant 39 | .find() 40 | .populate('foods.food') 41 | .exec() 42 | .then(restaurants => res.json(restaurants)) 43 | .catch(e => next(e)); 44 | } 45 | }; 46 | 47 | // Creates a new restaurant 48 | const createRestaurant = function (req, res, next) { 49 | const { 50 | name, 51 | details, 52 | } = req.body; 53 | 54 | const restaurant = new Restaurant({ 55 | name, 56 | details, 57 | foods: [], 58 | }); 59 | 60 | restaurant 61 | .save() 62 | .then(restaurants => res.json(restaurants)) 63 | .catch(e => next(e)); 64 | }; 65 | 66 | // Deletes a Restaurant 67 | const deleteRestaurant = function (req, res, next) { 68 | const { 69 | id, 70 | } = req.body; 71 | 72 | Restaurant 73 | .findByIdAndRemove(id) 74 | .then(() => res.json({ success: true })) 75 | .catch(e => next(e)); 76 | 77 | }; 78 | 79 | // Adds a food to restaurant and creates a link to Food Model 80 | const addFoodToRestaurant = function (req, res, next) { 81 | const { 82 | restaurantId, 83 | foodId, 84 | price, 85 | } = req.body; 86 | Restaurant 87 | .findByIdAndUpdate(restaurantId, { 88 | $push: { 89 | foods: { 90 | food: foodId, 91 | price, 92 | active: false, 93 | } 94 | }, 95 | 96 | }) 97 | .exec() 98 | .then(restaurant => res.json(restaurant)) 99 | .catch(e => next(e)); 100 | 101 | }; 102 | 103 | module.exports = { 104 | createRestaurant, 105 | getAllRestaurants, 106 | getRestaurantsByType, 107 | deleteRestaurant, 108 | addFoodToRestaurant, 109 | }; -------------------------------------------------------------------------------- /app/Restaurant/index.js: -------------------------------------------------------------------------------- 1 | var Router = require('express').Router(); 2 | var RestaurantController = require('./controller'); 3 | const Authorization = require('../utils/roleAuthorization'); 4 | 5 | module.exports = function (passport) { 6 | /* Gets All Restaurants or by query id */ 7 | Router.get('/', 8 | passport.authenticate('jwt', { session: false }), 9 | Authorization.roleAuthorization(['admin', 'user']), 10 | RestaurantController.getAllRestaurants 11 | ); 12 | 13 | /* Gets Restaurants by type */ 14 | Router.get('/filter', 15 | passport.authenticate('jwt', { session: false }), 16 | Authorization.roleAuthorization(['admin', 'user']), 17 | RestaurantController.getRestaurantsByType 18 | ); 19 | 20 | /* Creates a new Restaurant */ 21 | Router.post('/', 22 | passport.authenticate('jwt', { session: false }), 23 | Authorization.roleAuthorization(['admin']), 24 | RestaurantController.createRestaurant 25 | ); 26 | 27 | /* Delete a restaurant */ 28 | Router.delete('/', 29 | passport.authenticate('jwt', { session: false }), 30 | Authorization.roleAuthorization(['admin']), 31 | RestaurantController.deleteRestaurant 32 | ); 33 | 34 | /* Adds a food to a restaurant */ 35 | Router.post('/addFoodToRestaurant', 36 | passport.authenticate('jwt', { session: false }), 37 | Authorization.roleAuthorization(['admin']), 38 | RestaurantController.addFoodToRestaurant 39 | ); 40 | 41 | /* Changes a food in a restaurant to the given state (In stock or Out of stock) */ 42 | 43 | return Router; 44 | } -------------------------------------------------------------------------------- /app/Restaurant/model.js: -------------------------------------------------------------------------------- 1 | const mongoose = require('mongoose'); 2 | 3 | const Restaurant = new mongoose.Schema({ 4 | name: { 5 | type: String, 6 | required: true, 7 | }, 8 | details: { 9 | type: String, 10 | required: true, 11 | }, 12 | foods: [{ 13 | food: { 14 | type: mongoose.Schema.Types.ObjectId, 15 | ref: 'Food', 16 | }, 17 | price: { 18 | type: Number, 19 | }, 20 | active: { 21 | type: Boolean, 22 | }, 23 | }] 24 | }); 25 | 26 | module.exports = mongoose.model('Restaurant', Restaurant); -------------------------------------------------------------------------------- /app/User/controller.js: -------------------------------------------------------------------------------- 1 | const User = require('./model'); 2 | const crypto = require('../utils/crypto'); 3 | const jwt = require('jsonwebtoken'); 4 | 5 | const register = function (req, res, next) { 6 | const { 7 | email, 8 | password, 9 | admin, 10 | } = req.body; 11 | 12 | const hashedPassword = crypto.encrypt(password); 13 | 14 | const user = new User({ 15 | email, 16 | password: hashedPassword, 17 | role: admin ? 'admin' : 'user', 18 | }); 19 | 20 | user 21 | .save() 22 | .then((user) => { 23 | res.json({ 24 | user, 25 | success: true, 26 | }); 27 | }) 28 | .catch(e => next(e)); 29 | }; 30 | 31 | const getUsers = function (req, res, next) { 32 | const { 33 | userId 34 | } = req.params; 35 | 36 | if (userId) { 37 | User 38 | .findById(userId) 39 | .exec() 40 | .then(user => res.json(user)) 41 | .catch(e => next(e)); 42 | } else { 43 | User 44 | .find() 45 | .exec() 46 | .then(user => res.json(user)) 47 | .catch(e => next(e)); 48 | } 49 | 50 | }; 51 | 52 | /* Delete a user */ 53 | const deleteUserById = function (req, res, next) { 54 | const { 55 | userId 56 | } = req.params; 57 | 58 | User 59 | .findByIdAndRemove(userId) 60 | .exec() 61 | .then(() => res.json({ success: true })) 62 | .catch(e => next(e)); 63 | }; 64 | 65 | // Login is a curried function which takes passport 66 | const login = function (passport) { 67 | return function (req, res, next) { 68 | passport.authenticate('local', { session: false }, (err, user, info) => { 69 | if (err || !user) { 70 | return res.status(400).json({ 71 | message: 'User Id or Password is wrong', 72 | user : user 73 | }); 74 | } 75 | req.login(user, { session: false }, (err) => { 76 | if (err) { 77 | next(err); 78 | } 79 | // Provide data since user is not a proper serialized object 80 | const token = jwt.sign(user.toObject(), 'secret_restaurant_app'); 81 | 82 | return res.json({ 83 | success: true, 84 | token, 85 | }); 86 | }); 87 | })(req, res); 88 | }; 89 | 90 | }; 91 | 92 | module.exports = { 93 | register, 94 | getUsers, 95 | deleteUserById, 96 | login, 97 | }; -------------------------------------------------------------------------------- /app/User/index.js: -------------------------------------------------------------------------------- 1 | var Router = require('express').Router(); 2 | var UserController = require('./controller'); 3 | 4 | module.exports = function (passport) { 5 | 6 | Router.post('/register', UserController.register); 7 | 8 | Router.get('/users', UserController.getUsers); 9 | 10 | Router.delete('/:userId', passport.authenticate('jwt', { session: false }), UserController.deleteUserById); 11 | 12 | Router.post('/login', UserController.login(passport)); 13 | 14 | return Router; 15 | } -------------------------------------------------------------------------------- /app/User/model.js: -------------------------------------------------------------------------------- 1 | 2 | /*! 3 | * Module dependencies 4 | */ 5 | 6 | var mongoose = require('mongoose'); 7 | var Schema = mongoose.Schema; 8 | 9 | /** 10 | * User schema 11 | */ 12 | 13 | var UserSchema = new Schema({ 14 | email: { 15 | type: String, 16 | required: true 17 | }, 18 | password: { 19 | type: String, 20 | required: true, 21 | }, 22 | role: { 23 | type: String, 24 | enum: ['admin', 'user'], 25 | default: 'user', 26 | } 27 | }); 28 | 29 | module.exports = mongoose.model('User', UserSchema); 30 | -------------------------------------------------------------------------------- /app/utils/crypto.js: -------------------------------------------------------------------------------- 1 | var CryptoJS = require('crypto-js'); 2 | 3 | const secret = 'secretrestaurantapp'; 4 | 5 | const encrypt = function (password) { 6 | return CryptoJS.AES.encrypt(password, secret).toString(); 7 | }; 8 | 9 | const decrypt = function (hash) { 10 | const bytes = CryptoJS.AES.decrypt(hash.toString(), secret); 11 | return bytes.toString(CryptoJS.enc.Utf8); 12 | }; 13 | 14 | module.exports = { 15 | encrypt, 16 | decrypt, 17 | }; 18 | 19 | 20 | -------------------------------------------------------------------------------- /app/utils/getErrorResponse.js: -------------------------------------------------------------------------------- 1 | module.exports = function (err) { 2 | return { 3 | success: false, 4 | message: err.message, 5 | } 6 | } -------------------------------------------------------------------------------- /app/utils/roleAuthorization.js: -------------------------------------------------------------------------------- 1 | const User = require('../User/model'); 2 | const findIndex = require('lodash/findIndex'); 3 | 4 | const roleAuthorization = function (roles) { 5 | return function (req, res, next) { 6 | const userId = req.user._id; 7 | User 8 | .findById(userId) 9 | .exec() 10 | .then((user) => { 11 | const isPresent = (findIndex(roles, role => role === user.role) !== -1); 12 | if (!isPresent) { 13 | return res.status(401).json({ 14 | message: 'Not Authorized' 15 | }); 16 | } else { 17 | return next(); 18 | } 19 | }) 20 | .catch((e) => { 21 | res 22 | .status(42) 23 | .json({ 24 | message: 'User Does not exist' 25 | }); 26 | }); 27 | }; 28 | }; 29 | 30 | module.exports = { 31 | roleAuthorization, 32 | }; 33 | 34 | 35 | -------------------------------------------------------------------------------- /config/env/development.js: -------------------------------------------------------------------------------- 1 | 2 | /** 3 | * Expose 4 | */ 5 | 6 | module.exports = { 7 | db: 'mongodb://localhost/your_project_development', 8 | facebook: { 9 | clientID: 'APP_ID', 10 | clientSecret: 'SECRET', 11 | callbackURL: 'http://localhost:3000/auth/facebook/callback', 12 | scope: [ 13 | 'email', 14 | 'user_about_me', 15 | 'user_friends' 16 | ] 17 | }, 18 | google: { 19 | clientID: 'APP_ID', 20 | clientSecret: 'SECRET', 21 | callbackURL: 'http://localhost:3000/auth/google/callback', 22 | scope: [ 23 | 'https://www.googleapis.com/auth/userinfo.profile', 24 | 'https://www.googleapis.com/auth/userinfo.email', 25 | 'https://www.google.com/m8/feeds', 26 | ] 27 | } 28 | }; 29 | -------------------------------------------------------------------------------- /config/env/production.js: -------------------------------------------------------------------------------- 1 | 2 | /** 3 | * Expose 4 | */ 5 | 6 | module.exports = { 7 | db: 'mongodb://localhost/your_project_production', 8 | facebook: { 9 | clientID: 'APP_ID', 10 | clientSecret: 'SECRET', 11 | callbackURL: 'http://localhost:3000/auth/facebook/callback', 12 | scope: [ 13 | 'email', 14 | 'user_about_me', 15 | 'user_friends' 16 | ] 17 | }, 18 | google: { 19 | clientID: 'APP_ID', 20 | clientSecret: 'SECRET', 21 | callbackURL: 'http://localhost:3000/auth/google/callback', 22 | scope: [ 23 | 'https://www.googleapis.com/auth/userinfo.profile', 24 | 'https://www.googleapis.com/auth/userinfo.email', 25 | 'https://www.google.com/m8/feeds', 26 | ] 27 | } 28 | }; 29 | -------------------------------------------------------------------------------- /config/env/test.js: -------------------------------------------------------------------------------- 1 | 2 | /** 3 | * Expose 4 | */ 5 | 6 | module.exports = { 7 | db: 'mongodb://localhost/your_project_test', 8 | facebook: { 9 | clientID: 'APP_ID', 10 | clientSecret: 'SECRET', 11 | callbackURL: 'http://localhost:3000/auth/facebook/callback', 12 | scope: [ 13 | 'email', 14 | 'user_about_me', 15 | 'user_friends' 16 | ] 17 | }, 18 | google: { 19 | clientID: 'APP_ID', 20 | clientSecret: 'SECRET', 21 | callbackURL: 'http://localhost:3000/auth/google/callback', 22 | scope: [ 23 | 'https://www.googleapis.com/auth/userinfo.profile', 24 | 'https://www.googleapis.com/auth/userinfo.email', 25 | 'https://www.google.com/m8/feeds', 26 | ] 27 | } 28 | }; 29 | -------------------------------------------------------------------------------- /config/express.js: -------------------------------------------------------------------------------- 1 | 2 | /** 3 | * Module dependencies. 4 | */ 5 | 6 | var express = require('express'); 7 | var session = require('express-session'); 8 | var compression = require('compression'); 9 | var morgan = require('morgan'); 10 | var cookieParser = require('cookie-parser'); 11 | var cookieSession = require('cookie-session'); 12 | var bodyParser = require('body-parser'); 13 | var methodOverride = require('method-override'); 14 | var csrf = require('csurf'); 15 | 16 | var mongoStore = require('connect-mongo')(session); 17 | var flash = require('connect-flash'); 18 | var winston = require('winston'); 19 | var helpers = require('view-helpers'); 20 | var jade = require('jade'); 21 | var config = require('./'); 22 | var pkg = require('../package.json'); 23 | 24 | var env = process.env.NODE_ENV || 'development'; 25 | 26 | /** 27 | * Expose 28 | */ 29 | 30 | module.exports = function (app) { 31 | 32 | // Compression middleware (should be placed before express.static) 33 | app.use(compression({ 34 | threshold: 512 35 | })); 36 | 37 | // Static files middleware 38 | app.use(express.static(config.root + '/public')); 39 | 40 | // Use winston on production 41 | var log; 42 | if (env !== 'development') { 43 | log = { 44 | stream: { 45 | write: function (message, encoding) { 46 | winston.info(message); 47 | } 48 | } 49 | }; 50 | } else { 51 | log = 'dev'; 52 | } 53 | 54 | // Don't log during tests 55 | // Logging middleware 56 | if (env !== 'test') app.use(morgan(log)); 57 | 58 | // set views path and default layout 59 | app.set('views', config.root + '/app/views'); 60 | app.set('view engine', 'jade'); 61 | 62 | // expose package.json to views 63 | app.use(function (req, res, next) { 64 | res.locals.pkg = pkg; 65 | res.locals.env = env; 66 | next(); 67 | }); 68 | 69 | // bodyParser should be above methodOverride 70 | app.use(bodyParser.urlencoded({ 71 | extended: true 72 | })); 73 | app.use(bodyParser.json()); 74 | app.use(methodOverride(function (req, res) { 75 | if (req.body && typeof req.body === 'object' && '_method' in req.body) { 76 | // look in urlencoded POST bodies and delete it 77 | var method = req.body._method; 78 | delete req.body._method; 79 | return method; 80 | } 81 | })); 82 | 83 | // cookieParser should be above session 84 | app.use(cookieParser()); 85 | app.use(cookieSession({ secret: 'secret' })); 86 | app.use(session({ 87 | secret: pkg.name, 88 | proxy: true, 89 | resave: true, 90 | saveUninitialized: true, 91 | store: new mongoStore({ 92 | url: config.db, 93 | collection : 'sessions' 94 | }) 95 | })); 96 | 97 | // connect flash for flash messages - should be declared after sessions 98 | app.use(flash()); 99 | 100 | // should be declared after session and flash 101 | app.use(helpers(pkg.name)); 102 | 103 | }; 104 | -------------------------------------------------------------------------------- /config/index.js: -------------------------------------------------------------------------------- 1 | 2 | /** 3 | * Module dependencies. 4 | */ 5 | 6 | var path = require('path'); 7 | var development = require('./env/development'); 8 | var test = require('./env/test'); 9 | var production = require('./env/production'); 10 | var defaults = { 11 | root: path.normalize(__dirname + '/..') 12 | }; 13 | 14 | /** 15 | * Expose 16 | */ 17 | 18 | module.exports = { 19 | development: Object.assign({}, development, defaults), 20 | test: Object.assign({}, test, defaults), 21 | production: Object.assign({}, production, defaults) 22 | }[process.env.NODE_ENV || 'development']; 23 | -------------------------------------------------------------------------------- /config/passport/local.js: -------------------------------------------------------------------------------- 1 | 2 | /** 3 | * Module dependencies. 4 | */ 5 | 6 | var mongoose = require('mongoose'); 7 | var passport = require('passport'); 8 | 9 | const passportJWT = require('passport-jwt'); 10 | const ExtractJWT = passportJWT.ExtractJwt; 11 | const JWTStrategy = passportJWT.Strategy; 12 | 13 | var LocalStrategy = require('passport-local').Strategy; 14 | var User = require('../../app/User/model'); 15 | var crypto = require('../../app/utils/crypto'); 16 | 17 | /** 18 | * Expose 19 | */ 20 | // passport related code 21 | passport.use(new LocalStrategy({ 22 | usernameField: 'email', 23 | passwordField: 'password', 24 | session: false, 25 | }, 26 | function (email, password, done) { 27 | 28 | User.findOne({ 29 | email, 30 | }, function (err, user) { 31 | 32 | if (err) return done(err); 33 | if (!user || crypto.decrypt(user.password) !== password) { 34 | return done(null, false, { message: 'Credential Error' }); 35 | } 36 | return done(null, user); 37 | }); 38 | } 39 | )); 40 | 41 | passport.use(new JWTStrategy({ 42 | jwtFromRequest: ExtractJWT.fromAuthHeaderAsBearerToken(), 43 | secretOrKey : 'secret_restaurant_app' 44 | }, 45 | function (jwtPayload, cb) { 46 | // find the user in db if needed 47 | User.findById(jwtPayload._id) 48 | .then(user => { 49 | return cb(null, user); 50 | }) 51 | .catch(err => { 52 | return cb(err); 53 | }); 54 | } 55 | )); 56 | -------------------------------------------------------------------------------- /config/routes.js: -------------------------------------------------------------------------------- 1 | 'use strict'; 2 | /** 3 | * Expose 4 | */ 5 | 6 | module.exports = function (app, passport) { 7 | 8 | // Home Routes 9 | app.use('/', require('../app/Home')(passport)); 10 | 11 | // User Routes 12 | app.use('/auth', require('../app/User')(passport)); 13 | 14 | // Food Routes 15 | app.use('/food', require('../app/Food')(passport)); 16 | 17 | // Restaurant Routes 18 | app.use('/restaurant', require('../app/Restaurant')(passport)); 19 | 20 | // Order Routes 21 | app.use('/orders', require('../app/Orders')(passport)); 22 | 23 | 24 | /** 25 | * Error handling 26 | */ 27 | app.use(function (err, req, res, next) { 28 | // treat as 404 29 | if (err.message 30 | && (~err.message.indexOf('not found') 31 | || (~err.message.indexOf('Cast to ObjectId failed')))) { 32 | return next(); 33 | } 34 | console.error(err.stack); 35 | // error page 36 | res.status(500).json({ 37 | success: false, 38 | message: err.message, 39 | }); 40 | }); 41 | 42 | // assume 404 since no middleware responded 43 | app.use(function (req, res, next) { 44 | res.status(404).json({ 45 | success: false, 46 | }); 47 | }); 48 | }; 49 | -------------------------------------------------------------------------------- /package.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "express-mongoose-boilerplate", 3 | "description": "A boilerplate application for express mongoose", 4 | "version": "1.0.0", 5 | "author": { 6 | "name": "Codebrahma" 7 | }, 8 | "engines": { 9 | "node": ">=6.x" 10 | }, 11 | "scripts": { 12 | "start": "./node_modules/.bin/cross-env NODE_ENV=development ./node_modules/.bin/nodemon server.js", 13 | "test": "./node_modules/.bin/cross-env NODE_ENV=test ./node_modules/.bin/babel-tape-runner test/test-*.js" 14 | }, 15 | "dependencies": { 16 | "bcrypt": "^2.0.1", 17 | "body-parser": "^1.18.3", 18 | "co": "~4.6.0", 19 | "compression": "~1.7.0", 20 | "connect-flash": "~0.1.1", 21 | "connect-mongo": "~1.3.1", 22 | "cookie-parser": "~1.4.1", 23 | "cookie-session": "~1.2.0", 24 | "cross-env": "~5.0.0", 25 | "crypto-js": "^3.1.9-1", 26 | "csurf": "1.9.0", 27 | "dotenv": "~4.0.0", 28 | "express": "4.15.3", 29 | "express-session": "~1.15.1", 30 | "forever": "~0.15.1", 31 | "jade": "~1.11.0", 32 | "jsonwebtoken": "^8.2.2", 33 | "method-override": "~2.3.5", 34 | "mongoose": "^5.1.4", 35 | "mongoose-user": "0.0.1", 36 | "morgan": "~1.8.1", 37 | "passport": "^0.4.0", 38 | "passport-jwt": "^4.0.0", 39 | "passport-local": "^1.0.0", 40 | "view-helpers": "~0.1.5", 41 | "winston": "~2.3.0" 42 | }, 43 | "devDependencies": { 44 | "babel-eslint": "~7.2.1", 45 | "babel-preset-stage-0": "~6.24.1", 46 | "babel-tape-runner": "~2.0.1", 47 | "eslint": "~4.1.0", 48 | "nodemon": "~1.11.0", 49 | "supertest": "~3.0.0", 50 | "tape": "~4.7.0" 51 | } 52 | } 53 | -------------------------------------------------------------------------------- /server.js: -------------------------------------------------------------------------------- 1 | 'use strict'; 2 | 3 | /* 4 | * nodejs-express-mongoose 5 | */ 6 | 7 | /** 8 | * Module dependencies 9 | */ 10 | 11 | require('dotenv').config(); 12 | 13 | const fs = require('fs'); 14 | const join = require('path').join; 15 | const express = require('express'); 16 | const mongoose = require('mongoose'); 17 | const passport = require('passport'); 18 | const config = require('./config'); 19 | 20 | const passportJWT = require('passport-jwt'); 21 | const ExtractJWT = passportJWT.ExtractJwt; 22 | const JWTStrategy = passportJWT.Strategy; 23 | 24 | var LocalStrategy = require('passport-local').Strategy; 25 | var User = require('./app/User/model'); 26 | var crypto = require('./app/utils/crypto'); 27 | 28 | const port = process.env.PORT || 3000; 29 | 30 | const app = express(); 31 | 32 | mongoose.connect(config.db); 33 | 34 | const connection = mongoose.connection; 35 | 36 | /** 37 | * Expose 38 | */ 39 | 40 | module.exports = { 41 | app, 42 | connection 43 | }; 44 | 45 | // Bootstrap routes 46 | require('./config/express')(app); 47 | 48 | // Adding passport initialization 49 | require('./config/passport/local'); 50 | 51 | require('./config/routes')(app, passport); 52 | 53 | connection 54 | .on('error', console.error.bind(console, 'connection error:')) 55 | .once('open', listen); 56 | 57 | function listen () { 58 | if (app.get('env') === 'test') return; 59 | app.listen(port); 60 | console.log('Express app started on port ' + port); 61 | } 62 | -------------------------------------------------------------------------------- /test/test-home.js: -------------------------------------------------------------------------------- 1 | 'use strict'; 2 | 3 | /* 4 | * Module dependencies. 5 | */ 6 | 7 | const test = require('tape'); 8 | const request = require('supertest'); 9 | const { app } = require('../server'); 10 | 11 | test('Home page', t => { 12 | request(app) 13 | .get('/') 14 | .expect(200) 15 | .end(t.end); 16 | }); 17 | 18 | test.onFinish(() => process.exit(0)); 19 | --------------------------------------------------------------------------------