├── 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 | {this.props.imgalt} 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 |
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 |
43 |
44 |
45 | 46 | 47 |
48 |
49 |
50 |
51 | 52 | 53 |
54 |
55 | {remembercardcheck} 56 |
57 | 58 |
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 |
15 | Sign Out 16 |
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 | {this.props.imgalt} 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 | {props.imgalt} 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 |
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 |
43 |
44 |
45 | 46 | 47 |
48 |
49 |
50 |
51 | 52 | 53 |
54 |
55 | {remembercardcheck} 56 |
57 | 58 |
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 |
15 | Sign Out 16 |
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 | {this.props.imgalt} 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 | {props.imgalt} 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 |
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 |
43 |
44 |
45 | 46 | 47 |
48 |
49 |
50 |
51 | 52 | 53 |
54 |
55 | {remembercardcheck} 56 |
57 | 58 |
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 |
15 | Sign Out 16 |
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 | {this.props.imgalt} 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 | {props.imgalt} 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 | --------------------------------------------------------------------------------