├── .gitignore ├── .DS_Store ├── client ├── .DS_Store ├── components │ ├── .DS_Store │ ├── img │ │ ├── logo.png │ │ ├── logo2.png │ │ ├── Database.png │ │ └── soundboard-excalidraw.png │ ├── sounds │ │ ├── c3.wav │ │ ├── tom.wav │ │ ├── .DS_Store │ │ ├── boom.wav │ │ ├── clap.wav │ │ ├── fire.mp3 │ │ ├── geese.wav │ │ ├── hihat.wav │ │ ├── kick.mp3 │ │ ├── kick2.wav │ │ ├── ride.wav │ │ ├── snare.wav │ │ ├── tink.wav │ │ ├── dogbark.wav │ │ ├── gameover.wav │ │ ├── openhat.wav │ │ ├── scratch.mp3 │ │ ├── djairhorn.mp3 │ │ └── sadtrombone.wav │ ├── App.jsx │ ├── Button.js │ ├── Signup.js │ ├── Board.js │ └── Login.js ├── index.js ├── test.html ├── index.html └── index.css ├── postcss.config.js ├── .github ├── workflows │ ├── proof-html.yml │ └── auto-assign.yml └── auto-assign.yml ├── tailwind.config.js ├── server ├── models │ └── soundboardModel.js ├── controllers │ ├── soundboardController.js │ └── loginController.js ├── routes │ └── api.js └── server.js ├── package.json ├── README.md └── webpack.config.js /.gitignore: -------------------------------------------------------------------------------- 1 | node_modules/ -------------------------------------------------------------------------------- /.DS_Store: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Red-Laboratories/soundboard/HEAD/.DS_Store -------------------------------------------------------------------------------- /client/.DS_Store: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Red-Laboratories/soundboard/HEAD/client/.DS_Store -------------------------------------------------------------------------------- /client/components/.DS_Store: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Red-Laboratories/soundboard/HEAD/client/components/.DS_Store -------------------------------------------------------------------------------- /client/components/img/logo.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Red-Laboratories/soundboard/HEAD/client/components/img/logo.png -------------------------------------------------------------------------------- /client/components/img/logo2.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Red-Laboratories/soundboard/HEAD/client/components/img/logo2.png -------------------------------------------------------------------------------- /client/components/sounds/c3.wav: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Red-Laboratories/soundboard/HEAD/client/components/sounds/c3.wav -------------------------------------------------------------------------------- /client/components/sounds/tom.wav: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Red-Laboratories/soundboard/HEAD/client/components/sounds/tom.wav -------------------------------------------------------------------------------- /postcss.config.js: -------------------------------------------------------------------------------- 1 | module.exports = { 2 | plugins: { 3 | tailwindcss: {}, 4 | autoprefixer: {}, 5 | }, 6 | } 7 | 8 | -------------------------------------------------------------------------------- /client/components/img/Database.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Red-Laboratories/soundboard/HEAD/client/components/img/Database.png -------------------------------------------------------------------------------- /client/components/sounds/.DS_Store: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Red-Laboratories/soundboard/HEAD/client/components/sounds/.DS_Store -------------------------------------------------------------------------------- /client/components/sounds/boom.wav: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Red-Laboratories/soundboard/HEAD/client/components/sounds/boom.wav -------------------------------------------------------------------------------- /client/components/sounds/clap.wav: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Red-Laboratories/soundboard/HEAD/client/components/sounds/clap.wav -------------------------------------------------------------------------------- /client/components/sounds/fire.mp3: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Red-Laboratories/soundboard/HEAD/client/components/sounds/fire.mp3 -------------------------------------------------------------------------------- /client/components/sounds/geese.wav: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Red-Laboratories/soundboard/HEAD/client/components/sounds/geese.wav -------------------------------------------------------------------------------- /client/components/sounds/hihat.wav: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Red-Laboratories/soundboard/HEAD/client/components/sounds/hihat.wav -------------------------------------------------------------------------------- /client/components/sounds/kick.mp3: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Red-Laboratories/soundboard/HEAD/client/components/sounds/kick.mp3 -------------------------------------------------------------------------------- /client/components/sounds/kick2.wav: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Red-Laboratories/soundboard/HEAD/client/components/sounds/kick2.wav -------------------------------------------------------------------------------- /client/components/sounds/ride.wav: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Red-Laboratories/soundboard/HEAD/client/components/sounds/ride.wav -------------------------------------------------------------------------------- /client/components/sounds/snare.wav: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Red-Laboratories/soundboard/HEAD/client/components/sounds/snare.wav -------------------------------------------------------------------------------- /client/components/sounds/tink.wav: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Red-Laboratories/soundboard/HEAD/client/components/sounds/tink.wav -------------------------------------------------------------------------------- /client/components/sounds/dogbark.wav: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Red-Laboratories/soundboard/HEAD/client/components/sounds/dogbark.wav -------------------------------------------------------------------------------- /client/components/sounds/gameover.wav: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Red-Laboratories/soundboard/HEAD/client/components/sounds/gameover.wav -------------------------------------------------------------------------------- /client/components/sounds/openhat.wav: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Red-Laboratories/soundboard/HEAD/client/components/sounds/openhat.wav -------------------------------------------------------------------------------- /client/components/sounds/scratch.mp3: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Red-Laboratories/soundboard/HEAD/client/components/sounds/scratch.mp3 -------------------------------------------------------------------------------- /client/components/sounds/djairhorn.mp3: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Red-Laboratories/soundboard/HEAD/client/components/sounds/djairhorn.mp3 -------------------------------------------------------------------------------- /client/components/sounds/sadtrombone.wav: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Red-Laboratories/soundboard/HEAD/client/components/sounds/sadtrombone.wav -------------------------------------------------------------------------------- /client/components/img/soundboard-excalidraw.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Red-Laboratories/soundboard/HEAD/client/components/img/soundboard-excalidraw.png -------------------------------------------------------------------------------- /.github/workflows/proof-html.yml: -------------------------------------------------------------------------------- 1 | name: Proof HTML 2 | on: 3 | push: 4 | workflow_dispatch: 5 | jobs: 6 | build: 7 | runs-on: ubuntu-latest 8 | steps: 9 | - uses: anishathalye/proof-html@v1.1.0 10 | with: 11 | directory: ./ 12 | -------------------------------------------------------------------------------- /client/index.js: -------------------------------------------------------------------------------- 1 | import React from 'react'; 2 | import { createRoot } from 'react-dom/client'; 3 | import App from './components/App.jsx' 4 | import './index.css'; 5 | 6 | 7 | const container = document.getElementById('root') 8 | const root = createRoot(container); 9 | root.render( 10 | 11 | ); -------------------------------------------------------------------------------- /tailwind.config.js: -------------------------------------------------------------------------------- 1 | /** @type {import('tailwindcss').Config} */ 2 | module.exports = { 3 | content: [ 4 | "./client/**/*.{html,js,jsx,ts,tsx}", 5 | ], 6 | theme: { 7 | extend: { 8 | padding: { 9 | '1/2': '50%', 10 | full: '100%', 11 | }, 12 | }, 13 | }, 14 | plugins: [], 15 | } 16 | -------------------------------------------------------------------------------- /client/test.html: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | 7 | Document 8 | 9 | 10 |

