├── .gitignore ├── .idea ├── .gitignore ├── vcs.xml ├── misc.xml ├── modules.xml └── workshop-social-media-tiktok.iml ├── public ├── robots.txt ├── favicon.ico ├── logo192.png ├── logo512.png ├── manifest.json └── index.html ├── netlify.toml ├── screenshot.jpg ├── slides └── slides.pdf ├── .env.sample ├── tutorial └── images │ ├── .DS_Store │ ├── db-create.png │ ├── db-resume.png │ ├── deploy-1.png │ ├── deploy-2.png │ ├── deploy-3.png │ ├── deploy-4.png │ ├── deploy-5.png │ ├── deploy-6.png │ ├── deploy-7.png │ ├── deploy-8.png │ ├── download.png │ ├── page-home.png │ ├── pages-all.png │ ├── astra-token.png │ ├── db-pending.png │ ├── db_connect.png │ ├── download (1).png │ ├── netlify-link.png │ ├── page-upload.png │ ├── show-swagger.png │ ├── tiktok-badge.png │ ├── 2122723-middle.png │ ├── 2781017-middle.png │ ├── access-swagger.png │ ├── netlify-empty.png │ ├── netlify-signin.png │ ├── netlify-start.png │ ├── netlify_login.png │ ├── swaggerui_link.png │ ├── create_astra_db.png │ ├── db-create-button.png │ ├── deploy-to-netlify.gif │ ├── netlify-livesite.png │ ├── netlify_functions.png │ ├── react-components.png │ ├── tik-tok-logo-6fh.png │ ├── tiktok-round-line.png │ ├── astra-create-token.gif │ ├── correct-not-correct.png │ ├── create-collection-1.png │ ├── db-create-keyspace.png │ ├── list-collections-1.png │ ├── list-collections-2.png │ ├── netlify-install-cli.png │ ├── netlify-createsite-01.png │ ├── netlify-createsite-02.png │ ├── netlify-createsite-03.png │ ├── netlify-createsite-04.png │ ├── netlify-deploycomplete.png │ ├── netlify_endpoint_nav.gif │ ├── netlify_functions_tab.gif │ ├── netlify-deployinprogress.png │ ├── swaggerui_listcollections_01.png │ ├── swaggerui_listcollections_02.png │ ├── swaggerui_listcollections_03.png │ ├── swaggerui_listcollections_04.png │ ├── swaggerui_searchdocuments_01.png │ ├── swaggerui_searchdocuments_02.png │ ├── swaggerui_searchdocuments_03.png │ ├── swaggerui_searchdocuments_04.png │ ├── tiktok_logo_png_transparent512.png │ ├── 2021_04 - SpringBoot, Stargate and SDK.png │ ├── tiktok-icon-black-1-logo.svg │ ├── chrome-logo.svg │ └── firefox-logo.svg ├── .vscode └── settings.json ├── src ├── components │ ├── MicroCard.js │ ├── Header.js │ ├── MiniCard.js │ ├── FollowersColumn.js │ └── Card.js ├── index.css ├── index.js ├── pages │ ├── Upload.js │ └── Home.js └── App.css ├── astra.json ├── functions ├── edit.js ├── posts.js ├── add.js ├── utils │ └── astraClient.js └── addData.js ├── .github └── ISSUE_TEMPLATE │ └── homework-assignment.md ├── .gitpod.yml ├── package.json ├── CONTRIBUTING.md ├── CODE_OF_CONDUCT.md ├── jamstack.md ├── LICENSE.md └── README.md /.gitignore: -------------------------------------------------------------------------------- 1 | lambda 2 | .env 3 | node_modules 4 | venv 5 | .DS_Store 6 | -------------------------------------------------------------------------------- /.idea/.gitignore: -------------------------------------------------------------------------------- 1 | # Default ignored files 2 | /shelf/ 3 | /workspace.xml 4 | -------------------------------------------------------------------------------- /public/robots.txt: -------------------------------------------------------------------------------- 1 | # https://www.robotstxt.org/robotstxt.html 2 | User-agent: * 3 | Disallow: 4 | -------------------------------------------------------------------------------- /netlify.toml: -------------------------------------------------------------------------------- 1 | [build] 2 | command = "npm run build" 3 | functions = "functions" 4 | publish = "build" -------------------------------------------------------------------------------- /screenshot.jpg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/datastaxdevs/workshop-social-media-tiktok/master/screenshot.jpg -------------------------------------------------------------------------------- /slides/slides.pdf: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/datastaxdevs/workshop-social-media-tiktok/master/slides/slides.pdf -------------------------------------------------------------------------------- /public/favicon.ico: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/datastaxdevs/workshop-social-media-tiktok/master/public/favicon.ico -------------------------------------------------------------------------------- /public/logo192.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/datastaxdevs/workshop-social-media-tiktok/master/public/logo192.png -------------------------------------------------------------------------------- /public/logo512.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/datastaxdevs/workshop-social-media-tiktok/master/public/logo512.png -------------------------------------------------------------------------------- /.env.sample: -------------------------------------------------------------------------------- 1 | ASTRA_DB_ID= 2 | ASTRA_DB_REGION= 3 | ASTRA_DB_KEYSPACE= 4 | ASTRA_DB_APPLICATION_TOKEN= -------------------------------------------------------------------------------- /tutorial/images/.DS_Store: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/datastaxdevs/workshop-social-media-tiktok/master/tutorial/images/.DS_Store -------------------------------------------------------------------------------- /tutorial/images/db-create.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/datastaxdevs/workshop-social-media-tiktok/master/tutorial/images/db-create.png -------------------------------------------------------------------------------- /tutorial/images/db-resume.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/datastaxdevs/workshop-social-media-tiktok/master/tutorial/images/db-resume.png -------------------------------------------------------------------------------- /tutorial/images/deploy-1.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/datastaxdevs/workshop-social-media-tiktok/master/tutorial/images/deploy-1.png -------------------------------------------------------------------------------- /tutorial/images/deploy-2.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/datastaxdevs/workshop-social-media-tiktok/master/tutorial/images/deploy-2.png -------------------------------------------------------------------------------- /tutorial/images/deploy-3.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/datastaxdevs/workshop-social-media-tiktok/master/tutorial/images/deploy-3.png -------------------------------------------------------------------------------- /tutorial/images/deploy-4.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/datastaxdevs/workshop-social-media-tiktok/master/tutorial/images/deploy-4.png -------------------------------------------------------------------------------- /tutorial/images/deploy-5.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/datastaxdevs/workshop-social-media-tiktok/master/tutorial/images/deploy-5.png -------------------------------------------------------------------------------- /tutorial/images/deploy-6.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/datastaxdevs/workshop-social-media-tiktok/master/tutorial/images/deploy-6.png -------------------------------------------------------------------------------- /tutorial/images/deploy-7.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/datastaxdevs/workshop-social-media-tiktok/master/tutorial/images/deploy-7.png -------------------------------------------------------------------------------- /tutorial/images/deploy-8.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/datastaxdevs/workshop-social-media-tiktok/master/tutorial/images/deploy-8.png -------------------------------------------------------------------------------- /tutorial/images/download.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/datastaxdevs/workshop-social-media-tiktok/master/tutorial/images/download.png -------------------------------------------------------------------------------- /tutorial/images/page-home.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/datastaxdevs/workshop-social-media-tiktok/master/tutorial/images/page-home.png -------------------------------------------------------------------------------- /tutorial/images/pages-all.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/datastaxdevs/workshop-social-media-tiktok/master/tutorial/images/pages-all.png -------------------------------------------------------------------------------- /tutorial/images/astra-token.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/datastaxdevs/workshop-social-media-tiktok/master/tutorial/images/astra-token.png -------------------------------------------------------------------------------- /tutorial/images/db-pending.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/datastaxdevs/workshop-social-media-tiktok/master/tutorial/images/db-pending.png -------------------------------------------------------------------------------- /tutorial/images/db_connect.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/datastaxdevs/workshop-social-media-tiktok/master/tutorial/images/db_connect.png -------------------------------------------------------------------------------- /tutorial/images/download (1).png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/datastaxdevs/workshop-social-media-tiktok/master/tutorial/images/download (1).png -------------------------------------------------------------------------------- /tutorial/images/netlify-link.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/datastaxdevs/workshop-social-media-tiktok/master/tutorial/images/netlify-link.png -------------------------------------------------------------------------------- /tutorial/images/page-upload.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/datastaxdevs/workshop-social-media-tiktok/master/tutorial/images/page-upload.png -------------------------------------------------------------------------------- /tutorial/images/show-swagger.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/datastaxdevs/workshop-social-media-tiktok/master/tutorial/images/show-swagger.png -------------------------------------------------------------------------------- /tutorial/images/tiktok-badge.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/datastaxdevs/workshop-social-media-tiktok/master/tutorial/images/tiktok-badge.png -------------------------------------------------------------------------------- /tutorial/images/2122723-middle.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/datastaxdevs/workshop-social-media-tiktok/master/tutorial/images/2122723-middle.png -------------------------------------------------------------------------------- /tutorial/images/2781017-middle.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/datastaxdevs/workshop-social-media-tiktok/master/tutorial/images/2781017-middle.png -------------------------------------------------------------------------------- /tutorial/images/access-swagger.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/datastaxdevs/workshop-social-media-tiktok/master/tutorial/images/access-swagger.png -------------------------------------------------------------------------------- /tutorial/images/netlify-empty.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/datastaxdevs/workshop-social-media-tiktok/master/tutorial/images/netlify-empty.png -------------------------------------------------------------------------------- /tutorial/images/netlify-signin.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/datastaxdevs/workshop-social-media-tiktok/master/tutorial/images/netlify-signin.png -------------------------------------------------------------------------------- /tutorial/images/netlify-start.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/datastaxdevs/workshop-social-media-tiktok/master/tutorial/images/netlify-start.png -------------------------------------------------------------------------------- /tutorial/images/netlify_login.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/datastaxdevs/workshop-social-media-tiktok/master/tutorial/images/netlify_login.png -------------------------------------------------------------------------------- /tutorial/images/swaggerui_link.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/datastaxdevs/workshop-social-media-tiktok/master/tutorial/images/swaggerui_link.png -------------------------------------------------------------------------------- /tutorial/images/create_astra_db.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/datastaxdevs/workshop-social-media-tiktok/master/tutorial/images/create_astra_db.png -------------------------------------------------------------------------------- /tutorial/images/db-create-button.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/datastaxdevs/workshop-social-media-tiktok/master/tutorial/images/db-create-button.png -------------------------------------------------------------------------------- /tutorial/images/deploy-to-netlify.gif: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/datastaxdevs/workshop-social-media-tiktok/master/tutorial/images/deploy-to-netlify.gif -------------------------------------------------------------------------------- /tutorial/images/netlify-livesite.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/datastaxdevs/workshop-social-media-tiktok/master/tutorial/images/netlify-livesite.png -------------------------------------------------------------------------------- /tutorial/images/netlify_functions.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/datastaxdevs/workshop-social-media-tiktok/master/tutorial/images/netlify_functions.png -------------------------------------------------------------------------------- /tutorial/images/react-components.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/datastaxdevs/workshop-social-media-tiktok/master/tutorial/images/react-components.png -------------------------------------------------------------------------------- /tutorial/images/tik-tok-logo-6fh.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/datastaxdevs/workshop-social-media-tiktok/master/tutorial/images/tik-tok-logo-6fh.png -------------------------------------------------------------------------------- /tutorial/images/tiktok-round-line.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/datastaxdevs/workshop-social-media-tiktok/master/tutorial/images/tiktok-round-line.png -------------------------------------------------------------------------------- /tutorial/images/astra-create-token.gif: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/datastaxdevs/workshop-social-media-tiktok/master/tutorial/images/astra-create-token.gif -------------------------------------------------------------------------------- /tutorial/images/correct-not-correct.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/datastaxdevs/workshop-social-media-tiktok/master/tutorial/images/correct-not-correct.png -------------------------------------------------------------------------------- /tutorial/images/create-collection-1.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/datastaxdevs/workshop-social-media-tiktok/master/tutorial/images/create-collection-1.png -------------------------------------------------------------------------------- /tutorial/images/db-create-keyspace.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/datastaxdevs/workshop-social-media-tiktok/master/tutorial/images/db-create-keyspace.png -------------------------------------------------------------------------------- /tutorial/images/list-collections-1.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/datastaxdevs/workshop-social-media-tiktok/master/tutorial/images/list-collections-1.png -------------------------------------------------------------------------------- /tutorial/images/list-collections-2.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/datastaxdevs/workshop-social-media-tiktok/master/tutorial/images/list-collections-2.png -------------------------------------------------------------------------------- /tutorial/images/netlify-install-cli.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/datastaxdevs/workshop-social-media-tiktok/master/tutorial/images/netlify-install-cli.png -------------------------------------------------------------------------------- /tutorial/images/netlify-createsite-01.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/datastaxdevs/workshop-social-media-tiktok/master/tutorial/images/netlify-createsite-01.png -------------------------------------------------------------------------------- /tutorial/images/netlify-createsite-02.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/datastaxdevs/workshop-social-media-tiktok/master/tutorial/images/netlify-createsite-02.png -------------------------------------------------------------------------------- /tutorial/images/netlify-createsite-03.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/datastaxdevs/workshop-social-media-tiktok/master/tutorial/images/netlify-createsite-03.png -------------------------------------------------------------------------------- /tutorial/images/netlify-createsite-04.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/datastaxdevs/workshop-social-media-tiktok/master/tutorial/images/netlify-createsite-04.png -------------------------------------------------------------------------------- /tutorial/images/netlify-deploycomplete.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/datastaxdevs/workshop-social-media-tiktok/master/tutorial/images/netlify-deploycomplete.png -------------------------------------------------------------------------------- /tutorial/images/netlify_endpoint_nav.gif: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/datastaxdevs/workshop-social-media-tiktok/master/tutorial/images/netlify_endpoint_nav.gif -------------------------------------------------------------------------------- /tutorial/images/netlify_functions_tab.gif: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/datastaxdevs/workshop-social-media-tiktok/master/tutorial/images/netlify_functions_tab.gif -------------------------------------------------------------------------------- /tutorial/images/netlify-deployinprogress.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/datastaxdevs/workshop-social-media-tiktok/master/tutorial/images/netlify-deployinprogress.png -------------------------------------------------------------------------------- /tutorial/images/swaggerui_listcollections_01.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/datastaxdevs/workshop-social-media-tiktok/master/tutorial/images/swaggerui_listcollections_01.png -------------------------------------------------------------------------------- /tutorial/images/swaggerui_listcollections_02.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/datastaxdevs/workshop-social-media-tiktok/master/tutorial/images/swaggerui_listcollections_02.png -------------------------------------------------------------------------------- /tutorial/images/swaggerui_listcollections_03.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/datastaxdevs/workshop-social-media-tiktok/master/tutorial/images/swaggerui_listcollections_03.png -------------------------------------------------------------------------------- /tutorial/images/swaggerui_listcollections_04.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/datastaxdevs/workshop-social-media-tiktok/master/tutorial/images/swaggerui_listcollections_04.png -------------------------------------------------------------------------------- /tutorial/images/swaggerui_searchdocuments_01.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/datastaxdevs/workshop-social-media-tiktok/master/tutorial/images/swaggerui_searchdocuments_01.png -------------------------------------------------------------------------------- /tutorial/images/swaggerui_searchdocuments_02.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/datastaxdevs/workshop-social-media-tiktok/master/tutorial/images/swaggerui_searchdocuments_02.png -------------------------------------------------------------------------------- /tutorial/images/swaggerui_searchdocuments_03.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/datastaxdevs/workshop-social-media-tiktok/master/tutorial/images/swaggerui_searchdocuments_03.png -------------------------------------------------------------------------------- /tutorial/images/swaggerui_searchdocuments_04.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/datastaxdevs/workshop-social-media-tiktok/master/tutorial/images/swaggerui_searchdocuments_04.png -------------------------------------------------------------------------------- /tutorial/images/tiktok_logo_png_transparent512.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/datastaxdevs/workshop-social-media-tiktok/master/tutorial/images/tiktok_logo_png_transparent512.png -------------------------------------------------------------------------------- /tutorial/images/2021_04 - SpringBoot, Stargate and SDK.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/datastaxdevs/workshop-social-media-tiktok/master/tutorial/images/2021_04 - SpringBoot, Stargate and SDK.png -------------------------------------------------------------------------------- /.idea/vcs.xml: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | -------------------------------------------------------------------------------- /.idea/misc.xml: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | -------------------------------------------------------------------------------- /.vscode/settings.json: -------------------------------------------------------------------------------- 1 | { 2 | "workbench.editor.enablePreviewFromCodeNavigation": true, 3 | "workbench.editor.enablePreviewFromQuickOpen": true, 4 | "workbench.editor.enablePreview": true, 5 | "workbench.editorAssociations": { 6 | "*.md": "vscode.markdown.preview.editor" 7 | } 8 | } 9 | -------------------------------------------------------------------------------- /.idea/modules.xml: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | 7 | 8 | -------------------------------------------------------------------------------- /.idea/workshop-social-media-tiktok.iml: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | 7 | 8 | 9 | -------------------------------------------------------------------------------- /src/components/MicroCard.js: -------------------------------------------------------------------------------- 1 | import React from 'react' 2 | 3 | const MicroCard = ({ user }) => { 4 | return ( 5 |
6 | 7 |
8 |

