├── 3-Front-End with React
├── README.md
├── package.json
├── public
│ ├── cards.json
│ ├── favicon.ico
│ ├── img
│ │ ├── blackguitar.jpeg
│ │ ├── coolguitar.jpeg
│ │ ├── drums.jpg
│ │ ├── flute.jpeg
│ │ ├── mics.jpeg
│ │ ├── redguitar.jpeg
│ │ ├── saxophone.jpeg
│ │ ├── strings.png
│ │ └── sunnyguitar.jpeg
│ ├── index.html
│ └── manifest.json
└── src
│ └── index.js
├── 4-Build-GoMusic
├── README.MD
├── package.json
├── public
│ ├── cards.json
│ ├── favicon.ico
│ ├── img
│ │ ├── blackguitar.jpeg
│ │ ├── coolguitar.jpeg
│ │ ├── drums.jpg
│ │ ├── flute.jpeg
│ │ ├── img-small
│ │ │ ├── drums.jpg
│ │ │ ├── pexels-photo-290660(1).jpeg
│ │ │ ├── redguitar.jpeg
│ │ │ └── strings.png
│ │ ├── mics.jpeg
│ │ ├── redguitar.jpeg
│ │ ├── saxophone.jpeg
│ │ ├── strings.png
│ │ └── sunnyguitar.jpeg
│ ├── index.html
│ ├── manifest.json
│ ├── promos.json
│ └── user.json
└── src
│ ├── About.js
│ ├── App.js
│ ├── App.test.js
│ ├── CreditCards.js
│ ├── Navigation.js
│ ├── ProductCards.js
│ ├── index.js
│ ├── modalwindows.js
│ ├── orders.js
│ └── registerServiceWorker.js
├── 5-RESTFul-API
├── README.md
├── backend
│ └── src
│ │ ├── dblayer
│ │ ├── dblayer.go
│ │ └── orm.go
│ │ ├── main.go
│ │ ├── models
│ │ └── models.go
│ │ └── rest
│ │ ├── handler.go
│ │ └── rest.go
└── frontend
│ ├── README.md
│ ├── package.json
│ ├── public
│ ├── cards.json
│ ├── favicon.ico
│ ├── img
│ │ ├── blackguitar.jpeg
│ │ ├── coolguitar.jpeg
│ │ ├── drums.jpg
│ │ ├── flute.jpeg
│ │ ├── img-small
│ │ │ ├── drums.jpg
│ │ │ ├── pexels-photo-290660(1).jpeg
│ │ │ ├── redguitar.jpeg
│ │ │ └── strings.png
│ │ ├── mics.jpeg
│ │ ├── redguitar.jpeg
│ │ ├── saxophone.jpeg
│ │ ├── strings.png
│ │ └── sunnyguitar.jpeg
│ ├── index.html
│ ├── manifest.json
│ ├── promos.json
│ └── user.json
│ └── src
│ ├── About.js
│ ├── App.js
│ ├── App.test.js
│ ├── CreditCards.js
│ ├── Navigation.js
│ ├── ProductCards.js
│ ├── index.js
│ ├── modalwindows.js
│ ├── orders.js
│ └── registerServiceWorker.js
├── 6-Advanced-Web
├── Frontend
│ ├── package.json
│ ├── public
│ │ ├── cards.json
│ │ ├── favicon.ico
│ │ ├── img
│ │ │ ├── blackguitar.jpeg
│ │ │ ├── coolguitar.jpeg
│ │ │ ├── drums.jpg
│ │ │ ├── flute.jpeg
│ │ │ ├── img-small
│ │ │ │ ├── drums.jpg
│ │ │ │ ├── pexels-photo-290660(1).jpeg
│ │ │ │ ├── redguitar.jpeg
│ │ │ │ └── strings.png
│ │ │ ├── mics.jpeg
│ │ │ ├── redguitar.jpeg
│ │ │ ├── saxophone.jpeg
│ │ │ ├── strings.png
│ │ │ └── sunnyguitar.jpeg
│ │ ├── index.html
│ │ ├── manifest.json
│ │ ├── promos.json
│ │ └── user.json
│ ├── readme.md
│ └── src
│ │ ├── About.js
│ │ ├── App.js
│ │ ├── App.test.js
│ │ ├── CreditCards.js
│ │ ├── Navigation.js
│ │ ├── ProductCards.js
│ │ ├── index.js
│ │ ├── modalwindows.js
│ │ ├── orders.js
│ │ └── registerServiceWorker.js
├── backend
│ ├── ginreadme.md
│ └── src
│ │ ├── dblayer
│ │ ├── dblayer.go
│ │ └── orm.go
│ │ ├── main.go
│ │ ├── models
│ │ └── models.go
│ │ └── rest
│ │ ├── handler.go
│ │ └── rest.go
└── readme.md
├── 7-Testing-and-benchmarking
├── backend
│ ├── ginreadme.md
│ ├── public
│ │ └── img
│ │ │ ├── blackguitar.jpeg
│ │ │ ├── coolguitar.jpeg
│ │ │ ├── drums.jpg
│ │ │ ├── flute.jpeg
│ │ │ ├── img-small
│ │ │ ├── blackguitar.jpeg
│ │ │ ├── coolguitar.jpeg
│ │ │ ├── drums.jpg
│ │ │ ├── flute.jpeg
│ │ │ ├── redguitar.jpeg
│ │ │ ├── saxophone.jpeg
│ │ │ └── strings.png
│ │ │ ├── mics.jpeg
│ │ │ ├── redguitar.jpeg
│ │ │ ├── saxophone.jpeg
│ │ │ ├── strings.png
│ │ │ └── sunnyguitar.jpeg
│ └── src
│ │ ├── dblayer
│ │ ├── dblayer.go
│ │ ├── mockdblayer.go
│ │ ├── orm.go
│ │ └── orm_test.go
│ │ ├── main.go
│ │ ├── mockdata.json
│ │ ├── models
│ │ └── models.go
│ │ └── rest
│ │ ├── handler.go
│ │ ├── handler_test.go
│ │ └── rest.go
└── readme.md
├── 9-Isomorphic-Go
├── README.md
├── browser
│ ├── main.go
│ ├── main.js
│ └── main.js.map
├── nodeprojects
│ ├── calc
│ │ ├── addsub.js
│ │ ├── addsubgo.go
│ │ ├── addsubgo.js
│ │ ├── addsubgo.js.map
│ │ └── calc.js
│ ├── gopackages
│ │ ├── gopackages.go
│ │ ├── gopackages.js
│ │ └── gopackages.js.map
│ ├── jsonbeautify
│ │ ├── jsonbeautify.go
│ │ ├── jsonbeautify.js
│ │ ├── jsonbeautify.js.map
│ │ ├── package-lock.json
│ │ └── package.json
│ └── musicalinstruments
│ │ ├── mi.go
│ │ ├── mi.js
│ │ ├── mi.js.map
│ │ └── usemi.js
└── reactproject
│ ├── app.go
│ ├── gen_App_reactGen.go
│ ├── hello_message
│ ├── gen_HelloMessage_reactGen.go
│ └── hello_message.go
│ ├── index.html
│ ├── main.go
│ ├── reactproject.js
│ └── reactproject.js.map
├── README.md
├── final-application
├── public
│ └── build
│ │ ├── asset-manifest.json
│ │ ├── cards.json
│ │ ├── favicon.ico
│ │ ├── img
│ │ ├── blackguitar.jpeg
│ │ ├── coolguitar.jpeg
│ │ ├── drums.jpg
│ │ ├── flute.jpeg
│ │ ├── img-small
│ │ │ ├── blackguitar.jpeg
│ │ │ ├── coolguitar.jpeg
│ │ │ ├── drums.jpg
│ │ │ ├── flute.jpeg
│ │ │ ├── redguitar.jpeg
│ │ │ ├── saxophone.jpeg
│ │ │ └── strings.png
│ │ ├── mics.jpeg
│ │ ├── redguitar.jpeg
│ │ ├── saxophone.jpeg
│ │ ├── strings.png
│ │ └── sunnyguitar.jpeg
│ │ ├── index.html
│ │ ├── manifest.json
│ │ ├── promos.json
│ │ ├── service-worker.js
│ │ ├── static
│ │ └── js
│ │ │ ├── main.04874240.js
│ │ │ └── main.04874240.js.map
│ │ └── user.json
├── readme.md
└── src
│ ├── dblayer
│ ├── dblayer.go
│ └── orm.go
│ ├── main.go
│ ├── mockdata.json
│ ├── models
│ └── models.go
│ └── rest
│ ├── handler.go
│ ├── mockHandler.go
│ └── rest.go
└── projectdb.sql
/3-Front-End with React/package.json:
--------------------------------------------------------------------------------
1 | {
2 | "name": "first-react-tutorial",
3 | "version": "0.1.0",
4 | "private": true,
5 | "dependencies": {
6 | "react": "^16.4.0",
7 | "react-dom": "^16.4.0",
8 | "react-scripts": "1.1.4"
9 | },
10 | "scripts": {
11 | "start": "react-scripts start",
12 | "build": "react-scripts build",
13 | "test": "react-scripts test --env=jsdom",
14 | "eject": "react-scripts eject"
15 | }
16 | }
--------------------------------------------------------------------------------
/3-Front-End with React/public/cards.json:
--------------------------------------------------------------------------------
1 | [{
2 | "id" : 1,
3 | "img" : "img/strings.png",
4 | "imgalt":"string",
5 | "desc":"A very authentic and beutiful instrument!!",
6 | "price" : 100.0,
7 | "productname" : "Strings"
8 | }, {
9 | "id" : 2,
10 | "img" : "img/redguitar.jpeg",
11 | "imgalt":"redg",
12 | "desc":"A really cool red guitar that can produce super cool music!!",
13 | "price" : 299.0,
14 | "productname" : "Red Guitar"
15 | },{
16 | "id" : 3,
17 | "img" : "img/drums.jpg",
18 | "imgalt":"drums",
19 | "desc":"A set of super awesome drums, combined with a guitar, they can product more than amazing music!!",
20 | "price" : 17000.0,
21 | "productname" : "Drums"
22 | },{
23 | "id" : 4,
24 | "img" : "img/flute.jpeg",
25 | "imgalt":"flute",
26 | "desc":"A super nice flute combined with some super nice musical notes!!",
27 | "price" : 210.0,
28 | "productname" : "Flute"
29 | },{
30 | "id" : 5,
31 | "img" : "img/blackguitar.jpeg",
32 | "imgalt":"Black guitar",
33 | "desc":"An awesome guitar that will product amazing sound!!",
34 | "price" : 200.0,
35 | "productname" : "Black Guitar"
36 | },{
37 | "id" : 6,
38 | "img" : "img/saxophone.jpeg",
39 | "imgalt":"Saxophone",
40 | "desc":"An great saxophone for a great musician!!",
41 | "price" : 1000.0,
42 | "productname" : "Saxophone"
43 | }]
44 |
--------------------------------------------------------------------------------
/3-Front-End with React/public/favicon.ico:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/minaandrawos/Hands-On-Full-Stack-Development-with-Go/bb0ccde535e21378f8f01aa7b20b66e7bba2a671/3-Front-End with React/public/favicon.ico
--------------------------------------------------------------------------------
/3-Front-End with React/public/img/blackguitar.jpeg:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/minaandrawos/Hands-On-Full-Stack-Development-with-Go/bb0ccde535e21378f8f01aa7b20b66e7bba2a671/3-Front-End with React/public/img/blackguitar.jpeg
--------------------------------------------------------------------------------
/3-Front-End with React/public/img/coolguitar.jpeg:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/minaandrawos/Hands-On-Full-Stack-Development-with-Go/bb0ccde535e21378f8f01aa7b20b66e7bba2a671/3-Front-End with React/public/img/coolguitar.jpeg
--------------------------------------------------------------------------------
/3-Front-End with React/public/img/drums.jpg:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/minaandrawos/Hands-On-Full-Stack-Development-with-Go/bb0ccde535e21378f8f01aa7b20b66e7bba2a671/3-Front-End with React/public/img/drums.jpg
--------------------------------------------------------------------------------
/3-Front-End with React/public/img/flute.jpeg:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/minaandrawos/Hands-On-Full-Stack-Development-with-Go/bb0ccde535e21378f8f01aa7b20b66e7bba2a671/3-Front-End with React/public/img/flute.jpeg
--------------------------------------------------------------------------------
/3-Front-End with React/public/img/mics.jpeg:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/minaandrawos/Hands-On-Full-Stack-Development-with-Go/bb0ccde535e21378f8f01aa7b20b66e7bba2a671/3-Front-End with React/public/img/mics.jpeg
--------------------------------------------------------------------------------
/3-Front-End with React/public/img/redguitar.jpeg:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/minaandrawos/Hands-On-Full-Stack-Development-with-Go/bb0ccde535e21378f8f01aa7b20b66e7bba2a671/3-Front-End with React/public/img/redguitar.jpeg
--------------------------------------------------------------------------------
/3-Front-End with React/public/img/saxophone.jpeg:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/minaandrawos/Hands-On-Full-Stack-Development-with-Go/bb0ccde535e21378f8f01aa7b20b66e7bba2a671/3-Front-End with React/public/img/saxophone.jpeg
--------------------------------------------------------------------------------
/3-Front-End with React/public/img/strings.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/minaandrawos/Hands-On-Full-Stack-Development-with-Go/bb0ccde535e21378f8f01aa7b20b66e7bba2a671/3-Front-End with React/public/img/strings.png
--------------------------------------------------------------------------------
/3-Front-End with React/public/img/sunnyguitar.jpeg:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/minaandrawos/Hands-On-Full-Stack-Development-with-Go/bb0ccde535e21378f8f01aa7b20b66e7bba2a671/3-Front-End with React/public/img/sunnyguitar.jpeg
--------------------------------------------------------------------------------
/3-Front-End with React/public/index.html:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 |
6 |
7 |
8 |
9 |
10 |
14 |
15 |
16 |
25 | React App
26 |
27 |
28 |
31 |
32 |
42 |
43 |
44 |
45 |
46 |
47 |
--------------------------------------------------------------------------------
/3-Front-End with React/public/manifest.json:
--------------------------------------------------------------------------------
1 | {
2 | "short_name": "React App",
3 | "name": "Create React App Sample",
4 | "icons": [
5 | {
6 | "src": "favicon.ico",
7 | "sizes": "64x64 32x32 24x24 16x16",
8 | "type": "image/x-icon"
9 | }
10 | ],
11 | "start_url": "./index.html",
12 | "display": "standalone",
13 | "theme_color": "#000000",
14 | "background_color": "#ffffff"
15 | }
16 |
--------------------------------------------------------------------------------
/3-Front-End with React/src/index.js:
--------------------------------------------------------------------------------
1 | import React from 'react';
2 | import ReactDOM from 'react-dom';
3 |
4 | class Card extends React.Component {
5 | render() {
6 | return (
7 |
8 |
9 |

10 |
11 |
{this.props.productname}
12 | Price:
{this.props.price}
13 |
{this.props.desc}
14 |
Buy
15 |
16 |
17 |
18 | );
19 | }
20 | }
21 |
22 | class CardContainer extends React.Component {
23 | constructor(props) {
24 | super(props);
25 | this.state = {
26 | cards: []
27 | };
28 | }
29 |
30 | componentDidMount() {
31 | console.log('Component Did Mount Called: ' + new Date().toLocaleString());
32 | fetch('cards.json')
33 | .then(res => res.json())
34 | .then((result) => {
35 | console.log('Fetch...');
36 | this.setState({
37 | cards: result
38 | });
39 | });
40 | }
41 |
42 | render() {
43 | const cards = this.state.cards;
44 | let items = cards.map(
45 | card =>
46 | );
47 | return (
48 |
49 |
Products
50 |
51 | {items}
52 |
53 |
54 | );
55 | }
56 | }
57 |
58 |
59 | // ========================================
60 |
61 | ReactDOM.render(
62 | ,
63 | document.getElementById('root')
64 | );
--------------------------------------------------------------------------------
/4-Build-GoMusic/package.json:
--------------------------------------------------------------------------------
1 | {
2 | "name": "gomusic",
3 | "version": "0.1.0",
4 | "private": true,
5 | "dependencies": {
6 | "react": "^16.4.1",
7 | "react-dom": "^16.4.1",
8 | "react-router-dom": "^4.3.1",
9 | "react-scripts": "1.1.4"
10 | },
11 | "scripts": {
12 | "start": "react-scripts start",
13 | "build": "react-scripts build",
14 | "test": "react-scripts test --env=jsdom",
15 | "eject": "react-scripts eject"
16 | }
17 | }
18 |
--------------------------------------------------------------------------------
/4-Build-GoMusic/public/cards.json:
--------------------------------------------------------------------------------
1 | [{
2 | "id" : 1,
3 | "img" : "img/strings.png",
4 | "imgalt":"string",
5 | "desc":"A very authentic and beautiful instrument!!",
6 | "price" : 100.0,
7 | "productname" : "Strings"
8 | }, {
9 | "id" : 2,
10 | "img" : "img/redguitar.jpeg",
11 | "imgalt":"redg",
12 | "desc":"A really cool red guitar that can produce super cool music!!",
13 | "price" : 299.0,
14 | "productname" : "Red Guitar"
15 | },{
16 | "id" : 3,
17 | "img" : "img/drums.jpg",
18 | "imgalt":"drums",
19 | "desc":"A set of super awesome drums, combined with a guitar, they can product more than amazing music!!",
20 | "price" : 17000.0,
21 | "productname" : "Drums"
22 | },{
23 | "id" : 4,
24 | "img" : "img/flute.jpeg",
25 | "imgalt":"flute",
26 | "desc":"A super nice flute combined with some super nice musical notes!!",
27 | "price" : 210.0,
28 | "productname" : "Flute"
29 | },{
30 | "id" : 5,
31 | "img" : "img/blackguitar.jpeg",
32 | "imgalt":"Black guitar",
33 | "desc":"An awesome guitar that will product amazing sound!!",
34 | "price" : 200.0,
35 | "productname" : "Black Guitar"
36 | },{
37 | "id" : 6,
38 | "img" : "img/saxophone.jpeg",
39 | "imgalt":"Saxophone",
40 | "desc":"An great saxophone for a great musician!!",
41 | "price" : 1000.0,
42 | "productname" : "Saxophone"
43 | }]
--------------------------------------------------------------------------------
/4-Build-GoMusic/public/favicon.ico:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/minaandrawos/Hands-On-Full-Stack-Development-with-Go/bb0ccde535e21378f8f01aa7b20b66e7bba2a671/4-Build-GoMusic/public/favicon.ico
--------------------------------------------------------------------------------
/4-Build-GoMusic/public/img/blackguitar.jpeg:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/minaandrawos/Hands-On-Full-Stack-Development-with-Go/bb0ccde535e21378f8f01aa7b20b66e7bba2a671/4-Build-GoMusic/public/img/blackguitar.jpeg
--------------------------------------------------------------------------------
/4-Build-GoMusic/public/img/coolguitar.jpeg:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/minaandrawos/Hands-On-Full-Stack-Development-with-Go/bb0ccde535e21378f8f01aa7b20b66e7bba2a671/4-Build-GoMusic/public/img/coolguitar.jpeg
--------------------------------------------------------------------------------
/4-Build-GoMusic/public/img/drums.jpg:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/minaandrawos/Hands-On-Full-Stack-Development-with-Go/bb0ccde535e21378f8f01aa7b20b66e7bba2a671/4-Build-GoMusic/public/img/drums.jpg
--------------------------------------------------------------------------------
/4-Build-GoMusic/public/img/flute.jpeg:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/minaandrawos/Hands-On-Full-Stack-Development-with-Go/bb0ccde535e21378f8f01aa7b20b66e7bba2a671/4-Build-GoMusic/public/img/flute.jpeg
--------------------------------------------------------------------------------
/4-Build-GoMusic/public/img/img-small/drums.jpg:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/minaandrawos/Hands-On-Full-Stack-Development-with-Go/bb0ccde535e21378f8f01aa7b20b66e7bba2a671/4-Build-GoMusic/public/img/img-small/drums.jpg
--------------------------------------------------------------------------------
/4-Build-GoMusic/public/img/img-small/pexels-photo-290660(1).jpeg:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/minaandrawos/Hands-On-Full-Stack-Development-with-Go/bb0ccde535e21378f8f01aa7b20b66e7bba2a671/4-Build-GoMusic/public/img/img-small/pexels-photo-290660(1).jpeg
--------------------------------------------------------------------------------
/4-Build-GoMusic/public/img/img-small/redguitar.jpeg:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/minaandrawos/Hands-On-Full-Stack-Development-with-Go/bb0ccde535e21378f8f01aa7b20b66e7bba2a671/4-Build-GoMusic/public/img/img-small/redguitar.jpeg
--------------------------------------------------------------------------------
/4-Build-GoMusic/public/img/img-small/strings.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/minaandrawos/Hands-On-Full-Stack-Development-with-Go/bb0ccde535e21378f8f01aa7b20b66e7bba2a671/4-Build-GoMusic/public/img/img-small/strings.png
--------------------------------------------------------------------------------
/4-Build-GoMusic/public/img/mics.jpeg:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/minaandrawos/Hands-On-Full-Stack-Development-with-Go/bb0ccde535e21378f8f01aa7b20b66e7bba2a671/4-Build-GoMusic/public/img/mics.jpeg
--------------------------------------------------------------------------------
/4-Build-GoMusic/public/img/redguitar.jpeg:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/minaandrawos/Hands-On-Full-Stack-Development-with-Go/bb0ccde535e21378f8f01aa7b20b66e7bba2a671/4-Build-GoMusic/public/img/redguitar.jpeg
--------------------------------------------------------------------------------
/4-Build-GoMusic/public/img/saxophone.jpeg:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/minaandrawos/Hands-On-Full-Stack-Development-with-Go/bb0ccde535e21378f8f01aa7b20b66e7bba2a671/4-Build-GoMusic/public/img/saxophone.jpeg
--------------------------------------------------------------------------------
/4-Build-GoMusic/public/img/strings.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/minaandrawos/Hands-On-Full-Stack-Development-with-Go/bb0ccde535e21378f8f01aa7b20b66e7bba2a671/4-Build-GoMusic/public/img/strings.png
--------------------------------------------------------------------------------
/4-Build-GoMusic/public/img/sunnyguitar.jpeg:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/minaandrawos/Hands-On-Full-Stack-Development-with-Go/bb0ccde535e21378f8f01aa7b20b66e7bba2a671/4-Build-GoMusic/public/img/sunnyguitar.jpeg
--------------------------------------------------------------------------------
/4-Build-GoMusic/public/index.html:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 |
6 |
7 |
8 |
10 |
11 |
12 |
16 |
17 |
18 |
27 | React App
28 |
29 |
30 |
31 |
34 |
35 |
36 |
37 |
38 |
40 |
42 |
44 |
45 |
46 |
--------------------------------------------------------------------------------
/4-Build-GoMusic/public/manifest.json:
--------------------------------------------------------------------------------
1 | {
2 | "short_name": "React App",
3 | "name": "Create React App Sample",
4 | "icons": [
5 | {
6 | "src": "favicon.ico",
7 | "sizes": "64x64 32x32 24x24 16x16",
8 | "type": "image/x-icon"
9 | }
10 | ],
11 | "start_url": "./index.html",
12 | "display": "standalone",
13 | "theme_color": "#000000",
14 | "background_color": "#ffffff"
15 | }
16 |
--------------------------------------------------------------------------------
/4-Build-GoMusic/public/promos.json:
--------------------------------------------------------------------------------
1 | [{
2 | "id" : 3,
3 | "img" : "img/drums.jpg",
4 | "imgalt":"drums",
5 | "desc":"A set of super awesome drums, combined with a guitar, they can product more than amazing music!!",
6 | "price" : 17000.0,
7 | "productname" : "Drums"
8 | },{
9 | "id" : 4,
10 | "img" : "img/flute.jpeg",
11 | "imgalt":"flute",
12 | "desc":"A super nice flute combined with some super nice musical notes!!",
13 | "price" : 210.0,
14 | "productname" : "Flute"
15 | },{
16 | "id" : 5,
17 | "img" : "img/blackguitar.jpeg",
18 | "imgalt":"Black guitar",
19 | "desc":"An awesome guitar that will product amazing sound!!",
20 | "price" : 200.0,
21 | "productname" : "Black Guitar"
22 | },{
23 | "id" : 6,
24 | "img" : "img/saxophone.jpeg",
25 | "imgalt":"Saxophone",
26 | "desc":"An great saxophone for a great musician!!",
27 | "price" : 1000.0,
28 | "productname" : "Saxophone"
29 | }]
--------------------------------------------------------------------------------
/4-Build-GoMusic/public/user.json:
--------------------------------------------------------------------------------
1 | {
2 | "name": "Mina",
3 | "loggedin": false,
4 | "orders": [
5 | {
6 | "id" : 1,
7 | "img" : "img/img-small/strings.png",
8 | "imgalt":"string",
9 | "desc":"A very authentic and beautiful instrument!!",
10 | "price" : 100.0,
11 | "productname" : "Strings",
12 | "days": 32
13 | }, {
14 | "id" : 2,
15 | "img" : "img/img-small/redguitar.jpeg",
16 | "imgalt":"redg",
17 | "desc":"A really cool red guitar that can produce super cool music!!",
18 | "price" : 299.0,
19 | "productname" : "Red Guitar",
20 | "days": 99
21 | },{
22 | "id" : 3,
23 | "img" : "img/img-small/drums.jpg",
24 | "imgalt":"drums",
25 | "desc":"A set of super awesome drums, combined with a guitar, they can product more than amazing music!!",
26 | "price" : 17000.0,
27 | "productname" : "Drums",
28 | "days": 45
29 | }
30 | ]
31 | }
--------------------------------------------------------------------------------
/4-Build-GoMusic/src/About.js:
--------------------------------------------------------------------------------
1 | import React from 'react';
2 |
3 |
4 | export default function About(props) {
5 | return (
6 |
7 |
8 |
About the Go Music Store
9 |
Go music is a modern online msucial instruments store
10 |
Explore how you can combine the power of React and Go, to build a fast and beautiful looking online store.
11 |
We will cover how to build this website step by step.
12 |
13 |
);
14 | }
--------------------------------------------------------------------------------
/4-Build-GoMusic/src/App.js:
--------------------------------------------------------------------------------
1 | import React from 'react';
2 | import CardContainer from './ProductCards';
3 | import { BrowserRouter as Router, Route } from "react-router-dom";
4 | import Nav from './Navigation';
5 | import { SignInModalWindow, BuyModalWindow } from './modalwindows';
6 | import About from './About';
7 | import Orders from './orders';
8 |
9 |
10 | class App extends React.Component {
11 |
12 | constructor(props) {
13 | super(props);
14 | this.state = {
15 | user: {
16 | loggedin: false,
17 | name: ""
18 | }
19 | };
20 | }
21 |
22 | handleSignedIn(user) {
23 | this.setState({
24 | user: user
25 | });
26 | }
27 |
28 | componentDidMount() {
29 | fetch('user.json')
30 | .then(res => res.json())
31 | .then((result) => {
32 | console.log('Fetch...');
33 | this.setState({
34 | user: result
35 | });
36 | });
37 | }
38 |
39 | render() {
40 | return (
41 |
42 |
43 |
44 |
45 |
46 | } />
47 | } />
48 | {this.state.user.loggedin ? }/> : null}
49 |
50 |
51 |
52 |
53 |
54 |
55 |
56 | );
57 | }
58 | }
59 |
60 | export default App;
61 |
--------------------------------------------------------------------------------
/4-Build-GoMusic/src/App.test.js:
--------------------------------------------------------------------------------
1 | import React from 'react';
2 | import ReactDOM from 'react-dom';
3 | import App from './App';
4 |
5 | it('renders without crashing', () => {
6 | const div = document.createElement('div');
7 | ReactDOM.render(, div);
8 | ReactDOM.unmountComponentAtNode(div);
9 | });
10 |
--------------------------------------------------------------------------------
/4-Build-GoMusic/src/CreditCards.js:
--------------------------------------------------------------------------------
1 | import React from 'react';
2 | import { injectStripe, StripeProvider, Elements, CardElement } from 'react-stripe-elements';
3 |
4 | const INITIALSTATE = "INITIAL", SUCCESSSTATE = "COMPLETE", FAILEDSTATE = "FAILED";
5 |
6 | class CreditCardForm extends React.Component {
7 | constructor(props) {
8 | super(props);
9 | this.handleSubmit = this.handleSubmit.bind(this);
10 | this.handleInputChange = this.handleInputChange.bind(this);
11 | this.state = {
12 | value: '',
13 | status: INITIALSTATE
14 | };
15 | }
16 |
17 | renderCreditCardInformation() {
18 | const style = {
19 | base: {
20 | 'fontSize': '20px',
21 | 'color': '#495057',
22 | 'fontFamily': 'apple-system,BlinkMacSystemFont,"Segoe UI",Roboto,"Helvetica Neue",Arial,sans-serif'
23 | }
24 | };
25 | const usersavedcard =
26 |
27 |
28 |
29 |
30 |
31 |
32 | const remembercardcheck =
33 |
34 |
37 |
;
38 | return (
39 |
40 | {usersavedcard}
41 |
Payment Info
42 |
59 |
60 | );
61 | }
62 |
63 | renderSuccess() {
64 | return (
65 |
66 |
Request Successfull....
67 |
68 |
69 | );
70 | }
71 |
72 | renderFailure() {
73 | return (
74 |
75 |
Credit card information invalid, try again or exit
76 | {this.renderCreditCardInformation()}
77 |
78 | );
79 | }
80 |
81 | async handleSubmit(event) {
82 |
83 | event.preventDefault();
84 | console.log("Handle submit called, with name: " + this.state.value);
85 |
86 | let { token } = await this.props.stripe.createToken({ name: this.state.value });
87 | if (token == null) {
88 | console.log("invalid token");
89 | this.setState({ status: FAILEDSTATE });
90 | return;
91 | }
92 |
93 | let response = await fetch("/charge", {
94 | method: "POST",
95 | headers: { "Content-Type": "text/plain" },
96 | body: JSON.stringify({
97 | token: token.id,
98 | operation: this.props.operation,
99 | })
100 | });
101 | console.log(response.ok);
102 | if (response.ok) {
103 | console.log("Purchase Complete!");
104 | this.setState({ status: SUCCESSSTATE });
105 | }
106 | // document.getElementsByClassName('#modal').modal('hide');
107 | }
108 |
109 | handleInputChange(event) {
110 | this.setState({
111 | value: event.target.value
112 | });
113 | }
114 |
115 | render() {
116 |
117 | let body = null;
118 | switch (this.state.status) {
119 | case SUCCESSSTATE:
120 | body = this.renderSuccess();
121 | break;
122 | case FAILEDSTATE:
123 | body = this.renderFailure();
124 | break;
125 | default:
126 | body = this.renderCreditCardInformation();
127 | }
128 |
129 | return (
130 |
131 | {body}
132 |
133 | );
134 | }
135 | }
136 |
137 | export default function CreditCardInformation(props) {
138 | if (!props.show) {
139 | return null;
140 | }
141 | const CCFormWithStripe = injectStripe(CreditCardForm);
142 | return (
143 |
144 | {props.separator ?
: null}
145 |
146 |
147 |
148 |
149 |
150 |
151 | );
152 | }
153 |
154 |
--------------------------------------------------------------------------------
/4-Build-GoMusic/src/Navigation.js:
--------------------------------------------------------------------------------
1 | import React from 'react';
2 | import { NavLink } from 'react-router-dom';
3 |
4 |
5 | export default class Navigation extends React.Component {
6 |
7 | buildLoggedInMenu() {
8 | return (
9 |
10 |
11 |
14 |
17 |
18 |
19 | );
20 | }
21 |
22 | render() {
23 | return (
24 |
25 |
43 |
44 | );
45 | }
46 | }
--------------------------------------------------------------------------------
/4-Build-GoMusic/src/ProductCards.js:
--------------------------------------------------------------------------------
1 | import React from 'react';
2 |
3 | class Card extends React.Component {
4 | render() {
5 | const priceColor = (this.props.promo)? "text-danger" : "text-dark";
6 | return (
7 |
8 |
9 |

10 |
11 |
{this.props.productname}
12 | Price:
{this.props.price}
13 |
{this.props.desc}
14 |
Buy
15 |
16 |
17 |
18 | );
19 | }
20 | }
21 |
22 | export default class CardContainer extends React.Component {
23 | constructor(props) {
24 | super(props);
25 | this.state = {
26 | cards: []
27 | };
28 | }
29 |
30 | componentDidMount() {
31 | fetch(this.props.location)
32 | .then(res => res.json())
33 | .then((result) => {
34 | this.setState({
35 | cards: result
36 | });
37 | });
38 | }
39 |
40 | render() {
41 | const cards = this.state.cards;
42 | let items = cards.map(
43 | card =>
44 | );
45 | return (
46 |
47 |
48 | {items}
49 |
50 |
51 | );
52 | }
53 | }
54 |
--------------------------------------------------------------------------------
/4-Build-GoMusic/src/index.js:
--------------------------------------------------------------------------------
1 | import React from 'react';
2 | import ReactDOM from 'react-dom';
3 | import App from './App';
4 | import registerServiceWorker from './registerServiceWorker';
5 |
6 | ReactDOM.render(, document.getElementById('root'));
7 | registerServiceWorker();
8 |
--------------------------------------------------------------------------------
/4-Build-GoMusic/src/orders.js:
--------------------------------------------------------------------------------
1 | import React from 'react';
2 |
3 | function Order(props) {
4 | return (
5 |
6 |
7 |
{props.productname}
8 |
9 |
10 |
11 |

12 |
13 |
14 |
{props.desc}
15 |
16 | Price: {props.price}
17 |
18 |
19 |
20 |
21 |
22 | Purchased {props.days} days ago
23 |
24 |
25 |
26 |
27 | );
28 | }
29 |
30 | export default class OrderContainer extends React.Component {
31 | constructor(props) {
32 | super(props);
33 | this.state = {
34 | orders: []
35 | };
36 | }
37 |
38 | componentDidMount() {
39 | fetch(this.props.location)
40 | .then(res => res.json())
41 | .then((result) => {
42 | this.setState({
43 | orders: result.orders
44 | });
45 | });
46 | }
47 |
48 | render() {
49 |
50 | return (
51 |
52 | {this.state.orders.map(order => )}
53 |
54 | );
55 | }
56 | }
--------------------------------------------------------------------------------
/4-Build-GoMusic/src/registerServiceWorker.js:
--------------------------------------------------------------------------------
1 | // In production, we register a service worker to serve assets from local cache.
2 |
3 | // This lets the app load faster on subsequent visits in production, and gives
4 | // it offline capabilities. However, it also means that developers (and users)
5 | // will only see deployed updates on the "N+1" visit to a page, since previously
6 | // cached resources are updated in the background.
7 |
8 | // To learn more about the benefits of this model, read https://goo.gl/KwvDNy.
9 | // This link also includes instructions on opting out of this behavior.
10 |
11 | const isLocalhost = Boolean(
12 | window.location.hostname === 'localhost' ||
13 | // [::1] is the IPv6 localhost address.
14 | window.location.hostname === '[::1]' ||
15 | // 127.0.0.1/8 is considered localhost for IPv4.
16 | window.location.hostname.match(
17 | /^127(?:\.(?:25[0-5]|2[0-4][0-9]|[01]?[0-9][0-9]?)){3}$/
18 | )
19 | );
20 |
21 | export default function register() {
22 | if (process.env.NODE_ENV === 'production' && 'serviceWorker' in navigator) {
23 | // The URL constructor is available in all browsers that support SW.
24 | const publicUrl = new URL(process.env.PUBLIC_URL, window.location);
25 | if (publicUrl.origin !== window.location.origin) {
26 | // Our service worker won't work if PUBLIC_URL is on a different origin
27 | // from what our page is served on. This might happen if a CDN is used to
28 | // serve assets; see https://github.com/facebookincubator/create-react-app/issues/2374
29 | return;
30 | }
31 |
32 | window.addEventListener('load', () => {
33 | const swUrl = `${process.env.PUBLIC_URL}/service-worker.js`;
34 |
35 | if (isLocalhost) {
36 | // This is running on localhost. Lets check if a service worker still exists or not.
37 | checkValidServiceWorker(swUrl);
38 |
39 | // Add some additional logging to localhost, pointing developers to the
40 | // service worker/PWA documentation.
41 | navigator.serviceWorker.ready.then(() => {
42 | console.log(
43 | 'This web app is being served cache-first by a service ' +
44 | 'worker. To learn more, visit https://goo.gl/SC7cgQ'
45 | );
46 | });
47 | } else {
48 | // Is not local host. Just register service worker
49 | registerValidSW(swUrl);
50 | }
51 | });
52 | }
53 | }
54 |
55 | function registerValidSW(swUrl) {
56 | navigator.serviceWorker
57 | .register(swUrl)
58 | .then(registration => {
59 | registration.onupdatefound = () => {
60 | const installingWorker = registration.installing;
61 | installingWorker.onstatechange = () => {
62 | if (installingWorker.state === 'installed') {
63 | if (navigator.serviceWorker.controller) {
64 | // At this point, the old content will have been purged and
65 | // the fresh content will have been added to the cache.
66 | // It's the perfect time to display a "New content is
67 | // available; please refresh." message in your web app.
68 | console.log('New content is available; please refresh.');
69 | } else {
70 | // At this point, everything has been precached.
71 | // It's the perfect time to display a
72 | // "Content is cached for offline use." message.
73 | console.log('Content is cached for offline use.');
74 | }
75 | }
76 | };
77 | };
78 | })
79 | .catch(error => {
80 | console.error('Error during service worker registration:', error);
81 | });
82 | }
83 |
84 | function checkValidServiceWorker(swUrl) {
85 | // Check if the service worker can be found. If it can't reload the page.
86 | fetch(swUrl)
87 | .then(response => {
88 | // Ensure service worker exists, and that we really are getting a JS file.
89 | if (
90 | response.status === 404 ||
91 | response.headers.get('content-type').indexOf('javascript') === -1
92 | ) {
93 | // No service worker found. Probably a different app. Reload the page.
94 | navigator.serviceWorker.ready.then(registration => {
95 | registration.unregister().then(() => {
96 | window.location.reload();
97 | });
98 | });
99 | } else {
100 | // Service worker found. Proceed as normal.
101 | registerValidSW(swUrl);
102 | }
103 | })
104 | .catch(() => {
105 | console.log(
106 | 'No internet connection found. App is running in offline mode.'
107 | );
108 | });
109 | }
110 |
111 | export function unregister() {
112 | if ('serviceWorker' in navigator) {
113 | navigator.serviceWorker.ready.then(registration => {
114 | registration.unregister();
115 | });
116 | }
117 | }
118 |
--------------------------------------------------------------------------------
/5-RESTFul-API/README.md:
--------------------------------------------------------------------------------
1 | Chapter 5: RESTFul APIs
2 |
3 | This folder contains the backend and front end files for chapter 5.
4 |
--------------------------------------------------------------------------------
/5-RESTFul-API/backend/src/dblayer/dblayer.go:
--------------------------------------------------------------------------------
1 | package dblayer
2 |
3 | import (
4 | "github.com/minaandrawos/Hands-On-Full-Stack-Development-with-Go/5-RESTFul-API/backend/src/models"
5 | )
6 |
7 | type DBLayer interface {
8 | GetAllProducts() ([]models.Product, error)
9 | GetPromos() ([]models.Product, error)
10 | GetCustomerByName(string, string) (models.Customer, error)
11 | GetCustomerByID(int) (models.Customer, error)
12 | GetProduct(uint) (models.Product, error)
13 | AddUser(models.Customer) error
14 | SignInUser(models.Customer) error
15 | SignOutUserById(int) error
16 | GetCustomerOrdersByID(int) ([]models.Order, error)
17 | }
18 |
--------------------------------------------------------------------------------
/5-RESTFul-API/backend/src/dblayer/orm.go:
--------------------------------------------------------------------------------
1 | package dblayer
2 |
3 | import (
4 | "github.com/jinzhu/gorm"
5 | "github.com/minaandrawos/Hands-On-Full-Stack-Development-with-Go/5-RESTFul-API/backend/src/models"
6 | )
7 |
8 | //get products
9 | //get promos
10 | //post user sign in
11 | //get user orders
12 | //post user sign out
13 | //post purchase charge
14 |
15 | type DBORM struct {
16 | *gorm.DB
17 | }
18 |
19 | func NewORM(dbname, con string) (*DBORM, error) {
20 | db, err := gorm.Open(dbname, con)
21 | return &DBORM{
22 | DB: db,
23 | }, err
24 | }
25 |
26 | func (db *DBORM) GetAllProducts() ([]models.Product, error) {
27 | return nil, nil
28 | }
29 |
30 | func (db *DBORM) GetPromos() ([]models.Product, error) {
31 | return nil, nil
32 | }
33 |
34 | func (db *DBORM) GetCustomerByName(string, string) (models.Customer, error) {
35 | return models.Customer{}, nil
36 | }
37 |
38 | func (db *DBORM) GetCustomerByID(int) (models.Customer, error) {
39 | return models.Customer{}, nil
40 | }
41 |
42 | func (db *DBORM) GetProduct(uint) (models.Product, error) {
43 | return models.Product{}, nil
44 | }
45 |
46 | func (db *DBORM) SignInUser(models.Customer) error {
47 | return nil
48 | }
49 |
50 | func (db *DBORM) SignOutUserById(int) error {
51 | return nil
52 | }
53 |
54 | func (db *DBORM) GetCustomerOrdersByID(int) ([]models.Order, error) {
55 | return nil, nil
56 | }
57 |
--------------------------------------------------------------------------------
/5-RESTFul-API/backend/src/main.go:
--------------------------------------------------------------------------------
1 | package main
2 |
3 | import "log"
4 |
5 | func main() {
6 | log.Println("Main log....")
7 | }
8 |
--------------------------------------------------------------------------------
/5-RESTFul-API/backend/src/models/models.go:
--------------------------------------------------------------------------------
1 | package models
2 |
3 | import (
4 | "time"
5 |
6 | "github.com/jinzhu/gorm"
7 | )
8 |
9 | type Product struct {
10 | gorm.Model
11 | Image string `json:"img"`
12 | ImagAlt string `json:"imgalt" gorm:"column:imgalt"`
13 | Price float64 `json:"price"`
14 | Promotion float64 `json:"promotion"`
15 | PoructName string `gorm:"column:productname" json:"productname"`
16 | Description string
17 | }
18 |
19 | func (Product) TableName() string {
20 | return "products"
21 | }
22 |
23 | type Customer struct {
24 | gorm.Model
25 | FirstName string `gorm:"column:firstname" json:"firstname"`
26 | LastName string `gorm:"column:lastname" json:"lastname"`
27 | Email string `gorm:"column:email" json:"email"`
28 | CCToken string `gorm:"column:cctoken" json:"cctoken"`
29 | LoggedIn bool `gorm:"column:loggedin" json:"loggedin"`
30 | }
31 |
32 | func (Customer) TableName() string {
33 | return "customers"
34 | }
35 |
36 | type Order struct {
37 | gorm.Model
38 | Product
39 | Customer
40 | CustomerID int `gorm:"column:customer_id"`
41 | ProductID int `gorm:"column:product_id"`
42 | Price float64 `gorm:"column:price" json:"sell_price"`
43 | PurchaseDate time.Time `gorm:"column:purchase_date" json:"purchase_date"`
44 | }
45 |
46 | func (Order) TableName() string {
47 | return "orders"
48 | }
49 |
--------------------------------------------------------------------------------
/5-RESTFul-API/backend/src/rest/handler.go:
--------------------------------------------------------------------------------
1 | package rest
2 |
3 | import (
4 | "net/http"
5 | "strconv"
6 |
7 | "github.com/minaandrawos/Hands-On-Full-Stack-Development-with-Go/5-RESTFul-API/backend/src/models"
8 |
9 | "github.com/gin-gonic/gin"
10 | "github.com/minaandrawos/Hands-On-Full-Stack-Development-with-Go/5-RESTFul-API/backend/src/dblayer"
11 | )
12 |
13 | type Handler struct {
14 | db dblayer.DBLayer
15 | }
16 |
17 | func NewHandler() (*Handler, error) {
18 | return new(Handler), nil
19 | }
20 |
21 | func (h *Handler) GetProducts(c *gin.Context) {
22 | if h.db == nil {
23 | return
24 | }
25 | products, err := h.db.GetAllProducts()
26 | if err != nil {
27 | c.JSON(http.StatusInternalServerError, gin.H{"error": err.Error()})
28 | return
29 | }
30 | c.JSON(http.StatusOK, products)
31 | }
32 |
33 | func (h *Handler) GetPromos(c *gin.Context) {
34 | if h.db == nil {
35 | return
36 | }
37 | promos, err := h.db.GetPromos()
38 | if err != nil {
39 | c.JSON(http.StatusInternalServerError, gin.H{"error": err.Error()})
40 | return
41 | }
42 | c.JSON(http.StatusOK, promos)
43 | }
44 |
45 | func (h *Handler) SignIn(c *gin.Context) {
46 | if h.db == nil {
47 | return
48 | }
49 | var customer models.Customer
50 | err := c.ShouldBindJSON(&customer)
51 | if err != nil {
52 | c.JSON(http.StatusBadRequest, gin.H{"error": err.Error()})
53 | return
54 | }
55 | err = h.db.SignInUser(customer)
56 | if err != nil {
57 | c.JSON(http.StatusInternalServerError, gin.H{"error": err.Error()})
58 | return
59 | }
60 | }
61 |
62 | func (h *Handler) SignOut(c *gin.Context) {
63 | if h.db == nil {
64 | return
65 | }
66 | p := c.Param("id")
67 | id, err := strconv.Atoi(p)
68 | if err != nil {
69 | c.JSON(http.StatusBadRequest, gin.H{"error": err.Error()})
70 | return
71 | }
72 |
73 | err = h.db.SignOutUserById(id)
74 | if err != nil {
75 | c.JSON(http.StatusInternalServerError, gin.H{"error": err.Error()})
76 | return
77 | }
78 | }
79 |
80 | func (h *Handler) GetOrders(c *gin.Context) {
81 | if h.db == nil {
82 | return
83 | }
84 | p := c.Param("id")
85 | id, err := strconv.Atoi(p)
86 | if err != nil {
87 | c.JSON(http.StatusBadRequest, gin.H{"error": err.Error()})
88 | return
89 | }
90 | orders, err := h.db.GetCustomerOrdersByID(id)
91 | if err != nil {
92 | c.JSON(http.StatusInternalServerError, gin.H{"error": err.Error()})
93 | return
94 | }
95 | c.JSON(http.StatusOK, orders)
96 | }
97 |
98 | func (h *Handler) Charge(c *gin.Context) {
99 | if h.db == nil {
100 | return
101 | }
102 |
103 | }
104 |
--------------------------------------------------------------------------------
/5-RESTFul-API/backend/src/rest/rest.go:
--------------------------------------------------------------------------------
1 | package rest
2 |
3 | import (
4 | "github.com/gin-gonic/gin"
5 | )
6 |
7 | func RunAPI(address string) error {
8 | //Get gin's default engine
9 | r := gin.Default()
10 | //load homepage
11 | r.GET("/", h.GetMainPage)
12 | //get products
13 | r.GET("/products", h.GetProducts)
14 | //get promos
15 | r.GET("/promos", h.GetPromos)
16 | /*
17 | //post user sign in
18 | r.POST("/user/signin", h.SignIn)
19 | //post user sign out
20 | r.POST("/user/:id/signout", h.SignOut)
21 | //get user orders
22 | r.GET("/user/:id/orders", h.GetOrders)
23 | //post purchase charge
24 | r.POST("/user/charge", h.Charge)
25 | */
26 |
27 | userGroup := r.Group("/user")
28 | {
29 | userGroup.POST("/:id/signout", h.SignOut)
30 | userGroup.GET("/:id/orders", h.GetOrders)
31 | }
32 |
33 | usersGroup := r.Group("/users")
34 | {
35 | usersGroup.POST("/charge", h.Charge)
36 | usersGroup.POST("/signin", h.SignIn)
37 | usersGroup.POST("", h.AddUser)
38 | }
39 | return r.Run(address)
40 | }
41 |
--------------------------------------------------------------------------------
/5-RESTFul-API/frontend/README.md:
--------------------------------------------------------------------------------
1 | Front end code for chapter 5
2 |
--------------------------------------------------------------------------------
/5-RESTFul-API/frontend/package.json:
--------------------------------------------------------------------------------
1 | {
2 | "name": "gomusic",
3 | "version": "0.1.0",
4 | "private": true,
5 | "dependencies": {
6 | "react": "^16.4.1",
7 | "react-dom": "^16.4.1",
8 | "react-router-dom": "^4.3.1",
9 | "react-scripts": "1.1.4",
10 | "react-stripe-elements": "^2.0.1"
11 | },
12 | "scripts": {
13 | "start": "react-scripts start",
14 | "build": "react-scripts build",
15 | "test": "react-scripts test --env=jsdom",
16 | "eject": "react-scripts eject"
17 | }
18 | }
19 |
--------------------------------------------------------------------------------
/5-RESTFul-API/frontend/public/cards.json:
--------------------------------------------------------------------------------
1 | [{
2 | "id" : 1,
3 | "img" : "img/strings.png",
4 | "imgalt":"string",
5 | "desc":"A very authentic and beautiful instrument!!",
6 | "price" : 100.0,
7 | "productname" : "Strings"
8 | }, {
9 | "id" : 2,
10 | "img" : "img/redguitar.jpeg",
11 | "imgalt":"redg",
12 | "desc":"A really cool red guitar that can produce super cool music!!",
13 | "price" : 299.0,
14 | "productname" : "Red Guitar"
15 | },{
16 | "id" : 3,
17 | "img" : "img/drums.jpg",
18 | "imgalt":"drums",
19 | "desc":"A set of super awesome drums, combined with a guitar, they can product more than amazing music!!",
20 | "price" : 17000.0,
21 | "productname" : "Drums"
22 | },{
23 | "id" : 4,
24 | "img" : "img/flute.jpeg",
25 | "imgalt":"flute",
26 | "desc":"A super nice flute combined with some super nice musical notes!!",
27 | "price" : 210.0,
28 | "productname" : "Flute"
29 | },{
30 | "id" : 5,
31 | "img" : "img/blackguitar.jpeg",
32 | "imgalt":"Black guitar",
33 | "desc":"An awesome guitar that will product amazing sound!!",
34 | "price" : 200.0,
35 | "productname" : "Black Guitar"
36 | },{
37 | "id" : 6,
38 | "img" : "img/saxophone.jpeg",
39 | "imgalt":"Saxophone",
40 | "desc":"An great saxophone for a great musician!!",
41 | "price" : 1000.0,
42 | "productname" : "Saxophone"
43 | }]
--------------------------------------------------------------------------------
/5-RESTFul-API/frontend/public/favicon.ico:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/minaandrawos/Hands-On-Full-Stack-Development-with-Go/bb0ccde535e21378f8f01aa7b20b66e7bba2a671/5-RESTFul-API/frontend/public/favicon.ico
--------------------------------------------------------------------------------
/5-RESTFul-API/frontend/public/img/blackguitar.jpeg:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/minaandrawos/Hands-On-Full-Stack-Development-with-Go/bb0ccde535e21378f8f01aa7b20b66e7bba2a671/5-RESTFul-API/frontend/public/img/blackguitar.jpeg
--------------------------------------------------------------------------------
/5-RESTFul-API/frontend/public/img/coolguitar.jpeg:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/minaandrawos/Hands-On-Full-Stack-Development-with-Go/bb0ccde535e21378f8f01aa7b20b66e7bba2a671/5-RESTFul-API/frontend/public/img/coolguitar.jpeg
--------------------------------------------------------------------------------
/5-RESTFul-API/frontend/public/img/drums.jpg:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/minaandrawos/Hands-On-Full-Stack-Development-with-Go/bb0ccde535e21378f8f01aa7b20b66e7bba2a671/5-RESTFul-API/frontend/public/img/drums.jpg
--------------------------------------------------------------------------------
/5-RESTFul-API/frontend/public/img/flute.jpeg:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/minaandrawos/Hands-On-Full-Stack-Development-with-Go/bb0ccde535e21378f8f01aa7b20b66e7bba2a671/5-RESTFul-API/frontend/public/img/flute.jpeg
--------------------------------------------------------------------------------
/5-RESTFul-API/frontend/public/img/img-small/drums.jpg:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/minaandrawos/Hands-On-Full-Stack-Development-with-Go/bb0ccde535e21378f8f01aa7b20b66e7bba2a671/5-RESTFul-API/frontend/public/img/img-small/drums.jpg
--------------------------------------------------------------------------------
/5-RESTFul-API/frontend/public/img/img-small/pexels-photo-290660(1).jpeg:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/minaandrawos/Hands-On-Full-Stack-Development-with-Go/bb0ccde535e21378f8f01aa7b20b66e7bba2a671/5-RESTFul-API/frontend/public/img/img-small/pexels-photo-290660(1).jpeg
--------------------------------------------------------------------------------
/5-RESTFul-API/frontend/public/img/img-small/redguitar.jpeg:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/minaandrawos/Hands-On-Full-Stack-Development-with-Go/bb0ccde535e21378f8f01aa7b20b66e7bba2a671/5-RESTFul-API/frontend/public/img/img-small/redguitar.jpeg
--------------------------------------------------------------------------------
/5-RESTFul-API/frontend/public/img/img-small/strings.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/minaandrawos/Hands-On-Full-Stack-Development-with-Go/bb0ccde535e21378f8f01aa7b20b66e7bba2a671/5-RESTFul-API/frontend/public/img/img-small/strings.png
--------------------------------------------------------------------------------
/5-RESTFul-API/frontend/public/img/mics.jpeg:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/minaandrawos/Hands-On-Full-Stack-Development-with-Go/bb0ccde535e21378f8f01aa7b20b66e7bba2a671/5-RESTFul-API/frontend/public/img/mics.jpeg
--------------------------------------------------------------------------------
/5-RESTFul-API/frontend/public/img/redguitar.jpeg:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/minaandrawos/Hands-On-Full-Stack-Development-with-Go/bb0ccde535e21378f8f01aa7b20b66e7bba2a671/5-RESTFul-API/frontend/public/img/redguitar.jpeg
--------------------------------------------------------------------------------
/5-RESTFul-API/frontend/public/img/saxophone.jpeg:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/minaandrawos/Hands-On-Full-Stack-Development-with-Go/bb0ccde535e21378f8f01aa7b20b66e7bba2a671/5-RESTFul-API/frontend/public/img/saxophone.jpeg
--------------------------------------------------------------------------------
/5-RESTFul-API/frontend/public/img/strings.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/minaandrawos/Hands-On-Full-Stack-Development-with-Go/bb0ccde535e21378f8f01aa7b20b66e7bba2a671/5-RESTFul-API/frontend/public/img/strings.png
--------------------------------------------------------------------------------
/5-RESTFul-API/frontend/public/img/sunnyguitar.jpeg:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/minaandrawos/Hands-On-Full-Stack-Development-with-Go/bb0ccde535e21378f8f01aa7b20b66e7bba2a671/5-RESTFul-API/frontend/public/img/sunnyguitar.jpeg
--------------------------------------------------------------------------------
/5-RESTFul-API/frontend/public/index.html:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 |
6 |
7 |
8 |
10 |
11 |
12 |
16 |
17 |
18 |
27 | React App
28 |
29 |
30 |
31 |
32 |
33 |
36 |
37 |
38 |
39 |
40 |
42 |
44 |
46 |
47 |
48 |
--------------------------------------------------------------------------------
/5-RESTFul-API/frontend/public/manifest.json:
--------------------------------------------------------------------------------
1 | {
2 | "short_name": "React App",
3 | "name": "Create React App Sample",
4 | "icons": [
5 | {
6 | "src": "favicon.ico",
7 | "sizes": "64x64 32x32 24x24 16x16",
8 | "type": "image/x-icon"
9 | }
10 | ],
11 | "start_url": "./index.html",
12 | "display": "standalone",
13 | "theme_color": "#000000",
14 | "background_color": "#ffffff"
15 | }
16 |
--------------------------------------------------------------------------------
/5-RESTFul-API/frontend/public/promos.json:
--------------------------------------------------------------------------------
1 | [{
2 | "id" : 3,
3 | "img" : "img/drums.jpg",
4 | "imgalt":"drums",
5 | "desc":"A set of super awesome drums, combined with a guitar, they can product more than amazing music!!",
6 | "price" : 17000.0,
7 | "productname" : "Drums"
8 | },{
9 | "id" : 4,
10 | "img" : "img/flute.jpeg",
11 | "imgalt":"flute",
12 | "desc":"A super nice flute combined with some super nice musical notes!!",
13 | "price" : 210.0,
14 | "productname" : "Flute"
15 | },{
16 | "id" : 5,
17 | "img" : "img/blackguitar.jpeg",
18 | "imgalt":"Black guitar",
19 | "desc":"An awesome guitar that will product amazing sound!!",
20 | "price" : 200.0,
21 | "productname" : "Black Guitar"
22 | },{
23 | "id" : 6,
24 | "img" : "img/saxophone.jpeg",
25 | "imgalt":"Saxophone",
26 | "desc":"An great saxophone for a great musician!!",
27 | "price" : 1000.0,
28 | "productname" : "Saxophone"
29 | }]
--------------------------------------------------------------------------------
/5-RESTFul-API/frontend/public/user.json:
--------------------------------------------------------------------------------
1 | {
2 | "name": "Mina",
3 | "loggedin": true,
4 | "orders": [
5 | {
6 | "id" : 1,
7 | "img" : "img/img-small/strings.png",
8 | "imgalt":"string",
9 | "desc":"A very authentic and beautiful instrument!!",
10 | "price" : 100.0,
11 | "productname" : "Strings",
12 | "days": 32
13 | }, {
14 | "id" : 2,
15 | "img" : "img/img-small/redguitar.jpeg",
16 | "imgalt":"redg",
17 | "desc":"A really cool red guitar that can produce super cool music!!",
18 | "price" : 299.0,
19 | "productname" : "Red Guitar",
20 | "days": 99
21 | },{
22 | "id" : 3,
23 | "img" : "img/img-small/drums.jpg",
24 | "imgalt":"drums",
25 | "desc":"A set of super awesome drums, combined with a guitar, they can product more than amazing music!!",
26 | "price" : 17000.0,
27 | "productname" : "Drums",
28 | "days": 45
29 | }
30 | ]
31 | }
--------------------------------------------------------------------------------
/5-RESTFul-API/frontend/src/About.js:
--------------------------------------------------------------------------------
1 | import React from 'react';
2 |
3 |
4 | export default function About(props) {
5 | return (
6 |
7 |
8 |
About the Go Music Store
9 |
Go music is a modern online msucial instruments store
10 |
Explore how you can combine the power of React and Go, to build a fast and beautiful looking online store.
11 |
We will cover how to build this website step by step.
12 |
13 |
);
14 | }
--------------------------------------------------------------------------------
/5-RESTFul-API/frontend/src/App.js:
--------------------------------------------------------------------------------
1 | import React from 'react';
2 | import CardContainer from './ProductCards';
3 | import { BrowserRouter as Router, Route } from "react-router-dom";
4 | import Nav from './Navigation';
5 | import { SignInModalWindow, BuyModalWindow } from './modalwindows';
6 | import About from './About';
7 | import Orders from './orders';
8 |
9 |
10 | class App extends React.Component {
11 |
12 | constructor(props) {
13 | super(props);
14 | this.state = {
15 | user: {
16 | loggedin: false,
17 | name: ""
18 | }
19 | };
20 | }
21 |
22 | handleSignedIn(user) {
23 | this.setState({
24 | user: user
25 | });
26 | }
27 |
28 | componentDidMount() {
29 | fetch('user.json')
30 | .then(res => res.json())
31 | .then((result) => {
32 | console.log('Fetch...');
33 | this.setState({
34 | user: result
35 | });
36 | });
37 | }
38 |
39 | render() {
40 | return (
41 |
42 |
43 |
44 |
45 |
46 | } />
47 | } />
48 | {this.state.user.loggedin ? }/> : null}
49 |
50 |
51 |
52 |
53 |
54 |
55 |
56 | );
57 | }
58 | }
59 |
60 | export default App;
61 |
--------------------------------------------------------------------------------
/5-RESTFul-API/frontend/src/App.test.js:
--------------------------------------------------------------------------------
1 | import React from 'react';
2 | import ReactDOM from 'react-dom';
3 | import App from './App';
4 |
5 | it('renders without crashing', () => {
6 | const div = document.createElement('div');
7 | ReactDOM.render(, div);
8 | ReactDOM.unmountComponentAtNode(div);
9 | });
10 |
--------------------------------------------------------------------------------
/5-RESTFul-API/frontend/src/CreditCards.js:
--------------------------------------------------------------------------------
1 | import React from 'react';
2 | import { injectStripe, StripeProvider, Elements, CardElement } from 'react-stripe-elements';
3 |
4 | const INITIALSTATE = "INITIAL", SUCCESSSTATE = "COMPLETE", FAILEDSTATE = "FAILED";
5 |
6 | class CreditCardForm extends React.Component {
7 | constructor(props) {
8 | super(props);
9 | this.handleSubmit = this.handleSubmit.bind(this);
10 | this.handleInputChange = this.handleInputChange.bind(this);
11 | this.state = {
12 | value: '',
13 | status: INITIALSTATE
14 | };
15 | }
16 |
17 | renderCreditCardInformation() {
18 | const style = {
19 | base: {
20 | 'fontSize': '20px',
21 | 'color': '#495057',
22 | 'fontFamily': 'apple-system,BlinkMacSystemFont,"Segoe UI",Roboto,"Helvetica Neue",Arial,sans-serif'
23 | }
24 | };
25 | const usersavedcard =
26 |
27 |
28 |
29 |
30 |
31 |
32 | const remembercardcheck =
33 |
34 |
37 |
;
38 | return (
39 |
40 | {usersavedcard}
41 |
Payment Info
42 |
59 |
60 | );
61 | }
62 |
63 | renderSuccess() {
64 | return (
65 |
66 |
Request Successfull....
67 |
68 |
69 | );
70 | }
71 |
72 | renderFailure() {
73 | return (
74 |
75 |
Credit card information invalid, try again or exit
76 | {this.renderCreditCardInformation()}
77 |
78 | );
79 | }
80 |
81 | async handleSubmit(event) {
82 |
83 | event.preventDefault();
84 | console.log("Handle submit called, with name: " + this.state.value);
85 |
86 | let { token } = await this.props.stripe.createToken({ name: this.state.value });
87 | if (token == null) {
88 | console.log("invalid token");
89 | this.setState({ status: FAILEDSTATE });
90 | return;
91 | }
92 |
93 | let response = await fetch("/charge", {
94 | method: "POST",
95 | headers: { "Content-Type": "text/plain" },
96 | body: JSON.stringify({
97 | token: token.id,
98 | operation: this.props.operation,
99 | })
100 | });
101 | console.log(response.ok);
102 | if (response.ok) {
103 | console.log("Purchase Complete!");
104 | this.setState({ status: SUCCESSSTATE });
105 | }
106 | // document.getElementsByClassName('#modal').modal('hide');
107 | }
108 |
109 | handleInputChange(event) {
110 | this.setState({
111 | value: event.target.value
112 | });
113 | }
114 |
115 | render() {
116 |
117 | let body = null;
118 | switch (this.state.status) {
119 | case SUCCESSSTATE:
120 | body = this.renderSuccess();
121 | break;
122 | case FAILEDSTATE:
123 | body = this.renderFailure();
124 | break;
125 | default:
126 | body = this.renderCreditCardInformation();
127 | }
128 |
129 | return (
130 |
131 | {body}
132 |
133 | );
134 | }
135 | }
136 |
137 | export default function CreditCardInformation(props) {
138 | if (!props.show) {
139 | return null;
140 | }
141 | const CCFormWithStripe = injectStripe(CreditCardForm);
142 | return (
143 |
144 | {props.separator ?
: null}
145 |
146 |
147 |
148 |
149 |
150 |
151 | );
152 | }
153 |
154 |
--------------------------------------------------------------------------------
/5-RESTFul-API/frontend/src/Navigation.js:
--------------------------------------------------------------------------------
1 | import React from 'react';
2 | import { NavLink } from 'react-router-dom';
3 |
4 |
5 | export default class Navigation extends React.Component {
6 |
7 | buildLoggedInMenu() {
8 | return (
9 |
10 |
11 |
14 |
17 |
18 |
19 | );
20 | }
21 |
22 | render() {
23 | return (
24 |
25 |
43 |
44 | );
45 | }
46 | }
--------------------------------------------------------------------------------
/5-RESTFul-API/frontend/src/ProductCards.js:
--------------------------------------------------------------------------------
1 | import React from 'react';
2 |
3 | class Card extends React.Component {
4 | render() {
5 | const priceColor = (this.props.promo)? "text-danger" : "text-dark";
6 | return (
7 |
8 |
9 |

10 |
11 |
{this.props.productname}
12 | Price:
{this.props.price}
13 |
{this.props.desc}
14 |
Buy
15 |
16 |
17 |
18 | );
19 | }
20 | }
21 |
22 | export default class CardContainer extends React.Component {
23 | constructor(props) {
24 | super(props);
25 | this.state = {
26 | cards: []
27 | };
28 | }
29 |
30 | componentDidMount() {
31 | fetch(this.props.location)
32 | .then(res => res.json())
33 | .then((result) => {
34 | this.setState({
35 | cards: result
36 | });
37 | });
38 | }
39 |
40 | render() {
41 | const cards = this.state.cards;
42 | let items = cards.map(
43 | card =>
44 | );
45 | return (
46 |
47 |
48 | {items}
49 |
50 |
51 | );
52 | }
53 | }
54 |
--------------------------------------------------------------------------------
/5-RESTFul-API/frontend/src/index.js:
--------------------------------------------------------------------------------
1 | import React from 'react';
2 | import ReactDOM from 'react-dom';
3 | import App from './App';
4 | import registerServiceWorker from './registerServiceWorker';
5 |
6 | ReactDOM.render(, document.getElementById('root'));
7 | registerServiceWorker();
8 |
--------------------------------------------------------------------------------
/5-RESTFul-API/frontend/src/orders.js:
--------------------------------------------------------------------------------
1 | import React from 'react';
2 |
3 | function Order(props) {
4 | return (
5 |
6 |
7 |
{props.productname}
8 |
9 |
10 |
11 |

12 |
13 |
14 |
{props.desc}
15 |
16 | Price: {props.price}
17 |
18 |
19 |
20 |
21 |
22 | Purchased {props.days} days ago
23 |
24 |
25 |
26 |
27 | );
28 | }
29 |
30 | export default class OrderContainer extends React.Component {
31 | constructor(props) {
32 | super(props);
33 | this.state = {
34 | orders: []
35 | };
36 | }
37 |
38 | componentDidMount() {
39 | fetch(this.props.location)
40 | .then(res => res.json())
41 | .then((result) => {
42 | this.setState({
43 | orders: result.orders
44 | });
45 | });
46 | }
47 |
48 | render() {
49 |
50 | return (
51 |
52 | {this.state.orders.map(order => )}
53 |
54 | );
55 | }
56 | }
--------------------------------------------------------------------------------
/5-RESTFul-API/frontend/src/registerServiceWorker.js:
--------------------------------------------------------------------------------
1 | // In production, we register a service worker to serve assets from local cache.
2 |
3 | // This lets the app load faster on subsequent visits in production, and gives
4 | // it offline capabilities. However, it also means that developers (and users)
5 | // will only see deployed updates on the "N+1" visit to a page, since previously
6 | // cached resources are updated in the background.
7 |
8 | // To learn more about the benefits of this model, read https://goo.gl/KwvDNy.
9 | // This link also includes instructions on opting out of this behavior.
10 |
11 | const isLocalhost = Boolean(
12 | window.location.hostname === 'localhost' ||
13 | // [::1] is the IPv6 localhost address.
14 | window.location.hostname === '[::1]' ||
15 | // 127.0.0.1/8 is considered localhost for IPv4.
16 | window.location.hostname.match(
17 | /^127(?:\.(?:25[0-5]|2[0-4][0-9]|[01]?[0-9][0-9]?)){3}$/
18 | )
19 | );
20 |
21 | export default function register() {
22 | if (process.env.NODE_ENV === 'production' && 'serviceWorker' in navigator) {
23 | // The URL constructor is available in all browsers that support SW.
24 | const publicUrl = new URL(process.env.PUBLIC_URL, window.location);
25 | if (publicUrl.origin !== window.location.origin) {
26 | // Our service worker won't work if PUBLIC_URL is on a different origin
27 | // from what our page is served on. This might happen if a CDN is used to
28 | // serve assets; see https://github.com/facebookincubator/create-react-app/issues/2374
29 | return;
30 | }
31 |
32 | window.addEventListener('load', () => {
33 | const swUrl = `${process.env.PUBLIC_URL}/service-worker.js`;
34 |
35 | if (isLocalhost) {
36 | // This is running on localhost. Lets check if a service worker still exists or not.
37 | checkValidServiceWorker(swUrl);
38 |
39 | // Add some additional logging to localhost, pointing developers to the
40 | // service worker/PWA documentation.
41 | navigator.serviceWorker.ready.then(() => {
42 | console.log(
43 | 'This web app is being served cache-first by a service ' +
44 | 'worker. To learn more, visit https://goo.gl/SC7cgQ'
45 | );
46 | });
47 | } else {
48 | // Is not local host. Just register service worker
49 | registerValidSW(swUrl);
50 | }
51 | });
52 | }
53 | }
54 |
55 | function registerValidSW(swUrl) {
56 | navigator.serviceWorker
57 | .register(swUrl)
58 | .then(registration => {
59 | registration.onupdatefound = () => {
60 | const installingWorker = registration.installing;
61 | installingWorker.onstatechange = () => {
62 | if (installingWorker.state === 'installed') {
63 | if (navigator.serviceWorker.controller) {
64 | // At this point, the old content will have been purged and
65 | // the fresh content will have been added to the cache.
66 | // It's the perfect time to display a "New content is
67 | // available; please refresh." message in your web app.
68 | console.log('New content is available; please refresh.');
69 | } else {
70 | // At this point, everything has been precached.
71 | // It's the perfect time to display a
72 | // "Content is cached for offline use." message.
73 | console.log('Content is cached for offline use.');
74 | }
75 | }
76 | };
77 | };
78 | })
79 | .catch(error => {
80 | console.error('Error during service worker registration:', error);
81 | });
82 | }
83 |
84 | function checkValidServiceWorker(swUrl) {
85 | // Check if the service worker can be found. If it can't reload the page.
86 | fetch(swUrl)
87 | .then(response => {
88 | // Ensure service worker exists, and that we really are getting a JS file.
89 | if (
90 | response.status === 404 ||
91 | response.headers.get('content-type').indexOf('javascript') === -1
92 | ) {
93 | // No service worker found. Probably a different app. Reload the page.
94 | navigator.serviceWorker.ready.then(registration => {
95 | registration.unregister().then(() => {
96 | window.location.reload();
97 | });
98 | });
99 | } else {
100 | // Service worker found. Proceed as normal.
101 | registerValidSW(swUrl);
102 | }
103 | })
104 | .catch(() => {
105 | console.log(
106 | 'No internet connection found. App is running in offline mode.'
107 | );
108 | });
109 | }
110 |
111 | export function unregister() {
112 | if ('serviceWorker' in navigator) {
113 | navigator.serviceWorker.ready.then(registration => {
114 | registration.unregister();
115 | });
116 | }
117 | }
118 |
--------------------------------------------------------------------------------
/6-Advanced-Web/Frontend/package.json:
--------------------------------------------------------------------------------
1 | {
2 | "name": "gomusic",
3 | "version": "0.1.0",
4 | "private": true,
5 | "proxy": "http://localhost:8000/",
6 | "dependencies": {
7 | "react": "^16.4.1",
8 | "react-cookie": "^3.0.4",
9 | "react-dom": "^16.4.1",
10 | "react-router-dom": "^4.3.1",
11 | "react-scripts": "1.1.4",
12 | "react-stripe-elements": "^2.0.1"
13 | },
14 | "scripts": {
15 | "start": "react-scripts start",
16 | "build": "react-scripts build",
17 | "test": "react-scripts test --env=jsdom",
18 | "eject": "react-scripts eject"
19 | }
20 | }
21 |
--------------------------------------------------------------------------------
/6-Advanced-Web/Frontend/public/cards.json:
--------------------------------------------------------------------------------
1 | [{
2 | "id" : 1,
3 | "img" : "img/strings.png",
4 | "imgalt":"string",
5 | "desc":"A very authentic and beautiful instrument!!",
6 | "price" : 100.0,
7 | "productname" : "Strings"
8 | }, {
9 | "id" : 2,
10 | "img" : "img/redguitar.jpeg",
11 | "imgalt":"redg",
12 | "desc":"A really cool red guitar that can produce super cool music!!",
13 | "price" : 299.0,
14 | "productname" : "Red Guitar"
15 | },{
16 | "id" : 3,
17 | "img" : "img/drums.jpg",
18 | "imgalt":"drums",
19 | "desc":"A set of super awesome drums, combined with a guitar, they can product more than amazing music!!",
20 | "price" : 17000.0,
21 | "productname" : "Drums"
22 | },{
23 | "id" : 4,
24 | "img" : "img/flute.jpeg",
25 | "imgalt":"flute",
26 | "desc":"A super nice flute combined with some super nice musical notes!!",
27 | "price" : 210.0,
28 | "productname" : "Flute"
29 | },{
30 | "id" : 5,
31 | "img" : "img/blackguitar.jpeg",
32 | "imgalt":"Black guitar",
33 | "desc":"An awesome guitar that will product amazing sound!!",
34 | "price" : 200.0,
35 | "productname" : "Black Guitar"
36 | },{
37 | "id" : 6,
38 | "img" : "img/saxophone.jpeg",
39 | "imgalt":"Saxophone",
40 | "desc":"An great saxophone for a great musician!!",
41 | "price" : 1000.0,
42 | "productname" : "Saxophone"
43 | }]
--------------------------------------------------------------------------------
/6-Advanced-Web/Frontend/public/favicon.ico:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/minaandrawos/Hands-On-Full-Stack-Development-with-Go/bb0ccde535e21378f8f01aa7b20b66e7bba2a671/6-Advanced-Web/Frontend/public/favicon.ico
--------------------------------------------------------------------------------
/6-Advanced-Web/Frontend/public/img/blackguitar.jpeg:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/minaandrawos/Hands-On-Full-Stack-Development-with-Go/bb0ccde535e21378f8f01aa7b20b66e7bba2a671/6-Advanced-Web/Frontend/public/img/blackguitar.jpeg
--------------------------------------------------------------------------------
/6-Advanced-Web/Frontend/public/img/coolguitar.jpeg:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/minaandrawos/Hands-On-Full-Stack-Development-with-Go/bb0ccde535e21378f8f01aa7b20b66e7bba2a671/6-Advanced-Web/Frontend/public/img/coolguitar.jpeg
--------------------------------------------------------------------------------
/6-Advanced-Web/Frontend/public/img/drums.jpg:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/minaandrawos/Hands-On-Full-Stack-Development-with-Go/bb0ccde535e21378f8f01aa7b20b66e7bba2a671/6-Advanced-Web/Frontend/public/img/drums.jpg
--------------------------------------------------------------------------------
/6-Advanced-Web/Frontend/public/img/flute.jpeg:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/minaandrawos/Hands-On-Full-Stack-Development-with-Go/bb0ccde535e21378f8f01aa7b20b66e7bba2a671/6-Advanced-Web/Frontend/public/img/flute.jpeg
--------------------------------------------------------------------------------
/6-Advanced-Web/Frontend/public/img/img-small/drums.jpg:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/minaandrawos/Hands-On-Full-Stack-Development-with-Go/bb0ccde535e21378f8f01aa7b20b66e7bba2a671/6-Advanced-Web/Frontend/public/img/img-small/drums.jpg
--------------------------------------------------------------------------------
/6-Advanced-Web/Frontend/public/img/img-small/pexels-photo-290660(1).jpeg:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/minaandrawos/Hands-On-Full-Stack-Development-with-Go/bb0ccde535e21378f8f01aa7b20b66e7bba2a671/6-Advanced-Web/Frontend/public/img/img-small/pexels-photo-290660(1).jpeg
--------------------------------------------------------------------------------
/6-Advanced-Web/Frontend/public/img/img-small/redguitar.jpeg:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/minaandrawos/Hands-On-Full-Stack-Development-with-Go/bb0ccde535e21378f8f01aa7b20b66e7bba2a671/6-Advanced-Web/Frontend/public/img/img-small/redguitar.jpeg
--------------------------------------------------------------------------------
/6-Advanced-Web/Frontend/public/img/img-small/strings.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/minaandrawos/Hands-On-Full-Stack-Development-with-Go/bb0ccde535e21378f8f01aa7b20b66e7bba2a671/6-Advanced-Web/Frontend/public/img/img-small/strings.png
--------------------------------------------------------------------------------
/6-Advanced-Web/Frontend/public/img/mics.jpeg:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/minaandrawos/Hands-On-Full-Stack-Development-with-Go/bb0ccde535e21378f8f01aa7b20b66e7bba2a671/6-Advanced-Web/Frontend/public/img/mics.jpeg
--------------------------------------------------------------------------------
/6-Advanced-Web/Frontend/public/img/redguitar.jpeg:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/minaandrawos/Hands-On-Full-Stack-Development-with-Go/bb0ccde535e21378f8f01aa7b20b66e7bba2a671/6-Advanced-Web/Frontend/public/img/redguitar.jpeg
--------------------------------------------------------------------------------
/6-Advanced-Web/Frontend/public/img/saxophone.jpeg:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/minaandrawos/Hands-On-Full-Stack-Development-with-Go/bb0ccde535e21378f8f01aa7b20b66e7bba2a671/6-Advanced-Web/Frontend/public/img/saxophone.jpeg
--------------------------------------------------------------------------------
/6-Advanced-Web/Frontend/public/img/strings.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/minaandrawos/Hands-On-Full-Stack-Development-with-Go/bb0ccde535e21378f8f01aa7b20b66e7bba2a671/6-Advanced-Web/Frontend/public/img/strings.png
--------------------------------------------------------------------------------
/6-Advanced-Web/Frontend/public/img/sunnyguitar.jpeg:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/minaandrawos/Hands-On-Full-Stack-Development-with-Go/bb0ccde535e21378f8f01aa7b20b66e7bba2a671/6-Advanced-Web/Frontend/public/img/sunnyguitar.jpeg
--------------------------------------------------------------------------------
/6-Advanced-Web/Frontend/public/index.html:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 |
6 |
7 |
8 |
10 |
11 |
12 |
16 |
17 |
18 |
27 | React App
28 |
29 |
30 |
31 |
32 |
33 |
36 |
37 |
38 |
39 |
40 |
42 |
44 |
46 |
47 |
48 |
--------------------------------------------------------------------------------
/6-Advanced-Web/Frontend/public/manifest.json:
--------------------------------------------------------------------------------
1 | {
2 | "short_name": "React App",
3 | "name": "Create React App Sample",
4 | "icons": [
5 | {
6 | "src": "favicon.ico",
7 | "sizes": "64x64 32x32 24x24 16x16",
8 | "type": "image/x-icon"
9 | }
10 | ],
11 | "start_url": "./index.html",
12 | "display": "standalone",
13 | "theme_color": "#000000",
14 | "background_color": "#ffffff"
15 | }
16 |
--------------------------------------------------------------------------------
/6-Advanced-Web/Frontend/public/promos.json:
--------------------------------------------------------------------------------
1 | [{
2 | "id" : 3,
3 | "img" : "img/drums.jpg",
4 | "imgalt":"drums",
5 | "desc":"A set of super awesome drums, combined with a guitar, they can product more than amazing music!!",
6 | "price" : 17000.0,
7 | "productname" : "Drums"
8 | },{
9 | "id" : 4,
10 | "img" : "img/flute.jpeg",
11 | "imgalt":"flute",
12 | "desc":"A super nice flute combined with some super nice musical notes!!",
13 | "price" : 210.0,
14 | "productname" : "Flute"
15 | },{
16 | "id" : 5,
17 | "img" : "img/blackguitar.jpeg",
18 | "imgalt":"Black guitar",
19 | "desc":"An awesome guitar that will product amazing sound!!",
20 | "price" : 200.0,
21 | "productname" : "Black Guitar"
22 | },{
23 | "id" : 6,
24 | "img" : "img/saxophone.jpeg",
25 | "imgalt":"Saxophone",
26 | "desc":"An great saxophone for a great musician!!",
27 | "price" : 1000.0,
28 | "productname" : "Saxophone"
29 | }]
--------------------------------------------------------------------------------
/6-Advanced-Web/Frontend/public/user.json:
--------------------------------------------------------------------------------
1 | {
2 | "name": "Mina",
3 | "loggedin": false,
4 | "orders": [
5 | {
6 | "id" : 1,
7 | "img" : "img/img-small/strings.png",
8 | "imgalt":"string",
9 | "desc":"A very authentic and beautiful instrument!!",
10 | "price" : 100.0,
11 | "productname" : "Strings",
12 | "days": 32
13 | }, {
14 | "id" : 2,
15 | "img" : "img/img-small/redguitar.jpeg",
16 | "imgalt":"redg",
17 | "desc":"A really cool red guitar that can produce super cool music!!",
18 | "price" : 299.0,
19 | "productname" : "Red Guitar",
20 | "days": 99
21 | },{
22 | "id" : 3,
23 | "img" : "img/img-small/drums.jpg",
24 | "imgalt":"drums",
25 | "desc":"A set of super awesome drums, combined with a guitar, they can product more than amazing music!!",
26 | "price" : 17000.0,
27 | "productname" : "Drums",
28 | "days": 45
29 | }
30 | ]
31 | }
--------------------------------------------------------------------------------
/6-Advanced-Web/Frontend/readme.md:
--------------------------------------------------------------------------------
1 | Front end code
2 |
--------------------------------------------------------------------------------
/6-Advanced-Web/Frontend/src/About.js:
--------------------------------------------------------------------------------
1 | import React from 'react';
2 |
3 |
4 | export default function About(props) {
5 | return (
6 |
7 |
8 |
About the Go Music Store
9 |
Go music is a modern online msucial instruments store
10 |
Explore how you can combine the power of React and Go, to build a fast and beautiful looking online store.
11 |
We will cover how to build this website step by step.
12 |
13 |
);
14 | }
--------------------------------------------------------------------------------
/6-Advanced-Web/Frontend/src/App.js:
--------------------------------------------------------------------------------
1 | import React from 'react';
2 | import CardContainer from './ProductCards';
3 | import { BrowserRouter as Router, Route } from "react-router-dom";
4 | import Nav from './Navigation';
5 | import { SignInModalWindow, BuyModalWindow } from './modalwindows';
6 | import About from './About';
7 | import Orders from './orders';
8 | import cookie from "react-cookie";
9 |
10 |
11 | class App extends React.Component {
12 |
13 | constructor(props) {
14 | super(props);
15 | this.state = {
16 | user: {
17 | loggedin: cookie.load("loggedin"),
18 | name: ""
19 | }
20 | };
21 | }
22 |
23 | handleSignedIn(user) {
24 | this.setState({
25 | user: user
26 | });
27 | }
28 |
29 | componentDidMount() {
30 |
31 | /*
32 | fetch('user.json')
33 | .then(res => res.json())
34 | .then((result) => {
35 | console.log('Fetch...');
36 | this.setState({
37 | user: result
38 | });
39 | });
40 | */
41 | }
42 |
43 | render() {
44 | return (
45 |
46 |
47 |
48 |
49 |
50 | } />
51 | } />
52 | {this.state.user.loggedin ? }/> : null}
53 |
54 |
55 |
56 |
57 |
58 |
59 |
60 | );
61 | }
62 | }
63 |
64 | export default App;
65 |
--------------------------------------------------------------------------------
/6-Advanced-Web/Frontend/src/App.test.js:
--------------------------------------------------------------------------------
1 | import React from 'react';
2 | import ReactDOM from 'react-dom';
3 | import App from './App';
4 |
5 | it('renders without crashing', () => {
6 | const div = document.createElement('div');
7 | ReactDOM.render(, div);
8 | ReactDOM.unmountComponentAtNode(div);
9 | });
10 |
--------------------------------------------------------------------------------
/6-Advanced-Web/Frontend/src/CreditCards.js:
--------------------------------------------------------------------------------
1 | import React from 'react';
2 | import { injectStripe, StripeProvider, Elements, CardElement } from 'react-stripe-elements';
3 |
4 | const INITIALSTATE = "INITIAL", SUCCESSSTATE = "COMPLETE", FAILEDSTATE = "FAILED";
5 |
6 | class CreditCardForm extends React.Component {
7 | constructor(props) {
8 | super(props);
9 | this.handleSubmit = this.handleSubmit.bind(this);
10 | this.handleInputChange = this.handleInputChange.bind(this);
11 | this.state = {
12 | value: '',
13 | status: INITIALSTATE
14 | };
15 | }
16 |
17 | renderCreditCardInformation() {
18 | const style = {
19 | base: {
20 | 'fontSize': '20px',
21 | 'color': '#495057',
22 | 'fontFamily': 'apple-system,BlinkMacSystemFont,"Segoe UI",Roboto,"Helvetica Neue",Arial,sans-serif'
23 | }
24 | };
25 | const usersavedcard =
26 |
27 |
28 |
29 |
30 |
31 |
32 | const remembercardcheck =
33 |
34 |
37 |
;
38 | return (
39 |
40 | {usersavedcard}
41 |
Payment Info
42 |
59 |
60 | );
61 | }
62 |
63 | renderSuccess() {
64 | return (
65 |
66 |
Request Successfull....
67 |
68 |
69 | );
70 | }
71 |
72 | renderFailure() {
73 | return (
74 |
75 |
Credit card information invalid, try again or exit
76 | {this.renderCreditCardInformation()}
77 |
78 | );
79 | }
80 |
81 | async handleSubmit(event) {
82 |
83 | event.preventDefault();
84 | console.log("Handle submit called, with name: " + this.state.value);
85 |
86 | let { token } = await this.props.stripe.createToken({ name: this.state.value });
87 | if (token == null) {
88 | console.log("invalid token");
89 | this.setState({ status: FAILEDSTATE });
90 | return;
91 | }
92 |
93 | let response = await fetch("/charge", {
94 | method: "POST",
95 | headers: { "Content-Type": "text/plain" },
96 | body: JSON.stringify({
97 | token: token.id,
98 | operation: this.props.operation,
99 | })
100 | });
101 | console.log(response.ok);
102 | if (response.ok) {
103 | console.log("Purchase Complete!");
104 | this.setState({ status: SUCCESSSTATE });
105 | }
106 | // document.getElementsByClassName('#modal').modal('hide');
107 | }
108 |
109 | handleInputChange(event) {
110 | this.setState({
111 | value: event.target.value
112 | });
113 | }
114 |
115 | render() {
116 |
117 | let body = null;
118 | switch (this.state.status) {
119 | case SUCCESSSTATE:
120 | body = this.renderSuccess();
121 | break;
122 | case FAILEDSTATE:
123 | body = this.renderFailure();
124 | break;
125 | default:
126 | body = this.renderCreditCardInformation();
127 | }
128 |
129 | return (
130 |
131 | {body}
132 |
133 | );
134 | }
135 | }
136 |
137 | export default function CreditCardInformation(props) {
138 | if (!props.show) {
139 | return null;
140 | }
141 | const CCFormWithStripe = injectStripe(CreditCardForm);
142 | return (
143 |
144 | {props.separator ?
: null}
145 |
146 |
147 |
148 |
149 |
150 |
151 | );
152 | }
153 |
154 |
--------------------------------------------------------------------------------
/6-Advanced-Web/Frontend/src/Navigation.js:
--------------------------------------------------------------------------------
1 | import React from 'react';
2 | import { NavLink } from 'react-router-dom';
3 |
4 |
5 | export default class Navigation extends React.Component {
6 |
7 | buildLoggedInMenu() {
8 | return (
9 |
10 |
11 |
14 |
17 |
18 |
19 | );
20 | }
21 |
22 | render() {
23 | return (
24 |
25 |
43 |
44 | );
45 | }
46 | }
--------------------------------------------------------------------------------
/6-Advanced-Web/Frontend/src/ProductCards.js:
--------------------------------------------------------------------------------
1 | import React from 'react';
2 |
3 | class Card extends React.Component {
4 | render() {
5 | const priceColor = (this.props.promo)? "text-danger" : "text-dark";
6 | return (
7 |
8 |
9 |

10 |
11 |
{this.props.productname}
12 | Price:
{this.props.price}
13 |
{this.props.desc}
14 |
Buy
15 |
16 |
17 |
18 | );
19 | }
20 | }
21 |
22 | export default class CardContainer extends React.Component {
23 | constructor(props) {
24 | super(props);
25 | this.state = {
26 | cards: []
27 | };
28 | }
29 |
30 | componentDidMount() {
31 | fetch(this.props.location)
32 | .then(res => res.json())
33 | .then((result) => {
34 | this.setState({
35 | cards: result
36 | });
37 | });
38 | }
39 |
40 | render() {
41 | const cards = this.state.cards;
42 | let items = cards.map(
43 | card =>
44 | );
45 | return (
46 |
47 |
48 | {items}
49 |
50 |
51 | );
52 | }
53 | }
54 |
--------------------------------------------------------------------------------
/6-Advanced-Web/Frontend/src/index.js:
--------------------------------------------------------------------------------
1 | import React from 'react';
2 | import ReactDOM from 'react-dom';
3 | import App from './App';
4 | import registerServiceWorker from './registerServiceWorker';
5 |
6 | ReactDOM.render(, document.getElementById('root'));
7 | registerServiceWorker();
8 |
--------------------------------------------------------------------------------
/6-Advanced-Web/Frontend/src/orders.js:
--------------------------------------------------------------------------------
1 | import React from 'react';
2 |
3 | function Order(props) {
4 | return (
5 |
6 |
7 |
{props.productname}
8 |
9 |
10 |
11 |

12 |
13 |
14 |
{props.desc}
15 |
16 | Price: {props.price}
17 |
18 |
19 |
20 |
21 |
22 | Purchased {props.days} days ago
23 |
24 |
25 |
26 |
27 | );
28 | }
29 |
30 | export default class OrderContainer extends React.Component {
31 | constructor(props) {
32 | super(props);
33 | this.state = {
34 | orders: []
35 | };
36 | }
37 |
38 | componentDidMount() {
39 | fetch(this.props.location)
40 | .then(res => res.json())
41 | .then((result) => {
42 | this.setState({
43 | orders: result.orders
44 | });
45 | });
46 | }
47 |
48 | render() {
49 |
50 | return (
51 |
52 | {this.state.orders.map(order => )}
53 |
54 | );
55 | }
56 | }
--------------------------------------------------------------------------------
/6-Advanced-Web/Frontend/src/registerServiceWorker.js:
--------------------------------------------------------------------------------
1 | // In production, we register a service worker to serve assets from local cache.
2 |
3 | // This lets the app load faster on subsequent visits in production, and gives
4 | // it offline capabilities. However, it also means that developers (and users)
5 | // will only see deployed updates on the "N+1" visit to a page, since previously
6 | // cached resources are updated in the background.
7 |
8 | // To learn more about the benefits of this model, read https://goo.gl/KwvDNy.
9 | // This link also includes instructions on opting out of this behavior.
10 |
11 | const isLocalhost = Boolean(
12 | window.location.hostname === 'localhost' ||
13 | // [::1] is the IPv6 localhost address.
14 | window.location.hostname === '[::1]' ||
15 | // 127.0.0.1/8 is considered localhost for IPv4.
16 | window.location.hostname.match(
17 | /^127(?:\.(?:25[0-5]|2[0-4][0-9]|[01]?[0-9][0-9]?)){3}$/
18 | )
19 | );
20 |
21 | export default function register() {
22 | if (process.env.NODE_ENV === 'production' && 'serviceWorker' in navigator) {
23 | // The URL constructor is available in all browsers that support SW.
24 | const publicUrl = new URL(process.env.PUBLIC_URL, window.location);
25 | if (publicUrl.origin !== window.location.origin) {
26 | // Our service worker won't work if PUBLIC_URL is on a different origin
27 | // from what our page is served on. This might happen if a CDN is used to
28 | // serve assets; see https://github.com/facebookincubator/create-react-app/issues/2374
29 | return;
30 | }
31 |
32 | window.addEventListener('load', () => {
33 | const swUrl = `${process.env.PUBLIC_URL}/service-worker.js`;
34 |
35 | if (isLocalhost) {
36 | // This is running on localhost. Lets check if a service worker still exists or not.
37 | checkValidServiceWorker(swUrl);
38 |
39 | // Add some additional logging to localhost, pointing developers to the
40 | // service worker/PWA documentation.
41 | navigator.serviceWorker.ready.then(() => {
42 | console.log(
43 | 'This web app is being served cache-first by a service ' +
44 | 'worker. To learn more, visit https://goo.gl/SC7cgQ'
45 | );
46 | });
47 | } else {
48 | // Is not local host. Just register service worker
49 | registerValidSW(swUrl);
50 | }
51 | });
52 | }
53 | }
54 |
55 | function registerValidSW(swUrl) {
56 | navigator.serviceWorker
57 | .register(swUrl)
58 | .then(registration => {
59 | registration.onupdatefound = () => {
60 | const installingWorker = registration.installing;
61 | installingWorker.onstatechange = () => {
62 | if (installingWorker.state === 'installed') {
63 | if (navigator.serviceWorker.controller) {
64 | // At this point, the old content will have been purged and
65 | // the fresh content will have been added to the cache.
66 | // It's the perfect time to display a "New content is
67 | // available; please refresh." message in your web app.
68 | console.log('New content is available; please refresh.');
69 | } else {
70 | // At this point, everything has been precached.
71 | // It's the perfect time to display a
72 | // "Content is cached for offline use." message.
73 | console.log('Content is cached for offline use.');
74 | }
75 | }
76 | };
77 | };
78 | })
79 | .catch(error => {
80 | console.error('Error during service worker registration:', error);
81 | });
82 | }
83 |
84 | function checkValidServiceWorker(swUrl) {
85 | // Check if the service worker can be found. If it can't reload the page.
86 | fetch(swUrl)
87 | .then(response => {
88 | // Ensure service worker exists, and that we really are getting a JS file.
89 | if (
90 | response.status === 404 ||
91 | response.headers.get('content-type').indexOf('javascript') === -1
92 | ) {
93 | // No service worker found. Probably a different app. Reload the page.
94 | navigator.serviceWorker.ready.then(registration => {
95 | registration.unregister().then(() => {
96 | window.location.reload();
97 | });
98 | });
99 | } else {
100 | // Service worker found. Proceed as normal.
101 | registerValidSW(swUrl);
102 | }
103 | })
104 | .catch(() => {
105 | console.log(
106 | 'No internet connection found. App is running in offline mode.'
107 | );
108 | });
109 | }
110 |
111 | export function unregister() {
112 | if ('serviceWorker' in navigator) {
113 | navigator.serviceWorker.ready.then(registration => {
114 | registration.unregister();
115 | });
116 | }
117 | }
118 |
--------------------------------------------------------------------------------
/6-Advanced-Web/backend/src/dblayer/dblayer.go:
--------------------------------------------------------------------------------
1 | package dblayer
2 |
3 | import (
4 | "github.com/minaandrawos/Hands-On-Full-Stack-Development-with-Go/6-ADVANCED-WEB/backend/src/models"
5 | )
6 |
7 | type DBLayer interface {
8 | GetAllProducts() ([]models.Product, error)
9 | GetPromos() ([]models.Product, error)
10 | GetCustomerByName(string, string) (models.Customer, error)
11 | GetCustomerByID(int) (models.Customer, error)
12 | GetProduct(uint) (models.Product, error)
13 | AddUser(models.Customer) error
14 | SignInUser(models.Customer) error
15 | SignOutUserById(int) error
16 | GetCustomerOrdersByID(int) ([]models.Order, error)
17 | }
18 |
--------------------------------------------------------------------------------
/6-Advanced-Web/backend/src/dblayer/orm.go:
--------------------------------------------------------------------------------
1 | package dblayer
2 |
3 | import (
4 | "github.com/jinzhu/gorm"
5 | "github.com/minaandrawos/Hands-On-Full-Stack-Development-with-Go/6-ADVANCED-WEB/backend/src/models"
6 | )
7 |
8 | //get products
9 | //get promos
10 | //post user sign in
11 | //get user orders
12 | //post user sign out
13 | //post purchase charge
14 |
15 | type DBORM struct {
16 | *gorm.DB
17 | }
18 |
19 | func NewORM(dbname, con string) (*DBORM, error) {
20 | db, err := gorm.Open(dbname, con)
21 | return &DBORM{
22 | DB: db,
23 | }, err
24 | }
25 |
26 | func (db *DBORM) GetAllProducts() ([]models.Product, error) {
27 | return nil, nil
28 | }
29 |
30 | func (db *DBORM) GetPromos() ([]models.Product, error) {
31 | return nil, nil
32 | }
33 |
34 | func (db *DBORM) GetCustomerByName(string, string) (models.Customer, error) {
35 | return models.Customer{}, nil
36 | }
37 |
38 | func (db *DBORM) GetCustomerByID(int) (models.Customer, error) {
39 | return models.Customer{}, nil
40 | }
41 |
42 | func (db *DBORM) GetProduct(uint) (models.Product, error) {
43 | return models.Product{}, nil
44 | }
45 |
46 | func (db *DBORM) AddUser(models.Customer) error {
47 | return nil
48 | }
49 |
50 | func (db *DBORM) SignInUser(models.Customer) error {
51 | return nil
52 | }
53 |
54 | func (db *DBORM) SignOutUserById(int) error {
55 | return nil
56 | }
57 |
58 | func (db *DBORM) GetCustomerOrdersByID(int) ([]models.Order, error) {
59 | return nil, nil
60 | }
61 |
--------------------------------------------------------------------------------
/6-Advanced-Web/backend/src/main.go:
--------------------------------------------------------------------------------
1 | package main
2 |
3 | import (
4 | "log"
5 |
6 | "github.com/minaandrawos/Hands-On-Full-Stack-Development-with-Go/6-Advanced-Web/backend/src/rest"
7 | )
8 |
9 | func main() {
10 | log.Println("Main log....")
11 | log.Fatal(rest.RunAPI(":8000"))
12 | }
13 |
--------------------------------------------------------------------------------
/6-Advanced-Web/backend/src/models/models.go:
--------------------------------------------------------------------------------
1 | package models
2 |
3 | import (
4 | "time"
5 |
6 | "github.com/jinzhu/gorm"
7 | )
8 |
9 | type Product struct {
10 | gorm.Model
11 | Image string `json:"img"`
12 | ImagAlt string `json:"imgalt" gorm:"column:imgalt"`
13 | Price float64 `json:"price"`
14 | Promotion float64 `json:"promotion"`
15 | PoructName string `gorm:"column:productname" json:"productname"`
16 | Description string
17 | }
18 |
19 | func (Product) TableName() string {
20 | return "products"
21 | }
22 |
23 | type Customer struct {
24 | gorm.Model
25 | FirstName string `gorm:"column:firstname" json:"firstname"`
26 | LastName string `gorm:"column:lastname" json:"lastname"`
27 | Email string `gorm:"column:email" json:"email"`
28 | Pass string `json:"password"`
29 | CCToken string `gorm:"column:cctoken" json:"cctoken"`
30 | LoggedIn bool `gorm:"column:loggedin" json:"loggedin"`
31 | }
32 |
33 | func (Customer) TableName() string {
34 | return "customers"
35 | }
36 |
37 | type Order struct {
38 | gorm.Model
39 | Product
40 | Customer
41 | CustomerID int `gorm:"column:customer_id"`
42 | ProductID int `gorm:"column:product_id"`
43 | Price float64 `gorm:"column:price" json:"sell_price"`
44 | PurchaseDate time.Time `gorm:"column:purchase_date" json:"purchase_date"`
45 | }
46 |
47 | func (Order) TableName() string {
48 | return "orders"
49 | }
50 |
--------------------------------------------------------------------------------
/6-Advanced-Web/backend/src/rest/handler.go:
--------------------------------------------------------------------------------
1 | package rest
2 |
3 | import (
4 | "fmt"
5 | "log"
6 | "net/http"
7 | "strconv"
8 |
9 | "github.com/gin-gonic/gin"
10 | "github.com/minaandrawos/Hands-On-Full-Stack-Development-with-Go/6-ADVANCED-WEB/backend/src/dblayer"
11 | "github.com/minaandrawos/Hands-On-Full-Stack-Development-with-Go/6-ADVANCED-WEB/backend/src/models"
12 | )
13 |
14 | type Handler struct {
15 | db dblayer.DBLayer
16 | }
17 |
18 | func NewHandler() (*Handler, error) {
19 | return new(Handler), nil
20 | }
21 |
22 | func (h *Handler) GetMainPage(c *gin.Context) {
23 | log.Println("Main page....")
24 | fmt.Fprintf(c.Writer, "Main page for secure API!!")
25 | }
26 | func (h *Handler) GetProducts(c *gin.Context) {
27 | if h.db == nil {
28 | return
29 | }
30 | products, err := h.db.GetAllProducts()
31 | if err != nil {
32 | c.JSON(http.StatusInternalServerError, gin.H{"error": err.Error()})
33 | return
34 | }
35 | c.JSON(http.StatusOK, products)
36 | }
37 |
38 | func (h *Handler) GetPromos(c *gin.Context) {
39 | if h.db == nil {
40 | return
41 | }
42 | promos, err := h.db.GetPromos()
43 | if err != nil {
44 | c.JSON(http.StatusInternalServerError, gin.H{"error": err.Error()})
45 | return
46 | }
47 | c.JSON(http.StatusOK, promos)
48 | }
49 |
50 | func (h *Handler) AddUser(c *gin.Context) {
51 | if h.db == nil {
52 | return
53 | }
54 | var customer models.Customer
55 | err := c.ShouldBindJSON(&customer)
56 | if err != nil {
57 | c.JSON(http.StatusBadRequest, gin.H{"error": err.Error()})
58 | return
59 | }
60 | err = h.db.AddUser(customer)
61 | if err != nil {
62 | c.JSON(http.StatusInternalServerError, gin.H{"error": err.Error()})
63 | return
64 | }
65 | }
66 |
67 | func (h *Handler) SignIn(c *gin.Context) {
68 | if h.db == nil {
69 | return
70 | }
71 | var customer models.Customer
72 | err := c.ShouldBindJSON(&customer)
73 | if err != nil {
74 | c.JSON(http.StatusBadRequest, gin.H{"error": err.Error()})
75 | return
76 | }
77 | err = h.db.SignInUser(customer)
78 | if err != nil {
79 | c.JSON(http.StatusInternalServerError, gin.H{"error": err.Error()})
80 | return
81 | }
82 | }
83 |
84 | func (h *Handler) SignOut(c *gin.Context) {
85 | if h.db == nil {
86 | return
87 | }
88 | p := c.Param("id")
89 | id, err := strconv.Atoi(p)
90 | if err != nil {
91 | c.JSON(http.StatusBadRequest, gin.H{"error": err.Error()})
92 | return
93 | }
94 |
95 | err = h.db.SignOutUserById(id)
96 | if err != nil {
97 | c.JSON(http.StatusInternalServerError, gin.H{"error": err.Error()})
98 | return
99 | }
100 | }
101 |
102 | func (h *Handler) GetOrders(c *gin.Context) {
103 | if h.db == nil {
104 | return
105 | }
106 | p := c.Param("id")
107 | id, err := strconv.Atoi(p)
108 | if err != nil {
109 | c.JSON(http.StatusBadRequest, gin.H{"error": err.Error()})
110 | return
111 | }
112 | orders, err := h.db.GetCustomerOrdersByID(id)
113 | if err != nil {
114 | c.JSON(http.StatusInternalServerError, gin.H{"error": err.Error()})
115 | return
116 | }
117 | c.JSON(http.StatusOK, orders)
118 | }
119 |
120 | func (h *Handler) Charge(c *gin.Context) {
121 | if h.db == nil {
122 | return
123 | }
124 | }
125 |
--------------------------------------------------------------------------------
/6-Advanced-Web/backend/src/rest/rest.go:
--------------------------------------------------------------------------------
1 | package rest
2 |
3 | import (
4 | "github.com/gin-gonic/gin"
5 | )
6 |
7 | func RunAPI(address string) error {
8 | //Get gin's default engine
9 | r := gin.Default()
10 | //Define a handler
11 | h, _ := NewHandler()
12 | //load homepage
13 | r.GET("/", h.GetMainPage)
14 | //get products
15 | r.GET("/products", h.GetProducts)
16 | //get promos
17 | r.GET("/promos", h.GetPromos)
18 | /*
19 | //post user sign in
20 | r.POST("/user/signin", h.SignIn)
21 | //post user sign out
22 | r.POST("/user/:id/signout", h.SignOut)
23 | //get user orders
24 | r.GET("/user/:id/orders", h.GetOrders)
25 | //post purchase charge
26 | r.POST("/user/charge", h.Charge)
27 | */
28 |
29 | userGroup := r.Group("/user")
30 | {
31 | userGroup.POST("/:id/signout", h.SignOut)
32 | userGroup.GET("/:id/orders", h.GetOrders)
33 | }
34 |
35 | usersGroup := r.Group("/users")
36 | {
37 | usersGroup.POST("/charge", h.Charge)
38 | usersGroup.POST("/signin", h.SignIn)
39 | usersGroup.POST("", h.AddUser)
40 | }
41 | //return autotls.Run(r, address)
42 | return r.Run(address)
43 | }
44 |
--------------------------------------------------------------------------------
/6-Advanced-Web/readme.md:
--------------------------------------------------------------------------------
1 | Code for chapter 6
2 |
--------------------------------------------------------------------------------
/7-Testing-and-benchmarking/backend/public/img/blackguitar.jpeg:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/minaandrawos/Hands-On-Full-Stack-Development-with-Go/bb0ccde535e21378f8f01aa7b20b66e7bba2a671/7-Testing-and-benchmarking/backend/public/img/blackguitar.jpeg
--------------------------------------------------------------------------------
/7-Testing-and-benchmarking/backend/public/img/coolguitar.jpeg:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/minaandrawos/Hands-On-Full-Stack-Development-with-Go/bb0ccde535e21378f8f01aa7b20b66e7bba2a671/7-Testing-and-benchmarking/backend/public/img/coolguitar.jpeg
--------------------------------------------------------------------------------
/7-Testing-and-benchmarking/backend/public/img/drums.jpg:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/minaandrawos/Hands-On-Full-Stack-Development-with-Go/bb0ccde535e21378f8f01aa7b20b66e7bba2a671/7-Testing-and-benchmarking/backend/public/img/drums.jpg
--------------------------------------------------------------------------------
/7-Testing-and-benchmarking/backend/public/img/flute.jpeg:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/minaandrawos/Hands-On-Full-Stack-Development-with-Go/bb0ccde535e21378f8f01aa7b20b66e7bba2a671/7-Testing-and-benchmarking/backend/public/img/flute.jpeg
--------------------------------------------------------------------------------
/7-Testing-and-benchmarking/backend/public/img/img-small/blackguitar.jpeg:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/minaandrawos/Hands-On-Full-Stack-Development-with-Go/bb0ccde535e21378f8f01aa7b20b66e7bba2a671/7-Testing-and-benchmarking/backend/public/img/img-small/blackguitar.jpeg
--------------------------------------------------------------------------------
/7-Testing-and-benchmarking/backend/public/img/img-small/coolguitar.jpeg:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/minaandrawos/Hands-On-Full-Stack-Development-with-Go/bb0ccde535e21378f8f01aa7b20b66e7bba2a671/7-Testing-and-benchmarking/backend/public/img/img-small/coolguitar.jpeg
--------------------------------------------------------------------------------
/7-Testing-and-benchmarking/backend/public/img/img-small/drums.jpg:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/minaandrawos/Hands-On-Full-Stack-Development-with-Go/bb0ccde535e21378f8f01aa7b20b66e7bba2a671/7-Testing-and-benchmarking/backend/public/img/img-small/drums.jpg
--------------------------------------------------------------------------------
/7-Testing-and-benchmarking/backend/public/img/img-small/flute.jpeg:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/minaandrawos/Hands-On-Full-Stack-Development-with-Go/bb0ccde535e21378f8f01aa7b20b66e7bba2a671/7-Testing-and-benchmarking/backend/public/img/img-small/flute.jpeg
--------------------------------------------------------------------------------
/7-Testing-and-benchmarking/backend/public/img/img-small/redguitar.jpeg:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/minaandrawos/Hands-On-Full-Stack-Development-with-Go/bb0ccde535e21378f8f01aa7b20b66e7bba2a671/7-Testing-and-benchmarking/backend/public/img/img-small/redguitar.jpeg
--------------------------------------------------------------------------------
/7-Testing-and-benchmarking/backend/public/img/img-small/saxophone.jpeg:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/minaandrawos/Hands-On-Full-Stack-Development-with-Go/bb0ccde535e21378f8f01aa7b20b66e7bba2a671/7-Testing-and-benchmarking/backend/public/img/img-small/saxophone.jpeg
--------------------------------------------------------------------------------
/7-Testing-and-benchmarking/backend/public/img/img-small/strings.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/minaandrawos/Hands-On-Full-Stack-Development-with-Go/bb0ccde535e21378f8f01aa7b20b66e7bba2a671/7-Testing-and-benchmarking/backend/public/img/img-small/strings.png
--------------------------------------------------------------------------------
/7-Testing-and-benchmarking/backend/public/img/mics.jpeg:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/minaandrawos/Hands-On-Full-Stack-Development-with-Go/bb0ccde535e21378f8f01aa7b20b66e7bba2a671/7-Testing-and-benchmarking/backend/public/img/mics.jpeg
--------------------------------------------------------------------------------
/7-Testing-and-benchmarking/backend/public/img/redguitar.jpeg:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/minaandrawos/Hands-On-Full-Stack-Development-with-Go/bb0ccde535e21378f8f01aa7b20b66e7bba2a671/7-Testing-and-benchmarking/backend/public/img/redguitar.jpeg
--------------------------------------------------------------------------------
/7-Testing-and-benchmarking/backend/public/img/saxophone.jpeg:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/minaandrawos/Hands-On-Full-Stack-Development-with-Go/bb0ccde535e21378f8f01aa7b20b66e7bba2a671/7-Testing-and-benchmarking/backend/public/img/saxophone.jpeg
--------------------------------------------------------------------------------
/7-Testing-and-benchmarking/backend/public/img/strings.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/minaandrawos/Hands-On-Full-Stack-Development-with-Go/bb0ccde535e21378f8f01aa7b20b66e7bba2a671/7-Testing-and-benchmarking/backend/public/img/strings.png
--------------------------------------------------------------------------------
/7-Testing-and-benchmarking/backend/public/img/sunnyguitar.jpeg:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/minaandrawos/Hands-On-Full-Stack-Development-with-Go/bb0ccde535e21378f8f01aa7b20b66e7bba2a671/7-Testing-and-benchmarking/backend/public/img/sunnyguitar.jpeg
--------------------------------------------------------------------------------
/7-Testing-and-benchmarking/backend/src/dblayer/dblayer.go:
--------------------------------------------------------------------------------
1 | package dblayer
2 |
3 | import (
4 | "errors"
5 |
6 | "github.com/minaandrawos/Hands-On-Full-Stack-Development-with-Go/7-Testing-and-benchmarking/backend/src/models"
7 | )
8 |
9 | type DBLayer interface {
10 | GetAllProducts() ([]models.Product, error)
11 | GetPromos() ([]models.Product, error)
12 | GetCustomerByName(string, string) (models.Customer, error)
13 | GetCustomerByID(int) (models.Customer, error)
14 | GetProduct(int) (models.Product, error)
15 | AddUser(models.Customer) (models.Customer, error)
16 | SignInUser(username, password string) (models.Customer, error)
17 | SignOutUserById(int) error
18 | GetCustomerOrdersByID(int) ([]models.Order, error)
19 | AddOrder(models.Order) error
20 | GetCreditCardCID(int) (string, error)
21 | SaveCreditCardForCustomer(int, string) error
22 | }
23 |
24 | /*
25 | Passwords:
26 | mal:123
27 | john:1111
28 | jayne:123
29 | River:abc
30 | */
31 |
32 | var ErrINVALIDPASSWORD = errors.New("Invalid password")
33 |
--------------------------------------------------------------------------------
/7-Testing-and-benchmarking/backend/src/dblayer/orm.go:
--------------------------------------------------------------------------------
1 | package dblayer
2 |
3 | import (
4 | "errors"
5 |
6 | _ "github.com/go-sql-driver/mysql"
7 | "github.com/jinzhu/gorm"
8 | "github.com/minaandrawos/Hands-On-Full-Stack-Development-with-Go/7-Testing-and-benchmarking/backend/src/models"
9 | "golang.org/x/crypto/bcrypt"
10 | )
11 |
12 | //get products
13 | //get promos
14 | //post user sign in
15 | //get user orders
16 | //post user sign out
17 | //post purchase charge
18 |
19 | type DBORM struct {
20 | *gorm.DB
21 | }
22 |
23 | func NewORM(dbname, con string) (*DBORM, error) {
24 | db, err := gorm.Open(dbname, con+"?parseTime=true")
25 | return &DBORM{
26 | DB: db,
27 | }, err
28 | }
29 |
30 | func (db *DBORM) GetAllProducts() (products []models.Product, err error) {
31 | return products, db.Find(&products).Error
32 | }
33 |
34 | func (db *DBORM) GetPromos() (products []models.Product, err error) {
35 | return products, db.Where("promotion IS NOT NULL").Find(&products).Error
36 |
37 | }
38 |
39 | func (db *DBORM) GetCustomerByName(firstname string, lastname string) (customer models.Customer, err error) {
40 | return customer, db.Where(&models.Customer{FirstName: firstname, LastName: lastname}).Find(&customer).Error
41 | }
42 |
43 | func (db *DBORM) GetCustomerByID(id int) (customer models.Customer, err error) {
44 | return customer, db.First(&customer, id).Error
45 | }
46 |
47 | func (db *DBORM) GetProduct(id int) (product models.Product, error error) {
48 | return product, db.First(&product, id).Error
49 | }
50 |
51 | func (db *DBORM) AddUser(customer models.Customer) (models.Customer, error) {
52 | //pass received password by reference so that we can change it to it's hashed version
53 | hashPassword(&customer.Pass)
54 | customer.LoggedIn = true
55 | return customer, db.Create(&customer).Error
56 | }
57 |
58 | func hashPassword(s *string) error {
59 | if s == nil {
60 | return errors.New("Reference provided for hashing password is nil")
61 | }
62 | //converd password string to byte slice
63 | sBytes := []byte(*s)
64 | //Obtain hashed password
65 | hashedBytes, err := bcrypt.GenerateFromPassword(sBytes, bcrypt.DefaultCost)
66 | if err != nil {
67 | return err
68 | }
69 | //update password string with the hashed version
70 | *s = string(hashedBytes[:])
71 | return nil
72 | }
73 |
74 | func (db *DBORM) SignInUser(email, pass string) (customer models.Customer, err error) {
75 |
76 | //Obtain a *gorm.DB object representing our customer's row
77 | result := db.Table("Customers").Where(&models.Customer{Email: email})
78 | err = result.First(&customer).Error
79 | if err != nil {
80 | return customer, err
81 | }
82 |
83 | if !checkPassword(customer.Pass, pass) {
84 | return customer, ErrINVALIDPASSWORD
85 | }
86 | //update the loggedin field
87 | err = result.Update("loggedin", 1).Error
88 | if err != nil {
89 | return customer, err
90 | }
91 | //return the new customer row
92 | return customer, result.Find(&customer).Error
93 | }
94 |
95 | func checkPassword(existingHash, incomingPass string) bool {
96 | //this method will return an error if the hash does not match the provided password string
97 | return bcrypt.CompareHashAndPassword([]byte(existingHash), []byte(incomingPass)) == nil
98 | }
99 |
100 | func (db *DBORM) SignOutUserById(id int) error {
101 | customer := models.Customer{
102 | Model: gorm.Model{
103 | ID: uint(id),
104 | },
105 | }
106 | return db.Table("Customers").Where(&customer).Update("loggedin", 0).Error
107 | }
108 |
109 | func (db *DBORM) GetCustomerOrdersByID(id int) (orders []models.Order, err error) {
110 | return orders, db.Table("orders").Select("*").Joins("join customers on customers.id = customer_id").Joins("join products on products.id = product_id").Where("customer_id=?", id).Scan(&orders).Error //db.Find(&orders, models.Order{CustomerID: id}).Error
111 | }
112 |
113 | func (db *DBORM) AddOrder(order models.Order) error {
114 |
115 | return db.Create(&order).Error
116 | }
117 |
118 | func (db *DBORM) GetCreditCardCID(id int) (string, error) {
119 |
120 | cusomterWithCCID := struct {
121 | models.Customer
122 | CCID string `gorm:"column:cc_customerid"`
123 | }{}
124 |
125 | return cusomterWithCCID.CCID, db.First(&cusomterWithCCID, id).Error
126 | }
127 |
128 | func (db *DBORM) SaveCreditCardForCustomer(id int, ccid string) error {
129 | result := db.Table("customers").Where("id=?", id)
130 | return result.Update("cc_customerid", ccid).Error
131 | }
132 |
--------------------------------------------------------------------------------
/7-Testing-and-benchmarking/backend/src/dblayer/orm_test.go:
--------------------------------------------------------------------------------
1 | package dblayer
2 |
3 | import "testing"
4 |
5 | func BenchmarkHashPassword(b *testing.B) {
6 | text := "A String to be Hashed"
7 | for i := 0; i < b.N; i++ {
8 | hashPassword(&text)
9 | }
10 | }
11 |
--------------------------------------------------------------------------------
/7-Testing-and-benchmarking/backend/src/main.go:
--------------------------------------------------------------------------------
1 | package main
2 |
3 | import (
4 | "log"
5 |
6 | "github.com/minaandrawos/Hands-On-Full-Stack-Development-with-Go/7-Testing-and-benchmarking/backend/src/rest"
7 | )
8 |
9 | func main() {
10 | log.Println("Main log....")
11 | log.Fatal(rest.RunAPI("127.0.0.1:8000"))
12 | }
13 |
--------------------------------------------------------------------------------
/7-Testing-and-benchmarking/backend/src/mockdata.json:
--------------------------------------------------------------------------------
1 | {
2 | "name": "Mina",
3 | "loggedin": true,
4 | "orders": [
5 | {
6 | "id": 1,
7 | "img": "img/img-small/strings.png",
8 | "imgalt": "string",
9 | "desc": "A very authentic and beautiful instrument!!",
10 | "price": 100.0,
11 | "productname": "Strings",
12 | "days": 32
13 | }, {
14 | "id": 2,
15 | "img": "img/img-small/redguitar.jpeg",
16 | "imgalt": "redg",
17 | "desc": "A really cool red guitar that can produce super cool music!!",
18 | "price": 299.0,
19 | "productname": "Red Guitar",
20 | "days": 99
21 | }, {
22 | "id": 3,
23 | "img": "img/img-small/drums.jpg",
24 | "imgalt": "drums",
25 | "desc": "A set of super awesome drums, combined with a guitar, they can product more than amazing music!!",
26 | "price": 17000.0,
27 | "productname": "Drums",
28 | "days": 45
29 | }
30 | ]
31 | }
--------------------------------------------------------------------------------
/7-Testing-and-benchmarking/backend/src/models/models.go:
--------------------------------------------------------------------------------
1 | package models
2 |
3 | import (
4 | "time"
5 |
6 | "github.com/jinzhu/gorm"
7 | )
8 |
9 | type Product struct {
10 | gorm.Model
11 | Image string `json:"img"`
12 | SmallImage string `gorm:"column:smallimg" json:"small_img"`
13 | ImagAlt string `json:"imgalt" gorm:"column:imgalt"`
14 | Price float64 `json:"price"`
15 | Promotion float64 `json:"promotion"` //sql.NullFloat64
16 | PoructName string `gorm:"column:productname" json:"productname"`
17 | Description string
18 | }
19 |
20 | func (Product) TableName() string {
21 | return "products"
22 | }
23 |
24 | type Customer struct {
25 | gorm.Model
26 | Name string `json:"name" sql:"-"`
27 | FirstName string `gorm:"column:firstname" json:"firstname"`
28 | LastName string `gorm:"column:lastname" json:"lastname"`
29 | Email string `gorm:"column:email" json:"email"`
30 | Pass string `json:"password"`
31 | LoggedIn bool `gorm:"column:loggedin" json:"loggedin"`
32 | Orders []Order `json:"orders"`
33 | }
34 |
35 | func (Customer) TableName() string {
36 | return "customers"
37 | }
38 |
39 | type Order struct {
40 | gorm.Model
41 | Product `sql:"-"`
42 | Customer `sql:"-"`
43 | CustomerID int `json:"customer_id" gorm:"column:customer_id"`
44 | ProductID int `json:"product_id" gorm:"column:product_id"`
45 | Price float64 `gorm:"column:price" json:"sell_price"`
46 | PurchaseDate time.Time `gorm:"column:purchase_date" json:"purchase_date"`
47 | }
48 |
49 | func (Order) TableName() string {
50 | return "orders"
51 | }
52 |
--------------------------------------------------------------------------------
/7-Testing-and-benchmarking/backend/src/rest/handler.go:
--------------------------------------------------------------------------------
1 | package rest
2 |
3 | import (
4 | "fmt"
5 | "log"
6 | "net/http"
7 | "strconv"
8 |
9 | "github.com/gin-gonic/gin"
10 | "github.com/minaandrawos/Hands-On-Full-Stack-Development-with-Go/7-Testing-and-benchmarking/backend/src/dblayer"
11 | "github.com/minaandrawos/Hands-On-Full-Stack-Development-with-Go/7-Testing-and-benchmarking/backend/src/models"
12 | "github.com/stripe/stripe-go"
13 | "github.com/stripe/stripe-go/charge"
14 | "github.com/stripe/stripe-go/customer"
15 | )
16 |
17 | type HandlerInterface interface {
18 | GetMainPage(c *gin.Context)
19 | GetProducts(c *gin.Context)
20 | GetPromos(c *gin.Context)
21 | AddUser(c *gin.Context)
22 | SignIn(c *gin.Context)
23 | SignOut(c *gin.Context)
24 | GetOrders(c *gin.Context)
25 | Charge(c *gin.Context)
26 | }
27 |
28 | type Handler struct {
29 | db dblayer.DBLayer
30 | }
31 |
32 | func NewHandler() (HandlerInterface, error) {
33 | return NewHandlerWithParams("mysql", "root:root@/gomusic")
34 | }
35 |
36 | func NewHandlerWithParams(dbtype, conn string) (HandlerInterface, error) {
37 | db, err := dblayer.NewORM(dbtype, conn)
38 | if err != nil {
39 | return nil, err
40 | }
41 | return &Handler{
42 | db: db,
43 | }, nil
44 | }
45 |
46 | func NewHandlerWithDB(db dblayer.DBLayer) HandlerInterface {
47 | return &Handler{db: db}
48 | }
49 |
50 | func (h *Handler) GetMainPage(c *gin.Context) {
51 | log.Println("Main page....")
52 | c.String(http.StatusOK, "Main page for secure API!!")
53 | //fmt.Fprintf(c.Writer, "Main page for secure API!!")
54 | }
55 |
56 | func (h *Handler) GetProducts(c *gin.Context) {
57 | if h.db == nil {
58 | c.JSON(http.StatusInternalServerError, gin.H{"error": "server database error"})
59 | return
60 | }
61 | products, err := h.db.GetAllProducts()
62 | if err != nil {
63 | c.JSON(http.StatusInternalServerError, gin.H{"error": err.Error()})
64 | return
65 | }
66 | fmt.Printf("Found %d products\n", len(products))
67 | c.JSON(http.StatusOK, products)
68 | }
69 |
70 | func (h *Handler) GetPromos(c *gin.Context) {
71 | if h.db == nil {
72 | c.JSON(http.StatusInternalServerError, gin.H{"error": "server database error"})
73 | return
74 | }
75 | promos, err := h.db.GetPromos()
76 | if err != nil {
77 | c.JSON(http.StatusInternalServerError, gin.H{"error": err.Error()})
78 | return
79 | }
80 | c.JSON(http.StatusOK, promos)
81 | }
82 |
83 | func (h *Handler) AddUser(c *gin.Context) {
84 | if h.db == nil {
85 | c.JSON(http.StatusInternalServerError, gin.H{"error": "server database error"})
86 | return
87 | }
88 | var customer models.Customer
89 | err := c.ShouldBindJSON(&customer)
90 | if err != nil {
91 | c.JSON(http.StatusBadRequest, gin.H{"error": err.Error()})
92 | return
93 | }
94 | customer, err = h.db.AddUser(customer)
95 | if err != nil {
96 | c.JSON(http.StatusInternalServerError, gin.H{"error": err.Error()})
97 | return
98 | }
99 | c.JSON(http.StatusOK, customer)
100 | }
101 |
102 | func (h *Handler) SignIn(c *gin.Context) {
103 | if h.db == nil {
104 | c.JSON(http.StatusInternalServerError, gin.H{"error": "server database error"})
105 | return
106 | }
107 | var customer models.Customer
108 | err := c.ShouldBindJSON(&customer)
109 | if err != nil {
110 | c.JSON(http.StatusBadRequest, gin.H{"error": err.Error()})
111 | return
112 | }
113 | customer, err = h.db.SignInUser(customer.Email, customer.Pass)
114 | if err != nil {
115 | if err == dblayer.ErrINVALIDPASSWORD {
116 | c.JSON(http.StatusForbidden, gin.H{"error": err.Error()})
117 | return
118 | }
119 | c.JSON(http.StatusInternalServerError, gin.H{"error": err.Error()})
120 | return
121 | }
122 | c.JSON(http.StatusOK, customer)
123 | }
124 |
125 | func (h *Handler) SignOut(c *gin.Context) {
126 | if h.db == nil {
127 | c.JSON(http.StatusInternalServerError, gin.H{"error": "server database error"})
128 | return
129 | }
130 | p := c.Param("id")
131 | id, err := strconv.Atoi(p)
132 | if err != nil {
133 | c.JSON(http.StatusBadRequest, gin.H{"error": err.Error()})
134 | return
135 | }
136 |
137 | err = h.db.SignOutUserById(id)
138 | if err != nil {
139 | c.JSON(http.StatusInternalServerError, gin.H{"error": err.Error()})
140 | return
141 | }
142 | }
143 |
144 | func (h *Handler) GetOrders(c *gin.Context) {
145 | if h.db == nil {
146 | c.JSON(http.StatusInternalServerError, gin.H{"error": "server database error"})
147 | return
148 | }
149 | p := c.Param("id")
150 | id, err := strconv.Atoi(p)
151 | if err != nil {
152 | c.JSON(http.StatusBadRequest, gin.H{"error": err.Error()})
153 | return
154 | }
155 | orders, err := h.db.GetCustomerOrdersByID(id)
156 | if err != nil {
157 | fmt.Println(err)
158 | c.JSON(http.StatusInternalServerError, gin.H{"error": err.Error()})
159 | return
160 | }
161 | c.JSON(http.StatusOK, orders)
162 | }
163 |
164 | func (h *Handler) Charge(c *gin.Context) {
165 | if h.db == nil {
166 | c.JSON(http.StatusInternalServerError, gin.H{"error": "server database error"})
167 | return
168 | }
169 | request := struct {
170 | models.Order
171 | Remember bool `json:"rememberCard"`
172 | UseExisting bool `json:"useExisting"`
173 | Token string `json:"token"`
174 | }{}
175 |
176 | err := c.ShouldBindJSON(&request)
177 | log.Printf("request: %+v \n", request)
178 | if err != nil {
179 | c.JSON(http.StatusBadRequest, request)
180 | return
181 | }
182 | // Set your secret key: remember to change this to your live secret key in production
183 | // Keys can be obtained from: https://dashboard.stripe.com/account/apikeys
184 | // They key below is just for testing
185 | stripe.Key = "sk_test_4eC39HqLyjWDarjtT1zdp7dc"
186 | //test cards available at: https://stripe.com/docs/testing#cards
187 | //setting charge parameters
188 | chargeP := &stripe.ChargeParams{
189 | Amount: stripe.Int64(int64(request.Price)),
190 | Currency: stripe.String("usd"),
191 | Description: stripe.String("GoMusic charge..."),
192 | }
193 | stripeCustomerID := ""
194 | //Either remembercard or use exeisting should be enabled but not both
195 | if request.UseExisting {
196 | //use existing
197 | log.Println("Getting credit card id...")
198 | stripeCustomerID, err = h.db.GetCreditCardCID(request.CustomerID)
199 | if err != nil {
200 | log.Println(err)
201 | c.JSON(http.StatusInternalServerError, gin.H{"error": err.Error()})
202 | return
203 | }
204 | } else {
205 | cp := &stripe.CustomerParams{}
206 | cp.SetSource(request.Token)
207 | customer, err := customer.New(cp)
208 | if err != nil {
209 | c.JSON(http.StatusInternalServerError, gin.H{"error": err.Error()})
210 | return
211 | }
212 | stripeCustomerID = customer.ID
213 | if request.Remember {
214 | //save card!!
215 | err = h.db.SaveCreditCardForCustomer(request.CustomerID, stripeCustomerID)
216 | if err != nil {
217 | c.JSON(http.StatusInternalServerError, gin.H{"error": err.Error()})
218 | return
219 | }
220 | }
221 | }
222 | //we should check if the customer already ordered the same item or not but for simplicity, let's assume it's a new order
223 | chargeP.Customer = stripe.String(stripeCustomerID)
224 | _, err = charge.New(chargeP)
225 | if err != nil {
226 | c.JSON(http.StatusInternalServerError, gin.H{"error": err.Error()})
227 | return
228 | }
229 |
230 | err = h.db.AddOrder(request.Order)
231 | if err != nil {
232 | c.JSON(http.StatusInternalServerError, gin.H{"error": err.Error()})
233 | }
234 | }
235 |
--------------------------------------------------------------------------------
/7-Testing-and-benchmarking/backend/src/rest/handler_test.go:
--------------------------------------------------------------------------------
1 | package rest
2 |
3 | import (
4 | "encoding/json"
5 | "errors"
6 | "net/http"
7 | "net/http/httptest"
8 | "reflect"
9 | "testing"
10 |
11 | "github.com/gin-gonic/gin"
12 | "github.com/minaandrawos/Hands-On-Full-Stack-Development-with-Go/7-Testing-and-benchmarking/backend/src/dblayer"
13 | "github.com/minaandrawos/Hands-On-Full-Stack-Development-with-Go/7-Testing-and-benchmarking/backend/src/models"
14 | )
15 |
16 | func TestHandler_GetProducts(t *testing.T) {
17 | // Switch to test mode so you don't get such noisy output
18 | gin.SetMode(gin.TestMode)
19 | mockdbLayer := dblayer.NewMockDBLayerWithData()
20 | h := NewHandlerWithDB(mockdbLayer)
21 | const productsURL string = "/products"
22 | type errMSG struct {
23 | Error string `json:"error"`
24 | }
25 | tests := []struct {
26 | name string
27 | inErr error
28 | outStatusCode int
29 | expectedRespBody interface{}
30 | }{
31 | {
32 | "getproductsnoerrors",
33 | nil,
34 | http.StatusOK,
35 | mockdbLayer.GetMockProductData(),
36 | },
37 | {
38 | "getproductswitherror",
39 | errors.New("get products error"),
40 | http.StatusInternalServerError,
41 | errMSG{Error: "get products error"},
42 | },
43 | }
44 | for _, tt := range tests {
45 | t.Run(tt.name, func(t *testing.T) {
46 | //set the input error
47 | mockdbLayer.SetError(tt.inErr)
48 | //Create a test request
49 | req := httptest.NewRequest(http.MethodGet, productsURL, nil)
50 | //create an http response recorder
51 | w := httptest.NewRecorder()
52 | //create a fresh gin context and gin engine object from the response recorder
53 | _, engine := gin.CreateTestContext(w)
54 |
55 | //configure the get request
56 | engine.GET(productsURL, h.GetProducts)
57 | //serve the request
58 | engine.ServeHTTP(w, req)
59 |
60 | //test the output
61 | response := w.Result()
62 | if response.StatusCode != tt.outStatusCode {
63 | t.Errorf("Received Status code %d does not match expected status code %d", response.StatusCode, tt.outStatusCode)
64 | }
65 |
66 | var respBody interface{}
67 | if tt.inErr != nil {
68 | var errmsg errMSG
69 | json.NewDecoder(response.Body).Decode(&errmsg)
70 | respBody = errmsg
71 | } else {
72 | products := []models.Product{}
73 | json.NewDecoder(response.Body).Decode(&products)
74 | respBody = products
75 | }
76 |
77 | if !reflect.DeepEqual(respBody, tt.expectedRespBody) {
78 | t.Logf("%+v , %+v", respBody, tt.expectedRespBody)
79 | t.Errorf("Received HTTP response body %+v does not match expected HTTP response Body %+v", respBody, tt.expectedRespBody)
80 | }
81 | })
82 | }
83 | }
84 |
--------------------------------------------------------------------------------
/7-Testing-and-benchmarking/backend/src/rest/rest.go:
--------------------------------------------------------------------------------
1 | package rest
2 |
3 | import (
4 | "fmt" //"github.com/gin-gonic/autotls"
5 |
6 | "github.com/gin-gonic/gin"
7 | )
8 |
9 | func RunAPI(address string) error {
10 | h, err := NewHandler()
11 | if err != nil {
12 | return err
13 | }
14 | return RunAPIWithHandler(address, h)
15 | }
16 |
17 | func RunAPIWithHandler(address string, h HandlerInterface) error {
18 | //Get gin's default engine
19 | r := gin.Default()
20 | //r.Use(MyCustomLogger())
21 | //load homepage
22 | r.GET("/", h.GetMainPage)
23 | //get products
24 | r.GET("/products", h.GetProducts)
25 | //get promos
26 | r.GET("/promos", h.GetPromos)
27 | /*
28 | //post user sign in
29 | r.POST("/user/signin", h.SignIn)
30 | //post user sign out
31 | r.POST("/user/:id/signout", h.SignOut)
32 | //get user orders
33 | r.GET("/user/:id/orders", h.GetOrders)
34 | //post purchase charge
35 | r.POST("/user/charge", h.Charge)
36 | */
37 |
38 | userGroup := r.Group("/user")
39 | {
40 | userGroup.POST("/:id/signout", h.SignOut)
41 | userGroup.GET("/:id/orders", h.GetOrders)
42 | }
43 |
44 | usersGroup := r.Group("/users")
45 | {
46 | usersGroup.POST("/charge", h.Charge)
47 | usersGroup.POST("/signin", h.SignIn)
48 | usersGroup.POST("", h.AddUser)
49 | }
50 |
51 | r.Static("/img", "../public/img")
52 | //return autotls.Run(r, address)
53 | return r.Run(address)
54 | //return r.RunTLS(address, "cert.pem", "key.pem")
55 | }
56 |
57 | func MyCustomLogger() gin.HandlerFunc {
58 | return func(c *gin.Context) {
59 | fmt.Println("************************************")
60 | c.Next()
61 | fmt.Println("************************************")
62 | }
63 | }
64 |
--------------------------------------------------------------------------------
/7-Testing-and-benchmarking/readme.md:
--------------------------------------------------------------------------------
1 | Code for chapter 7
2 |
--------------------------------------------------------------------------------
/9-Isomorphic-Go/README.md:
--------------------------------------------------------------------------------
1 | # Chapter 9
2 |
3 | This repo hosts chapter 9 code: Isomorphic Go
4 |
--------------------------------------------------------------------------------
/9-Isomorphic-Go/browser/main.go:
--------------------------------------------------------------------------------
1 | package main
2 |
3 | import (
4 | "github.com/gopherjs/gopherjs/js"
5 | )
6 |
7 | func main() {
8 | document := js.Global.Get("document")
9 | document.Call("write", "Hello world!!")
10 | }
11 |
--------------------------------------------------------------------------------
/9-Isomorphic-Go/browser/main.js.map:
--------------------------------------------------------------------------------
1 | {"version":3,"file":"main.js","sources":["github.com/gopherjs/gopherjs/js/js.go","runtime.go","runtime/error.go","/github.com/minaandrawos/Hands-On-Full-Stack-Development-with-Go/9-Isomorphic-Go/browser/main.go"],"mappings":";;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;AA+B4C;A;;;;;AAGW;A;;;;;AAGhB;A;;;;;AAGN;A;;;;;AAGQ;A;;;;;AAGc;A;;;;;AAGY;A;;;;;AAGX;A;;;;;AAGH;A;;;;;AAGrB;A;;;;;AAGI;A;;;;;AAGN;A;;;;;AAGI;A;;;;;AAGE;A;;;;;AAGA;A;;;;;AAGQ;A;;;;;AAGP;A;;;;;AASnC;A;;;;;AAKA;A;;;;AAwEA;AACA;A;;;;;;;;A;;;;;;;;;;;;;;;;;;;;;;A;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;AC/IA;A;;;;;AAGA;A;;;;AAIA;AACA;AACA;AACA;AAEI;AACJ;AACA;A;;;AA2LA;A;;A;;;;;AClMA;A;AAEC;A;AAED;A;AAEC;A;AAED;A;AAEC;A;A;AAIE;A;AAEA;A;A;AAGF;A;AAED;A;;;;;A;;;;;AAUA;A;;;;;;;;;;A;A;A;A;;;;;;;;;;;ACrDA;AACA;A;;;;A;A;A;A;A"}
2 |
--------------------------------------------------------------------------------
/9-Isomorphic-Go/nodeprojects/calc/addsub.js:
--------------------------------------------------------------------------------
1 |
2 | function add(i,j){
3 | return i+j;
4 | }
5 |
6 | function sub(i,j){
7 | return i-j;
8 | }
9 |
10 | function formatnumbers(Obj){
11 | return "First number: " + Obj.first + " second number: " + Obj.second;
12 | }
13 |
14 | module.exports={
15 | Add: add,
16 | Sub: sub,
17 | FormatNumbers: formatnumbers
18 | }
--------------------------------------------------------------------------------
/9-Isomorphic-Go/nodeprojects/calc/addsubgo.go:
--------------------------------------------------------------------------------
1 | package main
2 |
3 | import (
4 | "fmt"
5 |
6 | "github.com/gopherjs/gopherjs/js"
7 | )
8 |
9 | func main() {
10 | exports := js.Module.Get("exports")
11 | exports.Set("Add", Add)
12 | exports.Set("Sub", Sub)
13 | //Make the FormatNumbers function exportable as a Javascript module
14 | exports.Set("FormatNumbers", FormatNumbers)
15 | }
16 |
17 | func Add(i, j int) int {
18 | return i + j
19 | }
20 |
21 | func Sub(i, j int) int {
22 | return i - j
23 | }
24 |
25 | type Obj struct {
26 | *js.Object //For any struct type expected to be processed by Gopherjs, we need to embed the *js.Object type to it
27 | First int `js:"first"` //struct tag represents the field name in Javascript
28 | Second int `js:"second"` //struct tag represents the field name in Javascript
29 | }
30 |
31 | func FormatNumbers(o Obj) string {
32 | return fmt.Sprintf("First number: %d second number: %d", o.First, o.Second)
33 | }
34 |
--------------------------------------------------------------------------------
/9-Isomorphic-Go/nodeprojects/calc/calc.js:
--------------------------------------------------------------------------------
1 | var calc = require('./addsubgo.js');
2 |
3 | //Call Add() then save result in the add variable
4 | var add = calc.Add(2,3);
5 |
6 | //Call Sub() then save result in the sub variable
7 | var sub = calc.Sub(5,2);
8 |
9 | //Call FormatWords then save the result in the fw variable
10 | var fw = calc.FormatNumbers({
11 | first: 10,
12 | second: 20,
13 | });
14 |
15 | console.log(add);
16 | console.log(sub);
17 | console.log(fw);
18 |
19 |
--------------------------------------------------------------------------------
/9-Isomorphic-Go/nodeprojects/gopackages/gopackages.go:
--------------------------------------------------------------------------------
1 | package main
2 |
3 | import (
4 | "encoding/csv"
5 | "fmt"
6 | "strings"
7 | )
8 |
9 | func main() {
10 | data := "item11,item12,item13\nitem21,item22,item23\nitem31,item32,item33\n"
11 | csvReader := csv.NewReader(strings.NewReader(data))
12 | i := 0
13 | for {
14 | row, err := csvReader.Read()
15 | if err != nil {
16 | break
17 | }
18 | i++
19 | fmt.Println("Line", i, "of CSV data:", row)
20 | }
21 | }
22 |
--------------------------------------------------------------------------------
/9-Isomorphic-Go/nodeprojects/jsonbeautify/jsonbeautify.go:
--------------------------------------------------------------------------------
1 | package main
2 |
3 | import (
4 | "fmt"
5 |
6 | "github.com/gopherjs/gopherjs/js"
7 | )
8 |
9 | func main() {
10 | type MyType struct {
11 | Name string
12 | Projects []string
13 | }
14 | value := MyType{Name: "mina", Projects: []string{"GopherJS", "ReactJS"}}
15 | prettyjson := js.Global.Call("require", "prettyjson")
16 | fmt.Println(prettyjson.Call("render", value))
17 | }
18 |
--------------------------------------------------------------------------------
/9-Isomorphic-Go/nodeprojects/jsonbeautify/package-lock.json:
--------------------------------------------------------------------------------
1 | {
2 | "name": "jsonbeautify",
3 | "version": "1.0.0",
4 | "lockfileVersion": 1,
5 | "requires": true,
6 | "dependencies": {
7 | "colors": {
8 | "version": "1.3.2",
9 | "resolved": "https://registry.npmjs.org/colors/-/colors-1.3.2.tgz",
10 | "integrity": "sha512-rhP0JSBGYvpcNQj4s5AdShMeE5ahMop96cTeDl/v9qQQm2fYClE2QXZRi8wLzc+GmXSxdIqqbOIAhyObEXDbfQ=="
11 | },
12 | "minimist": {
13 | "version": "1.2.0",
14 | "resolved": "http://registry.npmjs.org/minimist/-/minimist-1.2.0.tgz",
15 | "integrity": "sha1-o1AIsg9BOD7sH7kU9M1d95omQoQ="
16 | },
17 | "prettyjson": {
18 | "version": "1.2.1",
19 | "resolved": "https://registry.npmjs.org/prettyjson/-/prettyjson-1.2.1.tgz",
20 | "integrity": "sha1-/P+rQdGcq0365eV15kJGYZsS0ok=",
21 | "requires": {
22 | "colors": "1.3.2",
23 | "minimist": "1.2.0"
24 | }
25 | }
26 | }
27 | }
28 |
--------------------------------------------------------------------------------
/9-Isomorphic-Go/nodeprojects/jsonbeautify/package.json:
--------------------------------------------------------------------------------
1 | {
2 | "name": "jsonbeautify",
3 | "version": "1.0.0",
4 | "description": "",
5 | "main": "index.js",
6 | "scripts": {
7 | "test": "echo \"Error: no test specified\" && exit 1"
8 | },
9 | "author": "",
10 | "license": "ISC",
11 | "dependencies": {
12 | "prettyjson": "^1.2.1"
13 | }
14 | }
15 |
--------------------------------------------------------------------------------
/9-Isomorphic-Go/nodeprojects/musicalinstruments/mi.go:
--------------------------------------------------------------------------------
1 | package main
2 |
3 | import (
4 | "github.com/gopherjs/gopherjs/js"
5 | )
6 |
7 | func main() {
8 | js.Global.Set("musicalInstruments", map[string]interface{}{
9 | "New": New,
10 | })
11 |
12 | }
13 |
14 | type MI struct {
15 | MIType string
16 | Price float64
17 | Color string
18 | Age int
19 | }
20 |
21 | func (mi *MI) SetMIType(s string) {
22 | mi.MIType = s
23 | }
24 |
25 | func (mi *MI) GetMIType() string {
26 | return mi.MIType
27 | }
28 |
29 | func (mi *MI) SetPrice(f float64) {
30 | mi.Price = f
31 | }
32 |
33 | func (mi *MI) GetPrice() float64 {
34 | return mi.Price
35 | }
36 |
37 | func (mi *MI) SetColor(c string) {
38 | mi.Color = c
39 | }
40 |
41 | func (mi *MI) GetColor() string {
42 | return mi.Color
43 | }
44 |
45 | func (mi *MI) SetAge(a int) {
46 | mi.Age = a
47 | }
48 |
49 | func (mi *MI) GetAge() int {
50 | return mi.Age
51 | }
52 |
53 | func New() *js.Object {
54 | return js.MakeWrapper(&MI{})
55 | }
56 |
--------------------------------------------------------------------------------
/9-Isomorphic-Go/nodeprojects/musicalinstruments/mi.js.map:
--------------------------------------------------------------------------------
1 | {"version":3,"file":"mi.js","sources":["github.com/gopherjs/gopherjs/js/js.go","runtime.go","runtime/error.go","/github.com/minaandrawos/Hands-On-Full-Stack-Development-with-Go/9-Isomorphic-Go/node/musicalinstruments/mi.go"],"mappings":";;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;AA+B4C;A;;;;;AAGW;A;;;;;AAGhB;A;;;;;AAGN;A;;;;;AAGQ;A;;;;;AAGc;A;;;;;AAGY;A;;;;;AAGX;A;;;;;AAGH;A;;;;;AAGrB;A;;;;;AAGI;A;;;;;AAGN;A;;;;;AAGI;A;;;;;AAGE;A;;;;;AAGA;A;;;;;AAGQ;A;;;;;AAGP;A;;;;;AASnC;A;;;;;AAKA;A;;;;AAwCA;AACA;AACA;AACA;AACI;;;;AACH;A;AADiC;A;A;AAKjC;;AACC;A;AANgC;A;AASlC;A;;;;AAmBA;AACA;A;;;;;;;;A;;;;;;;;;;;;;;;;;;;;;;A;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;AC/IA;A;;;;;AAGA;A;;;;AAIA;AACA;AACA;AACA;AAEI;AACJ;AACA;A;;;AA2LA;A;;A;;;;;AClMA;A;AAEC;A;AAED;A;AAEC;A;AAED;A;AAEC;A;A;AAIE;A;AAEA;A;A;AAGF;A;AAED;A;;;;;A;;;;;AAUA;A;;;;;;;;;;A;A;A;A;;;;;;;;;;;;;;;;;;;;;;;;;;;;ACtDA;A;;;;AAcA;A;;;;;AAIA;A;;;;;AAIA;A;;;;;AAIA;A;;;;;AAIA;A;;;;;AAIA;A;;;;;AAIA;A;;;;;AAIA;A;;;AAIA;A;;;;;;;A;A;A;A;A"}
2 |
--------------------------------------------------------------------------------
/9-Isomorphic-Go/nodeprojects/musicalinstruments/usemi.js:
--------------------------------------------------------------------------------
1 | require("./mi.js");
2 |
3 | var mi = musicalInstruments.New();
4 |
5 | mi.SetAge(20);
6 |
7 | console.log(mi.GetAge());
--------------------------------------------------------------------------------
/9-Isomorphic-Go/reactproject/app.go:
--------------------------------------------------------------------------------
1 | // Template generated by reactGen
2 |
3 | package main
4 |
5 | import (
6 | "github.com/minaandrawos/Hands-On-Full-Stack-Development-with-Go/9-Isomorphic-Go/reactproject/hello_message"
7 | "myitcv.io/react"
8 | )
9 |
10 | type AppDef struct {
11 | react.ComponentDef
12 | }
13 |
14 | func App() *AppElem {
15 | return buildAppElem()
16 | }
17 |
18 | func (a AppDef) Render() react.Element {
19 | /*
20 | Return a react div that hosts a title, as well as our custom hello message component
21 | */
22 | return react.Div(nil,
23 | react.P(nil,
24 | react.S("This is my first GopherJS React App."),
25 | ),
26 | react.H1(nil,
27 | hellomessage.HelloMessage(hellomessage.HelloMessageProps{Message: "Hello"}),
28 | ),
29 | )
30 | }
31 |
--------------------------------------------------------------------------------
/9-Isomorphic-Go/reactproject/gen_App_reactGen.go:
--------------------------------------------------------------------------------
1 | // Code generated by reactGen. DO NOT EDIT.
2 |
3 | package main
4 |
5 | import "myitcv.io/react"
6 |
7 | type AppElem struct {
8 | react.Element
9 | }
10 |
11 | func buildApp(cd react.ComponentDef) react.Component {
12 | return AppDef{ComponentDef: cd}
13 | }
14 |
15 | func buildAppElem(children ...react.Element) *AppElem {
16 | return &AppElem{
17 | Element: react.CreateElement(buildApp, nil, children...),
18 | }
19 | }
20 |
21 | func (a AppDef) RendersElement() react.Element {
22 | return a.Render()
23 | }
24 |
--------------------------------------------------------------------------------
/9-Isomorphic-Go/reactproject/hello_message/gen_HelloMessage_reactGen.go:
--------------------------------------------------------------------------------
1 | // Code generated by reactGen. DO NOT EDIT.
2 |
3 | package hellomessage
4 |
5 | import "myitcv.io/react"
6 |
7 | type HelloMessageElem struct {
8 | react.Element
9 | }
10 |
11 | func buildHelloMessage(cd react.ComponentDef) react.Component {
12 | return HelloMessageDef{ComponentDef: cd}
13 | }
14 |
15 | func buildHelloMessageElem(props HelloMessageProps, children ...react.Element) *HelloMessageElem {
16 | return &HelloMessageElem{
17 | Element: react.CreateElement(buildHelloMessage, props, children...),
18 | }
19 | }
20 |
21 | func (h HelloMessageDef) RendersElement() react.Element {
22 | return h.Render()
23 | }
24 |
25 | // SetState is an auto-generated proxy proxy to update the state for the
26 | // HelloMessage component. SetState does not immediately mutate h.State()
27 | // but creates a pending state transition.
28 | func (h HelloMessageDef) SetState(state HelloMessageState) {
29 | h.ComponentDef.SetState(state)
30 | }
31 |
32 | // State is an auto-generated proxy to return the current state in use for the
33 | // render of the HelloMessage component
34 | func (h HelloMessageDef) State() HelloMessageState {
35 | return h.ComponentDef.State().(HelloMessageState)
36 | }
37 |
38 | // IsState is an auto-generated definition so that HelloMessageState implements
39 | // the myitcv.io/react.State interface.
40 | func (h HelloMessageState) IsState() {}
41 |
42 | var _ react.State = HelloMessageState{}
43 |
44 | // GetInitialStateIntf is an auto-generated proxy to GetInitialState
45 | func (h HelloMessageDef) GetInitialStateIntf() react.State {
46 | return HelloMessageState{}
47 | }
48 |
49 | func (h HelloMessageState) EqualsIntf(val react.State) bool {
50 | return h.Equals(val.(HelloMessageState))
51 | }
52 |
53 | // IsProps is an auto-generated definition so that HelloMessageProps implements
54 | // the myitcv.io/react.Props interface.
55 | func (h HelloMessageProps) IsProps() {}
56 |
57 | // Props is an auto-generated proxy to the current props of HelloMessage
58 | func (h HelloMessageDef) Props() HelloMessageProps {
59 | uprops := h.ComponentDef.Props()
60 | return uprops.(HelloMessageProps)
61 | }
62 |
63 | func (h HelloMessageProps) EqualsIntf(val react.Props) bool {
64 | return h == val.(HelloMessageProps)
65 | }
66 |
67 | var _ react.Props = HelloMessageProps{}
68 |
--------------------------------------------------------------------------------
/9-Isomorphic-Go/reactproject/hello_message/hello_message.go:
--------------------------------------------------------------------------------
1 | // hello_message.go
2 |
3 | package hellomessage
4 |
5 | import (
6 | "fmt"
7 |
8 | "honnef.co/go/js/dom"
9 | "myitcv.io/react"
10 | )
11 |
12 | //go:generate reactGen
13 |
14 | // Step 1
15 | // Declare a type that has (at least) an anonymous embedded react.ComponentDef
16 | // (it can have other fields); this type must have the suffix 'Def', which corresponds to
17 | // 'Definition'
18 | //
19 | type HelloMessageDef struct {
20 | react.ComponentDef
21 | }
22 |
23 | // Step 2
24 | // Optionally declare a props type; the naming convention is *Props
25 | //
26 | type HelloMessageProps struct {
27 | Message string
28 | }
29 |
30 | // Step 3
31 | // Optionally declare a state type; the naming convention is *State
32 | //
33 | type HelloMessageState struct {
34 | CurrName string
35 | Names []string
36 | }
37 |
38 | // hello_message.go continued....
39 |
40 | // Step 4
41 | // Declare a function to create instances of the component, i.e. an element. If
42 | // your component requires props to be specified, add this to the function
43 | // signature. If the props are optional, use a props pointer type.
44 | //
45 | // buildHelloMessageElem is code generated to wrap a call to react.CreateElement.
46 | //
47 | // Convention is that this function is given the name of the component, HelloMessage
48 | // in this instance. Because this component has props, we also accept these as part
49 | // of the constructor.
50 | //
51 | func HelloMessage(p HelloMessageProps) *HelloMessageElem {
52 | fmt.Println("Building element...")
53 | return buildHelloMessageElem(p)
54 | }
55 |
56 | // Step 5
57 | // Define a Render method on the component's non-pointer type
58 | //
59 | func (r HelloMessageDef) Render() react.Element {
60 | InputName := react.Input(&react.InputProps{
61 | Type: "text",
62 | Key: "FirstName",
63 | Placeholder: "Mina",
64 | Value: r.State().CurrName,
65 | OnChange: r,
66 | }, nil)
67 | InputBtn := react.Input(&react.InputProps{
68 | Type: "Submit",
69 | Value: "Submit",
70 | }, nil)
71 | Form := react.Form(&react.FormProps{
72 | OnSubmit: r,
73 | },
74 | react.S("Name: "),
75 | InputName,
76 | InputBtn)
77 | names := r.State().Names
78 | fmt.Println(names)
79 | entries := make([]react.RendersLi, len(names))
80 | for i, name := range names {
81 | entries[i] = react.Li(nil, react.S(r.Props().Message+" "+name))
82 | }
83 | return react.Div(nil,
84 | Form,
85 | react.S(r.Props().Message+" "+r.State().CurrName),
86 | react.Ul(nil, entries...),
87 | )
88 | }
89 |
90 | func (r HelloMessageDef) OnSubmit(e *react.SyntheticEvent) {
91 | //Prevent the default form submission action
92 | e.PreventDefault()
93 | //Add the new name to the list of names in the state object
94 | names := r.State().Names
95 | names = append(names, r.State().CurrName)
96 | /*
97 | Change the state so that the current name is now empty, and the new name gets added to the existing list of names
98 | */
99 | r.SetState(HelloMessageState{CurrName: "", Names: names})
100 | }
101 |
102 | func (c HelloMessageState) Equals(v HelloMessageState) bool {
103 | if c.CurrName != v.CurrName {
104 | return false
105 | }
106 |
107 | if len(c.Names) != len(v.Names) {
108 | return false
109 | }
110 |
111 | for i := range v.Names {
112 | if v.Names[i] != c.Names[i] {
113 | return false
114 | }
115 | }
116 | return true
117 | }
118 |
119 | func (r HelloMessageDef) OnChange(e *react.SyntheticEvent) {
120 | //we need to import "honnef.co/go/js/dom" for this to work
121 | //get target: our input text component
122 | target := e.Target().(*dom.HTMLInputElement)
123 | //get current state
124 | currState := r.State()
125 | //change state to include new value in our input text component, as well as the existing history of names
126 | r.SetState(HelloMessageState{CurrName: target.Value, Names: currState.Names})
127 | }
128 |
--------------------------------------------------------------------------------
/9-Isomorphic-Go/reactproject/index.html:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 |
6 |
7 |
8 |
9 |
10 |
11 | Hello World
12 |
13 |
14 |
15 |
16 |
17 |
18 |
19 |
20 |
21 |
22 |
--------------------------------------------------------------------------------
/9-Isomorphic-Go/reactproject/main.go:
--------------------------------------------------------------------------------
1 | // Template generated by reactGen
2 |
3 | package main
4 |
5 | import (
6 | "myitcv.io/react"
7 |
8 | "honnef.co/go/js/dom"
9 | )
10 |
11 | //go:generate reactGen
12 |
13 | var document = dom.GetWindow().Document()
14 |
15 | func main() {
16 | domTarget := document.GetElementByID("app")
17 |
18 | react.Render(App(), domTarget)
19 | }
20 |
--------------------------------------------------------------------------------
/README.md:
--------------------------------------------------------------------------------
1 | # Hands-On-Full-Stack-Development-with-Go
2 | Code repo for the Hands-On Full-Stack Development with Go book
3 |
4 |
--------------------------------------------------------------------------------
/final-application/public/build/asset-manifest.json:
--------------------------------------------------------------------------------
1 | {
2 | "main.js": "static/js/main.04874240.js",
3 | "main.js.map": "static/js/main.04874240.js.map"
4 | }
--------------------------------------------------------------------------------
/final-application/public/build/cards.json:
--------------------------------------------------------------------------------
1 | [{
2 | "id" : 1,
3 | "img" : "img/strings.png",
4 | "imgalt":"string",
5 | "desc":"A very authentic and beautiful instrument!!",
6 | "price" : 100.0,
7 | "productname" : "Strings"
8 | }, {
9 | "id" : 2,
10 | "img" : "img/redguitar.jpeg",
11 | "imgalt":"redg",
12 | "desc":"A really cool red guitar that can produce super cool music!!",
13 | "price" : 299.0,
14 | "productname" : "Red Guitar"
15 | },{
16 | "id" : 3,
17 | "img" : "img/drums.jpg",
18 | "imgalt":"drums",
19 | "desc":"A set of super awesome drums, combined with a guitar, they can product more than amazing music!!",
20 | "price" : 17000.0,
21 | "productname" : "Drums"
22 | },{
23 | "id" : 4,
24 | "img" : "img/flute.jpeg",
25 | "imgalt":"flute",
26 | "desc":"A super nice flute combined with some super nice musical notes!!",
27 | "price" : 210.0,
28 | "productname" : "Flute"
29 | },{
30 | "id" : 5,
31 | "img" : "img/blackguitar.jpeg",
32 | "imgalt":"Black guitar",
33 | "desc":"An awesome guitar that will product amazing sound!!",
34 | "price" : 200.0,
35 | "productname" : "Black Guitar"
36 | },{
37 | "id" : 6,
38 | "img" : "img/saxophone.jpeg",
39 | "imgalt":"Saxophone",
40 | "desc":"An great saxophone for a great musician!!",
41 | "price" : 1000.0,
42 | "productname" : "Saxophone"
43 | }]
--------------------------------------------------------------------------------
/final-application/public/build/favicon.ico:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/minaandrawos/Hands-On-Full-Stack-Development-with-Go/bb0ccde535e21378f8f01aa7b20b66e7bba2a671/final-application/public/build/favicon.ico
--------------------------------------------------------------------------------
/final-application/public/build/img/blackguitar.jpeg:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/minaandrawos/Hands-On-Full-Stack-Development-with-Go/bb0ccde535e21378f8f01aa7b20b66e7bba2a671/final-application/public/build/img/blackguitar.jpeg
--------------------------------------------------------------------------------
/final-application/public/build/img/coolguitar.jpeg:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/minaandrawos/Hands-On-Full-Stack-Development-with-Go/bb0ccde535e21378f8f01aa7b20b66e7bba2a671/final-application/public/build/img/coolguitar.jpeg
--------------------------------------------------------------------------------
/final-application/public/build/img/drums.jpg:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/minaandrawos/Hands-On-Full-Stack-Development-with-Go/bb0ccde535e21378f8f01aa7b20b66e7bba2a671/final-application/public/build/img/drums.jpg
--------------------------------------------------------------------------------
/final-application/public/build/img/flute.jpeg:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/minaandrawos/Hands-On-Full-Stack-Development-with-Go/bb0ccde535e21378f8f01aa7b20b66e7bba2a671/final-application/public/build/img/flute.jpeg
--------------------------------------------------------------------------------
/final-application/public/build/img/img-small/blackguitar.jpeg:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/minaandrawos/Hands-On-Full-Stack-Development-with-Go/bb0ccde535e21378f8f01aa7b20b66e7bba2a671/final-application/public/build/img/img-small/blackguitar.jpeg
--------------------------------------------------------------------------------
/final-application/public/build/img/img-small/coolguitar.jpeg:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/minaandrawos/Hands-On-Full-Stack-Development-with-Go/bb0ccde535e21378f8f01aa7b20b66e7bba2a671/final-application/public/build/img/img-small/coolguitar.jpeg
--------------------------------------------------------------------------------
/final-application/public/build/img/img-small/drums.jpg:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/minaandrawos/Hands-On-Full-Stack-Development-with-Go/bb0ccde535e21378f8f01aa7b20b66e7bba2a671/final-application/public/build/img/img-small/drums.jpg
--------------------------------------------------------------------------------
/final-application/public/build/img/img-small/flute.jpeg:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/minaandrawos/Hands-On-Full-Stack-Development-with-Go/bb0ccde535e21378f8f01aa7b20b66e7bba2a671/final-application/public/build/img/img-small/flute.jpeg
--------------------------------------------------------------------------------
/final-application/public/build/img/img-small/redguitar.jpeg:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/minaandrawos/Hands-On-Full-Stack-Development-with-Go/bb0ccde535e21378f8f01aa7b20b66e7bba2a671/final-application/public/build/img/img-small/redguitar.jpeg
--------------------------------------------------------------------------------
/final-application/public/build/img/img-small/saxophone.jpeg:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/minaandrawos/Hands-On-Full-Stack-Development-with-Go/bb0ccde535e21378f8f01aa7b20b66e7bba2a671/final-application/public/build/img/img-small/saxophone.jpeg
--------------------------------------------------------------------------------
/final-application/public/build/img/img-small/strings.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/minaandrawos/Hands-On-Full-Stack-Development-with-Go/bb0ccde535e21378f8f01aa7b20b66e7bba2a671/final-application/public/build/img/img-small/strings.png
--------------------------------------------------------------------------------
/final-application/public/build/img/mics.jpeg:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/minaandrawos/Hands-On-Full-Stack-Development-with-Go/bb0ccde535e21378f8f01aa7b20b66e7bba2a671/final-application/public/build/img/mics.jpeg
--------------------------------------------------------------------------------
/final-application/public/build/img/redguitar.jpeg:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/minaandrawos/Hands-On-Full-Stack-Development-with-Go/bb0ccde535e21378f8f01aa7b20b66e7bba2a671/final-application/public/build/img/redguitar.jpeg
--------------------------------------------------------------------------------
/final-application/public/build/img/saxophone.jpeg:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/minaandrawos/Hands-On-Full-Stack-Development-with-Go/bb0ccde535e21378f8f01aa7b20b66e7bba2a671/final-application/public/build/img/saxophone.jpeg
--------------------------------------------------------------------------------
/final-application/public/build/img/strings.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/minaandrawos/Hands-On-Full-Stack-Development-with-Go/bb0ccde535e21378f8f01aa7b20b66e7bba2a671/final-application/public/build/img/strings.png
--------------------------------------------------------------------------------
/final-application/public/build/img/sunnyguitar.jpeg:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/minaandrawos/Hands-On-Full-Stack-Development-with-Go/bb0ccde535e21378f8f01aa7b20b66e7bba2a671/final-application/public/build/img/sunnyguitar.jpeg
--------------------------------------------------------------------------------
/final-application/public/build/index.html:
--------------------------------------------------------------------------------
1 | React App
--------------------------------------------------------------------------------
/final-application/public/build/manifest.json:
--------------------------------------------------------------------------------
1 | {
2 | "short_name": "React App",
3 | "name": "Create React App Sample",
4 | "icons": [
5 | {
6 | "src": "favicon.ico",
7 | "sizes": "64x64 32x32 24x24 16x16",
8 | "type": "image/x-icon"
9 | }
10 | ],
11 | "start_url": "./index.html",
12 | "display": "standalone",
13 | "theme_color": "#000000",
14 | "background_color": "#ffffff"
15 | }
16 |
--------------------------------------------------------------------------------
/final-application/public/build/promos.json:
--------------------------------------------------------------------------------
1 | [{
2 | "id" : 3,
3 | "img" : "img/drums.jpg",
4 | "imgalt":"drums",
5 | "desc":"A set of super awesome drums, combined with a guitar, they can product more than amazing music!!",
6 | "price" : 17000.0,
7 | "productname" : "Drums"
8 | },{
9 | "id" : 4,
10 | "img" : "img/flute.jpeg",
11 | "imgalt":"flute",
12 | "desc":"A super nice flute combined with some super nice musical notes!!",
13 | "price" : 210.0,
14 | "productname" : "Flute"
15 | },{
16 | "id" : 5,
17 | "img" : "img/blackguitar.jpeg",
18 | "imgalt":"Black guitar",
19 | "desc":"An awesome guitar that will product amazing sound!!",
20 | "price" : 200.0,
21 | "productname" : "Black Guitar"
22 | },{
23 | "id" : 6,
24 | "img" : "img/saxophone.jpeg",
25 | "imgalt":"Saxophone",
26 | "desc":"An great saxophone for a great musician!!",
27 | "price" : 1000.0,
28 | "productname" : "Saxophone"
29 | }]
--------------------------------------------------------------------------------
/final-application/public/build/service-worker.js:
--------------------------------------------------------------------------------
1 | "use strict";var precacheConfig=[["/index.html","7524eabc1eeb8f065211fb63433cf3d4"],["/static/js/main.04874240.js","12b59aea7cb190c7822301a4b018a513"]],cacheName="sw-precache-v3-sw-precache-webpack-plugin-"+(self.registration?self.registration.scope:""),ignoreUrlParametersMatching=[/^utm_/],addDirectoryIndex=function(e,t){var n=new URL(e);return"/"===n.pathname.slice(-1)&&(n.pathname+=t),n.toString()},cleanResponse=function(t){return t.redirected?("body"in t?Promise.resolve(t.body):t.blob()).then(function(e){return new Response(e,{headers:t.headers,status:t.status,statusText:t.statusText})}):Promise.resolve(t)},createCacheKey=function(e,t,n,r){var a=new URL(e);return r&&a.pathname.match(r)||(a.search+=(a.search?"&":"")+encodeURIComponent(t)+"="+encodeURIComponent(n)),a.toString()},isPathWhitelisted=function(e,t){if(0===e.length)return!0;var n=new URL(t).pathname;return e.some(function(e){return n.match(e)})},stripIgnoredUrlParameters=function(e,n){var t=new URL(e);return t.hash="",t.search=t.search.slice(1).split("&").map(function(e){return e.split("=")}).filter(function(t){return n.every(function(e){return!e.test(t[0])})}).map(function(e){return e.join("=")}).join("&"),t.toString()},hashParamName="_sw-precache",urlsToCacheKeys=new Map(precacheConfig.map(function(e){var t=e[0],n=e[1],r=new URL(t,self.location),a=createCacheKey(r,hashParamName,n,/\.\w{8}\./);return[r.toString(),a]}));function setOfCachedUrls(e){return e.keys().then(function(e){return e.map(function(e){return e.url})}).then(function(e){return new Set(e)})}self.addEventListener("install",function(e){e.waitUntil(caches.open(cacheName).then(function(r){return setOfCachedUrls(r).then(function(n){return Promise.all(Array.from(urlsToCacheKeys.values()).map(function(t){if(!n.has(t)){var e=new Request(t,{credentials:"same-origin"});return fetch(e).then(function(e){if(!e.ok)throw new Error("Request for "+t+" returned a response with status "+e.status);return cleanResponse(e).then(function(e){return r.put(t,e)})})}}))})}).then(function(){return self.skipWaiting()}))}),self.addEventListener("activate",function(e){var n=new Set(urlsToCacheKeys.values());e.waitUntil(caches.open(cacheName).then(function(t){return t.keys().then(function(e){return Promise.all(e.map(function(e){if(!n.has(e.url))return t.delete(e)}))})}).then(function(){return self.clients.claim()}))}),self.addEventListener("fetch",function(t){if("GET"===t.request.method){var e,n=stripIgnoredUrlParameters(t.request.url,ignoreUrlParametersMatching),r="index.html";(e=urlsToCacheKeys.has(n))||(n=addDirectoryIndex(n,r),e=urlsToCacheKeys.has(n));var a="/index.html";!e&&"navigate"===t.request.mode&&isPathWhitelisted(["^(?!\\/__).*"],t.request.url)&&(n=new URL(a,self.location).toString(),e=urlsToCacheKeys.has(n)),e&&t.respondWith(caches.open(cacheName).then(function(e){return e.match(urlsToCacheKeys.get(n)).then(function(e){if(e)return e;throw Error("The cached response that was expected is missing.")})}).catch(function(e){return console.warn('Couldn\'t serve response for "%s" from cache: %O',t.request.url,e),fetch(t.request)}))}});
--------------------------------------------------------------------------------
/final-application/public/build/user.json:
--------------------------------------------------------------------------------
1 | {
2 | "name": "Mina",
3 | "loggedin": false,
4 | "orders": [
5 | {
6 | "id" : 1,
7 | "img" : "img/img-small/strings.png",
8 | "imgalt":"string",
9 | "desc":"A very authentic and beautiful instrument!!",
10 | "price" : 100.0,
11 | "productname" : "Strings",
12 | "days": 32
13 | }, {
14 | "id" : 2,
15 | "img" : "img/img-small/redguitar.jpeg",
16 | "imgalt":"redg",
17 | "desc":"A really cool red guitar that can produce super cool music!!",
18 | "price" : 299.0,
19 | "productname" : "Red Guitar",
20 | "days": 99
21 | },{
22 | "id" : 3,
23 | "img" : "img/img-small/drums.jpg",
24 | "imgalt":"drums",
25 | "desc":"A set of super awesome drums, combined with a guitar, they can product more than amazing music!!",
26 | "price" : 17000.0,
27 | "productname" : "Drums",
28 | "days": 45
29 | }
30 | ]
31 | }
--------------------------------------------------------------------------------
/final-application/readme.md:
--------------------------------------------------------------------------------
1 | The GoMusic full application. This includes the Go code, combined with the React output build folder.
2 |
--------------------------------------------------------------------------------
/final-application/src/dblayer/dblayer.go:
--------------------------------------------------------------------------------
1 | package dblayer
2 |
3 | import (
4 | "errors"
5 |
6 | "github.com/minaandrawos/Hands-On-Full-Stack-Development-with-Go/final-application/src/models"
7 | )
8 |
9 | type DBLayer interface {
10 | GetAllProducts() ([]models.Product, error)
11 | GetPromos() ([]models.Product, error)
12 | GetCustomerByName(string, string) (models.Customer, error)
13 | GetCustomerByID(int) (models.Customer, error)
14 | GetProduct(int) (models.Product, error)
15 | AddUser(models.Customer) (models.Customer, error)
16 | SignInUser(username, password string) (models.Customer, error)
17 | SignOutUserById(int) error
18 | GetCustomerOrdersByID(int) ([]models.Order, error)
19 | AddOrder(models.Order) error
20 | GetCreditCardCID(int) (string, error)
21 | SaveCreditCardForCustomer(int, string) error
22 | }
23 |
24 | /*
25 | Passwords:
26 | mal:123
27 | john:1111
28 | jayne:123
29 | River:abc
30 | */
31 |
32 | var ErrINVALIDPASSWORD = errors.New("Invalid password")
33 |
--------------------------------------------------------------------------------
/final-application/src/dblayer/orm.go:
--------------------------------------------------------------------------------
1 | package dblayer
2 |
3 | import (
4 | "errors"
5 |
6 | _ "github.com/go-sql-driver/mysql"
7 | "github.com/jinzhu/gorm"
8 | "github.com/minaandrawos/Hands-On-Full-Stack-Development-with-Go/final-application/src/models"
9 | "golang.org/x/crypto/bcrypt"
10 | )
11 |
12 | //get products
13 | //get promos
14 | //post user sign in
15 | //get user orders
16 | //post user sign out
17 | //post purchase charge
18 |
19 | type DBORM struct {
20 | *gorm.DB
21 | }
22 |
23 | func NewORM(dbname, con string) (*DBORM, error) {
24 | db, err := gorm.Open(dbname, con+"?parseTime=true")
25 | return &DBORM{
26 | DB: db,
27 | }, err
28 | }
29 |
30 | func (db *DBORM) GetAllProducts() (products []models.Product, err error) {
31 | return products, db.Find(&products).Error
32 | }
33 |
34 | func (db *DBORM) GetPromos() (products []models.Product, err error) {
35 | return products, db.Where("promotion IS NOT NULL").Find(&products).Error
36 |
37 | }
38 |
39 | func (db *DBORM) GetCustomerByName(firstname string, lastname string) (customer models.Customer, err error) {
40 | return customer, db.Where(&models.Customer{FirstName: firstname, LastName: lastname}).Find(&customer).Error
41 | }
42 |
43 | func (db *DBORM) GetCustomerByID(id int) (customer models.Customer, err error) {
44 | return customer, db.First(&customer, id).Error
45 | }
46 |
47 | func (db *DBORM) GetProduct(id int) (product models.Product, error error) {
48 | return product, db.First(&product, id).Error
49 | }
50 |
51 | func (db *DBORM) AddUser(customer models.Customer) (models.Customer, error) {
52 | //pass received password by reference so that we can change it to it's hashed version
53 | hashPassword(&customer.Pass)
54 | customer.LoggedIn = true
55 | err := db.Create(&customer).Error
56 | customer.Pass = ""
57 | return customer, err
58 | }
59 |
60 | func hashPassword(s *string) error {
61 | if s == nil {
62 | return errors.New("Reference provided for hashing password is nil")
63 | }
64 | //converd password string to byte slice
65 | sBytes := []byte(*s)
66 | //Obtain hashed password
67 | hashedBytes, err := bcrypt.GenerateFromPassword(sBytes, bcrypt.DefaultCost)
68 | if err != nil {
69 | return err
70 | }
71 | //update password string with the hashed version
72 | *s = string(hashedBytes[:])
73 | return nil
74 | }
75 |
76 | func (db *DBORM) SignInUser(email, pass string) (customer models.Customer, err error) {
77 |
78 | //Obtain a *gorm.DB object representing our customer's row
79 | result := db.Table("Customers").Where(&models.Customer{Email: email})
80 | err = result.First(&customer).Error
81 | if err != nil {
82 | return customer, err
83 | }
84 |
85 | if !checkPassword(customer.Pass, pass) {
86 | return customer, ErrINVALIDPASSWORD
87 | }
88 |
89 | customer.Pass = ""
90 | //update the loggedin field
91 | err = result.Update("loggedin", 1).Error
92 | if err != nil {
93 | return customer, err
94 | }
95 | //return the new customer row
96 | return customer, result.Find(&customer).Error
97 | }
98 |
99 | func checkPassword(existingHash, incomingPass string) bool {
100 | //this method will return an error if the hash does not match the provided password string
101 | return bcrypt.CompareHashAndPassword([]byte(existingHash), []byte(incomingPass)) == nil
102 | }
103 |
104 | func (db *DBORM) SignOutUserById(id int) error {
105 | customer := models.Customer{
106 | Model: gorm.Model{
107 | ID: uint(id),
108 | },
109 | }
110 | return db.Table("Customers").Where(&customer).Update("loggedin", 0).Error
111 | }
112 |
113 | func (db *DBORM) GetCustomerOrdersByID(id int) (orders []models.Order, err error) {
114 | return orders, db.Table("orders").Select("*").Joins("join customers on customers.id = customer_id").Joins("join products on products.id = product_id").Where("customer_id=?", id).Scan(&orders).Error //db.Find(&orders, models.Order{CustomerID: id}).Error
115 | }
116 |
117 | func (db *DBORM) AddOrder(order models.Order) error {
118 |
119 | return db.Create(&order).Error
120 | }
121 |
122 | func (db *DBORM) GetCreditCardCID(id int) (string, error) {
123 |
124 | cusomterWithCCID := struct {
125 | models.Customer
126 | CCID string `gorm:"column:cc_customerid"`
127 | }{}
128 |
129 | return cusomterWithCCID.CCID, db.First(&cusomterWithCCID, id).Error
130 | }
131 |
132 | func (db *DBORM) SaveCreditCardForCustomer(id int, ccid string) error {
133 | result := db.Table("customers").Where("id=?", id)
134 | return result.Update("cc_customerid", ccid).Error
135 | }
136 |
--------------------------------------------------------------------------------
/final-application/src/main.go:
--------------------------------------------------------------------------------
1 | package main
2 |
3 | import (
4 | "log"
5 |
6 | "github.com/minaandrawos/Hands-On-Full-Stack-Development-with-Go/final-application/src/rest"
7 | )
8 |
9 | func main() {
10 | log.Println("Main log....")
11 | log.Fatal(rest.RunAPI("127.0.0.1:8080"))
12 | }
13 |
--------------------------------------------------------------------------------
/final-application/src/mockdata.json:
--------------------------------------------------------------------------------
1 | {
2 | "name": "Mina",
3 | "loggedin": true,
4 | "orders": [
5 | {
6 | "id": 1,
7 | "img": "img/img-small/strings.png",
8 | "imgalt": "string",
9 | "desc": "A very authentic and beautiful instrument!!",
10 | "price": 100.0,
11 | "productname": "Strings",
12 | "days": 32
13 | }, {
14 | "id": 2,
15 | "img": "img/img-small/redguitar.jpeg",
16 | "imgalt": "redg",
17 | "desc": "A really cool red guitar that can produce super cool music!!",
18 | "price": 299.0,
19 | "productname": "Red Guitar",
20 | "days": 99
21 | }, {
22 | "id": 3,
23 | "img": "img/img-small/drums.jpg",
24 | "imgalt": "drums",
25 | "desc": "A set of super awesome drums, combined with a guitar, they can product more than amazing music!!",
26 | "price": 17000.0,
27 | "productname": "Drums",
28 | "days": 45
29 | }
30 | ]
31 | }
--------------------------------------------------------------------------------
/final-application/src/models/models.go:
--------------------------------------------------------------------------------
1 | package models
2 |
3 | import (
4 | "time"
5 |
6 | "github.com/jinzhu/gorm"
7 | )
8 |
9 | type Product struct {
10 | gorm.Model
11 | Image string `json:"img"`
12 | SmallImage string `gorm:"column:smallimg" json:"small_img"`
13 | ImagAlt string `json:"imgalt" gorm:"column:imgalt"`
14 | Price float64 `json:"price"`
15 | Promotion float64 `json:"promotion"` //sql.NullFloat64
16 | PoructName string `gorm:"column:productname" json:"productname"`
17 | Description string
18 | }
19 |
20 | func (Product) TableName() string {
21 | return "products"
22 | }
23 |
24 | type Customer struct {
25 | gorm.Model
26 | Name string `json:"name" sql:"-"`
27 | FirstName string `gorm:"column:firstname" json:"firstname"`
28 | LastName string `gorm:"column:lastname" json:"lastname"`
29 | Email string `gorm:"column:email" json:"email"`
30 | Pass string `json:"password"`
31 | LoggedIn bool `gorm:"column:loggedin" json:"loggedin"`
32 | Orders []Order `json:"orders"`
33 | }
34 |
35 | func (Customer) TableName() string {
36 | return "customers"
37 | }
38 |
39 | type Order struct {
40 | gorm.Model
41 | Product
42 | Customer
43 | CustomerID int `json:"customer_id" gorm:"column:customer_id"`
44 | ProductID int `json:"product_id" gorm:"column:product_id"`
45 | Price float64 `gorm:"column:price" json:"sell_price"`
46 | PurchaseDate time.Time `gorm:"column:purchase_date" json:"purchase_date"`
47 | }
48 |
49 | func (Order) TableName() string {
50 | return "orders"
51 | }
52 |
--------------------------------------------------------------------------------
/final-application/src/rest/handler.go:
--------------------------------------------------------------------------------
1 | package rest
2 |
3 | import (
4 | "fmt"
5 | "log"
6 | "net/http"
7 | "strconv"
8 |
9 | "github.com/gin-gonic/gin"
10 | "github.com/minaandrawos/Hands-On-Full-Stack-Development-with-Go/final-application/src/dblayer"
11 | "github.com/minaandrawos/Hands-On-Full-Stack-Development-with-Go/final-application/src/models"
12 | "github.com/stripe/stripe-go"
13 | "github.com/stripe/stripe-go/charge"
14 | "github.com/stripe/stripe-go/customer"
15 | )
16 |
17 | type HandlerInterface interface {
18 | GetProducts(c *gin.Context)
19 | GetPromos(c *gin.Context)
20 | AddUser(c *gin.Context)
21 | SignIn(c *gin.Context)
22 | SignOut(c *gin.Context)
23 | GetOrders(c *gin.Context)
24 | Charge(c *gin.Context)
25 | }
26 |
27 | type Handler struct {
28 | db dblayer.DBLayer
29 | }
30 |
31 | func NewHandler() (HandlerInterface, error) {
32 | db, err := dblayer.NewORM("mysql", "root:root@/gomusic")
33 | if err != nil {
34 | return nil, err
35 | }
36 | return &Handler{
37 | db: db,
38 | }, nil
39 | }
40 |
41 | func (h *Handler) GetMainPage(c *gin.Context) {
42 | log.Println("Main page....")
43 | c.String(http.StatusOK, "Main page for secure API!!")
44 | //fmt.Fprintf(c.Writer, "Main page for secure API!!")
45 | }
46 |
47 | func (h *Handler) GetProducts(c *gin.Context) {
48 | if h.db == nil {
49 | c.JSON(http.StatusInternalServerError, gin.H{"error": "server database error"})
50 | return
51 | }
52 | products, err := h.db.GetAllProducts()
53 | if err != nil {
54 | c.JSON(http.StatusInternalServerError, gin.H{"error": err.Error()})
55 | return
56 | }
57 | fmt.Printf("Found %d products\n", len(products))
58 | c.JSON(http.StatusOK, products)
59 | }
60 |
61 | func (h *Handler) GetPromos(c *gin.Context) {
62 | if h.db == nil {
63 | c.JSON(http.StatusInternalServerError, gin.H{"error": "server database error"})
64 | return
65 | }
66 | promos, err := h.db.GetPromos()
67 | if err != nil {
68 | c.JSON(http.StatusInternalServerError, gin.H{"error": err.Error()})
69 | return
70 | }
71 | c.JSON(http.StatusOK, promos)
72 | }
73 |
74 | func (h *Handler) AddUser(c *gin.Context) {
75 | if h.db == nil {
76 | c.JSON(http.StatusInternalServerError, gin.H{"error": "server database error"})
77 | return
78 | }
79 | var customer models.Customer
80 | err := c.ShouldBindJSON(&customer)
81 | if err != nil {
82 | c.JSON(http.StatusBadRequest, gin.H{"error": err.Error()})
83 | return
84 | }
85 | customer, err = h.db.AddUser(customer)
86 | if err != nil {
87 | c.JSON(http.StatusInternalServerError, gin.H{"error": err.Error()})
88 | return
89 | }
90 | c.JSON(http.StatusOK, customer)
91 | }
92 |
93 | func (h *Handler) SignIn(c *gin.Context) {
94 | if h.db == nil {
95 | c.JSON(http.StatusInternalServerError, gin.H{"error": "server database error"})
96 | return
97 | }
98 | var customer models.Customer
99 | err := c.ShouldBindJSON(&customer)
100 | if err != nil {
101 | c.JSON(http.StatusBadRequest, gin.H{"error": err.Error()})
102 | return
103 | }
104 | customer, err = h.db.SignInUser(customer.Email, customer.Pass)
105 | if err != nil {
106 | if err == dblayer.ErrINVALIDPASSWORD {
107 | c.JSON(http.StatusForbidden, gin.H{"error": err.Error()})
108 | return
109 | }
110 | c.JSON(http.StatusInternalServerError, gin.H{"error": err.Error()})
111 | return
112 | }
113 | c.JSON(http.StatusOK, customer)
114 | }
115 |
116 | func (h *Handler) SignOut(c *gin.Context) {
117 | if h.db == nil {
118 | c.JSON(http.StatusInternalServerError, gin.H{"error": "server database error"})
119 | return
120 | }
121 | p := c.Param("id")
122 | id, err := strconv.Atoi(p)
123 | if err != nil {
124 | c.JSON(http.StatusBadRequest, gin.H{"error": err.Error()})
125 | return
126 | }
127 |
128 | err = h.db.SignOutUserById(id)
129 | if err != nil {
130 | c.JSON(http.StatusInternalServerError, gin.H{"error": err.Error()})
131 | return
132 | }
133 | }
134 |
135 | func (h *Handler) GetOrders(c *gin.Context) {
136 | if h.db == nil {
137 | c.JSON(http.StatusInternalServerError, gin.H{"error": "server database error"})
138 | return
139 | }
140 | p := c.Param("id")
141 | id, err := strconv.Atoi(p)
142 | if err != nil {
143 | c.JSON(http.StatusBadRequest, gin.H{"error": err.Error()})
144 | return
145 | }
146 | orders, err := h.db.GetCustomerOrdersByID(id)
147 | if err != nil {
148 | fmt.Println(err)
149 | c.JSON(http.StatusInternalServerError, gin.H{"error": err.Error()})
150 | return
151 | }
152 | c.JSON(http.StatusOK, orders)
153 | }
154 |
155 | func (h *Handler) Charge(c *gin.Context) {
156 | if h.db == nil {
157 | c.JSON(http.StatusInternalServerError, gin.H{"error": "server database error"})
158 | return
159 | }
160 | request := struct {
161 | models.Order
162 | Remember bool `json:"rememberCard"`
163 | UseExisting bool `json:"useExisting"`
164 | Token string `json:"token"`
165 | }{}
166 |
167 | err := c.ShouldBindJSON(&request)
168 | log.Printf("request: %+v \n", request)
169 | if err != nil {
170 | c.JSON(http.StatusBadRequest, request)
171 | return
172 | }
173 | // Set your secret key: remember to change this to your live secret key in production
174 | // Keys can be obtained from: https://dashboard.stripe.com/account/apikeys
175 | // They key below is just for testing
176 | stripe.Key = "sk_test_4eC39HqLyjWDarjtT1zdp7dc"
177 | //test cards available at: https://stripe.com/docs/testing#cards
178 | //setting charge parameters
179 | chargeP := &stripe.ChargeParams{
180 | Amount: stripe.Int64(int64(request.Price)),
181 | Currency: stripe.String("usd"),
182 | Description: stripe.String("GoMusic charge..."),
183 | }
184 | stripeCustomerID := ""
185 | //Either remembercard or use exeisting should be enabled but not both
186 | if request.UseExisting {
187 | //use existing
188 | log.Println("Getting credit card id...")
189 | stripeCustomerID, err = h.db.GetCreditCardCID(request.CustomerID)
190 | if err != nil {
191 | log.Println(err)
192 | c.JSON(http.StatusInternalServerError, gin.H{"error": err.Error()})
193 | return
194 | }
195 | } else {
196 | cp := &stripe.CustomerParams{}
197 | cp.SetSource(request.Token)
198 | customer, err := customer.New(cp)
199 | if err != nil {
200 | c.JSON(http.StatusInternalServerError, gin.H{"error": err.Error()})
201 | return
202 | }
203 | stripeCustomerID = customer.ID
204 | if request.Remember {
205 | //save card!!
206 | err = h.db.SaveCreditCardForCustomer(request.CustomerID, stripeCustomerID)
207 | if err != nil {
208 | c.JSON(http.StatusInternalServerError, gin.H{"error": err.Error()})
209 | return
210 | }
211 | }
212 | }
213 | //we should check if the customer already ordered the same item or not but for simplicity, let's assume it's a new order
214 | chargeP.Customer = stripe.String(stripeCustomerID)
215 | _, err = charge.New(chargeP)
216 | if err != nil {
217 | c.JSON(http.StatusInternalServerError, gin.H{"error": err.Error()})
218 | return
219 | }
220 |
221 | err = h.db.AddOrder(request.Order)
222 | if err != nil {
223 | c.JSON(http.StatusInternalServerError, gin.H{"error": err.Error()})
224 | }
225 | }
226 |
--------------------------------------------------------------------------------
/final-application/src/rest/mockHandler.go:
--------------------------------------------------------------------------------
1 | package rest
2 |
3 | import (
4 | "encoding/json"
5 | "log"
6 | "net/http"
7 | "os"
8 |
9 | "github.com/gin-gonic/gin"
10 | "github.com/minaandrawos/Hands-On-Full-Stack-Development-with-Go/final-application/src/models"
11 | )
12 |
13 | /*
14 | var user = {
15 | "name": "Mina",
16 | "loggedin": true,
17 | "orders": [
18 | {
19 | "id": 1,
20 | "img": "img/img-small/strings.png",
21 | "imgalt": "string",
22 | "desc": "A very authentic and beautiful instrument!!",
23 | "price": 100.0,
24 | "productname": "Strings",
25 | "days": 32
26 | }, {
27 | "id": 2,
28 | "img": "img/img-small/redguitar.jpeg",
29 | "imgalt": "redg",
30 | "desc": "A really cool red guitar that can produce super cool music!!",
31 | "price": 299.0,
32 | "productname": "Red Guitar",
33 | "days": 99
34 | }, {
35 | "id": 3,
36 | "img": "img/img-small/drums.jpg",
37 | "imgalt": "drums",
38 | "desc": "A set of super awesome drums, combined with a guitar, they can product more than amazing music!!",
39 | "price": 17000.0,
40 | "productname": "Drums",
41 | "days": 45
42 | }
43 | ]
44 | };
45 |
46 | */
47 |
48 | type MockHandler struct {
49 | }
50 |
51 | func NewMockHandler() *MockHandler {
52 | return &MockHandler{}
53 | }
54 |
55 | func (mh *MockHandler) GetMainPage(c *gin.Context) {
56 |
57 | }
58 | func (mh *MockHandler) GetProducts(c *gin.Context) {
59 |
60 | }
61 | func (mh *MockHandler) GetPromos(c *gin.Context) {
62 |
63 | }
64 | func (mh *MockHandler) AddUser(c *gin.Context) {
65 | f, err := os.Open("mockdata.json")
66 | if err != nil {
67 | log.Println("Could not add user", err)
68 | c.JSON(http.StatusNotFound, gin.H{"error": "mock data file not found"})
69 | return
70 | }
71 | var user models.Customer
72 | err = json.NewDecoder(f).Decode(&user)
73 | if err != nil {
74 | log.Println("Could not add user", err)
75 | c.JSON(http.StatusNotFound, gin.H{"error": "could not decode mock data json file"})
76 | return
77 | }
78 | c.JSON(http.StatusOK, user)
79 | }
80 |
81 | func (mh *MockHandler) SignIn(c *gin.Context) {
82 | f, err := os.Open("mockdata.json")
83 | if err != nil {
84 | log.Println("Could not add user", err)
85 | c.JSON(http.StatusNotFound, gin.H{"error": "mock data file not found"})
86 | return
87 | }
88 | var user models.Customer
89 | err = json.NewDecoder(f).Decode(&user)
90 | if err != nil {
91 | log.Println("Could not add user", err)
92 | c.JSON(http.StatusNotFound, gin.H{"error": "could not decode mock data json file"})
93 | return
94 | }
95 | c.JSON(http.StatusOK, user)
96 | }
97 | func (mh *MockHandler) SignOut(c *gin.Context) {
98 |
99 | }
100 | func (mh *MockHandler) GetOrders(c *gin.Context) {
101 |
102 | }
103 | func (mh *MockHandler) Charge(c *gin.Context) {
104 |
105 | }
106 |
--------------------------------------------------------------------------------
/final-application/src/rest/rest.go:
--------------------------------------------------------------------------------
1 | package rest
2 |
3 | import (
4 | "fmt" //"github.com/gin-gonic/autotls"
5 |
6 | "github.com/gin-contrib/static"
7 | "github.com/gin-gonic/gin"
8 | )
9 |
10 | func RunAPI(address string) error {
11 | h, err := NewHandler()
12 | if err != nil {
13 | return err
14 | }
15 | return RunAPIWithHandler(address, h)
16 | }
17 |
18 | func RunMockAPI(address string) error {
19 | h := NewMockHandler()
20 | return RunAPIWithHandler(address, h)
21 | }
22 |
23 | func RunAPIWithHandler(address string, h HandlerInterface) error {
24 | //Get gin's default engine
25 | r := gin.Default()
26 | //r.Use(MyCustomLogger())
27 |
28 | //get products
29 | r.GET("/products", h.GetProducts)
30 | //get promos
31 | r.GET("/promos", h.GetPromos)
32 | /*
33 | //post user sign in
34 | r.POST("/user/signin", h.SignIn)
35 | //post user sign out
36 | r.POST("/user/:id/signout", h.SignOut)
37 | //get user orders
38 | r.GET("/user/:id/orders", h.GetOrders)
39 | //post purchase charge
40 | r.POST("/user/charge", h.Charge)
41 | */
42 |
43 | userGroup := r.Group("/user")
44 | {
45 | userGroup.POST("/:id/signout", h.SignOut)
46 | userGroup.GET("/:id/orders", h.GetOrders)
47 | }
48 |
49 | usersGroup := r.Group("/users")
50 | {
51 | usersGroup.POST("/charge", h.Charge)
52 | usersGroup.POST("/signin", h.SignIn)
53 | usersGroup.POST("", h.AddUser)
54 | }
55 | r.Use(static.ServeRoot("/", "../public/build"))
56 | return r.Run(address)
57 | //return r.RunTLS(address, "cert.pem", "key.pem")
58 | }
59 |
60 | func MyCustomLogger() gin.HandlerFunc {
61 | return func(c *gin.Context) {
62 | fmt.Println("************************************")
63 | c.Next()
64 | fmt.Println("************************************")
65 | }
66 | }
67 |
--------------------------------------------------------------------------------
/projectdb.sql:
--------------------------------------------------------------------------------
1 | -- --------------------------------------------------------
2 | -- Host: 127.0.0.1
3 | -- Server version: 8.0.12 - MySQL Community Server - GPL
4 | -- Server OS: Win64
5 | -- HeidiSQL Version: 9.5.0.5196
6 | -- --------------------------------------------------------
7 |
8 | /*!40101 SET @OLD_CHARACTER_SET_CLIENT=@@CHARACTER_SET_CLIENT */;
9 | /*!40101 SET NAMES utf8 */;
10 | /*!50503 SET NAMES utf8mb4 */;
11 | /*!40014 SET @OLD_FOREIGN_KEY_CHECKS=@@FOREIGN_KEY_CHECKS, FOREIGN_KEY_CHECKS=0 */;
12 | /*!40101 SET @OLD_SQL_MODE=@@SQL_MODE, SQL_MODE='NO_AUTO_VALUE_ON_ZERO' */;
13 |
14 |
15 | -- Dumping database structure for gomusic
16 | CREATE DATABASE IF NOT EXISTS `gomusic` /*!40100 DEFAULT CHARACTER SET utf8mb4 COLLATE utf8mb4_0900_ai_ci */;
17 | USE `gomusic`;
18 |
19 | -- Dumping structure for table gomusic.customers
20 | CREATE TABLE IF NOT EXISTS `customers` (
21 | `id` int(11) NOT NULL AUTO_INCREMENT,
22 | `firstname` varchar(50) NOT NULL DEFAULT '0',
23 | `lastname` varchar(50) NOT NULL DEFAULT '0',
24 | `email` varchar(100) NOT NULL DEFAULT '0',
25 | `pass` varchar(100) NOT NULL DEFAULT '0',
26 | `cc_customerid` varchar(50) NOT NULL DEFAULT '0',
27 | `loggedin` tinyint(1) NOT NULL DEFAULT '0',
28 | `created_at` timestamp NULL DEFAULT CURRENT_TIMESTAMP,
29 | `updated_at` timestamp NULL DEFAULT CURRENT_TIMESTAMP ON UPDATE CURRENT_TIMESTAMP,
30 | `deleted_at` timestamp NULL DEFAULT NULL,
31 | PRIMARY KEY (`id`),
32 | UNIQUE KEY `email` (`email`)
33 | ) ENGINE=InnoDB AUTO_INCREMENT=22 DEFAULT CHARSET=utf8mb4 COLLATE=utf8mb4_0900_ai_ci;
34 |
35 | -- Dumping data for table gomusic.customers: ~4 rows (approximately)
36 | /*!40000 ALTER TABLE `customers` DISABLE KEYS */;
37 | INSERT INTO `customers` (`id`, `firstname`, `lastname`, `email`, `pass`, `cc_customerid`, `loggedin`, `created_at`, `updated_at`, `deleted_at`) VALUES
38 | (1, 'Mal', 'Zein', 'mal.zein@email.com', '$2a$10$ZeZI4pPPlQg89zfOOyQmiuKW9Z7pO9/KvG7OfdgjPAZF0Vz9D8fhC', 'cus_EL08toK8pfDcom', 0, '2018-08-14 07:52:54', '2019-03-03 19:01:16', NULL),
39 | (2, 'River', 'Sam', 'river.sam@email.com', '$2a$10$mNbCLmfCAc0.4crDg3V3fe0iO1yr03aRfE7Rr3vdfKMGVnnzovCZq', '', 0, '2018-08-14 07:52:55', '2019-01-12 22:39:01', NULL),
40 | (3, 'Jayne', 'Ra', 'jayne.ra@email.com', '$2a$10$ZeZI4pPPlQg89zfOOyQmiuKW9Z7pO9/KvG7OfdgjPAZF0Vz9D8fhC', 'cus_EL4GpQmVjwvUUZ', 0, '2018-08-14 07:52:55', '2019-01-13 21:56:05', NULL),
41 | (19, 'John', 'Doe', 'john.doe@bla.com', '$2a$10$T4c8rmpbgKrUA0sIqtHCaO0g2XGWWxFY4IGWkkpVQOD/iuBrwKrZu', '', 0, '2019-01-13 08:43:44', '2019-01-13 15:12:25', NULL);
42 | /*!40000 ALTER TABLE `customers` ENABLE KEYS */;
43 |
44 | -- Dumping structure for table gomusic.orders
45 | CREATE TABLE IF NOT EXISTS `orders` (
46 | `id` int(11) NOT NULL AUTO_INCREMENT,
47 | `customer_id` int(11) NOT NULL,
48 | `product_id` int(11) NOT NULL,
49 | `price` int(11) NOT NULL,
50 | `purchase_date` timestamp NOT NULL DEFAULT CURRENT_TIMESTAMP,
51 | `created_at` timestamp NULL DEFAULT CURRENT_TIMESTAMP ON UPDATE CURRENT_TIMESTAMP,
52 | `updated_at` timestamp NULL DEFAULT CURRENT_TIMESTAMP ON UPDATE CURRENT_TIMESTAMP,
53 | `deleted_at` timestamp NULL DEFAULT NULL,
54 | PRIMARY KEY (`id`)
55 | ) ENGINE=InnoDB AUTO_INCREMENT=12 DEFAULT CHARSET=utf8mb4 COLLATE=utf8mb4_0900_ai_ci;
56 |
57 | -- Dumping data for table gomusic.orders: ~11 rows (approximately)
58 | /*!40000 ALTER TABLE `orders` DISABLE KEYS */;
59 | INSERT INTO `orders` (`id`, `customer_id`, `product_id`, `price`, `purchase_date`, `created_at`, `updated_at`, `deleted_at`) VALUES
60 | (1, 1, 1, 90, '2018-12-29 23:34:32', '2018-12-29 23:35:36', '2018-12-29 23:35:36', NULL),
61 | (2, 1, 2, 299, '2018-12-29 23:34:53', '2018-12-29 23:35:48', '2018-12-29 23:35:48', NULL),
62 | (3, 1, 3, 16000, '2018-12-29 23:35:05', '2018-12-29 23:35:57', '2018-12-29 23:35:57', NULL),
63 | (4, 2, 1, 95, '2018-12-29 23:36:18', '2018-12-29 23:36:18', '2018-12-29 23:36:18', NULL),
64 | (5, 2, 2, 299, '2018-12-29 23:36:39', '2018-12-29 23:36:39', '2018-12-29 23:36:39', NULL),
65 | (6, 2, 4, 205, '2018-12-29 23:37:01', '2018-12-29 23:38:13', '2018-12-29 23:38:13', NULL),
66 | (7, 3, 4, 210, '2018-12-29 23:37:28', '2018-12-29 23:38:19', '2018-12-29 23:38:19', NULL),
67 | (8, 3, 5, 200, '2018-12-29 23:37:41', '2018-12-29 23:38:28', '2018-12-29 23:38:28', NULL),
68 | (9, 3, 6, 1000, '2018-12-29 23:37:54', '2018-12-29 23:38:32', '2018-12-29 23:38:32', NULL),
69 | (10, 19, 6, 1000, '2018-12-29 23:37:54', '2019-01-13 00:44:55', '2019-01-13 00:44:55', NULL),
70 | (11, 1, 3, 17000, '0000-00-00 00:00:00', '2019-01-14 06:03:08', '2019-01-14 06:03:08', NULL);
71 | /*!40000 ALTER TABLE `orders` ENABLE KEYS */;
72 |
73 | -- Dumping structure for table gomusic.products
74 | CREATE TABLE IF NOT EXISTS `products` (
75 | `id` int(11) NOT NULL AUTO_INCREMENT,
76 | `image` varchar(100) DEFAULT NULL,
77 | `smallimg` varchar(100) DEFAULT NULL,
78 | `imgalt` varchar(50) DEFAULT NULL,
79 | `description` text,
80 | `productname` varchar(50) DEFAULT NULL,
81 | `price` float DEFAULT NULL,
82 | `promotion` float DEFAULT NULL,
83 | `created_at` timestamp NULL DEFAULT CURRENT_TIMESTAMP,
84 | `updated_at` timestamp NULL DEFAULT CURRENT_TIMESTAMP ON UPDATE CURRENT_TIMESTAMP,
85 | `deleted_at` timestamp NULL DEFAULT NULL,
86 | PRIMARY KEY (`id`)
87 | ) ENGINE=InnoDB AUTO_INCREMENT=7 DEFAULT CHARSET=utf8mb4 COLLATE=utf8mb4_0900_ai_ci;
88 |
89 | -- Dumping data for table gomusic.products: ~6 rows (approximately)
90 | /*!40000 ALTER TABLE `products` DISABLE KEYS */;
91 | INSERT INTO `products` (`id`, `image`, `smallimg`, `imgalt`, `description`, `productname`, `price`, `promotion`, `created_at`, `updated_at`, `deleted_at`) VALUES
92 | (1, 'img/strings.png', 'img/img-small/strings.png', 'string', '', 'Strings', 100, NULL, '2018-08-14 07:54:19', '2019-01-11 00:28:40', NULL),
93 | (2, 'img/redguitar.jpeg', 'img/img-small/redguitar.jpeg', 'redg', '', 'Red Guitar', 299, 240, '2018-08-14 07:54:20', '2019-01-11 00:29:11', NULL),
94 | (3, 'img/drums.jpg', 'img/img-small/drums.jpg', 'drums', '', 'Drums', 17000, NULL, '2018-08-14 07:54:20', '2019-01-11 22:05:42', NULL),
95 | (4, 'img/flute.jpeg', 'img/img-small/flute.jpeg', 'flute', '', 'Flute', 210, 190, '2018-08-14 07:54:20', '2019-01-11 00:29:53', NULL),
96 | (5, 'img/blackguitar.jpeg', 'img/img-small/blackguitar.jpeg', 'Black guitar', '', 'Black Guitar', 200, NULL, '2018-08-14 07:54:20', '2019-01-11 00:30:12', NULL),
97 | (6, 'img/saxophone.jpeg', 'img/img-small/saxophone.jpeg', 'Saxophone', '', 'Saxophone', 1000, 980, '2018-08-14 07:54:20', '2019-01-11 00:30:35', NULL);
98 | /*!40000 ALTER TABLE `products` ENABLE KEYS */;
99 |
100 | /*!40101 SET SQL_MODE=IFNULL(@OLD_SQL_MODE, '') */;
101 | /*!40014 SET FOREIGN_KEY_CHECKS=IF(@OLD_FOREIGN_KEY_CHECKS IS NULL, 1, @OLD_FOREIGN_KEY_CHECKS) */;
102 | /*!40101 SET CHARACTER_SET_CLIENT=@OLD_CHARACTER_SET_CLIENT */;
103 |
--------------------------------------------------------------------------------