Test route

11 | 12 | -------------------------------------------------------------------------------- /.github/workflows/auto-assign.yml: -------------------------------------------------------------------------------- 1 | name: Auto Assign 2 | on: 3 | issues: 4 | types: [opened] 5 | pull_request: 6 | types: [opened] 7 | jobs: 8 | run: 9 | runs-on: ubuntu-latest 10 | steps: 11 | - uses: wow-actions/auto-assign@v1 12 | with: 13 | GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }} 14 | CONFIG_FILE: .github/auto-assign.yml 15 | -------------------------------------------------------------------------------- /client/index.html: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | 7 | 8 | Soundboard 9 | 10 | 11 | 12 |
13 | 14 | 15 | 16 | 17 | -------------------------------------------------------------------------------- /server/models/soundboardModel.js: -------------------------------------------------------------------------------- 1 | const { Pool } = require('pg'); 2 | 3 | const PG_URI = 'postgres://todkkube:CvOHogNm7N1EuKBBFbei6muuGH_Uq160@heffalump.db.elephantsql.com/todkkube'; 4 | 5 | const pool = new Pool ({ 6 | connectionString: PG_URI 7 | }); 8 | 9 | 10 | module.exports = { 11 | query: (text, params, callback) => { 12 | console.log('Executed query', text); 13 | return pool.query(text, params, callback); 14 | } 15 | }; -------------------------------------------------------------------------------- /client/index.css: -------------------------------------------------------------------------------- 1 | @import url(https://fonts.googleapis.com/css2?family=Josefin+Sans:wght@100;400;500;600;700&display=swap); 2 | 3 | *{ 4 | margin: 0; 5 | padding: 2.5%; 6 | box-sizing: border-box; 7 | } 8 | 9 | body{ 10 | font-family: 'Josefin+Sans', sans-serif, system-ui, -apple-system, BlinkMacSystemFont, 'Segoe UI', Roboto, Oxygen, Ubuntu, Cantarell, 'Open Sans', 'Helvetica Neue', sans-serif; 11 | } 12 | 13 | html, 14 | body{ 15 | scroll-behavior: smooth; 16 | } 17 | 18 | @tailwind base; 19 | @tailwind components; 20 | @tailwind utilities; -------------------------------------------------------------------------------- /client/components/App.jsx: -------------------------------------------------------------------------------- 1 | import React, {useState} from 'react'; 2 | import {BrowserRouter, Routes, Route} from 'react-router-dom'; 3 | 4 | import Login from './Login'; 5 | import Board from './Board'; 6 | import Signup from './Signup'; 7 | 8 | 9 | function App() { 10 | return ( 11 | 12 | 13 | } /> 14 | } /> 15 | } /> 16 | 17 | 18 | ) 19 | 20 | } 21 | 22 | export default App; 23 | 24 | 25 | -------------------------------------------------------------------------------- /.github/auto-assign.yml: -------------------------------------------------------------------------------- 1 | # Set to true to add reviewers to PRs 2 | addReviewers: true 3 | 4 | # Set to 'author' to add PR's author as a assignee 5 | addAssignees: author 6 | 7 | # A list of reviewers to be added to PRs (GitHub user name) 8 | reviewers: 9 | - omahomie1 10 | 11 | # A number of reviewers added to the PR 12 | # Set 0 to add all the reviewers (default: 0) 13 | numberOfReviewers: 1 14 | 15 | # A list of assignees, overrides reviewers if set 16 | assignees: 17 | - omahomie1 18 | 19 | # A number of assignees to add to the PRs 20 | # Set to 0 to add all of the assignees. 21 | # Uses numberOfReviewers if unset. 22 | numberOfAssignees: 0 23 | 24 | # A list of keywords to be skipped the process if PR's title include it 25 | skipKeywords: 26 | - wip 27 | -------------------------------------------------------------------------------- /client/components/Button.js: -------------------------------------------------------------------------------- 1 | import React from 'react'; 2 | import { Howl, Howler } from 'howler'; 3 | 4 | function Button(props) { 5 | const logKey = (e) => { 6 | console.log(e.keyCode) 7 | } 8 | 9 | // const playSound = (e) => { 10 | // const audio = document.getElementById(`${e.keyCode}`); 11 | // if (!audio) return; 12 | 13 | // audio.click();s 14 | // } 15 | 16 | // window.addEventListener('keydown', playSound); 17 | 18 | const sound = require(`./sounds/${props.sound}`).default; 19 | 20 | const clickSound = (src) => { 21 | const sound = new Howl({ 22 | src 23 | }); 24 | // sound.currentTime = 0; 25 | sound.play(); 26 | } 27 | 28 | Howler.volume(0.5); 29 | return ( 30 |
clickSound(sound)} id={props.btnKey.charCodeAt(0)} className="w-full h-0 pb-full bg-slate-300 text-black hover:bg-slate-200 shadow-2xl py-2 px-4 border-b-8 border-slate-500 transition-all ease-in-out rounded-xl cursor-pointer border"> 31 | {props.btnKey} 32 |
33 | ... 34 |
35 |
36 | ) 37 | } 38 | 39 | 40 | export default Button; -------------------------------------------------------------------------------- /server/controllers/soundboardController.js: -------------------------------------------------------------------------------- 1 | const db = require('../models/soundboardModel'); 2 | 3 | const soundboardController = {}; 4 | 5 | const createErr = (method) => { 6 | return ({ 7 | log: `This error occured in ${method} method inside soundboardController`, 8 | message: `This error occured in ${method} method inside soundboardController, check terminal for error info` 9 | }); 10 | }; 11 | 12 | soundboardController.getButtons = (req, res, next) => { 13 | const buttonStr = 'SELECT button_components.*, sounds.sound FROM button_components LEFT JOIN sounds ON button_components.sound_id = sounds.id' 14 | db.query(buttonStr) 15 | .then(data => { 16 | res.locals.buttons = data.rows; 17 | return next(); 18 | }) 19 | .catch(e => { 20 | return next(createErr('getButtons')); 21 | }) 22 | } 23 | 24 | 25 | soundboardController.getSounds = (req, res, next) => { 26 | const query = 'SELECT * FROM sounds' 27 | db 28 | .query(query) 29 | .then(data => { 30 | const arrOfSounds = []; 31 | for (let i = 0; i < data.rows.length; i++) { 32 | arrOfSounds.push(data.rows[i].sound) 33 | } 34 | res.locals.sounds = arrOfSounds; 35 | return next(); 36 | }) 37 | .catch(e => { 38 | return next(createErr('getButtons')); 39 | }) 40 | } 41 | 42 | 43 | module.exports = soundboardController; 44 | -------------------------------------------------------------------------------- /server/routes/api.js: -------------------------------------------------------------------------------- 1 | const express = require('express'); 2 | const soundboardController = require('../controllers/soundboardController'); 3 | const loginController = require('../controllers/loginController'); 4 | const router = express.Router(); 5 | 6 | 7 | router.get('/main', 8 | loginController.checkCookies, 9 | soundboardController.getButtons, 10 | (req, res) => res.status(200).json(res.locals.buttons)); 11 | 12 | 13 | // main page 14 | // app.get('/', (req, res) => { 15 | // return res.status(200).sendFile(path.join(__dirname, '../client/index.html')) 16 | // }) 17 | 18 | router.post('/login', 19 | loginController.checkCredentials, 20 | loginController.setCookie, 21 | (req, res) => res.status(200).json({})); 22 | 23 | router.delete('/logout', 24 | loginController.logout, 25 | (req, res) => res.status(200).json('success')); 26 | 27 | 28 | // router.get('/main', 29 | // loginController.checkCookie, 30 | // (req, res) => res.status(200).json({})); 31 | 32 | 33 | // router.post('/signup', (req, res) => res.status(200).json({})); 34 | 35 | // router.get('/login', (req, res) => res.status(200).json({})); 36 | 37 | 38 | 39 | // router.get('/login', (req, res) => res.status(200).json({})); 40 | 41 | // router.patch('/main', (req, res) => res.status(200).json({})); 42 | 43 | router.get('/sounds', soundboardController.getSounds, (req, res) => res.status(200).json(res.locals.sounds)); 44 | 45 | module.exports = router; -------------------------------------------------------------------------------- /package.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "demo-repo", 3 | "version": "0.2.0", 4 | "description": "A sample package.json", 5 | "main": "index.js", 6 | "scripts": { 7 | "start": "export NODE_ENV=production; nodemon server/server.js", 8 | "build": "export NODE_ENV=production; webpack", 9 | "dev": "export NODE_ENV=development; webpack-dev-server --open & nodemon server/server.js" 10 | }, 11 | "dependencies": { 12 | "@babel/core": "^7.19.3", 13 | "@babel/plugin-proposal-class-properties": "^7.18.6", 14 | "@babel/preset-env": "^7.19.3", 15 | "@babel/preset-react": "^7.18.6", 16 | "@primer/css": "17.0.1", 17 | "babel-loader": "^8.2.5", 18 | "cookie-parser": "^1.4.6", 19 | "css-loader": "^6.7.1", 20 | "express": "^4.18.1", 21 | "file-loader": "^6.2.0", 22 | "howler": "^2.2.3", 23 | "html-webpack-plugin": "^5.5.0", 24 | "node-sass": "^7.0.3", 25 | "nodemon": "^2.0.20", 26 | "pg": "^8.8.0", 27 | "postcss-loader": "^7.0.1", 28 | "postcss-preset-env": "^7.8.2", 29 | "react": "^18.2.0", 30 | "react-dom": "^18.2.0", 31 | "react-router": "^6.4.1", 32 | "react-router-dom": "^6.4.1", 33 | "sass-loader": "^13.0.2", 34 | "style-loader": "^3.3.1", 35 | "styles-loader": "^4.0.1", 36 | "webpack": "^5.74.0", 37 | "webpack-cli": "^4.10.0", 38 | "webpack-dev-server": "^4.11.1" 39 | }, 40 | "license": "MIT", 41 | "devDependencies": { 42 | "autoprefixer": "^10.4.12", 43 | "postcss": "^8.4.17", 44 | "tailwindcss": "^3.1.8" 45 | } 46 | } 47 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | # DJ SoundBoard 2 | Online launchpad that allows you to customize a grid of buttons and play them using your keyboard 3 | 4 | ## Overview 5 | 6 | ### Techstack 7 | 1. React 8 | 2. React Router 9 | 3. Howler - audio library 10 | 4. SQL 11 | 5. Express 12 | 6. Node 13 | 7. Tailwind - styling 14 | 15 | ### Diagrams 16 | * Importable Scene to [Excalidraw](./client/components/img/soundboard-excalidraw.png) 17 | -Contains mapping of frontend and backend 18 | * [ER Diagram](./client/components/img/Database.png) 19 | 20 | ### Setup 21 | 1. Execute npm install 22 | 2. devServer - use npm run dev 23 | 3. production - **sounds currently not working** 24 | 1. npm run build 25 | 2. npm start 26 | 4. Login 27 | * At server startup you will be auto directed to a login page. Please enter the below credentials to use the sound board 28 | * Username: tracy 29 | * Password: ilovedogs 30 | 31 | ### Working features 32 | 1. Login/Logout 33 | 2. Cookies and Sessions 34 | *unable to bypass login screen without correct credentials 35 | 3. playing sounds via mouse click and/or keyboard 36 | 4. Database storing button keyboard value and sound file name 37 | 38 | ### Notes 39 | 1. Sounds currently not playing while in production 40 | 1. You may want to change the URI for the SQL database for better configuring. 41 | [URI Location](server/models/soundboardModel.js) 42 | 2. Importing new sounds - 43 | 1. Download sounds to your local machine first 44 | 2. Test sounds using your preferred media player 45 | 3. Drag and drop into [sound](client/components/sounds) 46 | 3. Keyboard values on board are supposed to be in the following order for better UX, but order got rearranged working on an imcomplete feature. 47 | * Q W E 48 | * A S D 49 | * Z X C 50 | 51 | 52 | 53 | 54 | 55 | 56 | -------------------------------------------------------------------------------- /webpack.config.js: -------------------------------------------------------------------------------- 1 | const path = require('path'); 2 | const HTMLWebPackPlugin = require('html-webpack-plugin'); 3 | const environment = process.env.Node_ENV; 4 | 5 | module.exports = { 6 | // add mode 7 | mode: environment, 8 | // define entry point 9 | entry: './client/index.js', 10 | // define output 11 | output: { 12 | path: path.resolve(__dirname, './build'), 13 | filename: 'bundle.js', 14 | publicPath: '/' 15 | }, 16 | // add plugin(s) html webpack 17 | plugins: [ 18 | new HTMLWebPackPlugin({ 19 | template: './client/index.html' 20 | }) 21 | ], 22 | 23 | // add rules with all necessary loader and presets 24 | module: { 25 | rules: [ 26 | { 27 | test: /\.(js|jsx)$/, 28 | exclude: /node_modules/, 29 | use: { 30 | loader: 'babel-loader', 31 | options: { 32 | presets: ['@babel/preset-env', '@babel/preset-react'] 33 | } 34 | } 35 | }, 36 | { 37 | test: /\.css$/i, 38 | use: ['style-loader', 'css-loader', 'postcss-loader'], 39 | }, 40 | { 41 | test: /\.png|svg|jpg|gif$/, 42 | use: [ 43 | { 44 | loader: 'file-loader', 45 | options: { 46 | name: '[name].[ext]' 47 | } 48 | } 49 | ] 50 | }, 51 | { 52 | test: /\.(wav|mp3)$/, 53 | use: [ 54 | { 55 | loader: 'file-loader', 56 | options: { 57 | name: '[name].[ext]' 58 | } 59 | } 60 | ] 61 | }, 62 | ], 63 | }, 64 | 65 | // set up dev server 66 | devServer: { 67 | static: { 68 | directory: path.join(__dirname, './build'), // path.resolve => user/tommyli/desktop/scractchproject/soundboard/build 69 | }, 70 | historyApiFallback: true, 71 | // set up proxy 72 | compress: true, 73 | port: 8080, 74 | hot: true, 75 | proxy: { 76 | '/api': 'http://localhost:3000' // localhost:3000/ 77 | } 78 | } 79 | 80 | }; -------------------------------------------------------------------------------- /server/server.js: -------------------------------------------------------------------------------- 1 | const express = require('express'); 2 | const app = express(); 3 | const path = require('path'); 4 | const apiRouter = require('./routes/api'); 5 | const cookieParser = require('cookie-parser'); 6 | 7 | app.use(express.json()); 8 | app.use(express.urlencoded({ extended: true })); 9 | app.use(cookieParser()); 10 | 11 | // send to api router for requests to database 12 | app.use('/api', apiRouter); 13 | 14 | // must keep express.static to serve bundle.js 15 | app.use('/build', express.static(path.join(__dirname, "../build"))); 16 | 17 | app.get('/', (req, res) => { 18 | console.log('we should see this '); 19 | return res.status(200).sendFile(path.join(__dirname, '../client/index.html')) 20 | }) 21 | 22 | 23 | 24 | app.get('/*', (req, res) => { 25 | return res.status(200).sendFile(path.join(__dirname, '../client/index.html')); 26 | }); 27 | 28 | 29 | // local error handler 30 | app.use((req, res) => { 31 | // server log 32 | console.log('this endpoint does not exist in server.js'); 33 | // response to client 34 | res.status(404).send('this page does not exist'); 35 | }) 36 | 37 | // global error handler 38 | app.use((err, req, res, next) => { 39 | // create default error object 40 | let defaultErr = { 41 | log: 'there is an error with unspecified middleware', 42 | status: 500, 43 | message: { err: 'there is a server side error' } 44 | } 45 | // reassign default error object with info passed in via err arg 46 | defaultErr = Object.assign(defaultErr, err); 47 | // log server side error to server terminal 48 | console.log(defaultErr.log); 49 | // return status and client side error to client 50 | res.status(defaultErr.status).send(defaultErr.message); 51 | }); 52 | 53 | 54 | 55 | 56 | app.listen(3000, () => { 57 | console.log('Server listening on Port 3000') 58 | }); 59 | 60 | // module.exports = app; -------------------------------------------------------------------------------- /client/components/Signup.js: -------------------------------------------------------------------------------- 1 | import React from 'react'; 2 | import login from './img/logo.png' 3 | 4 | function Signup () { 5 | return( 6 | //
7 | // 8 | // 9 | // 10 | //
11 | 12 |
13 |
14 |