{user.username}

9 |

{user.name}

10 |
11 |
12 | ) 13 | } 14 | 15 | export default MicroCard -------------------------------------------------------------------------------- /src/index.css: -------------------------------------------------------------------------------- 1 | body { 2 | margin: 0; 3 | font-family: -apple-system, BlinkMacSystemFont, 'Segoe UI', 'Roboto', 'Oxygen', 4 | 'Ubuntu', 'Cantarell', 'Fira Sans', 'Droid Sans', 'Helvetica Neue', 5 | sans-serif; 6 | -webkit-font-smoothing: antialiased; 7 | -moz-osx-font-smoothing: grayscale; 8 | } 9 | 10 | code { 11 | font-family: source-code-pro, Menlo, Monaco, Consolas, 'Courier New', 12 | monospace; 13 | } 14 | -------------------------------------------------------------------------------- /astra.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "TikTok clone using React, JAMStack and AstraDB", 3 | "description": "A simple Tik-Tok clone running on AstraDB that leverages the Document API.", 4 | "duration": "50 minutes", 5 | "skillLevel": "Advanced", 6 | "githubUrl": "https://github.com/DataStax-Examples/astra-tik-tok", 7 | "tags": [ 8 | { "name": "javascript" }, 9 | { "name": "document API" } 10 | ], 11 | "demo":"https://fanciful-licorice-ea1437.netlify.app/", 12 | "category": "workshop", 13 | "priority": 1 14 | } 15 | -------------------------------------------------------------------------------- /functions/edit.js: -------------------------------------------------------------------------------- 1 | const { getCollection } = require("./utils/astraClient"); 2 | 3 | exports.handler = async function (event) { 4 | const users = await getCollection(); 5 | const body = JSON.parse(event.body); 6 | 7 | try { 8 | users.update(body.userId, body.data); 9 | return { 10 | statusCode: 200, 11 | headers: { 12 | 'Content-type': 'application/json', 13 | }, 14 | }; 15 | } catch (e) { 16 | console.error(e); 17 | return { 18 | statusCode: 500, 19 | body: JSON.stringify(e), 20 | }; 21 | } 22 | }; 23 | -------------------------------------------------------------------------------- /.github/ISSUE_TEMPLATE/homework-assignment.md: -------------------------------------------------------------------------------- 1 | --- 2 | name: Homework Assignment 3 | about: Use this template to submit the homework 4 | title: "[HW] " 5 | labels: homework, wait for review 6 | assignees: SonicDMG, RyanWelford, synedra 7 | 8 | --- 9 | 10 | **Name:** 11 | **Email:** 12 | **Linkedin Profile:** 13 | 14 | Attach the homework step I screenshots below: 15 | ----------------------------------------- 16 | 17 | 18 | 19 | Attach the homework step II screenshots below: 20 | ----------------------------------------- 21 | 22 | 23 | -------------------------------------------------------------------------------- /functions/posts.js: -------------------------------------------------------------------------------- 1 | const { getCollection } = require("./utils/astraClient"); 2 | 3 | exports.handler = async function () { 4 | const users = await getCollection(); 5 | try { 6 | const res = await users.find({}); 7 | return { 8 | statusCode: 200, 9 | body: JSON.stringify(Object.keys(res.data).map((i) => res.data[i])), 10 | headers: { 11 | 'Content-type': 'application/json', 12 | }, 13 | }; 14 | } catch (e) { 15 | console.error(e); 16 | return { 17 | statusCode: 500, 18 | body: JSON.stringify(e), 19 | }; 20 | } 21 | }; 22 | -------------------------------------------------------------------------------- /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 | "src": "logo192.png", 12 | "type": "image/png", 13 | "sizes": "192x192" 14 | }, 15 | { 16 | "src": "logo512.png", 17 | "type": "image/png", 18 | "sizes": "512x512" 19 | } 20 | ], 21 | "start_url": ".", 22 | "display": "standalone", 23 | "theme_color": "#000000", 24 | "background_color": "#ffffff" 25 | } 26 | -------------------------------------------------------------------------------- /src/components/Header.js: -------------------------------------------------------------------------------- 1 | import React from 'react' 2 | import { Link } from 'react-router-dom' 3 | 4 | const Header = () => { 5 | return ( 6 |
7 | 8 |
9 | 10 |
11 |
12 | 13 |
14 | 15 | 16 |
17 |
18 |
19 | ) 20 | } 21 | 22 | export default Header -------------------------------------------------------------------------------- /functions/add.js: -------------------------------------------------------------------------------- 1 | const faker = require("faker"); 2 | const { getCollection } = require("./utils/astraClient"); 3 | 4 | let id = faker.random.uuid(); 5 | 6 | exports.handler = async function (event) { 7 | const users = await getCollection(); 8 | try { 9 | const user = await users.create(id, event.body); 10 | return { 11 | statusCode: 200, 12 | body: JSON.stringify(user), 13 | headers: { 14 | 'Content-type': 'application/json', 15 | }, 16 | }; 17 | } catch (e) { 18 | console.error(e); 19 | return { 20 | statusCode: 500, 21 | body: JSON.stringify(e), 22 | }; 23 | } 24 | }; 25 | -------------------------------------------------------------------------------- /src/components/MiniCard.js: -------------------------------------------------------------------------------- 1 | import React from 'react' 2 | 3 | const MiniCard = ({ user, toggleFollow }) => { 4 | return ( 5 |
6 |
7 | 8 |
9 |

{user.username}

10 |

{user.name}

11 |
12 |
13 | {user.button_visible &&
toggleFollow(user)} 15 | > 16 | {user.is_followed? "Following": "Follow"} 17 |
} 18 |
19 | ) 20 | } 21 | 22 | export default MiniCard -------------------------------------------------------------------------------- /src/index.js: -------------------------------------------------------------------------------- 1 | import React from 'react' 2 | import ReactDOM from 'react-dom' 3 | import { HashRouter, Route , Switch} from 'react-router-dom' 4 | import Home from './pages/Home' 5 | import Upload from './pages/Upload' 6 | import Header from './components/Header' 7 | import "core-js/stable" 8 | import "regenerator-runtime/runtime" 9 | import './App.css' 10 | 11 | const App = () => { 12 | 13 | return ( 14 | 15 |
16 | 17 | 18 | 19 | 20 | 21 | ) 22 | } 23 | 24 | export default App 25 | 26 | ReactDOM.render( 27 | 28 | 29 | , 30 | document.getElementById('root') 31 | ) -------------------------------------------------------------------------------- /functions/utils/astraClient.js: -------------------------------------------------------------------------------- 1 | const { createClient } = require("@astrajs/collections"); 2 | 3 | let astraClient = null; 4 | 5 | const getAstraClient = async () => { 6 | if (astraClient === null) { 7 | astraClient = await createClient( 8 | { 9 | astraDatabaseId: process.env.ASTRA_DB_ID, 10 | astraDatabaseRegion: process.env.ASTRA_DB_REGION, 11 | applicationToken: process.env.ASTRA_DB_APPLICATION_TOKEN, 12 | }, 13 | 30000 14 | ); 15 | } 16 | return astraClient; 17 | }; 18 | 19 | const getCollection = async () => { 20 | const documentClient = await getAstraClient(); 21 | return documentClient 22 | .namespace(process.env.ASTRA_DB_KEYSPACE) 23 | .collection("tktkposts"); 24 | }; 25 | 26 | module.exports = { getAstraClient, getCollection }; 27 | -------------------------------------------------------------------------------- /src/components/FollowersColumn.js: -------------------------------------------------------------------------------- 1 | import React from 'react' 2 | import MicroCard from '../components/MicroCard' 3 | 4 | const FollowersColumn = (topFiveFollowing) => { 5 | const users = topFiveFollowing.users 6 | 7 | return ( 8 |
9 |
10 |
11 |

For You

12 |
13 |
14 |
15 |

Following

16 |
17 |
18 |

Your top accounts

19 | {users && users.map((user, index) => ( 20 | ))} 23 |
24 |
25 | ) 26 | } 27 | 28 | export default FollowersColumn -------------------------------------------------------------------------------- /.gitpod.yml: -------------------------------------------------------------------------------- 1 | tasks: 2 | - name: workshop-astra-tik-tok 3 | before: | 4 | printf 'export PATH="$HOME%s:$PATH"\n' "/.astra/cli" >> $HOME/.bashrc 5 | printf 'unset JAVA_TOOL_OPTIONS\n' >> $HOME/.bashrc 6 | curl -Ls "https://dtsx.io/get-astra-cli" | bash >> ./install.log 7 | cd /workspace/workshop-astra-tik-tok 8 | nvm install 16.13.0 9 | npm install -g npm@latest 10 | npm install -g netlify-cli 11 | npm install 12 | command: | 13 | source ~/.bashrc 14 | echo "workshop-astra-tik-tok gitpod ready - LET'S DO THIS!" 15 | gp open README.md 16 | astra 17 | github: 18 | prebuilds: 19 | master: true 20 | branches: true 21 | pullRequests: true 22 | pullRequestsFromForks: false 23 | addCheck: true 24 | addComment: false 25 | addBadge: true 26 | addLabel: false 27 | ports: 28 | - port: 8888 29 | onOpen: open-preview 30 | - port: 3000 31 | onOpen: ignore 32 | -------------------------------------------------------------------------------- /package.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "tik-tok-stargate", 3 | "version": "0.1.0", 4 | "private": true, 5 | "dependencies": { 6 | "@astrajs/collections": "0.3.4", 7 | "@testing-library/jest-dom": "^5.11.9", 8 | "@testing-library/react": "^11.2.5", 9 | "@testing-library/user-event": "^12.6.3", 10 | "axios": "^0.21.1", 11 | "faker": "^5.3.1", 12 | "react": "^17.0.1", 13 | "react-dom": "^17.0.1", 14 | "react-router-dom": "^5.2.0", 15 | "react-scripts": "4.0.2" 16 | }, 17 | "scripts": { 18 | "start": "react-scripts --openssl-legacy-provider start", 19 | "build": "react-scripts --openssl-legacy-provider build", 20 | "test": "react-scripts test", 21 | "eject": "react-scripts eject", 22 | "dev": "netlify dev" 23 | }, 24 | "eslintConfig": { 25 | "extends": [ 26 | "react-app", 27 | "react-app/jest" 28 | ] 29 | }, 30 | "browserslist": { 31 | "production": [ 32 | ">0.2%", 33 | "not dead", 34 | "not op_mini all" 35 | ], 36 | "development": [ 37 | "last 1 chrome version", 38 | "last 1 firefox version", 39 | "last 1 safari version" 40 | ] 41 | }, 42 | "devDependencies": { 43 | "netlify-cli": "^3.5.0" 44 | } 45 | } 46 | -------------------------------------------------------------------------------- /src/components/Card.js: -------------------------------------------------------------------------------- 1 | import React from 'react' 2 | 3 | const Card = ({ user, toggleFollow }) => { 4 | 5 | const timestamp = user.timestamp 6 | const timeStampReformat = timestamp.slice(2, 7) 7 | 8 | return ( 9 |
10 |
11 |
12 |
13 | 14 |
15 |
16 |

{user.username}

17 |

{user.name}

18 |

{timeStampReformat}

19 |
20 |

{user.caption}

21 |
22 |
23 | {user.button_visible &&
toggleFollow(user)}> 25 | {user.is_followed? "Following": "Follow"} 26 |
} 27 |
28 | 31 |
32 | 33 |
{user.likes}
34 | 35 |
{user.comments}
36 | 37 |
38 |
39 | ) 40 | } 41 | 42 | export default Card -------------------------------------------------------------------------------- /public/index.html: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | 7 | 8 | 12 | 13 | 14 | 18 | 19 | 28 | Stargate Tik-Tok App 29 | 30 | 31 | 32 |
33 | 43 | 44 | 45 | -------------------------------------------------------------------------------- /src/pages/Upload.js: -------------------------------------------------------------------------------- 1 | import React, { useState } from 'react' 2 | import axios from 'axios' 3 | import faker from "faker" 4 | 5 | const Upload = () => { 6 | const username = 'aniak100' 7 | const name = 'Ania Kubow' 8 | const avatar = 'https://i.imgur.com/QwZod6m.png' 9 | const [video, setVideo] = useState(null) 10 | const [caption, setCaption] = useState(null) 11 | const today = new Date() 12 | const timestamp = today.toISOString() 13 | 14 | let id = faker.random.uuid() 15 | 16 | const handleSubmit = async (e) => { 17 | e.preventDefault() 18 | 19 | const data = { 20 | id: id, 21 | name: name, 22 | username: username, 23 | avatar: avatar, 24 | is_followed: false, 25 | video: video, 26 | caption: caption, 27 | likes: 0, 28 | comments: 0, 29 | timestamp: timestamp, 30 | button_visible: false 31 | } 32 | 33 | axios.post('/.netlify/functions/add', data) 34 | .then((response) => { 35 | console.log(response) 36 | }) 37 | .catch((err) => { 38 | console.error(err) 39 | }) 40 | } 41 | 42 | return ( 43 |
44 |
45 |

Upload video

46 |

This video will be published to @{username}

47 |
48 |
49 |
50 |
51 |
52 |
53 | 54 | setCaption(e.target.value)} 58 | /> 59 |
60 |
61 |
62 | 63 | setVideo(e.target.value)} 67 | /> 68 |
69 |
70 |
71 | 72 |
73 |
74 |
75 | ) 76 | } 77 | 78 | export default Upload -------------------------------------------------------------------------------- /functions/addData.js: -------------------------------------------------------------------------------- 1 | const { getCollection } = require("./utils/astraClient"); 2 | 3 | exports.handler = async function () { 4 | const posts = await getCollection(); 5 | const data = [ 6 | { 7 | id: 0, 8 | name: "Mo Farooq", 9 | username: "mofarooq32", 10 | avatar: "https://i.imgur.com/9KYq7VG.png", 11 | is_followed: true, 12 | video: "https://i.imgur.com/FTBP02Y.mp4", 13 | caption: "These ducks are MEGA cute", 14 | likes: 10, 15 | comments: 2, 16 | timestamp: "2019-03-10T09:08:31.020Z", 17 | button_visible: true, 18 | }, 19 | { 20 | id: 1, 21 | name: "Tim Salowski", 22 | username: "timmytam", 23 | avatar: "https://i.imgur.com/rWYtZa6.png", 24 | is_followed: false, 25 | video: "https://i.imgur.com/1A7AKoF.mp4", 26 | caption: "When your fries give you attitude #getInMyBelly", 27 | likes: 12, 28 | comments: 2, 29 | timestamp: "2020-03-10T09:08:31.020Z", 30 | button_visible: true, 31 | }, 32 | { 33 | id: 2, 34 | name: "Angela Lee", 35 | username: "angiecakes", 36 | avatar: "https://i.imgur.com/eX3hkoc.png", 37 | is_followed: true, 38 | video: "https://i.imgur.com/al6MLay.mp4", 39 | caption: "Happiest of Birthdays my Angel", 40 | likes: 2, 41 | comments: 4, 42 | timestamp: "2020-04-10T09:08:31.020Z", 43 | button_visible: true, 44 | }, 45 | { 46 | id: 3, 47 | name: "Nina Xen", 48 | username: "nina_lina", 49 | avatar: "https://i.imgur.com/IigY4Hm.png", 50 | is_followed: false, 51 | video: "https://i.imgur.com/Kzvbeup.mp4", 52 | caption: "The new normal", 53 | likes: 10, 54 | comments: 2, 55 | timestamp: "2020-05-10T09:08:31.020Z", 56 | button_visible: true, 57 | }, 58 | { 59 | id: 0, 60 | name: "Lana Del Mont", 61 | username: "lana_del_away", 62 | avatar: "https://i.imgur.com/jONHmE5.png", 63 | is_followed: true, 64 | video: "https://i.imgur.com/H9UX0Jm.mp4", 65 | caption: "Art is for everyone", 66 | likes: 231, 67 | comments: 20, 68 | timestamp: "2020-09-10T09:08:31.020Z", 69 | button_visible: true, 70 | }, 71 | ]; 72 | 73 | try { 74 | for (let i = 0; i < data.length; i++) { 75 | await posts.create(data[i].id.toString(), data[i]); 76 | } 77 | 78 | return { 79 | statusCode: 200, 80 | headers: { 81 | 'Content-type': 'application/json', 82 | }, 83 | }; 84 | } catch (e) { 85 | console.error(e); 86 | return { 87 | statusCode: 500, 88 | body: JSON.stringify(e), 89 | }; 90 | } 91 | }; 92 | -------------------------------------------------------------------------------- /CONTRIBUTING.md: -------------------------------------------------------------------------------- 1 | # Contributing 2 | 3 | When contributing to this repository, please first discuss the change you wish to make via issue, 4 | email, or any other method with the owners of this repository before making a change. 5 | 6 | Please note we have a [Code of Conduct](CODE_OF_CONDUCT.md), please follow it in all your interactions with the project. 7 | 8 | ## Found an Issue? 9 | If you find a bug in the source code or a mistake in the documentation, you can help us by 10 | [submitting an issue](#submit-issue) to the GitHub Repository. Even better, you can 11 | [submit a Pull Request](#submit-pr) with a fix. 12 | 13 | ## Want a Feature? 14 | You can *request* a new feature by [submitting an issue](#submit-issue) to the GitHub 15 | Repository. If you would like to *implement* a new feature, please submit an issue with 16 | a proposal for your work first, to be sure that we can use it. 17 | 18 | * **Small Features** can be crafted and directly [submitted as a Pull Request](#submit-pr). 19 | 20 | ## Contribution Guidelines 21 | 22 | ### Submitting an Issue 23 | Before you submit an issue, search the archive, maybe your question was already answered. 24 | 25 | If your issue appears to be a bug, and hasn't been reported, open a new issue. 26 | Help us to maximize the effort we can spend fixing issues and adding new 27 | features, by not reporting duplicate issues. Providing the following information will increase the 28 | chances of your issue being dealt with quickly: 29 | 30 | * **Overview of the Issue** - if an error is being thrown a non-minified stack trace helps 31 | * **Motivation for or Use Case** - explain what are you trying to do and why the current behavior is a bug for you 32 | * **Reproduce the Error** - provide a live example or a unambiguous set of steps 33 | * **Suggest a Fix** - if you can't fix the bug yourself, perhaps you can point to what might be 34 | causing the problem (line of code or commit) 35 | 36 | ### Submitting a Pull Request (PR) 37 | Before you submit your Pull Request (PR) consider the following guidelines: 38 | 39 | * Search the repository (https://github.com/bechbd/[repository-name]/pulls) for an open or closed PR that relates to your submission. You don't want to duplicate effort. 40 | 41 | * Create a fork of the repo 42 | * Navigate to the repo you want to fork 43 | * In the top right corner of the page click **Fork**: 44 | ![](https://help.github.com/assets/images/help/repository/fork_button.jpg) 45 | 46 | * Make your changes in the forked repo 47 | * Commit your changes using a descriptive commit message 48 | * In GitHub, create a pull request: https://help.github.com/en/articles/creating-a-pull-request-from-a-fork 49 | * If we suggest changes then: 50 | * Make the required updates. 51 | * Rebase your fork and force push to your GitHub repository (this will update your Pull Request): 52 | 53 | ```shell 54 | git rebase master -i 55 | git push -f 56 | ``` 57 | 58 | That's it! Thank you for your contribution! -------------------------------------------------------------------------------- /src/pages/Home.js: -------------------------------------------------------------------------------- 1 | import React, { useState, useEffect } from 'react' 2 | import FollowersColumn from '../components/FollowersColumn' 3 | import Card from '../components/Card' 4 | import MiniCard from '../components/MiniCard' 5 | import axios from 'axios' 6 | 7 | const Home = () => { 8 | const [users, setUsers] = useState(null) 9 | const [userToToggle, setUserToToggle] = useState(null) 10 | 11 | let descendingUsers 12 | let topFiveNotFollowing 13 | let topFiveFollowing 14 | 15 | //auto populating with dummy data 16 | const addData = async () => { 17 | await axios.post('/.netlify/functions/addData') 18 | } 19 | 20 | //fetch all the tik-tok posts to your feed 21 | const fetchData = async () => { 22 | const results = await axios.get('/.netlify/functions/posts') 23 | console.log(results.data) 24 | setUsers(results.data) 25 | } 26 | 27 | //toggle user from followed to unfollowed 28 | if (userToToggle) { 29 | const newValue = userToToggle.is_followed ? false : true 30 | const data = {is_followed: newValue} 31 | 32 | axios.put('/.netlify/functions/edit', {userId: userToToggle.id, data: data }) 33 | .then(res => res.json()) 34 | .then(json => console.log(json)) 35 | .catch(err => console.error('error:' + err)) 36 | .then(() => fetchData()) 37 | setUserToToggle(null) 38 | } 39 | 40 | useEffect(() => { 41 | addData() 42 | fetchData() 43 | }, []) 44 | 45 | if (users) { 46 | descendingUsers = users.sort((a, b) => a.id < b.id ? 1 : -1) 47 | const following = users.filter(user => user.is_followed === true) 48 | const descendingFollowing = following.sort((a, b) => a.likes < b.likes ? 1 : -1) 49 | topFiveFollowing = descendingFollowing.slice(0, 5) 50 | 51 | const notFollowing = users.filter((user) => user.is_followed === false) 52 | const descendingNotFollowing = notFollowing.sort((a, b) => a.likes < b.likes ? 1 : -1) 53 | topFiveNotFollowing = descendingNotFollowing.slice(0, 5) 54 | } 55 | 56 | return ( 57 | <> 58 | {descendingUsers && ( 59 |
60 | 61 |
62 | {descendingUsers.map((descendingUser, index) => ( 63 | setUserToToggle(userToToggle)} 67 | /> 68 | ))} 69 |
70 |
71 |
72 |
73 |

