├── Chapter02
└── mern-simplesetup
│ ├── .babelrc
│ ├── .gitignore
│ ├── LICENSE.md
│ ├── README.md
│ ├── client
│ ├── HelloWorld.js
│ └── main.js
│ ├── nodemon.json
│ ├── package.json
│ ├── server
│ ├── devBundle.js
│ └── server.js
│ ├── template.js
│ ├── webpack.config.client.js
│ ├── webpack.config.client.production.js
│ ├── webpack.config.server.js
│ └── yarn.lock
├── Chapter03 and 04
└── mern-skeleton
│ ├── .babelrc
│ ├── .gitignore
│ ├── LICENSE.md
│ ├── README.md
│ ├── client
│ ├── App.js
│ ├── MainRouter.js
│ ├── assets
│ │ └── images
│ │ │ └── unicornbike.jpg
│ ├── auth
│ │ ├── PrivateRoute.js
│ │ ├── Signin.js
│ │ ├── api-auth.js
│ │ └── auth-helper.js
│ ├── core
│ │ ├── Home.js
│ │ └── Menu.js
│ ├── main.js
│ ├── theme.js
│ └── user
│ │ ├── DeleteUser.js
│ │ ├── EditProfile.js
│ │ ├── Profile.js
│ │ ├── Signup.js
│ │ ├── Users.js
│ │ └── api-user.js
│ ├── config
│ └── config.js
│ ├── nodemon.json
│ ├── package.json
│ ├── server
│ ├── controllers
│ │ ├── auth.controller.js
│ │ └── user.controller.js
│ ├── devBundle.js
│ ├── express.js
│ ├── helpers
│ │ └── dbErrorHandler.js
│ ├── models
│ │ └── user.model.js
│ ├── routes
│ │ ├── auth.routes.js
│ │ └── user.routes.js
│ └── server.js
│ ├── template.js
│ ├── webpack.config.client.js
│ ├── webpack.config.client.production.js
│ ├── webpack.config.server.js
│ └── yarn.lock
├── Chapter05
└── mern-social
│ ├── .babelrc
│ ├── .gitignore
│ ├── LICENSE.md
│ ├── README.md
│ ├── client
│ ├── App.js
│ ├── MainRouter.js
│ ├── assets
│ │ └── images
│ │ │ ├── profile-pic.png
│ │ │ └── unicornbike.jpg
│ ├── auth
│ │ ├── PrivateRoute.js
│ │ ├── Signin.js
│ │ ├── api-auth.js
│ │ └── auth-helper.js
│ ├── core
│ │ ├── Home.js
│ │ └── Menu.js
│ ├── main.js
│ ├── post
│ │ ├── Comments.js
│ │ ├── NewPost.js
│ │ ├── Newsfeed.js
│ │ ├── Post.js
│ │ ├── PostList.js
│ │ └── api-post.js
│ ├── theme.js
│ └── user
│ │ ├── DeleteUser.js
│ │ ├── EditProfile.js
│ │ ├── FindPeople.js
│ │ ├── FollowGrid.js
│ │ ├── FollowProfileButton.js
│ │ ├── Profile.js
│ │ ├── ProfileTabs.js
│ │ ├── Signup.js
│ │ ├── Users.js
│ │ └── api-user.js
│ ├── config
│ └── config.js
│ ├── nodemon.json
│ ├── package.json
│ ├── server
│ ├── controllers
│ │ ├── auth.controller.js
│ │ ├── post.controller.js
│ │ └── user.controller.js
│ ├── devBundle.js
│ ├── express.js
│ ├── helpers
│ │ └── dbErrorHandler.js
│ ├── models
│ │ ├── post.model.js
│ │ └── user.model.js
│ ├── routes
│ │ ├── auth.routes.js
│ │ ├── post.routes.js
│ │ └── user.routes.js
│ └── server.js
│ ├── template.js
│ ├── webpack.config.client.js
│ ├── webpack.config.client.production.js
│ ├── webpack.config.server.js
│ └── yarn.lock
├── Chapter06
└── mern-classroom
│ ├── .babelrc
│ ├── .gitignore
│ ├── LICENSE.md
│ ├── README.md
│ ├── client
│ ├── App.js
│ ├── MainRouter.js
│ ├── assets
│ │ └── images
│ │ │ ├── default.png
│ │ │ └── unicornbike.jpg
│ ├── auth
│ │ ├── PrivateRoute.js
│ │ ├── Signin.js
│ │ ├── api-auth.js
│ │ └── auth-helper.js
│ ├── core
│ │ ├── Home.js
│ │ └── Menu.js
│ ├── course
│ │ ├── Course.js
│ │ ├── Courses.js
│ │ ├── DeleteCourse.js
│ │ ├── EditCourse.js
│ │ ├── MyCourses.js
│ │ ├── NewCourse.js
│ │ ├── NewLesson.js
│ │ └── api-course.js
│ ├── enrollment
│ │ ├── Enroll.js
│ │ ├── Enrollment.js
│ │ ├── Enrollments.js
│ │ └── api-enrollment.js
│ ├── main.js
│ ├── theme.js
│ └── user
│ │ ├── DeleteUser.js
│ │ ├── EditProfile.js
│ │ ├── Profile.js
│ │ ├── Signup.js
│ │ ├── Users.js
│ │ └── api-user.js
│ ├── config
│ └── config.js
│ ├── nodemon.json
│ ├── package.json
│ ├── server
│ ├── controllers
│ │ ├── auth.controller.js
│ │ ├── course.controller.js
│ │ ├── enrollment.controller.js
│ │ └── user.controller.js
│ ├── devBundle.js
│ ├── express.js
│ ├── helpers
│ │ └── dbErrorHandler.js
│ ├── models
│ │ ├── course.model.js
│ │ ├── enrollment.model.js
│ │ └── user.model.js
│ ├── routes
│ │ ├── auth.routes.js
│ │ ├── course.routes.js
│ │ ├── enrollment.routes.js
│ │ └── user.routes.js
│ └── server.js
│ ├── template.js
│ ├── webpack.config.client.js
│ ├── webpack.config.client.production.js
│ ├── webpack.config.server.js
│ └── yarn.lock
├── Chapter07 and 08
└── mern-marketplace
│ ├── .babelrc
│ ├── .gitignore
│ ├── LICENSE.md
│ ├── README.md
│ ├── client
│ ├── App.js
│ ├── MainRouter.js
│ ├── assets
│ │ └── images
│ │ │ ├── default.png
│ │ │ ├── stripeButton.png
│ │ │ └── unicornbike.jpg
│ ├── auth
│ │ ├── PrivateRoute.js
│ │ ├── Signin.js
│ │ ├── api-auth.js
│ │ └── auth-helper.js
│ ├── cart
│ │ ├── AddToCart.js
│ │ ├── Cart.js
│ │ ├── CartItems.js
│ │ ├── Checkout.js
│ │ ├── PlaceOrder.js
│ │ └── cart-helper.js
│ ├── core
│ │ ├── Home.js
│ │ └── Menu.js
│ ├── main.js
│ ├── order
│ │ ├── MyOrders.js
│ │ ├── Order.js
│ │ ├── ProductOrderEdit.js
│ │ ├── ShopOrders.js
│ │ └── api-order.js
│ ├── product
│ │ ├── Categories.js
│ │ ├── DeleteProduct.js
│ │ ├── EditProduct.js
│ │ ├── MyProducts.js
│ │ ├── NewProduct.js
│ │ ├── Product.js
│ │ ├── Products.js
│ │ ├── Search.js
│ │ ├── Suggestions.js
│ │ └── api-product.js
│ ├── shop
│ │ ├── DeleteShop.js
│ │ ├── EditShop.js
│ │ ├── MyShops.js
│ │ ├── NewShop.js
│ │ ├── Shop.js
│ │ ├── Shops.js
│ │ └── api-shop.js
│ ├── theme.js
│ └── user
│ │ ├── DeleteUser.js
│ │ ├── EditProfile.js
│ │ ├── Profile.js
│ │ ├── Signup.js
│ │ ├── StripeConnect.js
│ │ ├── Users.js
│ │ └── api-user.js
│ ├── config
│ └── config.js
│ ├── nodemon.json
│ ├── package.json
│ ├── server
│ ├── controllers
│ │ ├── auth.controller.js
│ │ ├── order.controller.js
│ │ ├── product.controller.js
│ │ ├── shop.controller.js
│ │ └── user.controller.js
│ ├── devBundle.js
│ ├── express.js
│ ├── helpers
│ │ └── dbErrorHandler.js
│ ├── models
│ │ ├── order.model.js
│ │ ├── product.model.js
│ │ ├── shop.model.js
│ │ └── user.model.js
│ ├── routes
│ │ ├── auth.routes.js
│ │ ├── order.routes.js
│ │ ├── product.routes.js
│ │ ├── shop.routes.js
│ │ └── user.routes.js
│ └── server.js
│ ├── template.js
│ ├── webpack.config.client.js
│ ├── webpack.config.client.production.js
│ ├── webpack.config.server.js
│ └── yarn.lock
├── Chapter09
└── mern-marketplace-bidding
│ ├── .babelrc
│ ├── .gitignore
│ ├── LICENSE.md
│ ├── README.md
│ ├── client
│ ├── App.js
│ ├── MainRouter.js
│ ├── assets
│ │ └── images
│ │ │ ├── default.png
│ │ │ ├── stripeButton.png
│ │ │ └── unicornbike.jpg
│ ├── auction
│ │ ├── Auction.js
│ │ ├── Auctions.js
│ │ ├── Bidding.js
│ │ ├── DeleteAuction.js
│ │ ├── EditAuction.js
│ │ ├── MyAuctions.js
│ │ ├── NewAuction.js
│ │ ├── OpenAuctions.js
│ │ ├── Timer.js
│ │ └── api-auction.js
│ ├── auth
│ │ ├── PrivateRoute.js
│ │ ├── Signin.js
│ │ ├── api-auth.js
│ │ └── auth-helper.js
│ ├── cart
│ │ ├── AddToCart.js
│ │ ├── Cart.js
│ │ ├── CartItems.js
│ │ ├── Checkout.js
│ │ ├── PlaceOrder.js
│ │ └── cart-helper.js
│ ├── core
│ │ ├── Home.js
│ │ └── Menu.js
│ ├── main.js
│ ├── order
│ │ ├── MyOrders.js
│ │ ├── Order.js
│ │ ├── ProductOrderEdit.js
│ │ ├── ShopOrders.js
│ │ └── api-order.js
│ ├── product
│ │ ├── Categories.js
│ │ ├── DeleteProduct.js
│ │ ├── EditProduct.js
│ │ ├── MyProducts.js
│ │ ├── NewProduct.js
│ │ ├── Product.js
│ │ ├── Products.js
│ │ ├── Search.js
│ │ ├── Suggestions.js
│ │ └── api-product.js
│ ├── shop
│ │ ├── DeleteShop.js
│ │ ├── EditShop.js
│ │ ├── MyShops.js
│ │ ├── NewShop.js
│ │ ├── Shop.js
│ │ ├── Shops.js
│ │ └── api-shop.js
│ ├── theme.js
│ └── user
│ │ ├── DeleteUser.js
│ │ ├── EditProfile.js
│ │ ├── Profile.js
│ │ ├── Signup.js
│ │ ├── StripeConnect.js
│ │ ├── Users.js
│ │ └── api-user.js
│ ├── config
│ └── config.js
│ ├── nodemon.json
│ ├── package.json
│ ├── server
│ ├── controllers
│ │ ├── auction.controller.js
│ │ ├── auth.controller.js
│ │ ├── bidding.controller.js
│ │ ├── order.controller.js
│ │ ├── product.controller.js
│ │ ├── shop.controller.js
│ │ └── user.controller.js
│ ├── devBundle.js
│ ├── express.js
│ ├── helpers
│ │ └── dbErrorHandler.js
│ ├── models
│ │ ├── auction.model.js
│ │ ├── order.model.js
│ │ ├── product.model.js
│ │ ├── shop.model.js
│ │ └── user.model.js
│ ├── routes
│ │ ├── auction.routes.js
│ │ ├── auth.routes.js
│ │ ├── order.routes.js
│ │ ├── product.routes.js
│ │ ├── shop.routes.js
│ │ └── user.routes.js
│ └── server.js
│ ├── template.js
│ ├── webpack.config.client.js
│ ├── webpack.config.client.production.js
│ ├── webpack.config.server.js
│ └── yarn.lock
├── Chapter10
└── mern-expense-tracker
│ ├── .babelrc
│ ├── .gitignore
│ ├── LICENSE.md
│ ├── README.md
│ ├── client
│ ├── App.js
│ ├── MainRouter.js
│ ├── assets
│ │ └── images
│ │ │ ├── unicornbike.jpg
│ │ │ └── unicorncoin.jpg
│ ├── auth
│ │ ├── PrivateRoute.js
│ │ ├── Signin.js
│ │ ├── api-auth.js
│ │ └── auth-helper.js
│ ├── core
│ │ ├── Home.js
│ │ └── Menu.js
│ ├── expense
│ │ ├── DeleteExpense.js
│ │ ├── ExpenseOverview.js
│ │ ├── Expenses.js
│ │ ├── NewExpense.js
│ │ └── api-expense.js
│ ├── main.js
│ ├── report
│ │ ├── CategoryPie.js
│ │ ├── MonthlyScatter.js
│ │ ├── Reports.js
│ │ └── YearlyBar.js
│ ├── theme.js
│ └── user
│ │ ├── DeleteUser.js
│ │ ├── EditProfile.js
│ │ ├── Profile.js
│ │ ├── Signup.js
│ │ ├── Users.js
│ │ └── api-user.js
│ ├── config
│ └── config.js
│ ├── nodemon.json
│ ├── package.json
│ ├── server
│ ├── controllers
│ │ ├── auth.controller.js
│ │ ├── expense.controller.js
│ │ └── user.controller.js
│ ├── devBundle.js
│ ├── express.js
│ ├── helpers
│ │ └── dbErrorHandler.js
│ ├── models
│ │ ├── expense.model.js
│ │ └── user.model.js
│ ├── routes
│ │ ├── auth.routes.js
│ │ ├── expense.routes.js
│ │ └── user.routes.js
│ └── server.js
│ ├── template.js
│ ├── webpack.config.client.js
│ ├── webpack.config.client.production.js
│ ├── webpack.config.server.js
│ └── yarn.lock
├── Chapter11 and 12
└── mern-mediastream
│ ├── .babelrc
│ ├── .gitignore
│ ├── LICENSE.md
│ ├── README.md
│ ├── client
│ ├── App.js
│ ├── MainRouter.js
│ ├── assets
│ │ └── images
│ │ │ └── unicornbike.jpg
│ ├── auth
│ │ ├── PrivateRoute.js
│ │ ├── Signin.js
│ │ ├── api-auth.js
│ │ └── auth-helper.js
│ ├── core
│ │ ├── Home.js
│ │ └── Menu.js
│ ├── main.js
│ ├── media
│ │ ├── DeleteMedia.js
│ │ ├── EditMedia.js
│ │ ├── Media.js
│ │ ├── MediaList.js
│ │ ├── MediaPlayer.js
│ │ ├── NewMedia.js
│ │ ├── PlayMedia.js
│ │ ├── RelatedMedia.js
│ │ └── api-media.js
│ ├── routeConfig.js
│ ├── theme.js
│ └── user
│ │ ├── DeleteUser.js
│ │ ├── EditProfile.js
│ │ ├── Profile.js
│ │ ├── Signup.js
│ │ ├── Users.js
│ │ └── api-user.js
│ ├── config
│ └── config.js
│ ├── nodemon.json
│ ├── package.json
│ ├── server
│ ├── controllers
│ │ ├── auth.controller.js
│ │ ├── media.controller.js
│ │ └── user.controller.js
│ ├── devBundle.js
│ ├── express.js
│ ├── helpers
│ │ └── dbErrorHandler.js
│ ├── models
│ │ ├── media.model.js
│ │ └── user.model.js
│ ├── routes
│ │ ├── auth.routes.js
│ │ ├── media.routes.js
│ │ └── user.routes.js
│ └── server.js
│ ├── template.js
│ ├── webpack.config.client.js
│ ├── webpack.config.client.production.js
│ ├── webpack.config.server.js
│ └── yarn.lock
├── Chapter13
└── MERNVR
│ ├── .babelrc
│ ├── .flowconfig
│ ├── .gitignore
│ ├── .watchmanconfig
│ ├── README.md
│ ├── __tests__
│ └── index-test.js
│ ├── client.js
│ ├── index.html
│ ├── index.js
│ ├── package.json
│ ├── rn-cli.config.js
│ ├── static_assets
│ ├── 360_world.jpg
│ ├── 360_world_black.jpg
│ ├── clog-up.mp3
│ ├── collect.mp3
│ └── happy-bot.mp3
│ └── yarn.lock
├── Chapter14
└── mern-vrgame
│ ├── .babelrc
│ ├── .gitignore
│ ├── LICENSE.md
│ ├── README.md
│ ├── client
│ ├── App.js
│ ├── MainRouter.js
│ ├── assets
│ │ └── images
│ │ │ ├── unicornbike.jpg
│ │ │ └── unicorncoin.jpg
│ ├── auth
│ │ ├── PrivateRoute.js
│ │ ├── Signin.js
│ │ ├── api-auth.js
│ │ └── auth-helper.js
│ ├── core
│ │ ├── Home.js
│ │ └── Menu.js
│ ├── game
│ │ ├── DeleteGame.js
│ │ ├── EditGame.js
│ │ ├── GameDetail.js
│ │ ├── GameForm.js
│ │ ├── NewGame.js
│ │ ├── VRObjectForm.js
│ │ └── api-game.js
│ ├── main.js
│ ├── theme.js
│ └── user
│ │ ├── DeleteUser.js
│ │ ├── EditProfile.js
│ │ ├── Profile.js
│ │ ├── Signup.js
│ │ ├── Users.js
│ │ └── api-user.js
│ ├── config
│ └── config.js
│ ├── dist
│ ├── client.bundle.js
│ ├── index.bundle.js
│ └── static_assets
│ │ ├── 360_world.jpg
│ │ ├── 360_world_black.jpg
│ │ ├── clog-up.mp3
│ │ ├── collect.mp3
│ │ └── happy-bot.mp3
│ ├── nodemon.json
│ ├── package.json
│ ├── server
│ ├── controllers
│ │ ├── auth.controller.js
│ │ ├── game.controller.js
│ │ └── user.controller.js
│ ├── devBundle.js
│ ├── express.js
│ ├── helpers
│ │ └── dbErrorHandler.js
│ ├── models
│ │ ├── game.model.js
│ │ └── user.model.js
│ ├── routes
│ │ ├── auth.routes.js
│ │ ├── game.routes.js
│ │ └── user.routes.js
│ ├── server.js
│ └── vr
│ │ └── index.html
│ ├── template.js
│ ├── webpack.config.client.js
│ ├── webpack.config.client.production.js
│ ├── webpack.config.server.js
│ └── yarn.lock
├── LICENSE
└── README.md
/Chapter02/mern-simplesetup/.babelrc:
--------------------------------------------------------------------------------
1 | {
2 | "presets": [
3 | ["@babel/preset-env",
4 | {
5 | "targets": {
6 | "node": "current"
7 | }
8 | }
9 | ],
10 | "@babel/preset-react"
11 | ],
12 | "plugins": [
13 | "react-hot-loader/babel"
14 | ]
15 | }
16 |
--------------------------------------------------------------------------------
/Chapter02/mern-simplesetup/.gitignore:
--------------------------------------------------------------------------------
1 | /node_modules/
2 | /dist/
3 | /data/
4 | npm-debug.log
5 |
--------------------------------------------------------------------------------
/Chapter02/mern-simplesetup/LICENSE.md:
--------------------------------------------------------------------------------
1 | MIT License
2 |
3 | Copyright (c) 2018 Shama Hoque
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 |
--------------------------------------------------------------------------------
/Chapter02/mern-simplesetup/README.md:
--------------------------------------------------------------------------------
1 | # MERN - Simple Setup Check
2 |
3 | ### [Live Demo](http://simplesetup2.mernbook.com/ "MERN Simple Setup")
4 |
5 | #### What you need to run this code
6 | 1. Node (13.12.0)
7 | 2. NPM (6.14.4) or Yarn (1.22.4)
8 | 3. MongoDB (4.2.0)
9 |
10 |
11 | #### How to run this code
12 | 1. Clone this repository
13 | 2. Open command line in the cloned folder,
14 | - To install dependencies, run ``` npm install ``` or ``` yarn ```
15 | - To run the application for development, run ``` npm run development ``` or ``` yarn development ```
16 | 4. Open [localhost:3000](http://localhost:3000/) in the browser
17 |
18 |
--------------------------------------------------------------------------------
/Chapter02/mern-simplesetup/client/HelloWorld.js:
--------------------------------------------------------------------------------
1 | import React from 'react'
2 | import { hot } from 'react-hot-loader'
3 |
4 | const HelloWorld = () => {
5 | return (
6 |
7 |
Hello World!
8 |
9 | )
10 | }
11 |
12 | export default hot(module)(HelloWorld)
13 |
--------------------------------------------------------------------------------
/Chapter02/mern-simplesetup/client/main.js:
--------------------------------------------------------------------------------
1 | import React from 'react'
2 | import { render } from 'react-dom'
3 | import HelloWorld from './HelloWorld'
4 |
5 |
6 | render(, document.getElementById('root'))
7 |
--------------------------------------------------------------------------------
/Chapter02/mern-simplesetup/nodemon.json:
--------------------------------------------------------------------------------
1 | {
2 | "verbose": false,
3 | "watch": [
4 | "./server"
5 | ],
6 | "exec": "webpack --mode=development --config webpack.config.server.js && node ./dist/server.generated.js"
7 | }
8 |
--------------------------------------------------------------------------------
/Chapter02/mern-simplesetup/package.json:
--------------------------------------------------------------------------------
1 | {
2 | "name": "mern-simplesetup",
3 | "version": "2.0.0",
4 | "description": "A quick check for your MERN stack installation",
5 | "author": "Shama Hoque",
6 | "license": "MIT",
7 | "keywords": [
8 | "react",
9 | "node",
10 | "express",
11 | "mongodb",
12 | "mern"
13 | ],
14 | "repository": {
15 | "type": "git",
16 | "url": "https://github.com/shamahoque/mern-simplesetup.git"
17 | },
18 | "homepage": "https://github.com/shamahoque/mern-simplesetup",
19 | "main": "template.js",
20 | "scripts": {
21 | "development": "nodemon",
22 | "build": "webpack --config webpack.config.client.production.js && webpack --mode=production --config webpack.config.server.js",
23 | "start": "NODE_ENV=production node ./dist/server.generated.js"
24 | },
25 | "engines": {
26 | "node": "13.12.0",
27 | "npm": "6.14.4"
28 | },
29 | "dependencies": {
30 | "@hot-loader/react-dom": "16.13.0",
31 | "express": "4.17.1",
32 | "mongodb": "3.5.5",
33 | "react": "16.13.1",
34 | "react-dom": "16.13.1",
35 | "react-hot-loader": "4.12.20"
36 | },
37 | "devDependencies": {
38 | "@babel/core": "7.9.0",
39 | "@babel/preset-env": "7.9.0",
40 | "@babel/preset-react": "7.9.4",
41 | "babel-loader": "8.1.0",
42 | "nodemon": "2.0.2",
43 | "webpack": "4.42.1",
44 | "webpack-cli": "3.3.11",
45 | "webpack-dev-middleware": "3.7.2",
46 | "webpack-hot-middleware": "2.25.0",
47 | "webpack-node-externals": "1.7.2"
48 | }
49 | }
50 |
--------------------------------------------------------------------------------
/Chapter02/mern-simplesetup/server/devBundle.js:
--------------------------------------------------------------------------------
1 | import webpack from 'webpack'
2 | import webpackMiddleware from 'webpack-dev-middleware'
3 | import webpackHotMiddleware from 'webpack-hot-middleware'
4 | import webpackConfig from './../webpack.config.client.js'
5 |
6 | const compile = (app) => {
7 | if(process.env.NODE_ENV == "development"){
8 | const compiler = webpack(webpackConfig)
9 | const middleware = webpackMiddleware(compiler, {
10 | publicPath: webpackConfig.output.publicPath
11 | })
12 | app.use(middleware)
13 | app.use(webpackHotMiddleware(compiler))
14 | }
15 | }
16 |
17 | export default {
18 | compile
19 | }
20 |
--------------------------------------------------------------------------------
/Chapter02/mern-simplesetup/server/server.js:
--------------------------------------------------------------------------------
1 | import path from 'path'
2 | import express from 'express'
3 | import { MongoClient } from 'mongodb'
4 | import template from './../template'
5 | //comment out before building for production
6 | import devBundle from './devBundle'
7 |
8 | const app = express()
9 | //comment out before building for production
10 | devBundle.compile(app)
11 |
12 | const CURRENT_WORKING_DIR = process.cwd()
13 | app.use('/dist', express.static(path.join(CURRENT_WORKING_DIR, 'dist')))
14 |
15 | app.get('/', (req, res) => {
16 | res.status(200).send(template())
17 | })
18 |
19 | let port = process.env.PORT || 3000
20 | app.listen(port, function onStart(err) {
21 | if (err) {
22 | console.log(err)
23 | }
24 | console.info('Server started on port %s.', port)
25 | })
26 |
27 | // Database Connection URL
28 | const url = process.env.MONGODB_URI || 'mongodb://localhost:27017/mernSimpleSetup'
29 | // Use connect method to connect to the server
30 | MongoClient.connect(url, { useNewUrlParser: true, useUnifiedTopology: true },(err, db)=>{
31 | console.log("Connected successfully to mongodb server")
32 | db.close()
33 | })
34 |
--------------------------------------------------------------------------------
/Chapter02/mern-simplesetup/template.js:
--------------------------------------------------------------------------------
1 | export default () => {
2 | return `
3 |
4 |
5 |
6 | MERN Kickstart
7 |
8 |
9 |
10 |
11 |
12 | `
13 | }
14 |
--------------------------------------------------------------------------------
/Chapter02/mern-simplesetup/webpack.config.client.js:
--------------------------------------------------------------------------------
1 | const path = require('path')
2 | const webpack = require('webpack')
3 | const CURRENT_WORKING_DIR = process.cwd()
4 |
5 | const config = {
6 | name: "browser",
7 | mode: "development",
8 | devtool: 'eval-source-map',
9 | entry: [
10 | 'webpack-hot-middleware/client?reload=true',
11 | path.join(CURRENT_WORKING_DIR, 'client/main.js')
12 | ],
13 | output: {
14 | path: path.join(CURRENT_WORKING_DIR , '/dist'),
15 | filename: 'bundle.js',
16 | publicPath: '/dist/'
17 | },
18 | module: {
19 | rules: [
20 | {
21 | test: /\.jsx?$/,
22 | exclude: /node_modules/,
23 | use: [
24 | 'babel-loader'
25 | ]
26 | }
27 | ]
28 | },
29 | plugins: [
30 | new webpack.HotModuleReplacementPlugin(),
31 | new webpack.NoEmitOnErrorsPlugin()
32 | ],
33 | resolve: {
34 | alias: {
35 | 'react-dom': '@hot-loader/react-dom'
36 | }
37 | }
38 | }
39 |
40 | module.exports = config
41 |
--------------------------------------------------------------------------------
/Chapter02/mern-simplesetup/webpack.config.client.production.js:
--------------------------------------------------------------------------------
1 | const path = require('path')
2 | const CURRENT_WORKING_DIR = process.cwd()
3 |
4 | const config = {
5 | mode: "production",
6 | entry: [
7 | path.join(CURRENT_WORKING_DIR, 'client/main.js')
8 | ],
9 | output: {
10 | path: path.join(CURRENT_WORKING_DIR , '/dist'),
11 | filename: 'bundle.js',
12 | publicPath: "/dist/"
13 | },
14 | module: {
15 | rules: [
16 | {
17 | test: /\.jsx?$/,
18 | exclude: /node_modules/,
19 | use: [
20 | 'babel-loader'
21 | ]
22 | }
23 | ]
24 | }
25 | }
26 |
27 | module.exports = config
28 |
--------------------------------------------------------------------------------
/Chapter02/mern-simplesetup/webpack.config.server.js:
--------------------------------------------------------------------------------
1 | const path = require('path')
2 | const nodeExternals = require('webpack-node-externals')
3 | const CURRENT_WORKING_DIR = process.cwd()
4 |
5 | const config = {
6 | name: "server",
7 | entry: [ path.join(CURRENT_WORKING_DIR , './server/server.js') ],
8 | target: "node",
9 | output: {
10 | path: path.join(CURRENT_WORKING_DIR , '/dist/'),
11 | filename: "server.generated.js",
12 | publicPath: '/dist/',
13 | libraryTarget: "commonjs2"
14 | },
15 | externals: [nodeExternals()],
16 | module: {
17 | rules: [
18 | {
19 | test: /\.js$/,
20 | exclude: /node_modules/,
21 | use: [ 'babel-loader' ]
22 | }
23 | ]
24 | }
25 |
26 | }
27 |
28 | module.exports = config
29 |
--------------------------------------------------------------------------------
/Chapter03 and 04/mern-skeleton/.babelrc:
--------------------------------------------------------------------------------
1 | {
2 | "presets": [
3 | ["@babel/preset-env",
4 | {
5 | "targets": {
6 | "node": "current"
7 | }
8 | }
9 | ],
10 | "@babel/preset-react"
11 | ],
12 | "plugins": [
13 | "react-hot-loader/babel"
14 | ]
15 | }
16 |
--------------------------------------------------------------------------------
/Chapter03 and 04/mern-skeleton/.gitignore:
--------------------------------------------------------------------------------
1 | /node_modules/
2 | /dist/
3 | /data/
4 | npm-debug.log
5 |
--------------------------------------------------------------------------------
/Chapter03 and 04/mern-skeleton/LICENSE.md:
--------------------------------------------------------------------------------
1 | MIT License
2 |
3 | Copyright (c) 2018 Shama Hoque
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 |
--------------------------------------------------------------------------------
/Chapter03 and 04/mern-skeleton/README.md:
--------------------------------------------------------------------------------
1 | # MERN Skeleton
2 |
3 | A skeleton application with basic user CRUD and auth features - developed using React, Node, Express and MongoDB.
4 |
5 | 
6 |
7 | ### [Live Demo](http://skeleton2.mernbook.com/ "MERN Skeleton")
8 |
9 | #### What you need to run this code
10 | 1. Node (13.12.0)
11 | 2. NPM (6.14.4) or Yarn (1.22.4)
12 | 3. MongoDB (4.2.0)
13 |
14 | #### How to run this code
15 | 1. Make sure MongoDB is running on your system
16 | 2. Clone this repository
17 | 3. Open command line in the cloned folder,
18 | - To install dependencies, run ``` npm install ``` or ``` yarn ```
19 | - To run the application for development, run ``` npm run development ``` or ``` yarn development ```
20 | 4. Open [localhost:3000](http://localhost:3000/) in the browser
21 | ----
22 |
--------------------------------------------------------------------------------
/Chapter03 and 04/mern-skeleton/client/App.js:
--------------------------------------------------------------------------------
1 | import React from 'react'
2 | import MainRouter from './MainRouter'
3 | import {BrowserRouter} from 'react-router-dom'
4 | import { ThemeProvider } from '@material-ui/styles'
5 | import theme from './theme'
6 | import { hot } from 'react-hot-loader'
7 |
8 | const App = () => {
9 | React.useEffect(() => {
10 | const jssStyles = document.querySelector('#jss-server-side')
11 | if (jssStyles) {
12 | jssStyles.parentNode.removeChild(jssStyles)
13 | }
14 | }, [])
15 | return (
16 |
17 |
18 |
19 |
20 |
21 | )}
22 |
23 | export default hot(module)(App)
24 |
--------------------------------------------------------------------------------
/Chapter03 and 04/mern-skeleton/client/MainRouter.js:
--------------------------------------------------------------------------------
1 | import React from 'react'
2 | import {Route, Switch} from 'react-router-dom'
3 | import Home from './core/Home'
4 | import Users from './user/Users'
5 | import Signup from './user/Signup'
6 | import Signin from './auth/Signin'
7 | import EditProfile from './user/EditProfile'
8 | import Profile from './user/Profile'
9 | import PrivateRoute from './auth/PrivateRoute'
10 | import Menu from './core/Menu'
11 |
12 | const MainRouter = () => {
13 | return (
14 |
15 |
16 |
17 |
18 |
19 |
20 |
21 |
22 |
23 |
)
24 | }
25 |
26 | export default MainRouter
27 |
--------------------------------------------------------------------------------
/Chapter03 and 04/mern-skeleton/client/assets/images/unicornbike.jpg:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/PacktPublishing/Full-Stack-React-Projects-Second-Edition/c4007202bd767682b9122cb196b80cd4952658f2/Chapter03 and 04/mern-skeleton/client/assets/images/unicornbike.jpg
--------------------------------------------------------------------------------
/Chapter03 and 04/mern-skeleton/client/auth/PrivateRoute.js:
--------------------------------------------------------------------------------
1 | import React, { Component } from 'react'
2 | import { Route, Redirect } from 'react-router-dom'
3 | import auth from './auth-helper'
4 |
5 | const PrivateRoute = ({ component: Component, ...rest }) => (
6 | (
7 | auth.isAuthenticated() ? (
8 |
9 | ) : (
10 |
14 | )
15 | )}/>
16 | )
17 |
18 | export default PrivateRoute
19 |
--------------------------------------------------------------------------------
/Chapter03 and 04/mern-skeleton/client/auth/api-auth.js:
--------------------------------------------------------------------------------
1 | const signin = async (user) => {
2 | try {
3 | let response = await fetch('/auth/signin/', {
4 | method: 'POST',
5 | headers: {
6 | 'Accept': 'application/json',
7 | 'Content-Type': 'application/json'
8 | },
9 | credentials: 'include',
10 | body: JSON.stringify(user)
11 | })
12 | return await response.json()
13 | } catch(err) {
14 | console.log(err)
15 | }
16 | }
17 |
18 | const signout = async () => {
19 | try {
20 | let response = await fetch('/auth/signout/', { method: 'GET' })
21 | return await response.json()
22 | } catch(err) {
23 | console.log(err)
24 | }
25 | }
26 |
27 | export {
28 | signin,
29 | signout
30 | }
--------------------------------------------------------------------------------
/Chapter03 and 04/mern-skeleton/client/auth/auth-helper.js:
--------------------------------------------------------------------------------
1 | import { signout } from './api-auth.js'
2 |
3 | const auth = {
4 | isAuthenticated() {
5 | if (typeof window == "undefined")
6 | return false
7 |
8 | if (sessionStorage.getItem('jwt'))
9 | return JSON.parse(sessionStorage.getItem('jwt'))
10 | else
11 | return false
12 | },
13 | authenticate(jwt, cb) {
14 | if (typeof window !== "undefined")
15 | sessionStorage.setItem('jwt', JSON.stringify(jwt))
16 | cb()
17 | },
18 | clearJWT(cb) {
19 | if (typeof window !== "undefined")
20 | sessionStorage.removeItem('jwt')
21 | cb()
22 | //optional
23 | signout().then((data) => {
24 | document.cookie = "t=; expires=Thu, 01 Jan 1970 00:00:00 UTC; path=/;"
25 | })
26 | }
27 | }
28 |
29 | export default auth
30 |
--------------------------------------------------------------------------------
/Chapter03 and 04/mern-skeleton/client/main.js:
--------------------------------------------------------------------------------
1 | import React from 'react'
2 | import { hydrate } from 'react-dom'
3 | import App from './App'
4 |
5 | hydrate(, document.getElementById('root'))
6 |
--------------------------------------------------------------------------------
/Chapter03 and 04/mern-skeleton/client/theme.js:
--------------------------------------------------------------------------------
1 | import { createMuiTheme } from '@material-ui/core/styles'
2 | import { pink } from '@material-ui/core/colors'
3 |
4 | const theme = createMuiTheme({
5 | typography: {
6 | useNextVariants: true,
7 | },
8 | palette: {
9 | primary: {
10 | light: '#5c67a3',
11 | main: '#3f4771',
12 | dark: '#2e355b',
13 | contrastText: '#fff',
14 | },
15 | secondary: {
16 | light: '#ff79b0',
17 | main: '#ff4081',
18 | dark: '#c60055',
19 | contrastText: '#000',
20 | },
21 | openTitle: '#3f4771',
22 | protectedTitle: pink['400'],
23 | type: 'light'
24 | }
25 | })
26 |
27 | export default theme
--------------------------------------------------------------------------------
/Chapter03 and 04/mern-skeleton/config/config.js:
--------------------------------------------------------------------------------
1 | const config = {
2 | env: process.env.NODE_ENV || 'development',
3 | port: process.env.PORT || 3000,
4 | jwtSecret: process.env.JWT_SECRET || "YOUR_secret_key",
5 | mongoUri: process.env.MONGODB_URI ||
6 | process.env.MONGO_HOST ||
7 | 'mongodb://' + (process.env.IP || 'localhost') + ':' +
8 | (process.env.MONGO_PORT || '27017') +
9 | '/mernproject'
10 | }
11 |
12 | export default config
13 |
--------------------------------------------------------------------------------
/Chapter03 and 04/mern-skeleton/nodemon.json:
--------------------------------------------------------------------------------
1 | {
2 | "verbose": false,
3 | "watch": [
4 | "./server"
5 | ],
6 | "exec": "webpack --mode=development --config webpack.config.server.js && node ./dist/server.generated.js"
7 | }
8 |
--------------------------------------------------------------------------------
/Chapter03 and 04/mern-skeleton/server/devBundle.js:
--------------------------------------------------------------------------------
1 | import config from './../config/config'
2 | import webpack from 'webpack'
3 | import webpackMiddleware from 'webpack-dev-middleware'
4 | import webpackHotMiddleware from 'webpack-hot-middleware'
5 | import webpackConfig from './../webpack.config.client.js'
6 |
7 | const compile = (app) => {
8 | if(config.env === "development"){
9 | const compiler = webpack(webpackConfig)
10 | const middleware = webpackMiddleware(compiler, {
11 | publicPath: webpackConfig.output.publicPath
12 | })
13 | app.use(middleware)
14 | app.use(webpackHotMiddleware(compiler))
15 | }
16 | }
17 |
18 | export default {
19 | compile
20 | }
21 |
--------------------------------------------------------------------------------
/Chapter03 and 04/mern-skeleton/server/helpers/dbErrorHandler.js:
--------------------------------------------------------------------------------
1 | 'use strict'
2 |
3 | /**
4 | * Get unique error field name
5 | */
6 | const getUniqueErrorMessage = (err) => {
7 | let output
8 | try {
9 | let fieldName = err.message.substring(err.message.lastIndexOf('.$') + 2, err.message.lastIndexOf('_1'))
10 | output = fieldName.charAt(0).toUpperCase() + fieldName.slice(1) + ' already exists'
11 | } catch (ex) {
12 | output = 'Unique field already exists'
13 | }
14 |
15 | return output
16 | }
17 |
18 | /**
19 | * Get the error message from error object
20 | */
21 | const getErrorMessage = (err) => {
22 | let message = ''
23 |
24 | if (err.code) {
25 | switch (err.code) {
26 | case 11000:
27 | case 11001:
28 | message = getUniqueErrorMessage(err)
29 | break
30 | default:
31 | message = 'Something went wrong'
32 | }
33 | } else {
34 | for (let errName in err.errors) {
35 | if (err.errors[errName].message) message = err.errors[errName].message
36 | }
37 | }
38 |
39 | return message
40 | }
41 |
42 | export default {getErrorMessage}
43 |
--------------------------------------------------------------------------------
/Chapter03 and 04/mern-skeleton/server/routes/auth.routes.js:
--------------------------------------------------------------------------------
1 | import express from 'express'
2 | import authCtrl from '../controllers/auth.controller'
3 |
4 | const router = express.Router()
5 |
6 | router.route('/auth/signin')
7 | .post(authCtrl.signin)
8 | router.route('/auth/signout')
9 | .get(authCtrl.signout)
10 |
11 | export default router
12 |
--------------------------------------------------------------------------------
/Chapter03 and 04/mern-skeleton/server/routes/user.routes.js:
--------------------------------------------------------------------------------
1 | import express from 'express'
2 | import userCtrl from '../controllers/user.controller'
3 | import authCtrl from '../controllers/auth.controller'
4 |
5 | const router = express.Router()
6 |
7 | router.route('/api/users')
8 | .get(userCtrl.list)
9 | .post(userCtrl.create)
10 |
11 | router.route('/api/users/:userId')
12 | .get(authCtrl.requireSignin, userCtrl.read)
13 | .put(authCtrl.requireSignin, authCtrl.hasAuthorization, userCtrl.update)
14 | .delete(authCtrl.requireSignin, authCtrl.hasAuthorization, userCtrl.remove)
15 |
16 | router.param('userId', userCtrl.userByID)
17 |
18 | export default router
19 |
--------------------------------------------------------------------------------
/Chapter03 and 04/mern-skeleton/server/server.js:
--------------------------------------------------------------------------------
1 | import config from './../config/config'
2 | import app from './express'
3 | import mongoose from 'mongoose'
4 |
5 | // Connection URL
6 | mongoose.Promise = global.Promise
7 | mongoose.connect(config.mongoUri, { useNewUrlParser: true, useCreateIndex: true, useUnifiedTopology: true })
8 | mongoose.connection.on('error', () => {
9 | throw new Error(`unable to connect to database: ${config.mongoUri}`)
10 | })
11 |
12 | app.listen(config.port, (err) => {
13 | if (err) {
14 | console.log(err)
15 | }
16 | console.info('Server started on port %s.', config.port)
17 | })
18 |
--------------------------------------------------------------------------------
/Chapter03 and 04/mern-skeleton/template.js:
--------------------------------------------------------------------------------
1 | export default ({markup, css}) => {
2 | return `
3 |
4 |
5 |
6 |
10 | MERN Skeleton
11 |
12 |
13 |
19 |
20 |
21 | ${markup}
22 |
23 |
24 |
25 | `
26 | }
27 |
--------------------------------------------------------------------------------
/Chapter03 and 04/mern-skeleton/webpack.config.client.js:
--------------------------------------------------------------------------------
1 | const path = require('path')
2 | const webpack = require('webpack')
3 | const CURRENT_WORKING_DIR = process.cwd()
4 |
5 | const config = {
6 | name: "browser",
7 | mode: "development",
8 | devtool: 'eval-source-map',
9 | entry: [
10 | 'webpack-hot-middleware/client?reload=true',
11 | path.join(CURRENT_WORKING_DIR, 'client/main.js')
12 | ],
13 | output: {
14 | path: path.join(CURRENT_WORKING_DIR , '/dist'),
15 | filename: 'bundle.js',
16 | publicPath: '/dist/'
17 | },
18 | module: {
19 | rules: [
20 | {
21 | test: /\.jsx?$/,
22 | exclude: /node_modules/,
23 | use: [
24 | 'babel-loader'
25 | ]
26 | },
27 | {
28 | test: /\.(ttf|eot|svg|gif|jpg|png)(\?[\s\S]+)?$/,
29 | use: 'file-loader'
30 | }
31 | ]
32 | },
33 | plugins: [
34 | new webpack.HotModuleReplacementPlugin(),
35 | new webpack.NoEmitOnErrorsPlugin()
36 | ],
37 | resolve: {
38 | alias: {
39 | 'react-dom': '@hot-loader/react-dom'
40 | }
41 | }
42 | }
43 |
44 | module.exports = config
45 |
--------------------------------------------------------------------------------
/Chapter03 and 04/mern-skeleton/webpack.config.client.production.js:
--------------------------------------------------------------------------------
1 | const path = require('path')
2 | const CURRENT_WORKING_DIR = process.cwd()
3 |
4 | const config = {
5 | mode: "production",
6 | entry: [
7 | path.join(CURRENT_WORKING_DIR, 'client/main.js')
8 | ],
9 | output: {
10 | path: path.join(CURRENT_WORKING_DIR , '/dist'),
11 | filename: 'bundle.js',
12 | publicPath: "/dist/"
13 | },
14 | module: {
15 | rules: [
16 | {
17 | test: /\.jsx?$/,
18 | exclude: /node_modules/,
19 | use: [
20 | 'babel-loader'
21 | ]
22 | },
23 | {
24 | test: /\.(ttf|eot|svg|gif|jpg|png)(\?[\s\S]+)?$/,
25 | use: 'file-loader'
26 | }
27 | ]
28 | }
29 | }
30 |
31 | module.exports = config
32 |
--------------------------------------------------------------------------------
/Chapter03 and 04/mern-skeleton/webpack.config.server.js:
--------------------------------------------------------------------------------
1 | const path = require('path')
2 | const nodeExternals = require('webpack-node-externals')
3 | const CURRENT_WORKING_DIR = process.cwd()
4 |
5 | const config = {
6 | name: "server",
7 | entry: [ path.join(CURRENT_WORKING_DIR , './server/server.js') ],
8 | target: "node",
9 | output: {
10 | path: path.join(CURRENT_WORKING_DIR , '/dist/'),
11 | filename: "server.generated.js",
12 | publicPath: '/dist/',
13 | libraryTarget: "commonjs2"
14 | },
15 | externals: [nodeExternals()],
16 | module: {
17 | rules: [
18 | {
19 | test: /\.js$/,
20 | exclude: /node_modules/,
21 | use: [ 'babel-loader' ]
22 | },
23 | {
24 | test: /\.(ttf|eot|svg|gif|jpg|png)(\?[\s\S]+)?$/,
25 | use: 'file-loader'
26 | }
27 | ]
28 | }
29 | }
30 |
31 | module.exports = config
32 |
--------------------------------------------------------------------------------
/Chapter05/mern-social/.babelrc:
--------------------------------------------------------------------------------
1 | {
2 | "presets": [
3 | ["@babel/preset-env",
4 | {
5 | "targets": {
6 | "node": "current"
7 | }
8 | }
9 | ],
10 | "@babel/preset-react"
11 | ],
12 | "plugins": [
13 | "react-hot-loader/babel"
14 | ]
15 | }
16 |
--------------------------------------------------------------------------------
/Chapter05/mern-social/.gitignore:
--------------------------------------------------------------------------------
1 | /node_modules/
2 | /dist/
3 | /data/
4 | npm-debug.log
5 |
--------------------------------------------------------------------------------
/Chapter05/mern-social/LICENSE.md:
--------------------------------------------------------------------------------
1 | MIT License
2 |
3 | Copyright (c) 2018 Shama Hoque
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 |
--------------------------------------------------------------------------------
/Chapter05/mern-social/README.md:
--------------------------------------------------------------------------------
1 | # MERN Social
2 |
3 | A simple social media application with users, posts, likes and comments - developed using React, Node, Express and MongoDB.
4 |
5 | 
6 |
7 | ### [Live Demo](http://social2.mernbook.com/ "MERN Social")
8 |
9 | #### What you need to run this code
10 | 1. Node (13.12.0)
11 | 2. NPM (6.14.4) or Yarn (1.22.4)
12 | 3. MongoDB (4.2.0)
13 |
14 | #### How to run this code
15 | 1. Make sure MongoDB is running on your system
16 | 2. Clone this repository
17 | 3. Open command line in the cloned folder,
18 | - To install dependencies, run ``` npm install ``` or ``` yarn ```
19 | - To run the application for development, run ``` npm run development ``` or ``` yarn development ```
20 | 4. Open [localhost:3000](http://localhost:3000/) in the browser
21 | ----
--------------------------------------------------------------------------------
/Chapter05/mern-social/client/App.js:
--------------------------------------------------------------------------------
1 | import React from 'react'
2 | import MainRouter from './MainRouter'
3 | import {BrowserRouter} from 'react-router-dom'
4 | import { ThemeProvider } from '@material-ui/styles'
5 | import theme from './theme'
6 | import { hot } from 'react-hot-loader'
7 |
8 | const App = () => {
9 | React.useEffect(() => {
10 | const jssStyles = document.querySelector('#jss-server-side')
11 | if (jssStyles) {
12 | jssStyles.parentNode.removeChild(jssStyles)
13 | }
14 | }, [])
15 | return (
16 |
17 |
18 |
19 |
20 |
21 | )}
22 |
23 | export default hot(module)(App)
24 |
--------------------------------------------------------------------------------
/Chapter05/mern-social/client/MainRouter.js:
--------------------------------------------------------------------------------
1 | import React, {Component} from 'react'
2 | import {Route, Switch} from 'react-router-dom'
3 | import Home from './core/Home'
4 | import Users from './user/Users'
5 | import Signup from './user/Signup'
6 | import Signin from './auth/Signin'
7 | import EditProfile from './user/EditProfile'
8 | import Profile from './user/Profile'
9 | import PrivateRoute from './auth/PrivateRoute'
10 | import Menu from './core/Menu'
11 |
12 | const MainRouter = () => {
13 | return (
14 |
15 |
16 |
17 |
18 |
19 |
20 |
21 |
22 |
23 |
)
24 | }
25 |
26 | export default MainRouter
27 |
--------------------------------------------------------------------------------
/Chapter05/mern-social/client/assets/images/profile-pic.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/PacktPublishing/Full-Stack-React-Projects-Second-Edition/c4007202bd767682b9122cb196b80cd4952658f2/Chapter05/mern-social/client/assets/images/profile-pic.png
--------------------------------------------------------------------------------
/Chapter05/mern-social/client/assets/images/unicornbike.jpg:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/PacktPublishing/Full-Stack-React-Projects-Second-Edition/c4007202bd767682b9122cb196b80cd4952658f2/Chapter05/mern-social/client/assets/images/unicornbike.jpg
--------------------------------------------------------------------------------
/Chapter05/mern-social/client/auth/PrivateRoute.js:
--------------------------------------------------------------------------------
1 | import React, { Component } from 'react'
2 | import { Route, Redirect } from 'react-router-dom'
3 | import auth from './auth-helper'
4 |
5 | const PrivateRoute = ({ component: Component, ...rest }) => (
6 | (
7 | auth.isAuthenticated() ? (
8 |
9 | ) : (
10 |
14 | )
15 | )}/>
16 | )
17 |
18 | export default PrivateRoute
19 |
--------------------------------------------------------------------------------
/Chapter05/mern-social/client/auth/api-auth.js:
--------------------------------------------------------------------------------
1 | const signin = async (user) => {
2 | try {
3 | let response = await fetch('/auth/signin/', {
4 | method: 'POST',
5 | headers: {
6 | 'Accept': 'application/json',
7 | 'Content-Type': 'application/json'
8 | },
9 | credentials: 'include',
10 | body: JSON.stringify(user)
11 | })
12 | return await response.json()
13 | } catch(err) {
14 | console.log(err)
15 | }
16 | }
17 |
18 | const signout = async () => {
19 | try {
20 | let response = await fetch('/auth/signout/', { method: 'GET' })
21 | return await response.json()
22 | } catch(err) {
23 | console.log(err)
24 | }
25 | }
26 |
27 | export {
28 | signin,
29 | signout
30 | }
31 |
--------------------------------------------------------------------------------
/Chapter05/mern-social/client/auth/auth-helper.js:
--------------------------------------------------------------------------------
1 | import { signout } from './api-auth.js'
2 |
3 | const auth = {
4 | isAuthenticated() {
5 | if (typeof window == "undefined")
6 | return false
7 |
8 | if (sessionStorage.getItem('jwt'))
9 | return JSON.parse(sessionStorage.getItem('jwt'))
10 | else
11 | return false
12 | },
13 | authenticate(jwt, cb) {
14 | if (typeof window !== "undefined")
15 | sessionStorage.setItem('jwt', JSON.stringify(jwt))
16 | cb()
17 | },
18 | clearJWT(cb) {
19 | if (typeof window !== "undefined")
20 | sessionStorage.removeItem('jwt')
21 | cb()
22 | //optional
23 | signout().then((data) => {
24 | document.cookie = "t=; expires=Thu, 01 Jan 1970 00:00:00 UTC; path=/;"
25 | })
26 | }
27 | }
28 |
29 | export default auth
30 |
--------------------------------------------------------------------------------
/Chapter05/mern-social/client/main.js:
--------------------------------------------------------------------------------
1 | import React from 'react'
2 | import { hydrate } from 'react-dom'
3 | import App from './App'
4 |
5 | hydrate(, document.getElementById('root'))
6 |
--------------------------------------------------------------------------------
/Chapter05/mern-social/client/post/PostList.js:
--------------------------------------------------------------------------------
1 | import React from 'react'
2 | import PropTypes from 'prop-types'
3 | import Post from './Post'
4 |
5 | export default function PostList (props) {
6 | return (
7 |
8 | {props.posts.map((item, i) => {
9 | return
10 | })
11 | }
12 |
13 | )
14 | }
15 | PostList.propTypes = {
16 | posts: PropTypes.array.isRequired,
17 | removeUpdate: PropTypes.func.isRequired
18 | }
19 |
--------------------------------------------------------------------------------
/Chapter05/mern-social/client/theme.js:
--------------------------------------------------------------------------------
1 | import { createMuiTheme } from '@material-ui/core/styles'
2 | import { teal, orange } from '@material-ui/core/colors'
3 |
4 | const theme = createMuiTheme({
5 | palette: {
6 | primary: {
7 | light: '#52c7b8',
8 | main: '#009688',
9 | dark: '#00675b',
10 | contrastText: '#fff',
11 | },
12 | secondary: {
13 | light: '#ffd95b',
14 | main: '#ffa726',
15 | dark: '#c77800',
16 | contrastText: '#000',
17 | },
18 | openTitle: teal['700'],
19 | protectedTitle: orange['700'],
20 | type: 'light'
21 | }
22 | })
23 |
24 | export default theme
--------------------------------------------------------------------------------
/Chapter05/mern-social/client/user/FollowGrid.js:
--------------------------------------------------------------------------------
1 | import React from 'react'
2 | import {makeStyles} from '@material-ui/core/styles'
3 | import PropTypes from 'prop-types'
4 | import Avatar from '@material-ui/core/Avatar'
5 | import Typography from '@material-ui/core/Typography'
6 | import {Link} from 'react-router-dom'
7 | import GridList from '@material-ui/core/GridList'
8 | import GridListTile from '@material-ui/core/GridListTile'
9 |
10 | const useStyles = makeStyles(theme => ({
11 | root: {
12 | paddingTop: theme.spacing(2),
13 | display: 'flex',
14 | flexWrap: 'wrap',
15 | justifyContent: 'space-around',
16 | overflow: 'hidden',
17 | background: theme.palette.background.paper,
18 | },
19 | bigAvatar: {
20 | width: 60,
21 | height: 60,
22 | margin: 'auto'
23 | },
24 | gridList: {
25 | width: 500,
26 | height: 220,
27 | },
28 | tileText: {
29 | textAlign: 'center',
30 | marginTop: 10
31 | }
32 | }))
33 | export default function FollowGrid (props) {
34 | const classes = useStyles()
35 | return (
36 |
37 | {props.people.map((person, i) => {
38 | return
39 |
40 |
41 | {person.name}
42 |
43 |
44 | })}
45 |
46 |
)
47 | }
48 |
49 | FollowGrid.propTypes = {
50 | people: PropTypes.array.isRequired
51 | }
52 |
53 |
--------------------------------------------------------------------------------
/Chapter05/mern-social/client/user/FollowProfileButton.js:
--------------------------------------------------------------------------------
1 | import React from 'react'
2 | import PropTypes from 'prop-types'
3 | import Button from '@material-ui/core/Button'
4 | import {unfollow, follow} from './api-user.js'
5 |
6 | export default function FollowProfileButton (props) {
7 | const followClick = () => {
8 | props.onButtonClick(follow)
9 | }
10 | const unfollowClick = () => {
11 | props.onButtonClick(unfollow)
12 | }
13 | return (
14 | { props.following
15 | ? ()
16 | : ()
17 | }
18 |
)
19 | }
20 | FollowProfileButton.propTypes = {
21 | following: PropTypes.bool.isRequired,
22 | onButtonClick: PropTypes.func.isRequired
23 | }
24 |
--------------------------------------------------------------------------------
/Chapter05/mern-social/config/config.js:
--------------------------------------------------------------------------------
1 | const config = {
2 | env: process.env.NODE_ENV || 'development',
3 | port: process.env.PORT || 3000,
4 | jwtSecret: process.env.JWT_SECRET || "YOUR_secret_key",
5 | mongoUri: process.env.MONGODB_URI ||
6 | process.env.MONGO_HOST ||
7 | 'mongodb://' + (process.env.IP || 'localhost') + ':' +
8 | (process.env.MONGO_PORT || '27017') +
9 | '/mernproject'
10 | }
11 |
12 | export default config
13 |
--------------------------------------------------------------------------------
/Chapter05/mern-social/nodemon.json:
--------------------------------------------------------------------------------
1 | {
2 | "verbose": false,
3 | "watch": [
4 | "./server"
5 | ],
6 | "exec": "webpack --mode=development --config webpack.config.server.js && node ./dist/server.generated.js"
7 | }
8 |
--------------------------------------------------------------------------------
/Chapter05/mern-social/server/devBundle.js:
--------------------------------------------------------------------------------
1 | import config from './../config/config'
2 | import webpack from 'webpack'
3 | import webpackMiddleware from 'webpack-dev-middleware'
4 | import webpackHotMiddleware from 'webpack-hot-middleware'
5 | import webpackConfig from './../webpack.config.client.js'
6 |
7 | const compile = (app) => {
8 | if(config.env === "development"){
9 | const compiler = webpack(webpackConfig)
10 | const middleware = webpackMiddleware(compiler, {
11 | publicPath: webpackConfig.output.publicPath
12 | })
13 | app.use(middleware)
14 | app.use(webpackHotMiddleware(compiler))
15 | }
16 | }
17 |
18 | export default {
19 | compile
20 | }
21 |
--------------------------------------------------------------------------------
/Chapter05/mern-social/server/helpers/dbErrorHandler.js:
--------------------------------------------------------------------------------
1 | 'use strict'
2 |
3 | /**
4 | * Get unique error field name
5 | */
6 | const getUniqueErrorMessage = (err) => {
7 | let output
8 | try {
9 | let fieldName = err.message.substring(err.message.lastIndexOf('.$') + 2, err.message.lastIndexOf('_1'))
10 | output = fieldName.charAt(0).toUpperCase() + fieldName.slice(1) + ' already exists'
11 | } catch (ex) {
12 | output = 'Unique field already exists'
13 | }
14 |
15 | return output
16 | }
17 |
18 | /**
19 | * Get the error message from error object
20 | */
21 | const getErrorMessage = (err) => {
22 | let message = ''
23 |
24 | if (err.code) {
25 | switch (err.code) {
26 | case 11000:
27 | case 11001:
28 | message = getUniqueErrorMessage(err)
29 | break
30 | default:
31 | message = 'Something went wrong'
32 | }
33 | } else {
34 | for (let errName in err.errors) {
35 | if (err.errors[errName].message) message = err.errors[errName].message
36 | }
37 | }
38 |
39 | return message
40 | }
41 |
42 | export default {getErrorMessage}
43 |
--------------------------------------------------------------------------------
/Chapter05/mern-social/server/models/post.model.js:
--------------------------------------------------------------------------------
1 | import mongoose from 'mongoose'
2 | const PostSchema = new mongoose.Schema({
3 | text: {
4 | type: String,
5 | required: 'Text is required'
6 | },
7 | photo: {
8 | data: Buffer,
9 | contentType: String
10 | },
11 | likes: [{type: mongoose.Schema.ObjectId, ref: 'User'}],
12 | comments: [{
13 | text: String,
14 | created: { type: Date, default: Date.now },
15 | postedBy: { type: mongoose.Schema.ObjectId, ref: 'User'}
16 | }],
17 | postedBy: {type: mongoose.Schema.ObjectId, ref: 'User'},
18 | created: {
19 | type: Date,
20 | default: Date.now
21 | }
22 | })
23 |
24 | export default mongoose.model('Post', PostSchema)
25 |
--------------------------------------------------------------------------------
/Chapter05/mern-social/server/routes/auth.routes.js:
--------------------------------------------------------------------------------
1 | import express from 'express'
2 | import authCtrl from '../controllers/auth.controller'
3 |
4 | const router = express.Router()
5 |
6 | router.route('/auth/signin')
7 | .post(authCtrl.signin)
8 | router.route('/auth/signout')
9 | .get(authCtrl.signout)
10 |
11 | export default router
12 |
--------------------------------------------------------------------------------
/Chapter05/mern-social/server/routes/post.routes.js:
--------------------------------------------------------------------------------
1 | import express from 'express'
2 | import userCtrl from '../controllers/user.controller'
3 | import authCtrl from '../controllers/auth.controller'
4 | import postCtrl from '../controllers/post.controller'
5 |
6 | const router = express.Router()
7 |
8 | router.route('/api/posts/new/:userId')
9 | .post(authCtrl.requireSignin, postCtrl.create)
10 |
11 | router.route('/api/posts/photo/:postId')
12 | .get(postCtrl.photo)
13 |
14 | router.route('/api/posts/by/:userId')
15 | .get(authCtrl.requireSignin, postCtrl.listByUser)
16 |
17 | router.route('/api/posts/feed/:userId')
18 | .get(authCtrl.requireSignin, postCtrl.listNewsFeed)
19 |
20 | router.route('/api/posts/like')
21 | .put(authCtrl.requireSignin, postCtrl.like)
22 | router.route('/api/posts/unlike')
23 | .put(authCtrl.requireSignin, postCtrl.unlike)
24 |
25 | router.route('/api/posts/comment')
26 | .put(authCtrl.requireSignin, postCtrl.comment)
27 | router.route('/api/posts/uncomment')
28 | .put(authCtrl.requireSignin, postCtrl.uncomment)
29 |
30 | router.route('/api/posts/:postId')
31 | .delete(authCtrl.requireSignin, postCtrl.isPoster, postCtrl.remove)
32 |
33 | router.param('userId', userCtrl.userByID)
34 | router.param('postId', postCtrl.postByID)
35 |
36 | export default router
37 |
--------------------------------------------------------------------------------
/Chapter05/mern-social/server/routes/user.routes.js:
--------------------------------------------------------------------------------
1 | import express from 'express'
2 | import userCtrl from '../controllers/user.controller'
3 | import authCtrl from '../controllers/auth.controller'
4 |
5 | const router = express.Router()
6 |
7 | router.route('/api/users')
8 | .get(userCtrl.list)
9 | .post(userCtrl.create)
10 |
11 | router.route('/api/users/photo/:userId')
12 | .get(userCtrl.photo, userCtrl.defaultPhoto)
13 | router.route('/api/users/defaultphoto')
14 | .get(userCtrl.defaultPhoto)
15 |
16 | router.route('/api/users/follow')
17 | .put(authCtrl.requireSignin, userCtrl.addFollowing, userCtrl.addFollower)
18 | router.route('/api/users/unfollow')
19 | .put(authCtrl.requireSignin, userCtrl.removeFollowing, userCtrl.removeFollower)
20 |
21 | router.route('/api/users/findpeople/:userId')
22 | .get(authCtrl.requireSignin, userCtrl.findPeople)
23 |
24 | router.route('/api/users/:userId')
25 | .get(authCtrl.requireSignin, userCtrl.read)
26 | .put(authCtrl.requireSignin, authCtrl.hasAuthorization, userCtrl.update)
27 | .delete(authCtrl.requireSignin, authCtrl.hasAuthorization, userCtrl.remove)
28 |
29 | router.param('userId', userCtrl.userByID)
30 |
31 | export default router
32 |
--------------------------------------------------------------------------------
/Chapter05/mern-social/server/server.js:
--------------------------------------------------------------------------------
1 | import config from './../config/config'
2 | import app from './express'
3 | import mongoose from 'mongoose'
4 |
5 | // Connection URL
6 | mongoose.Promise = global.Promise
7 | mongoose.connect(config.mongoUri, { useNewUrlParser: true, useCreateIndex: true, useUnifiedTopology: true, useFindAndModify: false })
8 | mongoose.connection.on('error', () => {
9 | throw new Error(`unable to connect to database: ${config.mongoUri}`)
10 | })
11 |
12 | app.listen(config.port, (err) => {
13 | if (err) {
14 | console.log(err)
15 | }
16 | console.info('Server started on port %s.', config.port)
17 | })
18 |
--------------------------------------------------------------------------------
/Chapter05/mern-social/template.js:
--------------------------------------------------------------------------------
1 | export default ({markup, css}) => {
2 | return `
3 |
4 |
5 |
6 |
10 | MERN Social
11 |
12 |
13 |
18 |
19 |
20 | ${markup}
21 |
22 |
23 |
24 | `
25 | }
26 |
--------------------------------------------------------------------------------
/Chapter05/mern-social/webpack.config.client.js:
--------------------------------------------------------------------------------
1 | const path = require('path')
2 | const webpack = require('webpack')
3 | const CURRENT_WORKING_DIR = process.cwd()
4 |
5 | const config = {
6 | name: "browser",
7 | mode: "development",
8 | devtool: 'eval-source-map',
9 | entry: [
10 | 'webpack-hot-middleware/client?reload=true',
11 | path.join(CURRENT_WORKING_DIR, 'client/main.js')
12 | ],
13 | output: {
14 | path: path.join(CURRENT_WORKING_DIR , '/dist'),
15 | filename: 'bundle.js',
16 | publicPath: '/dist/'
17 | },
18 | module: {
19 | rules: [
20 | {
21 | test: /\.jsx?$/,
22 | exclude: /node_modules/,
23 | use: [
24 | 'babel-loader'
25 | ]
26 | },
27 | {
28 | test: /\.(ttf|eot|svg|gif|jpg|png)(\?[\s\S]+)?$/,
29 | use: 'file-loader'
30 | }
31 | ]
32 | },
33 | plugins: [
34 | new webpack.HotModuleReplacementPlugin(),
35 | new webpack.NoEmitOnErrorsPlugin()
36 | ],
37 | resolve: {
38 | alias: {
39 | 'react-dom': '@hot-loader/react-dom'
40 | }
41 | }
42 | }
43 |
44 | module.exports = config
45 |
--------------------------------------------------------------------------------
/Chapter05/mern-social/webpack.config.client.production.js:
--------------------------------------------------------------------------------
1 | const path = require('path')
2 | const CURRENT_WORKING_DIR = process.cwd()
3 |
4 | const config = {
5 | mode: "production",
6 | entry: [
7 | path.join(CURRENT_WORKING_DIR, 'client/main.js')
8 | ],
9 | output: {
10 | path: path.join(CURRENT_WORKING_DIR , '/dist'),
11 | filename: 'bundle.js',
12 | publicPath: "/dist/"
13 | },
14 | module: {
15 | rules: [
16 | {
17 | test: /\.jsx?$/,
18 | exclude: /node_modules/,
19 | use: [
20 | 'babel-loader'
21 | ]
22 | },
23 | {
24 | test: /\.(ttf|eot|svg|gif|jpg|png)(\?[\s\S]+)?$/,
25 | use: 'file-loader'
26 | }
27 | ]
28 | }
29 | }
30 |
31 | module.exports = config
32 |
--------------------------------------------------------------------------------
/Chapter05/mern-social/webpack.config.server.js:
--------------------------------------------------------------------------------
1 | const path = require('path')
2 | const nodeExternals = require('webpack-node-externals')
3 | const CURRENT_WORKING_DIR = process.cwd()
4 |
5 | const config = {
6 | name: "server",
7 | entry: [ path.join(CURRENT_WORKING_DIR , './server/server.js') ],
8 | target: "node",
9 | output: {
10 | path: path.join(CURRENT_WORKING_DIR , '/dist/'),
11 | filename: "server.generated.js",
12 | publicPath: '/dist/',
13 | libraryTarget: "commonjs2"
14 | },
15 | externals: [nodeExternals()],
16 | module: {
17 | rules: [
18 | {
19 | test: /\.js$/,
20 | exclude: /node_modules/,
21 | use: [ 'babel-loader' ]
22 | },
23 | {
24 | test: /\.(ttf|eot|svg|gif|jpg|png)(\?[\s\S]+)?$/,
25 | use: 'file-loader'
26 | }
27 | ]
28 | }
29 | }
30 |
31 | module.exports = config
32 |
--------------------------------------------------------------------------------
/Chapter06/mern-classroom/.babelrc:
--------------------------------------------------------------------------------
1 | {
2 | "presets": [
3 | ["@babel/preset-env",
4 | {
5 | "targets": {
6 | "node": "current"
7 | }
8 | }
9 | ],
10 | "@babel/preset-react"
11 | ],
12 | "plugins": [
13 | "react-hot-loader/babel"
14 | ]
15 | }
16 |
--------------------------------------------------------------------------------
/Chapter06/mern-classroom/.gitignore:
--------------------------------------------------------------------------------
1 | /node_modules/
2 | /dist/
3 | /data/
4 | npm-debug.log
5 |
--------------------------------------------------------------------------------
/Chapter06/mern-classroom/LICENSE.md:
--------------------------------------------------------------------------------
1 | MIT License
2 |
3 | Copyright (c) 2018 Shama Hoque
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 |
--------------------------------------------------------------------------------
/Chapter06/mern-classroom/README.md:
--------------------------------------------------------------------------------
1 | # MERN Classroom
2 |
3 | A simple web-based classroom application that allows instructors to add courses with lessons, while students can enroll in these courses and track their progress. - developed using React, Node, Express and MongoDB.
4 |
5 | 
6 |
7 | ### [Live Demo](http://classroom.mernbook.com/ "MERN Classroom")
8 |
9 | #### What you need to run this code
10 | 1. Node (13.12.0)
11 | 2. NPM (6.14.4) or Yarn (1.22.4)
12 | 3. MongoDB (4.2.0)
13 |
14 | #### How to run this code
15 | 1. Make sure MongoDB is running on your system
16 | 2. Clone this repository
17 | 3. Open command line in the cloned folder,
18 | - To install dependencies, run ``` npm install ``` or ``` yarn ```
19 | - To run the application for development, run ``` npm run development ``` or ``` yarn development ```
20 | 4. Open [localhost:3000](http://localhost:3000/) in the browser
21 | ----
--------------------------------------------------------------------------------
/Chapter06/mern-classroom/client/App.js:
--------------------------------------------------------------------------------
1 | import React from 'react'
2 | import MainRouter from './MainRouter'
3 | import {BrowserRouter} from 'react-router-dom'
4 | import { ThemeProvider } from '@material-ui/styles'
5 | import theme from './theme'
6 | import { hot } from 'react-hot-loader'
7 |
8 | const App = () => {
9 | React.useEffect(() => {
10 | const jssStyles = document.querySelector('#jss-server-side')
11 | if (jssStyles) {
12 | jssStyles.parentNode.removeChild(jssStyles)
13 | }
14 | }, [])
15 | return (
16 |
17 |
18 |
19 |
20 |
21 | )}
22 |
23 | export default hot(module)(App)
24 |
--------------------------------------------------------------------------------
/Chapter06/mern-classroom/client/MainRouter.js:
--------------------------------------------------------------------------------
1 | import React from 'react'
2 | import {Route, Switch} from 'react-router-dom'
3 | import Home from './core/Home'
4 | import Users from './user/Users'
5 | import Signup from './user/Signup'
6 | import Signin from './auth/Signin'
7 | import EditProfile from './user/EditProfile'
8 | import Profile from './user/Profile'
9 | import PrivateRoute from './auth/PrivateRoute'
10 | import Menu from './core/Menu'
11 | import NewCourse from './course/NewCourse'
12 | //import Courses from './course/Courses'
13 | import Course from './course/Course'
14 | import EditCourse from './course/EditCourse'
15 | import MyCourses from './course/MyCourses'
16 | import Enrollment from './enrollment/Enrollment'
17 |
18 | const MainRouter = () => {
19 | return (
20 |
21 |
22 |
23 |
24 |
25 |
26 |
27 |
28 |
29 |
30 |
31 |
32 |
33 |
34 |
35 |
36 |
37 |
)
38 | }
39 |
40 | export default MainRouter
41 |
--------------------------------------------------------------------------------
/Chapter06/mern-classroom/client/assets/images/default.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/PacktPublishing/Full-Stack-React-Projects-Second-Edition/c4007202bd767682b9122cb196b80cd4952658f2/Chapter06/mern-classroom/client/assets/images/default.png
--------------------------------------------------------------------------------
/Chapter06/mern-classroom/client/assets/images/unicornbike.jpg:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/PacktPublishing/Full-Stack-React-Projects-Second-Edition/c4007202bd767682b9122cb196b80cd4952658f2/Chapter06/mern-classroom/client/assets/images/unicornbike.jpg
--------------------------------------------------------------------------------
/Chapter06/mern-classroom/client/auth/PrivateRoute.js:
--------------------------------------------------------------------------------
1 | import React, { Component } from 'react'
2 | import { Route, Redirect } from 'react-router-dom'
3 | import auth from './auth-helper'
4 |
5 | const PrivateRoute = ({ component: Component, ...rest }) => (
6 | (
7 | auth.isAuthenticated() ? (
8 |
9 | ) : (
10 |
14 | )
15 | )}/>
16 | )
17 |
18 | export default PrivateRoute
19 |
--------------------------------------------------------------------------------
/Chapter06/mern-classroom/client/auth/api-auth.js:
--------------------------------------------------------------------------------
1 | const signin = async (user) => {
2 | try {
3 | let response = await fetch('/auth/signin/', {
4 | method: 'POST',
5 | headers: {
6 | 'Accept': 'application/json',
7 | 'Content-Type': 'application/json'
8 | },
9 | credentials: 'include',
10 | body: JSON.stringify(user)
11 | })
12 | return await response.json()
13 | } catch(err) {
14 | console.log(err)
15 | }
16 | }
17 |
18 | const signout = async () => {
19 | try {
20 | let response = await fetch('/auth/signout/', { method: 'GET' })
21 | return await response.json()
22 | } catch(err) {
23 | console.log(err)
24 | }
25 | }
26 |
27 | export {
28 | signin,
29 | signout
30 | }
--------------------------------------------------------------------------------
/Chapter06/mern-classroom/client/auth/auth-helper.js:
--------------------------------------------------------------------------------
1 | import { signout } from './api-auth.js'
2 |
3 | const auth = {
4 | isAuthenticated() {
5 | if (typeof window == "undefined")
6 | return false
7 |
8 | if (sessionStorage.getItem('jwt'))
9 | return JSON.parse(sessionStorage.getItem('jwt'))
10 | else
11 | return false
12 | },
13 | authenticate(jwt, cb) {
14 | if (typeof window !== "undefined")
15 | sessionStorage.setItem('jwt', JSON.stringify(jwt))
16 | cb()
17 | },
18 | clearJWT(cb) {
19 | if (typeof window !== "undefined")
20 | sessionStorage.removeItem('jwt')
21 | cb()
22 | //optional
23 | signout().then((data) => {
24 | document.cookie = "t=; expires=Thu, 01 Jan 1970 00:00:00 UTC; path=/;"
25 | })
26 | },
27 | updateUser(user, cb) {
28 | if(typeof window !== "undefined"){
29 | if(sessionStorage.getItem('jwt')){
30 | let auth = JSON.parse(sessionStorage.getItem('jwt'))
31 | auth.user = user
32 | sessionStorage.setItem('jwt', JSON.stringify(auth))
33 | cb()
34 | }
35 | }
36 | }
37 | }
38 |
39 | export default auth
40 |
--------------------------------------------------------------------------------
/Chapter06/mern-classroom/client/enrollment/Enroll.js:
--------------------------------------------------------------------------------
1 | import React, {useState} from 'react'
2 | import PropTypes from 'prop-types'
3 | import Button from '@material-ui/core/Button'
4 | import {makeStyles} from '@material-ui/core/styles'
5 | import {create} from './api-enrollment'
6 | import auth from './../auth/auth-helper'
7 | import {Redirect} from 'react-router-dom'
8 |
9 | const useStyles = makeStyles(theme => ({
10 | form: {
11 | minWidth: 500
12 | }
13 | }))
14 |
15 | export default function Enroll(props) {
16 | const classes = useStyles()
17 | const [values, setValues] = useState({
18 | enrollmentId: '',
19 | error: '',
20 | redirect: false
21 | })
22 | const jwt = auth.isAuthenticated()
23 | const clickEnroll = () => {
24 | create({
25 | courseId: props.courseId
26 | }, {
27 | t: jwt.token
28 | }).then((data) => {
29 | if (data && data.error) {
30 | setValues({...values, error: data.error})
31 | } else {
32 | setValues({...values, enrollmentId: data._id, redirect: true})
33 | }
34 | })
35 | }
36 |
37 | if(values.redirect){
38 | return ()
39 | }
40 |
41 | return (
42 |
43 | )
44 | }
45 |
46 | Enroll.propTypes = {
47 | courseId: PropTypes.string.isRequired
48 | }
49 |
--------------------------------------------------------------------------------
/Chapter06/mern-classroom/client/main.js:
--------------------------------------------------------------------------------
1 | import React from 'react'
2 | import { hydrate } from 'react-dom'
3 | import App from './App'
4 |
5 | hydrate(, document.getElementById('root'))
6 |
--------------------------------------------------------------------------------
/Chapter06/mern-classroom/client/theme.js:
--------------------------------------------------------------------------------
1 | import { createMuiTheme } from '@material-ui/core/styles'
2 |
3 | const theme = createMuiTheme({
4 | typography: {
5 | useNextVariants: true,
6 | },
7 | palette: {
8 | primary: {
9 | light: '#8e8e8e',
10 | main: '#616161',
11 | dark: '#373737',
12 | contrastText: '#fffde7',
13 | },
14 | secondary: {
15 | light: '#ffad42',
16 | main: '#f57c00',
17 | dark: '#bb4d00',
18 | contrastText: '#fffde7',
19 | },
20 | openTitle: '#455a64',
21 | protectedTitle: '#f57c00',
22 | type: 'light'
23 | }
24 | })
25 |
26 | export default theme
--------------------------------------------------------------------------------
/Chapter06/mern-classroom/config/config.js:
--------------------------------------------------------------------------------
1 | const config = {
2 | env: process.env.NODE_ENV || 'development',
3 | port: process.env.PORT || 3000,
4 | jwtSecret: process.env.JWT_SECRET || "YOUR_secret_key",
5 | mongoUri: process.env.MONGODB_URI ||
6 | process.env.MONGO_HOST ||
7 | 'mongodb://' + (process.env.IP || 'localhost') + ':' +
8 | (process.env.MONGO_PORT || '27017') +
9 | '/mernproject'
10 | }
11 |
12 | export default config
13 |
--------------------------------------------------------------------------------
/Chapter06/mern-classroom/nodemon.json:
--------------------------------------------------------------------------------
1 | {
2 | "verbose": false,
3 | "watch": [
4 | "./server"
5 | ],
6 | "exec": "webpack --mode=development --config webpack.config.server.js && node ./dist/server.generated.js"
7 | }
8 |
--------------------------------------------------------------------------------
/Chapter06/mern-classroom/server/devBundle.js:
--------------------------------------------------------------------------------
1 | import config from './../config/config'
2 | import webpack from 'webpack'
3 | import webpackMiddleware from 'webpack-dev-middleware'
4 | import webpackHotMiddleware from 'webpack-hot-middleware'
5 | import webpackConfig from './../webpack.config.client.js'
6 |
7 | const compile = (app) => {
8 | if(config.env === "development"){
9 | const compiler = webpack(webpackConfig)
10 | const middleware = webpackMiddleware(compiler, {
11 | publicPath: webpackConfig.output.publicPath
12 | })
13 | app.use(middleware)
14 | app.use(webpackHotMiddleware(compiler))
15 | }
16 | }
17 |
18 | export default {
19 | compile
20 | }
21 |
--------------------------------------------------------------------------------
/Chapter06/mern-classroom/server/helpers/dbErrorHandler.js:
--------------------------------------------------------------------------------
1 | 'use strict'
2 |
3 | /**
4 | * Get unique error field name
5 | */
6 | const getUniqueErrorMessage = (err) => {
7 | let output
8 | try {
9 | let fieldName = err.message.substring(err.message.lastIndexOf('.$') + 2, err.message.lastIndexOf('_1'))
10 | output = fieldName.charAt(0).toUpperCase() + fieldName.slice(1) + ' already exists'
11 | } catch (ex) {
12 | output = 'Unique field already exists'
13 | }
14 |
15 | return output
16 | }
17 |
18 | /**
19 | * Get the error message from error object
20 | */
21 | const getErrorMessage = (err) => {
22 | let message = ''
23 |
24 | if (err.code) {
25 | switch (err.code) {
26 | case 11000:
27 | case 11001:
28 | message = getUniqueErrorMessage(err)
29 | break
30 | default:
31 | message = 'Something went wrong'
32 | }
33 | } else {
34 | for (let errName in err.errors) {
35 | if (err.errors[errName].message) message = err.errors[errName].message
36 | }
37 | }
38 |
39 | return message
40 | }
41 |
42 | export default {getErrorMessage}
43 |
--------------------------------------------------------------------------------
/Chapter06/mern-classroom/server/models/course.model.js:
--------------------------------------------------------------------------------
1 | import mongoose from 'mongoose'
2 |
3 | const LessonSchema = new mongoose.Schema({
4 | title: String,
5 | content: String,
6 | resource_url: String
7 | })
8 | const Lesson = mongoose.model('Lesson', LessonSchema)
9 | const CourseSchema = new mongoose.Schema({
10 | name: {
11 | type: String,
12 | trim: true,
13 | required: 'Name is required'
14 | },
15 | image: {
16 | data: Buffer,
17 | contentType: String
18 | },
19 | description: {
20 | type: String,
21 | trim: true
22 | },
23 | category: {
24 | type: String,
25 | required: 'Category is required'
26 | },
27 | updated: Date,
28 | created: {
29 | type: Date,
30 | default: Date.now
31 | },
32 | instructor: {type: mongoose.Schema.ObjectId, ref: 'User'},
33 | published: {
34 | type: Boolean,
35 | default: false
36 | },
37 | lessons: [LessonSchema]
38 | })
39 |
40 | export default mongoose.model('Course', CourseSchema)
41 |
--------------------------------------------------------------------------------
/Chapter06/mern-classroom/server/models/enrollment.model.js:
--------------------------------------------------------------------------------
1 | import mongoose from 'mongoose'
2 |
3 | const EnrollmentSchema = new mongoose.Schema({
4 | course: {type: mongoose.Schema.ObjectId, ref: 'Course'},
5 | updated: Date,
6 | enrolled: {
7 | type: Date,
8 | default: Date.now
9 | },
10 | student: {type: mongoose.Schema.ObjectId, ref: 'User'},
11 | lessonStatus: [{
12 | lesson: {type: mongoose.Schema.ObjectId, ref: 'Lesson'},
13 | complete: Boolean}],
14 | completed: Date
15 | })
16 |
17 | export default mongoose.model('Enrollment', EnrollmentSchema)
18 |
--------------------------------------------------------------------------------
/Chapter06/mern-classroom/server/routes/auth.routes.js:
--------------------------------------------------------------------------------
1 | import express from 'express'
2 | import authCtrl from '../controllers/auth.controller'
3 |
4 | const router = express.Router()
5 |
6 | router.route('/auth/signin')
7 | .post(authCtrl.signin)
8 | router.route('/auth/signout')
9 | .get(authCtrl.signout)
10 |
11 | export default router
12 |
--------------------------------------------------------------------------------
/Chapter06/mern-classroom/server/routes/course.routes.js:
--------------------------------------------------------------------------------
1 | import express from 'express'
2 | import courseCtrl from '../controllers/course.controller'
3 | import userCtrl from '../controllers/user.controller'
4 | import authCtrl from '../controllers/auth.controller'
5 |
6 | const router = express.Router()
7 |
8 | router.route('/api/courses/published')
9 | .get(courseCtrl.listPublished)
10 |
11 | router.route('/api/courses/by/:userId')
12 | .post(authCtrl.requireSignin, authCtrl.hasAuthorization, userCtrl.isEducator, courseCtrl.create)
13 | .get(authCtrl.requireSignin, authCtrl.hasAuthorization, courseCtrl.listByInstructor)
14 |
15 | router.route('/api/courses/photo/:courseId')
16 | .get(courseCtrl.photo, courseCtrl.defaultPhoto)
17 |
18 | router.route('/api/courses/defaultphoto')
19 | .get(courseCtrl.defaultPhoto)
20 |
21 | router.route('/api/courses/:courseId/lesson/new')
22 | .put(authCtrl.requireSignin, courseCtrl.isInstructor, courseCtrl.newLesson)
23 |
24 | router.route('/api/courses/:courseId')
25 | .get(courseCtrl.read)
26 | .put(authCtrl.requireSignin, courseCtrl.isInstructor, courseCtrl.update)
27 | .delete(authCtrl.requireSignin, courseCtrl.isInstructor, courseCtrl.remove)
28 |
29 | router.param('courseId', courseCtrl.courseByID)
30 | router.param('userId', userCtrl.userByID)
31 |
32 | export default router
33 |
--------------------------------------------------------------------------------
/Chapter06/mern-classroom/server/routes/enrollment.routes.js:
--------------------------------------------------------------------------------
1 | import express from 'express'
2 | import enrollmentCtrl from '../controllers/enrollment.controller'
3 | import courseCtrl from '../controllers/course.controller'
4 | import authCtrl from '../controllers/auth.controller'
5 |
6 | const router = express.Router()
7 |
8 | router.route('/api/enrollment/enrolled')
9 | .get(authCtrl.requireSignin, enrollmentCtrl.listEnrolled)
10 |
11 | router.route('/api/enrollment/new/:courseId')
12 | .post(authCtrl.requireSignin, enrollmentCtrl.findEnrollment, enrollmentCtrl.create)
13 |
14 | router.route('/api/enrollment/stats/:courseId')
15 | .get(enrollmentCtrl.enrollmentStats)
16 |
17 | router.route('/api/enrollment/complete/:enrollmentId')
18 | .put(authCtrl.requireSignin, enrollmentCtrl.isStudent, enrollmentCtrl.complete)
19 |
20 | router.route('/api/enrollment/:enrollmentId')
21 | .get(authCtrl.requireSignin, enrollmentCtrl.isStudent, enrollmentCtrl.read)
22 | .delete(authCtrl.requireSignin, enrollmentCtrl.isStudent, enrollmentCtrl.remove)
23 |
24 | router.param('courseId', courseCtrl.courseByID)
25 | router.param('enrollmentId', enrollmentCtrl.enrollmentByID)
26 |
27 | export default router
28 |
--------------------------------------------------------------------------------
/Chapter06/mern-classroom/server/routes/user.routes.js:
--------------------------------------------------------------------------------
1 | import express from 'express'
2 | import userCtrl from '../controllers/user.controller'
3 | import authCtrl from '../controllers/auth.controller'
4 |
5 | const router = express.Router()
6 |
7 | router.route('/api/users')
8 | .get(userCtrl.list)
9 | .post(userCtrl.create)
10 |
11 | router.route('/api/users/:userId')
12 | .get(authCtrl.requireSignin, userCtrl.read)
13 | .put(authCtrl.requireSignin, authCtrl.hasAuthorization, userCtrl.update)
14 | .delete(authCtrl.requireSignin, authCtrl.hasAuthorization, userCtrl.remove)
15 |
16 | router.param('userId', userCtrl.userByID)
17 |
18 | export default router
19 |
--------------------------------------------------------------------------------
/Chapter06/mern-classroom/server/server.js:
--------------------------------------------------------------------------------
1 | import config from './../config/config'
2 | import app from './express'
3 | import mongoose from 'mongoose'
4 |
5 | // Connection URL
6 | mongoose.Promise = global.Promise
7 | mongoose.connect(config.mongoUri, { useNewUrlParser: true, useCreateIndex: true, useUnifiedTopology: true })
8 | mongoose.connection.on('error', () => {
9 | throw new Error(`unable to connect to database: ${config.mongoUri}`)
10 | })
11 |
12 | app.listen(config.port, (err) => {
13 | if (err) {
14 | console.log(err)
15 | }
16 | console.info('Server started on port %s.', config.port)
17 | })
18 |
--------------------------------------------------------------------------------
/Chapter06/mern-classroom/template.js:
--------------------------------------------------------------------------------
1 | export default ({markup, css}) => {
2 | return `
3 |
4 |
5 |
6 |
10 | MERN Classroom
11 |
12 |
13 |
19 |
20 |
21 | ${markup}
22 |
23 |
24 |
25 | `
26 | }
27 |
--------------------------------------------------------------------------------
/Chapter06/mern-classroom/webpack.config.client.js:
--------------------------------------------------------------------------------
1 | const path = require('path')
2 | const webpack = require('webpack')
3 | const CURRENT_WORKING_DIR = process.cwd()
4 |
5 | const config = {
6 | name: "browser",
7 | mode: "development",
8 | devtool: 'eval-source-map',
9 | entry: [
10 | 'webpack-hot-middleware/client?reload=true',
11 | path.join(CURRENT_WORKING_DIR, 'client/main.js')
12 | ],
13 | output: {
14 | path: path.join(CURRENT_WORKING_DIR , '/dist'),
15 | filename: 'bundle.js',
16 | publicPath: '/dist/'
17 | },
18 | module: {
19 | rules: [
20 | {
21 | test: /\.jsx?$/,
22 | exclude: /node_modules/,
23 | use: [
24 | 'babel-loader'
25 | ]
26 | },
27 | {
28 | test: /\.(ttf|eot|svg|gif|jpg|png)(\?[\s\S]+)?$/,
29 | use: 'file-loader'
30 | }
31 | ]
32 | },
33 | plugins: [
34 | new webpack.HotModuleReplacementPlugin(),
35 | new webpack.NoEmitOnErrorsPlugin()
36 | ],
37 | resolve: {
38 | alias: {
39 | 'react-dom': '@hot-loader/react-dom'
40 | }
41 | }
42 | }
43 |
44 | module.exports = config
45 |
--------------------------------------------------------------------------------
/Chapter06/mern-classroom/webpack.config.client.production.js:
--------------------------------------------------------------------------------
1 | const path = require('path')
2 | const CURRENT_WORKING_DIR = process.cwd()
3 |
4 | const config = {
5 | mode: "production",
6 | entry: [
7 | path.join(CURRENT_WORKING_DIR, 'client/main.js')
8 | ],
9 | output: {
10 | path: path.join(CURRENT_WORKING_DIR , '/dist'),
11 | filename: 'bundle.js',
12 | publicPath: "/dist/"
13 | },
14 | module: {
15 | rules: [
16 | {
17 | test: /\.jsx?$/,
18 | exclude: /node_modules/,
19 | use: [
20 | 'babel-loader'
21 | ]
22 | },
23 | {
24 | test: /\.(ttf|eot|svg|gif|jpg|png)(\?[\s\S]+)?$/,
25 | use: 'file-loader'
26 | }
27 | ]
28 | }
29 | }
30 |
31 | module.exports = config
32 |
--------------------------------------------------------------------------------
/Chapter06/mern-classroom/webpack.config.server.js:
--------------------------------------------------------------------------------
1 | const path = require('path')
2 | const nodeExternals = require('webpack-node-externals')
3 | const CURRENT_WORKING_DIR = process.cwd()
4 |
5 | const config = {
6 | name: "server",
7 | entry: [ path.join(CURRENT_WORKING_DIR , './server/server.js') ],
8 | target: "node",
9 | output: {
10 | path: path.join(CURRENT_WORKING_DIR , '/dist/'),
11 | filename: "server.generated.js",
12 | publicPath: '/dist/',
13 | libraryTarget: "commonjs2"
14 | },
15 | externals: [nodeExternals()],
16 | module: {
17 | rules: [
18 | {
19 | test: /\.js$/,
20 | exclude: /node_modules/,
21 | use: [ 'babel-loader' ]
22 | },
23 | {
24 | test: /\.(ttf|eot|svg|gif|jpg|png)(\?[\s\S]+)?$/,
25 | use: 'file-loader'
26 | }
27 | ]
28 | }
29 | }
30 |
31 | module.exports = config
32 |
--------------------------------------------------------------------------------
/Chapter07 and 08/mern-marketplace/.babelrc:
--------------------------------------------------------------------------------
1 | {
2 | "presets": [
3 | ["@babel/preset-env",
4 | {
5 | "targets": {
6 | "node": "current"
7 | }
8 | }
9 | ],
10 | "@babel/preset-react"
11 | ],
12 | "plugins": [
13 | "react-hot-loader/babel"
14 | ]
15 | }
16 |
--------------------------------------------------------------------------------
/Chapter07 and 08/mern-marketplace/.gitignore:
--------------------------------------------------------------------------------
1 | /node_modules/
2 | /dist/
3 | /data/
4 | npm-debug.log
5 |
--------------------------------------------------------------------------------
/Chapter07 and 08/mern-marketplace/LICENSE.md:
--------------------------------------------------------------------------------
1 | MIT License
2 |
3 | Copyright (c) 2018 Shama Hoque
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 |
--------------------------------------------------------------------------------
/Chapter07 and 08/mern-marketplace/README.md:
--------------------------------------------------------------------------------
1 | # MERN Marketplace
2 |
3 | An online marketplace application with seller accounts, product search and suggestions, shopping cart, order management, payment processing with Stripe, and live auction with Socket.io - developed using React, Node, Express and MongoDB.
4 |
5 | 
6 |
7 | ### [Live Demo](http://marketplace2.mernbook.com/ "MERN Marketplace")
8 |
9 | #### What you need to run this code
10 | 1. Node (13.12.0)
11 | 2. NPM (6.14.4) or Yarn (1.22.4)
12 | 3. MongoDB (4.2.0)
13 | 4. Stripe account with test data
14 |
15 | #### How to run this code
16 | 1. Make sure MongoDB is running on your system
17 | 2. Clone this repository
18 | 3. Update config/config.js with your test values for Stripe API keys and Stripe Connect Client ID
19 | 4. Open command line in the cloned folder,
20 | - To install dependencies, run ``` npm install ``` or ``` yarn ```
21 | - To run the application for development, run ``` npm run development ``` or ``` yarn development ```
22 | 5. Open [localhost:3000](http://localhost:3000/) in the browser
23 | ----
--------------------------------------------------------------------------------
/Chapter07 and 08/mern-marketplace/client/App.js:
--------------------------------------------------------------------------------
1 | import React from 'react'
2 | import MainRouter from './MainRouter'
3 | import {BrowserRouter} from 'react-router-dom'
4 | import { ThemeProvider } from '@material-ui/styles'
5 | import theme from './theme'
6 | import { hot } from 'react-hot-loader'
7 |
8 | const App = () => {
9 | React.useEffect(() => {
10 | const jssStyles = document.querySelector('#jss-server-side')
11 | if (jssStyles) {
12 | jssStyles.parentNode.removeChild(jssStyles)
13 | }
14 | }, [])
15 | return (
16 |
17 |
18 |
19 |
20 |
21 | )}
22 |
23 | export default hot(module)(App)
24 |
--------------------------------------------------------------------------------
/Chapter07 and 08/mern-marketplace/client/assets/images/default.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/PacktPublishing/Full-Stack-React-Projects-Second-Edition/c4007202bd767682b9122cb196b80cd4952658f2/Chapter07 and 08/mern-marketplace/client/assets/images/default.png
--------------------------------------------------------------------------------
/Chapter07 and 08/mern-marketplace/client/assets/images/stripeButton.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/PacktPublishing/Full-Stack-React-Projects-Second-Edition/c4007202bd767682b9122cb196b80cd4952658f2/Chapter07 and 08/mern-marketplace/client/assets/images/stripeButton.png
--------------------------------------------------------------------------------
/Chapter07 and 08/mern-marketplace/client/assets/images/unicornbike.jpg:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/PacktPublishing/Full-Stack-React-Projects-Second-Edition/c4007202bd767682b9122cb196b80cd4952658f2/Chapter07 and 08/mern-marketplace/client/assets/images/unicornbike.jpg
--------------------------------------------------------------------------------
/Chapter07 and 08/mern-marketplace/client/auth/PrivateRoute.js:
--------------------------------------------------------------------------------
1 | import React, { Component } from 'react'
2 | import { Route, Redirect } from 'react-router-dom'
3 | import auth from './auth-helper'
4 |
5 | const PrivateRoute = ({ component: Component, ...rest }) => (
6 | (
7 | auth.isAuthenticated() ? (
8 |
9 | ) : (
10 |
14 | )
15 | )}/>
16 | )
17 |
18 | export default PrivateRoute
19 |
--------------------------------------------------------------------------------
/Chapter07 and 08/mern-marketplace/client/auth/api-auth.js:
--------------------------------------------------------------------------------
1 | const signin = async (user) => {
2 | try {
3 | let response = await fetch('/auth/signin/', {
4 | method: 'POST',
5 | headers: {
6 | 'Accept': 'application/json',
7 | 'Content-Type': 'application/json'
8 | },
9 | credentials: 'include',
10 | body: JSON.stringify(user)
11 | })
12 | return await response.json()
13 | } catch(err) {
14 | console.log(err)
15 | }
16 | }
17 |
18 | const signout = async () => {
19 | try {
20 | let response = await fetch('/auth/signout/', { method: 'GET' })
21 | return await response.json()
22 | } catch(err) {
23 | console.log(err)
24 | }
25 | }
26 |
27 | export {
28 | signin,
29 | signout
30 | }
--------------------------------------------------------------------------------
/Chapter07 and 08/mern-marketplace/client/auth/auth-helper.js:
--------------------------------------------------------------------------------
1 | import { signout } from './api-auth.js'
2 |
3 | const auth = {
4 | isAuthenticated() {
5 | if (typeof window == "undefined")
6 | return false
7 |
8 | if (sessionStorage.getItem('jwt'))
9 | return JSON.parse(sessionStorage.getItem('jwt'))
10 | else
11 | return false
12 | },
13 | authenticate(jwt, cb) {
14 | if (typeof window !== "undefined")
15 | sessionStorage.setItem('jwt', JSON.stringify(jwt))
16 | cb()
17 | },
18 | clearJWT(cb) {
19 | if (typeof window !== "undefined")
20 | sessionStorage.removeItem('jwt')
21 | cb()
22 | //optional
23 | signout().then((data) => {
24 | document.cookie = "t=; expires=Thu, 01 Jan 1970 00:00:00 UTC; path=/;"
25 | })
26 | },
27 | updateUser(user, cb) {
28 | if(typeof window !== "undefined"){
29 | if(sessionStorage.getItem('jwt')){
30 | let auth = JSON.parse(sessionStorage.getItem('jwt'))
31 | auth.user = user
32 | sessionStorage.setItem('jwt', JSON.stringify(auth))
33 | cb()
34 | }
35 | }
36 | }
37 | }
38 |
39 | export default auth
40 |
--------------------------------------------------------------------------------
/Chapter07 and 08/mern-marketplace/client/cart/AddToCart.js:
--------------------------------------------------------------------------------
1 | import React, {useState} from 'react'
2 | import {makeStyles} from '@material-ui/core/styles'
3 | import PropTypes from 'prop-types'
4 | import IconButton from '@material-ui/core/IconButton'
5 | import AddCartIcon from '@material-ui/icons/AddShoppingCart'
6 | import DisabledCartIcon from '@material-ui/icons/RemoveShoppingCart'
7 | import cart from './cart-helper.js'
8 | import { Redirect } from 'react-router-dom'
9 |
10 | const useStyles = makeStyles(theme => ({
11 | iconButton: {
12 | width: '28px',
13 | height: '28px'
14 | },
15 | disabledIconButton: {
16 | color: '#7f7563',
17 | width: '28px',
18 | height: '28px'
19 | }
20 | }))
21 |
22 | export default function AddToCart(props) {
23 | const classes = useStyles()
24 | const [redirect, setRedirect] = useState(false)
25 |
26 | const addToCart = () => {
27 | cart.addItem(props.item, () => {
28 | setRedirect({redirect:true})
29 | })
30 | }
31 | if (redirect) {
32 | return ()
33 | }
34 | return (
35 | {props.item.quantity >= 0 ?
36 |
37 |
38 | :
39 |
40 |
41 | }
42 | )
43 | }
44 |
45 | AddToCart.propTypes = {
46 | item: PropTypes.object.isRequired,
47 | cartStyle: PropTypes.string
48 | }
--------------------------------------------------------------------------------
/Chapter07 and 08/mern-marketplace/client/cart/Cart.js:
--------------------------------------------------------------------------------
1 | import React, {useState, useEffect} from 'react'
2 | import Grid from '@material-ui/core/Grid'
3 | import {makeStyles} from '@material-ui/core/styles'
4 | import CartItems from './CartItems'
5 | import {StripeProvider} from 'react-stripe-elements'
6 | import config from './../../config/config'
7 | import Checkout from './Checkout'
8 |
9 | const useStyles = makeStyles(theme => ({
10 | root: {
11 | flexGrow: 1,
12 | margin: 30,
13 | }
14 | }))
15 |
16 | export default function Cart () {
17 | const classes = useStyles()
18 | const [checkout, setCheckout] = useState(false)
19 |
20 | const showCheckout = val => {
21 | setCheckout(val)
22 | }
23 |
24 | return (
25 |
26 |
27 |
29 |
30 | {checkout &&
31 |
32 |
33 |
34 |
35 | }
36 |
37 |
)
38 | }
39 |
--------------------------------------------------------------------------------
/Chapter07 and 08/mern-marketplace/client/main.js:
--------------------------------------------------------------------------------
1 | import React from 'react'
2 | import { hydrate } from 'react-dom'
3 | import App from './App'
4 |
5 | hydrate(, document.getElementById('root'))
6 |
--------------------------------------------------------------------------------
/Chapter07 and 08/mern-marketplace/client/theme.js:
--------------------------------------------------------------------------------
1 | import { createMuiTheme } from '@material-ui/core/styles'
2 | import { blueGrey, lightGreen } from '@material-ui/core/colors'
3 |
4 | const theme = createMuiTheme({
5 | palette: {
6 | primary: {
7 | light: '#8eacbb',
8 | main: '#607d8b',
9 | dark: '#34515e',
10 | contrastText: '#fff',
11 | },
12 | secondary: {
13 | light: '#e7ff8c',
14 | main: '#b2ff59',
15 | dark: '#7ecb20',
16 | contrastText: '#000',
17 | },
18 | openTitle: blueGrey['400'],
19 | protectedTitle: lightGreen['400'],
20 | type: 'light'
21 | }
22 | })
23 |
24 | export default theme
--------------------------------------------------------------------------------
/Chapter07 and 08/mern-marketplace/config/config.js:
--------------------------------------------------------------------------------
1 | const config = {
2 | env: process.env.NODE_ENV || 'development',
3 | port: process.env.PORT || 3000,
4 | jwtSecret: process.env.JWT_SECRET || "YOUR_secret_key",
5 | mongoUri: process.env.MONGODB_URI ||
6 | process.env.MONGO_HOST ||
7 | 'mongodb://' + (process.env.IP || 'localhost') + ':' +
8 | (process.env.MONGO_PORT || '27017') +
9 | '/mernproject',
10 | stripe_connect_test_client_id: 'YOUR_stripe_connect_test_client',
11 | stripe_test_secret_key: 'YOUR_stripe_test_secret_key',
12 | stripe_test_api_key: 'YOUR_stripe_test_api_key'
13 | }
14 |
15 | export default config
16 |
--------------------------------------------------------------------------------
/Chapter07 and 08/mern-marketplace/nodemon.json:
--------------------------------------------------------------------------------
1 | {
2 | "verbose": false,
3 | "watch": [
4 | "./server"
5 | ],
6 | "exec": "webpack --mode=development --config webpack.config.server.js && node ./dist/server.generated.js"
7 | }
8 |
--------------------------------------------------------------------------------
/Chapter07 and 08/mern-marketplace/server/devBundle.js:
--------------------------------------------------------------------------------
1 | import config from './../config/config'
2 | import webpack from 'webpack'
3 | import webpackMiddleware from 'webpack-dev-middleware'
4 | import webpackHotMiddleware from 'webpack-hot-middleware'
5 | import webpackConfig from './../webpack.config.client.js'
6 |
7 | const compile = (app) => {
8 | if(config.env === "development"){
9 | const compiler = webpack(webpackConfig)
10 | const middleware = webpackMiddleware(compiler, {
11 | publicPath: webpackConfig.output.publicPath
12 | })
13 | app.use(middleware)
14 | app.use(webpackHotMiddleware(compiler))
15 | }
16 | }
17 |
18 | export default {
19 | compile
20 | }
21 |
--------------------------------------------------------------------------------
/Chapter07 and 08/mern-marketplace/server/helpers/dbErrorHandler.js:
--------------------------------------------------------------------------------
1 | 'use strict'
2 |
3 | /**
4 | * Get unique error field name
5 | */
6 | const getUniqueErrorMessage = (err) => {
7 | let output
8 | try {
9 | let fieldName = err.message.substring(err.message.lastIndexOf('.$') + 2, err.message.lastIndexOf('_1'))
10 | output = fieldName.charAt(0).toUpperCase() + fieldName.slice(1) + ' already exists'
11 | } catch (ex) {
12 | output = 'Unique field already exists'
13 | }
14 |
15 | return output
16 | }
17 |
18 | /**
19 | * Get the error message from error object
20 | */
21 | const getErrorMessage = (err) => {
22 | let message = ''
23 |
24 | if (err.code) {
25 | switch (err.code) {
26 | case 11000:
27 | case 11001:
28 | message = getUniqueErrorMessage(err)
29 | break
30 | default:
31 | message = 'Something went wrong'
32 | }
33 | } else {
34 | for (let errName in err.errors) {
35 | if (err.errors[errName].message) message = err.errors[errName].message
36 | }
37 | }
38 |
39 | return message
40 | }
41 |
42 | export default {getErrorMessage}
43 |
--------------------------------------------------------------------------------
/Chapter07 and 08/mern-marketplace/server/models/order.model.js:
--------------------------------------------------------------------------------
1 | import mongoose from 'mongoose'
2 | const CartItemSchema = new mongoose.Schema({
3 | product: {type: mongoose.Schema.ObjectId, ref: 'Product'},
4 | quantity: Number,
5 | shop: {type: mongoose.Schema.ObjectId, ref: 'Shop'},
6 | status: {type: String,
7 | default: 'Not processed',
8 | enum: ['Not processed' , 'Processing', 'Shipped', 'Delivered', 'Cancelled']}
9 | })
10 | const CartItem = mongoose.model('CartItem', CartItemSchema)
11 | const OrderSchema = new mongoose.Schema({
12 | products: [CartItemSchema],
13 | customer_name: {
14 | type: String,
15 | trim: true,
16 | required: 'Name is required'
17 | },
18 | customer_email: {
19 | type: String,
20 | trim: true,
21 | match: [/.+\@.+\..+/, 'Please fill a valid email address'],
22 | required: 'Email is required'
23 | },
24 | delivery_address: {
25 | street: {type: String, required: 'Street is required'},
26 | city: {type: String, required: 'City is required'},
27 | state: {type: String},
28 | zipcode: {type: String, required: 'Zip Code is required'},
29 | country: {type: String, required: 'Country is required'}
30 | },
31 | payment_id: {},
32 | updated: Date,
33 | created: {
34 | type: Date,
35 | default: Date.now
36 | },
37 | user: {type: mongoose.Schema.ObjectId, ref: 'User'}
38 | })
39 |
40 | const Order = mongoose.model('Order', OrderSchema)
41 |
42 | export {Order, CartItem}
43 |
--------------------------------------------------------------------------------
/Chapter07 and 08/mern-marketplace/server/models/product.model.js:
--------------------------------------------------------------------------------
1 | import mongoose from 'mongoose'
2 | const ProductSchema = new mongoose.Schema({
3 | name: {
4 | type: String,
5 | trim: true,
6 | required: 'Name is required'
7 | },
8 | image: {
9 | data: Buffer,
10 | contentType: String
11 | },
12 | description: {
13 | type: String,
14 | trim: true
15 | },
16 | category: {
17 | type: String
18 | },
19 | quantity: {
20 | type: Number,
21 | required: "Quantity is required"
22 | },
23 | price: {
24 | type: Number,
25 | required: "Price is required"
26 | },
27 | updated: Date,
28 | created: {
29 | type: Date,
30 | default: Date.now
31 | },
32 | shop: {type: mongoose.Schema.ObjectId, ref: 'Shop'}
33 | })
34 |
35 | export default mongoose.model('Product', ProductSchema)
36 |
--------------------------------------------------------------------------------
/Chapter07 and 08/mern-marketplace/server/models/shop.model.js:
--------------------------------------------------------------------------------
1 | import mongoose from 'mongoose'
2 | const ShopSchema = new mongoose.Schema({
3 | name: {
4 | type: String,
5 | trim: true,
6 | required: 'Name is required'
7 | },
8 | image: {
9 | data: Buffer,
10 | contentType: String
11 | },
12 | description: {
13 | type: String,
14 | trim: true
15 | },
16 | updated: Date,
17 | created: {
18 | type: Date,
19 | default: Date.now
20 | },
21 | owner: {type: mongoose.Schema.ObjectId, ref: 'User'}
22 | })
23 |
24 | export default mongoose.model('Shop', ShopSchema)
25 |
--------------------------------------------------------------------------------
/Chapter07 and 08/mern-marketplace/server/routes/auth.routes.js:
--------------------------------------------------------------------------------
1 | import express from 'express'
2 | import authCtrl from '../controllers/auth.controller'
3 |
4 | const router = express.Router()
5 |
6 | router.route('/auth/signin')
7 | .post(authCtrl.signin)
8 | router.route('/auth/signout')
9 | .get(authCtrl.signout)
10 |
11 | export default router
12 |
--------------------------------------------------------------------------------
/Chapter07 and 08/mern-marketplace/server/routes/order.routes.js:
--------------------------------------------------------------------------------
1 | import express from 'express'
2 | import orderCtrl from '../controllers/order.controller'
3 | import productCtrl from '../controllers/product.controller'
4 | import authCtrl from '../controllers/auth.controller'
5 | import shopCtrl from '../controllers/shop.controller'
6 | import userCtrl from '../controllers/user.controller'
7 |
8 | const router = express.Router()
9 |
10 | router.route('/api/orders/:userId')
11 | .post(authCtrl.requireSignin, userCtrl.stripeCustomer, productCtrl.decreaseQuantity, orderCtrl.create)
12 |
13 | router.route('/api/orders/shop/:shopId')
14 | .get(authCtrl.requireSignin, shopCtrl.isOwner, orderCtrl.listByShop)
15 |
16 | router.route('/api/orders/user/:userId')
17 | .get(authCtrl.requireSignin, orderCtrl.listByUser)
18 |
19 | router.route('/api/order/status_values')
20 | .get(orderCtrl.getStatusValues)
21 |
22 | router.route('/api/order/:shopId/cancel/:productId')
23 | .put(authCtrl.requireSignin, shopCtrl.isOwner, productCtrl.increaseQuantity, orderCtrl.update)
24 |
25 | router.route('/api/order/:orderId/charge/:userId/:shopId')
26 | .put(authCtrl.requireSignin, shopCtrl.isOwner, userCtrl.createCharge, orderCtrl.update)
27 |
28 | router.route('/api/order/status/:shopId')
29 | .put(authCtrl.requireSignin, shopCtrl.isOwner, orderCtrl.update)
30 |
31 | router.route('/api/order/:orderId')
32 | .get(orderCtrl.read)
33 |
34 | router.param('userId', userCtrl.userByID)
35 | router.param('shopId', shopCtrl.shopByID)
36 | router.param('productId', productCtrl.productByID)
37 | router.param('orderId', orderCtrl.orderByID)
38 |
39 | export default router
40 |
--------------------------------------------------------------------------------
/Chapter07 and 08/mern-marketplace/server/routes/product.routes.js:
--------------------------------------------------------------------------------
1 | import express from 'express'
2 | import productCtrl from '../controllers/product.controller'
3 | import authCtrl from '../controllers/auth.controller'
4 | import shopCtrl from '../controllers/shop.controller'
5 |
6 | const router = express.Router()
7 |
8 | router.route('/api/products/by/:shopId')
9 | .post(authCtrl.requireSignin, shopCtrl.isOwner, productCtrl.create)
10 | .get(productCtrl.listByShop)
11 |
12 | router.route('/api/products/latest')
13 | .get(productCtrl.listLatest)
14 |
15 | router.route('/api/products/related/:productId')
16 | .get(productCtrl.listRelated)
17 |
18 | router.route('/api/products/categories')
19 | .get(productCtrl.listCategories)
20 |
21 | router.route('/api/products')
22 | .get(productCtrl.list)
23 |
24 | router.route('/api/products/:productId')
25 | .get(productCtrl.read)
26 |
27 | router.route('/api/product/image/:productId')
28 | .get(productCtrl.photo, productCtrl.defaultPhoto)
29 | router.route('/api/product/defaultphoto')
30 | .get(productCtrl.defaultPhoto)
31 |
32 | router.route('/api/product/:shopId/:productId')
33 | .put(authCtrl.requireSignin, shopCtrl.isOwner, productCtrl.update)
34 | .delete(authCtrl.requireSignin, shopCtrl.isOwner, productCtrl.remove)
35 |
36 | router.param('shopId', shopCtrl.shopByID)
37 | router.param('productId', productCtrl.productByID)
38 |
39 | export default router
40 |
--------------------------------------------------------------------------------
/Chapter07 and 08/mern-marketplace/server/routes/shop.routes.js:
--------------------------------------------------------------------------------
1 | import express from 'express'
2 | import userCtrl from '../controllers/user.controller'
3 | import authCtrl from '../controllers/auth.controller'
4 | import shopCtrl from '../controllers/shop.controller'
5 |
6 | const router = express.Router()
7 |
8 | router.route('/api/shops')
9 | .get(shopCtrl.list)
10 |
11 | router.route('/api/shop/:shopId')
12 | .get(shopCtrl.read)
13 |
14 | router.route('/api/shops/by/:userId')
15 | .post(authCtrl.requireSignin, authCtrl.hasAuthorization, userCtrl.isSeller, shopCtrl.create)
16 | .get(authCtrl.requireSignin, authCtrl.hasAuthorization, shopCtrl.listByOwner)
17 |
18 | router.route('/api/shops/:shopId')
19 | .put(authCtrl.requireSignin, shopCtrl.isOwner, shopCtrl.update)
20 | .delete(authCtrl.requireSignin, shopCtrl.isOwner, shopCtrl.remove)
21 |
22 | router.route('/api/shops/logo/:shopId')
23 | .get(shopCtrl.photo, shopCtrl.defaultPhoto)
24 |
25 | router.route('/api/shops/defaultphoto')
26 | .get(shopCtrl.defaultPhoto)
27 |
28 | router.param('shopId', shopCtrl.shopByID)
29 | router.param('userId', userCtrl.userByID)
30 |
31 | export default router
32 |
--------------------------------------------------------------------------------
/Chapter07 and 08/mern-marketplace/server/routes/user.routes.js:
--------------------------------------------------------------------------------
1 | import express from 'express'
2 | import userCtrl from '../controllers/user.controller'
3 | import authCtrl from '../controllers/auth.controller'
4 |
5 | const router = express.Router()
6 |
7 | router.route('/api/users')
8 | .get(userCtrl.list)
9 | .post(userCtrl.create)
10 |
11 | router.route('/api/users/:userId')
12 | .get(authCtrl.requireSignin, userCtrl.read)
13 | .put(authCtrl.requireSignin, authCtrl.hasAuthorization, userCtrl.update)
14 | .delete(authCtrl.requireSignin, authCtrl.hasAuthorization, userCtrl.remove)
15 | router.route('/api/stripe_auth/:userId')
16 | .put(authCtrl.requireSignin, authCtrl.hasAuthorization, userCtrl.stripe_auth, userCtrl.update)
17 |
18 | router.param('userId', userCtrl.userByID)
19 |
20 | export default router
21 |
--------------------------------------------------------------------------------
/Chapter07 and 08/mern-marketplace/server/server.js:
--------------------------------------------------------------------------------
1 | import config from './../config/config'
2 | import app from './express'
3 | import mongoose from 'mongoose'
4 |
5 | // Connection URL
6 | mongoose.Promise = global.Promise
7 | mongoose.connect(config.mongoUri, { useNewUrlParser: true, useCreateIndex: true, useUnifiedTopology: true, useFindAndModify: true })
8 | mongoose.connection.on('error', () => {
9 | throw new Error(`unable to connect to database: ${config.mongoUri}`)
10 | })
11 |
12 | app.listen(config.port, (err) => {
13 | if (err) {
14 | console.log(err)
15 | }
16 | console.info('Server started on port %s.', config.port)
17 | })
18 |
--------------------------------------------------------------------------------
/Chapter07 and 08/mern-marketplace/template.js:
--------------------------------------------------------------------------------
1 | export default ({markup, css}) => {
2 | return `
3 |
4 |
5 |
6 | MERN Marketplace
7 |
8 |
9 |
14 |
15 |
16 | ${markup}
17 |
18 |
19 |
20 |
21 | `
22 | }
23 |
--------------------------------------------------------------------------------
/Chapter07 and 08/mern-marketplace/webpack.config.client.js:
--------------------------------------------------------------------------------
1 | const path = require('path')
2 | const webpack = require('webpack')
3 | const CURRENT_WORKING_DIR = process.cwd()
4 |
5 | const config = {
6 | name: "browser",
7 | mode: "development",
8 | devtool: 'eval-source-map',
9 | entry: [
10 | 'react-hot-loader/patch',
11 | 'webpack-hot-middleware/client?reload=true',
12 | path.join(CURRENT_WORKING_DIR, 'client/main.js')
13 | ],
14 | output: {
15 | path: path.join(CURRENT_WORKING_DIR , '/dist'),
16 | filename: 'bundle.js',
17 | publicPath: '/dist/'
18 | },
19 | module: {
20 | rules: [
21 | {
22 | test: /\.jsx?$/,
23 | exclude: /node_modules/,
24 | use: [
25 | 'babel-loader'
26 | ]
27 | },
28 | {
29 | test: /\.(ttf|eot|svg|gif|jpg|png)(\?[\s\S]+)?$/,
30 | use: 'file-loader'
31 | }
32 | ]
33 | }, plugins: [
34 | new webpack.HotModuleReplacementPlugin(),
35 | new webpack.NoEmitOnErrorsPlugin()
36 | ]
37 | }
38 |
39 | module.exports = config
40 |
--------------------------------------------------------------------------------
/Chapter07 and 08/mern-marketplace/webpack.config.client.production.js:
--------------------------------------------------------------------------------
1 | const path = require('path')
2 | const CURRENT_WORKING_DIR = process.cwd()
3 |
4 | const config = {
5 | mode: "production",
6 | entry: [
7 | path.join(CURRENT_WORKING_DIR, 'client/main.js')
8 | ],
9 | output: {
10 | path: path.join(CURRENT_WORKING_DIR , '/dist'),
11 | filename: 'bundle.js',
12 | publicPath: "/dist/"
13 | },
14 | module: {
15 | rules: [
16 | {
17 | test: /\.jsx?$/,
18 | exclude: /node_modules/,
19 | use: [
20 | 'babel-loader'
21 | ]
22 | },
23 | {
24 | test: /\.(ttf|eot|svg|gif|jpg|png)(\?[\s\S]+)?$/,
25 | use: 'file-loader'
26 | }
27 | ]
28 | }
29 | }
30 |
31 | module.exports = config
32 |
--------------------------------------------------------------------------------
/Chapter07 and 08/mern-marketplace/webpack.config.server.js:
--------------------------------------------------------------------------------
1 | const path = require('path')
2 | const nodeExternals = require('webpack-node-externals')
3 | const CURRENT_WORKING_DIR = process.cwd()
4 |
5 | const config = {
6 | name: "server",
7 | entry: [ path.join(CURRENT_WORKING_DIR , './server/server.js') ],
8 | target: "node",
9 | output: {
10 | path: path.join(CURRENT_WORKING_DIR , '/dist/'),
11 | filename: "server.generated.js",
12 | publicPath: '/dist/',
13 | libraryTarget: "commonjs2"
14 | },
15 | externals: [nodeExternals()],
16 | module: {
17 | rules: [
18 | {
19 | test: /\.js$/,
20 | exclude: /node_modules/,
21 | use: [ 'babel-loader' ]
22 | },
23 | {
24 | test: /\.(ttf|eot|svg|gif|jpg|png)(\?[\s\S]+)?$/,
25 | use: 'file-loader'
26 | }
27 | ]
28 | }
29 | }
30 |
31 | module.exports = config
32 |
--------------------------------------------------------------------------------
/Chapter09/mern-marketplace-bidding/.babelrc:
--------------------------------------------------------------------------------
1 | {
2 | "presets": [
3 | ["@babel/preset-env",
4 | {
5 | "targets": {
6 | "node": "current"
7 | }
8 | }
9 | ],
10 | "@babel/preset-react"
11 | ],
12 | "plugins": [
13 | "react-hot-loader/babel"
14 | ]
15 | }
16 |
--------------------------------------------------------------------------------
/Chapter09/mern-marketplace-bidding/.gitignore:
--------------------------------------------------------------------------------
1 | /node_modules/
2 | /dist/
3 | /data/
4 | npm-debug.log
5 |
--------------------------------------------------------------------------------
/Chapter09/mern-marketplace-bidding/LICENSE.md:
--------------------------------------------------------------------------------
1 | MIT License
2 |
3 | Copyright (c) 2018 Shama Hoque
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 |
--------------------------------------------------------------------------------
/Chapter09/mern-marketplace-bidding/README.md:
--------------------------------------------------------------------------------
1 | # MERN Marketplace
2 |
3 | An online marketplace application with seller accounts, product search and suggestions, shopping cart, order management, payment processing with Stripe, and live auction with Socket.io - developed using React, Node, Express and MongoDB.
4 |
5 | 
6 |
7 | ### [Live Demo](http://marketplace2.mernbook.com/ "MERN Marketplace")
8 |
9 | #### What you need to run this code
10 | 1. Node (13.12.0)
11 | 2. NPM (6.14.4) or Yarn (1.22.4)
12 | 3. MongoDB (4.2.0)
13 | 4. Stripe account with test data
14 |
15 | #### How to run this code
16 | 1. Make sure MongoDB is running on your system
17 | 2. Clone this repository
18 | 3. Update config/config.js with your test values for Stripe API keys and Stripe Connect Client ID
19 | 4. Open command line in the cloned folder,
20 | - To install dependencies, run ``` npm install ``` or ``` yarn ```
21 | - To run the application for development, run ``` npm run development ``` or ``` yarn development ```
22 | 5. Open [localhost:3000](http://localhost:3000/) in the browser
23 | ----
--------------------------------------------------------------------------------
/Chapter09/mern-marketplace-bidding/client/App.js:
--------------------------------------------------------------------------------
1 | import React from 'react'
2 | import MainRouter from './MainRouter'
3 | import {BrowserRouter} from 'react-router-dom'
4 | import { ThemeProvider } from '@material-ui/styles'
5 | import theme from './theme'
6 | import { hot } from 'react-hot-loader'
7 |
8 | const App = () => {
9 | React.useEffect(() => {
10 | const jssStyles = document.querySelector('#jss-server-side')
11 | if (jssStyles) {
12 | jssStyles.parentNode.removeChild(jssStyles)
13 | }
14 | }, [])
15 | return (
16 |
17 |
18 |
19 |
20 |
21 | )}
22 |
23 | export default hot(module)(App)
24 |
--------------------------------------------------------------------------------
/Chapter09/mern-marketplace-bidding/client/assets/images/default.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/PacktPublishing/Full-Stack-React-Projects-Second-Edition/c4007202bd767682b9122cb196b80cd4952658f2/Chapter09/mern-marketplace-bidding/client/assets/images/default.png
--------------------------------------------------------------------------------
/Chapter09/mern-marketplace-bidding/client/assets/images/stripeButton.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/PacktPublishing/Full-Stack-React-Projects-Second-Edition/c4007202bd767682b9122cb196b80cd4952658f2/Chapter09/mern-marketplace-bidding/client/assets/images/stripeButton.png
--------------------------------------------------------------------------------
/Chapter09/mern-marketplace-bidding/client/assets/images/unicornbike.jpg:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/PacktPublishing/Full-Stack-React-Projects-Second-Edition/c4007202bd767682b9122cb196b80cd4952658f2/Chapter09/mern-marketplace-bidding/client/assets/images/unicornbike.jpg
--------------------------------------------------------------------------------
/Chapter09/mern-marketplace-bidding/client/auth/PrivateRoute.js:
--------------------------------------------------------------------------------
1 | import React, { Component } from 'react'
2 | import { Route, Redirect } from 'react-router-dom'
3 | import auth from './auth-helper'
4 |
5 | const PrivateRoute = ({ component: Component, ...rest }) => (
6 | (
7 | auth.isAuthenticated() ? (
8 |
9 | ) : (
10 |
14 | )
15 | )}/>
16 | )
17 |
18 | export default PrivateRoute
19 |
--------------------------------------------------------------------------------
/Chapter09/mern-marketplace-bidding/client/auth/api-auth.js:
--------------------------------------------------------------------------------
1 | const signin = async (user) => {
2 | try {
3 | let response = await fetch('/auth/signin/', {
4 | method: 'POST',
5 | headers: {
6 | 'Accept': 'application/json',
7 | 'Content-Type': 'application/json'
8 | },
9 | credentials: 'include',
10 | body: JSON.stringify(user)
11 | })
12 | return await response.json()
13 | } catch(err) {
14 | console.log(err)
15 | }
16 | }
17 |
18 | const signout = async () => {
19 | try {
20 | let response = await fetch('/auth/signout/', { method: 'GET' })
21 | return await response.json()
22 | } catch(err) {
23 | console.log(err)
24 | }
25 | }
26 |
27 | export {
28 | signin,
29 | signout
30 | }
--------------------------------------------------------------------------------
/Chapter09/mern-marketplace-bidding/client/auth/auth-helper.js:
--------------------------------------------------------------------------------
1 | import { signout } from './api-auth.js'
2 |
3 | const auth = {
4 | isAuthenticated() {
5 | if (typeof window == "undefined")
6 | return false
7 |
8 | if (sessionStorage.getItem('jwt'))
9 | return JSON.parse(sessionStorage.getItem('jwt'))
10 | else
11 | return false
12 | },
13 | authenticate(jwt, cb) {
14 | if (typeof window !== "undefined")
15 | sessionStorage.setItem('jwt', JSON.stringify(jwt))
16 | cb()
17 | },
18 | clearJWT(cb) {
19 | if (typeof window !== "undefined")
20 | sessionStorage.removeItem('jwt')
21 | cb()
22 | //optional
23 | signout().then((data) => {
24 | document.cookie = "t=; expires=Thu, 01 Jan 1970 00:00:00 UTC; path=/;"
25 | })
26 | },
27 | updateUser(user, cb) {
28 | if(typeof window !== "undefined"){
29 | if(sessionStorage.getItem('jwt')){
30 | let auth = JSON.parse(sessionStorage.getItem('jwt'))
31 | auth.user = user
32 | sessionStorage.setItem('jwt', JSON.stringify(auth))
33 | cb()
34 | }
35 | }
36 | }
37 | }
38 |
39 | export default auth
40 |
--------------------------------------------------------------------------------
/Chapter09/mern-marketplace-bidding/client/cart/AddToCart.js:
--------------------------------------------------------------------------------
1 | import React, {useState} from 'react'
2 | import {makeStyles} from '@material-ui/core/styles'
3 | import PropTypes from 'prop-types'
4 | import IconButton from '@material-ui/core/IconButton'
5 | import AddCartIcon from '@material-ui/icons/AddShoppingCart'
6 | import DisabledCartIcon from '@material-ui/icons/RemoveShoppingCart'
7 | import cart from './cart-helper.js'
8 | import { Redirect } from 'react-router-dom'
9 |
10 | const useStyles = makeStyles(theme => ({
11 | iconButton: {
12 | width: '28px',
13 | height: '28px'
14 | },
15 | disabledIconButton: {
16 | color: '#7f7563',
17 | width: '28px',
18 | height: '28px'
19 | }
20 | }))
21 |
22 | export default function AddToCart(props) {
23 | const classes = useStyles()
24 | const [redirect, setRedirect] = useState(false)
25 |
26 | const addToCart = () => {
27 | cart.addItem(props.item, () => {
28 | setRedirect({redirect:true})
29 | })
30 | }
31 | if (redirect) {
32 | return ()
33 | }
34 | return (
35 | {props.item.quantity >= 0 ?
36 |
37 |
38 | :
39 |
40 |
41 | }
42 | )
43 | }
44 |
45 | AddToCart.propTypes = {
46 | item: PropTypes.object.isRequired,
47 | cartStyle: PropTypes.string
48 | }
--------------------------------------------------------------------------------
/Chapter09/mern-marketplace-bidding/client/cart/Cart.js:
--------------------------------------------------------------------------------
1 | import React, {useState, useEffect} from 'react'
2 | import Grid from '@material-ui/core/Grid'
3 | import {makeStyles} from '@material-ui/core/styles'
4 | import CartItems from './CartItems'
5 | import {StripeProvider} from 'react-stripe-elements'
6 | import config from './../../config/config'
7 | import Checkout from './Checkout'
8 |
9 | const useStyles = makeStyles(theme => ({
10 | root: {
11 | flexGrow: 1,
12 | margin: 30,
13 | }
14 | }))
15 |
16 | export default function Cart () {
17 | const classes = useStyles()
18 | const [checkout, setCheckout] = useState(false)
19 |
20 | const showCheckout = val => {
21 | setCheckout(val)
22 | }
23 |
24 | return (
25 |
26 |
27 |
29 |
30 | {checkout &&
31 |
32 |
33 |
34 |
35 | }
36 |
37 |
)
38 | }
39 |
--------------------------------------------------------------------------------
/Chapter09/mern-marketplace-bidding/client/main.js:
--------------------------------------------------------------------------------
1 | import React from 'react'
2 | import { hydrate } from 'react-dom'
3 | import App from './App'
4 |
5 | hydrate(, document.getElementById('root'))
6 |
--------------------------------------------------------------------------------
/Chapter09/mern-marketplace-bidding/client/theme.js:
--------------------------------------------------------------------------------
1 | import { createMuiTheme } from '@material-ui/core/styles'
2 | import { blueGrey, lightGreen } from '@material-ui/core/colors'
3 |
4 | const theme = createMuiTheme({
5 | palette: {
6 | primary: {
7 | light: '#8eacbb',
8 | main: '#607d8b',
9 | dark: '#34515e',
10 | contrastText: '#fff',
11 | },
12 | secondary: {
13 | light: '#e7ff8c',
14 | main: '#b2ff59',
15 | dark: '#7ecb20',
16 | contrastText: '#000',
17 | },
18 | openTitle: blueGrey['400'],
19 | protectedTitle: lightGreen['400'],
20 | type: 'light'
21 | }
22 | })
23 |
24 | export default theme
--------------------------------------------------------------------------------
/Chapter09/mern-marketplace-bidding/config/config.js:
--------------------------------------------------------------------------------
1 | const config = {
2 | env: process.env.NODE_ENV || 'development',
3 | port: process.env.PORT || 3000,
4 | jwtSecret: process.env.JWT_SECRET || "YOUR_secret_key",
5 | mongoUri: process.env.MONGODB_URI ||
6 | process.env.MONGO_HOST ||
7 | 'mongodb://' + (process.env.IP || 'localhost') + ':' +
8 | (process.env.MONGO_PORT || '27017') +
9 | '/mernproject',
10 | stripe_connect_test_client_id: 'YOUR_stripe_connect_test_client',
11 | stripe_test_secret_key: 'YOUR_stripe_test_secret_key',
12 | stripe_test_api_key: 'YOUR_stripe_test_api_key'
13 | }
14 |
15 | export default config
16 |
--------------------------------------------------------------------------------
/Chapter09/mern-marketplace-bidding/nodemon.json:
--------------------------------------------------------------------------------
1 | {
2 | "verbose": false,
3 | "watch": [
4 | "./server"
5 | ],
6 | "exec": "webpack --mode=development --config webpack.config.server.js && node ./dist/server.generated.js"
7 | }
8 |
--------------------------------------------------------------------------------
/Chapter09/mern-marketplace-bidding/server/controllers/bidding.controller.js:
--------------------------------------------------------------------------------
1 | import Auction from '../models/auction.model'
2 |
3 | export default (server) => {
4 | const io = require('socket.io').listen(server)
5 | io.on('connection', function(socket){
6 | socket.on('join auction room', data => {
7 | socket.join(data.room)
8 | })
9 | socket.on('leave auction room', data => {
10 | socket.leave(data.room)
11 | })
12 | socket.on('new bid', data => {
13 | bid(data.bidInfo, data.room)
14 | })
15 | })
16 | const bid = async (bid, auction) => {
17 | try {
18 | let result = await Auction.findOneAndUpdate({_id:auction, $or: [{'bids.0.bid':{$lt:bid.bid}},{bids:{$eq:[]}} ]}, {$push: {bids: {$each:[bid], $position: 0}}}, {new: true})
19 | .populate('bids.bidder', '_id name')
20 | .populate('seller', '_id name')
21 | .exec()
22 | io
23 | .to(auction)
24 | .emit('new bid', result)
25 | } catch(err) {
26 | console.log(err)
27 | }
28 | }
29 | }
30 |
31 |
--------------------------------------------------------------------------------
/Chapter09/mern-marketplace-bidding/server/devBundle.js:
--------------------------------------------------------------------------------
1 | import config from './../config/config'
2 | import webpack from 'webpack'
3 | import webpackMiddleware from 'webpack-dev-middleware'
4 | import webpackHotMiddleware from 'webpack-hot-middleware'
5 | import webpackConfig from './../webpack.config.client.js'
6 |
7 | const compile = (app) => {
8 | if(config.env === "development"){
9 | const compiler = webpack(webpackConfig)
10 | const middleware = webpackMiddleware(compiler, {
11 | publicPath: webpackConfig.output.publicPath
12 | })
13 | app.use(middleware)
14 | app.use(webpackHotMiddleware(compiler))
15 | }
16 | }
17 |
18 | export default {
19 | compile
20 | }
21 |
--------------------------------------------------------------------------------
/Chapter09/mern-marketplace-bidding/server/helpers/dbErrorHandler.js:
--------------------------------------------------------------------------------
1 | 'use strict'
2 |
3 | /**
4 | * Get unique error field name
5 | */
6 | const getUniqueErrorMessage = (err) => {
7 | let output
8 | try {
9 | let fieldName = err.message.substring(err.message.lastIndexOf('.$') + 2, err.message.lastIndexOf('_1'))
10 | output = fieldName.charAt(0).toUpperCase() + fieldName.slice(1) + ' already exists'
11 | } catch (ex) {
12 | output = 'Unique field already exists'
13 | }
14 |
15 | return output
16 | }
17 |
18 | /**
19 | * Get the error message from error object
20 | */
21 | const getErrorMessage = (err) => {
22 | let message = ''
23 |
24 | if (err.code) {
25 | switch (err.code) {
26 | case 11000:
27 | case 11001:
28 | message = getUniqueErrorMessage(err)
29 | break
30 | default:
31 | message = 'Something went wrong'
32 | }
33 | } else {
34 | for (let errName in err.errors) {
35 | if (err.errors[errName].message) message = err.errors[errName].message
36 | }
37 | }
38 |
39 | return message
40 | }
41 |
42 | export default {getErrorMessage}
43 |
--------------------------------------------------------------------------------
/Chapter09/mern-marketplace-bidding/server/models/auction.model.js:
--------------------------------------------------------------------------------
1 | import mongoose from 'mongoose'
2 | const AuctionSchema = new mongoose.Schema({
3 | itemName: {
4 | type: String,
5 | trim: true,
6 | required: 'Item name is required'
7 | },
8 | description: {
9 | type: String,
10 | trim: true
11 | },
12 | image: {
13 | data: Buffer,
14 | contentType: String
15 | },
16 | updated: Date,
17 | created: {
18 | type: Date,
19 | default: Date.now
20 | },
21 | bidStart: {
22 | type: Date,
23 | default: Date.now
24 | },
25 | bidEnd: {
26 | type: Date,
27 | required: "Auction end time is required"
28 | },
29 | seller: {
30 | type: mongoose.Schema.ObjectId,
31 | ref: 'User'
32 | },
33 | startingBid: { type: Number, default: 0 },
34 | bids: [{
35 | bidder: {type: mongoose.Schema.ObjectId, ref: 'User'},
36 | bid: Number,
37 | time: Date
38 | }]
39 | })
40 |
41 | export default mongoose.model('Auction', AuctionSchema)
42 |
--------------------------------------------------------------------------------
/Chapter09/mern-marketplace-bidding/server/models/order.model.js:
--------------------------------------------------------------------------------
1 | import mongoose from 'mongoose'
2 | const CartItemSchema = new mongoose.Schema({
3 | product: {type: mongoose.Schema.ObjectId, ref: 'Product'},
4 | quantity: Number,
5 | shop: {type: mongoose.Schema.ObjectId, ref: 'Shop'},
6 | status: {type: String,
7 | default: 'Not processed',
8 | enum: ['Not processed' , 'Processing', 'Shipped', 'Delivered', 'Cancelled']}
9 | })
10 | const CartItem = mongoose.model('CartItem', CartItemSchema)
11 | const OrderSchema = new mongoose.Schema({
12 | products: [CartItemSchema],
13 | customer_name: {
14 | type: String,
15 | trim: true,
16 | required: 'Name is required'
17 | },
18 | customer_email: {
19 | type: String,
20 | trim: true,
21 | match: [/.+\@.+\..+/, 'Please fill a valid email address'],
22 | required: 'Email is required'
23 | },
24 | delivery_address: {
25 | street: {type: String, required: 'Street is required'},
26 | city: {type: String, required: 'City is required'},
27 | state: {type: String},
28 | zipcode: {type: String, required: 'Zip Code is required'},
29 | country: {type: String, required: 'Country is required'}
30 | },
31 | payment_id: {},
32 | updated: Date,
33 | created: {
34 | type: Date,
35 | default: Date.now
36 | },
37 | user: {type: mongoose.Schema.ObjectId, ref: 'User'}
38 | })
39 |
40 | const Order = mongoose.model('Order', OrderSchema)
41 |
42 | export {Order, CartItem}
43 |
--------------------------------------------------------------------------------
/Chapter09/mern-marketplace-bidding/server/models/product.model.js:
--------------------------------------------------------------------------------
1 | import mongoose from 'mongoose'
2 | const ProductSchema = new mongoose.Schema({
3 | name: {
4 | type: String,
5 | trim: true,
6 | required: 'Name is required'
7 | },
8 | image: {
9 | data: Buffer,
10 | contentType: String
11 | },
12 | description: {
13 | type: String,
14 | trim: true
15 | },
16 | category: {
17 | type: String
18 | },
19 | quantity: {
20 | type: Number,
21 | required: "Quantity is required"
22 | },
23 | price: {
24 | type: Number,
25 | required: "Price is required"
26 | },
27 | updated: Date,
28 | created: {
29 | type: Date,
30 | default: Date.now
31 | },
32 | shop: {type: mongoose.Schema.ObjectId, ref: 'Shop'}
33 | })
34 |
35 | export default mongoose.model('Product', ProductSchema)
36 |
--------------------------------------------------------------------------------
/Chapter09/mern-marketplace-bidding/server/models/shop.model.js:
--------------------------------------------------------------------------------
1 | import mongoose from 'mongoose'
2 | const ShopSchema = new mongoose.Schema({
3 | name: {
4 | type: String,
5 | trim: true,
6 | required: 'Name is required'
7 | },
8 | image: {
9 | data: Buffer,
10 | contentType: String
11 | },
12 | description: {
13 | type: String,
14 | trim: true
15 | },
16 | updated: Date,
17 | created: {
18 | type: Date,
19 | default: Date.now
20 | },
21 | owner: {type: mongoose.Schema.ObjectId, ref: 'User'}
22 | })
23 |
24 | export default mongoose.model('Shop', ShopSchema)
25 |
--------------------------------------------------------------------------------
/Chapter09/mern-marketplace-bidding/server/routes/auction.routes.js:
--------------------------------------------------------------------------------
1 | import express from 'express'
2 | import userCtrl from '../controllers/user.controller'
3 | import authCtrl from '../controllers/auth.controller'
4 | import auctionCtrl from '../controllers/auction.controller'
5 |
6 | const router = express.Router()
7 |
8 | router.route('/api/auctions')
9 | .get(auctionCtrl.listOpen)
10 |
11 | router.route('/api/auctions/bid/:userId')
12 | .get(auctionCtrl.listByBidder)
13 |
14 | router.route('/api/auction/:auctionId')
15 | .get(auctionCtrl.read)
16 |
17 | router.route('/api/auctions/by/:userId')
18 | .post(authCtrl.requireSignin, authCtrl.hasAuthorization, userCtrl.isSeller, auctionCtrl.create)
19 | .get(authCtrl.requireSignin, authCtrl.hasAuthorization, auctionCtrl.listBySeller)
20 |
21 | router.route('/api/auctions/:auctionId')
22 | .put(authCtrl.requireSignin, auctionCtrl.isSeller, auctionCtrl.update)
23 | .delete(authCtrl.requireSignin, auctionCtrl.isSeller, auctionCtrl.remove)
24 |
25 | router.route('/api/auctions/image/:auctionId')
26 | .get(auctionCtrl.photo, auctionCtrl.defaultPhoto)
27 |
28 | router.route('/api/auctions/defaultphoto')
29 | .get(auctionCtrl.defaultPhoto)
30 |
31 | router.param('auctionId', auctionCtrl.auctionByID)
32 | router.param('userId', userCtrl.userByID)
33 |
34 | export default router
35 |
--------------------------------------------------------------------------------
/Chapter09/mern-marketplace-bidding/server/routes/auth.routes.js:
--------------------------------------------------------------------------------
1 | import express from 'express'
2 | import authCtrl from '../controllers/auth.controller'
3 |
4 | const router = express.Router()
5 |
6 | router.route('/auth/signin')
7 | .post(authCtrl.signin)
8 | router.route('/auth/signout')
9 | .get(authCtrl.signout)
10 |
11 | export default router
12 |
--------------------------------------------------------------------------------
/Chapter09/mern-marketplace-bidding/server/routes/order.routes.js:
--------------------------------------------------------------------------------
1 | import express from 'express'
2 | import orderCtrl from '../controllers/order.controller'
3 | import productCtrl from '../controllers/product.controller'
4 | import authCtrl from '../controllers/auth.controller'
5 | import shopCtrl from '../controllers/shop.controller'
6 | import userCtrl from '../controllers/user.controller'
7 |
8 | const router = express.Router()
9 |
10 | router.route('/api/orders/:userId')
11 | .post(authCtrl.requireSignin, userCtrl.stripeCustomer, productCtrl.decreaseQuantity, orderCtrl.create)
12 |
13 | router.route('/api/orders/shop/:shopId')
14 | .get(authCtrl.requireSignin, shopCtrl.isOwner, orderCtrl.listByShop)
15 |
16 | router.route('/api/orders/user/:userId')
17 | .get(authCtrl.requireSignin, orderCtrl.listByUser)
18 |
19 | router.route('/api/order/status_values')
20 | .get(orderCtrl.getStatusValues)
21 |
22 | router.route('/api/order/:shopId/cancel/:productId')
23 | .put(authCtrl.requireSignin, shopCtrl.isOwner, productCtrl.increaseQuantity, orderCtrl.update)
24 |
25 | router.route('/api/order/:orderId/charge/:userId/:shopId')
26 | .put(authCtrl.requireSignin, shopCtrl.isOwner, userCtrl.createCharge, orderCtrl.update)
27 |
28 | router.route('/api/order/status/:shopId')
29 | .put(authCtrl.requireSignin, shopCtrl.isOwner, orderCtrl.update)
30 |
31 | router.route('/api/order/:orderId')
32 | .get(orderCtrl.read)
33 |
34 | router.param('userId', userCtrl.userByID)
35 | router.param('shopId', shopCtrl.shopByID)
36 | router.param('productId', productCtrl.productByID)
37 | router.param('orderId', orderCtrl.orderByID)
38 |
39 | export default router
40 |
--------------------------------------------------------------------------------
/Chapter09/mern-marketplace-bidding/server/routes/product.routes.js:
--------------------------------------------------------------------------------
1 | import express from 'express'
2 | import productCtrl from '../controllers/product.controller'
3 | import authCtrl from '../controllers/auth.controller'
4 | import shopCtrl from '../controllers/shop.controller'
5 |
6 | const router = express.Router()
7 |
8 | router.route('/api/products/by/:shopId')
9 | .post(authCtrl.requireSignin, shopCtrl.isOwner, productCtrl.create)
10 | .get(productCtrl.listByShop)
11 |
12 | router.route('/api/products/latest')
13 | .get(productCtrl.listLatest)
14 |
15 | router.route('/api/products/related/:productId')
16 | .get(productCtrl.listRelated)
17 |
18 | router.route('/api/products/categories')
19 | .get(productCtrl.listCategories)
20 |
21 | router.route('/api/products')
22 | .get(productCtrl.list)
23 |
24 | router.route('/api/products/:productId')
25 | .get(productCtrl.read)
26 |
27 | router.route('/api/product/image/:productId')
28 | .get(productCtrl.photo, productCtrl.defaultPhoto)
29 | router.route('/api/product/defaultphoto')
30 | .get(productCtrl.defaultPhoto)
31 |
32 | router.route('/api/product/:shopId/:productId')
33 | .put(authCtrl.requireSignin, shopCtrl.isOwner, productCtrl.update)
34 | .delete(authCtrl.requireSignin, shopCtrl.isOwner, productCtrl.remove)
35 |
36 | router.param('shopId', shopCtrl.shopByID)
37 | router.param('productId', productCtrl.productByID)
38 |
39 | export default router
40 |
--------------------------------------------------------------------------------
/Chapter09/mern-marketplace-bidding/server/routes/shop.routes.js:
--------------------------------------------------------------------------------
1 | import express from 'express'
2 | import userCtrl from '../controllers/user.controller'
3 | import authCtrl from '../controllers/auth.controller'
4 | import shopCtrl from '../controllers/shop.controller'
5 |
6 | const router = express.Router()
7 |
8 | router.route('/api/shops')
9 | .get(shopCtrl.list)
10 |
11 | router.route('/api/shop/:shopId')
12 | .get(shopCtrl.read)
13 |
14 | router.route('/api/shops/by/:userId')
15 | .post(authCtrl.requireSignin, authCtrl.hasAuthorization, userCtrl.isSeller, shopCtrl.create)
16 | .get(authCtrl.requireSignin, authCtrl.hasAuthorization, shopCtrl.listByOwner)
17 |
18 | router.route('/api/shops/:shopId')
19 | .put(authCtrl.requireSignin, shopCtrl.isOwner, shopCtrl.update)
20 | .delete(authCtrl.requireSignin, shopCtrl.isOwner, shopCtrl.remove)
21 |
22 | router.route('/api/shops/logo/:shopId')
23 | .get(shopCtrl.photo, shopCtrl.defaultPhoto)
24 |
25 | router.route('/api/shops/defaultphoto')
26 | .get(shopCtrl.defaultPhoto)
27 |
28 | router.param('shopId', shopCtrl.shopByID)
29 | router.param('userId', userCtrl.userByID)
30 |
31 | export default router
32 |
--------------------------------------------------------------------------------
/Chapter09/mern-marketplace-bidding/server/routes/user.routes.js:
--------------------------------------------------------------------------------
1 | import express from 'express'
2 | import userCtrl from '../controllers/user.controller'
3 | import authCtrl from '../controllers/auth.controller'
4 |
5 | const router = express.Router()
6 |
7 | router.route('/api/users')
8 | .get(userCtrl.list)
9 | .post(userCtrl.create)
10 |
11 | router.route('/api/users/:userId')
12 | .get(authCtrl.requireSignin, userCtrl.read)
13 | .put(authCtrl.requireSignin, authCtrl.hasAuthorization, userCtrl.update)
14 | .delete(authCtrl.requireSignin, authCtrl.hasAuthorization, userCtrl.remove)
15 | router.route('/api/stripe_auth/:userId')
16 | .put(authCtrl.requireSignin, authCtrl.hasAuthorization, userCtrl.stripe_auth, userCtrl.update)
17 |
18 | router.param('userId', userCtrl.userByID)
19 |
20 | export default router
21 |
--------------------------------------------------------------------------------
/Chapter09/mern-marketplace-bidding/server/server.js:
--------------------------------------------------------------------------------
1 | import config from './../config/config'
2 | import app from './express'
3 | import mongoose from 'mongoose'
4 | import bidding from './controllers/bidding.controller'
5 |
6 | // Connection URL
7 | mongoose.Promise = global.Promise
8 | mongoose.connect(config.mongoUri, { useNewUrlParser: true, useCreateIndex: true, useUnifiedTopology: true, useFindAndModify: true })
9 | mongoose.connection.on('error', () => {
10 | throw new Error(`unable to connect to database: ${config.mongoUri}`)
11 | })
12 |
13 | const server = app.listen(config.port, (err) => {
14 | if (err) {
15 | console.log(err)
16 | }
17 | console.info('Server started on port %s.', config.port)
18 | })
19 |
20 | bidding(server)
21 |
--------------------------------------------------------------------------------
/Chapter09/mern-marketplace-bidding/template.js:
--------------------------------------------------------------------------------
1 | export default ({markup, css}) => {
2 | return `
3 |
4 |
5 |
6 | MERN Marketplace
7 |
8 |
9 |
14 |
15 |
16 | ${markup}
17 |
18 |
19 |
20 |
21 | `
22 | }
23 |
--------------------------------------------------------------------------------
/Chapter09/mern-marketplace-bidding/webpack.config.client.js:
--------------------------------------------------------------------------------
1 | const path = require('path')
2 | const webpack = require('webpack')
3 | const CURRENT_WORKING_DIR = process.cwd()
4 |
5 | const config = {
6 | name: "browser",
7 | mode: "development",
8 | devtool: 'eval-source-map',
9 | entry: [
10 | 'react-hot-loader/patch',
11 | 'webpack-hot-middleware/client?reload=true',
12 | path.join(CURRENT_WORKING_DIR, 'client/main.js')
13 | ],
14 | output: {
15 | path: path.join(CURRENT_WORKING_DIR , '/dist'),
16 | filename: 'bundle.js',
17 | publicPath: '/dist/'
18 | },
19 | module: {
20 | rules: [
21 | {
22 | test: /\.jsx?$/,
23 | exclude: /node_modules/,
24 | use: [
25 | 'babel-loader'
26 | ]
27 | },
28 | {
29 | test: /\.(ttf|eot|svg|gif|jpg|png)(\?[\s\S]+)?$/,
30 | use: 'file-loader'
31 | }
32 | ]
33 | }, plugins: [
34 | new webpack.HotModuleReplacementPlugin(),
35 | new webpack.NoEmitOnErrorsPlugin()
36 | ]
37 | }
38 |
39 | module.exports = config
40 |
--------------------------------------------------------------------------------
/Chapter09/mern-marketplace-bidding/webpack.config.client.production.js:
--------------------------------------------------------------------------------
1 | const path = require('path')
2 | const CURRENT_WORKING_DIR = process.cwd()
3 |
4 | const config = {
5 | mode: "production",
6 | entry: [
7 | path.join(CURRENT_WORKING_DIR, 'client/main.js')
8 | ],
9 | output: {
10 | path: path.join(CURRENT_WORKING_DIR , '/dist'),
11 | filename: 'bundle.js',
12 | publicPath: "/dist/"
13 | },
14 | module: {
15 | rules: [
16 | {
17 | test: /\.jsx?$/,
18 | exclude: /node_modules/,
19 | use: [
20 | 'babel-loader'
21 | ]
22 | },
23 | {
24 | test: /\.(ttf|eot|svg|gif|jpg|png)(\?[\s\S]+)?$/,
25 | use: 'file-loader'
26 | }
27 | ]
28 | }
29 | }
30 |
31 | module.exports = config
32 |
--------------------------------------------------------------------------------
/Chapter09/mern-marketplace-bidding/webpack.config.server.js:
--------------------------------------------------------------------------------
1 | const path = require('path')
2 | const nodeExternals = require('webpack-node-externals')
3 | const CURRENT_WORKING_DIR = process.cwd()
4 |
5 | const config = {
6 | name: "server",
7 | entry: [ path.join(CURRENT_WORKING_DIR , './server/server.js') ],
8 | target: "node",
9 | output: {
10 | path: path.join(CURRENT_WORKING_DIR , '/dist/'),
11 | filename: "server.generated.js",
12 | publicPath: '/dist/',
13 | libraryTarget: "commonjs2"
14 | },
15 | externals: [nodeExternals()],
16 | module: {
17 | rules: [
18 | {
19 | test: /\.js$/,
20 | exclude: /node_modules/,
21 | use: [ 'babel-loader' ]
22 | },
23 | {
24 | test: /\.(ttf|eot|svg|gif|jpg|png)(\?[\s\S]+)?$/,
25 | use: 'file-loader'
26 | }
27 | ]
28 | }
29 | }
30 |
31 | module.exports = config
32 |
--------------------------------------------------------------------------------
/Chapter10/mern-expense-tracker/.babelrc:
--------------------------------------------------------------------------------
1 | {
2 | "presets": [
3 | ["@babel/preset-env",
4 | {
5 | "targets": {
6 | "node": "current"
7 | }
8 | }
9 | ],
10 | "@babel/preset-react"
11 | ],
12 | "plugins": [
13 | "react-hot-loader/babel"
14 | ]
15 | }
16 |
--------------------------------------------------------------------------------
/Chapter10/mern-expense-tracker/.gitignore:
--------------------------------------------------------------------------------
1 | /node_modules/
2 | /dist/
3 | /data/
4 | npm-debug.log
5 |
--------------------------------------------------------------------------------
/Chapter10/mern-expense-tracker/LICENSE.md:
--------------------------------------------------------------------------------
1 | MIT License
2 |
3 | Copyright (c) 2018 Shama Hoque
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 |
--------------------------------------------------------------------------------
/Chapter10/mern-expense-tracker/README.md:
--------------------------------------------------------------------------------
1 | # MERN Expense Tracker
2 |
3 | An expense tracking application with data visualization - developed using React, Node, Express, MongoDB and Victory.
4 |
5 | 
6 |
7 | 
8 |
9 | ### [Live Demo](http://expensetracker.mernbook.com/ "MERN Expense Tracker")
10 |
11 | #### What you need to run this code
12 | 1. Node (13.12.0)
13 | 2. NPM (6.14.4) or Yarn (1.22.4)
14 | 3. MongoDB (4.2.0)
15 |
16 | #### How to run this code
17 | 1. Make sure MongoDB is running on your system
18 | 2. Clone this repository
19 | 3. Open command line in the cloned folder,
20 | - To install dependencies, run ``` npm install ``` or ``` yarn ```
21 | - To run the application for development, run ``` npm run development ``` or ``` yarn development ```
22 | 4. Open [localhost:3000](http://localhost:3000/) in the browser
23 | ----
--------------------------------------------------------------------------------
/Chapter10/mern-expense-tracker/client/App.js:
--------------------------------------------------------------------------------
1 | import React from 'react'
2 | import MainRouter from './MainRouter'
3 | import {BrowserRouter} from 'react-router-dom'
4 | import { ThemeProvider } from '@material-ui/styles'
5 | import theme from './theme'
6 | import { hot } from 'react-hot-loader'
7 |
8 | const App = () => {
9 | React.useEffect(() => {
10 | const jssStyles = document.querySelector('#jss-server-side')
11 | if (jssStyles) {
12 | jssStyles.parentNode.removeChild(jssStyles)
13 | }
14 | }, [])
15 | return (
16 |
17 |
18 |
19 |
20 |
21 | )}
22 |
23 | export default hot(module)(App)
24 |
--------------------------------------------------------------------------------
/Chapter10/mern-expense-tracker/client/MainRouter.js:
--------------------------------------------------------------------------------
1 | import React from 'react'
2 | import {Route, Switch} from 'react-router-dom'
3 | import Home from './core/Home'
4 | import Users from './user/Users'
5 | import Signup from './user/Signup'
6 | import Signin from './auth/Signin'
7 | import EditProfile from './user/EditProfile'
8 | import Profile from './user/Profile'
9 | import PrivateRoute from './auth/PrivateRoute'
10 | import Menu from './core/Menu'
11 | import NewExpense from './expense/NewExpense'
12 | import Expenses from './expense/Expenses'
13 | import Reports from './report/Reports'
14 |
15 | const MainRouter = () => {
16 | return (
17 |
18 |
19 |
20 |
21 |
22 |
23 |
24 |
25 |
26 |
27 |
28 |
29 |
)
30 | }
31 |
32 | export default MainRouter
33 |
--------------------------------------------------------------------------------
/Chapter10/mern-expense-tracker/client/assets/images/unicornbike.jpg:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/PacktPublishing/Full-Stack-React-Projects-Second-Edition/c4007202bd767682b9122cb196b80cd4952658f2/Chapter10/mern-expense-tracker/client/assets/images/unicornbike.jpg
--------------------------------------------------------------------------------
/Chapter10/mern-expense-tracker/client/assets/images/unicorncoin.jpg:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/PacktPublishing/Full-Stack-React-Projects-Second-Edition/c4007202bd767682b9122cb196b80cd4952658f2/Chapter10/mern-expense-tracker/client/assets/images/unicorncoin.jpg
--------------------------------------------------------------------------------
/Chapter10/mern-expense-tracker/client/auth/PrivateRoute.js:
--------------------------------------------------------------------------------
1 | import React, { Component } from 'react'
2 | import { Route, Redirect } from 'react-router-dom'
3 | import auth from './auth-helper'
4 |
5 | const PrivateRoute = ({ component: Component, ...rest }) => (
6 | (
7 | auth.isAuthenticated() ? (
8 |
9 | ) : (
10 |
14 | )
15 | )}/>
16 | )
17 |
18 | export default PrivateRoute
19 |
--------------------------------------------------------------------------------
/Chapter10/mern-expense-tracker/client/auth/api-auth.js:
--------------------------------------------------------------------------------
1 | const signin = async (user) => {
2 | try {
3 | let response = await fetch('/auth/signin/', {
4 | method: 'POST',
5 | headers: {
6 | 'Accept': 'application/json',
7 | 'Content-Type': 'application/json'
8 | },
9 | credentials: 'include',
10 | body: JSON.stringify(user)
11 | })
12 | return await response.json()
13 | } catch(err) {
14 | console.log(err)
15 | }
16 | }
17 |
18 | const signout = async () => {
19 | try {
20 | let response = await fetch('/auth/signout/', { method: 'GET' })
21 | return await response.json()
22 | } catch(err) {
23 | console.log(err)
24 | }
25 | }
26 |
27 | export {
28 | signin,
29 | signout
30 | }
--------------------------------------------------------------------------------
/Chapter10/mern-expense-tracker/client/auth/auth-helper.js:
--------------------------------------------------------------------------------
1 | import { signout } from './api-auth.js'
2 |
3 | const auth = {
4 | isAuthenticated() {
5 | if (typeof window == "undefined")
6 | return false
7 |
8 | if (sessionStorage.getItem('jwt'))
9 | return JSON.parse(sessionStorage.getItem('jwt'))
10 | else
11 | return false
12 | },
13 | authenticate(jwt, cb) {
14 | if (typeof window !== "undefined")
15 | sessionStorage.setItem('jwt', JSON.stringify(jwt))
16 | cb()
17 | },
18 | clearJWT(cb) {
19 | if (typeof window !== "undefined")
20 | sessionStorage.removeItem('jwt')
21 | cb()
22 | //optional
23 | signout().then((data) => {
24 | document.cookie = "t=; expires=Thu, 01 Jan 1970 00:00:00 UTC; path=/;"
25 | })
26 | }
27 | }
28 |
29 | export default auth
30 |
--------------------------------------------------------------------------------
/Chapter10/mern-expense-tracker/client/main.js:
--------------------------------------------------------------------------------
1 | import React from 'react'
2 | import { hydrate } from 'react-dom'
3 | import App from './App'
4 |
5 | hydrate(, document.getElementById('root'))
6 |
--------------------------------------------------------------------------------
/Chapter10/mern-expense-tracker/client/report/Reports.js:
--------------------------------------------------------------------------------
1 | import React from 'react'
2 | import { makeStyles } from '@material-ui/core/styles'
3 | import Divider from '@material-ui/core/Divider'
4 | import CategoryPie from './CategoryPie'
5 | import YearlyBar from './YearlyBar'
6 | import MonthlyScatter from './MonthlyScatter'
7 |
8 | const useStyles = makeStyles(theme => ({
9 | root: {
10 | width: '90%',
11 | maxWidth: '800px',
12 | margin: 'auto',
13 | marginTop: 40,
14 | marginBottom: 40
15 | },
16 | separator: {
17 | marginBottom: 36
18 | }
19 | }))
20 |
21 | export default function Reports() {
22 | const classes = useStyles()
23 | return (
24 |
25 |
26 |
27 |
28 |
29 |
30 |
31 | )
32 | }
--------------------------------------------------------------------------------
/Chapter10/mern-expense-tracker/client/theme.js:
--------------------------------------------------------------------------------
1 | import { createMuiTheme } from '@material-ui/core/styles'
2 |
3 | const theme = createMuiTheme({
4 | typography: {
5 | useNextVariants: true,
6 | },
7 | palette: {
8 | primary: {
9 | light: '#4f83cc',
10 | main: '#01579b',
11 | dark: '#002f6c',
12 | contrastText: '#fff',
13 | },
14 | secondary: {
15 | light: '#9fffe0',
16 | main: '#69f0ae',
17 | dark: '#2bbd7e',
18 | contrastText: '#000',
19 | },
20 | openTitle: '#002f6c',
21 | protectedTitle: '#2bbd7e',
22 | type: 'light'
23 | }
24 | })
25 |
26 | export default theme
--------------------------------------------------------------------------------
/Chapter10/mern-expense-tracker/config/config.js:
--------------------------------------------------------------------------------
1 | const config = {
2 | env: process.env.NODE_ENV || 'development',
3 | port: process.env.PORT || 3000,
4 | jwtSecret: process.env.JWT_SECRET || "YOUR_secret_key",
5 | mongoUri: process.env.MONGODB_URI ||
6 | process.env.MONGO_HOST ||
7 | 'mongodb://' + (process.env.IP || 'localhost') + ':' +
8 | (process.env.MONGO_PORT || '27017') +
9 | '/mernproject'
10 | }
11 |
12 | export default config
13 |
--------------------------------------------------------------------------------
/Chapter10/mern-expense-tracker/nodemon.json:
--------------------------------------------------------------------------------
1 | {
2 | "verbose": false,
3 | "watch": [
4 | "./server"
5 | ],
6 | "exec": "webpack --mode=development --config webpack.config.server.js && node ./dist/server.generated.js"
7 | }
8 |
--------------------------------------------------------------------------------
/Chapter10/mern-expense-tracker/server/devBundle.js:
--------------------------------------------------------------------------------
1 | import config from './../config/config'
2 | import webpack from 'webpack'
3 | import webpackMiddleware from 'webpack-dev-middleware'
4 | import webpackHotMiddleware from 'webpack-hot-middleware'
5 | import webpackConfig from './../webpack.config.client.js'
6 |
7 | const compile = (app) => {
8 | if(config.env === "development"){
9 | const compiler = webpack(webpackConfig)
10 | const middleware = webpackMiddleware(compiler, {
11 | publicPath: webpackConfig.output.publicPath
12 | })
13 | app.use(middleware)
14 | app.use(webpackHotMiddleware(compiler))
15 | }
16 | }
17 |
18 | export default {
19 | compile
20 | }
21 |
--------------------------------------------------------------------------------
/Chapter10/mern-expense-tracker/server/helpers/dbErrorHandler.js:
--------------------------------------------------------------------------------
1 | 'use strict'
2 |
3 | /**
4 | * Get unique error field name
5 | */
6 | const getUniqueErrorMessage = (err) => {
7 | let output
8 | try {
9 | let fieldName = err.message.substring(err.message.lastIndexOf('.$') + 2, err.message.lastIndexOf('_1'))
10 | output = fieldName.charAt(0).toUpperCase() + fieldName.slice(1) + ' already exists'
11 | } catch (ex) {
12 | output = 'Unique field already exists'
13 | }
14 |
15 | return output
16 | }
17 |
18 | /**
19 | * Get the error message from error object
20 | */
21 | const getErrorMessage = (err) => {
22 | let message = ''
23 |
24 | if (err.code) {
25 | switch (err.code) {
26 | case 11000:
27 | case 11001:
28 | message = getUniqueErrorMessage(err)
29 | break
30 | default:
31 | message = 'Something went wrong'
32 | }
33 | } else {
34 | for (let errName in err.errors) {
35 | if (err.errors[errName].message) message = err.errors[errName].message
36 | }
37 | }
38 |
39 | return message
40 | }
41 |
42 | export default {getErrorMessage}
43 |
--------------------------------------------------------------------------------
/Chapter10/mern-expense-tracker/server/models/expense.model.js:
--------------------------------------------------------------------------------
1 | import mongoose from 'mongoose'
2 | const ExpenseSchema = new mongoose.Schema({
3 | title: {
4 | type: String,
5 | trim: true,
6 | required: 'Title is required'
7 | },
8 | category: {
9 | type: String,
10 | trim: true,
11 | required: 'Category is required'
12 | },
13 | amount: {
14 | type: Number,
15 | min: 0,
16 | required: 'Amount is required'
17 | },
18 | incurred_on: {
19 | type: Date,
20 | default: Date.now
21 | },
22 | notes: {
23 | type: String,
24 | trim: true
25 | },
26 | updated: Date,
27 | created: {
28 | type: Date,
29 | default: Date.now
30 | },
31 | recorded_by: {type: mongoose.Schema.ObjectId, ref: 'User'}
32 | })
33 |
34 | export default mongoose.model('Expense', ExpenseSchema)
35 |
--------------------------------------------------------------------------------
/Chapter10/mern-expense-tracker/server/routes/auth.routes.js:
--------------------------------------------------------------------------------
1 | import express from 'express'
2 | import authCtrl from '../controllers/auth.controller'
3 |
4 | const router = express.Router()
5 |
6 | router.route('/auth/signin')
7 | .post(authCtrl.signin)
8 | router.route('/auth/signout')
9 | .get(authCtrl.signout)
10 |
11 | export default router
12 |
--------------------------------------------------------------------------------
/Chapter10/mern-expense-tracker/server/routes/expense.routes.js:
--------------------------------------------------------------------------------
1 | import express from 'express'
2 | import expenseCtrl from '../controllers/expense.controller'
3 | import authCtrl from '../controllers/auth.controller'
4 |
5 | const router = express.Router()
6 |
7 | router.route('/api/expenses/current/preview')
8 | .get(authCtrl.requireSignin, expenseCtrl.currentMonthPreview)
9 |
10 | router.route('/api/expenses/by/category')
11 | .get(authCtrl.requireSignin, expenseCtrl.expenseByCategory)
12 |
13 | router.route('/api/expenses/plot')
14 | .get(authCtrl.requireSignin, expenseCtrl.plotExpenses)
15 |
16 | router.route('/api/expenses/category/averages')
17 | .get(authCtrl.requireSignin, expenseCtrl.averageCategories)
18 |
19 | router.route('/api/expenses/yearly')
20 | .get(authCtrl.requireSignin, expenseCtrl.yearlyExpenses)
21 |
22 | router.route('/api/expenses')
23 | .post(authCtrl.requireSignin, expenseCtrl.create)
24 | .get(authCtrl.requireSignin, expenseCtrl.listByUser)
25 |
26 | router.route('/api/expenses/:expenseId')
27 | // .get(authCtrl.requireSignin, expenseCtrl.read)
28 | .put(authCtrl.requireSignin, expenseCtrl.hasAuthorization, expenseCtrl.update)
29 | .delete(authCtrl.requireSignin, expenseCtrl.hasAuthorization, expenseCtrl.remove)
30 |
31 | router.param('expenseId', expenseCtrl.expenseByID)
32 |
33 | export default router
34 |
--------------------------------------------------------------------------------
/Chapter10/mern-expense-tracker/server/routes/user.routes.js:
--------------------------------------------------------------------------------
1 | import express from 'express'
2 | import userCtrl from '../controllers/user.controller'
3 | import authCtrl from '../controllers/auth.controller'
4 |
5 | const router = express.Router()
6 |
7 | router.route('/api/users')
8 | .get(userCtrl.list)
9 | .post(userCtrl.create)
10 |
11 | router.route('/api/users/:userId')
12 | .get(authCtrl.requireSignin, userCtrl.read)
13 | .put(authCtrl.requireSignin, authCtrl.hasAuthorization, userCtrl.update)
14 | .delete(authCtrl.requireSignin, authCtrl.hasAuthorization, userCtrl.remove)
15 |
16 | router.param('userId', userCtrl.userByID)
17 |
18 | export default router
19 |
--------------------------------------------------------------------------------
/Chapter10/mern-expense-tracker/server/server.js:
--------------------------------------------------------------------------------
1 | import config from './../config/config'
2 | import app from './express'
3 | import mongoose from 'mongoose'
4 |
5 | // Connection URL
6 | mongoose.Promise = global.Promise
7 | mongoose.connect(config.mongoUri, { useNewUrlParser: true, useCreateIndex: true, useUnifiedTopology: true })
8 | mongoose.connection.on('error', () => {
9 | throw new Error(`unable to connect to database: ${config.mongoUri}`)
10 | })
11 |
12 | app.listen(config.port, (err) => {
13 | if (err) {
14 | console.log(err)
15 | }
16 | console.info('Server started on port %s.', config.port)
17 | })
18 |
--------------------------------------------------------------------------------
/Chapter10/mern-expense-tracker/template.js:
--------------------------------------------------------------------------------
1 | export default ({markup, css}) => {
2 | return `
3 |
4 |
5 |
6 |
10 | MERN Expense Tracker
11 |
12 |
13 |
19 |
20 |
21 | ${markup}
22 |
23 |
24 |
25 | `
26 | }
27 |
--------------------------------------------------------------------------------
/Chapter10/mern-expense-tracker/webpack.config.client.js:
--------------------------------------------------------------------------------
1 | const path = require('path')
2 | const webpack = require('webpack')
3 | const CURRENT_WORKING_DIR = process.cwd()
4 |
5 | const config = {
6 | name: "browser",
7 | mode: "development",
8 | devtool: 'eval-source-map',
9 | entry: [
10 | 'webpack-hot-middleware/client?reload=true',
11 | path.join(CURRENT_WORKING_DIR, 'client/main.js')
12 | ],
13 | output: {
14 | path: path.join(CURRENT_WORKING_DIR , '/dist'),
15 | filename: 'bundle.js',
16 | publicPath: '/dist/'
17 | },
18 | module: {
19 | rules: [
20 | {
21 | test: /\.jsx?$/,
22 | exclude: /node_modules/,
23 | use: [
24 | 'babel-loader'
25 | ]
26 | },
27 | {
28 | test: /\.(ttf|eot|svg|gif|jpg|png)(\?[\s\S]+)?$/,
29 | use: 'file-loader'
30 | }
31 | ]
32 | },
33 | plugins: [
34 | new webpack.HotModuleReplacementPlugin(),
35 | new webpack.NoEmitOnErrorsPlugin()
36 | ],
37 | resolve: {
38 | alias: {
39 | 'react-dom': '@hot-loader/react-dom'
40 | }
41 | }
42 | }
43 |
44 | module.exports = config
45 |
--------------------------------------------------------------------------------
/Chapter10/mern-expense-tracker/webpack.config.client.production.js:
--------------------------------------------------------------------------------
1 | const path = require('path')
2 | const CURRENT_WORKING_DIR = process.cwd()
3 |
4 | const config = {
5 | mode: "production",
6 | entry: [
7 | path.join(CURRENT_WORKING_DIR, 'client/main.js')
8 | ],
9 | output: {
10 | path: path.join(CURRENT_WORKING_DIR , '/dist'),
11 | filename: 'bundle.js',
12 | publicPath: "/dist/"
13 | },
14 | module: {
15 | rules: [
16 | {
17 | test: /\.jsx?$/,
18 | exclude: /node_modules/,
19 | use: [
20 | 'babel-loader'
21 | ]
22 | },
23 | {
24 | test: /\.(ttf|eot|svg|gif|jpg|png)(\?[\s\S]+)?$/,
25 | use: 'file-loader'
26 | }
27 | ]
28 | }
29 | }
30 |
31 | module.exports = config
32 |
--------------------------------------------------------------------------------
/Chapter10/mern-expense-tracker/webpack.config.server.js:
--------------------------------------------------------------------------------
1 | const path = require('path')
2 | const nodeExternals = require('webpack-node-externals')
3 | const CURRENT_WORKING_DIR = process.cwd()
4 |
5 | const config = {
6 | name: "server",
7 | entry: [ path.join(CURRENT_WORKING_DIR , './server/server.js') ],
8 | target: "node",
9 | output: {
10 | path: path.join(CURRENT_WORKING_DIR , '/dist/'),
11 | filename: "server.generated.js",
12 | publicPath: '/dist/',
13 | libraryTarget: "commonjs2"
14 | },
15 | externals: [nodeExternals()],
16 | module: {
17 | rules: [
18 | {
19 | test: /\.js$/,
20 | exclude: /node_modules/,
21 | use: [ 'babel-loader' ]
22 | },
23 | {
24 | test: /\.(ttf|eot|svg|gif|jpg|png)(\?[\s\S]+)?$/,
25 | use: 'file-loader'
26 | }
27 | ]
28 | }
29 | }
30 |
31 | module.exports = config
32 |
--------------------------------------------------------------------------------
/Chapter11 and 12/mern-mediastream/.babelrc:
--------------------------------------------------------------------------------
1 | {
2 | "presets": [
3 | ["@babel/preset-env",
4 | {
5 | "targets": {
6 | "node": "current"
7 | }
8 | }
9 | ],
10 | "@babel/preset-react"
11 | ],
12 | "plugins": [
13 | "react-hot-loader/babel"
14 | ]
15 | }
16 |
--------------------------------------------------------------------------------
/Chapter11 and 12/mern-mediastream/.gitignore:
--------------------------------------------------------------------------------
1 | /node_modules/
2 | /dist/
3 | /data/
4 | npm-debug.log
5 |
--------------------------------------------------------------------------------
/Chapter11 and 12/mern-mediastream/LICENSE.md:
--------------------------------------------------------------------------------
1 | MIT License
2 |
3 | Copyright (c) 2018 Shama Hoque
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 |
--------------------------------------------------------------------------------
/Chapter11 and 12/mern-mediastream/README.md:
--------------------------------------------------------------------------------
1 | # MERN Mediastream
2 |
3 | A media streaming application with media upload and stream features - developed using React, Node, Express and MongoDB.
4 |
5 | 
6 |
7 | ### [Live Demo](http://mediastream2.mernbook.com/ "MERN Mediastream")
8 |
9 | #### What you need to run this code
10 | 1. Node (13.12.0)
11 | 2. NPM (6.14.4) or Yarn (1.22.4)
12 | 3. MongoDB (4.2.0)
13 |
14 | #### How to run this code
15 | 1. Clone this repository
16 | 2. Open command line in the cloned folder,
17 | - To install dependencies, run ``` npm install ``` or ``` yarn ```
18 | - To run the application for development, run ``` npm run development ``` or ``` yarn development ```
19 | 4. Open [localhost:3000](http://localhost:3000/) in the browser
20 | ----
21 |
--------------------------------------------------------------------------------
/Chapter11 and 12/mern-mediastream/client/App.js:
--------------------------------------------------------------------------------
1 | import React from 'react'
2 | import MainRouter from './MainRouter'
3 | import {BrowserRouter} from 'react-router-dom'
4 | import { ThemeProvider } from '@material-ui/styles'
5 | import theme from './theme'
6 | import { hot } from 'react-hot-loader'
7 |
8 | const App = () => {
9 | React.useEffect(() => {
10 | const jssStyles = document.querySelector('#jss-server-side')
11 | if (jssStyles) {
12 | jssStyles.parentNode.removeChild(jssStyles)
13 | }
14 | }, [])
15 | return (
16 |
17 |
18 |
19 |
20 |
21 | )}
22 |
23 | export default hot(module)(App)
24 |
--------------------------------------------------------------------------------
/Chapter11 and 12/mern-mediastream/client/MainRouter.js:
--------------------------------------------------------------------------------
1 | import React, {Component} from 'react'
2 | import {Route, Switch} from 'react-router-dom'
3 | import Home from './core/Home'
4 | import Users from './user/Users'
5 | import Signup from './user/Signup'
6 | import Signin from './auth/Signin'
7 | import EditProfile from './user/EditProfile'
8 | import Profile from './user/Profile'
9 | import PrivateRoute from './auth/PrivateRoute'
10 | import Menu from './core/Menu'
11 | import NewMedia from './media/NewMedia'
12 | import PlayMedia from './media/PlayMedia'
13 | import EditMedia from './media/EditMedia'
14 |
15 | const MainRouter = ({data}) => {
16 | return (
17 |
18 |
19 |
20 |
21 |
22 |
23 |
24 |
25 |
26 |
27 |
28 | (
29 |
30 | )} />
31 |
32 |
)
33 | }
34 |
35 | export default MainRouter
36 |
--------------------------------------------------------------------------------
/Chapter11 and 12/mern-mediastream/client/assets/images/unicornbike.jpg:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/PacktPublishing/Full-Stack-React-Projects-Second-Edition/c4007202bd767682b9122cb196b80cd4952658f2/Chapter11 and 12/mern-mediastream/client/assets/images/unicornbike.jpg
--------------------------------------------------------------------------------
/Chapter11 and 12/mern-mediastream/client/auth/PrivateRoute.js:
--------------------------------------------------------------------------------
1 | import React, { Component } from 'react'
2 | import { Route, Redirect } from 'react-router-dom'
3 | import auth from './auth-helper'
4 |
5 | const PrivateRoute = ({ component: Component, ...rest }) => (
6 | (
7 | auth.isAuthenticated() ? (
8 |
9 | ) : (
10 |
14 | )
15 | )}/>
16 | )
17 |
18 | export default PrivateRoute
19 |
--------------------------------------------------------------------------------
/Chapter11 and 12/mern-mediastream/client/auth/api-auth.js:
--------------------------------------------------------------------------------
1 | const signin = async (user) => {
2 | try {
3 | let response = await fetch('/auth/signin/', {
4 | method: 'POST',
5 | headers: {
6 | 'Accept': 'application/json',
7 | 'Content-Type': 'application/json'
8 | },
9 | credentials: 'include',
10 | body: JSON.stringify(user)
11 | })
12 | return await response.json()
13 | } catch(err) {
14 | console.log(err)
15 | }
16 | }
17 |
18 | const signout = async () => {
19 | try {
20 | let response = await fetch('/auth/signout/', { method: 'GET' })
21 | return await response.json()
22 | } catch(err) {
23 | console.log(err)
24 | }
25 | }
26 |
27 | export {
28 | signin,
29 | signout
30 | }
31 |
--------------------------------------------------------------------------------
/Chapter11 and 12/mern-mediastream/client/auth/auth-helper.js:
--------------------------------------------------------------------------------
1 | import { signout } from './api-auth.js'
2 |
3 | const auth = {
4 | isAuthenticated() {
5 | if (typeof window == "undefined")
6 | return false
7 |
8 | if (sessionStorage.getItem('jwt'))
9 | return JSON.parse(sessionStorage.getItem('jwt'))
10 | else
11 | return false
12 | },
13 | authenticate(jwt, cb) {
14 | if (typeof window !== "undefined")
15 | sessionStorage.setItem('jwt', JSON.stringify(jwt))
16 | cb()
17 | },
18 | clearJWT(cb) {
19 | if (typeof window !== "undefined")
20 | sessionStorage.removeItem('jwt')
21 | cb()
22 | //optional
23 | signout().then((data) => {
24 | document.cookie = "t=; expires=Thu, 01 Jan 1970 00:00:00 UTC; path=/;"
25 | })
26 | }
27 | }
28 |
29 | export default auth
--------------------------------------------------------------------------------
/Chapter11 and 12/mern-mediastream/client/core/Home.js:
--------------------------------------------------------------------------------
1 | import React, {useState, useEffect} from 'react'
2 | import { makeStyles } from '@material-ui/core/styles'
3 | import Card from '@material-ui/core/Card'
4 | import Typography from '@material-ui/core/Typography'
5 | import MediaList from '../media/MediaList'
6 | import {listPopular} from '../media/api-media.js'
7 |
8 | const useStyles = makeStyles(theme => ({
9 | card: {
10 | margin: `${theme.spacing(5)}px 30px`
11 | },
12 | title: {
13 | padding:`${theme.spacing(3)}px ${theme.spacing(2.5)}px 0px`,
14 | color: theme.palette.text.secondary,
15 | fontSize: '1em'
16 | },
17 | media: {
18 | minHeight: 330
19 | }
20 | }))
21 |
22 | export default function Home(){
23 | const classes = useStyles()
24 | const [media, setMedia] = useState([])
25 |
26 | useEffect(() => {
27 | const abortController = new AbortController()
28 | const signal = abortController.signal
29 | listPopular(signal).then((data) => {
30 | if (data.error) {
31 | console.log(data.error)
32 | } else {
33 | setMedia(data)
34 | }
35 | })
36 | return function cleanup(){
37 | abortController.abort()
38 | }
39 | }, [])
40 | return (
41 |
42 |
43 | Popular Videos
44 |
45 |
46 |
47 | )
48 | }
49 |
--------------------------------------------------------------------------------
/Chapter11 and 12/mern-mediastream/client/main.js:
--------------------------------------------------------------------------------
1 | import React from 'react'
2 | import { hydrate } from 'react-dom'
3 | import App from './App'
4 |
5 | hydrate(, document.getElementById('root'))
6 |
--------------------------------------------------------------------------------
/Chapter11 and 12/mern-mediastream/client/routeConfig.js:
--------------------------------------------------------------------------------
1 | import PlayMedia from './media/PlayMedia'
2 | import { read } from './media/api-media.js'
3 |
4 | const routes = [
5 | {
6 | path: '/media/:mediaId',
7 | component: PlayMedia,
8 | loadData: (params) => read(params)
9 | }
10 |
11 | ]
12 | export default routes
13 |
--------------------------------------------------------------------------------
/Chapter11 and 12/mern-mediastream/client/theme.js:
--------------------------------------------------------------------------------
1 | import { createMuiTheme } from '@material-ui/core/styles'
2 | import { red, brown } from '@material-ui/core/colors'
3 |
4 | const theme = createMuiTheme({
5 | typography: {
6 | useNextVariants: true,
7 | },
8 | palette: {
9 | primary: {
10 | light: '#f05545',
11 | main: '#b71c1c',
12 | dark: '#7f0000',
13 | contrastText: '#fff',
14 | },
15 | secondary: {
16 | light: '#fbfffc',
17 | main: '#c8e6c9',
18 | dark: '#97b498',
19 | contrastText: '#37474f',
20 | },
21 | openTitle: red['500'],
22 | protectedTitle: brown['300'],
23 | type: 'light'
24 | },
25 | })
26 |
27 | export default theme
--------------------------------------------------------------------------------
/Chapter11 and 12/mern-mediastream/config/config.js:
--------------------------------------------------------------------------------
1 | const config = {
2 | env: process.env.NODE_ENV || 'development',
3 | port: process.env.PORT || 3000,
4 | jwtSecret: process.env.JWT_SECRET || "YOUR_secret_key",
5 | mongoUri: process.env.MONGODB_URI ||
6 | process.env.MONGO_HOST ||
7 | 'mongodb://' + (process.env.IP || 'localhost') + ':' +
8 | (process.env.MONGO_PORT || '27017') +
9 | '/mernproject',
10 | serverUrl: process.env.serverUrl || 'http://localhost:3000'
11 | }
12 |
13 | export default config
14 |
--------------------------------------------------------------------------------
/Chapter11 and 12/mern-mediastream/nodemon.json:
--------------------------------------------------------------------------------
1 | {
2 | "verbose": false,
3 | "watch": [
4 | "./server"
5 | ],
6 | "exec": "webpack --mode=development --config webpack.config.server.js && node ./dist/server.generated.js"
7 | }
8 |
--------------------------------------------------------------------------------
/Chapter11 and 12/mern-mediastream/server/devBundle.js:
--------------------------------------------------------------------------------
1 | import config from './../config/config'
2 | import webpack from 'webpack'
3 | import webpackMiddleware from 'webpack-dev-middleware'
4 | import webpackHotMiddleware from 'webpack-hot-middleware'
5 | import webpackConfig from './../webpack.config.client.js'
6 |
7 | const compile = (app) => {
8 | if(config.env === "development"){
9 | const compiler = webpack(webpackConfig)
10 | const middleware = webpackMiddleware(compiler, {
11 | publicPath: webpackConfig.output.publicPath
12 | })
13 | app.use(middleware)
14 | app.use(webpackHotMiddleware(compiler))
15 | }
16 | }
17 |
18 | export default {
19 | compile
20 | }
21 |
--------------------------------------------------------------------------------
/Chapter11 and 12/mern-mediastream/server/helpers/dbErrorHandler.js:
--------------------------------------------------------------------------------
1 | 'use strict'
2 |
3 | /**
4 | * Get unique error field name
5 | */
6 | const getUniqueErrorMessage = (err) => {
7 | let output
8 | try {
9 | let fieldName = err.message.substring(err.message.lastIndexOf('.$') + 2, err.message.lastIndexOf('_1'))
10 | output = fieldName.charAt(0).toUpperCase() + fieldName.slice(1) + ' already exists'
11 | } catch (ex) {
12 | output = 'Unique field already exists'
13 | }
14 |
15 | return output
16 | }
17 |
18 | /**
19 | * Get the error message from error object
20 | */
21 | const getErrorMessage = (err) => {
22 | let message = ''
23 |
24 | if (err.code) {
25 | switch (err.code) {
26 | case 11000:
27 | case 11001:
28 | message = getUniqueErrorMessage(err)
29 | break
30 | default:
31 | message = 'Something went wrong'
32 | }
33 | } else {
34 | for (let errName in err.errors) {
35 | if (err.errors[errName].message) message = err.errors[errName].message
36 | }
37 | }
38 |
39 | return message
40 | }
41 |
42 | export default {getErrorMessage}
43 |
--------------------------------------------------------------------------------
/Chapter11 and 12/mern-mediastream/server/models/media.model.js:
--------------------------------------------------------------------------------
1 | import mongoose from 'mongoose'
2 | const MediaSchema = new mongoose.Schema({
3 | title: {
4 | type: String,
5 | required: 'title is required'
6 | },
7 | description: String,
8 | genre: String,
9 | views: {type: Number, default: 0},
10 | postedBy: {type: mongoose.Schema.ObjectId, ref: 'User'},
11 | created: {
12 | type: Date,
13 | default: Date.now
14 | },
15 | updated: {
16 | type: Date
17 | }
18 | })
19 |
20 | export default mongoose.model('Media', MediaSchema)
21 |
--------------------------------------------------------------------------------
/Chapter11 and 12/mern-mediastream/server/routes/auth.routes.js:
--------------------------------------------------------------------------------
1 | import express from 'express'
2 | import authCtrl from '../controllers/auth.controller'
3 |
4 | const router = express.Router()
5 |
6 | router.route('/auth/signin')
7 | .post(authCtrl.signin)
8 | router.route('/auth/signout')
9 | .get(authCtrl.signout)
10 |
11 | export default router
12 |
--------------------------------------------------------------------------------
/Chapter11 and 12/mern-mediastream/server/routes/media.routes.js:
--------------------------------------------------------------------------------
1 | import express from 'express'
2 | import userCtrl from '../controllers/user.controller'
3 | import authCtrl from '../controllers/auth.controller'
4 | import mediaCtrl from '../controllers/media.controller'
5 |
6 | const router = express.Router()
7 |
8 | router.route('/api/media/new/:userId')
9 | .post(authCtrl.requireSignin, mediaCtrl.create)
10 |
11 | router.route('/api/media/video/:mediaId')
12 | .get(mediaCtrl.video)
13 |
14 | router.route('/api/media/popular')
15 | .get(mediaCtrl.listPopular)
16 |
17 | router.route('/api/media/related/:mediaId')
18 | .get(mediaCtrl.listRelated)
19 |
20 | router.route('/api/media/by/:userId')
21 | .get(mediaCtrl.listByUser)
22 |
23 | router.route('/api/media/:mediaId')
24 | .get( mediaCtrl.incrementViews, mediaCtrl.read)
25 | .put(authCtrl.requireSignin, mediaCtrl.isPoster, mediaCtrl.update)
26 | .delete(authCtrl.requireSignin, mediaCtrl.isPoster, mediaCtrl.remove)
27 |
28 | router.param('userId', userCtrl.userByID)
29 | router.param('mediaId', mediaCtrl.mediaByID)
30 |
31 | export default router
32 |
--------------------------------------------------------------------------------
/Chapter11 and 12/mern-mediastream/server/routes/user.routes.js:
--------------------------------------------------------------------------------
1 | import express from 'express'
2 | import userCtrl from '../controllers/user.controller'
3 | import authCtrl from '../controllers/auth.controller'
4 |
5 | const router = express.Router()
6 |
7 | router.route('/api/users')
8 | .get(userCtrl.list)
9 | .post(userCtrl.create)
10 |
11 | router.route('/api/users/:userId')
12 | .get(authCtrl.requireSignin, userCtrl.read)
13 | .put(authCtrl.requireSignin, authCtrl.hasAuthorization, userCtrl.update)
14 | .delete(authCtrl.requireSignin, authCtrl.hasAuthorization, userCtrl.remove)
15 |
16 | router.param('userId', userCtrl.userByID)
17 |
18 | export default router
19 |
--------------------------------------------------------------------------------
/Chapter11 and 12/mern-mediastream/server/server.js:
--------------------------------------------------------------------------------
1 | import config from './../config/config'
2 | import app from './express'
3 | import mongoose from 'mongoose'
4 |
5 | // Connection URL
6 | mongoose.Promise = global.Promise
7 | mongoose.connect(config.mongoUri, { useNewUrlParser: true, useCreateIndex: true, useUnifiedTopology: true, useFindAndModify: false })
8 | mongoose.connection.on('error', () => {
9 | throw new Error(`unable to connect to database: ${config.mongoUri}`)
10 | })
11 |
12 | app.listen(config.port, (err) => {
13 | if (err) {
14 | console.log(err)
15 | }
16 | console.info('Server started on port %s.', config.port)
17 | })
18 |
--------------------------------------------------------------------------------
/Chapter11 and 12/mern-mediastream/template.js:
--------------------------------------------------------------------------------
1 | export default ({markup, css}) => {
2 | return `
3 |
4 |
5 |
6 | MERN Mediastream
7 |
8 |
9 |
14 |
15 |
16 | ${markup}
17 |
18 |
19 |
20 | `
21 | }
22 |
--------------------------------------------------------------------------------
/Chapter11 and 12/mern-mediastream/webpack.config.client.js:
--------------------------------------------------------------------------------
1 | const path = require('path')
2 | const webpack = require('webpack')
3 | const CURRENT_WORKING_DIR = process.cwd()
4 |
5 | const config = {
6 | name: "browser",
7 | mode: "development",
8 | devtool: 'eval-source-map',
9 | entry: [
10 | 'react-hot-loader/patch',
11 | 'webpack-hot-middleware/client?reload=true',
12 | path.join(CURRENT_WORKING_DIR, 'client/main.js')
13 | ],
14 | output: {
15 | path: path.join(CURRENT_WORKING_DIR , '/dist'),
16 | filename: 'bundle.js',
17 | publicPath: '/dist/'
18 | },
19 | module: {
20 | rules: [
21 | {
22 | test: /\.jsx?$/,
23 | exclude: /node_modules/,
24 | use: [
25 | 'babel-loader'
26 | ]
27 | },
28 | {
29 | test: /\.(ttf|eot|svg|gif|jpg|png)(\?[\s\S]+)?$/,
30 | use: 'file-loader'
31 | }
32 | ]
33 | }, plugins: [
34 | new webpack.HotModuleReplacementPlugin(),
35 | new webpack.NoEmitOnErrorsPlugin()
36 | ]
37 | }
38 |
39 | module.exports = config
40 |
--------------------------------------------------------------------------------
/Chapter11 and 12/mern-mediastream/webpack.config.client.production.js:
--------------------------------------------------------------------------------
1 | const path = require('path')
2 | const CURRENT_WORKING_DIR = process.cwd()
3 |
4 | const config = {
5 | mode: "production",
6 | entry: [
7 | path.join(CURRENT_WORKING_DIR, 'client/main.js')
8 | ],
9 | output: {
10 | path: path.join(CURRENT_WORKING_DIR , '/dist'),
11 | filename: 'bundle.js',
12 | publicPath: "/dist/"
13 | },
14 | module: {
15 | rules: [
16 | {
17 | test: /\.jsx?$/,
18 | exclude: /node_modules/,
19 | use: [
20 | 'babel-loader'
21 | ]
22 | },
23 | {
24 | test: /\.(ttf|eot|svg|gif|jpg|png)(\?[\s\S]+)?$/,
25 | use: 'file-loader'
26 | }
27 | ]
28 | }
29 | }
30 |
31 | module.exports = config
32 |
--------------------------------------------------------------------------------
/Chapter11 and 12/mern-mediastream/webpack.config.server.js:
--------------------------------------------------------------------------------
1 | const path = require('path')
2 | const nodeExternals = require('webpack-node-externals')
3 | const CURRENT_WORKING_DIR = process.cwd()
4 |
5 | const config = {
6 | name: "server",
7 | entry: [ path.join(CURRENT_WORKING_DIR , './server/server.js') ],
8 | target: "node",
9 | output: {
10 | path: path.join(CURRENT_WORKING_DIR , '/dist/'),
11 | filename: "server.generated.js",
12 | publicPath: '/dist/',
13 | libraryTarget: "commonjs2"
14 | },
15 | externals: [nodeExternals()],
16 | module: {
17 | rules: [
18 | {
19 | test: /\.js$/,
20 | exclude: /node_modules/,
21 | use: [ 'babel-loader' ]
22 | },
23 | {
24 | test: /\.(ttf|eot|svg|gif|jpg|png)(\?[\s\S]+)?$/,
25 | use: 'file-loader'
26 | }
27 | ]
28 | }
29 | }
30 |
31 | module.exports = config
32 |
--------------------------------------------------------------------------------
/Chapter13/MERNVR/.babelrc:
--------------------------------------------------------------------------------
1 | {
2 | "presets": ["react-native"]
3 | }
4 |
--------------------------------------------------------------------------------
/Chapter13/MERNVR/.flowconfig:
--------------------------------------------------------------------------------
1 | [ignore]
2 | .*/node_modules/.*
3 |
4 | [include]
5 |
6 | [libs]
7 |
8 | [options]
9 | emoji=true
10 | module.system=haste
11 |
--------------------------------------------------------------------------------
/Chapter13/MERNVR/.gitignore:
--------------------------------------------------------------------------------
1 | .DS_Store
2 | *~
3 | *.log
4 | *.js.meta
5 | node_modules/
6 | build/
7 |
--------------------------------------------------------------------------------
/Chapter13/MERNVR/.watchmanconfig:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/PacktPublishing/Full-Stack-React-Projects-Second-Edition/c4007202bd767682b9122cb196b80cd4952658f2/Chapter13/MERNVR/.watchmanconfig
--------------------------------------------------------------------------------
/Chapter13/MERNVR/README.md:
--------------------------------------------------------------------------------
1 | # MERNVR (React 360)
2 |
3 | A web-based VR game developed using React 360.
4 |
5 | 
6 |
7 | #### What you need to run this code
8 | 1. Node (13.7.0)
9 |
10 | #### How to run this code
11 | 1. Clone this repository
12 | 2. Open command line in the cloned folder,
13 | - To install dependencies, run ``` yarn ```
14 | - To run the application for development, run ``` yarn start ```
15 | 3. Open [localhost:8081/index.html](http://localhost:8081/index.html) in the browser
16 |
17 | ### Integration with a MERN stack web application
18 | Check out [MERN VR Game](https://github.com/shamahoque/mern-vrgame) - A dynamic version of this VR game where game details are retrieved from the server.
19 |
20 | ----
--------------------------------------------------------------------------------
/Chapter13/MERNVR/__tests__/index-test.js:
--------------------------------------------------------------------------------
1 | import 'react-native';
2 | import 'react-360';
3 | import React from 'react';
4 | import Index from '../index.js';
5 |
6 | // Note: test renderer must be required after react-native.
7 | import renderer from 'react-test-renderer';
8 |
9 | it('renders correctly', () => {
10 | const tree = renderer.create(
11 |
12 | );
13 | });
14 |
--------------------------------------------------------------------------------
/Chapter13/MERNVR/client.js:
--------------------------------------------------------------------------------
1 | // This file contains the boilerplate to execute your React app.
2 | // If you want to modify your application's content, start in "index.js"
3 |
4 | import {ReactInstance} from 'react-360-web';
5 |
6 | function init(bundle, parent, options = {}) {
7 | const r360 = new ReactInstance(bundle, parent, {
8 | // Add custom options here
9 | fullScreen: true,
10 | ...options,
11 | });
12 |
13 | // Render your app content to the default cylinder surface
14 | r360.renderToSurface(
15 | r360.createRoot('MERNVR', { /* initial props */ }),
16 | r360.getDefaultLocation()
17 | );
18 |
19 | // Load the initial environment
20 | r360.compositor.setBackground(r360.getAssetURL('360_world_black.jpg'))
21 | }
22 |
23 | window.React360 = {init};
24 |
--------------------------------------------------------------------------------
/Chapter13/MERNVR/index.html:
--------------------------------------------------------------------------------
1 |
2 |
3 | MERNVR
4 |
5 |
6 |
7 |
8 |
9 |
10 |
11 |
21 |
22 |
23 |
24 |
--------------------------------------------------------------------------------
/Chapter13/MERNVR/package.json:
--------------------------------------------------------------------------------
1 | {
2 | "name": "MERNVR",
3 | "version": "2.0.0",
4 | "private": true,
5 | "scripts": {
6 | "start": "node node_modules/react-360/scripts/packager.js",
7 | "bundle": "node node_modules/react-360/scripts/bundle.js",
8 | "open": "node -e \"require('xopen')('http://localhost:8081/index.html')\"",
9 | "devtools": "react-devtools",
10 | "test": "jest"
11 | },
12 | "dependencies": {
13 | "react": "16.3.2",
14 | "react-native": "~0.55.4",
15 | "three": "^0.87.0",
16 | "react-360": "~1.1.0",
17 | "react-360-web": "~1.1.0"
18 | },
19 | "devDependencies": {
20 | "babel-jest": "^19.0.0",
21 | "babel-preset-react-native": "^1.9.1",
22 | "jest": "^19.0.2",
23 | "react-devtools": "^2.5.2",
24 | "react-test-renderer": "16.0.0",
25 | "xopen": "1.0.0"
26 | },
27 | "jest": {
28 | "preset": "react-360"
29 | }
30 | }
--------------------------------------------------------------------------------
/Chapter13/MERNVR/rn-cli.config.js:
--------------------------------------------------------------------------------
1 | 'use strict';
2 |
3 | var path = require('path');
4 | var blacklist = require('metro/src/blacklist');
5 |
6 | var config = {
7 | getProjectRoots() {
8 | return getRoots();
9 | },
10 |
11 | getBlacklistRE() {
12 | return blacklist([
13 | ]);
14 | },
15 |
16 | getAssetExts() {
17 | return ['obj', 'mtl'];
18 | },
19 |
20 | getPlatforms() {
21 | return ['vr'];
22 | },
23 |
24 | getProvidesModuleNodeModules() {
25 | return ['react-native', 'react-360'];
26 | },
27 | };
28 |
29 | function getRoots() {
30 | var root = process.env.REACT_NATIVE_APP_ROOT;
31 | if (root) {
32 | return [path.resolve(root)];
33 | }
34 | return [path.resolve(__dirname)];
35 | }
36 |
37 | module.exports = config;
--------------------------------------------------------------------------------
/Chapter13/MERNVR/static_assets/360_world.jpg:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/PacktPublishing/Full-Stack-React-Projects-Second-Edition/c4007202bd767682b9122cb196b80cd4952658f2/Chapter13/MERNVR/static_assets/360_world.jpg
--------------------------------------------------------------------------------
/Chapter13/MERNVR/static_assets/360_world_black.jpg:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/PacktPublishing/Full-Stack-React-Projects-Second-Edition/c4007202bd767682b9122cb196b80cd4952658f2/Chapter13/MERNVR/static_assets/360_world_black.jpg
--------------------------------------------------------------------------------
/Chapter13/MERNVR/static_assets/clog-up.mp3:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/PacktPublishing/Full-Stack-React-Projects-Second-Edition/c4007202bd767682b9122cb196b80cd4952658f2/Chapter13/MERNVR/static_assets/clog-up.mp3
--------------------------------------------------------------------------------
/Chapter13/MERNVR/static_assets/collect.mp3:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/PacktPublishing/Full-Stack-React-Projects-Second-Edition/c4007202bd767682b9122cb196b80cd4952658f2/Chapter13/MERNVR/static_assets/collect.mp3
--------------------------------------------------------------------------------
/Chapter13/MERNVR/static_assets/happy-bot.mp3:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/PacktPublishing/Full-Stack-React-Projects-Second-Edition/c4007202bd767682b9122cb196b80cd4952658f2/Chapter13/MERNVR/static_assets/happy-bot.mp3
--------------------------------------------------------------------------------
/Chapter14/mern-vrgame/.babelrc:
--------------------------------------------------------------------------------
1 | {
2 | "presets": [
3 | ["@babel/preset-env",
4 | {
5 | "targets": {
6 | "node": "current"
7 | }
8 | }
9 | ],
10 | "@babel/preset-react"
11 | ],
12 | "plugins": [
13 | "react-hot-loader/babel"
14 | ]
15 | }
16 |
--------------------------------------------------------------------------------
/Chapter14/mern-vrgame/.gitignore:
--------------------------------------------------------------------------------
1 | /node_modules/
2 | /dist/bundle.js
3 | /dist/server.generated.js
4 | /data/
5 | npm-debug.log
6 |
--------------------------------------------------------------------------------
/Chapter14/mern-vrgame/LICENSE.md:
--------------------------------------------------------------------------------
1 | MIT License
2 |
3 | Copyright (c) 2018 Shama Hoque
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 |
--------------------------------------------------------------------------------
/Chapter14/mern-vrgame/README.md:
--------------------------------------------------------------------------------
1 | # MERN VR Game
2 |
3 | A web-based game application with VR features - developed using React, React 360, Node, Express and MongoDB.
4 |
5 | 
6 |
7 | ### [Live Demo](http://vrgame2.mernbook.com/ "MERN VR Game")
8 |
9 | #### What you need to run this code
10 | 1. Node (13.12.0)
11 | 2. NPM (6.14.4) or Yarn (1.22.4)
12 | 3. MongoDB (4.2.0)
13 |
14 | #### How to run this code
15 | 1. Make sure MongoDB is running on your system
16 | 2. Clone this repository
17 | 3. Open command line in the cloned folder,
18 | - To install dependencies, run ``` npm install ``` or ``` yarn ```
19 | - To run the application for development, run ``` npm run development ``` or ``` yarn development ```
20 | 4. Open [localhost:3000](http://localhost:3000/) in the browser
21 |
22 | ### React 360
23 | The VR game is developed using React 360 and integrated into this application.
24 |
25 | The React 360 code for the game is [available here](https://github.com/shamahoque/MERNVR).
26 |
27 | ----
--------------------------------------------------------------------------------
/Chapter14/mern-vrgame/client/App.js:
--------------------------------------------------------------------------------
1 | import React from 'react'
2 | import MainRouter from './MainRouter'
3 | import {BrowserRouter} from 'react-router-dom'
4 | import { ThemeProvider } from '@material-ui/styles'
5 | import theme from './theme'
6 | import { hot } from 'react-hot-loader'
7 |
8 | const App = () => {
9 | React.useEffect(() => {
10 | const jssStyles = document.querySelector('#jss-server-side')
11 | if (jssStyles) {
12 | jssStyles.parentNode.removeChild(jssStyles)
13 | }
14 | }, [])
15 | return (
16 |
17 |
18 |
19 |
20 |
21 | )}
22 |
23 | export default hot(module)(App)
24 |
--------------------------------------------------------------------------------
/Chapter14/mern-vrgame/client/MainRouter.js:
--------------------------------------------------------------------------------
1 | import React from 'react'
2 | import {Route, Switch} from 'react-router-dom'
3 | import Home from './core/Home'
4 | import Users from './user/Users'
5 | import Signup from './user/Signup'
6 | import Signin from './auth/Signin'
7 | import EditProfile from './user/EditProfile'
8 | import Profile from './user/Profile'
9 | import PrivateRoute from './auth/PrivateRoute'
10 | import Menu from './core/Menu'
11 | import NewGame from './game/NewGame'
12 | import EditGame from './game/EditGame'
13 |
14 | const MainRouter = () => {
15 | return (
16 |
17 |
18 |
19 |
20 |
21 |
22 |
23 |
24 |
25 |
26 |
27 |
28 |
)
29 | }
30 |
31 | export default MainRouter
--------------------------------------------------------------------------------
/Chapter14/mern-vrgame/client/assets/images/unicornbike.jpg:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/PacktPublishing/Full-Stack-React-Projects-Second-Edition/c4007202bd767682b9122cb196b80cd4952658f2/Chapter14/mern-vrgame/client/assets/images/unicornbike.jpg
--------------------------------------------------------------------------------
/Chapter14/mern-vrgame/client/assets/images/unicorncoin.jpg:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/PacktPublishing/Full-Stack-React-Projects-Second-Edition/c4007202bd767682b9122cb196b80cd4952658f2/Chapter14/mern-vrgame/client/assets/images/unicorncoin.jpg
--------------------------------------------------------------------------------
/Chapter14/mern-vrgame/client/auth/PrivateRoute.js:
--------------------------------------------------------------------------------
1 | import React, { Component } from 'react'
2 | import { Route, Redirect } from 'react-router-dom'
3 | import auth from './auth-helper'
4 |
5 | const PrivateRoute = ({ component: Component, ...rest }) => (
6 | (
7 | auth.isAuthenticated() ? (
8 |
9 | ) : (
10 |
14 | )
15 | )}/>
16 | )
17 |
18 | export default PrivateRoute
19 |
--------------------------------------------------------------------------------
/Chapter14/mern-vrgame/client/auth/api-auth.js:
--------------------------------------------------------------------------------
1 | const signin = async (user) => {
2 | try {
3 | let response = await fetch('/auth/signin/', {
4 | method: 'POST',
5 | headers: {
6 | 'Accept': 'application/json',
7 | 'Content-Type': 'application/json'
8 | },
9 | credentials: 'include',
10 | body: JSON.stringify(user)
11 | })
12 | return await response.json()
13 | } catch(err) {
14 | console.log(err)
15 | }
16 | }
17 |
18 | const signout = async () => {
19 | try {
20 | let response = await fetch('/auth/signout/', { method: 'GET' })
21 | return await response.json()
22 | } catch(err) {
23 | console.log(err)
24 | }
25 | }
26 |
27 | export {
28 | signin,
29 | signout
30 | }
--------------------------------------------------------------------------------
/Chapter14/mern-vrgame/client/auth/auth-helper.js:
--------------------------------------------------------------------------------
1 | import { signout } from './api-auth.js'
2 |
3 | const auth = {
4 | isAuthenticated() {
5 | if (typeof window == "undefined")
6 | return false
7 |
8 | if (sessionStorage.getItem('jwt'))
9 | return JSON.parse(sessionStorage.getItem('jwt'))
10 | else
11 | return false
12 | },
13 | authenticate(jwt, cb) {
14 | if (typeof window !== "undefined")
15 | sessionStorage.setItem('jwt', JSON.stringify(jwt))
16 | cb()
17 | },
18 | clearJWT(cb) {
19 | if (typeof window !== "undefined")
20 | sessionStorage.removeItem('jwt')
21 | cb()
22 | //optional
23 | signout().then((data) => {
24 | document.cookie = "t=; expires=Thu, 01 Jan 1970 00:00:00 UTC; path=/;"
25 | })
26 | }
27 | }
28 |
29 | export default auth
30 |
--------------------------------------------------------------------------------
/Chapter14/mern-vrgame/client/core/Home.js:
--------------------------------------------------------------------------------
1 | import React, {useState, useEffect} from 'react'
2 | import { makeStyles } from '@material-ui/core/styles'
3 | import {list} from '../game/api-game.js'
4 | import GameDetail from '../game/GameDetail'
5 |
6 | const useStyles = makeStyles(theme => ({
7 | root: {
8 | flexGrow: 1,
9 | margin: '10px 24px',
10 | }
11 | }))
12 |
13 | export default function Home(){
14 | const classes = useStyles()
15 | const [games, setGames] = useState([])
16 | useEffect(() => {
17 | const abortController = new AbortController()
18 | const signal = abortController.signal
19 |
20 | list(signal).then((data) => {
21 | if (data.error) {
22 | console.log(data.error)
23 | } else {
24 | setGames(data)
25 | }
26 | })
27 | return function cleanup(){
28 | abortController.abort()
29 | }
30 | }, [])
31 |
32 | const updateGames = (game) => {
33 | const updatedGames = [...games]
34 | const index = updatedGames.indexOf(game)
35 | updatedGames.splice(index, 1)
36 | setGames(updatedGames)
37 | }
38 | return (
39 |
40 | {games.map((game, i) => {
41 | return
42 | })}
43 |
44 | )
45 | }
46 |
--------------------------------------------------------------------------------
/Chapter14/mern-vrgame/client/game/EditGame.js:
--------------------------------------------------------------------------------
1 | import React, {useState} from 'react'
2 | import auth from './../auth/auth-helper'
3 | import {update} from './api-game.js'
4 | import {Redirect} from 'react-router-dom'
5 | import GameForm from './GameForm'
6 |
7 | export default function EditGame({ match }) {
8 | const [redirect, setRedirect] = useState(false)
9 | const [error, setError]= useState('')
10 |
11 | const clickSubmit = game => event => {
12 | const jwt = auth.isAuthenticated()
13 | update({
14 | gameId: match.params.gameId
15 | }, {
16 | t: jwt.token
17 | }, game).then((data) => {
18 | if (data.error) {
19 | setError(data.error)
20 | } else {
21 | setError('')
22 | setRedirect(true)
23 | }
24 | })
25 | }
26 |
27 | if (redirect) {
28 | return ()
29 | }
30 | return (
31 |
32 | )
33 |
34 | }
35 |
36 |
--------------------------------------------------------------------------------
/Chapter14/mern-vrgame/client/game/NewGame.js:
--------------------------------------------------------------------------------
1 | import React, {useState} from 'react'
2 | import auth from './../auth/auth-helper'
3 | import PropTypes from 'prop-types'
4 | import {create} from './api-game.js'
5 | import {Redirect} from 'react-router-dom'
6 | import GameForm from './GameForm'
7 |
8 | export default function NewGame(){
9 | const [redirect, setRedirect] = useState(false)
10 | const [error, setError]= useState('')
11 | const clickSubmit = game => event => {
12 | const jwt = auth.isAuthenticated()
13 | create({
14 | userId: jwt.user._id
15 | }, {
16 | t: jwt.token
17 | }, game).then((data) => {
18 | if (data.error) {
19 | setError(data.error)
20 | } else {
21 | setError('')
22 | setRedirect(true)
23 | }
24 | })
25 | }
26 | if (redirect) {
27 | return ()
28 | }
29 | return (
30 |
31 | )
32 | }
33 |
34 |
--------------------------------------------------------------------------------
/Chapter14/mern-vrgame/client/main.js:
--------------------------------------------------------------------------------
1 | import React from 'react'
2 | import { hydrate } from 'react-dom'
3 | import App from './App'
4 |
5 | hydrate(, document.getElementById('root'))
6 |
--------------------------------------------------------------------------------
/Chapter14/mern-vrgame/client/theme.js:
--------------------------------------------------------------------------------
1 | import { createMuiTheme } from '@material-ui/core/styles'
2 |
3 | const theme = createMuiTheme({
4 | typography: {
5 | useNextVariants: true,
6 | },
7 | palette: {
8 | primary: {
9 | light: '#484848',
10 | main: '#212121',
11 | dark: '#000000',
12 | contrastText: '#fff',
13 | },
14 | secondary: {
15 | light: '#ffff6e',
16 | main: '#cddc39',
17 | dark: '#99aa00',
18 | contrastText: '#000',
19 | },
20 | openTitle: '#484848',
21 | protectedTitle: '#7da453',
22 | type: 'light'
23 | }
24 | })
25 |
26 | export default theme
--------------------------------------------------------------------------------
/Chapter14/mern-vrgame/config/config.js:
--------------------------------------------------------------------------------
1 | const config = {
2 | env: process.env.NODE_ENV || 'development',
3 | port: process.env.PORT || 3000,
4 | jwtSecret: process.env.JWT_SECRET || "YOUR_secret_key",
5 | mongoUri: process.env.MONGODB_URI ||
6 | process.env.MONGO_HOST ||
7 | 'mongodb://' + (process.env.IP || 'localhost') + ':' +
8 | (process.env.MONGO_PORT || '27017') +
9 | '/mernproject'
10 | }
11 |
12 | export default config
13 |
--------------------------------------------------------------------------------
/Chapter14/mern-vrgame/dist/static_assets/360_world.jpg:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/PacktPublishing/Full-Stack-React-Projects-Second-Edition/c4007202bd767682b9122cb196b80cd4952658f2/Chapter14/mern-vrgame/dist/static_assets/360_world.jpg
--------------------------------------------------------------------------------
/Chapter14/mern-vrgame/dist/static_assets/360_world_black.jpg:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/PacktPublishing/Full-Stack-React-Projects-Second-Edition/c4007202bd767682b9122cb196b80cd4952658f2/Chapter14/mern-vrgame/dist/static_assets/360_world_black.jpg
--------------------------------------------------------------------------------
/Chapter14/mern-vrgame/dist/static_assets/clog-up.mp3:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/PacktPublishing/Full-Stack-React-Projects-Second-Edition/c4007202bd767682b9122cb196b80cd4952658f2/Chapter14/mern-vrgame/dist/static_assets/clog-up.mp3
--------------------------------------------------------------------------------
/Chapter14/mern-vrgame/dist/static_assets/collect.mp3:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/PacktPublishing/Full-Stack-React-Projects-Second-Edition/c4007202bd767682b9122cb196b80cd4952658f2/Chapter14/mern-vrgame/dist/static_assets/collect.mp3
--------------------------------------------------------------------------------
/Chapter14/mern-vrgame/dist/static_assets/happy-bot.mp3:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/PacktPublishing/Full-Stack-React-Projects-Second-Edition/c4007202bd767682b9122cb196b80cd4952658f2/Chapter14/mern-vrgame/dist/static_assets/happy-bot.mp3
--------------------------------------------------------------------------------
/Chapter14/mern-vrgame/nodemon.json:
--------------------------------------------------------------------------------
1 | {
2 | "verbose": false,
3 | "watch": [
4 | "./server"
5 | ],
6 | "exec": "webpack --mode=development --config webpack.config.server.js && node ./dist/server.generated.js"
7 | }
8 |
--------------------------------------------------------------------------------
/Chapter14/mern-vrgame/server/devBundle.js:
--------------------------------------------------------------------------------
1 | import config from './../config/config'
2 | import webpack from 'webpack'
3 | import webpackMiddleware from 'webpack-dev-middleware'
4 | import webpackHotMiddleware from 'webpack-hot-middleware'
5 | import webpackConfig from './../webpack.config.client.js'
6 |
7 | const compile = (app) => {
8 | if(config.env === "development"){
9 | const compiler = webpack(webpackConfig)
10 | const middleware = webpackMiddleware(compiler, {
11 | publicPath: webpackConfig.output.publicPath
12 | })
13 | app.use(middleware)
14 | app.use(webpackHotMiddleware(compiler))
15 | }
16 | }
17 |
18 | export default {
19 | compile
20 | }
21 |
--------------------------------------------------------------------------------
/Chapter14/mern-vrgame/server/helpers/dbErrorHandler.js:
--------------------------------------------------------------------------------
1 | 'use strict'
2 |
3 | /**
4 | * Get unique error field name
5 | */
6 | const getUniqueErrorMessage = (err) => {
7 | let output
8 | try {
9 | let fieldName = err.message.substring(err.message.lastIndexOf('.$') + 2, err.message.lastIndexOf('_1'))
10 | output = fieldName.charAt(0).toUpperCase() + fieldName.slice(1) + ' already exists'
11 | } catch (ex) {
12 | output = 'Unique field already exists'
13 | }
14 |
15 | return output
16 | }
17 |
18 | /**
19 | * Get the error message from error object
20 | */
21 | const getErrorMessage = (err) => {
22 | let message = ''
23 |
24 | if (err.code) {
25 | switch (err.code) {
26 | case 11000:
27 | case 11001:
28 | message = getUniqueErrorMessage(err)
29 | break
30 | default:
31 | message = 'Something went wrong'
32 | }
33 | } else {
34 | for (let errName in err.errors) {
35 | if (err.errors[errName].message) message = err.errors[errName].message
36 | }
37 | }
38 |
39 | return message
40 | }
41 |
42 | export default {getErrorMessage}
43 |
--------------------------------------------------------------------------------
/Chapter14/mern-vrgame/server/routes/auth.routes.js:
--------------------------------------------------------------------------------
1 | import express from 'express'
2 | import authCtrl from '../controllers/auth.controller'
3 |
4 | const router = express.Router()
5 |
6 | router.route('/auth/signin')
7 | .post(authCtrl.signin)
8 | router.route('/auth/signout')
9 | .get(authCtrl.signout)
10 |
11 | export default router
12 |
--------------------------------------------------------------------------------
/Chapter14/mern-vrgame/server/routes/game.routes.js:
--------------------------------------------------------------------------------
1 | import express from 'express'
2 | import userCtrl from '../controllers/user.controller'
3 | import authCtrl from '../controllers/auth.controller'
4 | import gameCtrl from '../controllers/game.controller'
5 |
6 | const router = express.Router()
7 |
8 | router.route('/api/games')
9 | .get(gameCtrl.list)
10 |
11 | router.route('/api/games/by/:userId')
12 | .post(authCtrl.requireSignin, authCtrl.hasAuthorization, gameCtrl.create)
13 | .get(gameCtrl.listByMaker)
14 |
15 | router.route('/api/game/:gameId')
16 | .get(gameCtrl.read)
17 |
18 | router.route('/api/games/:gameId')
19 | .put(authCtrl.requireSignin, gameCtrl.isMaker, gameCtrl.update)
20 | .delete(authCtrl.requireSignin, gameCtrl.isMaker, gameCtrl.remove)
21 |
22 | router.route('/game/play')
23 | .get(gameCtrl.playGame)
24 |
25 | router.param('gameId', gameCtrl.gameByID)
26 | router.param('userId', userCtrl.userByID)
27 |
28 | export default router
29 |
--------------------------------------------------------------------------------
/Chapter14/mern-vrgame/server/routes/user.routes.js:
--------------------------------------------------------------------------------
1 | import express from 'express'
2 | import userCtrl from '../controllers/user.controller'
3 | import authCtrl from '../controllers/auth.controller'
4 |
5 | const router = express.Router()
6 |
7 | router.route('/api/users')
8 | .get(userCtrl.list)
9 | .post(userCtrl.create)
10 |
11 | router.route('/api/users/:userId')
12 | .get(authCtrl.requireSignin, userCtrl.read)
13 | .put(authCtrl.requireSignin, authCtrl.hasAuthorization, userCtrl.update)
14 | .delete(authCtrl.requireSignin, authCtrl.hasAuthorization, userCtrl.remove)
15 |
16 | router.param('userId', userCtrl.userByID)
17 |
18 | export default router
19 |
--------------------------------------------------------------------------------
/Chapter14/mern-vrgame/server/server.js:
--------------------------------------------------------------------------------
1 | import config from './../config/config'
2 | import app from './express'
3 | import mongoose from 'mongoose'
4 |
5 | // Connection URL
6 | mongoose.Promise = global.Promise
7 | mongoose.connect(config.mongoUri, { useNewUrlParser: true, useCreateIndex: true, useUnifiedTopology: true })
8 | mongoose.connection.on('error', () => {
9 | throw new Error(`unable to connect to database: ${config.mongoUri}`)
10 | })
11 |
12 | app.listen(config.port, (err) => {
13 | if (err) {
14 | console.log(err)
15 | }
16 | console.info('Server started on port %s.', config.port)
17 | })
18 |
--------------------------------------------------------------------------------
/Chapter14/mern-vrgame/server/vr/index.html:
--------------------------------------------------------------------------------
1 |
2 |
3 | MERNVR
4 |
5 |
6 |
7 |
8 |
9 |
10 |
11 |
21 |
22 |
23 |
--------------------------------------------------------------------------------
/Chapter14/mern-vrgame/template.js:
--------------------------------------------------------------------------------
1 | export default ({markup, css}) => {
2 | return `
3 |
4 |
5 |
6 | MERN VR Game
7 |
8 |
9 |
14 |
15 |
16 | ${markup}
17 |
18 |
19 |
20 | `
21 | }
22 |
--------------------------------------------------------------------------------
/Chapter14/mern-vrgame/webpack.config.client.js:
--------------------------------------------------------------------------------
1 | const path = require('path')
2 | const webpack = require('webpack')
3 | const CURRENT_WORKING_DIR = process.cwd()
4 |
5 | const config = {
6 | name: "browser",
7 | mode: "development",
8 | devtool: 'eval-source-map',
9 | entry: [
10 | 'webpack-hot-middleware/client?reload=true',
11 | path.join(CURRENT_WORKING_DIR, 'client/main.js')
12 | ],
13 | output: {
14 | path: path.join(CURRENT_WORKING_DIR , '/dist'),
15 | filename: 'bundle.js',
16 | publicPath: '/dist/'
17 | },
18 | module: {
19 | rules: [
20 | {
21 | test: /\.jsx?$/,
22 | exclude: /node_modules/,
23 | use: [
24 | 'babel-loader'
25 | ]
26 | },
27 | {
28 | test: /\.(ttf|eot|svg|gif|jpg|png)(\?[\s\S]+)?$/,
29 | use: 'file-loader'
30 | }
31 | ]
32 | },
33 | plugins: [
34 | new webpack.HotModuleReplacementPlugin(),
35 | new webpack.NoEmitOnErrorsPlugin()
36 | ],
37 | resolve: {
38 | alias: {
39 | 'react-dom': '@hot-loader/react-dom'
40 | }
41 | }
42 | }
43 |
44 | module.exports = config
45 |
--------------------------------------------------------------------------------
/Chapter14/mern-vrgame/webpack.config.client.production.js:
--------------------------------------------------------------------------------
1 | const path = require('path')
2 | const CURRENT_WORKING_DIR = process.cwd()
3 |
4 | const config = {
5 | mode: "production",
6 | entry: [
7 | path.join(CURRENT_WORKING_DIR, 'client/main.js')
8 | ],
9 | output: {
10 | path: path.join(CURRENT_WORKING_DIR , '/dist'),
11 | filename: 'bundle.js',
12 | publicPath: "/dist/"
13 | },
14 | module: {
15 | rules: [
16 | {
17 | test: /\.jsx?$/,
18 | exclude: /node_modules/,
19 | use: [
20 | 'babel-loader'
21 | ]
22 | },
23 | {
24 | test: /\.(ttf|eot|svg|gif|jpg|png)(\?[\s\S]+)?$/,
25 | use: 'file-loader'
26 | }
27 | ]
28 | }
29 | }
30 |
31 | module.exports = config
32 |
--------------------------------------------------------------------------------
/Chapter14/mern-vrgame/webpack.config.server.js:
--------------------------------------------------------------------------------
1 | const path = require('path')
2 | const nodeExternals = require('webpack-node-externals')
3 | const CURRENT_WORKING_DIR = process.cwd()
4 |
5 | const config = {
6 | name: "server",
7 | entry: [ path.join(CURRENT_WORKING_DIR , './server/server.js') ],
8 | target: "node",
9 | output: {
10 | path: path.join(CURRENT_WORKING_DIR , '/dist/'),
11 | filename: "server.generated.js",
12 | publicPath: '/dist/',
13 | libraryTarget: "commonjs2"
14 | },
15 | externals: [nodeExternals()],
16 | module: {
17 | rules: [
18 | {
19 | test: /\.js$/,
20 | exclude: /node_modules/,
21 | use: [ 'babel-loader' ]
22 | },
23 | {
24 | test: /\.(ttf|eot|svg|gif|jpg|png)(\?[\s\S]+)?$/,
25 | use: 'file-loader'
26 | }
27 | ]
28 | }
29 | }
30 |
31 | module.exports = config
32 |
--------------------------------------------------------------------------------
/LICENSE:
--------------------------------------------------------------------------------
1 | MIT License
2 |
3 | Copyright (c) 2020 Packt
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 |
--------------------------------------------------------------------------------