├── .eslintrc.js ├── .gitignore ├── .travis.yml ├── .vscode └── launch.json ├── README.md ├── docker-compose.yml ├── e-Commerce-Admin ├── Dockerfile ├── README.md ├── app │ ├── config │ │ ├── email.ts │ │ └── environments │ │ │ ├── dev.ts │ │ │ ├── qa.ts │ │ │ └── test.ts │ ├── controller │ │ └── UserController.ts │ ├── events │ │ └── notification.ts │ ├── global │ │ └── templates │ │ │ ├── emails │ │ │ ├── assets │ │ │ │ └── images │ │ │ │ │ └── logo.png │ │ │ ├── password-reset-email │ │ │ │ ├── html.pug │ │ │ │ └── style.css │ │ │ └── welcome-email │ │ │ │ ├── html.pug │ │ │ │ └── style.css │ │ │ └── response │ │ │ └── index.ts │ ├── helper │ │ ├── bcrypt.ts │ │ ├── email.ts │ │ ├── errorHandler.ts │ │ ├── logger.ts │ │ ├── responseTemplate.ts │ │ └── twillo.ts │ ├── lib │ │ ├── logger.ts │ │ ├── mongoose.ts │ │ └── requestValidator.ts │ ├── middleware │ │ ├── authMiddleware.ts │ │ └── requestValidator.ts │ ├── models │ │ ├── plugin │ │ │ └── plugin.ts │ │ └── user.ts │ ├── routes.ts │ ├── routes │ │ ├── defaultRoutes.ts │ │ ├── provider │ │ │ ├── Facebook.ts │ │ │ ├── Google.ts │ │ │ ├── Linkedin.ts │ │ │ ├── Locale.ts │ │ │ └── Twitter.ts │ │ ├── routes.ts │ │ └── userRoutes.ts │ ├── seed │ │ ├── seedUsers.ts │ │ └── seedVehicle.ts │ ├── transformer │ │ └── userTransformer.ts │ └── types │ │ ├── global.d.ts │ │ └── vendor.d.ts ├── env.sh ├── express.ts ├── package.json ├── public │ ├── images │ │ ├── cinema.jpg │ │ └── favicon.ico │ ├── javascripts │ │ └── script.js │ └── style.scss ├── server.ts ├── tsconfig.json ├── tslint.json └── uploads │ ├── documents │ └── .gitkeep │ └── profile │ └── .gitkeep ├── e-Commerce-Auth ├── Dockerfile ├── README.md ├── app │ ├── config │ │ ├── email.ts │ │ └── environments │ │ │ ├── dev.ts │ │ │ ├── qa.ts │ │ │ └── test.ts │ ├── controller │ │ └── UserController.ts │ ├── events │ │ └── notification.ts │ ├── global │ │ └── templates │ │ │ ├── emails │ │ │ ├── assets │ │ │ │ └── images │ │ │ │ │ └── logo.png │ │ │ ├── password-reset-email │ │ │ │ ├── html.pug │ │ │ │ └── style.css │ │ │ └── welcome-email │ │ │ │ ├── html.pug │ │ │ │ └── style.css │ │ │ └── response │ │ │ └── index.ts │ ├── helper │ │ ├── bcrypt.ts │ │ ├── email.ts │ │ ├── errorHandler.ts │ │ ├── logger.ts │ │ ├── responseTemplate.ts │ │ └── twillo.ts │ ├── lib │ │ ├── logger.ts │ │ ├── mongoose.ts │ │ └── requestValidator.ts │ ├── middleware │ │ ├── authMiddleware.ts │ │ └── requestValidator.ts │ ├── models │ │ ├── plugin │ │ │ └── plugin.ts │ │ └── user.ts │ ├── routes.ts │ ├── routes │ │ ├── defaultRoutes.ts │ │ ├── provider │ │ │ ├── Facebook.ts │ │ │ ├── Google.ts │ │ │ ├── Linkedin.ts │ │ │ ├── Locale.ts │ │ │ └── Twitter.ts │ │ ├── routes.ts │ │ └── userRoutes.ts │ ├── seed │ │ ├── seedUsers.ts │ │ └── seedVehicle.ts │ ├── transformer │ │ └── userTransformer.ts │ └── types │ │ ├── global.d.ts │ │ └── vendor.d.ts ├── env.sh ├── express.ts ├── package.json ├── public │ ├── images │ │ ├── cinema.jpg │ │ └── favicon.ico │ ├── javascripts │ │ └── script.js │ └── style.scss ├── server.ts ├── tsconfig.json ├── tslint.json └── uploads │ ├── documents │ └── .gitkeep │ └── profile │ └── .gitkeep ├── e-Commerce-Cart ├── .sequelizerc ├── Dockerfile ├── README.md ├── app │ ├── config │ │ └── environments │ │ │ ├── dev.ts │ │ │ ├── qa.ts │ │ │ └── test.ts │ ├── events │ │ └── processEvent.ts │ ├── helper │ │ ├── errorHandler.ts │ │ ├── errors.ts │ │ ├── logger.ts │ │ └── responseTemplate.ts │ ├── lib │ │ ├── logger.ts │ │ ├── mysql.ts │ │ └── requestValidator.ts │ ├── middleware │ │ └── requestValidator.ts │ ├── models │ │ └── data │ │ │ └── cart.ts │ ├── routes.ts │ ├── routes │ │ └── defaultRoutes.ts │ └── types │ │ ├── global.d.ts │ │ └── vendor.d.ts ├── config │ └── config.js ├── env.sh ├── express.ts ├── mysql │ └── schema.sql ├── package.json ├── public │ ├── images │ │ ├── cinema.jpg │ │ └── favicon.ico │ ├── javascripts │ │ └── script.js │ └── style.scss ├── server.ts ├── tsconfig.json ├── tslint.json └── uploads │ ├── documents │ └── .gitkeep │ └── profile │ └── .gitkeep ├── e-Commerce-Client ├── Dockerfile ├── README.md ├── env.sh ├── firebase.json ├── package.json ├── public │ ├── favicon.ico │ ├── index.html │ ├── manifest.json │ └── normalize.css └── src │ ├── api │ └── index.js │ ├── components │ ├── App │ │ └── index.js │ ├── Checkbox │ │ └── index.js │ ├── FloatCart │ │ ├── CartProduct │ │ │ └── index.js │ │ ├── index.js │ │ └── style.scss │ ├── Selectbox │ │ └── index.js │ ├── Shelf │ │ ├── Filter │ │ │ ├── index.js │ │ │ └── style.scss │ │ ├── ProductList │ │ │ ├── Product │ │ │ │ └── index.js │ │ │ └── index.js │ │ ├── ShelfHeader │ │ │ └── index.js │ │ ├── Sort │ │ │ └── index.js │ │ ├── index.js │ │ └── style.scss │ ├── Spinner │ │ ├── index.js │ │ └── style.scss │ └── Thumb │ │ └── index.js │ ├── config │ ├── index.js │ └── server.js │ ├── index.js │ ├── index.scss │ ├── layout │ ├── Auth.js │ └── Public.js │ ├── services │ ├── auth │ │ ├── Login.js │ │ ├── Logout.js │ │ ├── Register.js │ │ ├── ResetPassword.js │ │ ├── ValidateToken.js │ │ ├── action.js │ │ ├── actionTypes.js │ │ ├── auth.scss │ │ └── reducer.js │ ├── cart │ │ ├── actionTypes.js │ │ ├── actions.js │ │ └── reducer.js │ ├── filters │ │ ├── actionTypes.js │ │ ├── actions.js │ │ └── reducer.js │ ├── reducers.js │ ├── shelf │ │ ├── actionTypes.js │ │ ├── actions.js │ │ └── reducer.js │ ├── sort │ │ ├── actionTypes.js │ │ ├── actions.js │ │ └── reducer.js │ ├── store.js │ ├── total │ │ ├── actionTypes.js │ │ ├── actions.js │ │ └── reducer.js │ └── util.js │ ├── setupTests.js │ ├── static │ ├── bag-icon.png │ ├── products │ │ ├── 100_1.jpg │ │ ├── 100_2.jpg │ │ ├── 101_1.jpg │ │ ├── 101_2.jpg │ │ ├── 10412368723880252_1.jpg │ │ ├── 10412368723880252_2.jpg │ │ ├── 10547961582846888_1.jpg │ │ ├── 10547961582846888_2.jpg │ │ ├── 10686354557628304_1.jpg │ │ ├── 10686354557628304_2.jpg │ │ ├── 11033926921508488_1.jpg │ │ ├── 11033926921508488_2.jpg │ │ ├── 11600983276356164_1.jpg │ │ ├── 11600983276356164_2.jpg │ │ ├── 11854078013954528_1.jpg │ │ ├── 11854078013954528_2.jpg │ │ ├── 12064273040195392_1.jpg │ │ ├── 12064273040195392_2.jpg │ │ ├── 18532669286405344_1.jpg │ │ ├── 18532669286405344_2.jpg │ │ ├── 18644119330491310_1.jpg │ │ ├── 18644119330491310_2.jpg │ │ ├── 27250082398145996_1.jpg │ │ ├── 27250082398145996_2.jpg │ │ ├── 39876704341265610_1.jpg │ │ ├── 39876704341265610_2.jpg │ │ ├── 51498472915966370_1.jpg │ │ ├── 51498472915966370_2.jpg │ │ ├── 5619496040738316_1.jpg │ │ ├── 5619496040738316_2.jpg │ │ ├── 6090484789343891_1.jpg │ │ ├── 6090484789343891_2.jpg │ │ ├── 8552515751438644_1.jpg │ │ ├── 8552515751438644_2.jpg │ │ ├── 876661122392077_1.jpg │ │ ├── 876661122392077_2.jpg │ │ ├── 9197907543445676_1.jpg │ │ └── 9197907543445676_2.jpg │ └── sprite_delete-icon.png │ └── util │ ├── helper │ └── index.js │ └── middleware │ ├── auth.js │ └── index.js ├── proxy ├── default.conf ├── hosts └── ssl │ ├── pac.crt │ └── pac.key └── screens ├── 02.png └── 03.png /.eslintrc.js: -------------------------------------------------------------------------------- 1 | module.exports = { 2 | "extends": "airbnb-base", 3 | "rules": { 4 | "comma-dangle": 0, 5 | "no-console": 0, 6 | "no-unused-vars" :0 7 | } 8 | }; -------------------------------------------------------------------------------- /.gitignore: -------------------------------------------------------------------------------- 1 | # Created by .ignore support plugin (hsz.mobi) 2 | ### Node template 3 | # Logs 4 | logs 5 | *.log 6 | npm-debug.log* 7 | /dist 8 | .vscode 9 | /test 10 | /*/dist 11 | # Runtime data 12 | pids 13 | *.pid 14 | /node_modules 15 | *.seed 16 | # Directory for instrumented libs generated by jscoverage/JSCover 17 | lib-cov 18 | 19 | # Coverage directory used by tools like istanbul 20 | coverage 21 | 22 | # nyc test coverage 23 | .nyc_output 24 | 25 | # Grunt intermediate storage (http://gruntjs.com/creating-plugins#storing-task-files) 26 | .grunt 27 | 28 | # node-waf configuration 29 | .lock-wscript 30 | 31 | # Compiled binary addons (http://nodejs.org/api/addons.html) 32 | build/Release 33 | 34 | # Dependency directories 35 | node_modules 36 | jspm_packages 37 | package-lock.json 38 | # Optional npm cache directory 39 | .npm 40 | 41 | # Optional REPL history 42 | .node_repl_history 43 | 44 | # Jetbrains dir 45 | .idea/ 46 | .DS_Store 47 | 48 | # github dir 49 | .github/ 50 | 51 | # .env files 52 | bin/dev.env 53 | bin/test.env 54 | -------------------------------------------------------------------------------- /.travis.yml: -------------------------------------------------------------------------------- 1 | language: node_js 2 | sudo: false 3 | node_js: 4 | - 10 5 | install: 6 | - npm install 7 | script: 8 | - npm test -------------------------------------------------------------------------------- /.vscode/launch.json: -------------------------------------------------------------------------------- 1 | { 2 | // Use IntelliSense to learn about possible Node.js debug attributes. 3 | "version": "0.2.0", 4 | "configurations": [ 5 | 6 | { 7 | "type": "node", 8 | "request": "launch", 9 | "name": "Launch Program", 10 | "runtimeExecutable":"node", 11 | "cwd": "${workspaceRoot}", 12 | "protocol": "auto", 13 | "program": "${workspaceRoot}/server.js" 14 | }, 15 | { 16 | "type": "node", 17 | "request": "attach", 18 | "name": "Node: API-1", 19 | "restart": true, 20 | "protocol": "inspector", 21 | "address": "127.0.0.1", 22 | "port": 9230, 23 | "localRoot": "${workspaceRoot}/", 24 | "remoteRoot": "/usr/src/app/", 25 | "timeout": 20000 26 | } 27 | ] 28 | } 29 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | # Application for e-commerce Hub 2 | 3 | 4 | REST API to support application features 5 | - Express as web framework with Typescript 6 | - Passport js for social authentication 7 | - Express CORS enabled 8 | - boom for error codes & Joi for Validation 9 | - Winston for logging and express minitor for monitoring 10 | - Mongoose as ODM driver 11 | - eslint validation extending airbnb styleguide 12 | - git hooks & CI/CD in place 13 | - Typescript based compilation tsc compiler 14 | - TDD in progress with Mocha 15 | - JWT based authentication 16 | - multiple Mongoose collection with referencing 17 | - payment gateway Integration 18 | - Heroku deployment 19 | - Mini e-commerce platform 20 | 21 | # Cart Application # 22 | 23 | "It's just simple application to provide REST APIs for mini e-commerce platform where individual can buy products and can pay the bills 24 | - microservices architecture 25 | - Client application in React 26 | - User Auth microservices 27 | - Cart services 28 | - Admin Microservices 29 | 30 | ![deividing services](/screens/02.png "title") 31 | ![Micro services with Node JS](/screens/03.png "title") 32 | 33 | 34 | 35 | ``` 36 | # Application Execution 37 | ```javascript 38 | git clone repo 39 | npm install 40 | npm run startdev 41 | tsc -- watch 42 | ``` 43 | # Application configuration 44 | ```javascript 45 | env.sh need to be added locally 46 | export NODE_ENV="dev" 47 | export PORT="3005" 48 | export MONGOURL="mongodb://mongo/hello" 49 | export EXPRESS_SESSION_SECRET="************************" 50 | export F_CLIENTID="**************" 51 | export F_CLIENTSECRET="**********************" 52 | ``` 53 | # update etc/hosts file 54 | ``` 55 | ## 56 | # Host Database 57 | # 58 | # localhost is used to configure the loopback interface 59 | # when the system is booting. Do not change this entry. 60 | ## 61 | 127.0.0.1 localhost 62 | 255.255.255.255 broadcasthost 63 | ::1 localhost 64 | 127.0.0.1 mysql redis mongo 65 | 127.0.0.1 ms-commerce.com 66 | ``` 67 | # Sevices end-point 68 | 69 | - http://ms-commerce.com/api/v1 Auth services 70 | - http://ms-commerce.com/admin/v1 Admin APIs 71 | - http://ms-commerce.com/admin/v1 Cart APIs 72 | 73 | # Application NPM Script 74 | ```javascript 75 | "start": "cd dist && nodemon server.js", 76 | "prestart": "tsc && cp -r uploads dist/ && cp -r app/global dist/app/", 77 | "clean" : "rm -rf dist", 78 | "copy" : "cp -r uploads dist/ && cp -r app/global dist/app/" 79 | ``` 80 | -------------------------------------------------------------------------------- /docker-compose.yml: -------------------------------------------------------------------------------- 1 | version: '3.5' 2 | services: 3 | gateway: 4 | image: nginx:1.11 5 | ports: 6 | - 80:80 7 | - 443:443 8 | volumes: 9 | - ./proxy/default.conf:/etc/nginx/conf.d/default.conf:ro 10 | - ./proxy/ssl:/etc/nginx/ssl:ro 11 | depends_on: 12 | - ms_commerce_auth 13 | - ms_commerce_admin 14 | - ms_commerce_client 15 | networks: 16 | - ms_network 17 | ms_mysql: 18 | container_name: ms_mysql 19 | image: mysql:5.7 20 | volumes: 21 | - ~/datadir/mysql:/var/lib/mysql 22 | ports: 23 | - 3306:3306 24 | - 33060:33060 25 | environment: 26 | MYSQL_ROOT_PASSWORD: root 27 | networks: 28 | - ms_network 29 | ms_commerce_mongo: 30 | image: mongo 31 | container_name: ms_commerce_mongo 32 | restart: unless-stopped 33 | volumes: 34 | - ~/datadir/mongo:/data/db 35 | ports: 36 | - 27017:27017 37 | networks: 38 | - ms_network 39 | ms_commerce_auth: 40 | container_name: ms_commerce_auth 41 | build: ./e-Commerce-Auth/ 42 | image: e-commerce-auth 43 | volumes: 44 | - ./e-Commerce-Auth/:/usr/src/app 45 | - /usr/src/app/node_modules 46 | ports: 47 | - 3001:3001 48 | - 9201:9201 49 | depends_on: 50 | - ms_mysql 51 | networks: 52 | - ms_network 53 | ms_commerce_cart: 54 | container_name: ms_commerce_cart 55 | build: ./e-Commerce-Cart/ 56 | image: e-commerce-cart 57 | volumes: 58 | - ./e-Commerce-Cart/:/usr/src/app 59 | - /usr/src/app/node_modules 60 | ports: 61 | - 3004:3004 62 | - 9204:9204 63 | depends_on: 64 | - ms_mysql 65 | networks: 66 | - ms_network 67 | ms_commerce_admin: 68 | build: ./e-Commerce-Admin/ 69 | image: e-commerce-admin 70 | container_name: ms_commerce_admin 71 | environment: 72 | - NODE_ENV=local 73 | volumes: 74 | - ./e-Commerce-Admin/:/usr/src/app 75 | - /usr/src/app/node_modules 76 | ports: 77 | - 3002:3002 78 | - 9202:9202 79 | depends_on: 80 | - ms_commerce_mongo 81 | networks: 82 | - ms_network 83 | ms_commerce_client: 84 | build: ./e-Commerce-Client/ 85 | image: e-commerce-client 86 | container_name: ms_commerce_client 87 | environment: 88 | - NODE_ENV=local 89 | volumes: 90 | - ./e-Commerce-Client/:/usr/src/app 91 | - /usr/src/app/node_modules 92 | ports: 93 | - 3003:3003 94 | depends_on: 95 | - ms_commerce_admin 96 | - ms_commerce_auth 97 | networks: 98 | - ms_network 99 | networks: 100 | ms_network: 101 | driver: bridge 102 | name: ms_network 103 | 104 | -------------------------------------------------------------------------------- /e-Commerce-Admin/Dockerfile: -------------------------------------------------------------------------------- 1 | FROM node:carbon 2 | 3 | # Create app directory 4 | WORKDIR /usr/src/app 5 | 6 | # Bundle app source 7 | COPY . . 8 | 9 | # npm install 10 | RUN npm install 11 | # Run npm install --global grpc --unsafe-perm 12 | 13 | EXPOSE 3002 9202 14 | CMD [ "npm", "run", "watchserver" ] 15 | CMD [ "npm", "run", "startdev" ] 16 | 17 | -------------------------------------------------------------------------------- /e-Commerce-Admin/README.md: -------------------------------------------------------------------------------- 1 | # Application for e-commerce Hub 2 | 3 | 4 | REST API to support application features 5 | 6 | - Express as web framework with Typescript 7 | - Passport js for social authentication 8 | - Express CORS enabled 9 | - boom for error codes & Joi for Validation 10 | - Winston for logging and express minitor for monitoring 11 | - Mongoose as ODM driver 12 | - eslint validation extending airbnb styleguide 13 | - git hooks & CI/CD in place 14 | - Typescript based compilation tsc compiler 15 | - TDD in progress with Mocha 16 | - JWT based authentication 17 | - multiple Mongoose collection with referencing 18 | - payment gateway Integration 19 | - Heroku deployment 20 | - Mini e-commerce platform 21 | 22 | # Cart Application # 23 | 24 | "It's just simple application to provide REST APIs for mini e-commerce platform where individual can buy products and can pay the bills 25 | 26 | ``` 27 | # Application Execution 28 | ```javascript 29 | git clone repo 30 | npm install 31 | npm run startdev 32 | tsc -- watch 33 | ``` 34 | # Application configuration 35 | ```javascript 36 | env.sh need to be added locally 37 | export NODE_ENV="dev" 38 | export PORT="3005" 39 | export MONGOURL="mongodb://mongo/hello" 40 | export EXPRESS_SESSION_SECRET="************************" 41 | export F_CLIENTID="**************" 42 | export F_CLIENTSECRET="**********************" 43 | ``` 44 | 45 | # Application NPM Script 46 | ```javascript 47 | "start": "cd dist && nodemon server.js", 48 | "prestart": "tsc && cp -r uploads dist/ && cp -r app/global dist/app/", 49 | "clean" : "rm -rf dist", 50 | "copy" : "cp -r uploads dist/ && cp -r app/global dist/app/" 51 | ``` 52 | -------------------------------------------------------------------------------- /e-Commerce-Admin/app/config/email.ts: -------------------------------------------------------------------------------- 1 | 'use strict'; 2 | 3 | export default { 4 | 5 | global: { 6 | from: 'info@kpilibrary.com', 7 | }, 8 | welcome: { 9 | subject: 'Welcome to KPI Library', 10 | }, 11 | password_reset: { 12 | subject: 'KPI Library: Reset your password', 13 | }, 14 | event_booked_guest: { 15 | subject: 'KPI Library: Your Booking Has Been Confirmed', 16 | }, 17 | event_booked_host: { 18 | subject: 'KPI Library: Your Event Has Been Booked', 19 | }, 20 | event_booked_guests_notification: { 21 | subject: 'KPI Library: Your Booking Has Been Confirmed', 22 | }, 23 | message_received: { 24 | subject: 'KPI Library: New Message Received', 25 | }, 26 | guest_review_email: { 27 | subject: 'KPI Library: Event Completed', 28 | }, 29 | alacarte_booked_guest: { 30 | subject: 'KPI Library: Alacarte Booking Details', 31 | }, 32 | alacarte_booked_host: { 33 | subject: 'KPI Library: Your Alacarte Has Been Booked', 34 | } 35 | } 36 | 37 | -------------------------------------------------------------------------------- /e-Commerce-Admin/app/config/environments/dev.ts: -------------------------------------------------------------------------------- 1 | /* eslint quote-props: 0 */ 2 | export { } 3 | const configuration: any = {}; 4 | configuration.mongo = { 5 | url: process.env.MONGODB_URI || process.env.MONGOURL, 6 | }; 7 | configuration.URL = { 8 | frontEnd: process.env.FE_URL 9 | } 10 | configuration.facebook = { 11 | client_id: process.env.F_CLIENTID, 12 | client_secret: process.env.F_CLIENTSECRET, 13 | callback_url: process.env.F_CALLBACK 14 | }; 15 | configuration.google = { 16 | client_id: process.env.G_CLIENTID, 17 | client_secret: process.env.G_CLIENTSECRET, 18 | callback_url: process.env.G_CALLBACK 19 | }; 20 | configuration.linkedin = { 21 | client_id: process.env.L_CLIENTID, 22 | client_secret: process.env.L_CLIENTSECRET, 23 | callback_url: process.env.L_CALLBACK 24 | }; 25 | configuration.twitter = { 26 | client_id: process.env.T_CLIENTID, 27 | client_secret: process.env.T_CLIENTSECRET, 28 | callback_url: process.env.T_CALLBACK 29 | }; 30 | configuration.email = { 31 | apiKey: process.env.API_KEY, 32 | host: process.env.SMTP_HOST, 33 | port: process.env.SMTP_PORT, 34 | auth: { 35 | user: process.env.SMTP_USER, 36 | pass: process.env.SMTP_PASSWORD, 37 | } 38 | } 39 | configuration.twilio = { 40 | sid: process.env.SID, 41 | token: process.env.TOKEN, 42 | phone: process.env.PHONE, 43 | } 44 | configuration.url = { 45 | FE: process.env.FE, 46 | API: process.env.API, 47 | } 48 | configuration.uploadpath = { 49 | uploaddir: process.env.UPLOAD_DIR, 50 | profiledir: process.env.PROFILE_PICTURE_DIR 51 | } 52 | 53 | module.exports = configuration; -------------------------------------------------------------------------------- /e-Commerce-Admin/app/config/environments/qa.ts: -------------------------------------------------------------------------------- 1 | /* eslint quote-props: 0 */ 2 | export { } 3 | const configuration: any = {}; 4 | configuration.mongo = { 5 | url: process.env.MONGODB_URI || process.env.MONGOURL, 6 | }; 7 | configuration.URL = { 8 | frontEnd: process.env.FE_URL 9 | } 10 | configuration.facebook = { 11 | client_id: process.env.F_CLIENTID, 12 | client_secret: process.env.F_CLIENTSECRET, 13 | callback_url: process.env.F_CALLBACK 14 | }; 15 | configuration.google = { 16 | client_id: process.env.G_CLIENTID, 17 | client_secret: process.env.G_CLIENTSECRET, 18 | callback_url: process.env.G_CALLBACK 19 | }; 20 | configuration.linkedin = { 21 | client_id: process.env.L_CLIENTID, 22 | client_secret: process.env.L_CLIENTSECRET, 23 | callback_url: process.env.L_CALLBACK 24 | }; 25 | configuration.twitter = { 26 | client_id: process.env.T_CLIENTID, 27 | client_secret: process.env.T_CLIENTSECRET, 28 | callback_url: process.env.T_CALLBACK 29 | }; 30 | configuration.email = { 31 | apiKey: process.env.API_KEY, 32 | host: process.env.SMTP_HOST, 33 | port: process.env.SMTP_PORT, 34 | auth: { 35 | user: process.env.SMTP_USER, 36 | pass: process.env.SMTP_PASSWORD, 37 | } 38 | } 39 | configuration.twilio = { 40 | sid: process.env.SID, 41 | token: process.env.TOKEN, 42 | phone: process.env.PHONE, 43 | } 44 | configuration.url = { 45 | FE: process.env.FE, 46 | API: process.env.API, 47 | } 48 | configuration.uploadpath = { 49 | uploaddir: process.env.UPLOAD_DIR, 50 | profiledir: process.env.PROFILE_PICTURE_DIR 51 | } 52 | module.exports = configuration; 53 | -------------------------------------------------------------------------------- /e-Commerce-Admin/app/config/environments/test.ts: -------------------------------------------------------------------------------- 1 | /* eslint quote-props: 0 */ 2 | export { } 3 | const configuration: any = {}; 4 | configuration.mongo = { 5 | url: process.env.MONGODB_URI || process.env.MONGOURL, 6 | }; 7 | configuration.URL = { 8 | frontEnd: process.env.FE_URL 9 | } 10 | configuration.facebook = { 11 | client_id: process.env.F_CLIENTID, 12 | client_secret: process.env.F_CLIENTSECRET, 13 | callback_url: process.env.F_CALLBACK 14 | }; 15 | configuration.google = { 16 | client_id: process.env.G_CLIENTID, 17 | client_secret: process.env.G_CLIENTSECRET, 18 | callback_url: process.env.G_CALLBACK 19 | }; 20 | configuration.linkedin = { 21 | client_id: process.env.L_CLIENTID, 22 | client_secret: process.env.L_CLIENTSECRET, 23 | callback_url: process.env.L_CALLBACK 24 | }; 25 | configuration.twitter = { 26 | client_id: process.env.T_CLIENTID, 27 | client_secret: process.env.T_CLIENTSECRET, 28 | callback_url: process.env.T_CALLBACK 29 | }; 30 | configuration.email = { 31 | apiKey: process.env.API_KEY, 32 | host: process.env.SMTP_HOST, 33 | port: process.env.SMTP_PORT, 34 | auth: { 35 | user: process.env.SMTP_USER, 36 | pass: process.env.SMTP_PASSWORD, 37 | } 38 | } 39 | configuration.twilio = { 40 | sid: process.env.SID, 41 | token: process.env.TOKEN, 42 | phone: process.env.PHONE, 43 | } 44 | configuration.url = { 45 | FE: process.env.FE, 46 | API: process.env.API, 47 | } 48 | configuration.uploadpath = { 49 | uploaddir: process.env.UPLOAD_DIR, 50 | profiledir: process.env.PROFILE_PICTURE_DIR 51 | } 52 | module.exports = configuration; 53 | -------------------------------------------------------------------------------- /e-Commerce-Admin/app/events/notification.ts: -------------------------------------------------------------------------------- 1 | 2 | import Email from '../helper/email'; 3 | declare function require(name: string); 4 | 5 | const events = require('events'); 6 | const winston = require('winston'); 7 | // import Twillo from '../helper/twillo'; 8 | 9 | const eventEmitter = new events.EventEmitter(); 10 | eventEmitter.on('welcome', (user) => { 11 | winston.log('info', `sending welcome email to ${user.email}`); 12 | // Twillo.default_notification(user.phone, 'welcome') 13 | Email.welcome(user); 14 | }); 15 | eventEmitter.on('forgotPassword', (user, password, uuid) => { 16 | winston.log('info', `sending forgotPassword email to ${user.email}`); 17 | Email.password_reset(user, password); 18 | // Twillo.default_notification(user.phone, 'welcome') 19 | }); 20 | 21 | export default eventEmitter; 22 | -------------------------------------------------------------------------------- /e-Commerce-Admin/app/global/templates/emails/assets/images/logo.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/tkssharma/e-CommerseHub/044bb67bf47ef1808633ca8f5c0c9812d5fcdd44/e-Commerce-Admin/app/global/templates/emails/assets/images/logo.png -------------------------------------------------------------------------------- /e-Commerce-Admin/app/global/templates/response/index.ts: -------------------------------------------------------------------------------- 1 | class ResponseTemplate { 2 | static general(data) { 3 | return data; 4 | } 5 | static error(code, message, description) { 6 | return { 7 | statusCode: code || 400, 8 | message: message || 'some error occoured', 9 | description: description || 'error occoured on server, please try again after some time.' 10 | }; 11 | } 12 | static authError() { 13 | return this.error( 14 | 403, 15 | 'authentication error', 16 | 'no authentication token provided, please login first and provide the authentication token.' 17 | ); 18 | } 19 | static invalidAuthError() { 20 | return this.error( 21 | 403, 22 | 'authentication error', 23 | 'invalid Token provided, please login first and provide the authentication token.' 24 | ); 25 | } 26 | static emptyContent() { 27 | return this.general({ 28 | statusCode: 402, 29 | message: 'empty content found', 30 | description: 'you must provide valid data and it must not be empty.', 31 | helpful_links: ['http://stackoverflow.com/questions/18419428/what-is-the-minimum-valid-json'] 32 | }); 33 | } 34 | static invalidContentType() { 35 | return this.general({ 36 | statusCode: 400, 37 | message: 'invalid content type', 38 | description: 'you must specify content type and it must be application/json', 39 | helpful_links: ['http://stackoverflow.com/questions/477816/what-is-the-correct-json-content-type'] 40 | }); 41 | } 42 | static routeNotFound() { 43 | return this.error( 44 | 405, 45 | 'resource not found', 46 | 'the resource your tried to access doesn\'t exist or you dont have permissions to access it.' 47 | ); 48 | } 49 | static userNotFound() { 50 | return this.error( 51 | 400, 52 | 'user not found', 53 | "the user you're looking for doesn't exist or you dont have permissions to access it." 54 | ); 55 | } 56 | static updateErrorOccoured(error) { 57 | return this.error( 58 | 301, 59 | 'error occoured', 60 | error || 'error occoured while updating your data.' 61 | ); 62 | } 63 | static success(description, data=null) { 64 | return { 65 | statusCode: 200, 66 | message: 'success', 67 | description: description || 'data successfully saved.', 68 | ...data 69 | } 70 | } 71 | } 72 | 73 | export default ResponseTemplate; 74 | 75 | -------------------------------------------------------------------------------- /e-Commerce-Admin/app/helper/bcrypt.ts: -------------------------------------------------------------------------------- 1 | 2 | const bcrypt = require('bcrypt-nodejs'); 3 | const jwt = require('jsonwebtoken'); 4 | const {url} = global['configuration'] 5 | const {uploadpath} = global['configuration'] 6 | const path = require('path'); 7 | const fs = require('fs'); 8 | const helper = { 9 | generateSaltValue(password) { 10 | const salt = bcrypt.genSaltSync(); // enter number of rounds, default: 10 11 | const hash = bcrypt.hashSync(password, salt); 12 | return hash; 13 | }, 14 | comparePassword(userPassword, password ) { 15 | if (!userPassword.length || !( password && password.length > 0) ) { 16 | return false; 17 | } 18 | return bcrypt.compareSync(userPassword, password); 19 | }, 20 | authRedirectUrl( path ) { 21 | return `${url.FE}/#/auth/validate-token/${path}`; 22 | }, 23 | 24 | buildUserToken(data) { 25 | return { 26 | email: data.email, 27 | id:data._id, 28 | username: data.username ? data.username : data.email, 29 | hasPassword: data.password ? true : false, 30 | type : data.type || 1, 31 | picture : data.picture 32 | } 33 | }, 34 | resource( path ) { 35 | return `${url.API}${path}`; 36 | }, 37 | getFileExtension( file ) { 38 | let extensions = file.split('.'); 39 | if ( extensions.length === 1 ) { 40 | return 'jpg'; 41 | } else { 42 | return extensions.pop(); 43 | } 44 | }, 45 | avatarURL( filename ) { 46 | if ( filename.includes('://') ) { 47 | return filename; 48 | } 49 | return this.resource(`/${uploadpath.uploaddir}/${uploadpath.profiledir}/${filename}`); 50 | }, 51 | userDocumentURL( filename ) { 52 | if ( filename.includes('://') ) { 53 | return filename; 54 | } 55 | return this.resource(`/${uploadpath.uploaddir}/${uploadpath.documentdir}/${filename}`); 56 | }, 57 | randomString() { 58 | return Math.random().toString(36).substring(2, 7); 59 | }, 60 | deleteFile( type, filename ) { 61 | let location; 62 | if ( type === 'profile' ) { location = path.join( uploadpath.uploaddir, uploadpath.profiledir ) } 63 | else { location = uploadpath.uploaddir; } 64 | if (filename) { 65 | fs.unlink( path.join( location, filename ), () => { 66 | // in case we need to perform additional operations. 67 | }); 68 | } 69 | }, 70 | getPaymentMethodName( method ) { 71 | if ( method == 1 ) { return 'PayPal'; } 72 | else if ( method == 2 ) { return 'PayPal'; } 73 | else if ( method == 3 ) { return 'Instamojo'; } 74 | else { return 'Not Specified'; } 75 | }, 76 | getCurrency(currency) { 77 | let allCurrency = { 78 | 1: 'USD', 79 | 2: 'INR', 80 | }; 81 | 82 | if( allCurrency[currency] ) { 83 | return allCurrency[currency]; 84 | } else { 85 | return allCurrency[1]; 86 | } 87 | 88 | }, 89 | verificationCode() { 90 | let code = Math.floor((Math.random()*999999)+111111); 91 | return code; 92 | } 93 | }; 94 | 95 | export default helper; 96 | 97 | 98 | -------------------------------------------------------------------------------- /e-Commerce-Admin/app/helper/email.ts: -------------------------------------------------------------------------------- 1 | const path = require('path'); 2 | declare function require(name:string); 3 | 4 | const nodemailer = require('nodemailer'); 5 | const url = global['configuration'].url; 6 | const mailConfig = global['configuration'].email; 7 | 8 | import emailConfig from '../config/email'; 9 | import { EmailTemplate } from 'email-templates'; 10 | // const Twillo = require('./twillo'); 11 | 12 | const transport = nodemailer.createTransport({ 13 | host: 'smtp.gmail.com', 14 | port: 465, 15 | secure: true, 16 | auth: { 17 | // type: 'OAuth2', 18 | user: mailConfig.auth.user, 19 | pass: mailConfig.auth.pass 20 | }, 21 | }); 22 | const Email = { 23 | welcome(user) { 24 | if (user.email) { 25 | let templateDir = path.join('app/global/templates', 'emails', 'welcome-email'); 26 | let welcomeEmail = new EmailTemplate(templateDir); 27 | welcomeEmail.render({ user: user, activate_url: `${url.API}/auth/activate/${user.uuid}` }, (err, result) => { 28 | transport.sendMail( 29 | { 30 | from: emailConfig.global.from, 31 | to: user.email, 32 | subject: emailConfig.welcome.subject, 33 | html: result.html, 34 | }, (err, info) => { 35 | // some error occoured... 36 | console.log(err); 37 | } 38 | ); 39 | }); 40 | } 41 | }, 42 | password_reset(user, password) { 43 | if (user.email) { 44 | let templateDir = path.join('app/global/templates', 'emails', 'password-reset-email'); 45 | let passwordResetEmail = new EmailTemplate(templateDir); 46 | passwordResetEmail.render({ user: user, login_url: `${url.FE}/#/auth/login`, password: password }, (err, result) => { 47 | transport.sendMail( 48 | { 49 | from: emailConfig.global.from, 50 | to: user.email, 51 | subject: emailConfig.password_reset.subject, 52 | html: result.html, 53 | }, (err, info) => { 54 | // some error occoured... 55 | } 56 | ); 57 | }); 58 | } 59 | if (user.phone_verified) { 60 | // Twillo.password_reset_notification(user.phone); 61 | } 62 | 63 | }, 64 | }; 65 | export default Email; 66 | -------------------------------------------------------------------------------- /e-Commerce-Admin/app/helper/errorHandler.ts: -------------------------------------------------------------------------------- 1 | import * as winston from 'winston'; 2 | import ResponseTemplate from './responseTemplate'; 3 | /* eslint class-methods-use-this:0 */ 4 | const env = process.env.NODE_ENV; 5 | const onDevEnv = env === 'dev' || env === 'test' || env === 'local'; 6 | class errorHandler { 7 | public internalServerError(err, req, res, next) { 8 | winston.log('info',err); 9 | if (err.isBoom) { 10 | // Error From joi express validator 11 | const error = { 12 | message: err.output.payload.error, 13 | error: err.output.payload.message 14 | }; 15 | res.status(400).json(ResponseTemplate.BadRequestFromJoi(error)); 16 | } else { // internalServerError 17 | res.status(500).json({ 18 | success: false, 19 | message: err.message, 20 | error: (onDevEnv) ? err.stack : {} 21 | }); 22 | } 23 | } 24 | public PageNotFound(req, res, err) { 25 | res.status(404).json({ message: 'api not found' }); 26 | } 27 | } 28 | 29 | export default new errorHandler(); 30 | -------------------------------------------------------------------------------- /e-Commerce-Admin/app/helper/logger.ts: -------------------------------------------------------------------------------- 1 | const winston = require('winston'); 2 | 3 | // define the custom settings for each transport (file, console) 4 | const options = { 5 | console: { 6 | level: 'info', 7 | handleExceptions: true, 8 | json: true, 9 | colorize: true, 10 | prettyPrint: true, 11 | humanReadableUnhandledException: true 12 | } 13 | }; 14 | const logger = new winston.Logger({ 15 | transports: [ 16 | // new winston.transports.File(options.file), 17 | new winston 18 | .transports 19 | .Console(options.console), 20 | ], 21 | exceptionHandlers: [ 22 | // new winston.transports.File(options.errorLog) 23 | ], 24 | exitOnError: false, // do not exit on handled exceptions 25 | }); 26 | 27 | // create a stream object with a 'write' function that will be used by `morgan` 28 | logger.stream = { 29 | write(message, encoding) { 30 | logger.info(message); 31 | } 32 | }; 33 | export default logger; 34 | -------------------------------------------------------------------------------- /e-Commerce-Admin/app/helper/responseTemplate.ts: -------------------------------------------------------------------------------- 1 | /* istanbul ignore file */ 2 | const data: any = { 3 | general(data) { 4 | return data; 5 | }, 6 | successMessage(message) { 7 | return { 8 | success: true, 9 | message 10 | }; 11 | }, 12 | success(data, message) { 13 | return { 14 | success: true, 15 | message, 16 | data 17 | }; 18 | }, 19 | error(message, err, code= null) { 20 | return { 21 | success: false, 22 | message: message || 'some error occurred', 23 | error: err || 'error occurred on server, please try again after some time.' 24 | }; 25 | }, 26 | emptyContent() { 27 | return this.general({ 28 | message: 'empty content found', 29 | description: 'you must provide valid data and it must not be empty.', 30 | helpful_links: ['http://stackoverflow.com/questions/18419428/what-is-the-minimum-valid-json'] 31 | }); 32 | }, 33 | invalidContentType() { 34 | return this.general({ 35 | message: 'invalid content type', 36 | description: 'you must specify content type and it must be application/json', 37 | helpful_links: ['http://stackoverflow.com/questions/477816/what-is-the-correct-json-content-type'] 38 | }); 39 | }, 40 | BadRequestFromJoi(err) { 41 | return this.error( 42 | err.message, 43 | err.error 44 | ); 45 | }, 46 | userAlreadyExist(err) { 47 | return this.general({ 48 | success: false, 49 | message: 'user already registered in System', 50 | description: 'user already registered in System' 51 | }); 52 | }, 53 | userdoesNotExist(err) { 54 | return this.general({ 55 | success: false, 56 | message: err.message || 'user not registered in system', 57 | description: 'user account does not exist in system' 58 | }); 59 | }, 60 | commonAuthUserDataError() { 61 | return this.error( 62 | 'Authentication error', 63 | 'token verification failed, Please try again' 64 | ); 65 | }, 66 | tokenRequiredAuthError() { 67 | return this.error( 68 | 'Authentication error, Token is required in Header', 69 | 'token verification failed, Please try again' 70 | ); 71 | }, 72 | }; 73 | export default data; 74 | -------------------------------------------------------------------------------- /e-Commerce-Admin/app/helper/twillo.ts: -------------------------------------------------------------------------------- 1 | const twilioConfig = global['configuration'].twilio; 2 | // import twilio from 'twilio'; 3 | 4 | const twillo = require('twilio') 5 | const client = new twillo(twilioConfig.sid, twilioConfig.token); 6 | 7 | let TwilioHelper = { 8 | 9 | phone_verification(phone_number, code, callback) { 10 | client.sendMessage({ 11 | to: phone_number, 12 | from: twilioConfig.phone, 13 | body: `Hello from Bal Bla e-Commerce-Hub\nYour verification code is ${code}`, 14 | }, (err, message) => { 15 | callback(message); 16 | }); 17 | }, 18 | password_reset_notification(phone) { 19 | client.sendMessage({ 20 | to: phone, 21 | from: twilioConfig.phone, 22 | body: `Bla Bla\nYour password has been successfully reset.`, 23 | }, (err, message) => { 24 | // 25 | }); 26 | }, 27 | 28 | default_notification(phone, message) { 29 | console.log(client); 30 | client.sendMessage({ 31 | to: phone, 32 | from: twilioConfig.phone, 33 | body: `e-Commerce-Hub-TM\n${message}`, 34 | }, (err, message) => { 35 | // 36 | }); 37 | } 38 | } 39 | export default TwilioHelper; 40 | 41 | 42 | -------------------------------------------------------------------------------- /e-Commerce-Admin/app/lib/logger.ts: -------------------------------------------------------------------------------- 1 | const winston = require('winston'); 2 | const moment = require('moment'); 3 | 4 | // define the custom settings for each transport (file, console) 5 | const options = { 6 | console: { 7 | level: 'info', 8 | handleExceptions: true, 9 | json: true, 10 | colorize: true, 11 | timestamp() { 12 | return moment 13 | .utc() 14 | .format(); 15 | }, 16 | prettyPrint: true, 17 | humanReadableUnhandledException: true 18 | } 19 | }; 20 | const logger = new winston.Logger({ 21 | transports: [ 22 | // new winston.transports.File(options.file), 23 | new winston 24 | .transports 25 | .Console(options.console), 26 | ], 27 | exceptionHandlers: [ 28 | // new winston.transports.File(options.errorLog) 29 | ], 30 | exitOnError: false, // do not exit on handled exceptions 31 | }); 32 | 33 | // create a stream object with a 'write' function that will be used by `morgan` 34 | logger.stream = { 35 | write(message, encoding) { 36 | logger.info(message); 37 | } 38 | }; 39 | export default logger; 40 | -------------------------------------------------------------------------------- /e-Commerce-Admin/app/lib/mongoose.ts: -------------------------------------------------------------------------------- 1 | /* eslint func-names:0 */ 2 | const mongoose = require('mongoose'); 3 | mongoose.promises = require('bluebird'); 4 | const { url } = global['configuration'].mongo; 5 | const MongoConnect = function () { 6 | const db = mongoose.connect(url, { useNewUrlParser: true }, (error) => { 7 | if (error) { 8 | console.log(`Mongoose default connection error: ${error}`); 9 | } else { 10 | console.log('mongo Connected :)'); 11 | } 12 | }); 13 | mongoose.connection.on('connected', () => { 14 | console.log(`Mongoose default connection open to ${url}`); 15 | }); 16 | 17 | // If the connection throws an error 18 | mongoose.connection.on('error', (err) => { 19 | console.log(`Mongoose default connection error: ${err}`); 20 | }); 21 | 22 | // When the connection is disconnected 23 | mongoose.connection.on('disconnected', () => { 24 | console.log('Mongoose default connection disconnected'); 25 | }); 26 | 27 | // If the Node process ends, close the Mongoose connection 28 | process.on('SIGINT', () => { 29 | mongoose.connection.close(() => { 30 | console.log('Mongoose default connection disconnected through app termination'); 31 | process.exit(0); 32 | }); 33 | }); 34 | return db; 35 | }; 36 | 37 | export default MongoConnect; 38 | -------------------------------------------------------------------------------- /e-Commerce-Admin/app/lib/requestValidator.ts: -------------------------------------------------------------------------------- 1 | const Joi = require('joi'); 2 | 3 | const validation = { 4 | loginUser : { 5 | body : { 6 | email: Joi.string().regex(/^[\w.]+@[\w]+?(\.[a-zA-Z]{2,3}){1,3}$/).required(), 7 | password: Joi.string().min(8).max(50).required() 8 | } 9 | }, 10 | createUser: { 11 | body: { 12 | username: Joi.string().min(4).max(50).required(), 13 | password: Joi.string().min(8).max(50).required(), 14 | email: Joi.string().regex(/^[\w.]+@[\w]+?(\.[a-zA-Z]{2,3}){1,3}$/).required(), 15 | verify_password: Joi.string().min(6).max(50).required() 16 | }, 17 | }, 18 | resetPassword: { 19 | body: { 20 | email: Joi.string().regex(/^[\w.]+@[\w]+?(\.[a-zA-Z]{2,3}){1,3}$/).required(), 21 | } 22 | } 23 | }; 24 | export default validation 25 | -------------------------------------------------------------------------------- /e-Commerce-Admin/app/middleware/authMiddleware.ts: -------------------------------------------------------------------------------- 1 | const jwt = require("jsonwebtoken"); 2 | import responseTemplate from '../helper/responseTemplate'; 3 | import User from '../models/user'; 4 | const validation: any = { 5 | validateToken(req, res, next) { 6 | // validatr token here is its valid here 7 | const token = req.headers.authorization; 8 | if (token) { 9 | jwt.verify(token, "secretkey", (err, data) => { 10 | if (err) { 11 | res.status(403).json(responseTemplate.commonAuthUserDataError()); 12 | } else { 13 | User.findById(data.id, (error, user) => { 14 | if(error){ 15 | res.status(403).json(responseTemplate.commonAuthUserDataError()); 16 | } 17 | user.hasPassword = user.password ? true : false; 18 | req.user = user; 19 | next(); 20 | }) 21 | } 22 | }); 23 | } else { 24 | res.status(403).json(responseTemplate.tokenRequiredAuthError()); 25 | } 26 | } 27 | }; 28 | 29 | export default validation; 30 | -------------------------------------------------------------------------------- /e-Commerce-Admin/app/middleware/requestValidator.ts: -------------------------------------------------------------------------------- 1 | 2 | module.exports = { 3 | validatePayload(req, res, next) { 4 | // validatr token here is its valid here 5 | const token = req.body; 6 | if ((req.method === 'POST' || req.method === 'PUT') && req.body !== null) { 7 | next(); 8 | } 9 | res.status(403).json({ message: 'payload is required for HTTP Post & Put ' }); 10 | } 11 | }; 12 | -------------------------------------------------------------------------------- /e-Commerce-Admin/app/models/plugin/plugin.ts: -------------------------------------------------------------------------------- 1 | const datePlugin = function timestamp(schema) { 2 | // Add the two fields to the schema 3 | schema.add({ 4 | createdAt: Date, 5 | updatedAt: Date 6 | }) 7 | 8 | // Create a pre-save hook 9 | schema.pre('save', function (next) { 10 | let now = Date.now() 11 | 12 | 13 | this.updatedAt = now 14 | // Set a value for createdAt only if it is null 15 | if (!this.createdAt) { 16 | this.createdAt = now 17 | } 18 | // Call the next function in the pre-save chain 19 | next() 20 | }) 21 | } 22 | 23 | export default datePlugin; -------------------------------------------------------------------------------- /e-Commerce-Admin/app/models/user.ts: -------------------------------------------------------------------------------- 1 | const mongoose = require('mongoose'); 2 | const bcrypt = require('bcrypt-nodejs'); 3 | import helper from '../helper/bcrypt'; 4 | const { Schema } = mongoose; 5 | import timestampPlugin from './plugin/plugin'; 6 | import Helper from '../../app/helper/bcrypt'; 7 | const userSchema = new Schema({ 8 | provider: { 9 | type: String 10 | }, 11 | username: { 12 | type: String 13 | }, 14 | password: { type: String }, 15 | email: { 16 | index: { unique: true }, 17 | type: String 18 | }, 19 | address: { 20 | type: String 21 | }, 22 | meta: mongoose.Schema.Types.Mixed, 23 | picture : mongoose.Schema.Types.Mixed, 24 | /* 25 | reviews : [{ 26 | type: mongoose.Schema.ObjectId, 27 | ref: 'Review' 28 | }], 29 | booking : [{ 30 | type: mongoose.Schema.ObjectId, 31 | ref: 'Booking' 32 | }], 33 | vehicles: [{ 34 | type: mongoose.Schema.ObjectId, 35 | ref: 'Vehicle' 36 | }], */ 37 | uuid: { 38 | type: String 39 | }, 40 | type: { 41 | type: String, 42 | default: 1 43 | }, 44 | status: { 45 | type: String, 46 | default: 1 47 | }, 48 | profile_picture: mongoose.Schema.Types.Mixed, 49 | phone: String, 50 | email_verified: Boolean, 51 | phone_verified: Boolean, 52 | social: mongoose.Schema.Types.Mixed, 53 | documents: [mongoose.Schema.Types.Mixed], 54 | gender: Number, // 1: Male, 2: Female, 3: Unspecified 55 | },{ toJSON: { virtuals: true } }); 56 | 57 | userSchema.set('toObject', { virtuals: true }); 58 | userSchema.set('toJSON', { virtuals: true }); 59 | userSchema.plugin(timestampPlugin) 60 | 61 | const User = mongoose.model('User', userSchema); 62 | 63 | export default User; 64 | 65 | -------------------------------------------------------------------------------- /e-Commerce-Admin/app/routes.ts: -------------------------------------------------------------------------------- 1 | /* eslint func-names: ["error", "never"] */ 2 | /* eslint prefer-destructuring: 0 */ 3 | import * as express from 'express'; 4 | const expressRouter= express.Router(); 5 | import authRoutes from './routes/routes'; 6 | import userRoutes from './routes/userRoutes'; 7 | import defaultRoutes from './routes/defaultRoutes'; 8 | import validAuthTokenMiddleware from './middleware/authMiddleware'; 9 | expressRouter.use('/', defaultRoutes); 10 | expressRouter.use('/uploads/', express.static('uploads')); 11 | expressRouter.use('/auth', authRoutes); 12 | expressRouter.use('/user',validAuthTokenMiddleware.validateToken, userRoutes); 13 | 14 | 15 | 16 | export default expressRouter; 17 | -------------------------------------------------------------------------------- /e-Commerce-Admin/app/routes/defaultRoutes.ts: -------------------------------------------------------------------------------- 1 | import * as express from "express"; 2 | const router = express.Router(); 3 | 4 | import { Router } from "express"; 5 | 6 | 7 | 8 | export class DefaultRouter { 9 | router: Router; 10 | 11 | /** 12 | * Initialize the HeroRouter 13 | */ 14 | constructor() { 15 | this.router = Router(); 16 | } 17 | /** 18 | * @api {POST} /auth/reset-password update password sent in Mail 19 | * @apiName resetPassword 20 | * @apiGroup Auth 21 | * @apiSuccess {String} code HTTP status code from API. 22 | * @apiSuccess {String} message Message from API. 23 | */ 24 | public sayHello(req, res) { 25 | res.status(200).json({ success: true, message: 'i am up and running .. ⚡️⚡️⚡️⚡️⚡️⚡️⚡️' }); 26 | }; 27 | 28 | init() { 29 | this.router.get("/", this.sayHello); 30 | } 31 | } 32 | 33 | 34 | // Create the HeroRouter, and export its configured Express.Router 35 | const defaultRouter = new DefaultRouter(); 36 | defaultRouter.init(); 37 | 38 | export default defaultRouter.router; 39 | -------------------------------------------------------------------------------- /e-Commerce-Admin/app/routes/provider/Facebook.ts: -------------------------------------------------------------------------------- 1 | 2 | import * as passport from 'passport'; 3 | const FacebookStrategy = require('passport-facebook'); 4 | import userController from '../../controller/UserController'; 5 | /* eslint no-underscore-dangle: 0 */ 6 | 7 | passport.use(new FacebookStrategy( 8 | { 9 | clientID: global.configuration.facebook.client_id, 10 | clientSecret: global.configuration.facebook.client_secret, 11 | callbackURL: global.configuration.facebook.callback_url, 12 | profileFields: ['id', 'displayName', 'photos', 'email'], 13 | passReqToCallback: true, 14 | }, 15 | (req, accessToken, refreshToken, profile, done) => { 16 | const data = profile._json; 17 | if (!data.email) { 18 | data.email = 'ramnivas.yadav@srijan.net'; 19 | } 20 | userController.registerSocial({ 21 | provider: 'facebook', 22 | name: data.name, 23 | email: data.email, 24 | phone: '5436785432', 25 | meta: { 26 | provider: 'facebook', 27 | id: profile.id, 28 | token: accessToken, 29 | } 30 | }, (err, profileData) => { 31 | if (err) { 32 | done(err, null); 33 | } 34 | done(null, profileData); 35 | }); 36 | 37 | } 38 | )); 39 | 40 | const FacebookRoutes = { 41 | authenticate: () => passport.authenticate('facebook', { scope: ['email', 'public_profile', 'user_location'] }), 42 | callback: () => passport.authenticate('facebook', { 43 | failureRedirect: '/auth/failed' 44 | }) 45 | 46 | }; 47 | 48 | export default FacebookRoutes; 49 | -------------------------------------------------------------------------------- /e-Commerce-Admin/app/routes/provider/Google.ts: -------------------------------------------------------------------------------- 1 | 2 | export {} 3 | const passport = require('passport'); 4 | const GoogleStrategy = require('passport-google-oauth20').Strategy; 5 | import userController from '../../controller/UserController'; 6 | /* eslint no-underscore-dangle: 0 */ 7 | 8 | passport.use(new GoogleStrategy( 9 | { 10 | clientID: global.configuration.google.client_id, 11 | clientSecret: global.configuration.google.client_secret, 12 | callbackURL: `${global.configuration.url.API}/auth/callback/google`, 13 | profileFields: ['id', 'displayName', 'photos', 'email'] 14 | }, 15 | (accessToken, refreshToken, profile, done) => { 16 | const data = profile._json; 17 | console.log(data); 18 | userController.registerSocial({ 19 | provider: 'google', 20 | username: data.displayName, 21 | email: data.emails[0].value, 22 | phone: '5436785432', 23 | picture : data.image.url, 24 | meta: { 25 | provider: 'google', 26 | id: data.id, 27 | token: accessToken, 28 | } 29 | }, (err, profileData) => { 30 | if (err) { 31 | done(err, null); 32 | } 33 | done(null, profileData); 34 | }); 35 | } 36 | )); 37 | 38 | 39 | const GoogleRoutes = { 40 | authenticate: () => passport.authenticate('google', { scope: ['profile', 'email'] }), 41 | 42 | callback: () => passport.authenticate('google', { failureRedirect: '/login' }), 43 | function(req, res) { 44 | // Successful authentication, redirect home. 45 | res.redirect('/'); 46 | } 47 | }; 48 | 49 | 50 | export default GoogleRoutes; 51 | -------------------------------------------------------------------------------- /e-Commerce-Admin/app/routes/provider/Linkedin.ts: -------------------------------------------------------------------------------- 1 | export {} 2 | const passport = require('passport'); 3 | const LinkedInStrategy = require('passport-linkedin'); 4 | import userController from '../../controller/UserController'; 5 | /* eslint no-underscore-dangle: 0 */ 6 | passport.use(new LinkedInStrategy( 7 | { 8 | consumerKey: global.configuration.linkedin.client_id, 9 | consumerSecret: global.configuration.linkedin.client_secret, 10 | callbackURL: global.configuration.linkedin.callback_url, 11 | profileFields: ['id', 'first-name', 'last-name', 'email-address', 'headline'] 12 | }, 13 | ((token, tokenSecret, profile, done) => { 14 | console.log(profile); 15 | const data = profile._json; 16 | userController.registerSocial({ 17 | provider: 'linkedin', 18 | name: `${data.firstName} ${data.lastName}`, 19 | email: data.emailAddress, 20 | mobno: '5436785432', 21 | meta: { 22 | provider: 'linkedin', 23 | id: data.id, 24 | token, 25 | } 26 | }, (err, profileData) => { 27 | if (err) { 28 | done(err, null); 29 | } 30 | done(null, profileData); 31 | }); 32 | }) 33 | )); 34 | 35 | const LinkedinRoutes = { 36 | authenticate: () => passport.authenticate('linkedin', { scope: ['r_basicprofile', 'r_emailaddress'] }), 37 | callback: () => passport.authenticate('linkedin', { 38 | failureRedirect: '/auth/failed' 39 | }) 40 | 41 | }; 42 | 43 | export default LinkedinRoutes; 44 | -------------------------------------------------------------------------------- /e-Commerce-Admin/app/routes/provider/Locale.ts: -------------------------------------------------------------------------------- 1 | /* eslint prefer-destructuring:0 */ 2 | const passportModule = require('passport'); 3 | const LocalStrategy = require('passport-local'); 4 | import userController from '../../controller/UserController'; 5 | const User = require('../../models/user'); 6 | const helper = require('../../helper/bcrypt'); 7 | 8 | 9 | passportModule.use(new LocalStrategy( 10 | { 11 | usernameField: 'email', 12 | passwordField: 'password', 13 | passReqToCallback: true, 14 | session: false 15 | }, 16 | ((req, email, password, done) => { 17 | // write code here to find user if it exists in system 18 | User.find({ email }, (err, data) => { 19 | if (err) { 20 | return done(null, null); 21 | } else if (data.length === 0) { 22 | return done(null, null); 23 | } 24 | const flag = helper.comparePassword(password, data[0].password); 25 | if (!flag) { 26 | return done(null, null); 27 | } 28 | return done(null, data); 29 | }); 30 | }) 31 | )); 32 | 33 | const localRoutes = { 34 | authenticate() { 35 | return passportModule.authenticate('local', { session: false }); 36 | }, 37 | authenticate_with_callback: () => passportModule.authenticate('local', { 38 | successRedirect: '/auth/success', 39 | failureRedirect: '/auth/failed' 40 | }), 41 | }; 42 | 43 | 44 | export default localRoutes; 45 | -------------------------------------------------------------------------------- /e-Commerce-Admin/app/routes/provider/Twitter.ts: -------------------------------------------------------------------------------- 1 | 2 | const passport = require('passport'); 3 | const TwitterStrategy = require('passport-twitter').Strategy; 4 | import userController from '../../controller/UserController'; 5 | 6 | 7 | passport.use(new TwitterStrategy( 8 | { 9 | consumerKey: global.configuration.twitter.client_id, 10 | consumerSecret: global.configuration.twitter.client_secret, 11 | callbackURL: 'http://127.0.0.1:3005/auth/callback/twitter' 12 | }, 13 | (token, tokenSecret, profile, done) => { 14 | console.log('data>>>', profile); 15 | const data = profile; 16 | userController.registerSocial({ 17 | provider: 'twitter', 18 | username: data.username, 19 | email: data.email || 'raam.yaadav@gmail.com', 20 | mobno: '5436785432', 21 | meta: { 22 | provider: 'twitter', 23 | id: data.id, 24 | token, 25 | } 26 | }, (err, profileData) => { 27 | if (err) { 28 | done(err, null); 29 | } 30 | done(null, profileData); 31 | }); 32 | } 33 | )); 34 | 35 | const TwitterRoutes = { 36 | authenticate: () => passport.authenticate('twitter'), 37 | callback: () => passport.authenticate('twitter', { failureRedirect: '/auth/failed' }), 38 | function(req, res) { 39 | // Successful authentication, redirect home. 40 | res.redirect('/'); 41 | } 42 | }; 43 | 44 | 45 | export default TwitterRoutes; 46 | -------------------------------------------------------------------------------- /e-Commerce-Admin/app/transformer/userTransformer.ts: -------------------------------------------------------------------------------- 1 | 'use strict'; 2 | 3 | import _ from 'lodash'; 4 | // import ReviewTransformer from './ReviewTransformer'; 5 | 6 | 7 | let UserTransformer = { 8 | xx : (users) =>{ 9 | if ( Array.isArray(users) ) { 10 | let output = []; 11 | users.forEach(( user ) => { 12 | output.push( UserTransformer._transformUsers(user) ); 13 | }); 14 | return output; 15 | } 16 | else { 17 | return UserTransformer._transformUsers(users); 18 | } 19 | }, 20 | transform: (users) => { 21 | if (Array.isArray(users)) { 22 | let output = []; 23 | users.forEach((user) => { 24 | output.push(UserTransformer._transform(user)); 25 | }); 26 | return output; 27 | } 28 | else { 29 | return UserTransformer._transform(users); 30 | } 31 | }, 32 | calculateUsers: (users: any | null) => { 33 | if (Array.isArray(users)) { 34 | return { 35 | Users : users.length ? users.length : 100, 36 | vehicles : (users['vehicle'] ) ? users['vehicle'].length : 1000, 37 | cities :100 38 | } 39 | } 40 | }, 41 | 42 | _transform: (user) => { 43 | if (!user) { return {}; } 44 | let user_status = (user.status === 1) ? 'active' : 'disabled'; 45 | return { 46 | id: user._id, 47 | username : user.username, 48 | status: user_status, 49 | name: user.name, 50 | email: user.email, 51 | password: (user.password) ? true : false, 52 | phone: user.phone || '', 53 | gender: user.gender || '', 54 | birthday: user.birthday || '', 55 | type: user.type || 1, 56 | meta: user.meta || {}, 57 | social : user.social || [], 58 | phone_verified: user.phone_verified ? true : false, 59 | email_verified: user.email_verified ? true : false, 60 | profile_picture: user.profile_picture ? null : null // will fix later 61 | }; 62 | }, 63 | transformUsers: ( users ) => { 64 | if ( Array.isArray(users) ) { 65 | let output = []; 66 | users.forEach(( user ) => { 67 | output.push( UserTransformer._transformUsers(user) ); 68 | }); 69 | return output; 70 | } 71 | else { 72 | return UserTransformer._transformUsers(users); 73 | } 74 | }, 75 | _transformUsers: ( user ) => { 76 | if ( ! user ) { return {}; } 77 | let user_status = ( user.status === '1' ) ? 'active' : 'disabled'; 78 | const obj:any = {}; 79 | 80 | return Object.assign({}, { 81 | id: user._id, 82 | username : user.username, 83 | status: user_status, 84 | name: user.name, 85 | email_verified:user.email_verified, 86 | phone_verified :user.phone_verified, 87 | reviews : user.reviews, 88 | vehciles : user.vehciles, 89 | email: user.email, 90 | date : user.createdAt, 91 | type: user.type || 1, 92 | }, obj); 93 | } 94 | } 95 | 96 | export default UserTransformer; 97 | 98 | -------------------------------------------------------------------------------- /e-Commerce-Admin/app/types/global.d.ts: -------------------------------------------------------------------------------- 1 | declare namespace NodeJS { 2 | export interface Global { 3 | configuration: any 4 | } 5 | } -------------------------------------------------------------------------------- /e-Commerce-Admin/app/types/vendor.d.ts: -------------------------------------------------------------------------------- 1 | 2 | declare namespace NodeJS { 3 | export interface Global { 4 | configuration: any 5 | } 6 | } -------------------------------------------------------------------------------- /e-Commerce-Admin/env.sh: -------------------------------------------------------------------------------- 1 | # this environment vairables needs to be set in .env file in applciaiton root directory 2 | # copy this file as .env and add the appropriate values as per environment. 3 | # node & mysql 4 | export NODE_ENV="dev" 5 | export PORT="3002" 6 | export MONGOURL="mongodb://ms_commerce_mongo/blabla" 7 | export EXPRESS_SESSION_SECRET="######################" 8 | export F_CLIENTID="###################" 9 | export F_CLIENTSECRET="###########################" 10 | export F_CALLBACK="/auth/callback/facebook" 11 | export G_CLIENTID="@@@@@@@@@@@@@-###############.apps.%%%%%%%%%%%.com" 12 | export G_CLIENTSECRET="k##########@@@@@@@@@@@@Ub" 13 | export G_CALLBACK="/auth/callback/google" 14 | export L_CLIENTID="##################" 15 | export L_CLIENTSECRET="############" 16 | export L_CALLBACK="/auth/callback/linkedin" 17 | 18 | export T_CLIENTID="##################" 19 | export T_CLIENTSECRET="######################" 20 | export T_CALLBACK="/auth/callback/twitter" 21 | export FE_URL="localhost:3000" 22 | export API_KEY='XX0xxxxx-xX0X0XxXXxXxXXXxX0x' 23 | export SMTP_HOST='smtp.mandrillapp.com' 24 | export SMTP_PORT='587' 25 | export SMTP_USER='#############.net' 26 | export SMTP_PASSWORD='##############' 27 | export SID='XX0xxxxx-xX0X0XxXXxXxXXXxX0x' 28 | export TOKEN='XX0xxxxx-xX0X0XxXXxXxXXXxX0x' 29 | export PHONE='+9716156786' 30 | export FE='http://localhost:3000' 31 | export API='http://localhost:3005' 32 | export UPLOAD_DIR='uploads' 33 | export PROFILE_PICTURE_DIR='profile' 34 | export DOCUMENT_UPLOAD_DIR='documents' 35 | -------------------------------------------------------------------------------- /e-Commerce-Admin/package.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "e-commerce-hub", 3 | "version": "0.0.0", 4 | "private": true, 5 | "scripts": { 6 | "start": "cd dist && nodemon server.js", 7 | "prestart": "tsc && cp -r uploads dist/ && cp -r app/global dist/app/", 8 | "startdev": ". ./env.sh && cd dist && nodemon server.js", 9 | "clean": "rm -rf dist", 10 | "watch": "tsc", 11 | "copy": "cp -r uploads dist/", 12 | "test": ". ./env.sh && NODE_ENV=test && mocha ", 13 | "debug": ". ./env.sh && NODE_ENV=test && cd dist && nodemon --inspect=0.0.0.0:9230 server.js", 14 | "prestartdev": " npm run clean && tsc && npm run copy && npm run watch", 15 | "watchserver" : "tsc --watch", 16 | "dev": ". ./env.sh && ts-node server.ts", 17 | "start-tsc": ". ./env.sh && nodemon ./dist/server.js", 18 | "buildAndstart": ". ./env.sh && npm run build && npm run start" 19 | }, 20 | "dependencies": { 21 | "@types/express": "^4.11.1", 22 | "assert": "^1.4.1", 23 | "axios": "^0.18.0", 24 | "bcrypt-nodejs": "0.0.3", 25 | "bluebird": "^3.5.3", 26 | "body-parser": "^1.18.3", 27 | "cookie-parser": "^1.4.3", 28 | "cors": "^2.8.5", 29 | "dotenv": "^6.1.0", 30 | "email-templates": "^2.7.1", 31 | "express": "^4.16.4", 32 | "express-boom": "^2.0.0", 33 | "express-joi-validator": "^2.0.0", 34 | "express-session": "^1.15.6", 35 | "fast-csv": "^2.4.1", 36 | "hbs": "^4.0.1", 37 | "helmet": "^3.15.0", 38 | "joi": "^14.1.1", 39 | "jsonwebtoken": "^8.4.0", 40 | "mocha": "^5.2.0", 41 | "moment": "^2.22.2", 42 | "mongoose": "^4.5.9", 43 | "morgan": "^1.9.0", 44 | "multer": "^1.2.0", 45 | "nodemailer": "2.5.0", 46 | "nodemon": "^1.18.6", 47 | "passport": "0.3.2", 48 | "passport-facebook": "2.1.1", 49 | "passport-google-oauth": "1.0.0", 50 | "passport-google-oauth20": "^1.0.0", 51 | "passport-instagram": "1.0.0", 52 | "passport-linkedin": "^1.0.0", 53 | "passport-local": "1.0.0", 54 | "passport-twitter": "1.0.4", 55 | "pug": "2.0.0-beta6", 56 | "serve-favicon": "^2.5.0", 57 | "ts-lint": "^4.5.1", 58 | "ts-node": "^7.0.1", 59 | "twilio": "^2.11.1", 60 | "typescript": "^3.1.6", 61 | "uuid": "^3.3.2", 62 | "winston": "^2.4.2" 63 | }, 64 | "devDependencies": { 65 | "@types/async": "^2.0.45", 66 | "@types/bcrypt-nodejs": "^0.0.30", 67 | "@types/bluebird": "^3.5.20", 68 | "@types/body-parser": "^1.16.8", 69 | "@types/express": "^4.11.1", 70 | "@types/mongoose": "^4.7.34", 71 | "@types/morgan": "^1.7.35", 72 | "@types/node": "^9.6.39", 73 | "@types/nodemailer": "^4.3.4", 74 | "@types/passport": "^0.4.3", 75 | "babel-eslint": "^8.0.1", 76 | "eslint": "^4.19.1", 77 | "eslint-config-airbnb-base": "^12.1.0", 78 | "eslint-plugin-import": "^2.9.0", 79 | "eslint-plugin-node": "^5.2.1" 80 | } 81 | } 82 | -------------------------------------------------------------------------------- /e-Commerce-Admin/public/images/cinema.jpg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/tkssharma/e-CommerseHub/044bb67bf47ef1808633ca8f5c0c9812d5fcdd44/e-Commerce-Admin/public/images/cinema.jpg -------------------------------------------------------------------------------- /e-Commerce-Admin/public/javascripts/script.js: -------------------------------------------------------------------------------- 1 | document.addEventListener('DOMContentLoaded', () => { 2 | 3 | console.log('IronGenerator JS imported successfully!'); 4 | 5 | }, false); 6 | -------------------------------------------------------------------------------- /e-Commerce-Admin/public/style.scss: -------------------------------------------------------------------------------- 1 | body { 2 | padding: 50px; 3 | font: 14px "Lucida Grande", Helvetica, Arial, sans-serif; 4 | } 5 | 6 | a { 7 | color: #00B7FF; 8 | } 9 | 10 | .h1 { 11 | font-size: 40px; 12 | } 13 | -------------------------------------------------------------------------------- /e-Commerce-Admin/server.ts: -------------------------------------------------------------------------------- 1 | import * as http from 'http'; 2 | import * as debug from 'debug'; 3 | // After you declare "app" 4 | const env = process.env.NODE_ENV || 'dev' 5 | console.log(` using ${process.env.NODE_ENV} to run application`); 6 | global.configuration = require(`./app/config/environments/${env}`); 7 | import App from './express'; 8 | 9 | const port = (process.env.PORT); 10 | const logger = require('winston'); 11 | import mongoose from './app/lib/mongoose'; 12 | mongoose(); 13 | 14 | const server = http.createServer(App); 15 | server.listen(process.env.PORT); 16 | server.on('error', onError); 17 | server.on('listening', onListening); 18 | 19 | 20 | function onError(error: NodeJS.ErrnoException): void { 21 | if (error.syscall !== 'listen') throw error; 22 | let bind = (typeof port === 'string') ? 'Pipe ' + port : 'Port ' + port; 23 | switch(error.code) { 24 | case 'EACCES': 25 | console.error(`${bind} requires elevated privileges`); 26 | process.exit(1); 27 | break; 28 | case 'EADDRINUSE': 29 | console.error(`${bind} is already in use`); 30 | process.exit(1); 31 | break; 32 | default: 33 | throw error; 34 | } 35 | } 36 | 37 | const gracefulStopServer = function () { 38 | // Wait 10 secs for existing connection to close and then exit. 39 | setTimeout(() => { 40 | logger.info('Shutting down server'); 41 | process.exit(0); 42 | }, 1000); 43 | }; 44 | 45 | process.on('uncaughtException', (err) => { 46 | logger.error(err, 'Uncaught exception'); 47 | process.exit(1); 48 | }); 49 | 50 | process.on('unhandledRejection', (reason, promise) => { 51 | logger.error({ 52 | promise, 53 | reason 54 | }, 'unhandledRejection'); 55 | process.exit(1); 56 | }); 57 | 58 | process.on('SIGINT', gracefulStopServer); 59 | process.on('SIGTERM', gracefulStopServer); 60 | 61 | function onListening(): void { 62 | let addr = server.address(); 63 | let bind = (typeof addr === 'string') ? `pipe ${addr}` : `port ${addr.port}`; 64 | debug(`Listening on ${bind}`); 65 | } -------------------------------------------------------------------------------- /e-Commerce-Admin/tsconfig.json: -------------------------------------------------------------------------------- 1 | { 2 | "compilerOptions": { 3 | "module": "commonjs", 4 | "moduleResolution": "node", 5 | "pretty": true, 6 | "sourceMap": true, 7 | "target": "es6", 8 | "outDir": "./dist", 9 | "experimentalDecorators": false, 10 | "emitDecoratorMetadata": false, 11 | "skipDefaultLibCheck": false, 12 | "baseUrl": "./lib" 13 | }, 14 | "files" : [ 15 | "./app/types/vendor.d.ts" 16 | ], 17 | "include": [ 18 | "/**/*.ts" 19 | ], 20 | "exclude": [ 21 | "node_modules" 22 | ] 23 | } -------------------------------------------------------------------------------- /e-Commerce-Admin/tslint.json: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/tkssharma/e-CommerseHub/044bb67bf47ef1808633ca8f5c0c9812d5fcdd44/e-Commerce-Admin/tslint.json -------------------------------------------------------------------------------- /e-Commerce-Admin/uploads/documents/.gitkeep: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/tkssharma/e-CommerseHub/044bb67bf47ef1808633ca8f5c0c9812d5fcdd44/e-Commerce-Admin/uploads/documents/.gitkeep -------------------------------------------------------------------------------- /e-Commerce-Admin/uploads/profile/.gitkeep: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/tkssharma/e-CommerseHub/044bb67bf47ef1808633ca8f5c0c9812d5fcdd44/e-Commerce-Admin/uploads/profile/.gitkeep -------------------------------------------------------------------------------- /e-Commerce-Auth/Dockerfile: -------------------------------------------------------------------------------- 1 | FROM node:carbon 2 | 3 | # Create app directory 4 | WORKDIR /usr/src/app 5 | 6 | # Bundle app source 7 | COPY . . 8 | 9 | # npm install 10 | RUN npm install 11 | 12 | EXPOSE 3001 9201 13 | CMD [ "npm", "run", "watchserver" ] 14 | CMD [ "npm", "run", "startdev" ] 15 | -------------------------------------------------------------------------------- /e-Commerce-Auth/README.md: -------------------------------------------------------------------------------- 1 | # Application for e-commerce Hub 2 | 3 | 4 | REST API to support application features 5 | 6 | - Express as web framework with Typescript 7 | - Passport js for social authentication 8 | - Express CORS enabled 9 | - boom for error codes & Joi for Validation 10 | - Winston for logging and express minitor for monitoring 11 | - Mongoose as ODM driver 12 | - eslint validation extending airbnb styleguide 13 | - git hooks & CI/CD in place 14 | - Typescript based compilation tsc compiler 15 | - TDD in progress with Mocha 16 | - JWT based authentication 17 | - multiple Mongoose collection with referencing 18 | - payment gateway Integration 19 | - Heroku deployment 20 | - Mini e-commerce platform 21 | 22 | # Cart Application # 23 | 24 | "It's just simple application to provide REST APIs for mini e-commerce platform where individual can buy products and can pay the bills 25 | 26 | ``` 27 | # Application Execution 28 | ```javascript 29 | git clone repo 30 | npm install 31 | npm run startdev 32 | tsc -- watch 33 | ``` 34 | # Application configuration 35 | ```javascript 36 | env.sh need to be added locally 37 | export NODE_ENV="dev" 38 | export PORT="3005" 39 | export MONGOURL="mongodb://mongo/hello" 40 | export EXPRESS_SESSION_SECRET="************************" 41 | export F_CLIENTID="**************" 42 | export F_CLIENTSECRET="**********************" 43 | ``` 44 | 45 | # Application NPM Script 46 | ```javascript 47 | "start": "cd dist && nodemon server.js", 48 | "prestart": "tsc && cp -r uploads dist/ && cp -r app/global dist/app/", 49 | "clean" : "rm -rf dist", 50 | "copy" : "cp -r uploads dist/ && cp -r app/global dist/app/" 51 | ``` 52 | -------------------------------------------------------------------------------- /e-Commerce-Auth/app/config/email.ts: -------------------------------------------------------------------------------- 1 | 'use strict'; 2 | 3 | export default { 4 | 5 | global: { 6 | from: 'info@kpilibrary.com', 7 | }, 8 | welcome: { 9 | subject: 'Welcome to KPI Library', 10 | }, 11 | password_reset: { 12 | subject: 'KPI Library: Reset your password', 13 | }, 14 | event_booked_guest: { 15 | subject: 'KPI Library: Your Booking Has Been Confirmed', 16 | }, 17 | event_booked_host: { 18 | subject: 'KPI Library: Your Event Has Been Booked', 19 | }, 20 | event_booked_guests_notification: { 21 | subject: 'KPI Library: Your Booking Has Been Confirmed', 22 | }, 23 | message_received: { 24 | subject: 'KPI Library: New Message Received', 25 | }, 26 | guest_review_email: { 27 | subject: 'KPI Library: Event Completed', 28 | }, 29 | alacarte_booked_guest: { 30 | subject: 'KPI Library: Alacarte Booking Details', 31 | }, 32 | alacarte_booked_host: { 33 | subject: 'KPI Library: Your Alacarte Has Been Booked', 34 | } 35 | } 36 | 37 | -------------------------------------------------------------------------------- /e-Commerce-Auth/app/config/environments/dev.ts: -------------------------------------------------------------------------------- 1 | /* eslint quote-props: 0 */ 2 | export { } 3 | const configuration: any = {}; 4 | 5 | configuration.db = { 6 | mysql: { 7 | user: process.env.USERNAME, 8 | password: process.env.PASSWORD, 9 | database: process.env.DATABASE, 10 | host: process.env.HOST, 11 | connectTimeout: 100000 12 | }, 13 | mongo: { 14 | host: process.env.MONGO_HOST, 15 | port: process.env.MONGO_PORT, 16 | uername: process.env.MONGO_USERNAME, 17 | password: process.env.MONGO_PASSWORD, 18 | database: process.env.MONGO_DATABASE 19 | } 20 | }; 21 | 22 | configuration.URL = { 23 | frontEnd: process.env.FE_URL 24 | } 25 | configuration.facebook = { 26 | client_id: process.env.F_CLIENTID, 27 | client_secret: process.env.F_CLIENTSECRET, 28 | callback_url: process.env.F_CALLBACK 29 | }; 30 | configuration.google = { 31 | client_id: process.env.G_CLIENTID, 32 | client_secret: process.env.G_CLIENTSECRET, 33 | callback_url: process.env.G_CALLBACK 34 | }; 35 | configuration.linkedin = { 36 | client_id: process.env.L_CLIENTID, 37 | client_secret: process.env.L_CLIENTSECRET, 38 | callback_url: process.env.L_CALLBACK 39 | }; 40 | configuration.twitter = { 41 | client_id: process.env.T_CLIENTID, 42 | client_secret: process.env.T_CLIENTSECRET, 43 | callback_url: process.env.T_CALLBACK 44 | }; 45 | configuration.email = { 46 | apiKey: process.env.API_KEY, 47 | host: process.env.SMTP_HOST, 48 | port: process.env.SMTP_PORT, 49 | auth: { 50 | user: process.env.SMTP_USER, 51 | pass: process.env.SMTP_PASSWORD, 52 | } 53 | } 54 | configuration.twilio = { 55 | sid: process.env.SID, 56 | token: process.env.TOKEN, 57 | phone: process.env.PHONE, 58 | } 59 | configuration.url = { 60 | FE: process.env.FE, 61 | API: process.env.API, 62 | } 63 | configuration.uploadpath = { 64 | uploaddir: process.env.UPLOAD_DIR, 65 | profiledir: process.env.PROFILE_PICTURE_DIR 66 | } 67 | 68 | module.exports = configuration; -------------------------------------------------------------------------------- /e-Commerce-Auth/app/config/environments/qa.ts: -------------------------------------------------------------------------------- 1 | /* eslint quote-props: 0 */ 2 | export { } 3 | const configuration: any = {}; 4 | configuration.mongo = { 5 | url: process.env.MONGODB_URI || process.env.MONGOURL, 6 | }; 7 | configuration.URL = { 8 | frontEnd: process.env.FE_URL 9 | } 10 | configuration.facebook = { 11 | client_id: process.env.F_CLIENTID, 12 | client_secret: process.env.F_CLIENTSECRET, 13 | callback_url: process.env.F_CALLBACK 14 | }; 15 | configuration.google = { 16 | client_id: process.env.G_CLIENTID, 17 | client_secret: process.env.G_CLIENTSECRET, 18 | callback_url: process.env.G_CALLBACK 19 | }; 20 | configuration.linkedin = { 21 | client_id: process.env.L_CLIENTID, 22 | client_secret: process.env.L_CLIENTSECRET, 23 | callback_url: process.env.L_CALLBACK 24 | }; 25 | configuration.twitter = { 26 | client_id: process.env.T_CLIENTID, 27 | client_secret: process.env.T_CLIENTSECRET, 28 | callback_url: process.env.T_CALLBACK 29 | }; 30 | configuration.email = { 31 | apiKey: process.env.API_KEY, 32 | host: process.env.SMTP_HOST, 33 | port: process.env.SMTP_PORT, 34 | auth: { 35 | user: process.env.SMTP_USER, 36 | pass: process.env.SMTP_PASSWORD, 37 | } 38 | } 39 | configuration.twilio = { 40 | sid: process.env.SID, 41 | token: process.env.TOKEN, 42 | phone: process.env.PHONE, 43 | } 44 | configuration.url = { 45 | FE: process.env.FE, 46 | API: process.env.API, 47 | } 48 | configuration.uploadpath = { 49 | uploaddir: process.env.UPLOAD_DIR, 50 | profiledir: process.env.PROFILE_PICTURE_DIR 51 | } 52 | module.exports = configuration; 53 | -------------------------------------------------------------------------------- /e-Commerce-Auth/app/config/environments/test.ts: -------------------------------------------------------------------------------- 1 | /* eslint quote-props: 0 */ 2 | export { } 3 | const configuration: any = {}; 4 | configuration.mongo = { 5 | url: process.env.MONGODB_URI || process.env.MONGOURL, 6 | }; 7 | configuration.URL = { 8 | frontEnd: process.env.FE_URL 9 | } 10 | configuration.facebook = { 11 | client_id: process.env.F_CLIENTID, 12 | client_secret: process.env.F_CLIENTSECRET, 13 | callback_url: process.env.F_CALLBACK 14 | }; 15 | configuration.google = { 16 | client_id: process.env.G_CLIENTID, 17 | client_secret: process.env.G_CLIENTSECRET, 18 | callback_url: process.env.G_CALLBACK 19 | }; 20 | configuration.linkedin = { 21 | client_id: process.env.L_CLIENTID, 22 | client_secret: process.env.L_CLIENTSECRET, 23 | callback_url: process.env.L_CALLBACK 24 | }; 25 | configuration.twitter = { 26 | client_id: process.env.T_CLIENTID, 27 | client_secret: process.env.T_CLIENTSECRET, 28 | callback_url: process.env.T_CALLBACK 29 | }; 30 | configuration.email = { 31 | apiKey: process.env.API_KEY, 32 | host: process.env.SMTP_HOST, 33 | port: process.env.SMTP_PORT, 34 | auth: { 35 | user: process.env.SMTP_USER, 36 | pass: process.env.SMTP_PASSWORD, 37 | } 38 | } 39 | configuration.twilio = { 40 | sid: process.env.SID, 41 | token: process.env.TOKEN, 42 | phone: process.env.PHONE, 43 | } 44 | configuration.url = { 45 | FE: process.env.FE, 46 | API: process.env.API, 47 | } 48 | configuration.uploadpath = { 49 | uploaddir: process.env.UPLOAD_DIR, 50 | profiledir: process.env.PROFILE_PICTURE_DIR 51 | } 52 | module.exports = configuration; 53 | -------------------------------------------------------------------------------- /e-Commerce-Auth/app/events/notification.ts: -------------------------------------------------------------------------------- 1 | 2 | import Email from '../helper/email'; 3 | declare function require(name: string); 4 | 5 | const events = require('events'); 6 | const winston = require('winston'); 7 | // import Twillo from '../helper/twillo'; 8 | 9 | const eventEmitter = new events.EventEmitter(); 10 | eventEmitter.on('welcome', (user) => { 11 | winston.log('info', `sending welcome email to ${user.email}`); 12 | // Twillo.default_notification(user.phone, 'welcome') 13 | Email.welcome(user); 14 | }); 15 | eventEmitter.on('forgotPassword', (user, password, uuid) => { 16 | winston.log('info', `sending forgotPassword email to ${user.email}`); 17 | Email.password_reset(user, password); 18 | // Twillo.default_notification(user.phone, 'welcome') 19 | }); 20 | 21 | export default eventEmitter; 22 | -------------------------------------------------------------------------------- /e-Commerce-Auth/app/global/templates/emails/assets/images/logo.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/tkssharma/e-CommerseHub/044bb67bf47ef1808633ca8f5c0c9812d5fcdd44/e-Commerce-Auth/app/global/templates/emails/assets/images/logo.png -------------------------------------------------------------------------------- /e-Commerce-Auth/app/global/templates/response/index.ts: -------------------------------------------------------------------------------- 1 | class ResponseTemplate { 2 | static general(data) { 3 | return data; 4 | } 5 | static error(code, message, description) { 6 | return { 7 | statusCode: code || 400, 8 | message: message || 'some error occoured', 9 | description: description || 'error occoured on server, please try again after some time.' 10 | }; 11 | } 12 | static authError() { 13 | return this.error( 14 | 403, 15 | 'authentication error', 16 | 'no authentication token provided, please login first and provide the authentication token.' 17 | ); 18 | } 19 | static invalidAuthError() { 20 | return this.error( 21 | 403, 22 | 'authentication error', 23 | 'invalid Token provided, please login first and provide the authentication token.' 24 | ); 25 | } 26 | static emptyContent() { 27 | return this.general({ 28 | statusCode: 402, 29 | message: 'empty content found', 30 | description: 'you must provide valid data and it must not be empty.', 31 | helpful_links: ['http://stackoverflow.com/questions/18419428/what-is-the-minimum-valid-json'] 32 | }); 33 | } 34 | static invalidContentType() { 35 | return this.general({ 36 | statusCode: 400, 37 | message: 'invalid content type', 38 | description: 'you must specify content type and it must be application/json', 39 | helpful_links: ['http://stackoverflow.com/questions/477816/what-is-the-correct-json-content-type'] 40 | }); 41 | } 42 | static routeNotFound() { 43 | return this.error( 44 | 405, 45 | 'resource not found', 46 | 'the resource your tried to access doesn\'t exist or you dont have permissions to access it.' 47 | ); 48 | } 49 | static userNotFound() { 50 | return this.error( 51 | 400, 52 | 'user not found', 53 | "the user you're looking for doesn't exist or you dont have permissions to access it." 54 | ); 55 | } 56 | static updateErrorOccoured(error) { 57 | return this.error( 58 | 301, 59 | 'error occoured', 60 | error || 'error occoured while updating your data.' 61 | ); 62 | } 63 | static success(description, data=null) { 64 | return { 65 | statusCode: 200, 66 | message: 'success', 67 | description: description || 'data successfully saved.', 68 | ...data 69 | } 70 | } 71 | } 72 | 73 | export default ResponseTemplate; 74 | 75 | -------------------------------------------------------------------------------- /e-Commerce-Auth/app/helper/bcrypt.ts: -------------------------------------------------------------------------------- 1 | 2 | const bcrypt = require('bcrypt-nodejs'); 3 | const jwt = require('jsonwebtoken'); 4 | const {url} = global['configuration'] 5 | const {uploadpath} = global['configuration'] 6 | const path = require('path'); 7 | const fs = require('fs'); 8 | const helper = { 9 | generateSaltValue(password) { 10 | const salt = bcrypt.genSaltSync(); // enter number of rounds, default: 10 11 | const hash = bcrypt.hashSync(password, salt); 12 | return hash; 13 | }, 14 | comparePassword(userPassword, password ) { 15 | if (!userPassword.length || !( password && password.length > 0) ) { 16 | return false; 17 | } 18 | return bcrypt.compareSync(userPassword, password); 19 | }, 20 | authRedirectUrl( path ) { 21 | return `${url.FE}/#/auth/validate-token/${path}`; 22 | }, 23 | 24 | buildUserToken(data) { 25 | return { 26 | email: data.email, 27 | id:data._id, 28 | username: data.username ? data.username : data.email, 29 | hasPassword: data.password ? true : false, 30 | type : data.type || 1, 31 | picture : data.picture 32 | } 33 | }, 34 | resource( path ) { 35 | return `${url.API}${path}`; 36 | }, 37 | getFileExtension( file ) { 38 | let extensions = file.split('.'); 39 | if ( extensions.length === 1 ) { 40 | return 'jpg'; 41 | } else { 42 | return extensions.pop(); 43 | } 44 | }, 45 | avatarURL( filename ) { 46 | if ( filename.includes('://') ) { 47 | return filename; 48 | } 49 | return this.resource(`/${uploadpath.uploaddir}/${uploadpath.profiledir}/${filename}`); 50 | }, 51 | userDocumentURL( filename ) { 52 | if ( filename.includes('://') ) { 53 | return filename; 54 | } 55 | return this.resource(`/${uploadpath.uploaddir}/${uploadpath.documentdir}/${filename}`); 56 | }, 57 | randomString() { 58 | return Math.random().toString(36).substring(2, 7); 59 | }, 60 | deleteFile( type, filename ) { 61 | let location; 62 | if ( type === 'profile' ) { location = path.join( uploadpath.uploaddir, uploadpath.profiledir ) } 63 | else { location = uploadpath.uploaddir; } 64 | if (filename) { 65 | fs.unlink( path.join( location, filename ), () => { 66 | // in case we need to perform additional operations. 67 | }); 68 | } 69 | }, 70 | getPaymentMethodName( method ) { 71 | if ( method == 1 ) { return 'PayPal'; } 72 | else if ( method == 2 ) { return 'PayPal'; } 73 | else if ( method == 3 ) { return 'Instamojo'; } 74 | else { return 'Not Specified'; } 75 | }, 76 | getCurrency(currency) { 77 | let allCurrency = { 78 | 1: 'USD', 79 | 2: 'INR', 80 | }; 81 | 82 | if( allCurrency[currency] ) { 83 | return allCurrency[currency]; 84 | } else { 85 | return allCurrency[1]; 86 | } 87 | 88 | }, 89 | verificationCode() { 90 | let code = Math.floor((Math.random()*999999)+111111); 91 | return code; 92 | } 93 | }; 94 | 95 | export default helper; 96 | 97 | 98 | -------------------------------------------------------------------------------- /e-Commerce-Auth/app/helper/email.ts: -------------------------------------------------------------------------------- 1 | const path = require('path'); 2 | declare function require(name:string); 3 | 4 | const nodemailer = require('nodemailer'); 5 | const url = global['configuration'].url; 6 | const mailConfig = global['configuration'].email; 7 | 8 | import emailConfig from '../config/email'; 9 | import { EmailTemplate } from 'email-templates'; 10 | // const Twillo = require('./twillo'); 11 | 12 | const transport = nodemailer.createTransport({ 13 | host: 'smtp.gmail.com', 14 | port: 465, 15 | secure: true, 16 | auth: { 17 | // type: 'OAuth2', 18 | user: mailConfig.auth.user, 19 | pass: mailConfig.auth.pass 20 | }, 21 | }); 22 | const Email = { 23 | welcome(user) { 24 | if (user.email) { 25 | let templateDir = path.join('app/global/templates', 'emails', 'welcome-email'); 26 | let welcomeEmail = new EmailTemplate(templateDir); 27 | welcomeEmail.render({ user: user, activate_url: `${url.API}/auth/activate/${user.uuid}` }, (err, result) => { 28 | transport.sendMail( 29 | { 30 | from: emailConfig.global.from, 31 | to: user.email, 32 | subject: emailConfig.welcome.subject, 33 | html: result.html, 34 | }, (err, info) => { 35 | // some error occoured... 36 | console.log(err); 37 | } 38 | ); 39 | }); 40 | } 41 | }, 42 | password_reset(user, password) { 43 | if (user.email) { 44 | let templateDir = path.join('app/global/templates', 'emails', 'password-reset-email'); 45 | let passwordResetEmail = new EmailTemplate(templateDir); 46 | passwordResetEmail.render({ user: user, login_url: `${url.FE}/#/auth/login`, password: password }, (err, result) => { 47 | transport.sendMail( 48 | { 49 | from: emailConfig.global.from, 50 | to: user.email, 51 | subject: emailConfig.password_reset.subject, 52 | html: result.html, 53 | }, (err, info) => { 54 | // some error occoured... 55 | } 56 | ); 57 | }); 58 | } 59 | if (user.phone_verified) { 60 | // Twillo.password_reset_notification(user.phone); 61 | } 62 | 63 | }, 64 | }; 65 | export default Email; 66 | -------------------------------------------------------------------------------- /e-Commerce-Auth/app/helper/errorHandler.ts: -------------------------------------------------------------------------------- 1 | import * as winston from 'winston'; 2 | import ResponseTemplate from './responseTemplate'; 3 | /* eslint class-methods-use-this:0 */ 4 | const env = process.env.NODE_ENV; 5 | const onDevEnv = env === 'dev' || env === 'test' || env === 'local'; 6 | class errorHandler { 7 | public internalServerError(err, req, res, next) { 8 | winston.log('info',err); 9 | if (err.isBoom) { 10 | // Error From joi express validator 11 | const error = { 12 | message: err.output.payload.error, 13 | error: err.output.payload.message 14 | }; 15 | res.status(400).json(ResponseTemplate.BadRequestFromJoi(error)); 16 | } else { // internalServerError 17 | res.status(500).json({ 18 | success: false, 19 | message: err.message, 20 | error: (onDevEnv) ? err.stack : {} 21 | }); 22 | } 23 | } 24 | public PageNotFound(req, res, err) { 25 | res.status(404).json({ message: 'api not found' }); 26 | } 27 | } 28 | 29 | export default new errorHandler(); 30 | -------------------------------------------------------------------------------- /e-Commerce-Auth/app/helper/logger.ts: -------------------------------------------------------------------------------- 1 | const winston = require('winston'); 2 | 3 | // define the custom settings for each transport (file, console) 4 | const options = { 5 | console: { 6 | level: 'info', 7 | handleExceptions: true, 8 | json: true, 9 | colorize: true, 10 | prettyPrint: true, 11 | humanReadableUnhandledException: true 12 | } 13 | }; 14 | const logger = new winston.Logger({ 15 | transports: [ 16 | // new winston.transports.File(options.file), 17 | new winston 18 | .transports 19 | .Console(options.console), 20 | ], 21 | exceptionHandlers: [ 22 | // new winston.transports.File(options.errorLog) 23 | ], 24 | exitOnError: false, // do not exit on handled exceptions 25 | }); 26 | 27 | // create a stream object with a 'write' function that will be used by `morgan` 28 | logger.stream = { 29 | write(message, encoding) { 30 | logger.info(message); 31 | } 32 | }; 33 | export default logger; 34 | -------------------------------------------------------------------------------- /e-Commerce-Auth/app/helper/responseTemplate.ts: -------------------------------------------------------------------------------- 1 | /* istanbul ignore file */ 2 | const data: any = { 3 | general(data) { 4 | return data; 5 | }, 6 | successMessage(message) { 7 | return { 8 | success: true, 9 | message 10 | }; 11 | }, 12 | success(data, message) { 13 | return { 14 | success: true, 15 | message, 16 | data 17 | }; 18 | }, 19 | error(message, err, code= null) { 20 | return { 21 | success: false, 22 | message: message || 'some error occurred', 23 | error: err || 'error occurred on server, please try again after some time.' 24 | }; 25 | }, 26 | emptyContent() { 27 | return this.general({ 28 | message: 'empty content found', 29 | description: 'you must provide valid data and it must not be empty.', 30 | helpful_links: ['http://stackoverflow.com/questions/18419428/what-is-the-minimum-valid-json'] 31 | }); 32 | }, 33 | invalidContentType() { 34 | return this.general({ 35 | message: 'invalid content type', 36 | description: 'you must specify content type and it must be application/json', 37 | helpful_links: ['http://stackoverflow.com/questions/477816/what-is-the-correct-json-content-type'] 38 | }); 39 | }, 40 | BadRequestFromJoi(err) { 41 | return this.error( 42 | err.message, 43 | err.error 44 | ); 45 | }, 46 | userAlreadyExist(err) { 47 | return this.general({ 48 | success: false, 49 | message: 'user already registered in System', 50 | description: 'user already registered in System' 51 | }); 52 | }, 53 | userdoesNotExist(err) { 54 | return this.general({ 55 | success: false, 56 | message: err.message || 'user not registered in system', 57 | description: 'user account does not exist in system' 58 | }); 59 | }, 60 | commonAuthUserDataError() { 61 | return this.error( 62 | 'Authentication error', 63 | 'token verification failed, Please try again' 64 | ); 65 | }, 66 | tokenRequiredAuthError() { 67 | return this.error( 68 | 'Authentication error, Token is required in Header', 69 | 'token verification failed, Please try again' 70 | ); 71 | }, 72 | }; 73 | export default data; 74 | -------------------------------------------------------------------------------- /e-Commerce-Auth/app/helper/twillo.ts: -------------------------------------------------------------------------------- 1 | const twilioConfig = global['configuration'].twilio; 2 | // import twilio from 'twilio'; 3 | 4 | const twillo = require('twilio') 5 | const client = new twillo(twilioConfig.sid, twilioConfig.token); 6 | 7 | let TwilioHelper = { 8 | 9 | phone_verification(phone_number, code, callback) { 10 | client.sendMessage({ 11 | to: phone_number, 12 | from: twilioConfig.phone, 13 | body: `Hello from Bal Bla e-Commerce-Hub\nYour verification code is ${code}`, 14 | }, (err, message) => { 15 | callback(message); 16 | }); 17 | }, 18 | password_reset_notification(phone) { 19 | client.sendMessage({ 20 | to: phone, 21 | from: twilioConfig.phone, 22 | body: `Bla Bla\nYour password has been successfully reset.`, 23 | }, (err, message) => { 24 | // 25 | }); 26 | }, 27 | 28 | default_notification(phone, message) { 29 | console.log(client); 30 | client.sendMessage({ 31 | to: phone, 32 | from: twilioConfig.phone, 33 | body: `e-Commerce-Hub-TM\n${message}`, 34 | }, (err, message) => { 35 | // 36 | }); 37 | } 38 | } 39 | export default TwilioHelper; 40 | 41 | 42 | -------------------------------------------------------------------------------- /e-Commerce-Auth/app/lib/logger.ts: -------------------------------------------------------------------------------- 1 | const winston = require('winston'); 2 | const moment = require('moment'); 3 | 4 | // define the custom settings for each transport (file, console) 5 | const options = { 6 | console: { 7 | level: 'info', 8 | handleExceptions: true, 9 | json: true, 10 | colorize: true, 11 | timestamp() { 12 | return moment 13 | .utc() 14 | .format(); 15 | }, 16 | prettyPrint: true, 17 | humanReadableUnhandledException: true 18 | } 19 | }; 20 | const logger = new winston.Logger({ 21 | transports: [ 22 | // new winston.transports.File(options.file), 23 | new winston 24 | .transports 25 | .Console(options.console), 26 | ], 27 | exceptionHandlers: [ 28 | // new winston.transports.File(options.errorLog) 29 | ], 30 | exitOnError: false, // do not exit on handled exceptions 31 | }); 32 | 33 | // create a stream object with a 'write' function that will be used by `morgan` 34 | logger.stream = { 35 | write(message, encoding) { 36 | logger.info(message); 37 | } 38 | }; 39 | export default logger; 40 | -------------------------------------------------------------------------------- /e-Commerce-Auth/app/lib/mongoose.ts: -------------------------------------------------------------------------------- 1 | /* eslint no-undef: 0 */ 2 | /* eslint import/no-dynamic-require: 0 */ 3 | 4 | // Bring Mongoose into the app 5 | const env = process.env.NODE_ENV || 'dev'; 6 | const global1 = require(`../config/environments/${env}`); 7 | const mongoConfig = global1['db'].mongo; 8 | const mongoose = require('mongoose'); 9 | // Build the connection string 10 | let dbURI = `mongodb://${mongoConfig.host}:${mongoConfig.port}/${mongoConfig.database}`; 11 | //if(env === 'test' || env === 'dev'){ 12 | // dbURI += '?authSource=admin' 13 | //} 14 | const authOptions = { 15 | auth: { 16 | user: mongoConfig.uername, 17 | password: mongoConfig.password 18 | } 19 | }; 20 | const connect = () => { 21 | // Create the database connection 22 | mongoose.connect(dbURI, (err) => { 23 | if (err) { 24 | console.log(err.message); 25 | } else { 26 | console.log('Mongoose Connected! to Database'); 27 | } 28 | }); 29 | 30 | 31 | // CONNECTION EVENTS 32 | // When successfully connected 33 | mongoose.connection.on('connected', () => { 34 | console.log(`Mongoose default connection open to ${dbURI}`); 35 | }); 36 | 37 | // If the connection throws an error 38 | mongoose.connection.on('error', (err) => { 39 | console.log(`Mongoose default connection error: ${err}`); 40 | }); 41 | 42 | // When the connection is disconnected 43 | mongoose.connection.on('disconnected', () => { 44 | console.log('Mongoose default connection disconnected'); 45 | }); 46 | 47 | // If the Node process ends, close the Mongoose connection 48 | process.on('SIGINT', () => { 49 | mongoose.connection.close(() => { 50 | console.log('Mongoose default connection disconnected through app termination'); 51 | process.exit(0); 52 | }); 53 | }); 54 | } 55 | 56 | 57 | export default connect; -------------------------------------------------------------------------------- /e-Commerce-Auth/app/lib/requestValidator.ts: -------------------------------------------------------------------------------- 1 | const Joi = require('joi'); 2 | 3 | const validation = { 4 | loginUser : { 5 | body : { 6 | email: Joi.string().regex(/^[\w.]+@[\w]+?(\.[a-zA-Z]{2,3}){1,3}$/).required(), 7 | password: Joi.string().min(8).max(50).required() 8 | } 9 | }, 10 | createUser: { 11 | body: { 12 | username: Joi.string().min(4).max(50).required(), 13 | password: Joi.string().min(8).max(50).required(), 14 | email: Joi.string().regex(/^[\w.]+@[\w]+?(\.[a-zA-Z]{2,3}){1,3}$/).required(), 15 | verify_password: Joi.string().min(6).max(50).required() 16 | }, 17 | }, 18 | resetPassword: { 19 | body: { 20 | email: Joi.string().regex(/^[\w.]+@[\w]+?(\.[a-zA-Z]{2,3}){1,3}$/).required(), 21 | } 22 | } 23 | }; 24 | export default validation 25 | -------------------------------------------------------------------------------- /e-Commerce-Auth/app/middleware/authMiddleware.ts: -------------------------------------------------------------------------------- 1 | const jwt = require("jsonwebtoken"); 2 | import responseTemplate from '../helper/responseTemplate'; 3 | import User from '../models/user'; 4 | const validation: any = { 5 | validateToken(req, res, next) { 6 | // validatr token here is its valid here 7 | const token = req.headers.authorization; 8 | if (token) { 9 | jwt.verify(token, "secretkey", (err, data) => { 10 | if (err) { 11 | res.status(403).json(responseTemplate.commonAuthUserDataError()); 12 | } else { 13 | User.findById(data.id, (error, user) => { 14 | if(error){ 15 | res.status(403).json(responseTemplate.commonAuthUserDataError()); 16 | } 17 | user.hasPassword = user.password ? true : false; 18 | req.user = user; 19 | next(); 20 | }) 21 | } 22 | }); 23 | } else { 24 | res.status(403).json(responseTemplate.tokenRequiredAuthError()); 25 | } 26 | } 27 | }; 28 | 29 | export default validation; 30 | -------------------------------------------------------------------------------- /e-Commerce-Auth/app/middleware/requestValidator.ts: -------------------------------------------------------------------------------- 1 | 2 | module.exports = { 3 | validatePayload(req, res, next) { 4 | // validatr token here is its valid here 5 | const token = req.body; 6 | if ((req.method === 'POST' || req.method === 'PUT') && req.body !== null) { 7 | next(); 8 | } 9 | res.status(403).json({ message: 'payload is required for HTTP Post & Put ' }); 10 | } 11 | }; 12 | -------------------------------------------------------------------------------- /e-Commerce-Auth/app/models/plugin/plugin.ts: -------------------------------------------------------------------------------- 1 | const datePlugin = function timestamp(schema) { 2 | // Add the two fields to the schema 3 | schema.add({ 4 | createdAt: Date, 5 | updatedAt: Date 6 | }) 7 | 8 | // Create a pre-save hook 9 | schema.pre('save', function (next) { 10 | let now = Date.now() 11 | 12 | 13 | this.updatedAt = now 14 | // Set a value for createdAt only if it is null 15 | if (!this.createdAt) { 16 | this.createdAt = now 17 | } 18 | // Call the next function in the pre-save chain 19 | next() 20 | }) 21 | } 22 | 23 | export default datePlugin; -------------------------------------------------------------------------------- /e-Commerce-Auth/app/models/user.ts: -------------------------------------------------------------------------------- 1 | const mongoose = require('mongoose'); 2 | const bcrypt = require('bcrypt-nodejs'); 3 | import helper from '../helper/bcrypt'; 4 | const { Schema } = mongoose; 5 | import timestampPlugin from './plugin/plugin'; 6 | import Helper from '../../app/helper/bcrypt'; 7 | const userSchema = new Schema({ 8 | provider: { 9 | type: String 10 | }, 11 | username: { 12 | type: String 13 | }, 14 | password: { type: String }, 15 | email: { 16 | index: { unique: true }, 17 | type: String 18 | }, 19 | address: { 20 | type: String 21 | }, 22 | meta: mongoose.Schema.Types.Mixed, 23 | picture : mongoose.Schema.Types.Mixed, 24 | /* 25 | reviews : [{ 26 | type: mongoose.Schema.ObjectId, 27 | ref: 'Review' 28 | }], 29 | booking : [{ 30 | type: mongoose.Schema.ObjectId, 31 | ref: 'Booking' 32 | }], 33 | vehicles: [{ 34 | type: mongoose.Schema.ObjectId, 35 | ref: 'Vehicle' 36 | }], */ 37 | uuid: { 38 | type: String 39 | }, 40 | type: { 41 | type: String, 42 | default: 1 43 | }, 44 | status: { 45 | type: String, 46 | default: 1 47 | }, 48 | profile_picture: mongoose.Schema.Types.Mixed, 49 | phone: String, 50 | email_verified: Boolean, 51 | phone_verified: Boolean, 52 | social: mongoose.Schema.Types.Mixed, 53 | documents: [mongoose.Schema.Types.Mixed], 54 | gender: Number, // 1: Male, 2: Female, 3: Unspecified 55 | },{ toJSON: { virtuals: true } }); 56 | 57 | userSchema.set('toObject', { virtuals: true }); 58 | userSchema.set('toJSON', { virtuals: true }); 59 | userSchema.plugin(timestampPlugin) 60 | 61 | const User = mongoose.model('User', userSchema); 62 | 63 | export default User; 64 | 65 | -------------------------------------------------------------------------------- /e-Commerce-Auth/app/routes.ts: -------------------------------------------------------------------------------- 1 | /* eslint func-names: ["error", "never"] */ 2 | /* eslint prefer-destructuring: 0 */ 3 | import * as express from 'express'; 4 | const expressRouter= express.Router(); 5 | import authRoutes from './routes/routes'; 6 | import userRoutes from './routes/userRoutes'; 7 | import defaultRoutes from './routes/defaultRoutes'; 8 | import validAuthTokenMiddleware from './middleware/authMiddleware'; 9 | expressRouter.use('/', defaultRoutes); 10 | expressRouter.use('/uploads/', express.static('uploads')); 11 | expressRouter.use('/auth', authRoutes); 12 | expressRouter.use('/user',validAuthTokenMiddleware.validateToken, userRoutes); 13 | 14 | 15 | 16 | export default expressRouter; 17 | -------------------------------------------------------------------------------- /e-Commerce-Auth/app/routes/defaultRoutes.ts: -------------------------------------------------------------------------------- 1 | import * as express from "express"; 2 | const router = express.Router(); 3 | 4 | import { Router } from "express"; 5 | 6 | 7 | 8 | export class DefaultRouter { 9 | router: Router; 10 | 11 | /** 12 | * Initialize the HeroRouter 13 | */ 14 | constructor() { 15 | this.router = Router(); 16 | } 17 | /** 18 | * @api {POST} /auth/reset-password update password sent in Mail 19 | * @apiName resetPassword 20 | * @apiGroup Auth 21 | * @apiSuccess {String} code HTTP status code from API. 22 | * @apiSuccess {String} message Message from API. 23 | */ 24 | public sayHello(req, res) { 25 | res.status(200).json({ success: true, message: 'i am up and running node + mongo .. ⚡️⚡️⚡️⚡️⚡️⚡️⚡️' }); 26 | }; 27 | 28 | init() { 29 | this.router.get("/", this.sayHello); 30 | } 31 | } 32 | 33 | 34 | // Create the HeroRouter, and export its configured Express.Router 35 | const defaultRouter = new DefaultRouter(); 36 | defaultRouter.init(); 37 | 38 | export default defaultRouter.router; 39 | -------------------------------------------------------------------------------- /e-Commerce-Auth/app/routes/provider/Facebook.ts: -------------------------------------------------------------------------------- 1 | 2 | import * as passport from 'passport'; 3 | const FacebookStrategy = require('passport-facebook'); 4 | import userController from '../../controller/UserController'; 5 | /* eslint no-underscore-dangle: 0 */ 6 | 7 | passport.use(new FacebookStrategy( 8 | { 9 | clientID: global.configuration.facebook.client_id, 10 | clientSecret: global.configuration.facebook.client_secret, 11 | callbackURL: global.configuration.facebook.callback_url, 12 | profileFields: ['id', 'displayName', 'photos', 'email'], 13 | passReqToCallback: true, 14 | }, 15 | (req, accessToken, refreshToken, profile, done) => { 16 | const data = profile._json; 17 | if (!data.email) { 18 | data.email = 'ramnivas.yadav@srijan.net'; 19 | } 20 | userController.registerSocial({ 21 | provider: 'facebook', 22 | name: data.name, 23 | email: data.email, 24 | phone: '5436785432', 25 | meta: { 26 | provider: 'facebook', 27 | id: profile.id, 28 | token: accessToken, 29 | } 30 | }, (err, profileData) => { 31 | if (err) { 32 | done(err, null); 33 | } 34 | done(null, profileData); 35 | }); 36 | 37 | } 38 | )); 39 | 40 | const FacebookRoutes = { 41 | authenticate: () => passport.authenticate('facebook', { scope: ['email', 'public_profile', 'user_location'] }), 42 | callback: () => passport.authenticate('facebook', { 43 | failureRedirect: '/auth/failed' 44 | }) 45 | 46 | }; 47 | 48 | export default FacebookRoutes; 49 | -------------------------------------------------------------------------------- /e-Commerce-Auth/app/routes/provider/Google.ts: -------------------------------------------------------------------------------- 1 | 2 | export {} 3 | const passport = require('passport'); 4 | const GoogleStrategy = require('passport-google-oauth20').Strategy; 5 | import userController from '../../controller/UserController'; 6 | /* eslint no-underscore-dangle: 0 */ 7 | 8 | passport.use(new GoogleStrategy( 9 | { 10 | clientID: global.configuration.google.client_id, 11 | clientSecret: global.configuration.google.client_secret, 12 | callbackURL: `${global.configuration.url.API}/auth/callback/google`, 13 | profileFields: ['id', 'displayName', 'photos', 'email'] 14 | }, 15 | (accessToken, refreshToken, profile, done) => { 16 | const data = profile._json; 17 | console.log(data); 18 | userController.registerSocial({ 19 | provider: 'google', 20 | username: data.displayName, 21 | email: data.emails[0].value, 22 | phone: '5436785432', 23 | picture : data.image.url, 24 | meta: { 25 | provider: 'google', 26 | id: data.id, 27 | token: accessToken, 28 | } 29 | }, (err, profileData) => { 30 | if (err) { 31 | done(err, null); 32 | } 33 | done(null, profileData); 34 | }); 35 | } 36 | )); 37 | 38 | 39 | const GoogleRoutes = { 40 | authenticate: () => passport.authenticate('google', { scope: ['profile', 'email'] }), 41 | 42 | callback: () => passport.authenticate('google', { failureRedirect: '/login' }), 43 | function(req, res) { 44 | // Successful authentication, redirect home. 45 | res.redirect('/'); 46 | } 47 | }; 48 | 49 | 50 | export default GoogleRoutes; 51 | -------------------------------------------------------------------------------- /e-Commerce-Auth/app/routes/provider/Linkedin.ts: -------------------------------------------------------------------------------- 1 | export {} 2 | const passport = require('passport'); 3 | const LinkedInStrategy = require('passport-linkedin'); 4 | import userController from '../../controller/UserController'; 5 | /* eslint no-underscore-dangle: 0 */ 6 | passport.use(new LinkedInStrategy( 7 | { 8 | consumerKey: global.configuration.linkedin.client_id, 9 | consumerSecret: global.configuration.linkedin.client_secret, 10 | callbackURL: global.configuration.linkedin.callback_url, 11 | profileFields: ['id', 'first-name', 'last-name', 'email-address', 'headline'] 12 | }, 13 | ((token, tokenSecret, profile, done) => { 14 | console.log(profile); 15 | const data = profile._json; 16 | userController.registerSocial({ 17 | provider: 'linkedin', 18 | name: `${data.firstName} ${data.lastName}`, 19 | email: data.emailAddress, 20 | mobno: '5436785432', 21 | meta: { 22 | provider: 'linkedin', 23 | id: data.id, 24 | token, 25 | } 26 | }, (err, profileData) => { 27 | if (err) { 28 | done(err, null); 29 | } 30 | done(null, profileData); 31 | }); 32 | }) 33 | )); 34 | 35 | const LinkedinRoutes = { 36 | authenticate: () => passport.authenticate('linkedin', { scope: ['r_basicprofile', 'r_emailaddress'] }), 37 | callback: () => passport.authenticate('linkedin', { 38 | failureRedirect: '/auth/failed' 39 | }) 40 | 41 | }; 42 | 43 | export default LinkedinRoutes; 44 | -------------------------------------------------------------------------------- /e-Commerce-Auth/app/routes/provider/Locale.ts: -------------------------------------------------------------------------------- 1 | /* eslint prefer-destructuring:0 */ 2 | const passportModule = require('passport'); 3 | const LocalStrategy = require('passport-local'); 4 | import userController from '../../controller/UserController'; 5 | const User = require('../../models/user'); 6 | const helper = require('../../helper/bcrypt'); 7 | 8 | 9 | passportModule.use(new LocalStrategy( 10 | { 11 | usernameField: 'email', 12 | passwordField: 'password', 13 | passReqToCallback: true, 14 | session: false 15 | }, 16 | ((req, email, password, done) => { 17 | // write code here to find user if it exists in system 18 | User.find({ email }, (err, data) => { 19 | if (err) { 20 | return done(null, null); 21 | } else if (data.length === 0) { 22 | return done(null, null); 23 | } 24 | const flag = helper.comparePassword(password, data[0].password); 25 | if (!flag) { 26 | return done(null, null); 27 | } 28 | return done(null, data); 29 | }); 30 | }) 31 | )); 32 | 33 | const localRoutes = { 34 | authenticate() { 35 | return passportModule.authenticate('local', { session: false }); 36 | }, 37 | authenticate_with_callback: () => passportModule.authenticate('local', { 38 | successRedirect: '/auth/success', 39 | failureRedirect: '/auth/failed' 40 | }), 41 | }; 42 | 43 | 44 | export default localRoutes; 45 | -------------------------------------------------------------------------------- /e-Commerce-Auth/app/routes/provider/Twitter.ts: -------------------------------------------------------------------------------- 1 | 2 | const passport = require('passport'); 3 | const TwitterStrategy = require('passport-twitter').Strategy; 4 | import userController from '../../controller/UserController'; 5 | 6 | 7 | passport.use(new TwitterStrategy( 8 | { 9 | consumerKey: global.configuration.twitter.client_id, 10 | consumerSecret: global.configuration.twitter.client_secret, 11 | callbackURL: 'http://127.0.0.1:3005/auth/callback/twitter' 12 | }, 13 | (token, tokenSecret, profile, done) => { 14 | console.log('data>>>', profile); 15 | const data = profile; 16 | userController.registerSocial({ 17 | provider: 'twitter', 18 | username: data.username, 19 | email: data.email || 'raam.yaadav@gmail.com', 20 | mobno: '5436785432', 21 | meta: { 22 | provider: 'twitter', 23 | id: data.id, 24 | token, 25 | } 26 | }, (err, profileData) => { 27 | if (err) { 28 | done(err, null); 29 | } 30 | done(null, profileData); 31 | }); 32 | } 33 | )); 34 | 35 | const TwitterRoutes = { 36 | authenticate: () => passport.authenticate('twitter'), 37 | callback: () => passport.authenticate('twitter', { failureRedirect: '/auth/failed' }), 38 | function(req, res) { 39 | // Successful authentication, redirect home. 40 | res.redirect('/'); 41 | } 42 | }; 43 | 44 | 45 | export default TwitterRoutes; 46 | -------------------------------------------------------------------------------- /e-Commerce-Auth/app/transformer/userTransformer.ts: -------------------------------------------------------------------------------- 1 | 'use strict'; 2 | 3 | import _ from 'lodash'; 4 | // import ReviewTransformer from './ReviewTransformer'; 5 | 6 | 7 | let UserTransformer = { 8 | xx : (users) =>{ 9 | if ( Array.isArray(users) ) { 10 | let output = []; 11 | users.forEach(( user ) => { 12 | output.push( UserTransformer._transformUsers(user) ); 13 | }); 14 | return output; 15 | } 16 | else { 17 | return UserTransformer._transformUsers(users); 18 | } 19 | }, 20 | transform: (users) => { 21 | if (Array.isArray(users)) { 22 | let output = []; 23 | users.forEach((user) => { 24 | output.push(UserTransformer._transform(user)); 25 | }); 26 | return output; 27 | } 28 | else { 29 | return UserTransformer._transform(users); 30 | } 31 | }, 32 | calculateUsers: (users: any | null) => { 33 | if (Array.isArray(users)) { 34 | return { 35 | Users : users.length ? users.length : 100, 36 | vehicles : (users['vehicle'] ) ? users['vehicle'].length : 1000, 37 | cities :100 38 | } 39 | } 40 | }, 41 | 42 | _transform: (user) => { 43 | if (!user) { return {}; } 44 | let user_status = (user.status === 1) ? 'active' : 'disabled'; 45 | return { 46 | id: user._id, 47 | username : user.username, 48 | status: user_status, 49 | name: user.name, 50 | email: user.email, 51 | password: (user.password) ? true : false, 52 | phone: user.phone || '', 53 | gender: user.gender || '', 54 | birthday: user.birthday || '', 55 | type: user.type || 1, 56 | meta: user.meta || {}, 57 | social : user.social || [], 58 | phone_verified: user.phone_verified ? true : false, 59 | email_verified: user.email_verified ? true : false, 60 | profile_picture: user.profile_picture ? null : null // will fix later 61 | }; 62 | }, 63 | transformUsers: ( users ) => { 64 | if ( Array.isArray(users) ) { 65 | let output = []; 66 | users.forEach(( user ) => { 67 | output.push( UserTransformer._transformUsers(user) ); 68 | }); 69 | return output; 70 | } 71 | else { 72 | return UserTransformer._transformUsers(users); 73 | } 74 | }, 75 | _transformUsers: ( user ) => { 76 | if ( ! user ) { return {}; } 77 | let user_status = ( user.status === '1' ) ? 'active' : 'disabled'; 78 | const obj:any = {}; 79 | 80 | return Object.assign({}, { 81 | id: user._id, 82 | username : user.username, 83 | status: user_status, 84 | name: user.name, 85 | email_verified:user.email_verified, 86 | phone_verified :user.phone_verified, 87 | reviews : user.reviews, 88 | vehciles : user.vehciles, 89 | email: user.email, 90 | date : user.createdAt, 91 | type: user.type || 1, 92 | }, obj); 93 | } 94 | } 95 | 96 | export default UserTransformer; 97 | 98 | -------------------------------------------------------------------------------- /e-Commerce-Auth/app/types/global.d.ts: -------------------------------------------------------------------------------- 1 | declare namespace NodeJS { 2 | export interface Global { 3 | configuration: any 4 | } 5 | } -------------------------------------------------------------------------------- /e-Commerce-Auth/app/types/vendor.d.ts: -------------------------------------------------------------------------------- 1 | 2 | declare namespace NodeJS { 3 | export interface Global { 4 | configuration: any 5 | } 6 | } -------------------------------------------------------------------------------- /e-Commerce-Auth/env.sh: -------------------------------------------------------------------------------- 1 | # this environment vairables needs to be set in .env file in applciaiton root directory 2 | # copy this file as .env and add the appropriate values as per environment. 3 | # node & mysql 4 | export NODE_ENV="dev" 5 | export PORT="3001" 6 | export MONGO_HOST="ms_commerce_mongo" 7 | export MONGO_PORT="27017" 8 | export MONGO_USERNAME="root" 9 | export MONGO_PASSWORD="root" 10 | export MONGO_DATABASE="ecommerce" 11 | 12 | export EXPRESS_SESSION_SECRET="######################" 13 | export F_CLIENTID="###################" 14 | export F_CLIENTSECRET="###########################" 15 | export F_CALLBACK="/auth/callback/facebook" 16 | export G_CLIENTID="@@@@@@@@@@@@@-###############.apps.%%%%%%%%%%%.com" 17 | export G_CLIENTSECRET="k##########@@@@@@@@@@@@Ub" 18 | export G_CALLBACK="/auth/callback/google" 19 | export L_CLIENTID="##################" 20 | export L_CLIENTSECRET="############" 21 | export L_CALLBACK="/auth/callback/linkedin" 22 | 23 | export T_CLIENTID="##################" 24 | export T_CLIENTSECRET="######################" 25 | export T_CALLBACK="/auth/callback/twitter" 26 | export FE_URL="localhost:3000" 27 | export API_KEY='XX0xxxxx-xX0X0XxXXxXxXXXxX0x' 28 | export SMTP_HOST='smtp.mandrillapp.com' 29 | export SMTP_PORT='587' 30 | export SMTP_USER='#############.net' 31 | export SMTP_PASSWORD='##############' 32 | export SID='XX0xxxxx-xX0X0XxXXxXxXXXxX0x' 33 | export TOKEN='XX0xxxxx-xX0X0XxXXxXxXXXxX0x' 34 | export PHONE='+9716156786' 35 | export FE='http://localhost:3000' 36 | export API='http://localhost:3005' 37 | export UPLOAD_DIR='uploads' 38 | export PROFILE_PICTURE_DIR='profile' 39 | export DOCUMENT_UPLOAD_DIR='documents' -------------------------------------------------------------------------------- /e-Commerce-Auth/package.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "e-commerce-hub", 3 | "version": "0.0.0", 4 | "private": true, 5 | "scripts": { 6 | "start": "cd dist && nodemon server.js", 7 | "prestart": "tsc && cp -r uploads dist/ && cp -r app/global dist/app/", 8 | "startdev": ". ./env.sh && cd dist && nodemon --inspect=0.0.0.0:9201 server.js && tsc --watch", 9 | "clean": "rm -rf dist", 10 | "watch": "tsc", 11 | "copy": "cp -r uploads dist/ && cp -r app/global dist/app/", 12 | "test": ". ./env.sh && NODE_ENV=test && mocha ", 13 | "debug": ". ./env.sh && NODE_ENV=test && cd dist && nodemon server.js", 14 | "prestartdev": " npm run clean && tsc && npm run copy && npm run watch", 15 | "poststartdev": "tsc --watch", 16 | "watchserver": "tsc --watch", 17 | "dev": ". ./env.sh && ts-node server.ts", 18 | "start-tsc": ". ./env.sh && nodemon ./dist/server.js", 19 | "buildAndstart": ". ./env.sh && npm run build && npm run start" 20 | }, 21 | "dependencies": { 22 | "@types/express": "^4.11.1", 23 | "assert": "^1.4.1", 24 | "axios": "^0.18.0", 25 | "bcrypt-nodejs": "0.0.3", 26 | "bluebird": "^3.5.3", 27 | "body-parser": "^1.18.3", 28 | "cookie-parser": "^1.4.3", 29 | "cors": "^2.8.5", 30 | "dotenv": "^6.1.0", 31 | "email-templates": "^2.7.1", 32 | "express": "^4.16.4", 33 | "express-boom": "^2.0.0", 34 | "express-joi-validator": "^2.0.0", 35 | "express-session": "^1.15.6", 36 | "fast-csv": "^2.4.1", 37 | "hbs": "^4.0.1", 38 | "helmet": "^3.15.0", 39 | "joi": "^14.1.1", 40 | "jsonwebtoken": "^8.4.0", 41 | "mocha": "^5.2.0", 42 | "moment": "^2.22.2", 43 | "mongoose": "^4.5.9", 44 | "morgan": "^1.9.0", 45 | "multer": "^1.2.0", 46 | "nodemailer": "2.5.0", 47 | "nodemon": "^1.18.6", 48 | "passport": "0.3.2", 49 | "passport-facebook": "2.1.1", 50 | "passport-google-oauth": "1.0.0", 51 | "passport-google-oauth20": "^1.0.0", 52 | "passport-instagram": "1.0.0", 53 | "passport-linkedin": "^1.0.0", 54 | "passport-local": "1.0.0", 55 | "passport-twitter": "1.0.4", 56 | "pug": "2.0.0-beta6", 57 | "serve-favicon": "^2.5.0", 58 | "ts-lint": "^4.5.1", 59 | "ts-node": "^7.0.1", 60 | "twilio": "^2.11.1", 61 | "typescript": "^3.1.6", 62 | "uuid": "^3.3.2", 63 | "winston": "^2.4.2" 64 | }, 65 | "devDependencies": { 66 | "@types/async": "^2.0.45", 67 | "@types/bcrypt-nodejs": "^0.0.30", 68 | "@types/bluebird": "^3.5.20", 69 | "@types/body-parser": "^1.16.8", 70 | "@types/express": "^4.11.1", 71 | "@types/mongoose": "^4.7.34", 72 | "@types/morgan": "^1.7.35", 73 | "@types/node": "^9.6.39", 74 | "@types/nodemailer": "^4.3.4", 75 | "@types/passport": "^0.4.3", 76 | "babel-eslint": "^8.0.1", 77 | "eslint": "^4.19.1", 78 | "eslint-config-airbnb-base": "^12.1.0", 79 | "eslint-plugin-import": "^2.9.0", 80 | "eslint-plugin-node": "^5.2.1" 81 | } 82 | } 83 | -------------------------------------------------------------------------------- /e-Commerce-Auth/public/images/cinema.jpg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/tkssharma/e-CommerseHub/044bb67bf47ef1808633ca8f5c0c9812d5fcdd44/e-Commerce-Auth/public/images/cinema.jpg -------------------------------------------------------------------------------- /e-Commerce-Auth/public/javascripts/script.js: -------------------------------------------------------------------------------- 1 | document.addEventListener('DOMContentLoaded', () => { 2 | 3 | console.log('IronGenerator JS imported successfully!'); 4 | 5 | }, false); 6 | -------------------------------------------------------------------------------- /e-Commerce-Auth/public/style.scss: -------------------------------------------------------------------------------- 1 | body { 2 | padding: 50px; 3 | font: 14px "Lucida Grande", Helvetica, Arial, sans-serif; 4 | } 5 | 6 | a { 7 | color: #00B7FF; 8 | } 9 | 10 | .h1 { 11 | font-size: 40px; 12 | } 13 | -------------------------------------------------------------------------------- /e-Commerce-Auth/server.ts: -------------------------------------------------------------------------------- 1 | import * as http from 'http'; 2 | import * as debug from 'debug'; 3 | // After you declare "app" 4 | const env = process.env.NODE_ENV || 'dev' 5 | console.log(` using ${process.env.NODE_ENV} to run application`); 6 | global.configuration = require(`./app/config/environments/${env}`); 7 | import App from './express'; 8 | 9 | const port = (process.env.PORT); 10 | const logger = require('winston'); 11 | import mongoose from './app/lib/mongoose'; 12 | mongoose(); 13 | const server = http.createServer(App); 14 | server.listen(process.env.PORT); 15 | server.on('error', onError); 16 | server.on('listening', onListening); 17 | 18 | 19 | function onError(error: NodeJS.ErrnoException): void { 20 | if (error.syscall !== 'listen') throw error; 21 | let bind = (typeof port === 'string') ? 'Pipe ' + port : 'Port ' + port; 22 | switch(error.code) { 23 | case 'EACCES': 24 | console.error(`${bind} requires elevated privileges`); 25 | process.exit(1); 26 | break; 27 | case 'EADDRINUSE': 28 | console.error(`${bind} is already in use`); 29 | process.exit(1); 30 | break; 31 | default: 32 | throw error; 33 | } 34 | } 35 | 36 | const gracefulStopServer = function () { 37 | // Wait 10 secs for existing connection to close and then exit. 38 | setTimeout(() => { 39 | logger.info('Shutting down server'); 40 | process.exit(0); 41 | }, 1000); 42 | }; 43 | 44 | process.on('uncaughtException', (err) => { 45 | logger.error(err, 'Uncaught exception'); 46 | process.exit(1); 47 | }); 48 | 49 | process.on('unhandledRejection', (reason, promise) => { 50 | logger.error({ 51 | promise, 52 | reason 53 | }, 'unhandledRejection'); 54 | process.exit(1); 55 | }); 56 | 57 | process.on('SIGINT', gracefulStopServer); 58 | process.on('SIGTERM', gracefulStopServer); 59 | 60 | function onListening(): void { 61 | let addr = server.address(); 62 | let bind = (typeof addr === 'string') ? `pipe ${addr}` : `port ${addr.port}`; 63 | console.log(`Listening on ${bind}`); 64 | } -------------------------------------------------------------------------------- /e-Commerce-Auth/tsconfig.json: -------------------------------------------------------------------------------- 1 | { 2 | "compilerOptions": { 3 | "module": "commonjs", 4 | "moduleResolution": "node", 5 | "pretty": true, 6 | "sourceMap": true, 7 | "target": "es6", 8 | "outDir": "./dist", 9 | "experimentalDecorators": false, 10 | "emitDecoratorMetadata": false, 11 | "skipDefaultLibCheck": false, 12 | "baseUrl": "./lib" 13 | }, 14 | "files" : [ 15 | "./app/types/vendor.d.ts" 16 | ], 17 | "include": [ 18 | "/**/*.ts" 19 | ], 20 | "exclude": [ 21 | "node_modules" 22 | ] 23 | } -------------------------------------------------------------------------------- /e-Commerce-Auth/tslint.json: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/tkssharma/e-CommerseHub/044bb67bf47ef1808633ca8f5c0c9812d5fcdd44/e-Commerce-Auth/tslint.json -------------------------------------------------------------------------------- /e-Commerce-Auth/uploads/documents/.gitkeep: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/tkssharma/e-CommerseHub/044bb67bf47ef1808633ca8f5c0c9812d5fcdd44/e-Commerce-Auth/uploads/documents/.gitkeep -------------------------------------------------------------------------------- /e-Commerce-Auth/uploads/profile/.gitkeep: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/tkssharma/e-CommerseHub/044bb67bf47ef1808633ca8f5c0c9812d5fcdd44/e-Commerce-Auth/uploads/profile/.gitkeep -------------------------------------------------------------------------------- /e-Commerce-Cart/.sequelizerc: -------------------------------------------------------------------------------- 1 | const path = require('path'); 2 | 3 | module.exports = { 4 | 'config': path.resolve('config', 'config.js') 5 | } -------------------------------------------------------------------------------- /e-Commerce-Cart/Dockerfile: -------------------------------------------------------------------------------- 1 | FROM node:carbon 2 | 3 | # Create app directory 4 | WORKDIR /usr/src/app 5 | 6 | # Bundle app source 7 | COPY . . 8 | 9 | # npm install 10 | RUN npm install 11 | # Run npm install --global grpc --unsafe-perm 12 | 13 | EXPOSE 3004 9204 14 | CMD [ "npm", "run", "watchserver" ] 15 | CMD [ "npm", "run", "startdev" ] 16 | 17 | -------------------------------------------------------------------------------- /e-Commerce-Cart/README.md: -------------------------------------------------------------------------------- 1 | # Application for e-commerce Hub 2 | 3 | 4 | REST API to support application features 5 | 6 | - Express as web framework with Typescript 7 | - Passport js for social authentication 8 | - Express CORS enabled 9 | - boom for error codes & Joi for Validation 10 | - Winston for logging and express minitor for monitoring 11 | - Mongoose as ODM driver 12 | - eslint validation extending airbnb styleguide 13 | - git hooks & CI/CD in place 14 | - Typescript based compilation tsc compiler 15 | - TDD in progress with Mocha 16 | - JWT based authentication 17 | - multiple Mongoose collection with referencing 18 | - payment gateway Integration 19 | - Heroku deployment 20 | - Mini e-commerce platform 21 | 22 | # Cart Application # 23 | 24 | "It's just simple application to provide REST APIs for mini e-commerce platform where individual can buy products and can pay the bills 25 | 26 | ``` 27 | # Application Execution 28 | ```javascript 29 | git clone repo 30 | npm install 31 | npm run startdev 32 | tsc -- watch 33 | ``` 34 | # Application configuration 35 | ```javascript 36 | env.sh need to be added locally 37 | export NODE_ENV="dev" 38 | export PORT="3005" 39 | export MONGOURL="mongodb://mongo/hello" 40 | export EXPRESS_SESSION_SECRET="************************" 41 | export F_CLIENTID="**************" 42 | export F_CLIENTSECRET="**********************" 43 | ``` 44 | 45 | # Application NPM Script 46 | ```javascript 47 | "start": "cd dist && nodemon server.js", 48 | "prestart": "tsc && cp -r uploads dist/ && cp -r app/global dist/app/", 49 | "clean" : "rm -rf dist", 50 | "copy" : "cp -r uploads dist/ && cp -r app/global dist/app/" 51 | ``` 52 | -------------------------------------------------------------------------------- /e-Commerce-Cart/app/config/environments/dev.ts: -------------------------------------------------------------------------------- 1 | /* eslint quote-props: 0 */ 2 | export { } 3 | const configuration: any = {}; 4 | configuration.mongo = { 5 | url: process.env.MONGODB_URI || process.env.MONGOURL, 6 | }; 7 | configuration.URL = { 8 | frontEnd: process.env.FE_URL 9 | } 10 | configuration.db = { 11 | user: process.env.USERNAME, 12 | password: process.env.PASSWORD, 13 | database: process.env.DATABASE, 14 | host: process.env.HOST, 15 | connectTimeout: 80000, 16 | }; 17 | configuration.facebook = { 18 | client_id: process.env.F_CLIENTID, 19 | client_secret: process.env.F_CLIENTSECRET, 20 | callback_url: process.env.F_CALLBACK 21 | }; 22 | configuration.google = { 23 | client_id: process.env.G_CLIENTID, 24 | client_secret: process.env.G_CLIENTSECRET, 25 | callback_url: process.env.G_CALLBACK 26 | }; 27 | configuration.linkedin = { 28 | client_id: process.env.L_CLIENTID, 29 | client_secret: process.env.L_CLIENTSECRET, 30 | callback_url: process.env.L_CALLBACK 31 | }; 32 | configuration.twitter = { 33 | client_id: process.env.T_CLIENTID, 34 | client_secret: process.env.T_CLIENTSECRET, 35 | callback_url: process.env.T_CALLBACK 36 | }; 37 | configuration.email = { 38 | apiKey: process.env.API_KEY, 39 | host: process.env.SMTP_HOST, 40 | port: process.env.SMTP_PORT, 41 | auth: { 42 | user: process.env.SMTP_USER, 43 | pass: process.env.SMTP_PASSWORD, 44 | } 45 | } 46 | configuration.twilio = { 47 | sid: process.env.SID, 48 | token: process.env.TOKEN, 49 | phone: process.env.PHONE, 50 | } 51 | configuration.url = { 52 | FE: process.env.FE, 53 | API: process.env.API, 54 | } 55 | configuration.uploadpath = { 56 | uploaddir: process.env.UPLOAD_DIR, 57 | profiledir: process.env.PROFILE_PICTURE_DIR 58 | } 59 | configuration.logLevel ='info'; 60 | 61 | module.exports = configuration; -------------------------------------------------------------------------------- /e-Commerce-Cart/app/config/environments/qa.ts: -------------------------------------------------------------------------------- 1 | /* eslint quote-props: 0 */ 2 | export { } 3 | const configuration: any = {}; 4 | configuration.mongo = { 5 | url: process.env.MONGODB_URI || process.env.MONGOURL, 6 | }; 7 | configuration.URL = { 8 | frontEnd: process.env.FE_URL 9 | } 10 | configuration.db = { 11 | user: process.env.USERNAME, 12 | password: process.env.PASSWORD, 13 | database: process.env.DATABASE, 14 | host: process.env.HOST, 15 | connectTimeout: 80000, 16 | }; 17 | configuration.facebook = { 18 | client_id: process.env.F_CLIENTID, 19 | client_secret: process.env.F_CLIENTSECRET, 20 | callback_url: process.env.F_CALLBACK 21 | }; 22 | configuration.google = { 23 | client_id: process.env.G_CLIENTID, 24 | client_secret: process.env.G_CLIENTSECRET, 25 | callback_url: process.env.G_CALLBACK 26 | }; 27 | configuration.linkedin = { 28 | client_id: process.env.L_CLIENTID, 29 | client_secret: process.env.L_CLIENTSECRET, 30 | callback_url: process.env.L_CALLBACK 31 | }; 32 | configuration.twitter = { 33 | client_id: process.env.T_CLIENTID, 34 | client_secret: process.env.T_CLIENTSECRET, 35 | callback_url: process.env.T_CALLBACK 36 | }; 37 | configuration.email = { 38 | apiKey: process.env.API_KEY, 39 | host: process.env.SMTP_HOST, 40 | port: process.env.SMTP_PORT, 41 | auth: { 42 | user: process.env.SMTP_USER, 43 | pass: process.env.SMTP_PASSWORD, 44 | } 45 | } 46 | configuration.twilio = { 47 | sid: process.env.SID, 48 | token: process.env.TOKEN, 49 | phone: process.env.PHONE, 50 | } 51 | configuration.url = { 52 | FE: process.env.FE, 53 | API: process.env.API, 54 | } 55 | configuration.uploadpath = { 56 | uploaddir: process.env.UPLOAD_DIR, 57 | profiledir: process.env.PROFILE_PICTURE_DIR 58 | } 59 | configuration.logLevel ='info'; 60 | 61 | module.exports = configuration; 62 | -------------------------------------------------------------------------------- /e-Commerce-Cart/app/config/environments/test.ts: -------------------------------------------------------------------------------- 1 | /* eslint quote-props: 0 */ 2 | export { } 3 | const configuration: any = {}; 4 | configuration.mongo = { 5 | url: process.env.MONGODB_URI || process.env.MONGOURL, 6 | }; 7 | configuration.URL = { 8 | frontEnd: process.env.FE_URL 9 | } 10 | configuration.db = { 11 | user: process.env.USERNAME, 12 | password: process.env.PASSWORD, 13 | database: process.env.DATABASE, 14 | host: process.env.HOST, 15 | connectTimeout: 80000, 16 | }; 17 | 18 | configuration.facebook = { 19 | client_id: process.env.F_CLIENTID, 20 | client_secret: process.env.F_CLIENTSECRET, 21 | callback_url: process.env.F_CALLBACK 22 | }; 23 | configuration.google = { 24 | client_id: process.env.G_CLIENTID, 25 | client_secret: process.env.G_CLIENTSECRET, 26 | callback_url: process.env.G_CALLBACK 27 | }; 28 | configuration.linkedin = { 29 | client_id: process.env.L_CLIENTID, 30 | client_secret: process.env.L_CLIENTSECRET, 31 | callback_url: process.env.L_CALLBACK 32 | }; 33 | configuration.twitter = { 34 | client_id: process.env.T_CLIENTID, 35 | client_secret: process.env.T_CLIENTSECRET, 36 | callback_url: process.env.T_CALLBACK 37 | }; 38 | configuration.email = { 39 | apiKey: process.env.API_KEY, 40 | host: process.env.SMTP_HOST, 41 | port: process.env.SMTP_PORT, 42 | auth: { 43 | user: process.env.SMTP_USER, 44 | pass: process.env.SMTP_PASSWORD, 45 | } 46 | } 47 | configuration.twilio = { 48 | sid: process.env.SID, 49 | token: process.env.TOKEN, 50 | phone: process.env.PHONE, 51 | } 52 | configuration.url = { 53 | FE: process.env.FE, 54 | API: process.env.API, 55 | } 56 | configuration.uploadpath = { 57 | uploaddir: process.env.UPLOAD_DIR, 58 | profiledir: process.env.PROFILE_PICTURE_DIR 59 | } 60 | configuration.logLevel ='info'; 61 | 62 | module.exports = configuration; 63 | -------------------------------------------------------------------------------- /e-Commerce-Cart/app/events/processEvent.ts: -------------------------------------------------------------------------------- 1 | const events = require('events'); 2 | 3 | const eventEmitter = new events.EventEmitter(); 4 | 5 | export default eventEmitter; 6 | -------------------------------------------------------------------------------- /e-Commerce-Cart/app/helper/errorHandler.ts: -------------------------------------------------------------------------------- 1 | import logger from '../lib/logger'; 2 | import ResponseTemplate from './responseTemplate'; 3 | /* eslint class-methods-use-this:0 */ 4 | const env = process.env.NODE_ENV; 5 | const onDevEnv = env === 'dev' || env === 'test' || env === 'local'; 6 | class errorHandler { 7 | public internalServerError(err, req, res, next) { 8 | logger.log('info',err); 9 | if (err.isBoom) { 10 | // Error From joi express validator 11 | const error = { 12 | message: err.output.payload.error, 13 | error: err.output.payload.message 14 | }; 15 | res.status(400).json(ResponseTemplate.BadRequestFromJoi(error)); 16 | } else { // internalServerError 17 | res.status(500).json({ 18 | success: false, 19 | message: err.message, 20 | error: (onDevEnv) ? err.stack : {} 21 | }); 22 | } 23 | } 24 | public PageNotFound(req, res, err) { 25 | res.status(404).json({ message: 'api not found' }); 26 | } 27 | } 28 | 29 | export default new errorHandler(); 30 | -------------------------------------------------------------------------------- /e-Commerce-Cart/app/helper/errors.ts: -------------------------------------------------------------------------------- 1 | class APIError extends Error { 2 | constructor(message, ErrorID, code = null) { 3 | super(); 4 | Error.captureStackTrace(this, this.constructor); 5 | this.name = 'api error'; 6 | this.message = message; 7 | if (ErrorID) this['ErrorID'] = ErrorID; 8 | if (code) this['code'] = code; 9 | } 10 | } 11 | export default APIError; -------------------------------------------------------------------------------- /e-Commerce-Cart/app/helper/logger.ts: -------------------------------------------------------------------------------- 1 | const log = require('loglevel'); 2 | 3 | log.setLevel(global.configuration.logLevel); 4 | const logger = log; 5 | export default logger; -------------------------------------------------------------------------------- /e-Commerce-Cart/app/helper/responseTemplate.ts: -------------------------------------------------------------------------------- 1 | /* istanbul ignore file */ 2 | const data: any = { 3 | general(data) { 4 | return data; 5 | }, 6 | successMessage(message) { 7 | return { 8 | success: true, 9 | message 10 | }; 11 | }, 12 | success(data, message) { 13 | return { 14 | success: true, 15 | message, 16 | data 17 | }; 18 | }, 19 | error(message, err, code= null) { 20 | return { 21 | success: false, 22 | message: message || 'some error occurred', 23 | error: err || 'error occurred on server, please try again after some time.' 24 | }; 25 | }, 26 | emptyContent() { 27 | return this.general({ 28 | message: 'empty content found', 29 | description: 'you must provide valid data and it must not be empty.', 30 | helpful_links: ['http://stackoverflow.com/questions/18419428/what-is-the-minimum-valid-json'] 31 | }); 32 | }, 33 | invalidContentType() { 34 | return this.general({ 35 | message: 'invalid content type', 36 | description: 'you must specify content type and it must be application/json', 37 | helpful_links: ['http://stackoverflow.com/questions/477816/what-is-the-correct-json-content-type'] 38 | }); 39 | }, 40 | BadRequestFromJoi(err) { 41 | return this.error( 42 | err.message, 43 | err.error 44 | ); 45 | }, 46 | userAlreadyExist(err) { 47 | return this.general({ 48 | success: false, 49 | message: 'user already registered in System', 50 | description: 'user already registered in System' 51 | }); 52 | }, 53 | userdoesNotExist(err) { 54 | return this.general({ 55 | success: false, 56 | message: err.message || 'user not registered in system', 57 | description: 'user account does not exist in system' 58 | }); 59 | }, 60 | commonAuthUserDataError() { 61 | return this.error( 62 | 'Authentication error', 63 | 'token verification failed, Please try again' 64 | ); 65 | }, 66 | tokenRequiredAuthError() { 67 | return this.error( 68 | 'Authentication error, Token is required in Header', 69 | 'token verification failed, Please try again' 70 | ); 71 | }, 72 | }; 73 | export default data; 74 | -------------------------------------------------------------------------------- /e-Commerce-Cart/app/lib/logger.ts: -------------------------------------------------------------------------------- 1 | const log = require('loglevel'); 2 | 3 | log.setLevel(global.configuration.logLevel); 4 | const logger = log; 5 | export default logger; -------------------------------------------------------------------------------- /e-Commerce-Cart/app/lib/mysql.ts: -------------------------------------------------------------------------------- 1 | import APIError from '../helper/errors'; 2 | import logger from './logger'; 3 | 4 | const mysql = require('mysql2'); 5 | import eventEmitter from '../events/processEvent'; 6 | /* eslint func-names: ["error", "never"] */ 7 | const config = global['configuration'].db; 8 | console.log(config); 9 | let connection:any = null; 10 | 11 | try { 12 | connection = mysql.createConnection(config); 13 | } catch (err) { 14 | logger.error('Cannot establish a connection with the database'); 15 | /** To Prevent sensitive info leak, not raising actual error */ 16 | throw new APIError('Mysql connection failed (config)', 1); 17 | } 18 | connection.connect((err) => { 19 | // in case of error 20 | if (err) throw new APIError('Mysql connection failed (connect)', 1); 21 | else { 22 | logger.info(`MysqlClient connected to port ${config.port || 3306} and ${config.host} host`); 23 | eventEmitter.emit('dbReady', connection); 24 | } 25 | }); 26 | connection.on('error', (err) => { 27 | logger.error('Cannot establish a connection with the database'); 28 | /** To Prevent sensitive info leak, not raising actual error */ 29 | throw new APIError('Mysql connection failed (event)', 1); 30 | }); 31 | 32 | export default connection; 33 | 34 | -------------------------------------------------------------------------------- /e-Commerce-Cart/app/lib/requestValidator.ts: -------------------------------------------------------------------------------- 1 | const Joi = require('joi'); 2 | 3 | const validation = { 4 | loginUser : { 5 | body : { 6 | email: Joi.string().regex(/^[\w.]+@[\w]+?(\.[a-zA-Z]{2,3}){1,3}$/).required(), 7 | password: Joi.string().min(8).max(50).required() 8 | } 9 | }, 10 | createUser: { 11 | body: { 12 | username: Joi.string().min(4).max(50).required(), 13 | password: Joi.string().min(8).max(50).required(), 14 | email: Joi.string().regex(/^[\w.]+@[\w]+?(\.[a-zA-Z]{2,3}){1,3}$/).required(), 15 | verify_password: Joi.string().min(6).max(50).required() 16 | }, 17 | }, 18 | resetPassword: { 19 | body: { 20 | email: Joi.string().regex(/^[\w.]+@[\w]+?(\.[a-zA-Z]{2,3}){1,3}$/).required(), 21 | } 22 | } 23 | }; 24 | export default validation 25 | -------------------------------------------------------------------------------- /e-Commerce-Cart/app/middleware/requestValidator.ts: -------------------------------------------------------------------------------- 1 | 2 | module.exports = { 3 | validatePayload(req, res, next) { 4 | // validatr token here is its valid here 5 | const token = req.body; 6 | if ((req.method === 'POST' || req.method === 'PUT') && req.body !== null) { 7 | next(); 8 | } 9 | res.status(403).json({ message: 'payload is required for HTTP Post & Put ' }); 10 | } 11 | }; 12 | -------------------------------------------------------------------------------- /e-Commerce-Cart/app/routes.ts: -------------------------------------------------------------------------------- 1 | /* eslint func-names: ["error", "never"] */ 2 | /* eslint prefer-destructuring: 0 */ 3 | import * as express from 'express'; 4 | const expressRouter= express.Router(); 5 | import defaultRoutes from './routes/defaultRoutes'; 6 | expressRouter.use('/', defaultRoutes); 7 | 8 | 9 | 10 | export default expressRouter; 11 | -------------------------------------------------------------------------------- /e-Commerce-Cart/app/routes/defaultRoutes.ts: -------------------------------------------------------------------------------- 1 | import * as express from "express"; 2 | const router = express.Router(); 3 | 4 | import { Router } from "express"; 5 | import product from '../models/data/cart'; 6 | 7 | 8 | export class DefaultRouter { 9 | router: Router; 10 | 11 | /** 12 | * Initialize the HeroRouter 13 | */ 14 | constructor() { 15 | this.router = Router(); 16 | } 17 | /** 18 | * @api {POST} /auth/reset-password update password sent in Mail 19 | * @apiName resetPassword 20 | * @apiGroup Auth 21 | * @apiSuccess {String} code HTTP status code from API. 22 | * @apiSuccess {String} message Message from API. 23 | */ 24 | public sayHello(req, res) { 25 | res.status(200).json({ success: true, message: 'i am up and running with mysql + node .. ⚡️⚡️⚡️⚡️⚡️⚡️⚡️' }); 26 | }; 27 | public getProducts(req, res) { 28 | res.status(200).json(product); 29 | }; 30 | 31 | init() { 32 | this.router.get("/", this.sayHello); 33 | this.router.get("/products", this.getProducts); 34 | 35 | } 36 | } 37 | 38 | 39 | // Create the HeroRouter, and export its configured Express.Router 40 | const defaultRouter = new DefaultRouter(); 41 | defaultRouter.init(); 42 | 43 | export default defaultRouter.router; 44 | -------------------------------------------------------------------------------- /e-Commerce-Cart/app/types/global.d.ts: -------------------------------------------------------------------------------- 1 | declare namespace NodeJS { 2 | export interface Global { 3 | configuration: any 4 | } 5 | } -------------------------------------------------------------------------------- /e-Commerce-Cart/app/types/vendor.d.ts: -------------------------------------------------------------------------------- 1 | 2 | declare namespace NodeJS { 3 | export interface Global { 4 | configuration: any 5 | } 6 | } -------------------------------------------------------------------------------- /e-Commerce-Cart/config/config.js: -------------------------------------------------------------------------------- 1 | const fs = require('fs'); 2 | module.exports = { 3 | local: { 4 | username: process.env.USERNAME, 5 | password: process.env.PASSWORD, 6 | database: process.env.DATABASE, 7 | host: process.env.HOST, 8 | dialect: 'mysql' 9 | }, 10 | dev: { 11 | username: process.env.USERNAME, 12 | password: process.env.PASSWORD, 13 | database: process.env.DATABASE, 14 | host: process.env.HOST, 15 | dialect: 'mysql' 16 | }, 17 | test: { 18 | username: process.env.USERNAME, 19 | password: process.env.PASSWORD, 20 | database: process.env.DATABASE, 21 | host: process.env.HOST, 22 | dialect: 'mysql' 23 | }, 24 | qa: { 25 | username: process.env.USERNAME, 26 | password: process.env.PASSWORD, 27 | database: process.env.DATABASE, 28 | host: process.env.HOST, 29 | dialect: 'mysql' 30 | }, 31 | uat: { 32 | username: process.env.USERNAME, 33 | password: process.env.PASSWORD, 34 | database: process.env.DATABASE, 35 | host: process.env.HOST, 36 | dialect: 'mysql' 37 | }, 38 | prod: { 39 | username: process.env.USERNAME, 40 | password: process.env.PASSWORD, 41 | database: process.env.DATABASE, 42 | host: process.env.HOST, 43 | dialect: 'mysql' 44 | } 45 | }; 46 | -------------------------------------------------------------------------------- /e-Commerce-Cart/env.sh: -------------------------------------------------------------------------------- 1 | # this environment vairables needs to be set in .env file in applciaiton root directory 2 | # copy this file as .env and add the appropriate values as per environment. 3 | # node & mysql 4 | export NODE_ENV="dev" 5 | export PORT="3004" 6 | export USERNAME="root" 7 | export PASSWORD="root" 8 | export DATABASE="cartDB" 9 | export HOST="mysql" -------------------------------------------------------------------------------- /e-Commerce-Cart/package.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "e-commerce-hub", 3 | "version": "0.0.0", 4 | "private": true, 5 | "scripts": { 6 | "start:local": " . ./env.sh && NODE_ENV=local nodemon app/server.js ", 7 | "create:local": ". ./env.sh && NODE_ENV=local ./node_modules/.bin/sequelize db:create", 8 | "migrate:local": ". ./env.sh && NODE_ENV=local ./node_modules/.bin/sequelize db:migrate", 9 | "seed:local": " . ./env.sh && NODE_ENV=local ./node_modules/.bin/sequelize db:migrate --migrations-path seeders", 10 | "start": "cd dist && nodemon server.js", 11 | "prestart": "tsc && cp -r uploads dist/ && cp -r app/global dist/app/", 12 | "startdev": ". ./env.sh && cd dist && nodemon server.js", 13 | "clean": "rm -rf dist", 14 | "watch": "tsc", 15 | "copy": "cp -r uploads dist/ && cp -r app/global dist/app/", 16 | "test": ". ./env.sh && NODE_ENV=test && mocha ", 17 | "debug": ". ./env.sh && NODE_ENV=test && cd dist && nodemon --inspect=0.0.0.0:9230 server.js", 18 | "prestartdev": " npm run clean && tsc && npm run copy && npm run watch", 19 | "poststartdev": "tsc --watch", 20 | "watchserver": "tsc --watch", 21 | "dev": ". ./env.sh && ts-node server.ts", 22 | "start-tsc": ". ./env.sh && nodemon ./dist/server.js", 23 | "buildAndstart": ". ./env.sh && npm run build && npm run start" 24 | }, 25 | "dependencies": { 26 | "@types/express": "^4.11.1", 27 | "assert": "^1.4.1", 28 | "axios": "^0.18.0", 29 | "bcrypt-nodejs": "0.0.3", 30 | "bluebird": "^3.5.3", 31 | "body-parser": "^1.18.3", 32 | "cookie-parser": "^1.4.3", 33 | "cors": "^2.8.5", 34 | "dotenv": "^6.1.0", 35 | "email-templates": "^2.7.1", 36 | "express": "^4.16.4", 37 | "express-boom": "^2.0.0", 38 | "express-joi-validator": "^2.0.0", 39 | "express-session": "^1.15.6", 40 | "fast-csv": "^2.4.1", 41 | "hbs": "^4.0.1", 42 | "helmet": "^3.15.0", 43 | "joi": "^14.1.1", 44 | "jsonwebtoken": "^8.4.0", 45 | "loglevel": "^1.6.1", 46 | "mocha": "^5.2.0", 47 | "moment": "^2.22.2", 48 | "mongoose": "^4.5.9", 49 | "morgan": "^1.9.0", 50 | "multer": "^1.2.0", 51 | "mysql2": "^1.6.5", 52 | "nodemailer": "2.5.0", 53 | "nodemon": "^1.18.6", 54 | "passport": "0.3.2", 55 | "passport-facebook": "2.1.1", 56 | "passport-google-oauth": "1.0.0", 57 | "passport-google-oauth20": "^1.0.0", 58 | "passport-instagram": "1.0.0", 59 | "passport-linkedin": "^1.0.0", 60 | "passport-local": "1.0.0", 61 | "passport-twitter": "1.0.4", 62 | "pug": "2.0.0-beta6", 63 | "sequelize": "^4.37.4", 64 | "sequelize-cli": "^4.0.0", 65 | "serve-favicon": "^2.5.0", 66 | "ts-lint": "^4.5.1", 67 | "ts-node": "^7.0.1", 68 | "twilio": "^2.11.1", 69 | "typescript": "^3.1.6", 70 | "uuid": "^3.3.2" 71 | }, 72 | "devDependencies": { 73 | "@types/async": "^2.0.45", 74 | "@types/bcrypt-nodejs": "^0.0.30", 75 | "@types/bluebird": "^3.5.20", 76 | "@types/body-parser": "^1.16.8", 77 | "@types/express": "^4.11.1", 78 | "@types/mongoose": "^4.7.34", 79 | "@types/morgan": "^1.7.35", 80 | "@types/node": "^9.6.39", 81 | "@types/nodemailer": "^4.3.4", 82 | "@types/passport": "^0.4.3", 83 | "babel-eslint": "^8.0.1", 84 | "eslint": "^4.19.1", 85 | "eslint-config-airbnb-base": "^12.1.0", 86 | "eslint-plugin-import": "^2.9.0", 87 | "eslint-plugin-node": "^5.2.1" 88 | } 89 | } 90 | -------------------------------------------------------------------------------- /e-Commerce-Cart/public/images/cinema.jpg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/tkssharma/e-CommerseHub/044bb67bf47ef1808633ca8f5c0c9812d5fcdd44/e-Commerce-Cart/public/images/cinema.jpg -------------------------------------------------------------------------------- /e-Commerce-Cart/public/javascripts/script.js: -------------------------------------------------------------------------------- 1 | document.addEventListener('DOMContentLoaded', () => { 2 | 3 | console.log('IronGenerator JS imported successfully!'); 4 | 5 | }, false); 6 | -------------------------------------------------------------------------------- /e-Commerce-Cart/public/style.scss: -------------------------------------------------------------------------------- 1 | body { 2 | padding: 50px; 3 | font: 14px "Lucida Grande", Helvetica, Arial, sans-serif; 4 | } 5 | 6 | a { 7 | color: #00B7FF; 8 | } 9 | 10 | .h1 { 11 | font-size: 40px; 12 | } 13 | -------------------------------------------------------------------------------- /e-Commerce-Cart/server.ts: -------------------------------------------------------------------------------- 1 | import * as http from 'http'; 2 | import * as debug from 'debug'; 3 | // After you declare "app" 4 | const env = process.env.NODE_ENV || 'dev' 5 | console.log(` using ${process.env.NODE_ENV} to run application`); 6 | global.configuration = require(`./app/config/environments/${env}`); 7 | import App from './express'; 8 | import eventEmitter from './app/events/processEvent' 9 | const port = (process.env.PORT); 10 | import logger from './app/lib/logger'; 11 | import mysql from './app/lib/mysql'; 12 | global['connection'] = mysql 13 | 14 | const appServer = http.createServer(App); 15 | 16 | appServer.on('error', onError); 17 | appServer.on('listening', onListening); 18 | 19 | 20 | if (!module.parent) { 21 | eventEmitter.on('dbReady', (connection) => { 22 | const port = process.env.PORT; 23 | appServer.listen(process.env.PORT, 24 | () => { 25 | logger.info(`API running in environment ${process.env.NODE_ENV}`); 26 | logger.info(`API running at http://localhost:${port}`); 27 | }, 28 | ); 29 | appServer.setTimeout(200000); 30 | if (process['parent']) process.send('ready'); 31 | }); 32 | } 33 | 34 | 35 | function onError(error: NodeJS.ErrnoException): void { 36 | if (error.syscall !== 'listen') throw error; 37 | let bind = (typeof port === 'string') ? 'Pipe ' + port : 'Port ' + port; 38 | switch(error.code) { 39 | case 'EACCES': 40 | console.error(`${bind} requires elevated privileges`); 41 | process.exit(1); 42 | break; 43 | case 'EADDRINUSE': 44 | console.error(`${bind} is already in use`); 45 | process.exit(1); 46 | break; 47 | default: 48 | throw error; 49 | } 50 | } 51 | 52 | const gracefulStopServer = function () { 53 | // Wait 10 secs for existing connection to close and then exit. 54 | setTimeout(() => { 55 | logger.info('Shutting down server'); 56 | process.exit(0); 57 | }, 1000); 58 | }; 59 | 60 | process.on('uncaughtException', (err) => { 61 | logger.error(err, 'Uncaught exception'); 62 | process.exit(1); 63 | }); 64 | 65 | process.on('unhandledRejection', (reason, promise) => { 66 | logger.error({ 67 | promise, 68 | reason 69 | }, 'unhandledRejection'); 70 | process.exit(1); 71 | }); 72 | 73 | process.on('SIGINT', gracefulStopServer); 74 | process.on('SIGTERM', gracefulStopServer); 75 | 76 | function onListening(): void { 77 | let addr = appServer.address(); 78 | let bind = (typeof addr === 'string') ? `pipe ${addr}` : `port ${addr.port}`; 79 | debug(`Listening on ${bind}`); 80 | } -------------------------------------------------------------------------------- /e-Commerce-Cart/tsconfig.json: -------------------------------------------------------------------------------- 1 | { 2 | "compilerOptions": { 3 | "module": "commonjs", 4 | "moduleResolution": "node", 5 | "pretty": true, 6 | "sourceMap": true, 7 | "target": "es6", 8 | "outDir": "./dist", 9 | "experimentalDecorators": false, 10 | "emitDecoratorMetadata": false, 11 | "skipDefaultLibCheck": false, 12 | "baseUrl": "./lib" 13 | }, 14 | "files" : [ 15 | "./app/types/vendor.d.ts" 16 | ], 17 | "include": [ 18 | "/**/*.ts" 19 | ], 20 | "exclude": [ 21 | "node_modules" 22 | ] 23 | } -------------------------------------------------------------------------------- /e-Commerce-Cart/tslint.json: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/tkssharma/e-CommerseHub/044bb67bf47ef1808633ca8f5c0c9812d5fcdd44/e-Commerce-Cart/tslint.json -------------------------------------------------------------------------------- /e-Commerce-Cart/uploads/documents/.gitkeep: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/tkssharma/e-CommerseHub/044bb67bf47ef1808633ca8f5c0c9812d5fcdd44/e-Commerce-Cart/uploads/documents/.gitkeep -------------------------------------------------------------------------------- /e-Commerce-Cart/uploads/profile/.gitkeep: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/tkssharma/e-CommerseHub/044bb67bf47ef1808633ca8f5c0c9812d5fcdd44/e-Commerce-Cart/uploads/profile/.gitkeep -------------------------------------------------------------------------------- /e-Commerce-Client/Dockerfile: -------------------------------------------------------------------------------- 1 | FROM node:carbon 2 | 3 | # Create app directory 4 | WORKDIR /usr/src/app 5 | 6 | # Bundle app source 7 | COPY . . 8 | 9 | # npm install 10 | RUN npm install 11 | # Run npm install --global grpc --unsafe-perm 12 | 13 | EXPOSE 3003 14 | 15 | CMD [ "npm", "run", "start" ] -------------------------------------------------------------------------------- /e-Commerce-Client/README.md: -------------------------------------------------------------------------------- 1 | ## Simple ecommerce cart application 2 | 3 | ## Basic Overview - 4 | 5 | This simple shopping cart prototype shows how React components and Redux can be used to build a 6 | friendly user experience with instant visual updates and scaleable code in ecommerce applications. 7 | 8 | #### Features 9 | 10 | - Add and remove products from the floating cart 11 | - Sort products by highest to lowest and lowest to highest price 12 | - Filter products by available sizes 13 | - Products persist in floating cart even after page reloads 14 | - Responsive design for desktop, tablets and mobile 15 | - Product stoppers for free shipping 16 | - Unit tests, integration tests and e2e testing 17 | 18 | #### Using 19 | 20 | - React 21 | - Redux - state management 22 | - Nodejs 23 | - Express CORS Middleware (Node and React run in different port) 24 | - Nodemon - for a better development experience 25 | - Concurrently - To run multiple tasks at once 26 | - Axios - for promise HTTP requests 27 | - CSS 28 | - BEM methodology 29 | - SASS 30 | - Moxios - to stub http request 31 | - Enzyme - to mount, shallow, render and query the DOM tree of React components 32 | - Webdriverio - to do automated tests in a real browser environment 33 | - Native local storage - to persist products in cart even after page reload 34 | 35 | #### Requirements 36 | 37 | - Node.js 38 | - NPM 39 | 40 | ```javascript 41 | 42 | /* First, Install the needed packages */ 43 | npm install 44 | 45 | /* Then start both Node and React */ 46 | npm start 47 | 48 | /* To run the tests */ 49 | npm run test 50 | 51 | /* Running e2e tests */ 52 | npm run wdio 53 | 54 | 55 | ``` 56 | 57 | ## About tests 58 | 59 | - Unit tests 60 | - All components have at least a basic smoke test 61 | - Integration tests 62 | - Fetch product and add to cart properly 63 | - e2e 64 | - Webdriverio - Add and remove product from cart 65 | -------------------------------------------------------------------------------- /e-Commerce-Client/env.sh: -------------------------------------------------------------------------------- 1 | # this environment vairables needs to be set in .env file in applciaiton root directory 2 | # copy this file as .env and add the appropriate values as per environment. 3 | # node & mysql 4 | export NODE_ENV="local" 5 | export PORT="3003" -------------------------------------------------------------------------------- /e-Commerce-Client/firebase.json: -------------------------------------------------------------------------------- 1 | { 2 | "hosting": { 3 | "public": "build", 4 | "ignore": [ 5 | "firebase.json", 6 | "**/.*", 7 | "**/node_modules/**" 8 | ] 9 | } 10 | } 11 | -------------------------------------------------------------------------------- /e-Commerce-Client/package.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "app", 3 | "version": "0.1.0", 4 | "private": true, 5 | "dependencies": { 6 | "antd": "^3.13.2", 7 | "axios": "^0.18.0", 8 | "concurrently": "^4.0.1", 9 | "cors": "^2.8.5", 10 | "express": "^4.16.4", 11 | "immutable": "^4.0.0-rc.12", 12 | "moxios": "^0.4.0", 13 | "react": "^16.6.1", 14 | "react-dom": "^16.6.1", 15 | "react-redux": "^5.1.1", 16 | "react-router": "^3.2.0", 17 | "react-router-dom": "^4.3.1", 18 | "react-scripts": "^2.1.3", 19 | "redux": "^4.0.1", 20 | "redux-thunk": "^2.3.0" 21 | }, 22 | "scripts": { 23 | "start": ". ./env.sh && react-scripts start", 24 | "wdio": "wdio", 25 | "build": "react-scripts build", 26 | "test": "react-scripts test", 27 | "test:coverage": "npm run test -- --coverage", 28 | "format": "prettier --write \"**/*.+(js|json|css)\"", 29 | "eject": "react-scripts eject" 30 | }, 31 | "eslintConfig": { 32 | "extends": "react-app" 33 | }, 34 | "jest": { 35 | "collectCoverageFrom": [ 36 | "src/**/*.{js,jsx}", 37 | "!/node_modules/", 38 | "!src/index.js", 39 | "!src/Root.js" 40 | ] 41 | }, 42 | "browserslist": [ 43 | ">0.2%", 44 | "not dead", 45 | "not ie <= 11", 46 | "not op_mini all" 47 | ], 48 | "devDependencies": { 49 | "chai": "^4.2.0", 50 | "enzyme": "^3.7.0", 51 | "enzyme-adapter-react-16": "^1.7.0", 52 | "enzyme-to-json": "^3.3.4", 53 | "fetch-mock": "^7.2.5", 54 | "firebase-tools": "^6.2.2", 55 | "node-sass": "^4.10.0", 56 | "nodemon": "^1.18.6", 57 | "prop-types": "^15.6.2", 58 | "react-test-renderer": "^16.6.3", 59 | "redux-mock-store": "^1.5.3", 60 | "sinon": "^7.1.1", 61 | "wdio-mocha-framework": "^0.6.4", 62 | "wdio-selenium-standalone-service": "0.0.12", 63 | "wdio-spec-reporter": "^0.1.5", 64 | "webdriverio": "^4.14.1" 65 | } 66 | } 67 | -------------------------------------------------------------------------------- /e-Commerce-Client/public/favicon.ico: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/tkssharma/e-CommerseHub/044bb67bf47ef1808633ca8f5c0c9812d5fcdd44/e-Commerce-Client/public/favicon.ico -------------------------------------------------------------------------------- /e-Commerce-Client/public/index.html: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | 13 | 14 | 15 | 16 | 17 | 18 | 19 | 20 | 21 | 22 | 23 | 24 | 25 | 29 | 30 | 31 | 32 | 33 | 42 | React Shopping Cart 43 | 44 | 45 | 48 |
49 | 59 | 60 | 61 | -------------------------------------------------------------------------------- /e-Commerce-Client/public/manifest.json: -------------------------------------------------------------------------------- 1 | { 2 | "short_name": "React App", 3 | "name": "Create React App Sample", 4 | "icons": [ 5 | { 6 | "src": "favicon.ico", 7 | "sizes": "64x64 32x32 24x24 16x16", 8 | "type": "image/x-icon" 9 | } 10 | ], 11 | "start_url": ".", 12 | "display": "standalone", 13 | "theme_color": "#000000", 14 | "background_color": "#ffffff" 15 | } 16 | -------------------------------------------------------------------------------- /e-Commerce-Client/src/api/index.js: -------------------------------------------------------------------------------- 1 | import config from '../config/server'; 2 | import axios from 'axios'; 3 | 4 | export function url(resource) { 5 | if (!resource) { 6 | return `${config.url}` 7 | } 8 | console.log(`${config.url}${config[resource]}`); 9 | return `${config.url}${config[resource]}` 10 | } 11 | export function setAuthToken(token) { 12 | if (token) { 13 | axios.defaults.headers.common['authorization'] = `${token}`; 14 | } else { 15 | delete axios.defaults.headers.common['authorization']; 16 | } 17 | } 18 | export function setGeoLocation(location) { 19 | if (location) { 20 | axios.defaults.headers.common['Location'] = `${location.lng},${location.lat}`; 21 | } else { 22 | delete axios.defaults.headers.common['Location']; 23 | } 24 | } 25 | export function getAuthToken() { 26 | return localStorage.token; 27 | } -------------------------------------------------------------------------------- /e-Commerce-Client/src/components/App/index.js: -------------------------------------------------------------------------------- 1 | import React, { Component } from 'react'; 2 | 3 | import Shelf from '../Shelf'; 4 | import Filter from '../Shelf/Filter'; 5 | import FloatCart from '../FloatCart'; 6 | 7 | class App extends Component { 8 | render() { 9 | return ( 10 | 11 |
12 | 13 | 14 |
15 | 16 |
17 | ); 18 | } 19 | } 20 | 21 | export default App; 22 | -------------------------------------------------------------------------------- /e-Commerce-Client/src/components/Checkbox/index.js: -------------------------------------------------------------------------------- 1 | import React, { Component } from 'react'; 2 | import PropTypes from 'prop-types'; 3 | 4 | class Checkbox extends Component { 5 | static propTypes = { 6 | label: PropTypes.string.isRequired, 7 | handleCheckboxChange: PropTypes.func.isRequired 8 | }; 9 | 10 | state = { 11 | isChecked: false 12 | }; 13 | 14 | toggleCheckboxChange = () => { 15 | const { handleCheckboxChange, label } = this.props; 16 | 17 | this.setState(({ isChecked }) => ({ 18 | isChecked: !isChecked 19 | })); 20 | 21 | handleCheckboxChange(label); 22 | }; 23 | 24 | render() { 25 | const { label, classes } = this.props; 26 | const { isChecked } = this.state; 27 | 28 | return ( 29 |
30 | 40 |
41 | ); 42 | } 43 | } 44 | 45 | export default Checkbox; 46 | -------------------------------------------------------------------------------- /e-Commerce-Client/src/components/FloatCart/CartProduct/index.js: -------------------------------------------------------------------------------- 1 | import React, { Component } from 'react'; 2 | import PropTypes from 'prop-types'; 3 | 4 | import Thumb from './../../Thumb'; 5 | import { formatPrice } from '../../../services/util'; 6 | 7 | class CartProduct extends Component { 8 | static propTypes = { 9 | product: PropTypes.object.isRequired, 10 | removeProduct: PropTypes.func.isRequired 11 | }; 12 | 13 | state = { 14 | isMouseOver: false 15 | }; 16 | 17 | handleMouseOver = () => { 18 | this.setState({ isMouseOver: true }); 19 | }; 20 | 21 | handleMouseOut = () => { 22 | this.setState({ isMouseOver: false }); 23 | }; 24 | 25 | render() { 26 | const { product, removeProduct } = this.props; 27 | 28 | const classes = ['shelf-item']; 29 | 30 | if (!!this.state.isMouseOver) { 31 | classes.push('shelf-item--mouseover'); 32 | } 33 | 34 | return ( 35 |
36 |
this.handleMouseOver()} 39 | onMouseOut={() => this.handleMouseOut()} 40 | onClick={() => removeProduct(product)} 41 | /> 42 | 47 |
48 |

{product.title}

49 |

50 | {`${product.availableSizes[0]} | ${product.style}`}
51 | Quantity: {product.quantity} 52 |

53 |
54 |
55 |

{`${product.currencyFormat} ${formatPrice(product.price)}`}

56 |
57 |
58 | ); 59 | } 60 | } 61 | 62 | export default CartProduct; 63 | -------------------------------------------------------------------------------- /e-Commerce-Client/src/components/Selectbox/index.js: -------------------------------------------------------------------------------- 1 | import React, { Component } from 'react'; 2 | import PropTypes from 'prop-types'; 3 | 4 | class Selectbox extends Component { 5 | static propTypes = { 6 | options: PropTypes.array.isRequired, 7 | classes: PropTypes.string, 8 | handleOnChange: PropTypes.func.isRequired 9 | }; 10 | 11 | state = { 12 | selected: '' 13 | }; 14 | 15 | createOptions = options => 16 | options.map(o => ( 17 | 20 | )); 21 | 22 | onChange = e => { 23 | this.props.handleOnChange(e.target.value); 24 | }; 25 | 26 | render() { 27 | const { classes, options } = this.props; 28 | 29 | return ( 30 | 33 | ); 34 | } 35 | } 36 | 37 | export default Selectbox; 38 | -------------------------------------------------------------------------------- /e-Commerce-Client/src/components/Shelf/Filter/index.js: -------------------------------------------------------------------------------- 1 | import React, { Component } from 'react'; 2 | import PropTypes from 'prop-types'; 3 | 4 | import { connect } from 'react-redux'; 5 | import { updateFilters } from '../../../services/filters/actions'; 6 | import Checkbox from '../../Checkbox'; 7 | 8 | import './style.scss'; 9 | 10 | const availableSizes = ['XS', 'S', 'M', 'ML', 'L', 'XL', 'XXL']; 11 | 12 | class Filter extends Component { 13 | static propTypes = { 14 | updateFilters: PropTypes.func.isRequired, 15 | filters: PropTypes.array 16 | }; 17 | 18 | componentDidMount() { 19 | this.selectedCheckboxes = new Set(); 20 | } 21 | 22 | toggleCheckbox = label => { 23 | if (this.selectedCheckboxes.has(label)) { 24 | this.selectedCheckboxes.delete(label); 25 | } else { 26 | this.selectedCheckboxes.add(label); 27 | } 28 | 29 | this.props.updateFilters(Array.from(this.selectedCheckboxes)); 30 | }; 31 | 32 | createCheckbox = label => ( 33 | 39 | ); 40 | 41 | createCheckboxes = () => availableSizes.map(this.createCheckbox); 42 | 43 | render() { 44 | return ( 45 |
46 |

Sizes:

47 | {this.createCheckboxes()} 48 |
49 | ); 50 | } 51 | } 52 | 53 | const mapStateToProps = state => ({ 54 | filters: state.filters.items 55 | }); 56 | 57 | export default connect( 58 | mapStateToProps, 59 | { updateFilters } 60 | )(Filter); 61 | -------------------------------------------------------------------------------- /e-Commerce-Client/src/components/Shelf/Filter/style.scss: -------------------------------------------------------------------------------- 1 | .filters { 2 | width: 15%; 3 | margin-right: 15px; 4 | 5 | .star-button-container { 6 | text-align: center; 7 | small { 8 | color: #aaa; 9 | margin-bottom: 8px; 10 | display: inline-block; 11 | } 12 | } 13 | 14 | .title { 15 | margin-top: 2px; 16 | margin-bottom: 20px; 17 | } 18 | 19 | &-available-size { 20 | display: inline-block; 21 | margin-bottom: 10px; 22 | /* Customize the label (the container) */ 23 | label { 24 | display: inline-block; 25 | position: relative; 26 | cursor: pointer; 27 | font-size: 22px; 28 | -webkit-user-select: none; 29 | -moz-user-select: none; 30 | -ms-user-select: none; 31 | user-select: none; 32 | width: 35px; 33 | height: 35px; 34 | font-size: 0.8em; 35 | margin-bottom: 8px; 36 | margin-right: 8px; 37 | border-radius: 50%; 38 | line-height: 35px; 39 | text-align: center; 40 | 41 | /* On mouse-over, add a grey background color */ 42 | &:hover input ~ .checkmark { 43 | border: 1px solid #1b1a20; 44 | } 45 | 46 | /* When the checkbox is checked, add a blue background */ 47 | & input:checked ~ .checkmark { 48 | background-color: #1b1a20; 49 | color: #ececec; 50 | } 51 | 52 | /* Show the checkmark when checked */ 53 | & input:checked ~ .checkmark:after { 54 | display: block; 55 | } 56 | 57 | input { 58 | position: absolute; 59 | opacity: 0; 60 | cursor: pointer; 61 | } 62 | 63 | /* Create a custom checkbox */ 64 | .checkmark { 65 | position: absolute; 66 | top: 0; 67 | left: 0; 68 | width: 35px; 69 | height: 35px; 70 | font-size: 0.8em; 71 | border-radius: 50%; 72 | line-height: 35px; 73 | text-align: center; 74 | color: #1b1a20; 75 | background-color: #ececec; 76 | 77 | border: 1px solid transparent; 78 | } 79 | } 80 | } 81 | } 82 | -------------------------------------------------------------------------------- /e-Commerce-Client/src/components/Shelf/ProductList/Product/index.js: -------------------------------------------------------------------------------- 1 | import React from 'react'; 2 | import PropTypes from 'prop-types'; 3 | import { connect } from 'react-redux'; 4 | 5 | import Thumb from '../../../Thumb'; 6 | import { formatPrice } from '../../../../services/util'; 7 | import { addProduct } from '../../../../services/cart/actions'; 8 | 9 | const Product = ({ product, addProduct }) => { 10 | product.quantity = 1; 11 | 12 | let formattedPrice = formatPrice(product.price, product.currencyId); 13 | 14 | let productInstallment; 15 | 16 | if (!!product.installments) { 17 | const installmentPrice = product.price / product.installments; 18 | 19 | productInstallment = ( 20 |
21 | or {product.installments} x 22 | 23 | {product.currencyFormat} 24 | {formatPrice(installmentPrice, product.currencyId)} 25 | 26 |
27 | ); 28 | } 29 | 30 | return ( 31 |
addProduct(product)} 34 | data-sku={product.sku} 35 | > 36 | {product.isFreeShipping && ( 37 |
Free shipping
38 | )} 39 | 44 |

{product.title}

45 |
46 |
47 | {product.currencyFormat} 48 | {formattedPrice.substr(0, formattedPrice.length - 3)} 49 | {formattedPrice.substr(formattedPrice.length - 3, 3)} 50 |
51 | {productInstallment} 52 |
53 |
Add to cart
54 |
55 | ); 56 | }; 57 | 58 | Product.propTypes = { 59 | product: PropTypes.object.isRequired, 60 | addProduct: PropTypes.func.isRequired 61 | }; 62 | 63 | export default connect( 64 | null, 65 | { addProduct } 66 | )(Product); 67 | -------------------------------------------------------------------------------- /e-Commerce-Client/src/components/Shelf/ProductList/index.js: -------------------------------------------------------------------------------- 1 | import React from 'react'; 2 | 3 | import Product from './Product'; 4 | 5 | const ProductList = ({ products }) => { 6 | return products.map(p => { 7 | return ; 8 | }); 9 | }; 10 | 11 | export default ProductList; 12 | -------------------------------------------------------------------------------- /e-Commerce-Client/src/components/Shelf/ShelfHeader/index.js: -------------------------------------------------------------------------------- 1 | import React from 'react'; 2 | import PropTypes from 'prop-types'; 3 | 4 | import Sort from '../Sort'; 5 | 6 | const ShelfHeader = props => { 7 | return ( 8 |
9 | 10 | {props.productsLength} Product(s) found. 11 | 12 | 13 |
14 | ); 15 | }; 16 | 17 | ShelfHeader.propTypes = { 18 | productsLength: PropTypes.number.isRequired 19 | }; 20 | 21 | export default ShelfHeader; 22 | -------------------------------------------------------------------------------- /e-Commerce-Client/src/components/Shelf/Sort/index.js: -------------------------------------------------------------------------------- 1 | import React, { Component } from 'react'; 2 | import PropTypes from 'prop-types'; 3 | 4 | import { connect } from 'react-redux'; 5 | import { updateSort } from '../../../services/sort/actions'; 6 | import Selectbox from '../../Selectbox'; 7 | 8 | const sortBy = [ 9 | { value: '', label: 'Select' }, 10 | { value: 'lowestprice', label: 'Lowest to highest' }, 11 | { value: 'highestprice', label: 'Highest to lowest' } 12 | ]; 13 | 14 | class Sort extends Component { 15 | static propTypes = { 16 | updateSort: PropTypes.func.isRequired, 17 | sort: PropTypes.string.isRequired 18 | }; 19 | 20 | handleSort = value => { 21 | this.props.updateSort(value); 22 | }; 23 | 24 | render() { 25 | return ( 26 |
27 | Order by 28 | 29 |
30 | ); 31 | } 32 | } 33 | 34 | const mapStateToProps = state => ({ 35 | sort: state.sort.type 36 | }); 37 | 38 | export default connect( 39 | mapStateToProps, 40 | { updateSort } 41 | )(Sort); 42 | -------------------------------------------------------------------------------- /e-Commerce-Client/src/components/Shelf/index.js: -------------------------------------------------------------------------------- 1 | import React, { Component } from 'react'; 2 | import PropTypes from 'prop-types'; 3 | import { connect } from 'react-redux'; 4 | 5 | import { fetchProducts } from '../../services/shelf/actions'; 6 | 7 | import Spinner from '../Spinner'; 8 | import ShelfHeader from './ShelfHeader'; 9 | import ProductList from './ProductList'; 10 | 11 | import './style.scss'; 12 | 13 | class Shelf extends Component { 14 | static propTypes = { 15 | fetchProducts: PropTypes.func.isRequired, 16 | products: PropTypes.array.isRequired, 17 | filters: PropTypes.array, 18 | sort: PropTypes.string 19 | }; 20 | 21 | state = { 22 | isLoading: false 23 | }; 24 | 25 | componentDidMount() { 26 | this.handleFetchProducts(); 27 | } 28 | 29 | componentWillReceiveProps(nextProps) { 30 | const { filters: nextFilters, sort: nextSort } = nextProps; 31 | 32 | if (nextFilters !== this.props.filters) { 33 | this.handleFetchProducts(nextFilters, undefined); 34 | } 35 | 36 | if (nextSort !== this.props.sort) { 37 | this.handleFetchProducts(undefined, nextSort); 38 | } 39 | } 40 | 41 | handleFetchProducts = ( 42 | filters = this.props.filters, 43 | sort = this.props.sort 44 | ) => { 45 | this.setState({ isLoading: true }); 46 | this.props.fetchProducts(filters, sort, () => { 47 | this.setState({ isLoading: false }); 48 | }); 49 | }; 50 | 51 | render() { 52 | const { products } = this.props; 53 | const { isLoading } = this.state; 54 | 55 | return ( 56 | 57 | {isLoading && } 58 |
59 | 60 | 61 |
62 |
63 | ); 64 | } 65 | } 66 | 67 | const mapStateToProps = state => ({ 68 | products: state.shelf.products, 69 | filters: state.filters.items, 70 | sort: state.sort.type 71 | }); 72 | 73 | export default connect( 74 | mapStateToProps, 75 | { fetchProducts } 76 | )(Shelf); 77 | -------------------------------------------------------------------------------- /e-Commerce-Client/src/components/Shelf/style.scss: -------------------------------------------------------------------------------- 1 | .shelf-container { 2 | display: flex; 3 | flex-wrap: wrap; 4 | width: 85%; 5 | min-height: 600px; 6 | 7 | &-header { 8 | width: 100%; 9 | margin-bottom: 10px; 10 | 11 | .products-found { 12 | float: left; 13 | margin: 0; 14 | margin-top: 8px; 15 | } 16 | 17 | .sort { 18 | float: right; 19 | 20 | select { 21 | background-color: #fff; 22 | outline: none; 23 | border: 1px solid #ececec; 24 | border-radius: 2px; 25 | margin-left: 10px; 26 | width: auto; 27 | height: 35px; 28 | cursor: pointer; 29 | 30 | &:hover { 31 | border: 1px solid #5b5a5e; 32 | } 33 | } 34 | } 35 | } 36 | 37 | .shelf-item { 38 | width: 25%; 39 | position: relative; 40 | text-align: center; 41 | box-sizing: border-box; 42 | padding: 10px; 43 | margin-bottom: 30px; 44 | border: 1px solid transparent; 45 | cursor: pointer; 46 | 47 | &:hover { 48 | border: 1px solid #eee; 49 | 50 | .shelf-item__buy-btn { 51 | background-color: #eabf00; 52 | } 53 | } 54 | 55 | .shelf-stopper { 56 | position: absolute; 57 | color: #ececec; 58 | top: 10px; 59 | right: 10px; 60 | padding: 5px; 61 | font-size: 0.6em; 62 | background-color: #1b1a20; 63 | cursor: default; 64 | } 65 | 66 | &__thumb { 67 | img { 68 | width: 100%; 69 | } 70 | } 71 | 72 | &__title { 73 | position: relative; 74 | padding: 0 20px; 75 | height: 45px; 76 | 77 | &::before { 78 | content: ''; 79 | width: 20px; 80 | height: 2px; 81 | background-color: #eabf00; 82 | position: absolute; 83 | bottom: 0; 84 | left: 50%; 85 | margin-left: -10px; 86 | } 87 | } 88 | 89 | &__price { 90 | height: 60px; 91 | 92 | .val { 93 | b { 94 | font-size: 1.5em; 95 | margin-left: 5px; 96 | } 97 | } 98 | 99 | .installment { 100 | color: #9c9b9b; 101 | } 102 | } 103 | 104 | &__buy-btn { 105 | background-color: #1b1a20; 106 | color: #fff; 107 | padding: 15px 0; 108 | margin-top: 10px; 109 | cursor: pointer; 110 | // border-bottom: 2px solid #151419; 111 | 112 | transition: background-color 0.2s; 113 | } 114 | } 115 | } 116 | -------------------------------------------------------------------------------- /e-Commerce-Client/src/components/Spinner/index.js: -------------------------------------------------------------------------------- 1 | import React from 'react'; 2 | 3 | import './style.scss'; 4 | 5 | export default () => ( 6 |
7 |
8 |
9 |
10 |
11 |
12 | ); 13 | -------------------------------------------------------------------------------- /e-Commerce-Client/src/components/Spinner/style.scss: -------------------------------------------------------------------------------- 1 | .spinner.lds-ring { 2 | position: fixed; 3 | top: 50%; 4 | left: 50%; 5 | margin-left: -32px; 6 | margin-top: -32px; 7 | width: 64px; 8 | height: 64px; 9 | z-index: 10; 10 | border-radius: 5px; 11 | background-color: #000; 12 | 13 | div { 14 | box-sizing: border-box; 15 | display: block; 16 | position: absolute; 17 | width: 51px; 18 | height: 51px; 19 | margin: 6px; 20 | border: 6px solid #fff; 21 | border-radius: 50%; 22 | animation: lds-ring 1.2s cubic-bezier(0.5, 0, 0.5, 1) infinite; 23 | border-color: #fff transparent transparent transparent; 24 | &:nth-child(1) { 25 | animation-delay: -0.45s; 26 | } 27 | &:nth-child(2) { 28 | animation-delay: -0.3s; 29 | } 30 | &:nth-child(3) { 31 | animation-delay: -0.15s; 32 | } 33 | } 34 | 35 | @keyframes lds-ring { 36 | 0% { 37 | transform: rotate(0deg); 38 | } 39 | 40 | 100% { 41 | transform: rotate(360deg); 42 | } 43 | } 44 | } 45 | -------------------------------------------------------------------------------- /e-Commerce-Client/src/components/Thumb/index.js: -------------------------------------------------------------------------------- 1 | import React from 'react'; 2 | import PropTypes from 'prop-types'; 3 | 4 | const Thumb = props => { 5 | return ( 6 |
7 | {props.alt} 8 |
9 | ); 10 | }; 11 | 12 | Thumb.propTypes = { 13 | alt: PropTypes.string, 14 | title: PropTypes.string, 15 | classes: PropTypes.string, 16 | src: PropTypes.string.isRequired 17 | }; 18 | 19 | export default Thumb; 20 | -------------------------------------------------------------------------------- /e-Commerce-Client/src/config/index.js: -------------------------------------------------------------------------------- 1 | import server from '../config/server'; 2 | 3 | export default { 4 | // server: server 5 | 6 | social: { 7 | facebook: server.url + 'auth/login/facebook', 8 | google: server.url + 'auth/login/google', 9 | twitter: server.url + 'auth/login/twitter', 10 | instagram: server.url + 'auth/login/instagram', 11 | }, 12 | 13 | google: { 14 | api_key: '***', 15 | }, 16 | 17 | 18 | } 19 | -------------------------------------------------------------------------------- /e-Commerce-Client/src/config/server.js: -------------------------------------------------------------------------------- 1 | export default { 2 | url: 'http://ms-commerce.com/api/v1/', 3 | register: 'auth/register', 4 | login: 'auth/login', 5 | validate_auth: 'auth/validate', 6 | reset_password: 'auth/reset-password', 7 | userfetch : 'user/fetch', 8 | users: 'users', 9 | user : 'user', 10 | changeRrole: 'user/change-role' 11 | } 12 | -------------------------------------------------------------------------------- /e-Commerce-Client/src/index.js: -------------------------------------------------------------------------------- 1 | import React from 'react'; 2 | import {render} from 'react-dom'; 3 | import { 4 | Router, 5 | Route, 6 | hashHistory, 7 | IndexRoute 8 | } from 'react-router'; 9 | 10 | import {notLoggedIn} from './util/middleware/index'; 11 | 12 | import { Provider } from 'react-redux'; 13 | 14 | import store from './services/store'; 15 | // ------------------Login pages-------------------// 16 | import RegisterPage from './services/auth/Register'; 17 | import LoginPage from './services/auth/Login'; 18 | import LogoutPage from './services/auth/Logout'; 19 | import ResetPasswordPage from './services/auth/ResetPassword'; 20 | import ValidateTokenPage from './services/auth/ValidateToken'; 21 | import AuthLayout from './layout/Auth'; 22 | //------------------dashboard-------------------// 23 | import PublicLayout from './layout/Public'; 24 | import PublicIndexPage from './components/App'; 25 | 26 | render(( 27 | 28 | 29 | 30 | 31 | 32 | 33 | 37 | 38 | 39 | 40 | 44 | 45 | 46 | 47 | ), document.getElementById("root")); 48 | -------------------------------------------------------------------------------- /e-Commerce-Client/src/index.scss: -------------------------------------------------------------------------------- 1 | @import url('https://fonts.googleapis.com/css?family=Roboto'); 2 | 3 | body { 4 | margin: 0; 5 | padding: 0; 6 | font-family: -apple-system, BlinkMacSystemFont, 'Segoe UI', 'Roboto', 'Oxygen', 7 | 'Ubuntu', 'Cantarell', 'Fira Sans', 'Droid Sans', 'Helvetica Neue', 8 | sans-serif; 9 | -webkit-font-smoothing: antialiased; 10 | -moz-osx-font-smoothing: grayscale; 11 | } 12 | 13 | body { 14 | margin: 0; 15 | color: #1b1a20; 16 | font-family: 'Roboto', sans-serif; 17 | } 18 | 19 | main { 20 | display: flex; 21 | padding: 20px 2%; 22 | max-width: 1200px; 23 | margin: 50px auto 0 auto; 24 | } 25 | 26 | @media only screen and (max-width: 1024px) { 27 | body { 28 | .filters { 29 | width: 20%; 30 | } 31 | 32 | .shelf-container { 33 | width: 80%; 34 | 35 | .shelf-item { 36 | width: 33.33%; 37 | } 38 | } 39 | } 40 | } 41 | 42 | @media only screen and (max-width: 640px) { 43 | body { 44 | .filters { 45 | width: 25%; 46 | } 47 | 48 | .shelf-container { 49 | width: 75%; 50 | 51 | .shelf-item { 52 | width: 50%; 53 | padding: 10px; 54 | 55 | &__title { 56 | margin-top: 5px; 57 | padding: 0; 58 | } 59 | } 60 | } 61 | 62 | .float-cart { 63 | width: 100%; 64 | right: -100%; 65 | 66 | &--open { 67 | right: 0; 68 | } 69 | 70 | &__close-btn { 71 | left: 0px; 72 | z-index: 2; 73 | background-color: #1b1a20; 74 | } 75 | 76 | &__header { 77 | padding: 25px 0; 78 | } 79 | } 80 | } 81 | } 82 | 83 | @media only screen and (max-width: 460px) { 84 | body { 85 | main { 86 | display: flex; 87 | flex-wrap: wrap; 88 | padding: 2%; 89 | margin-top: 42px; 90 | } 91 | 92 | .filters { 93 | width: 100%; 94 | margin-right: 0; 95 | text-align: center; 96 | 97 | .title { 98 | margin-bottom: 15px; 99 | } 100 | } 101 | 102 | .shelf-container-header { 103 | .products-found { 104 | width: 100%; 105 | text-align: center; 106 | margin: 10px 0; 107 | } 108 | 109 | .sort { 110 | width: 100%; 111 | text-align: center; 112 | } 113 | } 114 | 115 | .shelf-container { 116 | width: 100%; 117 | 118 | .shelf-item { 119 | width: 50%; 120 | 121 | &__buy-btn { 122 | display: none; 123 | } 124 | } 125 | } 126 | } 127 | } 128 | -------------------------------------------------------------------------------- /e-Commerce-Client/src/layout/Auth.js: -------------------------------------------------------------------------------- 1 | import React from 'react'; 2 | let AuthLayout = (props) => { 3 | 4 | let backgroundChoices = ['bg1', 'bg4', 'bg3', 'bg4', 'bg5']; 5 | 6 | let background_image; 7 | delete localStorage.auth_background; 8 | if (!localStorage.auth_background) { 9 | background_image = backgroundChoices[Math.floor(Math.random() * backgroundChoices.length)]; 10 | localStorage.auth_background = background_image; 11 | } else { 12 | background_image = localStorage.auth_background; 13 | } 14 | 15 | return ( 16 |
17 | {props.children} 18 |
19 | ) 20 | 21 | } 22 | 23 | export default AuthLayout; 24 | -------------------------------------------------------------------------------- /e-Commerce-Client/src/layout/Public.js: -------------------------------------------------------------------------------- 1 | import React, {Component} from 'react'; 2 | import axios from 'axios'; 3 | import * as API from '../api/index'; 4 | import {message} from 'antd'; 5 | import Auth from '../util/middleware/auth'; 6 | import {connect} from 'react-redux'; 7 | import * as Action from '../services/auth/action'; 8 | 9 | const mapStateToProps = (state, ownProps) => { 10 | return { 11 | user: state.auth.get('user') 12 | } 13 | } 14 | const mapDispatchToProps = (dispatch, ownProps) => { 15 | return { 16 | authUpdateUserData: (data) => dispatch(Action.authUpdateUserData(data)) 17 | } 18 | } 19 | 20 | class PublicPage extends Component { 21 | 22 | constructor(props) { 23 | super(props); 24 | let access_token = Auth.getAccessToken() 25 | if (access_token) { 26 | API.setAuthToken(access_token); 27 | axios.get(API.url('validate_auth')) 28 | .then((response) => { 29 | const user = response.data.user; 30 | if (response.data.statusCode === 200 && response.data.success === true) { 31 | Auth.setAccessToken(access_token); 32 | this.props.authUpdateUserData(response.data.user); 33 | console.log(response.data.user); 34 | this.loadUserProfile(user.email); 35 | } else { 36 | message.error('Invalid auth token, please try logging in again', 3); 37 | Auth.deleteAccessToken(); 38 | API.setAuthToken(); 39 | } 40 | }) 41 | .catch((response) => { 42 | console.log('catch error', response); 43 | }); 44 | } 45 | } 46 | render() { 47 | return ( 48 |
49 | {this.props.children} 50 |
51 | ) 52 | } 53 | } 54 | 55 | const PublicRoutePage = connect(mapStateToProps, mapDispatchToProps)(PublicPage); 56 | 57 | export default PublicRoutePage; 58 | -------------------------------------------------------------------------------- /e-Commerce-Client/src/services/auth/Logout.js: -------------------------------------------------------------------------------- 1 | 2 | import React from 'react'; 3 | import { Link } from 'react-router'; 4 | import { connect } from 'react-redux'; 5 | import * as Action from './action'; 6 | 7 | import Auth from '../../util/middleware/auth'; 8 | import { message } from 'antd'; 9 | 10 | const mapStateToProps = ( state, ownProps ) => { 11 | return { 12 | user: state.auth.get('user'), 13 | } 14 | } 15 | 16 | const mapDispatchToProps = ( dispatch, ownProps ) => { 17 | return { 18 | authResetUserData: () => dispatch( Action.authResetUserData() ), 19 | reduxResetState: () => dispatch( Action.reduxResetState() ), 20 | 21 | } 22 | } 23 | 24 | let LogoutPage = (props) => { 25 | if ( props.user.size > 0 ) { 26 | setTimeout( () => { 27 | Auth.logout(); 28 | props.authResetUserData(); 29 | props.reduxResetState(); 30 | message.info('You have been successfully logged out', 3); 31 | }, 20 ); 32 | } 33 | const ui_message_logout = ( 34 |
35 |

Logging you out right now...

36 |

please be patient.

37 |
38 | ); 39 | 40 | const ui_message_done = ( 41 |
42 |
You have been successfully logged out.
43 | Login Again → 44 |
45 | ); 46 | 47 | return ( 48 | 49 |
50 |
51 |
52 |
53 |
54 |
55 | 56 |
57 |
58 |
59 |
64 |
65 |
66 | { props.user.get('id') ? ui_message_logout : ui_message_done } 67 |
68 |
69 |
70 |
71 |
72 |
73 |
74 |
75 |
76 |
77 | ) 78 | 79 | } 80 | 81 | const ConnectLogoutPage = connect( 82 | mapStateToProps, 83 | mapDispatchToProps 84 | )(LogoutPage) 85 | 86 | export default ConnectLogoutPage; 87 | -------------------------------------------------------------------------------- /e-Commerce-Client/src/services/auth/ValidateToken.js: -------------------------------------------------------------------------------- 1 | import React from 'react'; 2 | import {connect} from 'react-redux'; 3 | import * as Action from './action'; 4 | import {Link, hashHistory} from 'react-router'; 5 | import {message, Spin} from 'antd'; 6 | 7 | import * as API from '../../api' 8 | import Auth from '../../util/middleware/auth'; 9 | import axios from 'axios'; 10 | import jwt from 'jsonwebtoken'; 11 | 12 | const mapStateToProps = (state, ownProps) => { 13 | return {query: ownProps.location.query} 14 | } 15 | 16 | const mapDispatchToProps = (dispatch, ownProps) => { 17 | return { 18 | authUpdateUserData: (data) => dispatch(Action.authUpdateUserData(data)) 19 | } 20 | } 21 | 22 | let ValidateTokenPage = (props) => { 23 | 24 | const access_token = props.query.token; 25 | console.log(access_token); 26 | if (access_token) { 27 | API.setAuthToken(access_token); 28 | axios 29 | .get(API.url('validate_auth')) 30 | .then((response) => { 31 | if (response.data.statusCode === 200 && response.data.success === true) { 32 | message.success('Successfully logged in.', 3); 33 | Auth.setAccessToken(access_token); 34 | const userToken = jwt.decode(access_token); 35 | props.authUpdateUserData(userToken); 36 | 37 | if(userToken.type === '1'){ 38 | // hashHistory.push(routes.user_dashboard); 39 | } else { 40 | // hashHistory.push(routes.vendor_dashboard); 41 | } 42 | 43 | 44 | } else { 45 | message.error('Invalid auth token, please try logging in again', 3); 46 | Auth.deleteAccessToken(); 47 | API.setAuthToken(); 48 | hashHistory.push('/auth/login'); 49 | } 50 | }) 51 | .catch((response) => { 52 | console.log('catch error', response); 53 | }); 54 | 55 | } 56 | 57 | const ui_logo = ( 58 | 59 | ); 60 | 61 | const ui_no_access_token = ( 62 |
63 |

Access token not present

64 | Try logging in again → 67 |
68 | ); 69 | 70 | const ui_verifying = ( 71 |
72 |

Verifying your account, Please wait...

73 |
74 | 75 |
76 |
77 | ); 78 | 79 | return ( 80 |
81 |
82 | {ui_logo} 83 | {access_token 84 | ? ui_verifying 85 | : ui_no_access_token} 86 |
87 |
88 | ) 89 | 90 | } 91 | 92 | const ConnectValidateTokenPage = connect(mapStateToProps, mapDispatchToProps)(ValidateTokenPage) 93 | 94 | export default ConnectValidateTokenPage; 95 | -------------------------------------------------------------------------------- /e-Commerce-Client/src/services/auth/actionTypes.js: -------------------------------------------------------------------------------- 1 | export const HELLO_WORLD = 'HELLO_WORLD'; 2 | export const REDUX_RESET_STATE = 'REDUX_RESET_STATE'; 3 | 4 | //ADMIN AUTH 5 | export const ADMIN_GET_ALL_MENU = 'GET_ALL_MENU'; 6 | export const ADMIN_GET_ALL_MENU_SUCCESS = 'GET_ALL_MENU_SUCCESS'; 7 | export const ADMIN_UPDATE_NAVPATH = 'UPDATE_NAVPATH'; 8 | // ADMIN LOGIN 9 | export const ADMIN_FETCH_PROFILE_PENDING = 'FETCH_PROFILE_PENDING'; 10 | export const ADMIN_FETCH_PROFILE_SUCCESS = 'FETCH_PROFILE_SUCCESS'; 11 | export const ADMIN_LOGIN_PENDING = 'LOGIN_PENDING'; 12 | export const ADMIN_LOGIN_SUCCESS = 'LOGIN_SUCCESS'; 13 | export const ADMIN_LOGIN_ERROR = 'LOGIN_ERROR'; 14 | 15 | export const LOGOUT_SUCCESS = 'LOGOUT_SUCCESS'; 16 | 17 | // auth constants 18 | export const AUTH_UPDATE_REGISTER_FORM_FIELD = 'AUTH_UPDATE_REGISTER_FORM_FIELD'; 19 | export const AUTH_SUBMIT_REGISTER_FORM = 'AUTH_SUBMIT_REGISTER_FORM'; 20 | export const AUTH_INVALIDATE_REGISTER_FORM = 'AUTH_INVALIDATE_REGISTER_FORM'; 21 | export const AUTH_RESET_REGISTER_FORM_FIELDS = 'AUTH_RESET_REGISTER_FORM_FIELDS'; 22 | 23 | export const AUTH_UPDATE_USER_DATA = 'AUTH_UPDATE_USER_DATA'; 24 | export const AUTH_RESET_USER_DATA = 'AUTH_RESET_USER_DATA'; 25 | export const AUTH_UPDATE_USER_FIELD = 'AUTH_UPDATE_USER_FIELD'; 26 | 27 | export const AUTH_UPDATE_LOGIN_FORM_FIELD = 'AUTH_UPDATE_LOGIN_FORM_FIELD'; 28 | export const AUTH_SUBMIT_LOGIN_FORM = 'AUTH_SUBMIT_LOGIN_FORM'; 29 | export const AUTH_INVALIDATE_LOGIN_FORM = 'AUTH_INVALIDATE_LOGIN_FORM'; 30 | export const AUTH_RESET_LOGIN_FORM_FIELDS = 'AUTH_RESET_LOGIN_FORM_FIELDS'; 31 | 32 | export const AUTH_SUBMIT_RESET_PASSWORD_FORM = 'AUTH_SUBMIT_RESET_PASSWORD_FORM'; 33 | export const AUTH_UPDATE_RESET_PASSWORD_STATUS_FIELD = 'AUTH_UPDATE_RESET_PASSWORD_STATUS_FIELD'; 34 | export const AUTH_INVALIDATE_RESET_PASSWORD_FORM = 'AUTH_INVALIDATE_RESET_PASSWORD_FORM'; 35 | 36 | export const USER_PROFILE_LOADED = 'USER_PROFILE_LOADED'; 37 | export const USER_UPDATE_PROFILE = 'USER_UPDATE_PROFILE'; 38 | export const USER_UPDATE_PROFILE_FIELD = 'USER_UPDATE_PROFILE_FIELD'; 39 | export const USER_DELETE_PROFILE_FIELD = 'USER_DELETE_PROFILE_FIELD'; 40 | 41 | // UI MODALS 42 | export const UI_MODALS_UPDATE_FIELD = 'UI_MODALS_UPDATE_FIELD'; 43 | export const UI_PROCESSING_UPDATE_FIELD = 'UI_PROCESSING_UPDATE_FIELD'; 44 | export const UI_LOADED_UPDATE_FIELD = 'UI_LOADED_UPDATE_FIELD'; 45 | 46 | // app constants 47 | export const APP_CONFIG_TOGGLE_ASIDE = 'APP_CONFIG_TOGGLE_ASIDE'; 48 | export const INITIATE_LOADING = 'INITIATE_LOADING'; 49 | export const STOP_LOADING = 'STOP_LOADING'; 50 | 51 | export const COMMON_LOAD_VEHICLES = 'COMMON_LOAD_VEHICLES' 52 | export const COMMON_LOAD_REVIEWS = 'COMMON_LOAD_REVIEWS' 53 | export const COMMON_LOAD_VENDORS = 'COMMON_LOAD_VENDORS' 54 | export const COMMON_SET_GEO= 'COMMON_SET_GEO' 55 | export const COMMON_SET_GLOBAL_COUNT= 'COMMON_SET_GLOBAL_COUNT' 56 | 57 | export const UPDATE_FIELD = 'UPDATE_FIELD'; 58 | export const UPDATE_SECTION = 'UPDATE_SECTION'; 59 | export const UPDATE_TABS = 'UPDATE_TABS'; 60 | export const UPDATE_LOADED = 'UPDATE_LOADED'; 61 | export const SET_MESSAGES = 'SET_MESSAGES'; 62 | export const UPDATE_MESSAGES = 'UPDATE_MESSAGES'; 63 | export const SET_USER_LANGUAGE = 'SET_USER_LANGUAGE'; 64 | -------------------------------------------------------------------------------- /e-Commerce-Client/src/services/auth/auth.scss: -------------------------------------------------------------------------------- 1 | .errorMessage { 2 | color: #b83636 3 | } -------------------------------------------------------------------------------- /e-Commerce-Client/src/services/cart/actionTypes.js: -------------------------------------------------------------------------------- 1 | export const LOAD_CART = 'LOAD_CART'; 2 | export const ADD_PRODUCT = 'ADD_PRODUCT'; 3 | export const REMOVE_PRODUCT = 'REMOVE_PRODUCT'; 4 | export const UPDATE_CART = 'UPDATE_CART'; 5 | -------------------------------------------------------------------------------- /e-Commerce-Client/src/services/cart/actions.js: -------------------------------------------------------------------------------- 1 | import { LOAD_CART, ADD_PRODUCT, REMOVE_PRODUCT } from './actionTypes'; 2 | 3 | export const loadCart = products => ({ 4 | type: LOAD_CART, 5 | payload: products 6 | }); 7 | 8 | export const addProduct = product => ({ 9 | type: ADD_PRODUCT, 10 | payload: product 11 | }); 12 | 13 | export const removeProduct = product => ({ 14 | type: REMOVE_PRODUCT, 15 | payload: product 16 | }); 17 | -------------------------------------------------------------------------------- /e-Commerce-Client/src/services/cart/reducer.js: -------------------------------------------------------------------------------- 1 | import { LOAD_CART, ADD_PRODUCT, REMOVE_PRODUCT } from './actionTypes'; 2 | 3 | const initialState = { 4 | products: [] 5 | }; 6 | 7 | export default function(state = initialState, action) { 8 | switch (action.type) { 9 | case LOAD_CART: 10 | return { 11 | ...state, 12 | products: action.payload 13 | }; 14 | case ADD_PRODUCT: 15 | return { 16 | ...state, 17 | productToAdd: Object.assign({}, action.payload) 18 | }; 19 | case REMOVE_PRODUCT: 20 | return { 21 | ...state, 22 | productToRemove: Object.assign({}, action.payload) 23 | }; 24 | default: 25 | return state; 26 | } 27 | } 28 | -------------------------------------------------------------------------------- /e-Commerce-Client/src/services/filters/actionTypes.js: -------------------------------------------------------------------------------- 1 | export const UPDATE_FILTER = 'UPDATE_FILTER'; 2 | -------------------------------------------------------------------------------- /e-Commerce-Client/src/services/filters/actions.js: -------------------------------------------------------------------------------- 1 | import { UPDATE_FILTER } from './actionTypes'; 2 | 3 | export const updateFilters = filters => ({ 4 | type: UPDATE_FILTER, 5 | payload: filters 6 | }); 7 | -------------------------------------------------------------------------------- /e-Commerce-Client/src/services/filters/reducer.js: -------------------------------------------------------------------------------- 1 | import { UPDATE_FILTER } from './actionTypes'; 2 | 3 | const initialState = { 4 | item: [] 5 | }; 6 | 7 | export default function(state = initialState, action) { 8 | switch (action.type) { 9 | case UPDATE_FILTER: 10 | return { 11 | ...state, 12 | items: action.payload 13 | }; 14 | default: 15 | return state; 16 | } 17 | } 18 | -------------------------------------------------------------------------------- /e-Commerce-Client/src/services/reducers.js: -------------------------------------------------------------------------------- 1 | import { combineReducers } from 'redux'; 2 | import shelfReducer from './shelf/reducer'; 3 | import cartReducer from './cart/reducer'; 4 | import totalReducer from './total/reducer'; 5 | import filtersReducer from './filters/reducer'; 6 | import sortReducer from './sort/reducer'; 7 | import authReducer from './auth/reducer'; 8 | 9 | 10 | export default combineReducers({ 11 | shelf: shelfReducer, 12 | cart: cartReducer, 13 | total: totalReducer, 14 | filters: filtersReducer, 15 | sort: sortReducer, 16 | auth:authReducer 17 | }); 18 | -------------------------------------------------------------------------------- /e-Commerce-Client/src/services/shelf/actionTypes.js: -------------------------------------------------------------------------------- 1 | export const FETCH_PRODUCTS = 'FETCH_PRODUCTS'; 2 | -------------------------------------------------------------------------------- /e-Commerce-Client/src/services/shelf/actions.js: -------------------------------------------------------------------------------- 1 | import { FETCH_PRODUCTS } from './actionTypes'; 2 | import axios from 'axios'; 3 | 4 | import { productsAPI } from '../util'; 5 | 6 | const compare = { 7 | lowestprice: (a, b) => { 8 | if (a.price < b.price) return -1; 9 | if (a.price > b.price) return 1; 10 | return 0; 11 | }, 12 | highestprice: (a, b) => { 13 | if (a.price > b.price) return -1; 14 | if (a.price < b.price) return 1; 15 | return 0; 16 | } 17 | }; 18 | 19 | export const fetchProducts = (filters, sortBy, callback) => dispatch => { 20 | return axios 21 | .get(productsAPI) 22 | .then(res => { 23 | let { products } = res.data; 24 | 25 | if (!!filters && filters.length > 0) { 26 | products = products.filter(p => 27 | filters.find(f => p.availableSizes.find(size => size === f)) 28 | ); 29 | } 30 | 31 | if (!!sortBy) { 32 | products = products.sort(compare[sortBy]); 33 | } 34 | 35 | if (!!callback) { 36 | callback(); 37 | } 38 | 39 | return dispatch({ 40 | type: FETCH_PRODUCTS, 41 | payload: products 42 | }); 43 | }) 44 | .catch(err => { 45 | console.log('Could not fetch products. Try again later.'); 46 | }); 47 | }; 48 | -------------------------------------------------------------------------------- /e-Commerce-Client/src/services/shelf/reducer.js: -------------------------------------------------------------------------------- 1 | import { FETCH_PRODUCTS } from './actionTypes'; 2 | 3 | const initialState = { 4 | products: [] 5 | }; 6 | 7 | export default function(state = initialState, action) { 8 | switch (action.type) { 9 | case FETCH_PRODUCTS: 10 | return { 11 | ...state, 12 | products: action.payload 13 | }; 14 | default: 15 | return state; 16 | } 17 | } 18 | -------------------------------------------------------------------------------- /e-Commerce-Client/src/services/sort/actionTypes.js: -------------------------------------------------------------------------------- 1 | export const UPDATE_SORT = 'UPDATE_SORT'; 2 | -------------------------------------------------------------------------------- /e-Commerce-Client/src/services/sort/actions.js: -------------------------------------------------------------------------------- 1 | import { UPDATE_SORT } from './actionTypes'; 2 | 3 | export const updateSort = sort => ({ 4 | type: UPDATE_SORT, 5 | payload: sort 6 | }); 7 | -------------------------------------------------------------------------------- /e-Commerce-Client/src/services/sort/reducer.js: -------------------------------------------------------------------------------- 1 | import { UPDATE_SORT } from './actionTypes'; 2 | 3 | const initialState = { 4 | type: '' 5 | }; 6 | 7 | export default function(state = initialState, action) { 8 | switch (action.type) { 9 | case UPDATE_SORT: 10 | return { 11 | ...state, 12 | type: action.payload 13 | }; 14 | default: 15 | return state; 16 | } 17 | } 18 | -------------------------------------------------------------------------------- /e-Commerce-Client/src/services/store.js: -------------------------------------------------------------------------------- 1 | import { compose, createStore, applyMiddleware } from 'redux'; 2 | import thunk from 'redux-thunk'; 3 | import rootReducer from './reducers'; 4 | 5 | export default initialState => { 6 | initialState = 7 | JSON.parse(window.localStorage.getItem('state')) || initialState; 8 | 9 | const store = createStore( 10 | rootReducer, 11 | initialState, 12 | compose( applyMiddleware(thunk), window.devToolsExtension ? window.devToolsExtension() : f => f ) 13 | ); 14 | 15 | store.subscribe(() => { 16 | const state = store.getState(); 17 | const persist = { 18 | cart: state.cart, 19 | total: state.total 20 | }; 21 | 22 | window.localStorage.setItem('state', JSON.stringify(persist)); 23 | }); 24 | 25 | return store; 26 | }; 27 | -------------------------------------------------------------------------------- /e-Commerce-Client/src/services/total/actionTypes.js: -------------------------------------------------------------------------------- 1 | export const UPDATE_CART = 'UPDATE_CART'; 2 | -------------------------------------------------------------------------------- /e-Commerce-Client/src/services/total/actions.js: -------------------------------------------------------------------------------- 1 | import { UPDATE_CART } from './actionTypes'; 2 | 3 | export const updateCart = cartProducts => dispatch => { 4 | let productQuantity = cartProducts.reduce((sum, p) => { 5 | sum += p.quantity; 6 | return sum; 7 | }, 0); 8 | 9 | let totalPrice = cartProducts.reduce((sum, p) => { 10 | sum += p.price * p.quantity; 11 | return sum; 12 | }, 0); 13 | 14 | let installments = cartProducts.reduce((greater, p) => { 15 | greater = p.installments > greater ? p.installments : greater; 16 | return greater; 17 | }, 0); 18 | 19 | let cartTotal = { 20 | productQuantity, 21 | installments, 22 | totalPrice, 23 | currencyId: 'USD', 24 | currencyFormat: '$' 25 | }; 26 | 27 | dispatch({ 28 | type: UPDATE_CART, 29 | payload: cartTotal 30 | }); 31 | }; 32 | -------------------------------------------------------------------------------- /e-Commerce-Client/src/services/total/reducer.js: -------------------------------------------------------------------------------- 1 | import { UPDATE_CART } from './actionTypes'; 2 | 3 | const initialState = { 4 | data: { 5 | productQuantity: 0, 6 | installments: 0, 7 | totalPrice: 0, 8 | currencyId: 'USD', 9 | currencyFormat: '$' 10 | } 11 | }; 12 | 13 | export default function(state = initialState, action) { 14 | switch (action.type) { 15 | case UPDATE_CART: 16 | return { 17 | ...state, 18 | data: action.payload 19 | }; 20 | default: 21 | return state; 22 | } 23 | } 24 | -------------------------------------------------------------------------------- /e-Commerce-Client/src/services/util.js: -------------------------------------------------------------------------------- 1 | export const formatPrice = (x, currency) => { 2 | switch (currency) { 3 | case 'BRL': 4 | return x.toFixed(2).replace('.', ','); 5 | default: 6 | return x.toFixed(2); 7 | } 8 | }; 9 | 10 | export const productsAPI = 11 | 'http://ms-commerce.com/cart/v1/products'; 12 | -------------------------------------------------------------------------------- /e-Commerce-Client/src/setupTests.js: -------------------------------------------------------------------------------- 1 | import React from 'react'; 2 | import Enzyme, { shallow, render, mount } from 'enzyme'; 3 | import adapter from 'enzyme-adapter-react-16'; 4 | 5 | Enzyme.configure({ adapter: new adapter() }); 6 | 7 | /* Globals only for tests */ 8 | global.React = React; 9 | global.shallow = shallow; 10 | global.render = render; 11 | global.mount = mount; 12 | -------------------------------------------------------------------------------- /e-Commerce-Client/src/static/bag-icon.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/tkssharma/e-CommerseHub/044bb67bf47ef1808633ca8f5c0c9812d5fcdd44/e-Commerce-Client/src/static/bag-icon.png -------------------------------------------------------------------------------- /e-Commerce-Client/src/static/products/100_1.jpg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/tkssharma/e-CommerseHub/044bb67bf47ef1808633ca8f5c0c9812d5fcdd44/e-Commerce-Client/src/static/products/100_1.jpg -------------------------------------------------------------------------------- /e-Commerce-Client/src/static/products/100_2.jpg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/tkssharma/e-CommerseHub/044bb67bf47ef1808633ca8f5c0c9812d5fcdd44/e-Commerce-Client/src/static/products/100_2.jpg -------------------------------------------------------------------------------- /e-Commerce-Client/src/static/products/101_1.jpg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/tkssharma/e-CommerseHub/044bb67bf47ef1808633ca8f5c0c9812d5fcdd44/e-Commerce-Client/src/static/products/101_1.jpg -------------------------------------------------------------------------------- /e-Commerce-Client/src/static/products/101_2.jpg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/tkssharma/e-CommerseHub/044bb67bf47ef1808633ca8f5c0c9812d5fcdd44/e-Commerce-Client/src/static/products/101_2.jpg -------------------------------------------------------------------------------- /e-Commerce-Client/src/static/products/10412368723880252_1.jpg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/tkssharma/e-CommerseHub/044bb67bf47ef1808633ca8f5c0c9812d5fcdd44/e-Commerce-Client/src/static/products/10412368723880252_1.jpg -------------------------------------------------------------------------------- /e-Commerce-Client/src/static/products/10412368723880252_2.jpg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/tkssharma/e-CommerseHub/044bb67bf47ef1808633ca8f5c0c9812d5fcdd44/e-Commerce-Client/src/static/products/10412368723880252_2.jpg -------------------------------------------------------------------------------- /e-Commerce-Client/src/static/products/10547961582846888_1.jpg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/tkssharma/e-CommerseHub/044bb67bf47ef1808633ca8f5c0c9812d5fcdd44/e-Commerce-Client/src/static/products/10547961582846888_1.jpg -------------------------------------------------------------------------------- /e-Commerce-Client/src/static/products/10547961582846888_2.jpg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/tkssharma/e-CommerseHub/044bb67bf47ef1808633ca8f5c0c9812d5fcdd44/e-Commerce-Client/src/static/products/10547961582846888_2.jpg -------------------------------------------------------------------------------- /e-Commerce-Client/src/static/products/10686354557628304_1.jpg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/tkssharma/e-CommerseHub/044bb67bf47ef1808633ca8f5c0c9812d5fcdd44/e-Commerce-Client/src/static/products/10686354557628304_1.jpg -------------------------------------------------------------------------------- /e-Commerce-Client/src/static/products/10686354557628304_2.jpg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/tkssharma/e-CommerseHub/044bb67bf47ef1808633ca8f5c0c9812d5fcdd44/e-Commerce-Client/src/static/products/10686354557628304_2.jpg -------------------------------------------------------------------------------- /e-Commerce-Client/src/static/products/11033926921508488_1.jpg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/tkssharma/e-CommerseHub/044bb67bf47ef1808633ca8f5c0c9812d5fcdd44/e-Commerce-Client/src/static/products/11033926921508488_1.jpg -------------------------------------------------------------------------------- /e-Commerce-Client/src/static/products/11033926921508488_2.jpg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/tkssharma/e-CommerseHub/044bb67bf47ef1808633ca8f5c0c9812d5fcdd44/e-Commerce-Client/src/static/products/11033926921508488_2.jpg -------------------------------------------------------------------------------- /e-Commerce-Client/src/static/products/11600983276356164_1.jpg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/tkssharma/e-CommerseHub/044bb67bf47ef1808633ca8f5c0c9812d5fcdd44/e-Commerce-Client/src/static/products/11600983276356164_1.jpg -------------------------------------------------------------------------------- /e-Commerce-Client/src/static/products/11600983276356164_2.jpg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/tkssharma/e-CommerseHub/044bb67bf47ef1808633ca8f5c0c9812d5fcdd44/e-Commerce-Client/src/static/products/11600983276356164_2.jpg -------------------------------------------------------------------------------- /e-Commerce-Client/src/static/products/11854078013954528_1.jpg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/tkssharma/e-CommerseHub/044bb67bf47ef1808633ca8f5c0c9812d5fcdd44/e-Commerce-Client/src/static/products/11854078013954528_1.jpg -------------------------------------------------------------------------------- /e-Commerce-Client/src/static/products/11854078013954528_2.jpg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/tkssharma/e-CommerseHub/044bb67bf47ef1808633ca8f5c0c9812d5fcdd44/e-Commerce-Client/src/static/products/11854078013954528_2.jpg -------------------------------------------------------------------------------- /e-Commerce-Client/src/static/products/12064273040195392_1.jpg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/tkssharma/e-CommerseHub/044bb67bf47ef1808633ca8f5c0c9812d5fcdd44/e-Commerce-Client/src/static/products/12064273040195392_1.jpg -------------------------------------------------------------------------------- /e-Commerce-Client/src/static/products/12064273040195392_2.jpg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/tkssharma/e-CommerseHub/044bb67bf47ef1808633ca8f5c0c9812d5fcdd44/e-Commerce-Client/src/static/products/12064273040195392_2.jpg -------------------------------------------------------------------------------- /e-Commerce-Client/src/static/products/18532669286405344_1.jpg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/tkssharma/e-CommerseHub/044bb67bf47ef1808633ca8f5c0c9812d5fcdd44/e-Commerce-Client/src/static/products/18532669286405344_1.jpg -------------------------------------------------------------------------------- /e-Commerce-Client/src/static/products/18532669286405344_2.jpg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/tkssharma/e-CommerseHub/044bb67bf47ef1808633ca8f5c0c9812d5fcdd44/e-Commerce-Client/src/static/products/18532669286405344_2.jpg -------------------------------------------------------------------------------- /e-Commerce-Client/src/static/products/18644119330491310_1.jpg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/tkssharma/e-CommerseHub/044bb67bf47ef1808633ca8f5c0c9812d5fcdd44/e-Commerce-Client/src/static/products/18644119330491310_1.jpg -------------------------------------------------------------------------------- /e-Commerce-Client/src/static/products/18644119330491310_2.jpg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/tkssharma/e-CommerseHub/044bb67bf47ef1808633ca8f5c0c9812d5fcdd44/e-Commerce-Client/src/static/products/18644119330491310_2.jpg -------------------------------------------------------------------------------- /e-Commerce-Client/src/static/products/27250082398145996_1.jpg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/tkssharma/e-CommerseHub/044bb67bf47ef1808633ca8f5c0c9812d5fcdd44/e-Commerce-Client/src/static/products/27250082398145996_1.jpg -------------------------------------------------------------------------------- /e-Commerce-Client/src/static/products/27250082398145996_2.jpg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/tkssharma/e-CommerseHub/044bb67bf47ef1808633ca8f5c0c9812d5fcdd44/e-Commerce-Client/src/static/products/27250082398145996_2.jpg -------------------------------------------------------------------------------- /e-Commerce-Client/src/static/products/39876704341265610_1.jpg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/tkssharma/e-CommerseHub/044bb67bf47ef1808633ca8f5c0c9812d5fcdd44/e-Commerce-Client/src/static/products/39876704341265610_1.jpg -------------------------------------------------------------------------------- /e-Commerce-Client/src/static/products/39876704341265610_2.jpg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/tkssharma/e-CommerseHub/044bb67bf47ef1808633ca8f5c0c9812d5fcdd44/e-Commerce-Client/src/static/products/39876704341265610_2.jpg -------------------------------------------------------------------------------- /e-Commerce-Client/src/static/products/51498472915966370_1.jpg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/tkssharma/e-CommerseHub/044bb67bf47ef1808633ca8f5c0c9812d5fcdd44/e-Commerce-Client/src/static/products/51498472915966370_1.jpg -------------------------------------------------------------------------------- /e-Commerce-Client/src/static/products/51498472915966370_2.jpg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/tkssharma/e-CommerseHub/044bb67bf47ef1808633ca8f5c0c9812d5fcdd44/e-Commerce-Client/src/static/products/51498472915966370_2.jpg -------------------------------------------------------------------------------- /e-Commerce-Client/src/static/products/5619496040738316_1.jpg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/tkssharma/e-CommerseHub/044bb67bf47ef1808633ca8f5c0c9812d5fcdd44/e-Commerce-Client/src/static/products/5619496040738316_1.jpg -------------------------------------------------------------------------------- /e-Commerce-Client/src/static/products/5619496040738316_2.jpg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/tkssharma/e-CommerseHub/044bb67bf47ef1808633ca8f5c0c9812d5fcdd44/e-Commerce-Client/src/static/products/5619496040738316_2.jpg -------------------------------------------------------------------------------- /e-Commerce-Client/src/static/products/6090484789343891_1.jpg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/tkssharma/e-CommerseHub/044bb67bf47ef1808633ca8f5c0c9812d5fcdd44/e-Commerce-Client/src/static/products/6090484789343891_1.jpg -------------------------------------------------------------------------------- /e-Commerce-Client/src/static/products/6090484789343891_2.jpg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/tkssharma/e-CommerseHub/044bb67bf47ef1808633ca8f5c0c9812d5fcdd44/e-Commerce-Client/src/static/products/6090484789343891_2.jpg -------------------------------------------------------------------------------- /e-Commerce-Client/src/static/products/8552515751438644_1.jpg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/tkssharma/e-CommerseHub/044bb67bf47ef1808633ca8f5c0c9812d5fcdd44/e-Commerce-Client/src/static/products/8552515751438644_1.jpg -------------------------------------------------------------------------------- /e-Commerce-Client/src/static/products/8552515751438644_2.jpg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/tkssharma/e-CommerseHub/044bb67bf47ef1808633ca8f5c0c9812d5fcdd44/e-Commerce-Client/src/static/products/8552515751438644_2.jpg -------------------------------------------------------------------------------- /e-Commerce-Client/src/static/products/876661122392077_1.jpg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/tkssharma/e-CommerseHub/044bb67bf47ef1808633ca8f5c0c9812d5fcdd44/e-Commerce-Client/src/static/products/876661122392077_1.jpg -------------------------------------------------------------------------------- /e-Commerce-Client/src/static/products/876661122392077_2.jpg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/tkssharma/e-CommerseHub/044bb67bf47ef1808633ca8f5c0c9812d5fcdd44/e-Commerce-Client/src/static/products/876661122392077_2.jpg -------------------------------------------------------------------------------- /e-Commerce-Client/src/static/products/9197907543445676_1.jpg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/tkssharma/e-CommerseHub/044bb67bf47ef1808633ca8f5c0c9812d5fcdd44/e-Commerce-Client/src/static/products/9197907543445676_1.jpg -------------------------------------------------------------------------------- /e-Commerce-Client/src/static/products/9197907543445676_2.jpg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/tkssharma/e-CommerseHub/044bb67bf47ef1808633ca8f5c0c9812d5fcdd44/e-Commerce-Client/src/static/products/9197907543445676_2.jpg -------------------------------------------------------------------------------- /e-Commerce-Client/src/static/sprite_delete-icon.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/tkssharma/e-CommerseHub/044bb67bf47ef1808633ca8f5c0c9812d5fcdd44/e-Commerce-Client/src/static/sprite_delete-icon.png -------------------------------------------------------------------------------- /e-Commerce-Client/src/util/helper/index.js: -------------------------------------------------------------------------------- 1 | 2 | export default class Util{ 3 | static validateEmail(email){ 4 | var re = /^(([^<>()\[\]\\.,;:\s@"]+(\.[^<>()\[\]\\.,;:\s@"]+)*)|(".+"))@((\[[0-9]{1,3}\.[0-9]{1,3}\.[0-9]{1,3}\.[0-9]{1,3}])|(([a-zA-Z\-0-9]+\.)+[a-zA-Z]{2,}))$/; 5 | return re.test(email); 6 | } 7 | static getFileName( url ) { 8 | return url ? url.split('/').pop() : null; 9 | } 10 | static uid() { 11 | return Math.random().toString(34).slice(2); 12 | } 13 | } -------------------------------------------------------------------------------- /e-Commerce-Client/src/util/middleware/auth.js: -------------------------------------------------------------------------------- 1 | import * as API from '../../api' 2 | 3 | export default { 4 | 5 | loggedIn: () => { 6 | return !! localStorage.token 7 | }, 8 | 9 | setAccessToken: (token) => { 10 | localStorage.token = token; 11 | }, 12 | 13 | getAccessToken: (token) => { 14 | return localStorage.token; 15 | }, 16 | 17 | 18 | deleteAccessToken: () => { 19 | delete localStorage.token 20 | }, 21 | 22 | logout: () => { 23 | delete localStorage.token; 24 | delete localStorage.user_hasPassword; 25 | delete localStorage.user_userType; 26 | delete localStorage.auth_background; 27 | delete sessionStorage.redirect_after_login; 28 | API.setAuthToken(); 29 | }, 30 | } 31 | 32 | -------------------------------------------------------------------------------- /e-Commerce-Client/src/util/middleware/index.js: -------------------------------------------------------------------------------- 1 | import Auth from './auth'; 2 | 3 | 4 | function authenticatedUsersOnly( nextState, replace ) { 5 | if( nextState.location.pathname !== '/user/dashboard' ) { 6 | if ( sessionStorage.redirect_after_login ) { 7 | localStorage.removeItem('redirect_after_login'); 8 | } 9 | sessionStorage.setItem( 'redirect_after_login', nextState.location.pathname); 10 | } 11 | 12 | if ( !Auth.loggedIn() ) { 13 | replace({ 14 | pathname: '/auth/login', 15 | state: { nextPathname: nextState.location.pathname } 16 | }); 17 | } 18 | } 19 | 20 | 21 | 22 | function userTypeHostOnly( nextState, replace, store ) { 23 | const state = store.getState(); 24 | 25 | if ( state.auth.get('user').get('userType') !== 2 ) { 26 | replace({ 27 | pathname: '/user/dashboard/home', 28 | state: { nextPathname: nextState.location.pathname } 29 | }); 30 | } 31 | 32 | } 33 | function notLoggedIn( nextState, replace ) { 34 | if ( Auth.loggedIn() ) { 35 | replace({ 36 | pathname: '/user/dashboard/home', 37 | state: { nextPathname: nextState.location.pathname } 38 | }); 39 | } 40 | } 41 | 42 | function logoutUser( nextState, replace ) { 43 | Auth.logout(); 44 | replace({ 45 | pathname: '/user/dashboard/home', 46 | state: { nextPathname: nextState.location.pathname } 47 | }); 48 | 49 | } 50 | 51 | 52 | export { authenticatedUsersOnly }; 53 | export { notLoggedIn }; 54 | export { logoutUser }; 55 | export { userTypeHostOnly }; 56 | -------------------------------------------------------------------------------- /proxy/default.conf: -------------------------------------------------------------------------------- 1 | ssl_certificate /etc/nginx/ssl/pac.crt; 2 | ssl_certificate_key /etc/nginx/ssl/pac.key; 3 | 4 | server { 5 | server_name ms-commerce.com; 6 | listen 80; 7 | listen 443 ssl; 8 | location / { 9 | proxy_pass http://ms_commerce_client:3003; 10 | proxy_http_version 1.1; 11 | proxy_set_header Upgrade $http_upgrade; 12 | proxy_set_header Connection "upgrade"; 13 | } 14 | location /api/ { 15 | proxy_pass http://ms_commerce_auth:3001/api/; 16 | proxy_redirect off; 17 | proxy_set_header Host $host; 18 | proxy_set_header X-Real-IP $remote_addr; 19 | proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for; 20 | proxy_set_header X-Forwarded-Host $server_name; 21 | proxy_http_version 1.1; 22 | proxy_set_header Upgrade $http_upgrade; 23 | proxy_set_header Connection "upgrade"; 24 | } 25 | location /admin/ { 26 | proxy_pass http://ms_commerce_admin:3002/api/; 27 | proxy_redirect off; 28 | proxy_set_header Host $host; 29 | proxy_set_header X-Real-IP $remote_addr; 30 | proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for; 31 | proxy_set_header X-Forwarded-Host $server_name; 32 | proxy_http_version 1.1; 33 | proxy_set_header Upgrade $http_upgrade; 34 | proxy_set_header Connection "upgrade"; 35 | } 36 | location /cart/ { 37 | proxy_pass http://ms_commerce_cart:3004/api/; 38 | proxy_redirect off; 39 | proxy_set_header Host $host; 40 | proxy_set_header X-Real-IP $remote_addr; 41 | proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for; 42 | proxy_set_header X-Forwarded-Host $server_name; 43 | proxy_http_version 1.1; 44 | proxy_set_header Upgrade $http_upgrade; 45 | proxy_set_header Connection "upgrade"; 46 | } 47 | } -------------------------------------------------------------------------------- /proxy/hosts: -------------------------------------------------------------------------------- 1 | # 2 | # localhost is used to configure the loopback interface 3 | # when the system is booting. Do not change this entry. 4 | # 5 | 127.0.0.1 localhost ms-commerce.com 6 | 255.255.255.255 broadcasthost 7 | ::1 localhost 8 | -------------------------------------------------------------------------------- /proxy/ssl/pac.crt: -------------------------------------------------------------------------------- 1 | -----BEGIN CERTIFICATE----- 2 | MIICuzCCAiQCCQC+/UzcIDAe1jANBgkqhkiG9w0BAQsFADCBoTELMAkGA1UEBhMC 3 | SU4xDDAKBgNVBAgMA0dvYTERMA8GA1UEBwwIUG9ydm9yaW0xJDAiBgNVBAoMG1Ny 4 | aWphbiBUZWNobm9sb2dpZXMgUHZ0IEx0ZDEMMAoGA1UECwwDR29hMRgwFgYDVQQD 5 | DA9kaXZlcnNleWlvdC5jb20xIzAhBgkqhkiG9w0BCQEWFHN1cHBvcnRAZGl2ZXJz 6 | ZXkuY29tMB4XDTE5MDEyMDAwMjEwOVoXDTI5MDExNzAwMjEwOVowgaExCzAJBgNV 7 | BAYTAklOMQwwCgYDVQQIDANHb2ExETAPBgNVBAcMCFBvcnZvcmltMSQwIgYDVQQK 8 | DBtTcmlqYW4gVGVjaG5vbG9naWVzIFB2dCBMdGQxDDAKBgNVBAsMA0dvYTEYMBYG 9 | A1UEAwwPZGl2ZXJzZXlpb3QuY29tMSMwIQYJKoZIhvcNAQkBFhRzdXBwb3J0QGRp 10 | dmVyc2V5LmNvbTCBnzANBgkqhkiG9w0BAQEFAAOBjQAwgYkCgYEA1QfJ81Q9Drzp 11 | Eujr3pz5S0kvXrXREVNPeKTf+fN8iJr6bMlmaDd0KvVXKt1nRIJxOUHQ1AQuKNlR 12 | 1Ly5M8bLk6u1MIYZNzLl1x06UX501NtgVX2ig5wZbdOONeUreI5Ss5VsAJZHgFR8 13 | xNRecBeG+loQUmj9bAI6d5ilY/bhvV0CAwEAATANBgkqhkiG9w0BAQsFAAOBgQDV 14 | BhPhasAnTDoJdqRTkLIKHQngzYf91j7Wy8cm7WOtIpSH3BuzZEzrhgGmInJT0QWa 15 | bTMSemvkxnuKZnJloGC4BdE0z3ec/ubSrN4AmefoaDbENJrahPLULYkkaRasmdDo 16 | S4Mm2oN1mpNCE2VW8eudRIHlQbeaBjkgnHOhp6gBeg== 17 | -----END CERTIFICATE----- 18 | -------------------------------------------------------------------------------- /proxy/ssl/pac.key: -------------------------------------------------------------------------------- 1 | -----BEGIN PRIVATE KEY----- 2 | MIICdgIBADANBgkqhkiG9w0BAQEFAASCAmAwggJcAgEAAoGBANUHyfNUPQ686RLo 3 | 696c+UtJL1610RFTT3ik3/nzfIia+mzJZmg3dCr1VyrdZ0SCcTlB0NQELijZUdS8 4 | uTPGy5OrtTCGGTcy5dcdOlF+dNTbYFV9ooOcGW3TjjXlK3iOUrOVbACWR4BUfMTU 5 | XnAXhvpaEFJo/WwCOneYpWP24b1dAgMBAAECgYA2ya0HtreJTc6HvX3EIA0Bbs4P 6 | BqXBVfLPbV/pMdTqcSlMxzNeRDzNO5HyhUSk2wNxnVqu3HBesx2Xn/3lsg/y71rt 7 | PIM2vd8ccOZL39hZCwEnixvhwr1VktGXUBtdi7ipMkYDLl9SUFMNKFexHSQ96Xl6 8 | bfcdhwK/dZxOVOGJRQJBAPci1ij7gEL8xsA8xxCzqDDPruFCI5+SUcco88vWkXZ5 9 | ZYJZVNOk0SAAdYw9dUjKjwQKBDQ+SbMyhx2nT5QDTicCQQDcq8y5ulkxdzUHHoxq 10 | j8iZlNmErRfofiyOTJcc4/9kqyIyQGGLSAuCmAVqKxKzjwFXN/UuJYbLBlKAMMOc 11 | kk7bAkAYZOrozrKBajwgG5+2qVUvxEBJ4eJsTOAfnY47D6n6HM+FR1YVMg6mbwUr 12 | W6GpFr15M5fopEFYG+O0bKBxRsY/AkACwRozD0Jhva0pw5XZFqZYVGVKpKZxvnFr 13 | 7UTNlYLwjLpGikstY97Q6HjY1GTNXPGVVxt2Uf2WtyN8eh9W6vSVAkEAxtiPp8w5 14 | sXiWHKKq9VkYpYgUF4WkhUCxx4crWpaIQgXJwtQ6bKJeBN8r+JklYsPtveNYZYbl 15 | bn8bkcfdRwxcog== 16 | -----END PRIVATE KEY----- 17 | -------------------------------------------------------------------------------- /screens/02.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/tkssharma/e-CommerseHub/044bb67bf47ef1808633ca8f5c0c9812d5fcdd44/screens/02.png -------------------------------------------------------------------------------- /screens/03.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/tkssharma/e-CommerseHub/044bb67bf47ef1808633ca8f5c0c9812d5fcdd44/screens/03.png --------------------------------------------------------------------------------