15 | 16 |

17 |
18 |
19 | 20 | 21 |
22 | 23 |
24 | 25 | 26 |
27 | 28 |
29 | 32 |
33 | 34 |
35 |
36 |
37 | 38 | ) 39 | } 40 | 41 | 42 | export default Signup; -------------------------------------------------------------------------------- /client/components/Board.js: -------------------------------------------------------------------------------- 1 | import React, { useState, useEffect } from 'react'; 2 | import Button from './Button'; 3 | import logo2 from './img/logo2.png' 4 | 5 | 6 | // make get request to server to verify if there is cookie first 7 | const checkCookie = () => { 8 | // make get request to which endpoint? 9 | fetch('/api/main') 10 | .then(res => res.json()) 11 | .then(data => { 12 | if (data === 'unauthorized') { 13 | // re-route request to home page if they do not have a cookie 14 | window.location.href = '/'; 15 | } else { 16 | console.log('welcome back!') 17 | } 18 | }) 19 | .catch(err =>console.log('error with checkCookie in Board.js')) 20 | }; 21 | 22 | // create a logout function 23 | const logout = () => { 24 | fetch('/api/logout', { 25 | method: 'DELETE' 26 | }) 27 | .then(res => res.json()) 28 | .then(data => { 29 | // reroute to login page after deleting cookie 30 | window.location.href = '/'; 31 | }) 32 | .catch(err =>console.log('error with logout in Board.js')) 33 | }; 34 | 35 | 36 | 37 | function Board() { 38 | checkCookie(); 39 | const playSound = (e) => { 40 | const audio = document.getElementById(`${e.keyCode}`); 41 | if (!audio) return; 42 | 43 | audio.click(); 44 | } 45 | 46 | window.addEventListener('keydown', playSound); 47 | 48 | const [state, changeState] = useState(); 49 | 50 | useEffect(() => { 51 | fetch('/api/main') 52 | .then(res => res.json()) 53 | .then(data => { 54 | console.log(data) 55 | changeState(data) 56 | }) 57 | .catch(err => console.log(err)) 58 | }, []) 59 | 60 | if (!state) { 61 | return <> 62 | } 63 | 64 | const board = [] 65 | for (let i = 0; i < 9; i++) { 66 | board.push( 72 |
73 |