Suggested accounts

74 |
75 | {topFiveNotFollowing.map((notFollowingUser, index) => ( 76 | setUserToToggle(userToToggle)} 79 | />) 80 | )} 81 |
82 |
83 |
84 |
85 | )} 86 | 87 | ) 88 | } 89 | 90 | export default Home -------------------------------------------------------------------------------- /CODE_OF_CONDUCT.md: -------------------------------------------------------------------------------- 1 | # Contributor Covenant Code of Conduct 2 | 3 | ## Our Pledge 4 | 5 | In the interest of fostering an open and welcoming environment, we as 6 | contributors and maintainers pledge to making participation in our project and 7 | our community a harassment-free experience for everyone, regardless of age, body 8 | size, disability, ethnicity, sex characteristics, gender identity and expression, 9 | level of experience, education, socio-economic status, nationality, personal 10 | appearance, race, religion, or sexual identity and orientation. 11 | 12 | ## Our Standards 13 | 14 | Examples of behavior that contributes to creating a positive environment 15 | include: 16 | 17 | * Using welcoming and inclusive language 18 | * Being respectful of differing viewpoints and experiences 19 | * Gracefully accepting constructive criticism 20 | * Focusing on what is best for the community 21 | * Showing empathy towards other community members 22 | 23 | Examples of unacceptable behavior by participants include: 24 | 25 | * The use of sexualized language or imagery and unwelcome sexual attention or 26 | advances 27 | * Trolling, insulting/derogatory comments, and personal or political attacks 28 | * Public or private harassment 29 | * Publishing others' private information, such as a physical or electronic 30 | address, without explicit permission 31 | * Other conduct which could reasonably be considered inappropriate in a 32 | professional setting 33 | 34 | ## Our Responsibilities 35 | 36 | Project maintainers are responsible for clarifying the standards of acceptable 37 | behavior and are expected to take appropriate and fair corrective action in 38 | response to any instances of unacceptable behavior. 39 | 40 | Project maintainers have the right and responsibility to remove, edit, or 41 | reject comments, commits, code, wiki edits, issues, and other contributions 42 | that are not aligned to this Code of Conduct, or to ban temporarily or 43 | permanently any contributor for other behaviors that they deem inappropriate, 44 | threatening, offensive, or harmful. 45 | 46 | ## Scope 47 | 48 | This Code of Conduct applies both within project spaces and in public spaces 49 | when an individual is representing the project or its community. Examples of 50 | representing a project or community include using an official project e-mail 51 | address, posting via an official social media account, or acting as an appointed 52 | representative at an online or offline event. Representation of a project may be 53 | further defined and clarified by project maintainers. 54 | 55 | ## Enforcement 56 | 57 | Instances of abusive, harassing, or otherwise unacceptable behavior may be 58 | reported by contacting the project team at . All 59 | complaints will be reviewed and investigated and will result in a response that 60 | is deemed necessary and appropriate to the circumstances. The project team is 61 | obligated to maintain confidentiality with regard to the reporter of an incident. 62 | Further details of specific enforcement policies may be posted separately. 63 | 64 | Project maintainers who do not follow or enforce the Code of Conduct in good 65 | faith may face temporary or permanent repercussions as determined by other 66 | members of the project's leadership. 67 | 68 | ## Attribution 69 | 70 | This Code of Conduct is adapted from the [Contributor Covenant][homepage], version 1.4, 71 | available at https://www.contributor-covenant.org/version/1/4/code-of-conduct.html 72 | 73 | [homepage]: https://www.contributor-covenant.org 74 | 75 | For answers to common questions about this code of conduct, see 76 | https://www.contributor-covenant.org/faq 77 | -------------------------------------------------------------------------------- /tutorial/images/tiktok-icon-black-1-logo.svg: -------------------------------------------------------------------------------- 1 | -------------------------------------------------------------------------------- /tutorial/images/chrome-logo.svg: -------------------------------------------------------------------------------- 1 | -------------------------------------------------------------------------------- /src/App.css: -------------------------------------------------------------------------------- 1 | @import url('https://fonts.googleapis.com/css2?family=Quicksand:wght@300;400;600&display=swap'); 2 | 3 | body { 4 | font-family: 'Quicksand', sans-serif; 5 | font-weight: 300; 6 | } 7 | 8 | .App { 9 | text-align: center; 10 | } 11 | 12 | h1 { 13 | font-size: 60px; 14 | line-height: 0; 15 | } 16 | 17 | h2 { 18 | font-size: 20px; 19 | line-height: 0; 20 | font-weight: 300; 21 | } 22 | 23 | h3 { 24 | font-size: 15px; 25 | font-weight: 300; 26 | line-height: 0; 27 | } 28 | 29 | .bold { 30 | font-weight: 600; 31 | } 32 | 33 | .red { 34 | color:#fe2c55; 35 | } 36 | 37 | p { 38 | color: grey; 39 | font-size: 12px; 40 | line-height: 0; 41 | } 42 | 43 | hr { 44 | display: block; 45 | height: 1px; 46 | border: 0; 47 | border-top: 1px solid rgb(227,227,228); 48 | } 49 | 50 | .section { 51 | display: flex; 52 | flex-direction: row; 53 | margin: 0; 54 | } 55 | 56 | .break { 57 | height: 10px; 58 | } 59 | 60 | /* Styling for Header Component*/ 61 | .header { 62 | height: 60px; 63 | width: 100%; 64 | border-bottom: solid 1px rgb(227,227,228); 65 | display: flex; 66 | flex-direction: row; 67 | justify-content: space-between; 68 | } 69 | .logo { 70 | width: 129px; 71 | height: 40px; 72 | background-image: url('https://i.imgur.com/ZZ75Unh.png'); 73 | background-size: 129px; 74 | margin: 5px; 75 | } 76 | 77 | .upload { 78 | width: 30px; 79 | height: 25px; 80 | margin-top: 15px; 81 | background-image: url('https://i.imgur.com/0MtQ9RF.png'); 82 | background-size: 30px; 83 | } 84 | 85 | .personal { 86 | width: 40px; 87 | height: 40px; 88 | background-color: purple; 89 | border-radius: 20px; 90 | margin: 5px 20px 0 20px; 91 | } 92 | 93 | /* Styling for Home Page */ 94 | .container { 95 | display: flex; 96 | flex-direction: row; 97 | justify-content: space-between; 98 | } 99 | 100 | .suggested-box { 101 | min-width: 320px; 102 | height: 600px; 103 | padding: 10px; 104 | } 105 | 106 | .suggested { 107 | width: 280px; 108 | height: 350px; 109 | border: 1px solid rgb(227,227,228); 110 | border-radius: 10px; 111 | padding: 20px 15px; 112 | } 113 | 114 | 115 | /* Styling for Upload Page */ 116 | .upload-page { 117 | margin-left: 30px; 118 | } 119 | 120 | .image-upload { 121 | width: 400px; 122 | height: 230px; 123 | background-color: rgb(22,24,35,0.03); 124 | border-radius: 10px; 125 | } 126 | 127 | .input-section { 128 | margin: 10px; 129 | width: 700px; 130 | display: flex; 131 | flex-direction: row; 132 | justify-content: space-between; 133 | } 134 | 135 | .form-section { 136 | margin-left: 20px; 137 | margin-top: 60px; 138 | } 139 | 140 | label { 141 | font-size: 40px; 142 | line-height: 0; 143 | } 144 | 145 | input { 146 | height: 45px; 147 | width: 500px; 148 | font-size: 35px; 149 | margin-top: -20px; 150 | } 151 | 152 | button { 153 | height: 30px; 154 | width: 100px; 155 | float: right; 156 | } 157 | 158 | 159 | /* Styling for Card Components */ 160 | .card { 161 | border-bottom: 1px solid rgb(227,227,228); 162 | width: 500px; 163 | height: 600px; 164 | } 165 | 166 | .minicard { 167 | margin: 10px 0 0 0; 168 | justify-content: space-between; 169 | } 170 | 171 | .microcard { 172 | margin: 10px 0 0 0; 173 | } 174 | 175 | .user-profile { 176 | width: 50px; 177 | height: 50px; 178 | background-color: red; 179 | border-radius: 25px; 180 | margin-right: 15px; 181 | } 182 | 183 | .social-tag { 184 | margin: 0 20px 0 5px; 185 | } 186 | 187 | .video { 188 | width: 270px; 189 | border-radius: 15px; 190 | margin-left: 70px; 191 | } 192 | 193 | .user-info { 194 | display: flex; 195 | flex-direction: row; 196 | width: 412px; 197 | } 198 | 199 | .username { 200 | margin-left: 10px; 201 | margin-right: 10px; 202 | } 203 | 204 | .socials { 205 | margin-left: 70px; 206 | } 207 | 208 | .follow-button { 209 | border: 1px solid #fe2c55; 210 | border-radius: 3px; 211 | width: 88px; 212 | height: 16px; 213 | color: #fe2c55; 214 | font-weight: 600; 215 | padding: 2px 0 3px; 216 | text-align: center; 217 | font-size: 14px; 218 | } 219 | 220 | .followed-button { 221 | border: 1px solid rgb(227,227,228); 222 | border-radius: 3px; 223 | width: 88px; 224 | height: 16px; 225 | font-weight: 600; 226 | padding: 2px 0 3px; 227 | text-align: center; 228 | font-size: 14px; 229 | } 230 | 231 | 232 | /* Styling for FollowersColumn Component */ 233 | .followers-column { 234 | min-width: 230px; 235 | height: 600px; 236 | padding: 10px; 237 | } 238 | 239 | .followers-section { 240 | display: flex; 241 | flex-direction: row; 242 | margin: 30px 0 30px 0; 243 | } 244 | 245 | .home { 246 | width: 30px; 247 | height: 29px; 248 | background-image: url('https://i.imgur.com/Q582Dp0.png'); 249 | background-size: 30px; 250 | margin-right: 15px; 251 | } 252 | 253 | .following { 254 | width: 30px; 255 | height: 29px; 256 | background-image: url('https://i.imgur.com/Aq7L3jv.png'); 257 | background-size: 30px; 258 | margin-right: 15px; 259 | } -------------------------------------------------------------------------------- /jamstack.md: -------------------------------------------------------------------------------- 1 | # 📚 What is the JAM Stack? 2 | ### Reading time: _~9 minutes_ 3 | 4 | ## 1. Introduction to the JAMStack 5 | 6 | > *Sources [JamStack.org](https://jamstack.org/) and [Netlify Documentation](https://www.netlify.com/jamstack/)* 7 | 8 | Jamstack is the new standard architecture for the web. Using Git workflows and modern build tools, pre-rendered content is served to a CDN and made dynamic through APIs and serverless functions. Technologies in the stack include JavaScript frameworks, Static Site Generators, Headless CMSs, and CDNs. 9 | 10 | ![ok](https://github.com/DataStax-Examples/battlestax/blob/master/tutorial/jamstack/jam-1.png?raw=true) 11 | 12 | **🔵 Javascript:** can represent any modern Javascript/Typescript framework like `React`, `Vue`, or `Angular` or even vanilla JS. 13 | 14 | 15 | 16 | 17 | 18 | 19 | 20 | 21 | 22 | 23 | 24 | 25 | 26 |
ReactJS (we will use it today)
Angular
VueJS
27 | 28 |
29 | 30 | **🔵 API:** is a backend serving real time data through either REST or GraphQL, endpoints to interact with fetch, ajax,axios 31 | 32 | 33 | 34 | 35 | 36 | 37 | 38 | 39 | 40 | 41 |
REST (we will use it today)
GraphQL
42 | 43 |
44 | 45 | **🔵 Markup:** can be either plain old HTML or static content like markdown. This is the nature of markup that will determine the static page generator technology 46 | 47 | 48 | 49 | 50 | 51 | 52 | 53 | 54 | 55 | 56 |
Vanilla HTML (we will use this today)
Markdown
57 | 58 |
59 | 60 | **🔵 Static page generator** 61 | 62 | They apply data and content to templates, and generate a view of a page which can be served to the visitors of a site. 63 | 64 | The greatest difference between a static site generator and a traditional web application stack, is that instead of waiting until a page is requested and then generating its view on demand each time, a static site generator does this in advance so that the view is ready to serve ahead of time. And it does so for every possible view of a site at build time. 65 | 66 | ![ok](https://github.com/DataStax-Examples/battlestax/blob/master/tutorial/ssg-flow.png?raw=true) 67 | 68 | It improves performance and high compatibility with its rendering. *Automates code splitting, image optimization, inlining critical styles, lazy-loading, prefetching resources, and more to ensure your site is fully optimized. No manual tuning required.* 69 | 70 | You can find a pretty exhaustive list [here](https://jamstack.org/generators/). 71 | 72 | **🔵 CDN:** : geographically distributed group of servers which work together to provide fast delivery of Internet content. 73 | 74 | A CDN allows for the quick transfer of assets needed for loading Internet content including HTML pages, javascript files, stylesheets, images, and videos. The popularity of CDN services continues to grow, and today the majority of web traffic is served through CDNs, including traffic from major sites like Facebook, Netflix, and Amazon. 75 | 76 | ![ok](https://github.com/DataStax-Examples/battlestax/blob/master/tutorial/jamstack/CDN.png?raw=true) 77 | 78 | 79 | **🔵 Logical Architecture** 80 | 81 | The core principles of **pre-rendering**, and **decoupling**, enable sites and applications to be delivered with greater confidence and resilience than ever before. 82 | 83 | ![ok](https://github.com/DataStax-Examples/battlestax/blob/master/tutorial/jamstack/jam-2.png?raw=true) 84 | 85 | **🔵 Pre-rendering:** 86 | 87 | ![ok](https://github.com/DataStax-Examples/battlestax/blob/master/tutorial/jamstack/jam-4.png?raw=true) 88 | 89 | **🔵 Decoupling:** 90 | 91 | ![ok](https://github.com/DataStax-Examples/battlestax/blob/master/tutorial/jamstack/jam-5.png?raw=true) 92 | 93 | **🔵 Move to CDN:** 94 | 95 | ![ok](https://github.com/DataStax-Examples/battlestax/blob/master/tutorial/jamstack/jam-3.png?raw=true) 96 | 97 | ## 2. Why this is cool ? 98 | 99 | ![.](https://github.com/DataStax-Examples/battlestax/blob/master/tutorial/line.png?raw=true) 100 | 101 | ### 🔐 2.1 - Security 102 | 103 | > *Do you remember `/wp-admin.php` ? Me too.* The elder Cedrick 104 | 105 | The Jamstack removes multiple moving parts and systems from the hosting infrastructure resulting in fewer servers and systems to harden against attack. 106 | 107 | Serving pages and assets as pre-generated files allows **read-only hosting** reducing attack vectors even further. Meanwhile dynamic tools and services can be provided by vendors with teams dedicated to securing their specific systems and providing high levels of service. 108 | 109 | ![.](https://github.com/DataStax-Examples/battlestax/blob/master/tutorial/line.png?raw=true) 110 | 111 | ### 🌐 2.2 - Scalalibility 112 | 113 | > *Have you ever Ddos Amazon CloudFront ? Neither have I* the elder Cedrick. 114 | 115 | When sites can be served entirely from a CDN there is no complex logic or workflow to determine what assets can be cached and when. 116 | 117 | With Jamstack sites everything can be cached in a content delivery network. With simpler deployments, built-in redundancy and **incredible load capacity.** 118 | 119 | ![.](https://github.com/DataStax-Examples/battlestax/blob/master/tutorial/line.png?raw=true) 120 | 121 | ### 🚀 2.3 - Performance 122 | 123 | ![ok](https://github.com/DataStax-Examples/battlestax/blob/master/tutorial/jamstack/slow.jpeg?raw=true) 124 | 125 | Page loading speeds have an impact on user experience and conversion. Jamstack sites remove the need to generate page views on a server at request time by instead generating pages ahead of time during a build. 126 | 127 | With all the pages are already available on a CDN close to the user and ready to serve, very high performance is possible without introducing expensive or complex infrastructure. 128 | 129 | ![.](https://github.com/DataStax-Examples/battlestax/blob/master/tutorial/line.png?raw=true) 130 | 131 | ### 🆘 2.4 - Maintainability 132 | 133 | ![ok](https://github.com/DataStax-Examples/battlestax/blob/master/tutorial/jamstack/maintenance.gif?raw=true) 134 | 135 | When hosting complexity is reduced, so are maintenance tasks. A pre-generated site, being served directly from a simple host or directly from a CDN does not need a team of experts to "keep the lights on". 136 | 137 | The work was done during the build, so now the generated site is stable and can be hosted without servers which might require patching, updating and maintain. 138 | 139 | ![.](https://github.com/DataStax-Examples/battlestax/blob/master/tutorial/line.png?raw=true) 140 | 141 | ### 📦 2.5 - Portability 142 | 143 | ![ok](https://github.com/DataStax-Examples/battlestax/blob/master/tutorial/jamstack/your-problem.png?raw=true) 144 | 145 | Jamstack sites are pre-generated. That means that you can host them from a wide variety of hosting services and have greater ability to move them to your preferred host. Any simple static hosting solution should be able to serve a Jamstack site. 146 | 147 | Bye-bye infrastructure lock-in. 148 | 149 | ### 💡 2.6 - Developer Experience 150 | 151 | ![ok](https://github.com/DataStax-Examples/battlestax/blob/master/tutorial/jamstack/dx.gif?raw=true) 152 | 153 | Let us show you this one today 154 | 155 | ## 3. Introduction to Netlify 156 | 157 | > *Source [https://www.netlify.com](https://www.netlify.com/)* 158 | 159 | ### 3.1 Netlify in a nutshell 160 | 161 | **Decoupling the frontend from the backend**: Unlike the large legacy apps, Jamstack projects neatly separate the frontend pages and UI from the backend apps and databases. Freed from backend servers, the frontend can then be deployed globally, directly to a CDN. 162 | 163 | **Dynamic content via APIs**: The global frontend uses Javascript and APIs to talk to backend services, allowing pages to be enhanced and personalized. 164 | 165 | *overview of netlify* 166 | ![ok](https://github.com/DataStax-Examples/battlestax/blob/master/tutorial/netlify.png?raw=true) 167 | 168 | ### 3.2 Soooooooooo What about today ? 169 | 170 | - We will have our code in GITHUB. 171 | 172 | - Netlify is integrated with GITHUB, on a new commit it will BUILD and deploy the SITE on the CDN. 173 | 174 | - To code and commit to github we will use an IDE. You can use your own or we will provide you one in the Cloud called GITPOD. 175 | 176 | - The application we will deploy is BattleStax. This is not only static content but also a REST API to retrieve Data from a DB. The DB is DataStax Astra. 177 | 178 | 179 | ![Template CTRL click](https://github.com/DataStax-Examples/battlestax/blob/master/tutorial/architecture1.png?raw=true) 180 | 181 | ## 4. Want to learn more ? 182 | 183 | 184 | **Angular vs VueJS vs React** 185 | 1. 🎥 [Angular vs React vs Vue [2020 Update]](https://www.youtube.com/watch?v=lYWYWyX04JI) 186 | 2. 🎥 [React vs. Angular vs. Vue: Which Should You Choose](https://www.youtube.com/watch?v=xDhzYQ4VyCw) 187 | 1. 📄 [Angular vs Vue vs React: choosing the best framework in 2020](https://www.educative.io/blog/react-angular-vue-comparison) 188 | 2. 📄 [React vs Angular vs Vue.js — What to choose in 2020? (updated in 2020)](https://medium.com/techmagic/reactjs-vs-angular5-vs-vue-js-what-to-choose-in-2018-b91e028fa91d) 189 | 3. 📄 [Angular vs React vs Vue 2020](https://athemes.com/guides/angular-vs-react-vs-vue/) 190 | 191 | **Static site Generator (SSG)** 192 | 1. 📄 [What is a Static Site Generator? And 3 ways to find the best one](https://www.netlify.com/blog/2020/04/14/what-is-a-static-site-generator-and-3-ways-to-find-the-best-one/) 193 | 2. 📄 [List of static generators](https://jamstack.org/generators/) 194 | 195 | 196 | **Content Delivery Network (CDN)** 197 | 1. 🎥 [What is Content Delivery Network, by Ryan Sumner (4:32 min)](https://www.youtube.com/watch?v=Bsq5cKkS33I) 198 | 2. 🎥 [What is a CDN and why Developers should Care about using one (GOTO 2016) by Artur Bergman (32:10 min)](https://www.youtube.com/watch?v=farO15_0NUQ) -------------------------------------------------------------------------------- /LICENSE.md: -------------------------------------------------------------------------------- 1 | Apache License 2 | Version 2.0, January 2004 3 | http://www.apache.org/licenses/ 4 | 5 | TERMS AND CONDITIONS FOR USE, REPRODUCTION, AND DISTRIBUTION 6 | 7 | 1. Definitions. 8 | 9 | "License" shall mean the terms and conditions for use, reproduction, 10 | and distribution as defined by Sections 1 through 9 of this document. 11 | 12 | "Licensor" shall mean the copyright owner or entity authorized by 13 | the copyright owner that is granting the License. 14 | 15 | "Legal Entity" shall mean the union of the acting entity and all 16 | other entities that control, are controlled by, or are under common 17 | control with that entity. For the purposes of this definition, 18 | "control" means (i) the power, direct or indirect, to cause the 19 | direction or management of such entity, whether by contract or 20 | otherwise, or (ii) ownership of fifty percent (50%) or more of the 21 | outstanding shares, or (iii) beneficial ownership of such entity. 22 | 23 | "You" (or "Your") shall mean an individual or Legal Entity 24 | exercising permissions granted by this License. 25 | 26 | "Source" form shall mean the preferred form for making modifications, 27 | including but not limited to software source code, documentation 28 | source, and configuration files. 29 | 30 | "Object" form shall mean any form resulting from mechanical 31 | transformation or translation of a Source form, including but 32 | not limited to compiled object code, generated documentation, 33 | and conversions to other media types. 34 | 35 | "Work" shall mean the work of authorship, whether in Source or 36 | Object form, made available under the License, as indicated by a 37 | copyright notice that is included in or attached to the work 38 | (an example is provided in the Appendix below). 39 | 40 | "Derivative Works" shall mean any work, whether in Source or Object 41 | form, that is based on (or derived from) the Work and for which the 42 | editorial revisions, annotations, elaborations, or other modifications 43 | represent, as a whole, an original work of authorship. For the purposes 44 | of this License, Derivative Works shall not include works that remain 45 | separable from, or merely link (or bind by name) to the interfaces of, 46 | the Work and Derivative Works thereof. 47 | 48 | "Contribution" shall mean any work of authorship, including 49 | the original version of the Work and any modifications or additions 50 | to that Work or Derivative Works thereof, that is intentionally 51 | submitted to Licensor for inclusion in the Work by the copyright owner 52 | or by an individual or Legal Entity authorized to submit on behalf of 53 | the copyright owner. For the purposes of this definition, "submitted" 54 | means any form of electronic, verbal, or written communication sent 55 | to the Licensor or its representatives, including but not limited to 56 | communication on electronic mailing lists, source code control systems, 57 | and issue tracking systems that are managed by, or on behalf of, the 58 | Licensor for the purpose of discussing and improving the Work, but 59 | excluding communication that is conspicuously marked or otherwise 60 | designated in writing by the copyright owner as "Not a Contribution." 61 | 62 | "Contributor" shall mean Licensor and any individual or Legal Entity 63 | on behalf of whom a Contribution has been received by Licensor and 64 | subsequently incorporated within the Work. 65 | 66 | 2. Grant of Copyright License. Subject to the terms and conditions of 67 | this License, each Contributor hereby grants to You a perpetual, 68 | worldwide, non-exclusive, no-charge, royalty-free, irrevocable 69 | copyright license to reproduce, prepare Derivative Works of, 70 | publicly display, publicly perform, sublicense, and distribute the 71 | Work and such Derivative Works in Source or Object form. 72 | 73 | 3. Grant of Patent License. Subject to the terms and conditions of 74 | this License, each Contributor hereby grants to You a perpetual, 75 | worldwide, non-exclusive, no-charge, royalty-free, irrevocable 76 | (except as stated in this section) patent license to make, have made, 77 | use, offer to sell, sell, import, and otherwise transfer the Work, 78 | where such license applies only to those patent claims licensable 79 | by such Contributor that are necessarily infringed by their 80 | Contribution(s) alone or by combination of their Contribution(s) 81 | with the Work to which such Contribution(s) was submitted. If You 82 | institute patent litigation against any entity (including a 83 | cross-claim or counterclaim in a lawsuit) alleging that the Work 84 | or a Contribution incorporated within the Work constitutes direct 85 | or contributory patent infringement, then any patent licenses 86 | granted to You under this License for that Work shall terminate 87 | as of the date such litigation is filed. 88 | 89 | 4. Redistribution. You may reproduce and distribute copies of the 90 | Work or Derivative Works thereof in any medium, with or without 91 | modifications, and in Source or Object form, provided that You 92 | meet the following conditions: 93 | 94 | (a) You must give any other recipients of the Work or 95 | Derivative Works a copy of this License; and 96 | 97 | (b) You must cause any modified files to carry prominent notices 98 | stating that You changed the files; and 99 | 100 | (c) You must retain, in the Source form of any Derivative Works 101 | that You distribute, all copyright, patent, trademark, and 102 | attribution notices from the Source form of the Work, 103 | excluding those notices that do not pertain to any part of 104 | the Derivative Works; and 105 | 106 | (d) If the Work includes a "NOTICE" text file as part of its 107 | distribution, then any Derivative Works that You distribute must 108 | include a readable copy of the attribution notices contained 109 | within such NOTICE file, excluding those notices that do not 110 | pertain to any part of the Derivative Works, in at least one 111 | of the following places: within a NOTICE text file distributed 112 | as part of the Derivative Works; within the Source form or 113 | documentation, if provided along with the Derivative Works; or, 114 | within a display generated by the Derivative Works, if and 115 | wherever such third-party notices normally appear. The contents 116 | of the NOTICE file are for informational purposes only and 117 | do not modify the License. You may add Your own attribution 118 | notices within Derivative Works that You distribute, alongside 119 | or as an addendum to the NOTICE text from the Work, provided 120 | that such additional attribution notices cannot be construed 121 | as modifying the License. 122 | 123 | You may add Your own copyright statement to Your modifications and 124 | may provide additional or different license terms and conditions 125 | for use, reproduction, or distribution of Your modifications, or 126 | for any such Derivative Works as a whole, provided Your use, 127 | reproduction, and distribution of the Work otherwise complies with 128 | the conditions stated in this License. 129 | 130 | 5. Submission of Contributions. Unless You explicitly state otherwise, 131 | any Contribution intentionally submitted for inclusion in the Work 132 | by You to the Licensor shall be under the terms and conditions of 133 | this License, without any additional terms or conditions. 134 | Notwithstanding the above, nothing herein shall supersede or modify 135 | the terms of any separate license agreement you may have executed 136 | with Licensor regarding such Contributions. 137 | 138 | 6. Trademarks. This License does not grant permission to use the trade 139 | names, trademarks, service marks, or product names of the Licensor, 140 | except as required for reasonable and customary use in describing the 141 | origin of the Work and reproducing the content of the NOTICE file. 142 | 143 | 7. Disclaimer of Warranty. Unless required by applicable law or 144 | agreed to in writing, Licensor provides the Work (and each 145 | Contributor provides its Contributions) on an "AS IS" BASIS, 146 | WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or 147 | implied, including, without limitation, any warranties or conditions 148 | of TITLE, NON-INFRINGEMENT, MERCHANTABILITY, or FITNESS FOR A 149 | PARTICULAR PURPOSE. You are solely responsible for determining the 150 | appropriateness of using or redistributing the Work and assume any 151 | risks associated with Your exercise of permissions under this License. 152 | 153 | 8. Limitation of Liability. In no event and under no legal theory, 154 | whether in tort (including negligence), contract, or otherwise, 155 | unless required by applicable law (such as deliberate and grossly 156 | negligent acts) or agreed to in writing, shall any Contributor be 157 | liable to You for damages, including any direct, indirect, special, 158 | incidental, or consequential damages of any character arising as a 159 | result of this License or out of the use or inability to use the 160 | Work (including but not limited to damages for loss of goodwill, 161 | work stoppage, computer failure or malfunction, or any and all 162 | other commercial damages or losses), even if such Contributor 163 | has been advised of the possibility of such damages. 164 | 165 | 9. Accepting Warranty or Additional Liability. While redistributing 166 | the Work or Derivative Works thereof, You may choose to offer, 167 | and charge a fee for, acceptance of support, warranty, indemnity, 168 | or other liability obligations and/or rights consistent with this 169 | License. However, in accepting such obligations, You may act only 170 | on Your own behalf and on Your sole responsibility, not on behalf 171 | of any other Contributor, and only if You agree to indemnify, 172 | defend, and hold each Contributor harmless for any liability 173 | incurred by, or claims asserted against, such Contributor by reason 174 | of your accepting any such warranty or additional liability. 175 | 176 | END OF TERMS AND CONDITIONS 177 | 178 | APPENDIX: How to apply the Apache License to your work. 179 | 180 | To apply the Apache License to your work, attach the following 181 | boilerplate notice, with the fields enclosed by brackets "[]" 182 | replaced with your own identifying information. (Don't include 183 | the brackets!) The text should be enclosed in the appropriate 184 | comment syntax for the file format. We also recommend that a 185 | file or class name and description of purpose be included on the 186 | same "printed page" as the copyright notice for easier 187 | identification within third-party archives. 188 | 189 | Copyright [yyyy] [name of copyright owner] 190 | 191 | Licensed under the Apache License, Version 2.0 (the "License"); 192 | you may not use this file except in compliance with the License. 193 | You may obtain a copy of the License at 194 | 195 | http://www.apache.org/licenses/LICENSE-2.0 196 | 197 | Unless required by applicable law or agreed to in writing, software 198 | distributed under the License is distributed on an "AS IS" BASIS, 199 | WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 200 | See the License for the specific language governing permissions and 201 | limitations under the License. 202 | -------------------------------------------------------------------------------- /tutorial/images/firefox-logo.svg: -------------------------------------------------------------------------------- 1 | firefox-logo -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | 2 | # Astra DB TikTok Clone Workshop 3 | 4 | [![Gitpod ready-to-code](https://img.shields.io/badge/Gitpod-ready--to--code-blue?logo=gitpod)](https://github.com/datastaxdevs/workshop-social-media-tiktok) 5 | [![License Apache2](https://img.shields.io/hexpm/l/plug.svg)](http://www.apache.org/licenses/LICENSE-2.0) 6 | [![Discord](https://img.shields.io/discord/685554030159593522)](https://discord.com/widget?id=685554030159593522&theme=dark) 7 | 8 | ⏲️ **Duration :** 2 hours 9 | 10 | 🎓 **Level** Beginner to Intermediate 11 | 12 | A simple Tik-Tok clone running on Astra DB that leverages the Document API. You can see the Demo [here](https://fanciful-licorice-ea1437.netlify.app) 13 | 14 | ![image](./screenshot.jpg) 15 | 16 | > [🔖 Accessing HANDS-ON](#-start-hands-on) 17 | 18 | ## 📋 Table of contents 19 | 20 | - **HouseKeeping** 21 | - [1. Objectives](#1---objectives) 22 | - [2. Frequently asked questions](#2---frequently-asked-questions) 23 | - [3. Materials for the Session](#3---materials-for-the-session) 24 | - [4. Homework](#4---homework) 25 | - **[LAB 1 - Getting Started with Database](#lab-1---getting-started-with-database)** 26 | - [1.1 - Create Astra Account](#11---getting-started-with-database) 27 | - [1.2 - Create Astra Database](#11---getting-started-with-database) 28 | - **[LAB 2 - Document API](#lab-2---document-api)** 29 | - [2.1 - Using Document API](#21-using-document-api) 30 | - [2.2 - Create Astra Credentials](#22---create-astra-credentials) 31 | - **[LAB 3 - IDE Setup](#lab-3---ide-setup)** 32 | - [3.1 - Netlify site setup](#31---netlify-site-setup) 33 | - [3.2 - Launch GitPod](#32---launch-gitpod) 34 | - [3.3 - Astra CLI Setup](#33---astra-cli-setup) 35 | - [3.4 - Netlify CLI Setup](#34---netlify-cli-setup) 36 | - [3.5 - Launch the application](#35-launch-your-app) 37 | - **[LAB 4 - Coding Against DB](#lab-4---coding-against-db)** 38 | - [4.1 - Document API](#41---document-api) 39 | - [4.2 - AstraJS Client](#42---astrajs-client) 40 | - [4.3 - Serverless configuration](#43---serverless-configuration) 41 | - [4.4 - React Component](#44---react-component) 42 | - **[LAB 5 - Netlify Deployments](#lab-5---netlify-deployments)** 43 | - [5.1 - Connect Netlify to your site](#51---connect-netlify-to-your-site) 44 | - [5.2 - Deploy to production](#52---deploy-to-production) 45 | 46 | ## Housekeeping 47 | 48 | ### 1 - Objectives 49 | 50 | - 🎯 How to build a frontend application using React Components 51 | 52 | - 🎯 How to build a backend with Javascript without Express. 53 | 54 | - 🎯 Learn how to use the @astrajs document API to quickly and easily interact with JSON documents 55 | 56 | - 🎯 Learn what is gitpod and how you can use it in your projects 57 | 58 | ### 2 - Frequently asked questions 59 | 60 |

61 |

62 | 1️⃣ Can I run this workshop on my computer? 63 |
64 |

There is nothing preventing you from running the workshop on your own machine. If you do so, you will need the following: 65 |

    66 |
  1. git installed on your local system 67 |
  2. [node 15 and npm 7 or later](https://www.whitesourcesoftware.com/free-developer-tools/blog/update-node-js/) 68 |
69 |

70 | In this readme, we try to provide instructions for local development as well - but keep in mind that the main focus is development on Gitpod, hence we can't guarantee live support about local development in order to keep on track with the schedule. However, we will do our best to give you the info you need to succeed. 71 |
72 |

73 |

74 | 2️⃣ What other prerequisites are there? 75 |
76 |
    77 |
  • You will need an Github account 78 |
  • You will need an Astra account don't worry, we'll work through that in the workshop 79 |
  • You will need enough real estate on screen, we will ask you to open a few windows and it would not fit on mobiles (tablets should be OK) 80 | 81 |
82 |

83 |
84 |

85 |

86 | 3️⃣ Do I need to pay for anything for this workshop? 87 |
88 | No. All tools and services we provide here are FREE. FREE not only during the session but also after. 89 |
90 |

91 |

92 | 4️⃣ Will I get a certificate if I attend this workshop? 93 |
94 | Attending the session is not enough. You need to complete the homework detailed below and you will get a nice badge that you can share on linkedin or anywhere else *(open badge specification)* 95 |
96 |

97 | 98 | ### 3 - Materials for the Session 99 | 100 | It doesn't matter if you join our workshop live or you prefer to do at your own pace, we have you covered. In this repository, you'll find everything you need for this workshop: 101 | 102 | - [Slide deck](./slides/slides.pdf) 103 | - [Discord chat](https://bit.ly/cassandra-workshop) 104 | - [Questions and Answers](https://community.datastax.com/) 105 | - [What is JamStack?](jamstack.md) 106 | - [Video tutorial with Ania Kubow](#video-tutorial-with-ania-kubow) 107 | 108 | ### 4 - Homework 109 | 110 | 111 | 112 | Don't forget to complete your work and get your verified skill badge! Finish and submit your homework! 113 | 114 | 1. Complete the practice steps from this repository as described below. Take a screenshot and show us your deployed production TikTok clone up in Netlify. 115 | 2. (Optional extra credit) Watch the 2 hour Ania video [HERE](#video-tutorial-with-ania-kubow), build the app yourself, and show us the completed app. 116 | 3. Submit your homework [here](https://docs.google.com/forms/d/1BV5qJstc2Z8CV4XamolOLe5UjuDFoIunbMgpi4_iiys) 117 | 118 | That's it, you are done! Expect an email next week! 119 | 120 | # 🏁 Start Hands-on 121 | 122 | ## LAB 1 - Getting Started with Database 123 | 124 | ### 1.1 - Getting Started with Database 125 | 126 | _**`ASTRA`** is the simplest way to run both Cassandra and Pulsar with zero operations at all - just push the button and get your clusters. No credit card required_ 127 | 128 | #### `✅.1.1.a`- Create Astra Account 129 | 130 | > ↗️ _Right Click and select open as a new Tab..._ 131 | > 132 | > [![image](./tutorial/images/create_astra_db.png?raw=true)](https://astra.dev/yt-12-14) 133 | 134 | #### `✅.1.1.b`- Create Astra Database 135 | 136 | - On the home page spot one of the 3 [Create Database] button. They are all doing the same thing. 137 | 138 | ![image](./tutorial/images/db-create-button.png?raw=true) 139 | 140 | - Use the following values when creating the database: 141 | 142 | | # | Field Name | Field Value | 143 | |----|--------------------|---------------------------------| 144 | | 1 | **Database Name** | `workshops` | 145 | | 2 | **Keyspace Name** | `tiktok_keyspace` | 146 | | 3 | **Cloud Provider** | `Google Cloud Plaform` | 147 | | 4 | **Area** | `North America` | 148 | | 5 | **Region** | `Moncks Corner, South Carolina` | 149 | 150 | > 🖥️ `Output` 151 | > 152 | > ![image](./tutorial/images/db-create.png?raw=true) 153 | 154 | - Now click `[Create Database button]` that became blue. 155 | - The database is initializing, wait for the DB the move from `[PENDING]` to `[ACTIVE]` 156 | 157 | > 🖥️ `Output` 158 | > 159 | > ![image](./tutorial/images/db-pending.png?raw=true) 160 | 161 | - Click `[Go to Database]` 162 | 163 | #### `✅.1.1.c`- Database was already existing 164 | 165 | Here are some special situations you might encounter. 166 | 167 | > ℹ️ **Hibernated Database:** 168 | > 169 | > If the DB workshops already exists but is hibernated (you have not use it for a while): 170 | > - Click the `[Resume Database]` button on the top right-hand corner. 171 | > 172 | > ![image](./tutorial/images/db-resume.png?raw=true) 173 | 174 | > ℹ️ **Database already exists, create keyspace `tiktok_keyspace`** 175 | > 176 | > If DB exists and is active but `tiktok_keyspace` keyspace is not present you need to create the keyspace. 177 | > 178 | > - Click button `[Add Keyspace]` on the database dashboard (bottom right-hand corner) 179 | > - Provide the keyspace name `tiktok_keyspace` 180 | > - Click button `[Save]`. The database will switch in maintenance mode for a few seconds, you are set. 181 | > 182 | > ![image](./tutorial/images/db-create-keyspace.png?raw=true) 183 | 184 | ## LAB 2 - Document API 185 | 186 | ### 2.1 Using Document API 187 | 188 | #### `✅.2.1.a`- Open Swagger User Interface 189 | 190 | - (1) - Select your database in the left panel (if needed) 191 | - (2) - Select the tab `Connect` 192 | - (3) - Select the `Document API` bloc 193 | - (4) - In `Launch Swagger UI` right-click on the link to open in a new TAB 194 | 195 | ![image](tutorial/images/access-swagger.png?raw=true) 196 | 197 | - You should access this screen 198 | 199 | > 🖥️ `Swagger UI` 200 | > 201 | > ![image](tutorial/images/show-swagger.png?raw=true) 202 | 203 | #### `✅.2.1.b`- Lists Collections 204 | 205 | - (1) - Select the resource `GET/v2/namespaces/{namespace-id}/collections` 206 | 207 | ![image](tutorial/images/list-collections-1.png?raw=true) 208 | 209 | - (2) - Click the `[Try It Out]` button 210 | 211 | ![image](tutorial/images/list-collections-2.png?raw=true) 212 | 213 | - (3) - Populate the form with the following values 214 | 215 | |Field| Value| 216 | |---|---| 217 | |**X-Cassandra-Token**| _autopopulated_ | 218 | |**namespace-id**| `tiktok_keyspace` | 219 | 220 | - (4) - Click on `[Execute]` button 221 | 222 | The output is empty (expected): 223 | 224 | ```json 225 | [] 226 | ``` 227 | 228 | #### `✅.2.1.c`- Create Collection `story` 229 | 230 | - (1) - Select the resource `POST/v2/namespaces/{namespace-id}/collections` 231 | 232 | - (2) - Click the `[Try It Out]` button 233 | 234 | - (3) - Populate the form with the following values 235 | 236 | |Field| Value| 237 | |---|---| 238 | |**X-Cassandra-Token**| _autopopulated_ | 239 | |**namespace-id**| `tiktok_keyspace` | 240 | |**body**| `{"name":"story"}` | 241 | 242 | - (4) - Click on `[Execute]` button 243 | 244 | - (5) - You can see the output with `201` (created) code 245 | 246 | ![image](tutorial/images/create-collection-1.png?raw=true) 247 | 248 | - (6) - Following the steps in previous section list collections again, you should get 249 | 250 | > 🖥️ `Output` 251 | > ```json 252 | > { 253 | > "data": [ 254 | > { 255 | > "name": "story", 256 | > "upgradeAvailable": false 257 | > } 258 | > ] 259 | >} 260 | >``` 261 | 262 | #### `✅.2.1.d`- Create a first document 263 | 264 | With a document oriented API there is no strict schema to comply with. As such let us decide what a story could look like. 265 | 266 | - (1) - Select the resource `POST/v2/namespaces/{namespace-id}/collections/{collection-id}` _Create a new Document_ 267 | 268 | - (2) - Click the `[Try It Out]` button 269 | 270 | - (3) - Populate the form with the following values 271 | 272 | |Field| Value| 273 | |---|---| 274 | |**X-Cassandra-Token**| _autopopulated_ | 275 | |**namespace-id**| `tiktok_keyspace` | 276 | |**collection-id**| `story` | 277 | 278 | **body:** 279 | ```json 280 | { 281 | "name": "Mo Farooq", 282 | "username": "mofarooq32", 283 | "avatar": "https://i.imgur.com/9KYq7VG.png", 284 | "is_followed": true, 285 | "video": "https://i.imgur.com/FTBP02Y.mp4", 286 | "caption": "These ducks are MEGA cute", 287 | "likes": 10, 288 | "comments": 2, 289 | "button_visible": true 290 | } 291 | ``` 292 | 293 | - (4) - Click on `[Execute]` button 294 | 295 | - (5) - Should get a HTTP `201` (Created) and the output. A unique identifier has been created for our document. 296 | 297 | > 🖥️ `Output` 298 | > ```json 299 | > { 300 | > "documentId": "8aa07632-4ffb-46e5-9d78-b32e21847221" 301 | > } 302 | > ``` 303 | 304 | #### `✅.2.1.e`- Search documents in a collections 305 | 306 | - (1) - Select the resource `GET /v2/namespaces/{namespace-id}/collections/{collection-id}` _Search documents in a collection_ 307 | 308 | - (2) - Click the `[Try It Out]` button 309 | 310 | - (3) - Populate the form with the following values 311 | 312 | |Field| Value| 313 | |---|---| 314 | |**X-Cassandra-Token**| _autopopulated_ | 315 | |**namespace-id**| `tiktok_keyspace` | 316 | |**collection-id**| `story` | 317 | |**where**| `{"username": {"$eq": "mofarooq32"}}` | 318 | 319 | - (4) - Click on `[Execute]` button 320 | 321 | - (5) - You should get a result 322 | 323 | > 🖥️ `Output` 324 | > ```json 325 | > { 326 | > "data": { 327 | > "8aa07632-4ffb-46e5-9d78-b32e21847221": { 328 | > "avatar": "https://i.imgur.com/9KYq7VG.png", 329 | > "button_visible": true, 330 | > "caption": "These ducks are MEGA cute", 331 | > "comments": 2, 332 | > "is_followed": true, 333 | > "likes": 10, 334 | > "name": "Mo Farooq", 335 | > "username": "mofarooq32", 336 | > "video": "https://i.imgur.com/FTBP02Y.mp4" 337 | > } 338 | > } 339 | >} 340 | >``` 341 | > 342 | 343 | #### `✅.2.1.f`- Update a document 344 | 345 | - (1) - Select the resource `PUT /v2/namespaces/{namespace-id}/collections/{collection-id}/{document-id}` _Create or update a document with the provided document-id_ 346 | 347 | - (2) - Click the `[Try It Out]` button 348 | 349 | - (3) - Populate the form with the following values 350 | 351 | |Field| Value| 352 | |---|---| 353 | |**X-Cassandra-Token**| _autopopulated_ | 354 | |**namespace-id**| `tiktok_keyspace` | 355 | |**collection-id**| `story` | 356 | |**document-id**| document you got before here `8aa07632-4ffb-46e5-9d78-b32e21847221` | 357 | 358 | **body:** 359 | ```json 360 | { 361 | "name": "New Name", 362 | "username": "mofarooq32", 363 | "avatar": "https://i.imgur.com/9KYq7VG.png", 364 | "is_followed": true, 365 | "video": "https://i.imgur.com/FTBP02Y.mp4", 366 | "caption": "These ducks are MEGA cute", 367 | "likes": 10, 368 | "comments": 2, 369 | "button_visible": true 370 | } 371 | ``` 372 | 373 | - (4) - Click on `[Execute]` button 374 | 375 | - (5) - You should get an updated document 376 | 377 | #### `✅.2.1.g`- Delete a document 378 | 379 | - (1) - Select the resource `DELETE /v2/namespaces/{namespace-id}/collections/{collection-id}/{document-id} 380 | ` _ Delete a document_ 381 | 382 | - (3) - Populate the form with the following values 383 | 384 | |Field| Value| 385 | |---|---| 386 | |**X-Cassandra-Token**| _autopopulated_ | 387 | |**namespace-id**| `tiktok_keyspace` | 388 | |**collection-id**| `story` | 389 | |**document-id**| document you got before here `8aa07632-4ffb-46e5-9d78-b32e21847221` | 390 | 391 | - (4) - Click on `[Execute]` button 392 | 393 | - (5) - You should get a result code of `204` 394 | 395 | > 🖥️ `Output` 396 | > ``` 397 | > Code 204 398 | > access-control-allow-credentials: true 399 | > access-control-allow-origin: https://50b31120-2303-4f45-a9dd-1cfb03e24ff1-us-east1.apps.astra.datastax.com 400 | > access-control-expose-headers: Date 401 | > date: Mon,12 Dec 2022 18:12:43 GMT 402 | > vary: Origin 403 | > ``` 404 | 405 | ### 2.2 - Create Astra Credentials 406 | 407 | When using swagger in the user interface, you are already authenticated against the API. When using a third party application you will need a token. 408 | 409 | #### `✅.2.2.a`- Create Astra Credentials 410 | 411 | > ℹ️ _Skip this step is you already have a token. You can reuse the same token in our other workshops, too._ 412 | 413 | - (1) - Go the `Organization Settings` 414 | - (2) - Go to `Token Management` 415 | - (3) - Pick the role `Database Admnistrator` on the select box 416 | - (4) - Click `Generate token` 417 | 418 | ![image](./tutorial/images/astra-create-token.gif?raw=true) 419 | 420 | #### `✅.2.2.b`- Download Astra Credentials 421 | 422 | - Click the **`Download CSV`** button. You are going to need these values here in a moment. 423 | 424 | ![image](./tutorial/images/astra-token.png?raw=true) 425 | 426 | > **⚠️ Important** 427 | > ``` 428 | > The instructor will show you on screen how to create a token 429 | > but will have to destroy to token immediately for security reasons. 430 | > ``` 431 | 432 | Notice the clipboard icon at the end of each value. 433 | - `Client ID:` We will *not* use this during this workshop 434 | - `Client Secret:` We will *not* use this during this workshop 435 | - `Token:` ***This is your token!*** We will use it as a api Key to interact with APIS 436 | 437 | ## LAB 3 - IDE Setup 438 | 439 | ### 3.1 - Netlify Site Setup 440 | 441 | - (1) Click the button to deploy. 442 | 443 | > ↗️ _Right Click and select open as a new Tab..._ 444 | > 445 | > [![Deploy to Netlify](https://www.netlify.com/img/deploy/button.svg)](https://app.netlify.com/start/deploy?repository=https://github.com/datastaxdevs/workshop-astra-tik-tok) 446 | 447 | - (2) - Authenticate with your github Account 448 | 449 | - (3) - Select an account and the github repository where to clone 450 | 451 | ![image](./tutorial/images/deploy-to-netlify.gif?raw=true) 452 | 453 | - (4) - In netlify user interface click on `Site deploy in progress` 454 | 455 | > 456 | 457 | - (5) - Click the top deploy link to see the build process. 458 | 459 | > 460 | 461 | - (6) - Wait until the build complete `Netlify Build Complete`, **When you see Pushing to repository** you're ready to move on. 462 | 463 | > 464 | 465 | - (7) Scroll up to the top and click on the site name (it'll be after {yourlogin}'s Team next to the Netlify button). 466 | 467 | > 468 | 469 | - (8) - Click on the `GitHub` in `Deploys from GitHub` to get back to your new repository. Scroll to where you were in the README. 470 | 471 | > 472 | 473 | ### 3.2 - Launch GitPod 474 | 475 | #### `✅.3.2.a`- Open YOUR GITHUB REPOSITORY README 476 | 477 | - Click the button to launch the GitPod IDE from **YOUR** repository. 478 | 479 | #### WAIT! Before moving on ensure you are working out of YOUR repository, not the datastaxdevs repository. 480 | 481 | ![correct notcorrect](tutorial/images/correct-not-correct.png?raw=true) 482 | 483 | If you are still using the `datastaxdevs` repo please ensure to follow the previous step, [step3](#3-clone-your-github-repository) to get to your repo. 484 | 485 | #### `✅.3.2.b`- Open Gitpod IDE 486 | 487 | > Last time, be certain to click this when you MOVED TO YOUR REPOSITORY (not `datastaxdevs`) 488 | > 489 | > ↗️ _Right Click and select open as a new Tab..._ 490 | > 491 | > [![Open in Gitpod](https://gitpod.io/button/open-in-gitpod.svg)](https://gitpod.io/from-referrer/) 492 | 493 | #### `✅.3.2.c`- Validating your not using `datastaxdevs` 494 | 495 | - From your GitPod terminal execute the following command 496 | 497 | ```bash 498 | git remote -v 499 | ``` 500 | 501 | > 🖥️ `Output` 502 | > ``` 503 | > origin https://github.com/clun/workshop-astra-tik-tok.git (fetch) 504 | > origin https://github.com/clun/workshop-astra-tik-tok.git (push) 505 | >If the result returned from the command displays **`datastaxdevs`** then you are not in the correct repository. If this is the case please [repeat step 3 above](#3-access-your-github-repository), otherwise just move on to the next step. 506 | > ``` 507 | > 508 | ### 3.3 - Astra CLI Setup 509 | 510 | #### `✅.3.3.a`- Save your token 511 | 512 | Locate an open terminal and enter the following command replacing `` by the one we created before starting with `AstraCS:..` it should be in the CSV we download before 513 | 514 | ``` 515 | astra setup -t 516 | ``` 517 | 518 | #### `✅.3.3.b`- Validate your configuration 519 | 520 | - In the terminal panel in gitpod enter the following command: 521 | 522 | ``` 523 | astra user list 524 | ``` 525 | 526 | > 🖥️ `Output` 527 | > 528 | > ``` 529 | > gitpod /workspace/workshop-astra-tik-tok (master) $ astra user list 530 | > +--------------------------------------+-----------------------------+---------------------+ 531 | > | User Id | User Email | Status | 532 | > +--------------------------------------+-----------------------------+---------------------+ 533 | > | b665658a-ae6a-4f30-a740-2342a7fb469c | cedrick.lunven@datastax.com | active | 534 | > +--------------------------------------+-----------------------------+---------------------+ 535 | > ``` 536 | 537 | #### `✅.3.3.c`- List Database to see `workshops` 538 | 539 | - In the terminal panel in gitpod enter the following command: 540 | 541 | ``` 542 | astra db list 543 | ``` 544 | 545 | > 🖥️ `Output` 546 | > 547 | > ``` 548 | > +---------------------+--------------------------------------+---------------------+----------------+ 549 | > | Name | id | Default Region | Status | 550 | > +---------------------+--------------------------------------+---------------------+----------------+ 551 | > | mtg | dde308f5-a8b0-474d-afd6-81e5689e3e25 | eu-central-1 | ACTIVE | 552 | > | workshops | 50b31120-2303-4f45-a9dd-1cfb03e24ff1 | us-east1 | ACTIVE | 553 | > +---------------------+--------------------------------------+---------------------+----------------+ 554 | > ``` 555 | 556 | #### `✅.3.3.d`- Get db details 557 | 558 | - In the terminal panel in gitpod enter the following command: 559 | 560 | ``` 561 | astra db get workshops 562 | ``` 563 | 564 | > 🖥️ `Output` 565 | > 566 | > ``` 567 | > gitpod /workspace/workshop-astra-tik-tok (master) $ astra db get workshops 568 | > +------------------------+-----------------------------------------+ 569 | > | Attribute | Value | 570 | > +------------------------+-----------------------------------------+ 571 | > | Name | workshops | 572 | > | id | 50b31120-2303-4f45-a9dd-1cfb03e24ff1 | 573 | > | Status | ACTIVE | 574 | > | Default Cloud Provider | GCP | 575 | > | Default Region | us-east1 | 576 | > | Default Keyspace | tiktok_keyspace | 577 | > | Creation Time | 2022-12-12T11:14:58Z | 578 | > | | | 579 | > | Keyspaces | [0] tiktok_keyspace | 580 | > | | | 581 | > | | | 582 | > | Regions | [0] us-east1 | 583 | > | | | 584 | > +------------------------+-----------------------------------------+ 585 | > ``` 586 | 587 | #### `✅.3.3.e`- Create configuration file 588 | 589 | - Create `.env` file 590 | 591 | ``` 592 | astra db create-dotenv workshops -k tiktok_keyspace -r us-east1 593 | ``` 594 | 595 | - Show content 596 | 597 | ``` 598 | cat .env 599 | ``` 600 | 601 | > 🖥️ `Output` 602 | > 603 | > ``` 604 | > ASTRA_DB_APPLICATION_TOKEN="AstraCS:gfYSGwpaFNGmUZnZTvaCp......" 605 | > ASTRA_DB_GRAPHQL_URL="https://.....-us-east1.apps.astra.datastax.com/api/graphql/tiktok_keyspace" 606 | > ASTRA_DB_GRAPHQL_URL_ADMIN="https://.....-us-east1.apps.astra.datastax.com/api/graphql-admin" 607 | > ASTRA_DB_GRAPHQL_URL_PLAYGROUND="https://.....-us-east1.apps.astra.datastax.com/api/playground" 608 | > ASTRA_DB_GRAPHQL_URL_SCHEMA="https://.....-us-east1.apps.astra.datastax.com/api/graphql-schema" 609 | > ASTRA_DB_ID="....." 610 | > ASTRA_DB_KEYSPACE="tiktok_keyspace" 611 | > ASTRA_DB_REGION="us-east1" 612 | > ASTRA_DB_REST_URL="https://.....-us-east1.apps.astra.datastax.com/api/rest" 613 | > ASTRA_DB_REST_URL_SWAGGER="https://.....-us-east1.apps.astra.datastax.com/api/rest/swagger-ui/" 614 | > ASTRA_DB_SECURE_BUNDLE_PATH="/home/gitpod/.astra/scb/scb_....._us-east1.zip" 615 | > ASTRA_DB_SECURE_BUNDLE_URL="secured_url" 616 | > ASTRA_ORG_ID="f9460f14-9879-4ebe-83f2-48d3f3dce13c" 617 | > ASTRA_ORG_NAME="cedrick.lunven@datastax.com" 618 | > ASTRA_ORG_TOKEN="AstraCS:gfYSGwpaFNGmUZnZT....." 619 | > ``` 620 | 621 | ### 3.4 - Netlify CLI Setup 622 | 623 | #### `✅.3.4.a`- Install the package 624 | 625 | In the `workshop-astra-tik-tok` directory run the following command to install the netlify-cli 626 | 627 | ``` 628 | npm install -g netlify-cli 629 | ``` 630 | 631 | > 🖥️ `Output` 632 | > 633 | > 634 | 635 | 636 | ### 3.5 Launch your app 637 | 638 | #### `✅.3.5.a`- Start application 639 | 640 | ``` 641 | netlify dev 642 | ``` 643 | 644 | The application should automatically launch in the GitPod preview pane. You might see an error in the log for this firt launch, some data is inserted to the database. 645 | 646 | > 🖥️ `Output` 647 | 648 | ``` 649 | ◈ Netlify Dev ◈ 650 | ◈ Ignored general context env var: LANG (defined in process) 651 | ◈ Injected .env file env var: ASTRA_DB_APPLICATION_TOKEN 652 | ◈ Injected .env file env var: ASTRA_DB_GRAPHQL_URL 653 | ◈ Injected .env file env var: ASTRA_DB_GRAPHQL_URL_ADMIN 654 | ◈ Injected .env file env var: ASTRA_DB_GRAPHQL_URL_PLAYGROUND 655 | ◈ Injected .env file env var: ASTRA_DB_GRAPHQL_URL_SCHEMA 656 | ◈ Injected .env file env var: ASTRA_DB_ID 657 | ◈ Injected .env file env var: ASTRA_DB_KEYSPACE 658 | ◈ Injected .env file env var: ASTRA_DB_REGION 659 | ◈ Injected .env file env var: ASTRA_DB_REST_URL 660 | ◈ Injected .env file env var: ASTRA_DB_REST_URL_SWAGGER 661 | ◈ Injected .env file env var: ASTRA_DB_SECURE_BUNDLE_PATH 662 | ◈ Injected .env file env var: ASTRA_DB_SECURE_BUNDLE_URL 663 | ◈ Injected .env file env var: ASTRA_ORG_ID 664 | ◈ Injected .env file env var: ASTRA_ORG_NAME 665 | ◈ Injected .env file env var: ASTRA_ORG_TOKEN 666 | ◈ Loaded function add http://localhost:8888/.netlify/functions/add. 667 | ◈ Loaded function addData http://localhost:8888/.netlify/functions/addData. 668 | ◈ Loaded function edit http://localhost:8888/.netlify/functions/edit. 669 | ◈ Loaded function posts http://localhost:8888/.netlify/functions/posts. 670 | ◈ Functions server is listening on 36661 671 | ◈ Setting up local development server 672 | 673 | ──────────────────────────────────────────────────────────────── 674 | Netlify Build 675 | ──────────────────────────────────────────────────────────────── 676 | 677 | ❯ Version 678 | @netlify/build 28.4.5 679 | 680 | ❯ Flags 681 | {} 682 | 683 | ❯ Current directory 684 | /workspace/workshop-astra-tik-tok 685 | 686 | ❯ Config file 687 | /workspace/workshop-astra-tik-tok/netlify.toml 688 | 689 | ❯ Context 690 | dev 691 | 692 | ──────────────────────────────────────────────────────────────── 693 | 1. Run command for local development 694 | ──────────────────────────────────────────────────────────────── 695 | 696 | ◈ Starting Netlify Dev with Create React App 697 | 698 | > tik-tok-stargate@0.1.0 start 699 | > react-scripts start 700 | 701 | ℹ 「wds」: Project is running at http://10.0.5.2/ 702 | ℹ 「wds」: webpack output is served from 703 | ℹ 「wds」: Content not from webpack is served from /workspace/workshop-astra-tik-tok/public 704 | ℹ 「wds」: 404s will fallback to / 705 | Starting the development server... 706 | 707 | Compiled successfully! 708 | 709 | You can now view tik-tok-stargate in the browser. 710 | 711 | Local: http://localhost:3000 712 | On Your Network: http://10.0.5.2:3000 713 | 714 | Note that the development build is not optimized. 715 | To create a production build, use npm run build. 716 | 717 | ✔ Waiting for framework port 3000. This can be configured using the 'targetPort' property in the netlify.toml 718 | 719 | (dev.command completed in 9.4s) 720 | 721 | ┌─────────────────────────────────────────────────┐ 722 | │ │ 723 | │ ◈ Server now ready on http://localhost:8888 │ 724 | │ │ 725 | └─────────────────────────────────────────────────┘ 726 | 727 | ⠦ Setting up the Edge Functions environment. This may take a couple of minutes.Request from ::ffff:192.168.9.75: POST /.netlify/functions/addData 728 | ⠧ Setting up the Edge Functions environment. This may take a couple of minutes.Request from ::ffff:192.168.9.75: GET /.netlify/functions/posts 729 | ⠙ Setting up the Edge Functions environment. This may take a couple of minutes.Response with status 200 in 341 ms. 730 | ⠦ Setting up the Edge Functions environment. This may take a couple of minutes.Response with status 200 in 1609 ms. 731 | ✔ Setting up the Edge Functions environment. This may take a couple of minutes. 732 | ``` 733 | 734 | ![start](./tutorial/images/netlify-start.png?raw=true) 735 | 736 | ## LAB 4 - Coding Against DB 737 | 738 | ### 4.1 - Document API 739 | 740 | Using same instruction as #2.1 execute the following operation with the Document Api using swagger UI. 741 | 742 | #### `✅.4.1.a`- Reopen Swagger 743 | 744 | Now that we have locally deployed our TikTok app, let's take a look at this in our database. Head to your [Astra DB dashboard](astra.datastax.com) and click the `Connect` button next to your database ('workshops'). 745 | 746 | ![db_connect](./tutorial/images/db_connect.png?raw=true) 747 | 748 | Then scroll down to the section called 'Launching SwaggerUI' and click the link. We'll be using SwaggerUI to make api calls to our database and see the results. 749 | 750 | ![swaggerui_link](./tutorial/images/swaggerui_link.png?raw=true) 751 | 752 | #### `✅.4.1.a`- List Collections 753 | 754 | Open up the first section labelled "List collections in namespace" and click the button "Try it out". 755 | 756 | ![swaggerui_link](./tutorial/images/swaggerui_listcollections_02.png?raw=true) 757 | 758 | - Execture with `[Execute]` button 759 | 760 | > 🖥️ `Output` 761 | > 762 | > ```json 763 | > { 764 | > "data": [ 765 | > { 766 | > "name": "story", 767 | > "upgradeAvailable": false 768 | > }, 769 | > { 770 | > "name": "tktkposts", 771 | > "upgradeAvailable": false 772 | > } 773 | > ] 774 | > } 775 | > ``` 776 | 777 | #### `✅.4.1.b`- List Documents of `tktkposts` 778 | 779 | - Open resource `GET /v2/namespaces/{namespace-id}/collections/{collection-id}` _Search documents in a collection_ 780 | 781 | ![swaggerui_link](./tutorial/images/swaggerui_searchdocuments_02.png?raw=true) 782 | 783 | - Populate the form with the following values 784 | 785 | |Field| Value| 786 | |---|---| 787 | |**X-Cassandra-Token**| _autopopulated_ | 788 | |**namespace-id**| `tiktok_keyspace` | 789 | |**collection-id**| `tktkposts` | 790 | 791 | Let the rest of the fields untouched. You can see that every query is paged and default page size is `3`.And we see all of the documents stored in our database. 792 | 793 | ![swaggerui_link](./tutorial/images/swaggerui_searchdocuments_04.png?raw=true) 794 | 795 | > 🖥️ `Output` 796 | > 797 | > ```json 798 | > { 799 | > "pageState": "ATIA8H_____wf____w==", 800 | > "data": { 801 | > "0": { 802 | > "avatar": "https://i.imgur.com/jONHmE5.png", 803 | > "button_visible": true, 804 | > "caption": "Art is for everyone", 805 | > "comments": 20, 806 | > "id": 0, 807 | > "is_followed": true, 808 | > "likes": 231, 809 | > "name": "Lana Del Mont", 810 | > "timestamp": "2020-09-10T09:08:31.020Z", 811 | > "username": "lana_del_away", 812 | > "video": "https://i.imgur.com/H9UX0Jm.mp4" 813 | > }, 814 | > "2": { 815 | > "avatar": "https://i.imgur.com/eX3hkoc.png", 816 | > "button_visible": true, 817 | > "caption": "Happiest of Birthdays my Angel", 818 | > "comments": 4, 819 | > "id": 2, 820 | > "is_followed": true, 821 | > "likes": 2, 822 | > "name": "Angela Lee", 823 | > "timestamp": "2020-04-10T09:08:31.020Z", 824 | > "username": "angiecakes", 825 | > "video": "https://i.imgur.com/al6MLay.mp4" 826 | > }, 827 | > "3": { 828 | > "avatar": "https://i.imgur.com/IigY4Hm.png", 829 | > "button_visible": true, 830 | > "caption": "The new normal", 831 | > "comments": 2, 832 | > "id": 3, 833 | > "is_followed": false, 834 | > "likes": 10, 835 | > "name": "Nina Xen", 836 | > "timestamp": "2020-05-10T09:08:31.020Z", 837 | > "username": "nina_lina", 838 | > "video": "https://i.imgur.com/Kzvbeup.mp4" 839 | > } 840 | > } 841 | > } 842 | > ``` 843 | 844 | ### 4.2 - AstraJS Client 845 | 846 | #### `✅.4.2.1` - Initialization 847 | 848 | We are using the `@astrajs/collections` library to make the connection using the Document API. To do so, we start by creating a `client`. 849 | 850 | (See: [functions/utils/astraClient.js](./functions/utils/astraClient.js)) 851 | 852 | ```javascript 853 | const { createClient } = require("@astrajs/collections"); 854 | 855 | let astraClient = null; 856 | 857 | const getAstraClient = async () => { 858 | if (astraClient === null) { 859 | astraClient = await createClient( 860 | { 861 | astraDatabaseId: process.env.ASTRA_DB_ID, 862 | astraDatabaseRegion: process.env.ASTRA_DB_REGION, 863 | applicationToken: process.env.ASTRA_DB_APPLICATION_TOKEN, 864 | }, 865 | 30000 866 | ); 867 | } 868 | return astraClient; 869 | }; 870 | ``` 871 | 872 | Here we are defining a new method called `getAstraClient` that uses the `createClient` method from our `astrajs` library to create a connection to our database. We then provide it the needed database credentials we added to our environment variables earlier; 873 | 874 | - `ASTRA_DB_ID` _(unique identifier for a db)_ 875 | - `ASTRA_DB_REGION` _(cloud region, here `us-east1`)_ 876 | - `ASTRA_DB_APPLICATION_TOKEN` _(credentials)_ 877 | 878 | Then we return the `astraClient` we can then use in our API calls. 879 | 880 | We also need to create a document collection to store our data. 881 | 882 | ``` javascript 883 | const getCollection = async () => { 884 | const documentClient = await getAstraClient(); 885 | return documentClient 886 | .namespace(process.env.ASTRA_DB_KEYSPACE) 887 | .collection("tktkposts"); 888 | }; 889 | ``` 890 | 891 | In this method, we are using our previously created `getAstraClient` method to initialize the database connection, and then create a collection in the keyspace we defined in our environment variables; 892 | 893 | - `ASTRA_DB_KEYSPACE` (for us `tiktok_keyspace`) 894 | 895 | We will call the collection **tktkposts**. 896 | 897 | #### `✅.4.2.b` - Create document with `@astrajs/collections` 898 | 899 | For our TikTok app, we will not be dealing with the Document API directly. Instead `@astrajs/collections` does that for us, and provides us with easy to use methods. 900 | 901 | If you want a comprehensive list of the capabilities of `@astrajs/collections`, check out this documentation: [AstraJS Collections](https://docs.datastax.com/en/astra/docs/astra-collection-client.html) 902 | 903 | The `create` method is used when we want to add documents to our collection. For example, in **`functions/add.js`** we get our collection from the database using our `getCollection` method. 904 | 905 | ``` javascript 906 | const users = await getCollection(); 907 | ``` 908 | 909 | Then we use the `create` method to create a document, providing the _id_ and _body_ of the document. 910 | 911 | ``` javascript 912 | try { 913 | const user = await users.create(id, event.body); 914 | return { 915 | statusCode: 200, 916 | body: JSON.stringify(user), 917 | }; 918 | } 919 | ``` 920 | 921 | #### `✅.4.2.c` - Update document with `@astrajs/collections` 922 | 923 | The `update` method is used to update portions of existing documents. Take a look at **`functions/edit.js`**. Again we use `getCollection()` to get our collection from the database, then we use the `update` method, provide it with an id for the document we want to edit, and the data that needs updating. 924 | 925 | ``` javascript 926 | try { 927 | users.update(body.userId, body.data); 928 | return { 929 | statusCode: 200, 930 | }; 931 | } 932 | ``` 933 | 934 | #### `✅.4.2.d` - Search document with `@astrajs/collections` 935 | 936 | And finally the `find` method is used to retrieve documents. In **`functions/posts.js`** we are again using `getCollections()` and using the `find` method on the result. 937 | 938 | ``` javascript 939 | try { 940 | const res = await users.find({}); 941 | return { 942 | statusCode: 200, 943 | body: JSON.stringify(Object.keys(res).map((i) => res[i])), 944 | }; 945 | } 946 | ``` 947 | 948 | In this case, we are passing an empty object to retrieve all documents. In a real-world scenario, we would pass a qualifier to get only the documents relevant to a specific user. 949 | 950 | ### 4.3 - Serverless configuration 951 | 952 | Take a look at `netlify.toml`. 953 | 954 | ```init 955 | [build] 956 | command = "npm run build" 957 | functions = "functions" 958 | publish = "build" 959 | ``` 960 | 961 | This is the configuration file we include in our codebase that tells Netlify how to build our app. In our case it's really simple. First we give the `build` command to build our app: `npm run build`. Then we tell Netlify where to find our serverless functions, and finally where to find the resulting app after build. 962 | 963 | So Netlify will create endpoints for our serverless functions based on the files it finds in our functions folder. 964 | 965 | For example, we have a function called `posts.js`. As we saw before, this function returns all of the current posts in our database. Netlify will see that file in our `functions` directory and dynamically create an endpoint at [/.netlify/functions/posts](./functions/posts.js) 966 | 967 | ```javascript 968 | // Declaring 'getCollection' 969 | const { getCollection } = require("./utils/astraClient"); 970 | 971 | // Function exported as a REST API 972 | exports.handler = async function () { 973 | // Accessing the collection tkt 974 | const tktkpostsCollection = await getCollection(); 975 | try { 976 | // Access POST 977 | const res = await tktkpostsCollection.find({}); 978 | return { 979 | // Return POSTS in the body 980 | statusCode: 200, 981 | body: JSON.stringify(Object.keys(res).map((i) => res[i])), 982 | 983 | [...] 984 | ``` 985 | 986 | ✅ We can see these functions in our Netlify account. 987 | - Go to netlify.com and sign in. 988 | - Select your site from the list. 989 | - Select the "Functions" tab at the top. 990 | 991 | ![netlify_functions](./tutorial/images/netlify_functions_tab.gif) 992 | 993 | From here we can see all our functions and get direct links as well as watch real time logs. 994 | 995 | We can also see this in action by manually going to the endpoint on our Netlify site: `[your-site-url]/.netlify/functions/posts`. 996 | 997 | ![netlify_endpoint](./tutorial/images/netlify_endpoint_nav.gif) 998 | 999 | ### 4.4 - React Component 1000 | 1001 | The front end leverages on React. The code is organized in pages and each pages import a list of components. 1002 | 1003 | #### `✅.4.4.a` - React Router 1004 | 1005 | There are 2 pages `updload` and `Home` and describe in [index.js](./src/index.js) 1006 | 1007 | ```xml 1008 | 1009 | 1010 | 1011 | 1012 | ``` 1013 | 1014 | ![netlify_endpoint](./tutorial/images/pages-all.png) 1015 | 1016 | #### `✅.4.4.b` - Upload Page 1017 | 1018 | Access through the `cloud` icon of directly on `/upload` it is a static HTML form to create new posts. 1019 | 1020 | ![netlify_endpoint](./tutorial/images/page-upload.png) 1021 | 1022 | As you can see [Upload.js](./src/pages/Upload.js) there are no component used. 1023 | 1024 | ```html 1025 |

1026 |
1027 | 1028 | ``` 1029 | 1030 | - Function to post to backend 1031 | 1032 | ```javascript 1033 | const handleSubmit = async (e) => { 1034 | e.preventDefault() 1035 | // Create payload 1036 | const data = { 1037 | id: id, 1038 | name: name, 1039 | username: username, 1040 | avatar: avatar, 1041 | is_followed: false, 1042 | video: video, 1043 | caption: caption, 1044 | likes: 0, 1045 | comments: 0, 1046 | timestamp: timestamp, 1047 | button_visible: false 1048 | } 1049 | // Post Payload to Netlify functions 1050 | axios.post('/.netlify/functions/add', data) 1051 | .then((response) => { console.log(response)}) 1052 | .catch((err) => { console.error(err)}) 1053 | ``` 1054 | 1055 | #### `✅.4.4.b` - Home Page 1056 | 1057 | The page load data coming from the collection in Astra and push the information to multiple components that renders sections of the page. 1058 | 1059 | ![netlify_endpoint](./tutorial/images/react-components.png) 1060 | 1061 | More on [Home.js](./src/pages/Home.js). 1062 | 1063 | ## LAB 5 - Netlify Deployments 1064 | 1065 | ### 5.1 - Connect Netlify to your site 1066 | 1067 | Execute each of the commands below to link your code to your Netlify deployment. 1068 | * First thing, we'll need to **STOP** the `netlify dev` command we issued a moment ago. In the terminal where you executed the netlify command issue a `CTRL-C` (control key + the C key) in order to stop the process. 1069 | * Then continue with the following commands 1070 | * This will pop up a browser to authenticate with netlify 1071 | 1072 | #### `✅.5.1.a` - Authenticate in Netlify 1073 | 1074 | ``` 1075 | netlify login 1076 | ``` 1077 | 1078 | _Note, when using GitPod the preview pane will not display this properly. You must click the "open in a new window" button in the very top right of the preview pane._ 1079 | 1080 | ![swaggerui_link](./tutorial/images/netlify_login.png) 1081 | 1082 | > 🖥️ `Output` 1083 | > 1084 | > ```bash 1085 | > Logging into your Netlify account... 1086 | > Opening https://app.netlify.com/authorize?response_type=ticket&ticket=774701161c326912e718b3a86096f375 1087 | > You are now logged into your Netlify account! 1088 | > Run netlify status for account details 1089 | > To see all available commands run: netlify help 1090 | > ``` 1091 | 1092 | - Link your workspace to the associated site with the command Below 1093 | 1094 | ``` 1095 | netlify link 1096 | ``` 1097 | 1098 | - It will display a MENU where you can move UP and DOWN with arrows. Pick first choice 1099 | 1100 | ``` 1101 | > Use current git remote origin (https://github ...` 1102 | ``` 1103 | 1104 | ![swaggerui_link](./tutorial/images/netlify-link.png?raw=true) 1105 | 1106 | > 🖥️ `Output` 1107 | > ```bash 1108 | > netlify link will connect this folder to a site on Netlify 1109 | > ? How do you want to link this folder to a site? Use current git remote origin (https://github.com/clun/workshop-astra-tik-tok) 1110 | > Looking for sites connected to 'https://github.com/clun/workshop-astra-tik-tok'... 1111 | > Directory Linked 1112 | > Admin url: https://app.netlify.com/sites/fanciful-licorice-ea1437 1113 | > Site url: https://fanciful-licorice-ea1437.netlify.app 1114 | > You can now run other `netlify` cli commands in this directory 1115 | >``` 1116 | 1117 | #### `✅.5.1.b` - Import configuration in site 1118 | 1119 | * This will take the `.env` file created by astra-setup and upload it to netlify 1120 | 1121 | ``` 1122 | netlify env:import .env 1123 | ``` 1124 | 1125 | ### 5.2 - Deploy to production 1126 | 1127 | Now that you've hooked everything up, time to deploy to production. 1128 | 1129 | * Run 1130 | 1131 | ``` 1132 | netlify build 1133 | ``` 1134 | 1135 | 1136 | > 🖥️ `Output` 1137 | > ``` 1138 | > ──────────────────────────────────────────────────────────────── 1139 | > Netlify Build 1140 | > ──────────────────────────────────────────────────────────────── 1141 | > 1142 | > ❯ Version 1143 | > @netlify/build 28.4.5 1144 | > 1145 | > ❯ Flags 1146 | > dry: false 1147 | > offline: false 1148 | > 1149 | > ❯ Current directory 1150 | > /workspace/workshop-astra-tik-tok 1151 | > 1152 | > ❯ Config file 1153 | > /workspace/workshop-astra-tik-tok/netlify.toml 1154 | > 1155 | > ❯ Context 1156 | > production 1157 | > 1158 | > ──────────────────────────────────────────────────────────────── 1159 | > 1. build.command from netlify.toml 1160 | > ──────────────────────────────────────────────────────────────── 1161 | > 1162 | > $ npm run build 1163 | > 1164 | > tik-tok-stargate@0.1.0 build 1165 | > react-scripts build 1166 | > 1167 | > Creating an optimized production build... 1168 | > Compiled successfully. 1169 | > 1170 | > File sizes after gzip: 1171 | > 1172 | > 616.87 KB build/static/js/2.82b8325c.chunk.js 1173 | > 2.32 KB build/static/js/main.fd7c93f3.chunk.js 1174 | > 966 B build/static/css/main.9d8c5499.chunk.css 1175 | > 780 B build/static/js/runtime-main.f09b770f.js 1176 | > 1177 | > The project was built assuming it is hosted at /. 1178 | > You can control this with the homepage field in your package.json. 1179 | > 1180 | > The build folder is ready to be deployed. 1181 | > You may serve it with a static server: 1182 | > 1183 | > npm install -g serve 1184 | > serve -s build 1185 | > 1186 | > Find out more about deployment here: 1187 | > 1188 | > https://cra.link/deployment 1189 | > 1190 | > 1191 | > (build.command completed in 35.1s) 1192 | > 1193 | > ──────────────────────────────────────────────────────────────── 1194 | > 2. Functions bundling 1195 | > ──────────────────────────────────────────────────────────────── 1196 | > 1197 | > Packaging Functions from functions directory: 1198 | > - add.js 1199 | > - addData.js 1200 | > - edit.js 1201 | > - posts.js 1202 | > 1203 | > 1204 | > (Functions bundling completed in 6.1s) 1205 | > 1206 | > ──────────────────────────────────────────────────────────────── 1207 | > Netlify Build Complete 1208 | > ──────────────────────────────────────────────────────────────── 1209 | > 1210 | > (Netlify Build completed in 41.3s) 1211 | > ``` 1212 | 1213 | - Then run 1214 | 1215 | ``` 1216 | netlify deploy --prod 1217 | ``` 1218 | 1219 | > 🖥️ `Output` 1220 | > ``` 1221 | > Deploy path: /workspace/workshop-astra-tik-tok/build 1222 | > Functions path: /workspace/workshop-astra-tik-tok/functions 1223 | > Configuration path: /workspace/workshop-astra-tik-tok/netlify.toml 1224 | > Deploying to main site URL... 1225 | > ✔ Deploying functions from cache (use --skip-functions-cache to override) 1226 | > ✔ Finished hashing 17 files and 4 functions 1227 | > ✔ CDN requesting 0 files and 4 functions 1228 | > ✔ Finished uploading 4 assets 1229 | > ✔ Deploy is live! 1230 | > 1231 | > Logs: https://app.netlify.com/sites/fanciful-licorice-ea1437/deploys/63974804721fc334dc247455 1232 | > Unique Deploy URL: https://63974804721fc334dc247455--fanciful-licorice-ea1437.netlify.app 1233 | > Website URL: https://fanciful-licorice-ea1437.netlify.app 1234 | > gitpod /workspace/workshop-astra-tik-tok (master) $ 1235 | ``` 1236 | 1237 | - Then finally run 1238 | 1239 | ``` 1240 | netlify open:site 1241 | ``` 1242 | 1243 | > 🖥️ `Output` 1244 | > ``` 1245 | > Opening "fanciful-licorice-ea1437" site url: 1246 | > https://fanciful-licorice-ea1437.netlify.app 1247 | > ``` 1248 | 1249 | ## Extra Resources 1250 | 1251 | #### Video tutorial with Ania Kubow 1252 | 1253 | Thank you to our wonderful friend Ania Kubow for producing the TikTok clone. If you are not aware of Ania and love learning about coding you should absolutely check out her YouTube channel listed below. 1254 | 1255 | While we focused on getting you up and running to production with Astra DB and Netlify, Ania's video will dig into more details on the app itself. Check it out to dig in more. 1256 | 1257 | #### Running Astra DB Tik-Tok 1258 | We're using Create-React-App and the Astra DB Document API to create a simple Tik-Tok clone. Follow along in this video tutorial: [https://youtu.be/IATOicvih5A](https://youtu.be/IATOicvih5A). 1259 | 1260 | Follow the instructions below to get started. 1261 | 1262 | #### If you did like this video, please hit the Like and Subscribe button so I know to make more! 1263 | - Twitter: https://twitter.com/ania_kubow 1264 | - YouTube: https://youtube.com/aniakubow 1265 | - Instagram: https://instagram.com/aniakubow 1266 | --------------------------------------------------------------------------------