From 93679e15d570870535aa30353fe02df29929f06e Mon Sep 17 00:00:00 2001 From: Paul Glenn Date: Mon, 3 Jul 2023 19:57:26 -0400 Subject: [PATCH 01/41] backend is ... in existence --- .env | 5 ++ .gitignore | 1 - Client/containers/MainContainer.jsx | 6 ++ Server/controller.js | 82 ++++++++++-------- Server/controller/ExerciseController | 0 Server/controller/ExerciseController.js | 53 ++++++++++++ Server/controller/UserController.js | 12 +++ Server/database/UserModel.js | 43 ++++++--- Server/database/dbConnection.js | 14 +++ Server/server.js | 52 ++++++----- package.json | 3 +- webpack.config.js | 110 ++++++++++++------------ 12 files changed, 260 insertions(+), 121 deletions(-) create mode 100644 .env delete mode 100644 Server/controller/ExerciseController create mode 100644 Server/controller/ExerciseController.js create mode 100644 Server/controller/UserController.js create mode 100644 Server/database/dbConnection.js diff --git a/.env b/.env new file mode 100644 index 0000000..17f4aad --- /dev/null +++ b/.env @@ -0,0 +1,5 @@ +NODE_ENV=development + +API_KEY="SReYt5aEyGMKzrdSe87wew==boZAObqiLCiQPGrb" + +MONGO_URI="mongodb+srv://nlspglenn:TwTpQOuWG9VYoeSh@cluster0.k6qxbtg.mongodb.net/?retryWrites=true&w=majority" \ No newline at end of file diff --git a/.gitignore b/.gitignore index 8ece3ba..d1cb904 100644 --- a/.gitignore +++ b/.gitignore @@ -1,4 +1,3 @@ node_modules package-lock.json build -.env \ No newline at end of file diff --git a/Client/containers/MainContainer.jsx b/Client/containers/MainContainer.jsx index 6fbaaec..0f7b7c8 100644 --- a/Client/containers/MainContainer.jsx +++ b/Client/containers/MainContainer.jsx @@ -40,11 +40,17 @@ const MainContainer = () => { {/* this p tag just helps us see where this MainContainer is being rendered */}

This is the MainContainer in Client/containers/MainContainer.jsx

{/* insert search bar here (input textbox and submit button) */} +
+ submit + +
+ + {/* Stretch are individual search results from query to database/API */} {stretchComponents} diff --git a/Server/controller.js b/Server/controller.js index d06c26a..e79df3f 100644 --- a/Server/controller.js +++ b/Server/controller.js @@ -1,41 +1,53 @@ // require('dotenv').config(); -// const apiKEY = process.env.API_KEY; - -// need to pass above object and the API key into the request to the API. +const apiKEY = process.env.API_KEY; +// need to pass above object and the API key into the request to the API. // we added some .env stuff, not being used as this page is sufficient - // having a .env folder for private info like an API key or database log in info is best practice - // but we didn't have time to implement/didn't need to because of the scope of this project +// having a .env folder for private info like an API key or database log in info is best practice +// but we didn't have time to implement/didn't need to because of the scope of this project - // init const StretchController, an object that stores the functionality +// init const StretchController, an object that stores the functionality const StretchController = { - // The getStretches method is a function that accepts 3 params, req, res, next, and stores the result of a fetch request to the exercises api in our - getStretches: async (req, res, next) => { - try { - // init const muscle as muscle prop of request query - const { muscle } = req.query; - // init const apiRes as output from api request - const apiRes = await fetch(`https://api.api-ninjas.com/v1/exercises?muscle=${muscle}&type=stretching`, { - method: 'GET', - headers: { 'X-Api-Key': 'SReYt5aEyGMKzrdSe87wew==boZAObqiLCiQPGrb'}, - }) - // store apiRes in res.locals - res.locals.apiRes = apiRes; - // return the invocation of next to move to next middleware - return next(); - } catch (error) { - const errorObject = { - // log to developer - log: 'Error occurred in StretchController.GetExercise', - // message to client - message: { error: 'An error has occurred in getting an exericse'}, - status: 400 - }; - // pass error object to global error handler - return next(errorObject); - } - } -} + // The getStretches method is a function that accepts 3 params, req, res, next, and stores the result of a fetch request to the exercises api in our + getStretches: async (req, res, next) => { + try { + // init const muscle as muscle prop of request query + const { muscle, name, type, difficulty } = req.query; + //console.log(req.query); + // create API request string from query parameters + let apiString = "https://api.api-ninjas.com/v1/exercises?"; + + // allow for muscle group input + if (muscle) apiString += `muscle=${muscle}`; + // allow for workout name input + if (name) apiString += `&name=${name}`; + // allow variation in workout type - since stretching is the default for the app it defaults to that + if (type) apiString += `&type=${type}`; + else apiString += `&type=stretching`; + + if (difficulty) apiString += `&difficulty=${difficulty}`; + + // init const apiRes as output from api request + const apiRes = await fetch(apiString, { + method: "GET", + headers: { "X-Api-Key": apiKEY }, + }).then((response) => response.json()); + // store apiRes in res.locals + res.locals.apiRes = apiRes; + //console.log(apiRes); + // return the invocation of next to move to next middleware + return next(); + } catch (error) { + const errorObject = { + // log to developer + log: "Error occurred in StretchController.GetExercise", + // message to client + message: { error: "An error has occurred in getting an exericse" }, + status: 400, + }; + // pass error object to global error handler + return next(errorObject); + } + }, +}; -StretchController.getStretches({muscle: 'Ilikemuscles'}, {locals: {}}, ()=>{}); - module.exports = StretchController; diff --git a/Server/controller/ExerciseController b/Server/controller/ExerciseController deleted file mode 100644 index e69de29..0000000 diff --git a/Server/controller/ExerciseController.js b/Server/controller/ExerciseController.js new file mode 100644 index 0000000..e79df3f --- /dev/null +++ b/Server/controller/ExerciseController.js @@ -0,0 +1,53 @@ +// require('dotenv').config(); +const apiKEY = process.env.API_KEY; +// need to pass above object and the API key into the request to the API. +// we added some .env stuff, not being used as this page is sufficient +// having a .env folder for private info like an API key or database log in info is best practice +// but we didn't have time to implement/didn't need to because of the scope of this project + +// init const StretchController, an object that stores the functionality +const StretchController = { + // The getStretches method is a function that accepts 3 params, req, res, next, and stores the result of a fetch request to the exercises api in our + getStretches: async (req, res, next) => { + try { + // init const muscle as muscle prop of request query + const { muscle, name, type, difficulty } = req.query; + //console.log(req.query); + // create API request string from query parameters + let apiString = "https://api.api-ninjas.com/v1/exercises?"; + + // allow for muscle group input + if (muscle) apiString += `muscle=${muscle}`; + // allow for workout name input + if (name) apiString += `&name=${name}`; + // allow variation in workout type - since stretching is the default for the app it defaults to that + if (type) apiString += `&type=${type}`; + else apiString += `&type=stretching`; + + if (difficulty) apiString += `&difficulty=${difficulty}`; + + // init const apiRes as output from api request + const apiRes = await fetch(apiString, { + method: "GET", + headers: { "X-Api-Key": apiKEY }, + }).then((response) => response.json()); + // store apiRes in res.locals + res.locals.apiRes = apiRes; + //console.log(apiRes); + // return the invocation of next to move to next middleware + return next(); + } catch (error) { + const errorObject = { + // log to developer + log: "Error occurred in StretchController.GetExercise", + // message to client + message: { error: "An error has occurred in getting an exericse" }, + status: 400, + }; + // pass error object to global error handler + return next(errorObject); + } + }, +}; + +module.exports = StretchController; diff --git a/Server/controller/UserController.js b/Server/controller/UserController.js new file mode 100644 index 0000000..2cfd73c --- /dev/null +++ b/Server/controller/UserController.js @@ -0,0 +1,12 @@ +const User = require("../database/UserModel.js"); +const userController = {} + +// create user // username, password +userController.registerUser = () => { + +} +userController.auth = () => { + +} + +module.exports = userController; diff --git a/Server/database/UserModel.js b/Server/database/UserModel.js index 3ffb7af..dc61a3e 100644 --- a/Server/database/UserModel.js +++ b/Server/database/UserModel.js @@ -1,13 +1,34 @@ -const mongoose = require('mongoose'); -// init const Schema as Schema constructor -const Schema = mongoose.Schema; - -const userSchema = new Schema({ - username: { type: String, required: true }, - email: { type: String, required: true }, - password: { type: String, required: true }, +const mongoose = require("mongoose"); +const bcrypt = require("bcrypt"); + +const userSchema = mongoose.Schema({ + username: { + type: String, + required: true, + }, + password: { + type: String, + required: true, + }, + favorites: { + type: Array, + default: [], + }, +}); + +userSchema.methods.matchPassword = async function (enteredPassword) { + return await bcrypt.compare(enteredPassword, this.password); +}; + +userSchema.pre("save", async function (next) { + if (!this.isModified("password")) { + next(); + } + + const salt = await bcrypt.genSalt(10); + this.password = await bcrypt.hash(this.password, salt); }); -// Export user model through module.exports -// The collection name should be 'user' -module.exports = mongoose.model('user', userSchema); +const User = mongoose.model("users", userSchema); + +module.exports = User; diff --git a/Server/database/dbConnection.js b/Server/database/dbConnection.js new file mode 100644 index 0000000..2f2b78f --- /dev/null +++ b/Server/database/dbConnection.js @@ -0,0 +1,14 @@ +const mongoose = require("mongoose"); +const dotenv = require("dotenv"); +const uri = process.env.MONGO_URI; +dotenv.config(); + +async function startServer() { + mongoose.set("strictQuery", false); + const connection = await mongoose.connect(uri, { + useNewUrlParser: true, + useUnifiedTopology: true, + }); + console.log(`MongoDB is connected to: ${connection.connection.host}`); +} +module.exports = startServer; diff --git a/Server/server.js b/Server/server.js index f246ec8..cca59fd 100644 --- a/Server/server.js +++ b/Server/server.js @@ -1,17 +1,21 @@ // goal: set up express routing - +// enviroment variable configuration +require("dotenv").config(); // steps // create a controller.js - // this will contain routers +// this will contain routers // ensure exports/imports are handled // declare port // define endpoints and actions -// add listener for PORT +// add listener for PORT -const express = require('express'); +const express = require("express"); const app = express(); -const path = require('path'); -const controller = require('./controller.js'); +const path = require("path"); +const controller = require("./controller/ExerciseController.js"); +const startServer = require("./database/dbConnection.js"); +const userController = require("./controller/UserController.js") + const PORT = 3000; app.use(express.json()); @@ -20,16 +24,25 @@ app.use(express.urlencoded({ extended: true })); // this will be helpful for str // want to send get request on 'submit' from the dropdown selection on home page // dropdown body be the req.body that gets sent to controller.js - // the req.body is used to create and make the API call +// the req.body is used to create and make the API call // get response from res.locals.varName and res.status().json(stretch array) - // new instance of router // const stretchRouter = express.Router(); +startServer(); + +// to create user into database // takes in body // username, password +app.post("/user", userController.registerUser, (req, res) => { + return res.status(200).json(res.locals.users) +}) +// to authenticate user based on input username and password +app.get("/login", userController.auth, (req, res) => { + return res.status(200).json(res.locals.users) +}) // /API/exercises?muscle=${muscle}&type=stretching -app.get('/api', controller.getStretches, (req, res) => { - return res.status(200).json(res.locals.apiRes); +app.get("/api", controller.getStretches, (req, res) => { + return res.status(200).json(res.locals.apiRes); }); // app.get('/api', controller.getExercise, (req, res) => { @@ -38,16 +51,15 @@ app.get('/api', controller.getStretches, (req, res) => { // global error handler app.use((err, req, res, next) => { - const defaultErr = { - log: 'Express error handler caught unknown middleware error', - status: 400, - message: { err: 'An error occurred'}, - }; - const errorObj = Object.assign({}, defaultErr, err); - console.log(errorObj.log); - return res.status(errorObj.status).json(errorObj.message); -}) - + const defaultErr = { + log: "Express error handler caught unknown middleware error", + status: 400, + message: { err: "An error occurred" }, + }; + const errorObj = Object.assign({}, defaultErr, err); + console.log(errorObj.log); + return res.status(errorObj.status).json(errorObj.message); +}); // listener diff --git a/package.json b/package.json index fc32da4..b9310a6 100644 --- a/package.json +++ b/package.json @@ -7,11 +7,12 @@ "test": "echo \"Error: no test specified\" && exit 1", "start": "NODE_ENV=production node server/server.js", "build": "NODE_ENV=production webpack", - "dev": "concurrently \"nodemon server/server.js\" \"NODE_ENV=development webpack serve --open\"" + "dev": "concurrently --kill-others \"nodemon server/server.js\" \"NODE_ENV=development webpack serve --open\"" }, "author": "Alana Herlands, Serena Romano, Diane Moon, Josh Hall, Rodrigo Samour Calderon", "license": "ISC", "dependencies": { + "bcrypt": "^5.1.0", "dotenv": "^16.3.1", "express": "^4.16.3", "mongodb": "^5.6.0", diff --git a/webpack.config.js b/webpack.config.js index 8481973..1db203c 100644 --- a/webpack.config.js +++ b/webpack.config.js @@ -1,60 +1,64 @@ // require the path -const path = require('path'); +const path = require("path"); // require html webpack plugin const HtmlWebpackPlugin = require("html-webpack-plugin"); - module.exports = { - entry: './client/index.js', - output: { - path: path.resolve(__dirname, 'build'), - filename: 'bundle.js', - }, - mode: process.env.NODE_ENV, - module: { - rules: [ - { - test: /\.jsx?/, - exclude: /node_modules/, - //at this point install these: npm install -D babel-loader @babel/core @babel/preset-env @babel/preset-react - use: { - loader: 'babel-loader', - options: { - presets: ['@babel/preset-env', '@babel/preset-react'] - } - } - }, - //at this point install these: npm install -D sass style-loader css-loader sass-loader - { - test: /\.s?css/, - use: [ - 'style-loader', - 'css-loader', - // 'sass-loader' - ] + entry: "./client/index.js", + output: { + path: path.resolve(__dirname, "build"), + filename: "bundle.js", + }, + mode: process.env.NODE_ENV, + module: { + rules: [ + { + test: /\.jsx?/, + exclude: /node_modules/, + //at this point install these: npm install -D babel-loader @babel/core @babel/preset-env @babel/preset-react + use: { + loader: "babel-loader", + options: { + presets: ["@babel/preset-env", "@babel/preset-react"], + }, }, - ] - }, - //at this point, npm install webpack-dev-server --save-dev - //at this point, npm install -D webpack-cli - //at this point, npm install --save-dev html-webpack-plugin - //also, ensure to require in HtmlWebpackPlugin at the top of this file - // npm install nodemon - //maybe npm install webpack ! we got an error that webpack command not found - //npm install - plugins: [ - new HtmlWebpackPlugin({ - template: './client/index.html', - filename: 'index.html', - //template: path.resolve(__dirname, './index.html'),//ANOTHER WAY - }), - // new Dotenv(), + }, + //at this point install these: npm install -D sass style-loader css-loader sass-loader + { + test: /\.s?css/, + use: [ + "style-loader", + "css-loader", + // 'sass-loader' + ], + }, ], - //declare devServer - devServer : { - static : { - directory : path.resolve(__dirname, 'build') - }, - port: 3000, - } -} + }, + //at this point, npm install webpack-dev-server --save-dev + //at this point, npm install -D webpack-cli + //at this point, npm install --save-dev html-webpack-plugin + //also, ensure to require in HtmlWebpackPlugin at the top of this file + // npm install nodemon + //maybe npm install webpack ! we got an error that webpack command not found + //npm install + plugins: [ + new HtmlWebpackPlugin({ + template: "./client/index.html", + filename: "index.html", + //template: path.resolve(__dirname, './index.html'),//ANOTHER WAY + }), + // new Dotenv(), + ], + //declare devServer + devServer: { + static: { + directory: path.resolve(__dirname, "build"), + }, + proxy: { + // context: ['/character','/characters'], + // target: 'http://localhost:3000' + "/api": "http://localhost:3000", + }, + //port: 8080, + }, +}; From 5f836f65f16017ed34b750cbe00552a4a1135f8e Mon Sep 17 00:00:00 2001 From: morahgeist <114309093+morahgeist@users.noreply.github.com> Date: Mon, 3 Jul 2023 18:04:12 -0700 Subject: [PATCH 02/41] initial frontend possible webpack issue --- Client/App.jsx | 24 +++- Client/actionCreator/actionCreator.js | 6 + Client/constant/actionTypes.js | 1 + Client/containers/MenuContainer.jsx | 64 +++++++++++ ...erContainer.jsx => OldHeaderContainer.jsx} | 8 +- ...MainContainer.jsx => OldMainContainer.jsx} | 0 Client/containers/StretchContainer.jsx | 19 ++++ Client/containers/TimerContainer.jsx | 18 +++ Client/index.html | 3 +- Client/index.js | 29 ++++- Client/reducers/index.js | 8 ++ Client/reducers/stretchReducer.js | 17 +++ Client/store.js | 8 ++ Client/{ => stylesheets}/styles.css | 0 package.json | 9 +- webpack.config.js | 105 +++++++++--------- 16 files changed, 251 insertions(+), 68 deletions(-) create mode 100644 Client/actionCreator/actionCreator.js create mode 100644 Client/constant/actionTypes.js create mode 100644 Client/containers/MenuContainer.jsx rename Client/containers/{HeaderContainer.jsx => OldHeaderContainer.jsx} (80%) rename Client/containers/{MainContainer.jsx => OldMainContainer.jsx} (100%) create mode 100644 Client/containers/StretchContainer.jsx create mode 100644 Client/containers/TimerContainer.jsx create mode 100644 Client/reducers/index.js create mode 100644 Client/reducers/stretchReducer.js create mode 100644 Client/store.js rename Client/{ => stylesheets}/styles.css (100%) diff --git a/Client/App.jsx b/Client/App.jsx index e2b3b8f..625fdab 100644 --- a/Client/App.jsx +++ b/Client/App.jsx @@ -1,15 +1,29 @@ +/** + * ************************************ + * + * @module + * @author Eivind Del Fierro, Morah Geist + * @date 07/03/23 + * @description main app container rendering 3 child containers + * + * ************************************ + */ + import React from 'react'; -import MainContainer from './containers/MainContainer.jsx'; -import HeaderContainer from './containers/HeaderContainer.jsx'; +import TimerContainer from './containers/TimerContainer.jsx'; +import MenuContainer from './containers/MenuContainer.jsx'; +import StretchContainer from './containers/StretchContainer.jsx'; +import './stylesheets/styles.css'; // Init func app that returns our main containers const App = () => { return (
- - + + +
); }; -export default App \ No newline at end of file +export default App; diff --git a/Client/actionCreator/actionCreator.js b/Client/actionCreator/actionCreator.js new file mode 100644 index 0000000..9561cf2 --- /dev/null +++ b/Client/actionCreator/actionCreator.js @@ -0,0 +1,6 @@ +import * as types from '../constant/actionTypes'; + +export const updateExercisesFromAPI = (array) => ({ + type: types.UPDATE_FROM_API, + payload: array, +}); diff --git a/Client/constant/actionTypes.js b/Client/constant/actionTypes.js new file mode 100644 index 0000000..d38b340 --- /dev/null +++ b/Client/constant/actionTypes.js @@ -0,0 +1 @@ +export const UPDATE_FROM_API = "UPDATE_FROM_API" \ No newline at end of file diff --git a/Client/containers/MenuContainer.jsx b/Client/containers/MenuContainer.jsx new file mode 100644 index 0000000..9b39de0 --- /dev/null +++ b/Client/containers/MenuContainer.jsx @@ -0,0 +1,64 @@ +/** + * ************************************ + * + * @module + * @author Eivind Del Fierro, Morah Geist + * @date 07/03/23 + * @description drop down menu item for selecting stretch target muscle group and difficulty + * question: can we pass the data from the drop down menus in menu container to the stretch container to render the stretch components? menu container and stretch container are siblings, do we need a parent to hold both containers? + * + * ************************************ + */ + +import React from 'react'; +import { useDispatch } from 'react-redux'; +import * as actions from '../actionCreator/actionCreator.js'; + +const MenuContainer = (prop) => { + const dispatch = useDispatch(); + + // const refreshExercises = () => { + // const muscle = document.getElementById('muscle').value; + // const difficulty = document.getElementById('difficulty').value; + // fetch( + // `http://localhost:3000/api?muscle=${muscle}&difficulty=${difficulty}&type=stretching` + // ) + // .then((data) => json(data)) + // .then((data) => dispatch(actions.updateExercisesFromAPI(data))) + // .catch((error) => console.log('Error in MenuContainer.jsx fetch', error)); + // }; + + return ( +
+ + +
+ ); +}; + +export default MenuContainer; diff --git a/Client/containers/HeaderContainer.jsx b/Client/containers/OldHeaderContainer.jsx similarity index 80% rename from Client/containers/HeaderContainer.jsx rename to Client/containers/OldHeaderContainer.jsx index 022472b..bb253a3 100644 --- a/Client/containers/HeaderContainer.jsx +++ b/Client/containers/OldHeaderContainer.jsx @@ -8,15 +8,15 @@ import '../styles.css'; const HeaderContainer = () => { // insert any logic for the HeaderContainer here return ( -