74 | 75 | 76 |

77 |
78 | {board} 79 | {/* {renderButtons()} */} 80 |
81 |
82 | 83 | ) 84 | } 85 | 86 | export default Board; 87 | -------------------------------------------------------------------------------- /server/controllers/loginController.js: -------------------------------------------------------------------------------- 1 | const db = require('../models/soundboardModel'); 2 | 3 | const loginController = {}; 4 | 5 | const createErr = (method) => { 6 | return ({ 7 | log: `This error occured in ${method} method inside loginController`, 8 | message: `This error occured in ${method} method inside loginController, check terminal for error info` 9 | }); 10 | }; 11 | 12 | 13 | loginController.checkCredentials = (req, res, next) => { 14 | // get credentials from req.body and destructure them 15 | const { username, password:pw } = req.body; 16 | // console.log(pw) 17 | // create a query 18 | const query = 'SELECT * FROM person WHERE username = $1 AND password = $2;'; 19 | // query databse to see if that username and password exists 20 | db.query(query, [username, pw]) 21 | .then(dbResponse => { 22 | if (dbResponse.rows[0] === undefined) { 23 | // if file is not returned 24 | return res.status(401).json('failed login'); 25 | } else { 26 | // if a file is returned next 27 | const { username, id } = dbResponse.rows[0]; 28 | // console.log(username, id) 29 | res.locals.user = { 30 | 'username': username, 31 | 'user_id': id 32 | }; 33 | return next() 34 | } 35 | }) 36 | // err handling 37 | .catch(err => next(createErr(err))); 38 | }; 39 | 40 | loginController.setCookie = (req, res, next) => { 41 | // add current user to sessions table in database 42 | const { user_id, username } = res.locals.user; 43 | const session_id = Math.random().toString(); 44 | // creates cookie 45 | res.cookie('SSID', session_id);//add optionality to cookie? 46 | 47 | // add cookie and user to sessions table in db 48 | const add = 'INSERT INTO sessions (session, user_id) VALUES ($1, $2);' 49 | db.query(add, [session_id, user_id]) 50 | .then(res => { 51 | // console.log(res) 52 | return next(); 53 | }) 54 | .catch(err => next(createErr(err))); 55 | }; 56 | 57 | // req.cookies: { tracy: '0.023381441699068528' }, 58 | // req.cookies: { SSID: '0.023381441699068528' }, 59 | 60 | loginController.checkCookies = (req, res, next) => { 61 | // check if there is a current cookie 62 | const checkCookie = 'SELECT * FROM sessions WHERE $1 = session;'; 63 | db.query(checkCookie, [req.cookies.SSID]) // 64 | .then(dbRes => { 65 | // console.log('dbRes: ',dbRes.rows[0]); 66 | //if there is no response, redirect 67 | if (dbRes.rows[0] === undefined) { 68 | res.status(401).json('unauthorized'); 69 | } 70 | // if there is a response, proceed 71 | return next(); 72 | }) 73 | .catch(err => { next(createErr(err)) }); 74 | } 75 | 76 | loginController.logout = (req, res, next) => { 77 | // delete cookie in databse 78 | const deleteCookie = 'DELETE FROM sessions WHERE $1 = session;'; 79 | db.query(deleteCookie, [req.cookies.SSID]) // 80 | .then(dbRes => { 81 | console.log('dbRes: ',dbRes.rows[0]); 82 | return next(); 83 | }) 84 | .catch(err => { next(createErr(err)) }); 85 | } 86 | 87 | 88 | module.exports = loginController; -------------------------------------------------------------------------------- /client/components/Login.js: -------------------------------------------------------------------------------- 1 | import React from 'react'; 2 | import login from './img/logo.png' 3 | 4 | function loginClick() { 5 | const username = document.getElementById('username').value; 6 | const password = document.getElementById('password').value; 7 | console.log(username, password); 8 | if (!username) alert('Need to enter a username!'); 9 | if (!password) alert('Need to enter a password!'); 10 | // make post request to server with username and password 11 | fetch('api/login', { 12 | method: 'POST', 13 | body: JSON.stringify({ 14 | username, 15 | password 16 | }), 17 | headers: { 18 | 'Content-type': 'application/json' 19 | } 20 | }) 21 | .then(res => res.json()) 22 | .then(data => { 23 | if (data === 'failed login') { 24 | // send alert if login attemp failed 25 | alert('you have entered an incorrect username/password') 26 | } else { 27 | // if verified redirect to homepage 28 | window.location.href = '/board' 29 | } 30 | }) 31 | .catch(err => console.log('Login error: ', err)); 32 | } 33 | 34 | function Login () { 35 | return( 36 | //
37 | // 38 | // 39 | // 40 | //
41 | 42 |
43 |
44 |

45 | 46 |

47 |
48 |
49 | 50 | 51 |
52 | 53 |
54 | 55 | 56 |
57 | 58 |
59 | 62 |
63 | 64 |
65 |
66 |

Don't have an account? {window.location.href='/signup'}}>Create an Account.

67 |
68 |
69 |
70 | 71 | ) 72 | } 73 | 74 | 75 | export default Login; 76 | --------------------------------------------------------------------------------