├── .gitignore ├── README.md ├── backend ├── config │ └── db.js ├── controllers │ ├── recipesController.js │ └── userController.js ├── middleware │ ├── authMiddleware.js │ └── errorMiddleware.js ├── models │ ├── commentModel.js │ ├── recipeModel.js │ └── userModel.js ├── package-lock.json ├── package.json ├── routes │ ├── recipeRoutes.js │ └── userRoutes.js └── server.js ├── frontend ├── .gitignore ├── README.md ├── package-lock.json ├── package.json ├── public │ ├── index.html │ └── uploads │ │ ├── 66120547_716653968757263_2654443252404453376_n.jpg │ │ ├── IMG_20220224_124020.jpg │ │ ├── IMG_20220224_124200.jpg │ │ ├── Screenshot 2022-08-19 at 13.38.46.png │ │ ├── Screenshot 2022-08-19 at 13.49.23.png │ │ ├── Screenshot 2022-08-19 at 9.39.53.png │ │ ├── _tmp_IFJT2eQqQd3Ce1F4FZi9t8twc.jpg │ │ ├── aaaaaaffsdfdsfdljk.jpg │ │ ├── gazpacho.jpg │ │ ├── jljdfdfdljk.jpg │ │ ├── jljdfdfsfdsfsdfdsfdljk.jpg │ │ ├── jljljk.jpg │ │ ├── kartfdofi.jpeg │ │ ├── kartofi.jpeg │ │ ├── kartofkisyssirene.jpg │ │ ├── lukasz-niescioruk-wvIM0l6NP0o-unsplash.jpg │ │ ├── maleprofile.jpg │ │ ├── mujlkjklsak.jpg │ │ ├── muska.jpg │ │ ├── novtarator.png │ │ ├── register.png │ │ ├── shopska-salata.jpeg │ │ ├── spagetiboloneze.jpg │ │ ├── tarafdfdtor.jpg │ │ ├── tartdfsfdfsfsddsor3.jpeg │ │ ├── tartdfsfdsor3.jpeg │ │ ├── tartoch4e.jpeg │ │ ├── tartofdfdch4e.jpeg │ │ └── tikvichiki-po-gr.jpeg └── src │ ├── App.js │ ├── App.module.css │ ├── app │ └── store.js │ ├── assets │ ├── black-marble-texture.jpg │ ├── chicken-breast.jpg │ ├── chicken.jpg │ ├── chushka.jpg │ ├── chushka2.jpg │ ├── dish-kash.jpg │ ├── eggs.jpg │ ├── femaleprofile.jpg │ ├── fish.jpg │ ├── flat-lay-vegetables.jpg │ ├── fresh-raw-meat.jpg │ ├── fresh-salad.jpg │ ├── healthy-diet.jpg │ ├── healthy-dietwebp.webp │ ├── healthy-ingredients.jpg │ ├── maleprofile.jpg │ ├── menu-top.jpg │ ├── menu-top2.jpg │ ├── mushrooms-basil.jpg │ ├── mushrooms-basil2.jpg │ ├── pastaCarbonara.jpg │ ├── pink-marble-texture.jpg │ ├── pizzaHomemade.jpg │ ├── spice-pasta.jpg │ ├── spinner.gif │ ├── vegetables-table.jpg │ ├── veggie-quinoa.jpg │ ├── white-marble-texture.jpg │ └── white-painted-wall.jpg │ ├── components │ ├── Home.js │ ├── Home.module.css │ ├── Login.js │ ├── Login.module.css │ ├── PrivateRoute.js │ ├── Register.js │ ├── Register.module.css │ ├── layout │ │ ├── Button.js │ │ ├── Button.module.css │ │ ├── Footer.js │ │ ├── Footer.module.css │ │ ├── Line.js │ │ ├── Line.module.css │ │ ├── MainNavigation.js │ │ ├── MainNavigation.module.css │ │ ├── Spinner.js │ │ ├── Spinner.module.css │ │ └── UploadFile.js │ ├── profile │ │ ├── EditProfile.js │ │ ├── MyProfile.js │ │ └── MyProfile.module.css │ └── recipes │ │ ├── Comments.js │ │ ├── Comments.module.css │ │ ├── EditRecipe.js │ │ ├── EditRecipe.module.css │ │ ├── PostRecipe.js │ │ ├── PostRecipe.module.css │ │ ├── Recipes.js │ │ ├── Recipes.module.css │ │ ├── SingleIngredient.js │ │ ├── SingleIngredient.module.css │ │ ├── SingleRecipe.js │ │ ├── SingleRecipe.module.css │ │ ├── SmallRecipeItem.js │ │ └── SmallRecipeItem.module.css │ ├── features │ ├── auth │ │ ├── authService.js │ │ └── authSlice.js │ └── recipes │ │ ├── commentService.js │ │ ├── commentSlice.js │ │ ├── recipeService.js │ │ └── recipeSlice.js │ ├── index.css │ └── index.js ├── package-lock.json └── package.json /.gitignore: -------------------------------------------------------------------------------- 1 | node_modules 2 | .env -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | # react-cooking-app 2 | 3 | DESCRIPTION 4 | 5 | Cooking blog where users can register and post a recipe, edit and delete their own recipes. 6 | 7 | USERS and functionalities 8 | 9 | Guest users 10 | 11 | -- Guest users can register and login to the application and access the public part that contains the recipes 12 | 13 | register 14 | 15 | 16 | recipes 17 | 18 | 19 | LOGGED IN users 20 | 21 | They can view, update or delete their own recipe 22 | They can create new recipes 23 | 24 | 25 | addrecipe 26 | 27 | 28 | Used technologies and packages 29 | 30 | FRONTEND 31 | 32 | React v 18.2.0 33 | Font Awesome 34 | CSS3 35 | react-router-dom v 6.3.0 36 | react-toastify v 9.0.7 37 | react-redux v 8.0.2 38 | 39 | BACKEND 40 | bcryptjs v 2.4.3 41 | colors v 1.4.0 42 | dotenv v 16.0.1 43 | express 4.18.1 44 | express-async-handler v 1.2.0 45 | jsonwebtoken v 8.5.1 46 | mongoose v 6.5.0 47 | multer v 1.4.5-lts.1 48 | 49 | Get started 50 | 51 | npm i 52 | cd frontend npm i 53 | 54 | Navigate to http://localhost:3000/ 55 | 56 | 57 | -------------------------------------------------------------------------------- /backend/config/db.js: -------------------------------------------------------------------------------- 1 | const mongoose = require("mongoose"); 2 | 3 | const connectDB = async () => { 4 | try { 5 | const conn = await mongoose.connect(process.env.MONGO_URI); 6 | console.log(`MongoDB Connected ${conn.connection.host}`.cyan.underline); 7 | } catch (error) { 8 | console.log(`Error: ${error.message}`.red.underline.bold); 9 | process.exit(1); 10 | } 11 | }; 12 | 13 | module.exports = connectDB; 14 | -------------------------------------------------------------------------------- /backend/controllers/recipesController.js: -------------------------------------------------------------------------------- 1 | const asyncHandler = require("express-async-handler"); 2 | const User = require("../models/userModel"); 3 | const Recipe = require("../models/recipeModel"); 4 | const Comment = require("../models/commentModel"); 5 | 6 | // @desc Get all recipes 7 | // @route GET api/posts 8 | // @access public 9 | const getRecipes = asyncHandler(async (req, res) => { 10 | const recipes = await Recipe.find({}); 11 | 12 | if (recipes) { 13 | res.status(200).json(recipes); 14 | } 15 | }); 16 | 17 | // @desc Get latest 3 recipes 18 | // @route GET api/posts 19 | // @access public 20 | const getLastThree = asyncHandler(async (req, res) => { 21 | const recipes = await Recipe.find({}).sort({ createdAt: -1 }).limit(3); 22 | 23 | if (recipes) { 24 | res.status(200).json(recipes); 25 | } 26 | }); 27 | 28 | // @desc Get single recipe 29 | // @route api/posts/:id 30 | // @access public 31 | const getRecipeById = asyncHandler(async (req, res) => { 32 | const recipe = await Recipe.findById(req.params.id); 33 | 34 | if (!recipe) { 35 | res.status(404); 36 | throw new Error("Рецептата не е намерена"); 37 | } 38 | 39 | res.status(200).json(recipe); 40 | }); 41 | 42 | // @desc Post a recipe 43 | // @route POST /api/posts 44 | // @access for registered and logged in Users only 45 | const postRecipe = asyncHandler(async (req, res) => { 46 | const { title, preparation, suitableFor } = req.body; 47 | const photos = req.file.filename; 48 | const products = JSON.parse(req.body.products); 49 | // Validation 50 | // if (!title || !products || !preparation || !suitableFor) { 51 | // res.status(400); 52 | // throw new Error("Моля попълнете всички полета"); 53 | // } 54 | 55 | // Get user using the id in the JWT 56 | const user = await User.findById(req.user.id); 57 | 58 | if (!user) { 59 | res.status(401); 60 | throw new Error("User not found"); 61 | } 62 | 63 | // Create recipe 64 | const recipe = await Recipe.create({ 65 | user: req.user.id, 66 | title, 67 | products, 68 | preparation, 69 | suitableFor, 70 | photos, 71 | }); 72 | 73 | if (recipe) { 74 | res.status(201).json(recipe); 75 | } else { 76 | res.status(400); 77 | throw new Error("Невалидно въведена информация"); 78 | } 79 | }); 80 | 81 | // @desc Edit a recipe 82 | // @route PUT api/posts/:id 83 | // @access private 84 | const editRecipe = asyncHandler(async (req, res) => { 85 | const { title, preparation, suitableFor } = req.body; 86 | const photos = req.file.filename; 87 | const products = JSON.parse(req.body.products); 88 | const user = await User.findById(req.user.id); 89 | const recipeId = req.params; 90 | 91 | if (!user) { 92 | res.status(401); 93 | throw new Error("User not found"); 94 | } 95 | 96 | const recipe = await Recipe.findById(req.params.id); 97 | 98 | if (!recipe) { 99 | res.status(404); 100 | throw new Error("Рецептата не е намерена"); 101 | } 102 | 103 | if (recipe.user.toString() !== user.id) { 104 | res.status(401); 105 | throw new Error("Not authorized"); 106 | } 107 | 108 | const recipeUpdate = { 109 | title, 110 | products, 111 | preparation, 112 | suitableFor, 113 | photos, 114 | }; 115 | 116 | const updatedRecipe = await Recipe.findOneAndUpdate(recipeId, recipeUpdate, { 117 | new: true, 118 | }); 119 | 120 | res.status(200).json(updatedRecipe); 121 | }); 122 | 123 | // @desc get my list of recipes 124 | // @route api/posts/myRecipes 125 | // @access private 126 | const getMyRecipes = asyncHandler(async (req, res) => { 127 | // Get user using the id in the JWT 128 | const user = await User.findById(req.user.id); 129 | 130 | if (!user) { 131 | res.status(401); 132 | throw new Error("User not found"); 133 | } 134 | 135 | const recipes = await Recipe.find({ user: req.user.id }); 136 | 137 | if (recipes.user.toString() !== req.user.id) { 138 | res.status(401); 139 | throw new Error("Not authorized"); 140 | } 141 | 142 | res.status(200).json(recipes); 143 | }); 144 | 145 | // @desc Delete recipe 146 | // @route DELETE api/posts/:id 147 | // @access private 148 | const deleteRecipeById = asyncHandler(async (req, res) => { 149 | const user = await User.findById(req.user.id); 150 | if (!user) { 151 | res.status(401); 152 | throw new Error("User not found"); 153 | } 154 | 155 | const recipe = await Recipe.findById(req.params.id); 156 | 157 | if (!recipe) { 158 | res.status(404); 159 | throw new Error("Рецептата не е намерена"); 160 | } 161 | 162 | if (recipe.user.toString() !== req.user.id) { 163 | res.status(401); 164 | throw new Error("Not authorized"); 165 | } 166 | 167 | await recipe.remove(); 168 | 169 | res.status(200).json({ sucess: true }); 170 | }); 171 | 172 | // @desc Post a comment 173 | // @route POST /api/comments 174 | // @access for registered and logged in Users only 175 | const postComment = asyncHandler(async (req, res) => { 176 | const { recipeId, name, comment } = req.body; 177 | // Validation 178 | // if (!title || !products || !preparation || !suitableFor) { 179 | // res.status(400); 180 | // throw new Error("Моля попълнете всички полета"); 181 | // } 182 | 183 | // Get user using the id in the JWT 184 | const user = await User.findById(req.user.id); 185 | 186 | if (!user) { 187 | res.status(401); 188 | throw new Error("User not found"); 189 | } 190 | 191 | // POST comment 192 | const newComment = await Comment.create({ 193 | recipe: recipeId, 194 | name, 195 | comment, 196 | }); 197 | 198 | if (newComment) { 199 | res.status(201).json(newComment); 200 | } else { 201 | res.status(400); 202 | throw new Error("Невалидно въведена информация"); 203 | } 204 | }); 205 | 206 | // GET all comments 207 | // @desc Get single recipe 208 | // @route api/posts/:id 209 | // @access public 210 | const getAllComments = asyncHandler(async (req, res) => { 211 | const comments = await Comment.find({ recipe: req.params.id }); 212 | 213 | if (!comments) { 214 | res.status(404); 215 | throw new Error("Няма коментари към тази рецепта"); 216 | } 217 | 218 | res.status(200).json(comments); 219 | }); 220 | 221 | module.exports = { 222 | postRecipe, 223 | getMyRecipes, 224 | editRecipe, 225 | getRecipeById, 226 | getRecipes, 227 | deleteRecipeById, 228 | getLastThree, 229 | postComment, 230 | getAllComments, 231 | }; 232 | -------------------------------------------------------------------------------- /backend/controllers/userController.js: -------------------------------------------------------------------------------- 1 | const asyncHandler = require("express-async-handler"); 2 | const bcrypt = require("bcryptjs"); 3 | const jwt = require("jsonwebtoken"); 4 | 5 | const User = require("../models/userModel"); 6 | 7 | // @desc Register a new user 8 | // @route /api/users 9 | // @access Public 10 | const registerUser = asyncHandler(async (req, res) => { 11 | const { name, email, password, gender } = req.body; 12 | 13 | // Validation 14 | if (!name || !email || !password || !gender) { 15 | res.status(400); 16 | throw new Error("Моля въведете всички полета"); 17 | } 18 | 19 | // Find if user already exists 20 | const userExists = await User.findOne({ email }); 21 | 22 | if (userExists) { 23 | res.status(400); 24 | throw new Error("Акаунт с такъв имейл адрес вече съществува"); 25 | } 26 | 27 | // Hash password 28 | const salt = await bcrypt.genSalt(10); 29 | const hashedPassword = await bcrypt.hash(password, salt); 30 | 31 | // Create user 32 | const user = await User.create({ 33 | name, 34 | email, 35 | password: hashedPassword, 36 | gender, 37 | }); 38 | 39 | if (user) { 40 | res.status(201).json({ 41 | id: user._id, 42 | name: user.name, 43 | email: user.email, 44 | token: generateToken(user._id), 45 | gender: user.gender 46 | }); 47 | } else { 48 | res.status(400); 49 | throw new Error("Невалидни данни"); 50 | } 51 | }); 52 | 53 | // @desc Login a user 54 | // @route /api/users/login 55 | // @access Public 56 | const loginUser = asyncHandler(async (req, res) => { 57 | const { email, password } = req.body; 58 | 59 | const user = await User.findOne({ email }); 60 | 61 | // Check user and passwords matchs 62 | if (user && (await bcrypt.compare(password, user.password))) { 63 | res.status(200).json({ 64 | id: user._id, 65 | name: user.name, 66 | email: user.email, 67 | token: generateToken(user._id), 68 | }); 69 | } else { 70 | res.status(401); 71 | throw new Error("Невалидни имейл или парола"); 72 | } 73 | }); 74 | 75 | // @desc get current user 76 | // @route api/users/me 77 | // @access private 78 | const getMe = asyncHandler(async (req, res) => { 79 | const user = { 80 | id: req.user._id, 81 | email: req.user.email, 82 | name: req.user.name, 83 | }; 84 | res.status(200).json(user); 85 | }); 86 | 87 | // Generate token 88 | const generateToken = (id) => { 89 | return jwt.sign({ id }, process.env.JWT_SECRET, { expiresIn: "30d" }); 90 | }; 91 | 92 | module.exports = { 93 | registerUser, 94 | loginUser, 95 | getMe, 96 | }; 97 | 98 | // function registerUser(req, res) { 99 | // // 100 | // } 101 | -------------------------------------------------------------------------------- /backend/middleware/authMiddleware.js: -------------------------------------------------------------------------------- 1 | const express = require("express"); 2 | const jwt = require("jsonwebtoken"); 3 | const asyncHandler = require("express-async-handler"); 4 | const User = require("../models/userModel"); 5 | 6 | const protect = asyncHandler(async (req, res, next) => { 7 | let token; 8 | 9 | if ( 10 | req.headers.authorization && 11 | req.headers.authorization.startsWith("Bearer") 12 | ) { 13 | try { 14 | // Get token from header 15 | token = req.headers.authorization.split(" ")[1]; 16 | // Verify token 17 | const decoded = jwt.verify(token, process.env.JWT_SECRET); 18 | // Get user from token 19 | req.user = await User.findById(decoded.id).select("-password"); 20 | 21 | next(); 22 | } catch (error) { 23 | console.log(error); 24 | res.status(401); 25 | throw new Error("Not authorized"); 26 | } 27 | } 28 | 29 | if (!token) { 30 | res.status(401); 31 | throw new Error("Not authorized"); 32 | } 33 | }); 34 | 35 | module.exports = { protect }; 36 | -------------------------------------------------------------------------------- /backend/middleware/errorMiddleware.js: -------------------------------------------------------------------------------- 1 | const errorHandler = (err, req, res, next) => { 2 | const statusCode = res.statusCode ? res.statusCode : 500; 3 | 4 | res.status(statusCode); 5 | res.json({ 6 | message: err.message, 7 | stack: process.env.NODE_ENV === "production" ? null : err.stack, 8 | }); 9 | }; 10 | 11 | module.exports = { errorHandler }; 12 | -------------------------------------------------------------------------------- /backend/models/commentModel.js: -------------------------------------------------------------------------------- 1 | const mongoose = require("mongoose"); 2 | 3 | const commentSchema = mongoose.Schema( 4 | { 5 | recipe: { 6 | type: mongoose.Schema.Types.ObjectId, 7 | required: true, 8 | ref: "Recipe", 9 | }, 10 | name: { type: String, required: true }, 11 | comment: { type: String, required: [true, "Моля добавете коментар"] }, 12 | }, 13 | { timestamps: true } 14 | ); 15 | 16 | module.exports = mongoose.model("Comment", commentSchema); 17 | -------------------------------------------------------------------------------- /backend/models/recipeModel.js: -------------------------------------------------------------------------------- 1 | const mongoose = require("mongoose"); 2 | 3 | const recipeSchema = mongoose.Schema( 4 | { 5 | user: { 6 | type: mongoose.Schema.Types.ObjectId, 7 | required: true, 8 | ref: "User", 9 | }, 10 | title: { 11 | type: String, 12 | required: [true, "Моля добавете заглавие на рецептата"], 13 | }, 14 | products: { 15 | type: Array, 16 | required: [true, "Моля добавете продукти"], 17 | }, 18 | photos: { type: String, required: [true, "Моля добавете снимки"] }, 19 | preparation: { type: String, required: [true, "Моля добавете стъпки"] }, 20 | suitableFor: { type: Number, required: true, enum: [1, 2, 4, 6] }, 21 | }, 22 | { timestamps: true } 23 | ); 24 | 25 | module.exports = mongoose.model("Recipe", recipeSchema); 26 | -------------------------------------------------------------------------------- /backend/models/userModel.js: -------------------------------------------------------------------------------- 1 | const mongoose = require("mongoose"); 2 | 3 | const userSchema = mongoose.Schema( 4 | { 5 | name: { type: String, required: [true, "Моля добавете име"] }, 6 | email: { 7 | type: String, 8 | required: [true, "Моля добавете имейл"], 9 | unique: true, 10 | }, 11 | profilePic: { type: String, required: false }, 12 | password: { type: String, required: [true, "Моля добавете парола"] }, 13 | postedRecipes: {type: Number, required: false}, 14 | gender: {type: String, required: true, enum: ['male', 'female']} 15 | }, 16 | { timestamps: true } 17 | ); 18 | 19 | module.exports = mongoose.model("User", userSchema); 20 | -------------------------------------------------------------------------------- /backend/package.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "react-helpdesk-app", 3 | "version": "1.0.0", 4 | "description": "", 5 | "main": "index.js", 6 | "scripts": { 7 | "build": "npm install", 8 | "start": "node server.js", 9 | "server": "nodemon server.js" 10 | }, 11 | "keywords": [], 12 | "author": "", 13 | "license": "ISC", 14 | "dependencies": { 15 | "bcryptjs": "^2.4.3", 16 | "colors": "^1.4.0", 17 | "dotenv": "^16.0.1", 18 | "express": "^4.18.1", 19 | "express-async-handler": "^1.2.0", 20 | "jsonwebtoken": "^8.5.1", 21 | "mongoose": "^6.5.0", 22 | "multer": "^1.4.5-lts.1" 23 | }, 24 | "devDependencies": { 25 | "nodemon": "^2.0.19" 26 | } 27 | } 28 | -------------------------------------------------------------------------------- /backend/routes/recipeRoutes.js: -------------------------------------------------------------------------------- 1 | const express = require("express"); 2 | const router = express.Router(); 3 | const { 4 | postRecipe, 5 | getMyRecipes, 6 | editRecipe, 7 | getRecipeById, 8 | getRecipes, 9 | deleteRecipeById, 10 | getLastThree, 11 | postComment, 12 | getAllComments, 13 | } = require("../controllers/recipesController"); 14 | 15 | const { protect } = require("../middleware/authMiddleware"); 16 | const multer = require("multer"); 17 | const path = require("path"); 18 | 19 | const storage = multer.diskStorage({ 20 | destination: (req, file, cb) => { 21 | cb(null, "./frontend/public/uploads"); 22 | }, 23 | filename: (req, file, cb) => { 24 | console.log(file); 25 | 26 | cb(null, file.originalname); 27 | // UNUQIE FILENAME 28 | //cb(null, Date.now() + path.extname(file.originalname)); 29 | }, 30 | }); 31 | const upload = multer({ storage: storage }); 32 | 33 | // POST, EDIT and DELETE recipes 34 | router.post("/", protect, upload.single("photos"), postRecipe); 35 | router.put("/:id", protect, upload.single("photos"), editRecipe); 36 | router.delete("/:id", protect, deleteRecipeById); 37 | 38 | // GET list of user's recipes 39 | router.get("/myRecipes", getMyRecipes); 40 | 41 | // GET public recipes and single recipe 42 | router.get("/", getRecipes); 43 | router.get("/lastThree", getLastThree); 44 | router.get("/:id", getRecipeById); 45 | 46 | // GET, POST and DELETE comments 47 | router.post("/comments", protect, postComment); 48 | router.get("/:id/comments", getAllComments); 49 | 50 | module.exports = router; 51 | -------------------------------------------------------------------------------- /backend/routes/userRoutes.js: -------------------------------------------------------------------------------- 1 | const express = require("express"); 2 | const router = express.Router(); 3 | const { 4 | registerUser, 5 | loginUser, 6 | getMe, 7 | } = require("../controllers/userController"); 8 | 9 | const { protect } = require("../middleware/authMiddleware"); 10 | 11 | router.post("/", registerUser); 12 | router.post("/login", loginUser); 13 | router.get("/me", protect, getMe); 14 | 15 | module.exports = router; 16 | -------------------------------------------------------------------------------- /backend/server.js: -------------------------------------------------------------------------------- 1 | const express = require("express"); 2 | const path = require("path"); 3 | const dotenv = require("dotenv").config(); 4 | const colors = require("colors"); 5 | const connectDB = require("./config/db"); 6 | const { errorHandler } = require("./middleware/errorMiddleware"); 7 | const PORT = process.env.PORT || 8000; 8 | 9 | // Connect to Database 10 | connectDB(); 11 | 12 | const app = express(); 13 | 14 | app.use(express.json()); 15 | 16 | app.use(express.urlencoded({ extended: false })); 17 | 18 | app.use((req, res, next) => { 19 | res.setHeader("Access-Control-Allow-Credentials", "true"); 20 | res.header("Access-Control-Allow-Origin", "*"); 21 | res.header( 22 | "Access-Control-Allow-Headers", 23 | "Access-Control-Allow-Headers, Origin, X-Requested-With, Content-Type, Accept, Authorization, Access-Control-Request-Method, Access-Control-Request-Headers" 24 | ); 25 | res.header( 26 | "Access-Control-Allow-Methods", 27 | "GET, POST, PUT, PATCH, DELETE, OPTIONS" 28 | ); 29 | next(); 30 | }); 31 | 32 | // Routes 33 | app.use("/api/users", require("./routes/userRoutes")); 34 | app.use("/api/posts", require("./routes/recipeRoutes")); 35 | 36 | // Server Frontend 37 | // if (process.env.NODE_ENV === "production") { 38 | // // Set build folder as static 39 | // app.use(express.static(path.join(__dirname, "../frontend/build"))); 40 | 41 | // app.get("*", (req, res) => 42 | // res.sendFile(__dirname, "../", "frontend", "build", "index.html") 43 | // ); 44 | // } else { 45 | // // ADDED API here!!! 46 | // app.get("/api", (req, res) => { 47 | // res.send("Hello"); 48 | // }); 49 | // } 50 | 51 | app.use(errorHandler); 52 | 53 | app.listen(PORT, () => console.log(`Server started on port ${PORT}`)); 54 | -------------------------------------------------------------------------------- /frontend/.gitignore: -------------------------------------------------------------------------------- 1 | # See https://help.github.com/articles/ignoring-files/ for more about ignoring files. 2 | 3 | # dependencies 4 | /node_modules 5 | /.pnp 6 | .pnp.js 7 | 8 | # testing 9 | /coverage 10 | 11 | # production 12 | /build 13 | 14 | # misc 15 | .DS_Store 16 | .env.local 17 | .env.development.local 18 | .env.test.local 19 | .env.production.local 20 | 21 | npm-debug.log* 22 | yarn-debug.log* 23 | yarn-error.log* 24 | -------------------------------------------------------------------------------- /frontend/README.md: -------------------------------------------------------------------------------- 1 | # Getting Started with Create React App 2 | 3 | This project was bootstrapped with [Create React App](https://github.com/facebook/create-react-app). 4 | 5 | ## Available Scripts 6 | 7 | In the project directory, you can run: 8 | 9 | ### `npm start` 10 | 11 | Runs the app in the development mode.\ 12 | Open [http://localhost:3000](http://localhost:3000) to view it in your browser. 13 | 14 | The page will reload when you make changes.\ 15 | You may also see any lint errors in the console. 16 | 17 | ### `npm test` 18 | 19 | Launches the test runner in the interactive watch mode.\ 20 | See the section about [running tests](https://facebook.github.io/create-react-app/docs/running-tests) for more information. 21 | 22 | ### `npm run build` 23 | 24 | Builds the app for production to the `build` folder.\ 25 | It correctly bundles React in production mode and optimizes the build for the best performance. 26 | 27 | The build is minified and the filenames include the hashes.\ 28 | Your app is ready to be deployed! 29 | 30 | See the section about [deployment](https://facebook.github.io/create-react-app/docs/deployment) for more information. 31 | 32 | ### `npm run eject` 33 | 34 | **Note: this is a one-way operation. Once you `eject`, you can't go back!** 35 | 36 | If you aren't satisfied with the build tool and configuration choices, you can `eject` at any time. This command will remove the single build dependency from your project. 37 | 38 | Instead, it will copy all the configuration files and the transitive dependencies (webpack, Babel, ESLint, etc) right into your project so you have full control over them. All of the commands except `eject` will still work, but they will point to the copied scripts so you can tweak them. At this point you're on your own. 39 | 40 | You don't have to ever use `eject`. The curated feature set is suitable for small and middle deployments, and you shouldn't feel obligated to use this feature. However we understand that this tool wouldn't be useful if you couldn't customize it when you are ready for it. 41 | 42 | ## Learn More 43 | 44 | You can learn more in the [Create React App documentation](https://facebook.github.io/create-react-app/docs/getting-started). 45 | 46 | To learn React, check out the [React documentation](https://reactjs.org/). 47 | 48 | ### Code Splitting 49 | 50 | This section has moved here: [https://facebook.github.io/create-react-app/docs/code-splitting](https://facebook.github.io/create-react-app/docs/code-splitting) 51 | 52 | ### Analyzing the Bundle Size 53 | 54 | This section has moved here: [https://facebook.github.io/create-react-app/docs/analyzing-the-bundle-size](https://facebook.github.io/create-react-app/docs/analyzing-the-bundle-size) 55 | 56 | ### Making a Progressive Web App 57 | 58 | This section has moved here: [https://facebook.github.io/create-react-app/docs/making-a-progressive-web-app](https://facebook.github.io/create-react-app/docs/making-a-progressive-web-app) 59 | 60 | ### Advanced Configuration 61 | 62 | This section has moved here: [https://facebook.github.io/create-react-app/docs/advanced-configuration](https://facebook.github.io/create-react-app/docs/advanced-configuration) 63 | 64 | ### Deployment 65 | 66 | This section has moved here: [https://facebook.github.io/create-react-app/docs/deployment](https://facebook.github.io/create-react-app/docs/deployment) 67 | 68 | ### `npm run build` fails to minify 69 | 70 | This section has moved here: [https://facebook.github.io/create-react-app/docs/troubleshooting#npm-run-build-fails-to-minify](https://facebook.github.io/create-react-app/docs/troubleshooting#npm-run-build-fails-to-minify) 71 | -------------------------------------------------------------------------------- /frontend/package.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "frontend", 3 | "version": "0.1.0", 4 | "private": true, 5 | "dependencies": { 6 | "@reduxjs/toolkit": "^1.8.3", 7 | "@testing-library/jest-dom": "^5.16.5", 8 | "@testing-library/react": "^13.3.0", 9 | "@testing-library/user-event": "^13.5.0", 10 | "react": "^18.2.0", 11 | "react-dom": "^18.2.0", 12 | "react-icons": "^4.4.0", 13 | "react-redux": "^8.0.2", 14 | "react-router-dom": "^6.3.0", 15 | "react-scripts": "5.0.1", 16 | "react-toastify": "^9.0.7", 17 | "redux": "^4.2.0", 18 | "uuidv4": "^6.2.13", 19 | "web-vitals": "^2.1.4" 20 | }, 21 | "scripts": { 22 | "start": "react-scripts start", 23 | "build": "react-scripts build", 24 | "test": "react-scripts test", 25 | "eject": "react-scripts eject" 26 | }, 27 | "eslintConfig": { 28 | "extends": [ 29 | "react-app", 30 | "react-app/jest" 31 | ] 32 | }, 33 | "browserslist": { 34 | "production": [ 35 | ">0.2%", 36 | "not dead", 37 | "not op_mini all" 38 | ], 39 | "development": [ 40 | "last 1 chrome version", 41 | "last 1 firefox version", 42 | "last 1 safari version" 43 | ] 44 | }, 45 | "description": "This project was bootstrapped with [Create React App](https://github.com/facebook/create-react-app).", 46 | "main": "index.js", 47 | "keywords": [], 48 | "author": "", 49 | "license": "ISC" 50 | } 51 | -------------------------------------------------------------------------------- /frontend/public/index.html: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | 7 | 8 | 12 | 13 | 14 | Cooking Blog 15 | 16 | 17 | 18 |
19 | 20 | 21 | -------------------------------------------------------------------------------- /frontend/public/uploads/66120547_716653968757263_2654443252404453376_n.jpg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/onlinehub0808/reactCookingApp/ab1799695ecd939d93b53199c661197ad16180f4/frontend/public/uploads/66120547_716653968757263_2654443252404453376_n.jpg -------------------------------------------------------------------------------- /frontend/public/uploads/IMG_20220224_124020.jpg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/onlinehub0808/reactCookingApp/ab1799695ecd939d93b53199c661197ad16180f4/frontend/public/uploads/IMG_20220224_124020.jpg -------------------------------------------------------------------------------- /frontend/public/uploads/IMG_20220224_124200.jpg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/onlinehub0808/reactCookingApp/ab1799695ecd939d93b53199c661197ad16180f4/frontend/public/uploads/IMG_20220224_124200.jpg -------------------------------------------------------------------------------- /frontend/public/uploads/Screenshot 2022-08-19 at 13.38.46.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/onlinehub0808/reactCookingApp/ab1799695ecd939d93b53199c661197ad16180f4/frontend/public/uploads/Screenshot 2022-08-19 at 13.38.46.png -------------------------------------------------------------------------------- /frontend/public/uploads/Screenshot 2022-08-19 at 13.49.23.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/onlinehub0808/reactCookingApp/ab1799695ecd939d93b53199c661197ad16180f4/frontend/public/uploads/Screenshot 2022-08-19 at 13.49.23.png -------------------------------------------------------------------------------- /frontend/public/uploads/Screenshot 2022-08-19 at 9.39.53.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/onlinehub0808/reactCookingApp/ab1799695ecd939d93b53199c661197ad16180f4/frontend/public/uploads/Screenshot 2022-08-19 at 9.39.53.png -------------------------------------------------------------------------------- /frontend/public/uploads/_tmp_IFJT2eQqQd3Ce1F4FZi9t8twc.jpg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/onlinehub0808/reactCookingApp/ab1799695ecd939d93b53199c661197ad16180f4/frontend/public/uploads/_tmp_IFJT2eQqQd3Ce1F4FZi9t8twc.jpg -------------------------------------------------------------------------------- /frontend/public/uploads/aaaaaaffsdfdsfdljk.jpg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/onlinehub0808/reactCookingApp/ab1799695ecd939d93b53199c661197ad16180f4/frontend/public/uploads/aaaaaaffsdfdsfdljk.jpg -------------------------------------------------------------------------------- /frontend/public/uploads/gazpacho.jpg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/onlinehub0808/reactCookingApp/ab1799695ecd939d93b53199c661197ad16180f4/frontend/public/uploads/gazpacho.jpg -------------------------------------------------------------------------------- /frontend/public/uploads/jljdfdfdljk.jpg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/onlinehub0808/reactCookingApp/ab1799695ecd939d93b53199c661197ad16180f4/frontend/public/uploads/jljdfdfdljk.jpg -------------------------------------------------------------------------------- /frontend/public/uploads/jljdfdfsfdsfsdfdsfdljk.jpg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/onlinehub0808/reactCookingApp/ab1799695ecd939d93b53199c661197ad16180f4/frontend/public/uploads/jljdfdfsfdsfsdfdsfdljk.jpg -------------------------------------------------------------------------------- /frontend/public/uploads/jljljk.jpg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/onlinehub0808/reactCookingApp/ab1799695ecd939d93b53199c661197ad16180f4/frontend/public/uploads/jljljk.jpg -------------------------------------------------------------------------------- /frontend/public/uploads/kartfdofi.jpeg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/onlinehub0808/reactCookingApp/ab1799695ecd939d93b53199c661197ad16180f4/frontend/public/uploads/kartfdofi.jpeg -------------------------------------------------------------------------------- /frontend/public/uploads/kartofi.jpeg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/onlinehub0808/reactCookingApp/ab1799695ecd939d93b53199c661197ad16180f4/frontend/public/uploads/kartofi.jpeg -------------------------------------------------------------------------------- /frontend/public/uploads/kartofkisyssirene.jpg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/onlinehub0808/reactCookingApp/ab1799695ecd939d93b53199c661197ad16180f4/frontend/public/uploads/kartofkisyssirene.jpg -------------------------------------------------------------------------------- /frontend/public/uploads/lukasz-niescioruk-wvIM0l6NP0o-unsplash.jpg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/onlinehub0808/reactCookingApp/ab1799695ecd939d93b53199c661197ad16180f4/frontend/public/uploads/lukasz-niescioruk-wvIM0l6NP0o-unsplash.jpg -------------------------------------------------------------------------------- /frontend/public/uploads/maleprofile.jpg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/onlinehub0808/reactCookingApp/ab1799695ecd939d93b53199c661197ad16180f4/frontend/public/uploads/maleprofile.jpg -------------------------------------------------------------------------------- /frontend/public/uploads/mujlkjklsak.jpg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/onlinehub0808/reactCookingApp/ab1799695ecd939d93b53199c661197ad16180f4/frontend/public/uploads/mujlkjklsak.jpg -------------------------------------------------------------------------------- /frontend/public/uploads/muska.jpg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/onlinehub0808/reactCookingApp/ab1799695ecd939d93b53199c661197ad16180f4/frontend/public/uploads/muska.jpg -------------------------------------------------------------------------------- /frontend/public/uploads/novtarator.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/onlinehub0808/reactCookingApp/ab1799695ecd939d93b53199c661197ad16180f4/frontend/public/uploads/novtarator.png -------------------------------------------------------------------------------- /frontend/public/uploads/register.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/onlinehub0808/reactCookingApp/ab1799695ecd939d93b53199c661197ad16180f4/frontend/public/uploads/register.png -------------------------------------------------------------------------------- /frontend/public/uploads/shopska-salata.jpeg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/onlinehub0808/reactCookingApp/ab1799695ecd939d93b53199c661197ad16180f4/frontend/public/uploads/shopska-salata.jpeg -------------------------------------------------------------------------------- /frontend/public/uploads/spagetiboloneze.jpg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/onlinehub0808/reactCookingApp/ab1799695ecd939d93b53199c661197ad16180f4/frontend/public/uploads/spagetiboloneze.jpg -------------------------------------------------------------------------------- /frontend/public/uploads/tarafdfdtor.jpg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/onlinehub0808/reactCookingApp/ab1799695ecd939d93b53199c661197ad16180f4/frontend/public/uploads/tarafdfdtor.jpg -------------------------------------------------------------------------------- /frontend/public/uploads/tartdfsfdfsfsddsor3.jpeg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/onlinehub0808/reactCookingApp/ab1799695ecd939d93b53199c661197ad16180f4/frontend/public/uploads/tartdfsfdfsfsddsor3.jpeg -------------------------------------------------------------------------------- /frontend/public/uploads/tartdfsfdsor3.jpeg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/onlinehub0808/reactCookingApp/ab1799695ecd939d93b53199c661197ad16180f4/frontend/public/uploads/tartdfsfdsor3.jpeg -------------------------------------------------------------------------------- /frontend/public/uploads/tartoch4e.jpeg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/onlinehub0808/reactCookingApp/ab1799695ecd939d93b53199c661197ad16180f4/frontend/public/uploads/tartoch4e.jpeg -------------------------------------------------------------------------------- /frontend/public/uploads/tartofdfdch4e.jpeg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/onlinehub0808/reactCookingApp/ab1799695ecd939d93b53199c661197ad16180f4/frontend/public/uploads/tartofdfdch4e.jpeg -------------------------------------------------------------------------------- /frontend/public/uploads/tikvichiki-po-gr.jpeg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/onlinehub0808/reactCookingApp/ab1799695ecd939d93b53199c661197ad16180f4/frontend/public/uploads/tikvichiki-po-gr.jpeg -------------------------------------------------------------------------------- /frontend/src/App.js: -------------------------------------------------------------------------------- 1 | //import classes from "./App.module.css"; 2 | import { ToastContainer } from "react-toastify"; 3 | import "react-toastify/dist/ReactToastify.css"; 4 | 5 | import { Routes, Route } from "react-router-dom"; 6 | 7 | import Footer from "./components/layout/Footer"; 8 | import MainNavigation from "./components/layout/MainNavigation"; 9 | import PrivateRoute from "./components/PrivateRoute"; 10 | 11 | import Register from "./components/Register"; 12 | import Home from "./components/Home"; 13 | import Login from "./components/Login"; 14 | import MyProfile from "./components/profile/MyProfile"; 15 | import PostRecipe from "./components/recipes/PostRecipe"; 16 | import SingleRecipe from "./components/recipes/SingleRecipe"; 17 | import EditProfile from "./components/profile/EditProfile"; 18 | import EditRecipe from "./components/recipes/EditRecipe"; 19 | 20 | import React from "react"; 21 | 22 | function App() { 23 | return ( 24 | 25 | 26 | 27 | }> 28 | }> 29 | }> 30 | }> 31 | } /> 32 | 33 | }> 34 | } /> 35 | 36 | }> 37 | } /> 38 | {/* }> */} 39 | }> 40 | }> 41 | } /> 42 | 43 | 44 | 45 |