diff --git a/.env b/.env index 4ee2f7d..8a9870a 100644 --- a/.env +++ b/.env @@ -1,6 +1,8 @@ -PORT=5002 -TOKEN_ACCESS_SECRET= -DB_NAME= -DB_USER= -DB_PASSWORD= -DB_HOST= +PORT=3307 +TOKEN_ACCESS_SECRET=secret +DB_NAME=shop +DB_USER=debian-sys-maint +DB_PASSWORD=y8IS64T54fYUBfjR +DB_HOST=localhost +JWT_KEY=secret +SECRET_KEY=secret \ No newline at end of file diff --git a/controllers/recipe.js b/controllers/recipe.js index 0d945d0..6ae2a56 100644 --- a/controllers/recipe.js +++ b/controllers/recipe.js @@ -1,11 +1,65 @@ import query from '../config/db.js'; const recipeControllers = { - getAllRecipes: async (req, res) => {}, - getOneRecipe: async (req, res) => {}, - postRecipe: async (req, res) => {}, - updateRecipe: async (req, res) => {}, - deleteRecipe: async (req, res) => {}, + getAllRecipes: async (req, res) => { + try { + const recipes = await query('SELECT * FROM recipes'); + res.json(recipes); + } catch (error) { + res.status(500).send('Error retrieving recipes'); + } + }, + + getOneRecipe: async (req, res) => { + const { id } = req.params; + try { + const recipes = await query('SELECT * FROM recipes WHERE id = ?', [id]); + if (recipes.length === 0) { + return res.status(404).send('Recipe not found'); + } + res.json(recipes[0]); + } catch (error) { + res.status(500).send('Error retrieving the recipe'); + } + }, + + postRecipe: async (req, res) => { + const { title, description } = req.body; + try { + await query('INSERT INTO recipes (title, description) VALUES (?, ?)', [title, description]); + res.status(201).send('Recipe created successfully'); + } catch (error) { + res.status(500).send('Error creating the recipe'); + } + }, + + updateRecipe: async (req, res) => { + const { id } = req.params; + const { title, description } = req.body; + try { + const result = await query('UPDATE recipes SET title = ?, description = ? WHERE id = ?', [title, description, id]); + if (result.affectedRows === 0) { + return res.status(404).send('Recipe not found'); + } + res.send('Recipe updated successfully'); + } catch (error) { + res.status(500).send('Error updating the recipe'); + } + }, + + deleteRecipe: async (req, res) => { + const { id } = req.params; + try { + const result = await query('DELETE FROM recipes WHERE id = ?', [id]); + if (result.affectedRows === 0) { + return res.status(404).send('Recipe not found'); + } + res.send('Recipe deleted successfully'); + } catch (error) { + res.status(500).send('Error deleting the recipe'); + } + }, }; export default recipeControllers; + diff --git a/controllers/user.js b/controllers/user.js index b0fe546..fa382e8 100644 --- a/controllers/user.js +++ b/controllers/user.js @@ -8,11 +8,67 @@ import hashPassword from '../utils/hashPassword.js'; import query from '../config/db.js'; const userControllers = { - register: async (req, res) => {}, + register: async (req, res) => { + const { email, password } = req.body; - login: async (req, res) => {}, + // Validate email and password + if (!validateEmail(email)) { + return res.status(400).send('Invalid email format'); + } + if (!validatePassword(password)) { + return res.status(400).send('Password does not meet complexity requirements'); + } - logout: async (req, res) => {}, + // Check if the user already exists + const existingUser = await query('SELECT * FROM users WHERE email = ?', [email]); + if (existingUser.length > 0) { + return res.status(409).send('User already exists'); + } + + // Hash the password + const hashedPassword = await hashPassword(password); + + // Save the user to the database + await query('INSERT INTO users (email, password) VALUES (?, ?)', [email, hashedPassword]); + res.status(201).send('User registered successfully'); + }, + + login: async (req, res) => { + const { email, password } = req.body; + + + + // Validate email and password + if (!validateEmail(email)) { + return res.status(400).send('Invalid email or password'); + } + + + // Check if the user exists + const users = await query('SELECT * FROM users WHERE email = ?', [email]); + if (users.length === 0) { + return res.status(401).send('Invalid email or password'); + } + + + const user = users[0]; + + // Check if the password matches + const passwordMatches = await matchPasswords(password, user.password); + if (!passwordMatches) { + return res.status(401).send('Invalid email or password'); + } + + // Generate a token + const token = jwt.sign({ id: user.id }, process.env.SECRET_KEY, { expiresIn: '1h' }); + res.cookie('token', token, { httpOnly: true }); + res.send('Logged in successfully'); + }, + + logout: (req, res) => { + res.clearCookie('token'); + res.send('Logged out successfully'); + } }; export default userControllers; diff --git a/database/shop.sql b/database/shop.sql new file mode 100644 index 0000000..a3d8b6d --- /dev/null +++ b/database/shop.sql @@ -0,0 +1,27 @@ +-- MySQL dump 10.13 Distrib 8.0.19, for osx10.14 (x86_64) +-- +-- Host: 127.0.0.1 Database: world +-- ------------------------------------------------------ +-- Server version 8.0.19-debug + +/*!40101 SET @OLD_CHARACTER_SET_CLIENT=@@CHARACTER_SET_CLIENT */; +/*!40101 SET @OLD_CHARACTER_SET_RESULTS=@@CHARACTER_SET_RESULTS */; +/*!40101 SET @OLD_COLLATION_CONNECTION=@@COLLATION_CONNECTION */; +/*!50503 SET NAMES utf8mb4 */; +/*!40103 SET @OLD_TIME_ZONE=@@TIME_ZONE */; +/*!40103 SET TIME_ZONE='+00:00' */; +/*!40014 SET @OLD_UNIQUE_CHECKS=@@UNIQUE_CHECKS, UNIQUE_CHECKS=0 */; +/*!40014 SET @OLD_FOREIGN_KEY_CHECKS=@@FOREIGN_KEY_CHECKS, FOREIGN_KEY_CHECKS=0 */; +/*!40101 SET @OLD_SQL_MODE=@@SQL_MODE, SQL_MODE='NO_AUTO_VALUE_ON_ZERO' */; +/*!40111 SET @OLD_SQL_NOTES=@@SQL_NOTES, SQL_NOTES=0 */; +SET @old_autocommit=@@autocommit; + +-- +-- Current Database: `world` +-- + +/*!40000 DROP DATABASE IF EXISTS `world`*/; + +CREATE DATABASE `shop` DEFAULT CHARACTER SET utf8mb4; + +USE `shop`; \ No newline at end of file diff --git a/index.js b/index.js index dfe2189..79f5d41 100644 --- a/index.js +++ b/index.js @@ -1,7 +1,7 @@ import express from 'express'; import cookieParser from 'cookie-parser'; -import path, { dirname } from 'path'; + import { fileURLToPath } from 'url'; import createUserTable from './models/user.js'; @@ -10,13 +10,21 @@ import createRecipeTable from './models/recipe.js'; // import routes import userRoutes from './routes/user.js'; import recipeRoutes from './routes/recipe.js'; +// import dotenv from './env'; + +import path from 'path'; +// // Load environment variables from .env file +// dotenv.config(); // set port const PORT = process.env.PORT || 5009; + + // Construct path const __filename = fileURLToPath(import.meta.url); -const PATH = dirname(__filename); +const __dirname = path.dirname(__filename); +//const PATH = dirname(__filename); // initialize express const app = express(); @@ -26,8 +34,26 @@ app.use(express.json()); app.use(express.urlencoded({ extended: true })); app.use(cookieParser()); -// Serve static files -app.use(express.static(path.join(PATH, 'public'))); +// // Middleware to serve static files +// app.use(express.static(path.join(__dirname, 'public'))); + +// // Serve static files +// app.use(express.static(path.join(PATH, 'public'))); +//OST +// Route to serve login.html +app.get('/', (req, res) => { + res.sendFile(path.join(__dirname, 'public', 'login.html')); +}); + +app.get('/', (req, res) => { + res.sendFile(path.join(__dirname, 'public', 'index.html')); +}); +// Route to serve register.html +// app.get('/register', (req, res) => { +// res.sendFile(path.join(__dirname, 'public', 'register.html')); +// }); + + // create tables createUserTable(); @@ -37,6 +63,13 @@ createRecipeTable(); app.use(userRoutes); app.use(recipeRoutes); +// app.get('/', (req,res) => { + +// res.sendFile(path.join(PATH,'controllers','index.html')); + +// }); + + // error app.use((err, req, res, next) => { console.error(err); @@ -52,3 +85,7 @@ app.use('*', (req, res) => { app.listen(PORT, () => { console.log(`Server is up and running on port : ${PORT}`); }); + +// app.listen(process.env.PORT || 3000, () => { +// console.log('Server is running...'); +// }); diff --git a/middleware/verifyToken.js b/middleware/verifyToken.js index 92604ec..903817b 100644 --- a/middleware/verifyToken.js +++ b/middleware/verifyToken.js @@ -1,17 +1,20 @@ import jwt from 'jsonwebtoken'; const verifyToken = (req, res, next) => { - const token = req.cookies.token; - if (token) { - jwt.verify(token, process.env.TOKEN_ACCESS_SECRET, (err, data) => { - if (err) { - res.status(498).json({ message: 'token is not valid' }); - } - next(); - }); - } else { - res.status(498).json({ message: 'token is not valid' }); + const token = req.cookies.token || ''; + + if (!token) { + return res.status(403).send('A token is required for authentication'); + } + + try { + const decoded = jwt.verify(token, process.env.TOKEN_ACCESS_SECRET); + req.user = decoded; + } catch (err) { + return res.status(401).send('Invalid Token'); } + return next(); }; export default verifyToken; + diff --git a/models/recipe.js b/models/recipe.js index 1db0496..bb94da4 100644 --- a/models/recipe.js +++ b/models/recipe.js @@ -2,7 +2,19 @@ import query from '../config/db.js'; const createRecipeTable = async () => { try { - } catch (err) {} + const sql = ` + CREATE TABLE IF NOT EXISTS recipes ( + id INT AUTO_INCREMENT PRIMARY KEY, + title VARCHAR(255) NOT NULL, + description TEXT NOT NULL, + created_at TIMESTAMP DEFAULT CURRENT_TIMESTAMP + ) + `; + await query(sql); + console.log('Recipes table created successfully'); + } catch (err) { + console.error('Error creating recipes table:', err); + } }; -export default createRecipeTable; \ No newline at end of file +export default createRecipeTable; diff --git a/models/user.js b/models/user.js index 1c80be7..b393385 100644 --- a/models/user.js +++ b/models/user.js @@ -2,7 +2,19 @@ import query from '../config/db.js'; const createUserTable = async () => { try { - } catch (err) {} + const sql = ` + CREATE TABLE IF NOT EXISTS users ( + id INT AUTO_INCREMENT PRIMARY KEY, + email VARCHAR(255) NOT NULL UNIQUE, + password VARCHAR(255) NOT NULL, + created_at TIMESTAMP DEFAULT CURRENT_TIMESTAMP + ) + `; + await query(sql); + console.log('Users table created successfully'); + } catch (err) { + console.error('Error creating users table:', err); + } }; export default createUserTable; diff --git a/package.json b/package.json index b9025f4..88e4c34 100644 --- a/package.json +++ b/package.json @@ -3,7 +3,7 @@ "type": "module", "version": "1.0.0", "description": "", - "main": "index.js", + "main": "indPOSTex.js", "engines": { "node": "20.x" }, diff --git a/public/css/style.css b/public/css/style.css index e69de29..cd70313 100644 --- a/public/css/style.css +++ b/public/css/style.css @@ -0,0 +1,65 @@ +body { + font-family: Arial, sans-serif; + margin: 20px; + background-color: #f9f9f9; +} + +h1 { + text-align: center; + color: #333; +} + +h2 { + color: #555; +} + +form { + margin-bottom: 20px; + background-color: #fff; + padding: 20px; + border-radius: 5px; + box-shadow: 0 2px 10px rgba(0, 0, 0, 0.1); +} + +input, +textarea { + width: 100%; + padding: 10px; + margin: 5px 0; + border: 1px solid #ccc; + border-radius: 4px; + box-sizing: border-box; +} + +button { + padding: 10px; + color: white; + background-color: #28a745; + border: none; + border-radius: 4px; + cursor: pointer; +} + +button:hover { + background-color: #218838; +} + +ul { + list-style-type: none; + padding: 0; +} + +li { + background-color: #fff; + margin: 5px 0; + padding: 10px; + border-radius: 5px; + box-shadow: 0 2px 5px rgba(0, 0, 0, 0.1); + display: flex; + justify-content: space-between; + align-items: center; +} + +strong { + color: #333; +} diff --git a/public/index.html b/public/index.html new file mode 100644 index 0000000..911a003 --- /dev/null +++ b/public/index.html @@ -0,0 +1,105 @@ + + + + + + Recipe Manager + + + + + +

Recipe Manager

+ + +

Add Recipe

+
+ + + + +
+ +
+ +
+ +

All Recipes

+ + + + + + diff --git a/public/login.html b/public/login.html new file mode 100644 index 0000000..d5f8210 --- /dev/null +++ b/public/login.html @@ -0,0 +1,47 @@ + + + + + + Login - Recipe Manager + + + + +

Login

+ + +
+ + + +
+ + + + + diff --git a/public/registration.html b/public/registration.html new file mode 100644 index 0000000..4ace7e5 --- /dev/null +++ b/public/registration.html @@ -0,0 +1,53 @@ + + + + + + Register - Recipe Manager + + + + +

Register

+ + +
+ + + + +
+ + + + + diff --git a/routes/recipe.js b/routes/recipe.js index 946531f..7fc9f49 100644 --- a/routes/recipe.js +++ b/routes/recipe.js @@ -4,6 +4,28 @@ import recipeControllers from '../controllers/recipe.js'; const router = express.Router(); -// routes +// Public routes +router.get('/api/recipes', recipeControllers.getAllRecipes); +router.get('/api/recipes/:id', recipeControllers.getOneRecipe); +router.post('/api/recipes', recipeControllers.postRecipe); +router.put('/api/recipes/:id', recipeControllers.updateRecipe); +router.delete('/api/recipes/:id', recipeControllers.deleteRecipe); + + +// Protected routes +// router.post('/api/recipes', verifyToken,recipeControllers.postRecipe); +// router.put('/api/recipes/:id', verifyToken, recipeControllers.updateRecipe); +// router.delete('/api/recipes/:id', verifyToken, recipeControllers.deleteRecipe); + +// Create a new recipe (authenticated users only) +router.post('/recipes', verifyToken, recipeControllers.postRecipe); + +// Update a recipe by ID (authenticated users only) +router.put('/recipes/:id', verifyToken, recipeControllers.updateRecipe); + +// Delete a recipe by ID (authenticated users only) +router.delete('/recipes/:id', verifyToken, recipeControllers.deleteRecipe); + export default router; + diff --git a/routes/user.js b/routes/user.js index 33313ee..38c553d 100644 --- a/routes/user.js +++ b/routes/user.js @@ -3,7 +3,9 @@ import userControllers from '../controllers/user.js'; const router = express.Router(); - -// routes +// User routes +router.post('/api/register', userControllers.register); +router.post('/api/login', userControllers.login); +router.post('/api/logout', userControllers.logout); export default router;