From 0a5be1053e52fa4c98161a944b286d0c5b137e04 Mon Sep 17 00:00:00 2001 From: EvelynDelCarmen Date: Wed, 6 Dec 2023 21:18:48 +0100 Subject: [PATCH 01/46] added the userModel.js --- backend/package.json | 4 +-- backend/userModel.js | 62 ++++++++++++++++++++++++++++++++++++++++++++ package.json | 4 +++ 3 files changed, 68 insertions(+), 2 deletions(-) create mode 100644 backend/userModel.js diff --git a/backend/package.json b/backend/package.json index 8de5c4ce0..2f3a74a48 100644 --- a/backend/package.json +++ b/backend/package.json @@ -14,7 +14,7 @@ "@babel/preset-env": "^7.16.11", "cors": "^2.8.5", "express": "^4.17.3", - "mongoose": "^8.0.0", + "mongoose": "^8.0.2", "nodemon": "^3.0.1" } -} +} \ No newline at end of file diff --git a/backend/userModel.js b/backend/userModel.js new file mode 100644 index 000000000..08e5c684d --- /dev/null +++ b/backend/userModel.js @@ -0,0 +1,62 @@ +// Import necessary libraries +import mongoose from 'mongoose'; // Mongoose is an ODM (Object Document Mapper) for MongoDB +import bcrypt from 'bcrypt'; // Bcrypt is used for password hashing + +// Define the User schema +const userSchema = new mongoose.Schema({ + // Define the structure of the User model + username: { + type: String, + required: true, // Username is a required field + unique: true, // Username must be unique + trim: true, // Trim leading and trailing whitespaces + }, + email: { + type: String, + required: true, // Email is a required field + unique: true, // Email must be unique + trim: true, // Trim leading and trailing whitespaces + }, + password: { + type: String, + required: true, // Password is a required field + trim: true, // Trim leading and trailing whitespaces + }, + accessToken: { + type: String, + }, +}); + +// Hash the password before saving it to the database +userSchema.pre('save', async function (next) { + const user = this; + + // Hash the password only if it's modified or a new user + if (user.isModified('password') || user.isNew) { + try { + const hashedPassword = await bcrypt.hash(user.password, 10); // Hash the password with a salt factor of 10 + user.password = hashedPassword; // Replace the plain password with the hashed one + } catch (error) { + throw error; // Throw an error if hashing fails + } + } + + next(); // Continue with the save operation +}); + +// Create a method to compare passwords during login +userSchema.methods.comparePassword = function (candidatePassword, callback) { + // Use bcrypt to compare the entered password with the hashed password + bcrypt.compare(candidatePassword, this.password, (error, isMatch) => { + if (error) { + return callback(error); // Return an error if the comparison fails + } + callback(null, isMatch); // Return whether the passwords match + }); +}; + +// Create the User model using the schema +const User = mongoose.model('User', userSchema); + +// Export the User model for use in other parts of the application +export default User; diff --git a/package.json b/package.json index d774b8cc3..812fd7beb 100644 --- a/package.json +++ b/package.json @@ -3,5 +3,9 @@ "version": "1.0.0", "scripts": { "postinstall": "npm install --prefix backend" + }, + "dependencies": { + "bcrypt": "^5.1.1", + "mongoose": "^8.0.2" } } From 2418ebb9db8993f5f3be0ecb6ba93b6417b206f8 Mon Sep 17 00:00:00 2001 From: EvelynDelCarmen Date: Fri, 8 Dec 2023 18:39:40 +0100 Subject: [PATCH 02/46] started with a new code --- backend/middleware/authUser.js | 43 +++++++++++++++++++++ backend/models/userModel.js | 69 ++++++++++++++++++++++++++++++++++ backend/server.js | 4 ++ backend/userModel.js | 62 ------------------------------ 4 files changed, 116 insertions(+), 62 deletions(-) create mode 100644 backend/middleware/authUser.js create mode 100644 backend/models/userModel.js delete mode 100644 backend/userModel.js diff --git a/backend/middleware/authUser.js b/backend/middleware/authUser.js new file mode 100644 index 000000000..bc4ac15a6 --- /dev/null +++ b/backend/middleware/authUser.js @@ -0,0 +1,43 @@ +import express from 'express'; +import User from '../models/userModel'; // Import the User model + +// Create an instance of the Express Router +const router = express.Router(); + +// Middleware to handle user authentication +router.post('/login', async (req, res) => { + const { username, password } = req.body; + + try { + // Find the user by username in the database + const user = await User.findOne({ username }); + + // If the user is not found, return an error + if (!user) { + return res.status(401).json({ error: 'Invalid username or password' }); + } + + // Compare the entered password with the hashed password in the database + const isPasswordValid = await user.comparePassword(password); + + // If the password is not valid, return an error + if (!isPasswordValid) { + return res.status(401).json({ error: 'Invalid username or password' }); + } + + // If the password is valid, generate and attach the access token to the request object + const accessToken = 'generateYourAccessTokenHere'; // Replace with your token generation logic + user.accessToken = accessToken; + await user.save(); // Save the updated user with the access token + + // Send a response with the access token + res.json({ accessToken }); + } catch (error) { + // Handle any errors that occur during the authentication process + res.status(500).json({ error: 'Internal Server Error' }); + } +}); + +// Export the router for use in other files +export default router; + diff --git a/backend/models/userModel.js b/backend/models/userModel.js new file mode 100644 index 000000000..2abdda79a --- /dev/null +++ b/backend/models/userModel.js @@ -0,0 +1,69 @@ +// Import necessary libraries +import mongoose from 'mongoose'; +import bcrypt from 'bcrypt'; + +// Define the user schema +const userSchema = new mongoose.Schema({ + username: { + type: String, + unique: true, // Ensures that each username is unique in the database + required: true, // Username is required + trim: true, // Removes any leading or trailing whitespaces + }, + password: { + type: String, + required: true, + }, + // Access token for the user (will be set after successful login) + accessToken: { + type: String, + }, +}); + +// Mongoose middleware to hash the password before saving to the database +userSchema.pre('save', async function (next) { + const user = this; + + // Hash the password only if it is modified or new + if (user.isModified('password')) { + try { + const hashedPassword = await bcrypt.hash(user.password, 10); // Salt rounds: 10 + user.password = hashedPassword; + } catch (error) { + next(error); // Pass any error to the next middleware + } + } + + // Move to the next middleware + next(); +}); + +// Create a method to compare entered password with hashed password in the database +userSchema.methods.comparePassword = async function (enteredPassword) { + try { + // Use bcrypt to compare the entered password with the hashed password + return await bcrypt.compare(enteredPassword, this.password); + } catch (error) { + // Throw an error if the comparison fails + throw new Error(error); + } +}; + +// Create the User model using the user schema +const User = mongoose.model('User', userSchema); + +// Export the User model for use in other files +export default User; + + +//In cryptography, a "salt" is random data that is generated and used as an additional input to a one-way function (in this case, a password hashing function). The primary purpose of using a salt is to defend against dictionary attacks, pre-computed rainbow table attacks, and similar techniques. + +// Explanation of key points: + +// userSchema: This defines the structure of the user document in the MongoDB collection. It includes fields for username, password, and accessToken. + +// pre('save') middleware: This middleware runs before saving a new user or updating an existing user. It hashes the password using bcrypt before saving it to the database. + +// comparePassword method: This method is added to the user schema to compare entered passwords with the hashed password stored in the database. + +// User model: This is created using the mongoose.model function, which takes the model name ('User') and the user schema. \ No newline at end of file diff --git a/backend/server.js b/backend/server.js index 2d7ae8aa1..db1ce4890 100644 --- a/backend/server.js +++ b/backend/server.js @@ -1,6 +1,7 @@ import express from "express"; import cors from "cors"; import mongoose from "mongoose"; +import authUserMiddleware from './authUser'; const mongoUrl = process.env.MONGO_URL || "mongodb://localhost/project-mongo"; mongoose.connect(mongoUrl, { useNewUrlParser: true, useUnifiedTopology: true }); @@ -21,6 +22,9 @@ app.get("/", (req, res) => { res.send("Hello Technigo!"); }); +// Use the authUserMiddleware for authentication routes +app.use('/auth', authUserMiddleware); + // Start the server app.listen(port, () => { console.log(`Server running on http://localhost:${port}`); diff --git a/backend/userModel.js b/backend/userModel.js deleted file mode 100644 index 08e5c684d..000000000 --- a/backend/userModel.js +++ /dev/null @@ -1,62 +0,0 @@ -// Import necessary libraries -import mongoose from 'mongoose'; // Mongoose is an ODM (Object Document Mapper) for MongoDB -import bcrypt from 'bcrypt'; // Bcrypt is used for password hashing - -// Define the User schema -const userSchema = new mongoose.Schema({ - // Define the structure of the User model - username: { - type: String, - required: true, // Username is a required field - unique: true, // Username must be unique - trim: true, // Trim leading and trailing whitespaces - }, - email: { - type: String, - required: true, // Email is a required field - unique: true, // Email must be unique - trim: true, // Trim leading and trailing whitespaces - }, - password: { - type: String, - required: true, // Password is a required field - trim: true, // Trim leading and trailing whitespaces - }, - accessToken: { - type: String, - }, -}); - -// Hash the password before saving it to the database -userSchema.pre('save', async function (next) { - const user = this; - - // Hash the password only if it's modified or a new user - if (user.isModified('password') || user.isNew) { - try { - const hashedPassword = await bcrypt.hash(user.password, 10); // Hash the password with a salt factor of 10 - user.password = hashedPassword; // Replace the plain password with the hashed one - } catch (error) { - throw error; // Throw an error if hashing fails - } - } - - next(); // Continue with the save operation -}); - -// Create a method to compare passwords during login -userSchema.methods.comparePassword = function (candidatePassword, callback) { - // Use bcrypt to compare the entered password with the hashed password - bcrypt.compare(candidatePassword, this.password, (error, isMatch) => { - if (error) { - return callback(error); // Return an error if the comparison fails - } - callback(null, isMatch); // Return whether the passwords match - }); -}; - -// Create the User model using the schema -const User = mongoose.model('User', userSchema); - -// Export the User model for use in other parts of the application -export default User; From db523f270929b2ca24671ed8750e2821f505ef62 Mon Sep 17 00:00:00 2001 From: EvelynDelCarmen Date: Fri, 8 Dec 2023 18:43:28 +0100 Subject: [PATCH 03/46] added the userRoutes.js --- backend/routes/userRoutes.js | 64 ++++++++++++++++++++++++++++++++++++ 1 file changed, 64 insertions(+) create mode 100644 backend/routes/userRoutes.js diff --git a/backend/routes/userRoutes.js b/backend/routes/userRoutes.js new file mode 100644 index 000000000..9b436bc4f --- /dev/null +++ b/backend/routes/userRoutes.js @@ -0,0 +1,64 @@ +import express from 'express'; +import User from './userModel'; // Import the User model + +// Create an instance of the Express Router +const router = express.Router(); + +// Route for user registration ('/register') +router.post('/register', async (req, res) => { + // Extract user input from the request body + const { username, password } = req.body; + + try { + // Create a new user instance with the provided username and password + const newUser = new User({ username, password }); + + // Save the new user to the database + await newUser.save(); + + // Send a success response + res.status(201).json({ message: 'User registered successfully' }); + } catch (error) { + // Handle validation errors or other issues during registration + res.status(400).json({ error: 'Registration failed. Please check your input.' }); + } +}); + +// Route for user sign-in ('/signin') +router.post('/signin', async (req, res) => { + // Extract user credentials from the request body + const { username, password } = req.body; + + try { + // Find the user by username in the database + const user = await User.findOne({ username }); + + // If the user is not found, return an error + if (!user) { + return res.status(401).json({ error: 'Invalid username or password' }); + } + + // Compare the entered password with the hashed password in the database + const isPasswordValid = await user.comparePassword(password); + + // If the password is not valid, return an error + if (!isPasswordValid) { + return res.status(401).json({ error: 'Invalid username or password' }); + } + + // Send a success response + res.json({ message: 'User signed in successfully' }); + } catch (error) { + // Handle any errors that occur during the sign-in process + res.status(500).json({ error: 'Internal Server Error' }); + } +}); + +// Export the router for use in other files +export default router; + +// Explanation: + +// The '/register' route handles user registration. It creates a new user instance, saves it to the database, and returns a success message. +// The '/signin' route handles user sign-in. It searches for the user by the provided username, compares the entered password with the hashed password in the database, and returns a success message if the credentials are valid. +// Both routes handle potential errors and return appropriate responses. \ No newline at end of file From 984c8d1e74c6717d62dcd2f0c42e07a01c3537d4 Mon Sep 17 00:00:00 2001 From: EvelynDelCarmen Date: Fri, 8 Dec 2023 18:56:39 +0100 Subject: [PATCH 04/46] secretRoutes.js --- backend/middleware/authUser.js | 7 +++++++ backend/routes/secretRoutes.js | 29 +++++++++++++++++++++++++++++ backend/server.js | 6 +++--- 3 files changed, 39 insertions(+), 3 deletions(-) create mode 100644 backend/routes/secretRoutes.js diff --git a/backend/middleware/authUser.js b/backend/middleware/authUser.js index bc4ac15a6..fe4a815dd 100644 --- a/backend/middleware/authUser.js +++ b/backend/middleware/authUser.js @@ -41,3 +41,10 @@ router.post('/login', async (req, res) => { // Export the router for use in other files export default router; +//Explanation:This middleware defines a route ('/login') for handling user login. +// It retrieves the username and password from the request body. +// It searches for the user in the database by the provided username. +// If the user is found, it compares the entered password with the hashed password in the database using the comparePassword method from the 'userModel.js'. +// If the credentials are valid, it generates an access token (you need to replace the placeholder with your actual token generation logic) and attaches it to the user in the database. +// The middleware then sends a response with the access token. + diff --git a/backend/routes/secretRoutes.js b/backend/routes/secretRoutes.js new file mode 100644 index 000000000..05eaa3975 --- /dev/null +++ b/backend/routes/secretRoutes.js @@ -0,0 +1,29 @@ +import express from 'express'; +import authenticateUser from './authUser'; // Import the authentication middleware + +// Create an instance of the Express Router +const router = express.Router(); + +// Route for accessing the secret content ('/secret') +router.get('/secret', authenticateUser, async (req, res) => { + try { + // Access the authenticated user from the request object + const authenticatedUser = req.user; + + // You can now use the authenticatedUser to fetch user-specific content from the database + // For simplicity, let's just return a success message + res.json({ message: `Hello, ${authenticatedUser.username}! Welcome to the secret route.` }); + } catch (error) { + // Handle any errors that may occur + res.status(500).json({ error: 'Internal Server Error' }); + } +}); + +// Export the router for use in other files +export default router; + +// Explanation: + +// The '/secret' route is defined with a router.get method, indicating that it responds to HTTP GET requests. +// authenticateUser is used as middleware for this route. It ensures that only authenticated users can access this route. You can place this middleware on any route that requires authentication. +// Inside the route handler, you can access the authenticated user through req.user. This is possible because the authenticateUser middleware attaches the user to the request object. \ No newline at end of file diff --git a/backend/server.js b/backend/server.js index db1ce4890..cfb71865a 100644 --- a/backend/server.js +++ b/backend/server.js @@ -1,6 +1,6 @@ -import express from "express"; -import cors from "cors"; -import mongoose from "mongoose"; +import express from 'express'; +import cors from 'cors'; +import mongoose from 'mongoose'; import authUserMiddleware from './authUser'; const mongoUrl = process.env.MONGO_URL || "mongodb://localhost/project-mongo"; From 18f7fc3fd5dc825e49c826d93503824132a55452 Mon Sep 17 00:00:00 2001 From: EvelynDelCarmen Date: Fri, 8 Dec 2023 19:45:02 +0100 Subject: [PATCH 05/46] added the Endlist --- backend/middleware/authUser.js | 6 ++++++ backend/models/userModel.js | 12 ++++++++---- backend/routes/secretRoutes.js | 2 +- backend/routes/userRoutes.js | 13 ++++++++++++- backend/server.js | 15 +++++++++++++-- package.json | 1 + 6 files changed, 41 insertions(+), 8 deletions(-) diff --git a/backend/middleware/authUser.js b/backend/middleware/authUser.js index fe4a815dd..e5e8263cb 100644 --- a/backend/middleware/authUser.js +++ b/backend/middleware/authUser.js @@ -41,6 +41,12 @@ router.post('/login', async (req, res) => { // Export the router for use in other files export default router; +// 'authUser.js': +// Uses the comparePassword method from the User model to check if the entered password matches the hashed password stored in the database. +// This middleware is applied to routes where user authentication is required. + + + //Explanation:This middleware defines a route ('/login') for handling user login. // It retrieves the username and password from the request body. // It searches for the user in the database by the provided username. diff --git a/backend/models/userModel.js b/backend/models/userModel.js index 2abdda79a..5f87db5d2 100644 --- a/backend/models/userModel.js +++ b/backend/models/userModel.js @@ -56,14 +56,18 @@ const User = mongoose.model('User', userSchema); export default User; +// 'userModel.js': +// Contains a Mongoose middleware (pre-save hook) that automatically hashes the password before saving it to the database. +// This middleware is applied to the User schema using userSchema.pre('save', ...). +// The hashed password is stored in the database. + + //In cryptography, a "salt" is random data that is generated and used as an additional input to a one-way function (in this case, a password hashing function). The primary purpose of using a salt is to defend against dictionary attacks, pre-computed rainbow table attacks, and similar techniques. -// Explanation of key points: +// Explanation of key points: // userSchema: This defines the structure of the user document in the MongoDB collection. It includes fields for username, password, and accessToken. - // pre('save') middleware: This middleware runs before saving a new user or updating an existing user. It hashes the password using bcrypt before saving it to the database. - // comparePassword method: This method is added to the user schema to compare entered passwords with the hashed password stored in the database. +// User model: This is created using the mongoose.model function, which takes the model name ('User') and the user schema. -// User model: This is created using the mongoose.model function, which takes the model name ('User') and the user schema. \ No newline at end of file diff --git a/backend/routes/secretRoutes.js b/backend/routes/secretRoutes.js index 05eaa3975..7e5e20b99 100644 --- a/backend/routes/secretRoutes.js +++ b/backend/routes/secretRoutes.js @@ -1,5 +1,5 @@ import express from 'express'; -import authenticateUser from './authUser'; // Import the authentication middleware +import authenticateUser from '../middleware/authUser'; // Import the authentication middleware // Create an instance of the Express Router const router = express.Router(); diff --git a/backend/routes/userRoutes.js b/backend/routes/userRoutes.js index 9b436bc4f..2add60e66 100644 --- a/backend/routes/userRoutes.js +++ b/backend/routes/userRoutes.js @@ -61,4 +61,15 @@ export default router; // The '/register' route handles user registration. It creates a new user instance, saves it to the database, and returns a success message. // The '/signin' route handles user sign-in. It searches for the user by the provided username, compares the entered password with the hashed password in the database, and returns a success message if the credentials are valid. -// Both routes handle potential errors and return appropriate responses. \ No newline at end of file +// Both routes handle potential errors and return appropriate responses. + +// Registration (/register route): + +// When a new user is registered, you create a new instance of the User model using const newUser = new User({ username, password });. +// The userModel.js file contains a pre-save middleware using bcrypt that automatically hashes the password before saving it to the database. This ensures that the actual password is not stored in the database. + +// Sign-In (/signin route): + +// When a user attempts to sign in, you find the user in the database based on the provided username using const user = await User.findOne({ username });. +// If the user is found, you use the comparePassword method from the userModel.js file. This method compares the entered password with the hashed password stored in the database. It internally uses bcrypt to perform this comparison. +// If the password is valid, the user is signed in successfully. \ No newline at end of file diff --git a/backend/server.js b/backend/server.js index cfb71865a..5e5970f3c 100644 --- a/backend/server.js +++ b/backend/server.js @@ -1,7 +1,10 @@ import express from 'express'; import cors from 'cors'; import mongoose from 'mongoose'; -import authUserMiddleware from './authUser'; +import authUserMiddleware from '../backend/middleware/authUser'; +import userRoutes from './models/userModel'; +import secretRoute from './routes/secretRoutes'; +import listEndpoints from 'express-list-endpoints'; const mongoUrl = process.env.MONGO_URL || "mongodb://localhost/project-mongo"; mongoose.connect(mongoUrl, { useNewUrlParser: true, useUnifiedTopology: true }); @@ -19,11 +22,19 @@ app.use(express.json()); // Start defining your routes here app.get("/", (req, res) => { - res.send("Hello Technigo!"); + res.json(listEndpoints(app)); }); // Use the authUserMiddleware for authentication routes app.use('/auth', authUserMiddleware); +// Use the userRoutes for registration and sign-in routes +app.use('/user', userRoutes); +// Use the secretRoute for the authenticated secret content route +app.use('/secret', secretRoute); + +// Display a list of available endpoints +console.log(listEndpoints(app)); + // Start the server app.listen(port, () => { diff --git a/package.json b/package.json index 812fd7beb..6293715ba 100644 --- a/package.json +++ b/package.json @@ -6,6 +6,7 @@ }, "dependencies": { "bcrypt": "^5.1.1", + "express-list-endpoints": "^6.0.0", "mongoose": "^8.0.2" } } From 3d453adf8b7bf0dfaa5e44cc84b303006347b2a4 Mon Sep 17 00:00:00 2001 From: EvelynDelCarmen Date: Sat, 9 Dec 2023 23:27:49 +0100 Subject: [PATCH 06/46] added the .env file --- backend/routes/userRoutes.js | 5 +++-- backend/server.js | 10 ++++++---- package.json | 1 + 3 files changed, 10 insertions(+), 6 deletions(-) diff --git a/backend/routes/userRoutes.js b/backend/routes/userRoutes.js index 2add60e66..c47cdf0c6 100644 --- a/backend/routes/userRoutes.js +++ b/backend/routes/userRoutes.js @@ -4,8 +4,8 @@ import User from './userModel'; // Import the User model // Create an instance of the Express Router const router = express.Router(); -// Route for user registration ('/register') -router.post('/register', async (req, res) => { +// Route for user registration ('/signup') +router.post('/signup', async (req, res) => { // Extract user input from the request body const { username, password } = req.body; @@ -19,6 +19,7 @@ router.post('/register', async (req, res) => { // Send a success response res.status(201).json({ message: 'User registered successfully' }); } catch (error) { + console.error(error); // Handle validation errors or other issues during registration res.status(400).json({ error: 'Registration failed. Please check your input.' }); } diff --git a/backend/server.js b/backend/server.js index 5e5970f3c..7b869f025 100644 --- a/backend/server.js +++ b/backend/server.js @@ -20,6 +20,12 @@ const app = express(); app.use(cors()); app.use(express.json()); +// Add logging middleware to log incoming requests +app.use((req, res, next) => { + console.log(`Received ${req.method} request at ${req.url}`); + next(); +}); + // Start defining your routes here app.get("/", (req, res) => { res.json(listEndpoints(app)); @@ -32,10 +38,6 @@ app.use('/user', userRoutes); // Use the secretRoute for the authenticated secret content route app.use('/secret', secretRoute); -// Display a list of available endpoints -console.log(listEndpoints(app)); - - // Start the server app.listen(port, () => { console.log(`Server running on http://localhost:${port}`); diff --git a/package.json b/package.json index 6293715ba..4f44d9fde 100644 --- a/package.json +++ b/package.json @@ -7,6 +7,7 @@ "dependencies": { "bcrypt": "^5.1.1", "express-list-endpoints": "^6.0.0", + "mongodb": "^6.3.0", "mongoose": "^8.0.2" } } From 899ffd9c417f2c4202dcb0fb2e0056688c25f240 Mon Sep 17 00:00:00 2001 From: EvelynDelCarmen Date: Sat, 9 Dec 2023 23:44:56 +0100 Subject: [PATCH 07/46] trying to get it work --- backend/.gitignore | 3 ++- backend/server.js | 5 ++++- package.json | 1 + 3 files changed, 7 insertions(+), 2 deletions(-) diff --git a/backend/.gitignore b/backend/.gitignore index 25c8fdbab..8f5e467c8 100644 --- a/backend/.gitignore +++ b/backend/.gitignore @@ -1,2 +1,3 @@ node_modules -package-lock.json \ No newline at end of file +package-lock.json +.env \ No newline at end of file diff --git a/backend/server.js b/backend/server.js index 7b869f025..63cd9a23d 100644 --- a/backend/server.js +++ b/backend/server.js @@ -5,8 +5,11 @@ import authUserMiddleware from '../backend/middleware/authUser'; import userRoutes from './models/userModel'; import secretRoute from './routes/secretRoutes'; import listEndpoints from 'express-list-endpoints'; +import 'dotenv/config'; +require('dotenv').config(); -const mongoUrl = process.env.MONGO_URL || "mongodb://localhost/project-mongo"; + +const mongoUrl = process.env.MONGO_URI || "mongodb://localhost/project-mongo"; mongoose.connect(mongoUrl, { useNewUrlParser: true, useUnifiedTopology: true }); mongoose.Promise = Promise; diff --git a/package.json b/package.json index 4f44d9fde..791162209 100644 --- a/package.json +++ b/package.json @@ -6,6 +6,7 @@ }, "dependencies": { "bcrypt": "^5.1.1", + "dotenv": "^16.3.1", "express-list-endpoints": "^6.0.0", "mongodb": "^6.3.0", "mongoose": "^8.0.2" From 8dcee1959f3fad21bbc8d35175dea6392f96f72b Mon Sep 17 00:00:00 2001 From: EvelynDelCarmen Date: Sun, 10 Dec 2023 14:34:16 +0100 Subject: [PATCH 08/46] debugging --- backend/models/userModel.js | 5 +++++ backend/package.json | 3 ++- backend/routes/userRoutes.js | 13 +++++++++---- backend/server.js | 9 ++++----- 4 files changed, 20 insertions(+), 10 deletions(-) diff --git a/backend/models/userModel.js b/backend/models/userModel.js index 5f87db5d2..096bf84aa 100644 --- a/backend/models/userModel.js +++ b/backend/models/userModel.js @@ -10,6 +10,11 @@ const userSchema = new mongoose.Schema({ required: true, // Username is required trim: true, // Removes any leading or trailing whitespaces }, + email: { + type: String, + unique: true, + required: true, + }, password: { type: String, required: true, diff --git a/backend/package.json b/backend/package.json index 2f3a74a48..cf24e7521 100644 --- a/backend/package.json +++ b/backend/package.json @@ -14,7 +14,8 @@ "@babel/preset-env": "^7.16.11", "cors": "^2.8.5", "express": "^4.17.3", + "express-list-endpoints": "^6.0.0", "mongoose": "^8.0.2", "nodemon": "^3.0.1" } -} \ No newline at end of file +} diff --git a/backend/routes/userRoutes.js b/backend/routes/userRoutes.js index c47cdf0c6..05f707603 100644 --- a/backend/routes/userRoutes.js +++ b/backend/routes/userRoutes.js @@ -5,28 +5,33 @@ import User from './userModel'; // Import the User model const router = express.Router(); // Route for user registration ('/signup') -router.post('/signup', async (req, res) => { +router.post('/user/signup', async (req, res) => { + console.log('Received signup request'); // Extract user input from the request body const { username, password } = req.body; + console.log('Received POST request at /user/signup'); + try { // Create a new user instance with the provided username and password const newUser = new User({ username, password }); - // Save the new user to the database await newUser.save(); + console.log('User saved successfully'); + // Send a success response res.status(201).json({ message: 'User registered successfully' }); } catch (error) { - console.error(error); + console.error('Error during registration:', error); // Handle validation errors or other issues during registration res.status(400).json({ error: 'Registration failed. Please check your input.' }); } }); // Route for user sign-in ('/signin') -router.post('/signin', async (req, res) => { +router.post('/user/signin', async (req, res) => { + console.log('Received signin request'); // Extract user credentials from the request body const { username, password } = req.body; diff --git a/backend/server.js b/backend/server.js index 63cd9a23d..96b8bb53a 100644 --- a/backend/server.js +++ b/backend/server.js @@ -5,12 +5,11 @@ import authUserMiddleware from '../backend/middleware/authUser'; import userRoutes from './models/userModel'; import secretRoute from './routes/secretRoutes'; import listEndpoints from 'express-list-endpoints'; -import 'dotenv/config'; -require('dotenv').config(); +import dotenv from 'dotenv'; +dotenv.config(); - -const mongoUrl = process.env.MONGO_URI || "mongodb://localhost/project-mongo"; -mongoose.connect(mongoUrl, { useNewUrlParser: true, useUnifiedTopology: true }); +const mongoUrl = process.env.MONGO_URI || "mongodb://localhost:27017/project-mongo"; +mongoose.connect(mongoUrl, { useNewUrlParser: true, useUnifiedTopology: true, }); mongoose.Promise = Promise; // Defines the port the app will run on. Defaults to 8080, but can be overridden From 7ff98962272cd85a6eb39ae22985d72488061b3b Mon Sep 17 00:00:00 2001 From: EvelynDelCarmen Date: Sun, 10 Dec 2023 16:10:52 +0100 Subject: [PATCH 09/46] added the email and stuff --- backend/middleware/authUser.js | 13 +++++++++++- backend/models/userModel.js | 2 ++ backend/package.json | 2 ++ backend/server.js | 37 ++++++++++++++++++++++++---------- 4 files changed, 42 insertions(+), 12 deletions(-) diff --git a/backend/middleware/authUser.js b/backend/middleware/authUser.js index e5e8263cb..46c2816d9 100644 --- a/backend/middleware/authUser.js +++ b/backend/middleware/authUser.js @@ -1,5 +1,8 @@ import express from 'express'; import User from '../models/userModel'; // Import the User model +import jwt from 'jsonwebtoken'; +import crypto from 'crypto'; + // Create an instance of the Express Router const router = express.Router(); @@ -26,10 +29,12 @@ router.post('/login', async (req, res) => { } // If the password is valid, generate and attach the access token to the request object - const accessToken = 'generateYourAccessTokenHere'; // Replace with your token generation logic + const secretKey = generateRandomKey(); + const accessToken = jwt.sign({ userId: user._id }, secretKey, { expiresIn: '24h' }); user.accessToken = accessToken; await user.save(); // Save the updated user with the access token + // Send a response with the access token res.json({ accessToken }); } catch (error) { @@ -38,6 +43,12 @@ router.post('/login', async (req, res) => { } }); + +// Function to generate a random secret key +const generateRandomKey = () => { + return crypto.randomBytes(32).toString('hex'); +}; + // Export the router for use in other files export default router; diff --git a/backend/models/userModel.js b/backend/models/userModel.js index 096bf84aa..51238e7d8 100644 --- a/backend/models/userModel.js +++ b/backend/models/userModel.js @@ -2,6 +2,8 @@ import mongoose from 'mongoose'; import bcrypt from 'bcrypt'; + + // Define the user schema const userSchema = new mongoose.Schema({ username: { diff --git a/backend/package.json b/backend/package.json index cf24e7521..4aa6dbf02 100644 --- a/backend/package.json +++ b/backend/package.json @@ -13,8 +13,10 @@ "@babel/node": "^7.16.8", "@babel/preset-env": "^7.16.11", "cors": "^2.8.5", + "crypto": "^1.0.1", "express": "^4.17.3", "express-list-endpoints": "^6.0.0", + "jsonwebtoken": "^9.0.2", "mongoose": "^8.0.2", "nodemon": "^3.0.1" } diff --git a/backend/server.js b/backend/server.js index 96b8bb53a..ff06fc668 100644 --- a/backend/server.js +++ b/backend/server.js @@ -18,29 +18,44 @@ mongoose.Promise = Promise; const port = process.env.PORT || 8080; const app = express(); -// Add middlewares to enable cors and json body parsing -app.use(cors()); -app.use(express.json()); - -// Add logging middleware to log incoming requests -app.use((req, res, next) => { - console.log(`Received ${req.method} request at ${req.url}`); - next(); -}); - // Start defining your routes here app.get("/", (req, res) => { res.json(listEndpoints(app)); }); -// Use the authUserMiddleware for authentication routes app.use('/auth', authUserMiddleware); // Use the userRoutes for registration and sign-in routes app.use('/user', userRoutes); // Use the secretRoute for the authenticated secret content route app.use('/secret', secretRoute); +// Add middlewares to enable cors and json body parsing +app.use(cors()); +app.use(express.json()); + +// Add logging middleware to log incoming requests +app.use((req, res, next) => { + console.log(`Received ${req.method} request at ${req.url}`); + next(); +}); + // Start the server app.listen(port, () => { console.log(`Server running on http://localhost:${port}`); }); + + + + +// Use the authUserMiddleware for authentication routes +//The old code + +// app.use('/auth', (req, res, next) => { +// console.log('Auth middleware log:', req.url); +// authUserMiddleware(req, res, next); +// }); + + + + + From cb433b9b616e6328f22c0071bdc727aad4a05812 Mon Sep 17 00:00:00 2001 From: EvelynDelCarmen Date: Sun, 10 Dec 2023 20:37:50 +0100 Subject: [PATCH 10/46] I get a result in Postman --- backend/middleware/authMiddleware.js | 89 +++++++++++++++ backend/middleware/authUser.js | 67 ----------- backend/models/userModel.js | 103 +++++++++++------ backend/routes/authUserRoutes.js | 161 +++++++++++++++++++++++++++ backend/routes/secretRoutes.js | 36 +++++- backend/routes/userRoutes.js | 81 -------------- backend/server.js | 79 ++++++++----- 7 files changed, 403 insertions(+), 213 deletions(-) create mode 100644 backend/middleware/authMiddleware.js delete mode 100644 backend/middleware/authUser.js create mode 100644 backend/routes/authUserRoutes.js delete mode 100644 backend/routes/userRoutes.js diff --git a/backend/middleware/authMiddleware.js b/backend/middleware/authMiddleware.js new file mode 100644 index 000000000..e07135598 --- /dev/null +++ b/backend/middleware/authMiddleware.js @@ -0,0 +1,89 @@ +// authMiddleware.js +import jwt from 'jsonwebtoken'; + +const authenticateUser = async (req, res, next) => { + const { authorization } = req.headers; + + try { + if (!authorization || !authorization.startsWith('Bearer ')) { + return res.status(401).json({ error: 'Unauthorized' }); + } + + const token = authorization.split(' ')[1]; + const decoded = jwt.verify(token, process.env.JWT_SECRET); + + // Attach the user to the request object + req.user = await User.findById(decoded.userId); + + if (!req.user) { + return res.status(401).json({ error: 'Unauthorized' }); + } + + next(); + } catch (error) { + res.status(401).json({ error: 'Unauthorized' }); + } +}; + +export default authenticateUser; + + + + + + + +// authMiddleware.js +// import User from '../models/userModel'; +// import jwt from 'jsonwebtoken'; +// import crypto from 'crypto'; + +// // Middleware to handle user authentication +// const authenticateUser = async (req, res, next) => { +// const { authorization } = req.headers; + +// try { +// if (!authorization || !authorization.startsWith('Bearer ')) { +// return res.status(401).json({ error: 'Unauthorized' }); +// } + +// const token = authorization.split(' ')[1]; +// const secretKey = generateRandomKey(); + +// const decoded = jwt.verify(token, secretKey); + +// const user = await User.findById(decoded.userId); + +// if (!user) { +// return res.status(401).json({ error: 'Unauthorized' }); +// } + +// req.user = user; + +// next(); +// } catch (error) { +// res.status(401).json({ error: 'Unauthorized' }); +// } +// }; + +// // Function to generate a random secret key +// const generateRandomKey = () => { +// return crypto.randomBytes(32).toString('hex'); +// }; + +// export default authenticateUser; + + +// 'authUser.js': +// Uses the comparePassword method from the User model to check if the entered password matches the hashed password stored in the database. +// This middleware is applied to routes where user authentication is required. + + + +//Explanation:This middleware defines a route ('/login') for handling user login. +// It retrieves the username and password from the request body. +// It searches for the user in the database by the provided username. +// If the user is found, it compares the entered password with the hashed password in the database using the comparePassword method from the 'userModel.js'. +// If the credentials are valid, it generates an access token (you need to replace the placeholder with your actual token generation logic) and attaches it to the user in the database. +// The middleware then sends a response with the access token. + diff --git a/backend/middleware/authUser.js b/backend/middleware/authUser.js deleted file mode 100644 index 46c2816d9..000000000 --- a/backend/middleware/authUser.js +++ /dev/null @@ -1,67 +0,0 @@ -import express from 'express'; -import User from '../models/userModel'; // Import the User model -import jwt from 'jsonwebtoken'; -import crypto from 'crypto'; - - -// Create an instance of the Express Router -const router = express.Router(); - -// Middleware to handle user authentication -router.post('/login', async (req, res) => { - const { username, password } = req.body; - - try { - // Find the user by username in the database - const user = await User.findOne({ username }); - - // If the user is not found, return an error - if (!user) { - return res.status(401).json({ error: 'Invalid username or password' }); - } - - // Compare the entered password with the hashed password in the database - const isPasswordValid = await user.comparePassword(password); - - // If the password is not valid, return an error - if (!isPasswordValid) { - return res.status(401).json({ error: 'Invalid username or password' }); - } - - // If the password is valid, generate and attach the access token to the request object - const secretKey = generateRandomKey(); - const accessToken = jwt.sign({ userId: user._id }, secretKey, { expiresIn: '24h' }); - user.accessToken = accessToken; - await user.save(); // Save the updated user with the access token - - - // Send a response with the access token - res.json({ accessToken }); - } catch (error) { - // Handle any errors that occur during the authentication process - res.status(500).json({ error: 'Internal Server Error' }); - } -}); - - -// Function to generate a random secret key -const generateRandomKey = () => { - return crypto.randomBytes(32).toString('hex'); -}; - -// Export the router for use in other files -export default router; - -// 'authUser.js': -// Uses the comparePassword method from the User model to check if the entered password matches the hashed password stored in the database. -// This middleware is applied to routes where user authentication is required. - - - -//Explanation:This middleware defines a route ('/login') for handling user login. -// It retrieves the username and password from the request body. -// It searches for the user in the database by the provided username. -// If the user is found, it compares the entered password with the hashed password in the database using the comparePassword method from the 'userModel.js'. -// If the credentials are valid, it generates an access token (you need to replace the placeholder with your actual token generation logic) and attaches it to the user in the database. -// The middleware then sends a response with the access token. - diff --git a/backend/models/userModel.js b/backend/models/userModel.js index 51238e7d8..c278189f0 100644 --- a/backend/models/userModel.js +++ b/backend/models/userModel.js @@ -1,68 +1,101 @@ -// Import necessary libraries +// userModel.js import mongoose from 'mongoose'; import bcrypt from 'bcrypt'; - - -// Define the user schema const userSchema = new mongoose.Schema({ - username: { - type: String, - unique: true, // Ensures that each username is unique in the database - required: true, // Username is required - trim: true, // Removes any leading or trailing whitespaces - }, - email: { - type: String, - unique: true, - required: true, - }, - password: { - type: String, - required: true, - }, - // Access token for the user (will be set after successful login) - accessToken: { - type: String, - }, + username: { type: String, unique: true, required: true }, + email: { type: String, unique: true, required: true }, + password: { type: String, required: true }, + secretKey: { type: String }, }); -// Mongoose middleware to hash the password before saving to the database userSchema.pre('save', async function (next) { const user = this; - - // Hash the password only if it is modified or new if (user.isModified('password')) { try { - const hashedPassword = await bcrypt.hash(user.password, 10); // Salt rounds: 10 + const hashedPassword = await bcrypt.hash(user.password, 10); user.password = hashedPassword; } catch (error) { - next(error); // Pass any error to the next middleware + next(error); } } - - // Move to the next middleware next(); }); -// Create a method to compare entered password with hashed password in the database userSchema.methods.comparePassword = async function (enteredPassword) { try { - // Use bcrypt to compare the entered password with the hashed password return await bcrypt.compare(enteredPassword, this.password); } catch (error) { - // Throw an error if the comparison fails throw new Error(error); } }; -// Create the User model using the user schema const User = mongoose.model('User', userSchema); -// Export the User model for use in other files export default User; + + +// userModel.js +// import mongoose from 'mongoose'; +// import bcrypt from 'bcrypt'; + +// // Define the user schema +// const userSchema = new mongoose.Schema({ +// username: { +// type: String, +// unique: true, +// required: true, +// trim: true, +// }, +// email: { +// type: String, +// unique: true, +// required: true, +// }, +// password: { +// type: String, +// required: true, +// }, +// accessToken: { +// type: String, +// }, +// }); + +// // Mongoose middleware to hash the password before saving to the database +// userSchema.pre('save', async function (next) { +// const user = this; + +// if (user.isModified('password')) { +// try { +// const hashedPassword = await bcrypt.hash(user.password, 10); +// user.password = hashedPassword; +// } catch (error) { +// next(error); +// } +// } + +// next(); +// }); + +// // Create a method to compare entered password with hashed password in the database +// userSchema.methods.comparePassword = async function (enteredPassword) { +// try { +// return await bcrypt.compare(enteredPassword, this.password); +// } catch (error) { +// throw new Error(error); +// } +// }; + +// // Create the User model using the user schema +// const User = mongoose.model('User', userSchema); + +// // Export the User model for use in other files +// export default User; + + + // 'userModel.js': // Contains a Mongoose middleware (pre-save hook) that automatically hashes the password before saving it to the database. // This middleware is applied to the User schema using userSchema.pre('save', ...). diff --git a/backend/routes/authUserRoutes.js b/backend/routes/authUserRoutes.js new file mode 100644 index 000000000..f0cc396cb --- /dev/null +++ b/backend/routes/authUserRoutes.js @@ -0,0 +1,161 @@ +// authRoutes.js +import express from 'express'; +import User from '../models/userModel'; +import jwt from 'jsonwebtoken'; +import authenticateUser from '../middleware/authMiddleware'; +import crypto from 'crypto'; + +const router = express.Router(); + +router.post('/register', async (req, res) => { + const { username, email, password } = req.body; + + try { + // Check if the username or email is already taken + const existingUser = await User.findOne({ $or: [{ username }, { email }] }); + + if (existingUser) { + return res.status(400).json({ error: 'Username or email already exists' }); + } + + // Generate a unique secret key for the user + const secretKey = crypto.randomBytes(32).toString('hex'); + + // Create a new user instance with the secret key + const newUser = new User({ username, email, password, secretKey }); + // Save the new user to the database + await newUser.save(); + + // Use jsonwebtoken to create a token + const accessToken = jwt.sign({ userId: newUser._id }, secretKey, { expiresIn: '24h' }); + + res.status(201).json({ accessToken }); + } catch (error) { + console.error('Error during registration:', error); + res.status(500).json({ error: 'Internal Server Error' }); + } +}); + +// Route for user login ('/login') +router.post('/login', async (req, res) => { + const { username, password } = req.body; + + try { + const user = await User.findOne({ username }); + + if (!user) { + return res.status(401).json({ error: 'Invalid username or password' }); + } + + const isPasswordValid = await user.comparePassword(password); + + if (!isPasswordValid) { + return res.status(401).json({ error: 'Invalid username or password' }); + } + + // Use jsonwebtoken to create a token + const accessToken = jwt.sign({ userId: user._id }, process.env.JWT_SECRET, { expiresIn: '24h' }); + + res.json({ accessToken }); + } catch (error) { + res.status(500).json({ error: 'Internal Server Error' }); + } +}); + +router.get('/protected', authenticateUser, (req, res) => { + res.json({ message: 'This is a protected route' }); +}); + +export default router; + + + + + +// authUserRoutes.js +// import express from 'express'; +// import User from '../models/userModel'; +// import jwt from 'jsonwebtoken'; +// // import authenticateUser from '../middleware/authMiddleware'; + +// // Create an instance of the Express Router +// const router = express.Router(); + +// // Route for user login ('/login') +// router.post('/login', async (req, res) => { +// const { username, email, password } = req.body; + +// try { +// // Find the user by either username or email +// const user = await User.findOne({ $or: [{ username }, { email }] }); + +// if (!user) { +// return res.status(401).json({ error: 'Invalid username, email, or password' }); +// } + +// const isPasswordValid = await user.comparePassword(password); + +// if (!isPasswordValid) { +// return res.status(401).json({ error: 'Invalid username, email, or password' }); +// } + +// const secretKey = generateRandomKey(); +// const accessToken = jwt.sign({ userId: user._id }, secretKey, { expiresIn: '24h' }); + +// res.json({ accessToken }); +// } catch (error) { +// res.status(500).json({ error: 'Internal Server Error' }); +// } +// }); + +// // Route for user signup ('/signup') +// router.post('/signup', async (req, res) => { +// console.log('Received signup request'); +// const { username, email, password } = req.body; + +// console.log('Received POST request at /user/signup'); + +// try { +// // Check if the username or email is already in use +// const existingUser = await User.findOne({ $or: [{ username }, { email }] }); + +// if (existingUser) { +// return res.status(400).json({ error: 'Username or email already in use' }); +// } + +// // Create a new user instance with the provided username, email, and password +// const newUser = new User({ username, email, password }); +// // Save the new user to the database +// await newUser.save(); + +// console.log('User saved successfully'); + +// // Send a success response +// res.status(201).json({ message: 'User registered successfully' }); +// } catch (error) { +// console.error('Error during registration:', error); +// // Handle validation errors or other issues during registration +// res.status(500).json({ error: 'Internal Server Error' }); +// } +// }); + +// // Export the router for use in other files +// export default router; + + +// Explanation: + +// The '/register' route handles user registration. It creates a new user instance, saves it to the database, and returns a success message. +// The '/signin' route handles user sign-in. It searches for the user by the provided username, compares the entered password with the hashed password in the database, and returns a success message if the credentials are valid. +// Both routes handle potential errors and return appropriate responses. + +// Registration (/register route): + +// When a new user is registered, you create a new instance of the User model using const newUser = new User({ username, password });. +// The userModel.js file contains a pre-save middleware using bcrypt that automatically hashes the password before saving it to the database. This ensures that the actual password is not stored in the database. + +// Sign-In (/signin route): + +// When a user attempts to sign in, you find the user in the database based on the provided username using const user = await User.findOne({ username });. +// If the user is found, you use the comparePassword method from the userModel.js file. This method compares the entered password with the hashed password stored in the database. It internally uses bcrypt to perform this comparison. +// If the password is valid, the user is signed in successfully. \ No newline at end of file diff --git a/backend/routes/secretRoutes.js b/backend/routes/secretRoutes.js index 7e5e20b99..052176721 100644 --- a/backend/routes/secretRoutes.js +++ b/backend/routes/secretRoutes.js @@ -1,11 +1,11 @@ +// secretRoute.js import express from 'express'; -import authenticateUser from '../middleware/authUser'; // Import the authentication middleware +import authenticateUser from '../middleware/authMiddleware'; -// Create an instance of the Express Router const router = express.Router(); // Route for accessing the secret content ('/secret') -router.get('/secret', authenticateUser, async (req, res) => { +router.get('/', authenticateUser, async (req, res) => { try { // Access the authenticated user from the request object const authenticatedUser = req.user; @@ -19,9 +19,37 @@ router.get('/secret', authenticateUser, async (req, res) => { } }); -// Export the router for use in other files export default router; + + + + +// import express from 'express'; +// import authUserMiddleware from 'backend/middleware/authMiddleware.js'; // Import the authentication middleware + +// // Create an instance of the Express Router +// const router = express.Router(); + +// // Route for accessing the secret content ('/secret') +// router.get('/secret', authUserMiddleware, async (req, res) => { +// try { +// // Access the authenticated user from the request object +// const authenticatedUser = req.user; + +// // You can now use the authenticatedUser to fetch user-specific content from the database +// // For simplicity, let's just return a success message +// res.json({ message: `Hello, ${authenticatedUser.username}! Welcome to the secret route.` }); +// } catch (error) { +// // Handle any errors that may occur +// res.status(500).json({ error: 'Internal Server Error' }); +// } +// }); + +// // Export the router for use in other files +// export default router; + + // Explanation: // The '/secret' route is defined with a router.get method, indicating that it responds to HTTP GET requests. diff --git a/backend/routes/userRoutes.js b/backend/routes/userRoutes.js deleted file mode 100644 index 05f707603..000000000 --- a/backend/routes/userRoutes.js +++ /dev/null @@ -1,81 +0,0 @@ -import express from 'express'; -import User from './userModel'; // Import the User model - -// Create an instance of the Express Router -const router = express.Router(); - -// Route for user registration ('/signup') -router.post('/user/signup', async (req, res) => { - console.log('Received signup request'); - // Extract user input from the request body - const { username, password } = req.body; - - console.log('Received POST request at /user/signup'); - - try { - // Create a new user instance with the provided username and password - const newUser = new User({ username, password }); - // Save the new user to the database - await newUser.save(); - - console.log('User saved successfully'); - - // Send a success response - res.status(201).json({ message: 'User registered successfully' }); - } catch (error) { - console.error('Error during registration:', error); - // Handle validation errors or other issues during registration - res.status(400).json({ error: 'Registration failed. Please check your input.' }); - } -}); - -// Route for user sign-in ('/signin') -router.post('/user/signin', async (req, res) => { - console.log('Received signin request'); - // Extract user credentials from the request body - const { username, password } = req.body; - - try { - // Find the user by username in the database - const user = await User.findOne({ username }); - - // If the user is not found, return an error - if (!user) { - return res.status(401).json({ error: 'Invalid username or password' }); - } - - // Compare the entered password with the hashed password in the database - const isPasswordValid = await user.comparePassword(password); - - // If the password is not valid, return an error - if (!isPasswordValid) { - return res.status(401).json({ error: 'Invalid username or password' }); - } - - // Send a success response - res.json({ message: 'User signed in successfully' }); - } catch (error) { - // Handle any errors that occur during the sign-in process - res.status(500).json({ error: 'Internal Server Error' }); - } -}); - -// Export the router for use in other files -export default router; - -// Explanation: - -// The '/register' route handles user registration. It creates a new user instance, saves it to the database, and returns a success message. -// The '/signin' route handles user sign-in. It searches for the user by the provided username, compares the entered password with the hashed password in the database, and returns a success message if the credentials are valid. -// Both routes handle potential errors and return appropriate responses. - -// Registration (/register route): - -// When a new user is registered, you create a new instance of the User model using const newUser = new User({ username, password });. -// The userModel.js file contains a pre-save middleware using bcrypt that automatically hashes the password before saving it to the database. This ensures that the actual password is not stored in the database. - -// Sign-In (/signin route): - -// When a user attempts to sign in, you find the user in the database based on the provided username using const user = await User.findOne({ username });. -// If the user is found, you use the comparePassword method from the userModel.js file. This method compares the entered password with the hashed password stored in the database. It internally uses bcrypt to perform this comparison. -// If the password is valid, the user is signed in successfully. \ No newline at end of file diff --git a/backend/server.js b/backend/server.js index ff06fc668..830bf77a0 100644 --- a/backend/server.js +++ b/backend/server.js @@ -1,45 +1,39 @@ +// server.js import express from 'express'; import cors from 'cors'; import mongoose from 'mongoose'; -import authUserMiddleware from '../backend/middleware/authUser'; -import userRoutes from './models/userModel'; -import secretRoute from './routes/secretRoutes'; +import authUserMiddleware from './middleware/authMiddleware'; +// import userRoutes from './models/userModel'; +import authUserRoutes from './routes/authUserRoutes'; +import secretRoute from './routes/secretRoutes'; // Add this line import listEndpoints from 'express-list-endpoints'; import dotenv from 'dotenv'; dotenv.config(); const mongoUrl = process.env.MONGO_URI || "mongodb://localhost:27017/project-mongo"; -mongoose.connect(mongoUrl, { useNewUrlParser: true, useUnifiedTopology: true, }); +mongoose.connect(mongoUrl, { useNewUrlParser: true, useUnifiedTopology: true }); mongoose.Promise = Promise; -// Defines the port the app will run on. Defaults to 8080, but can be overridden -// when starting the server. Example command to overwrite PORT env variable value: -// PORT=9000 npm start const port = process.env.PORT || 8080; const app = express(); -// Start defining your routes here -app.get("/", (req, res) => { - res.json(listEndpoints(app)); -}); - -app.use('/auth', authUserMiddleware); -// Use the userRoutes for registration and sign-in routes -app.use('/user', userRoutes); -// Use the secretRoute for the authenticated secret content route -app.use('/secret', secretRoute); - -// Add middlewares to enable cors and json body parsing app.use(cors()); app.use(express.json()); -// Add logging middleware to log incoming requests app.use((req, res, next) => { console.log(`Received ${req.method} request at ${req.url}`); next(); }); -// Start the server +app.use('/auth', authUserMiddleware); +app.use('/user', authUserRoutes); +// app.use('/auth', authUserRoutes); +app.use('/secret', secretRoute); // Add this line + +app.get("/", (req, res) => { + res.json(listEndpoints(app)); +}); + app.listen(port, () => { console.log(`Server running on http://localhost:${port}`); }); @@ -47,14 +41,47 @@ app.listen(port, () => { -// Use the authUserMiddleware for authentication routes -//The old code +// server.js +// import express from 'express'; +// import cors from 'cors'; +// import mongoose from 'mongoose'; +// import authUserMiddleware from './middleware/authMiddleware'; +// import userRoutes from './models/userModel'; +// import authUserRoutes from './routes/authUserRoutes'; +// import listEndpoints from 'express-list-endpoints'; +// import dotenv from 'dotenv'; +// dotenv.config(); -// app.use('/auth', (req, res, next) => { -// console.log('Auth middleware log:', req.url); -// authUserMiddleware(req, res, next); +// const mongoUrl = process.env.MONGO_URI || "mongodb://localhost:27017/project-mongo"; +// mongoose.connect(mongoUrl, { useNewUrlParser: true, useUnifiedTopology: true }); +// mongoose.Promise = Promise; + +// const port = process.env.PORT || 8080; +// const app = express(); + +// app.use(cors()); +// app.use(express.json()); + +// app.use((req, res, next) => { +// console.log(`Received ${req.method} request at ${req.url}`); +// next(); // }); +// app.use('/auth', authUserMiddleware); + +// app.use('/user', userRoutes); +// app.use('/auth', authUserRoutes); + +// app.get("/", (req, res) => { +// res.json(listEndpoints(app)); +// }); + +// app.listen(port, () => { +// console.log(`Server running on http://localhost:${port}`); +// }); + + + From cbe00a40ec6f2ffa01a7be9193cf173237decadd Mon Sep 17 00:00:00 2001 From: EvelynDelCarmen Date: Sun, 10 Dec 2023 21:43:59 +0100 Subject: [PATCH 11/46] added the new info in .env and changed the structure, something wrong with the accessToken --- backend/middleware/authMiddleware.js | 13 ++++++++++--- backend/routes/authUserRoutes.js | 2 +- 2 files changed, 11 insertions(+), 4 deletions(-) diff --git a/backend/middleware/authMiddleware.js b/backend/middleware/authMiddleware.js index e07135598..e8688972b 100644 --- a/backend/middleware/authMiddleware.js +++ b/backend/middleware/authMiddleware.js @@ -5,23 +5,29 @@ const authenticateUser = async (req, res, next) => { const { authorization } = req.headers; try { + console.log('Received token:', authorization); if (!authorization || !authorization.startsWith('Bearer ')) { - return res.status(401).json({ error: 'Unauthorized' }); + return res.status(401).json({ error: 'Unauthorized - Missing or invalid token' }); } const token = authorization.split(' ')[1]; const decoded = jwt.verify(token, process.env.JWT_SECRET); + if (!decoded || !decoded.userId) { + return res.status(401).json({ error: 'Unauthorized - Invalid token content' }); + } + // Attach the user to the request object req.user = await User.findById(decoded.userId); if (!req.user) { - return res.status(401).json({ error: 'Unauthorized' }); + return res.status(401).json({ error: 'Unauthorized - User not found' }); } next(); } catch (error) { - res.status(401).json({ error: 'Unauthorized' }); + console.error('Error during token verification:', error); + res.status(401).json({ error: 'Unauthorized - Token verification failed' }); } }; @@ -33,6 +39,7 @@ export default authenticateUser; + // authMiddleware.js // import User from '../models/userModel'; // import jwt from 'jsonwebtoken'; diff --git a/backend/routes/authUserRoutes.js b/backend/routes/authUserRoutes.js index f0cc396cb..ac737d6b8 100644 --- a/backend/routes/authUserRoutes.js +++ b/backend/routes/authUserRoutes.js @@ -27,7 +27,7 @@ router.post('/register', async (req, res) => { await newUser.save(); // Use jsonwebtoken to create a token - const accessToken = jwt.sign({ userId: newUser._id }, secretKey, { expiresIn: '24h' }); + const accessToken = jwt.sign({ userId: newUser._id }, process.env.JWT_SECRET, { expiresIn: '24h' }); res.status(201).json({ accessToken }); } catch (error) { From 3026e685ab1c2dbca7f28400eaa31e884baf7ae1 Mon Sep 17 00:00:00 2001 From: EvelynDelCarmen Date: Mon, 11 Dec 2023 16:21:39 +0100 Subject: [PATCH 12/46] the sign up endpoint works --- backend/middleware/authMiddleware.js | 1 + 1 file changed, 1 insertion(+) diff --git a/backend/middleware/authMiddleware.js b/backend/middleware/authMiddleware.js index e8688972b..e00ad4418 100644 --- a/backend/middleware/authMiddleware.js +++ b/backend/middleware/authMiddleware.js @@ -1,5 +1,6 @@ // authMiddleware.js import jwt from 'jsonwebtoken'; +import User from '../models/userModel'; const authenticateUser = async (req, res, next) => { const { authorization } = req.headers; From 8bd9f459e7ea94aa54d57af975d024e981d65634 Mon Sep 17 00:00:00 2001 From: EvelynDelCarmen Date: Wed, 13 Dec 2023 17:25:18 +0100 Subject: [PATCH 13/46] frontend --- frontend/components/Dashboard.jsx | 31 +++++++++++ frontend/components/Register.jsx | 86 +++++++++++++++++++++++++++++++ frontend/components/SignIn.jsx | 27 ++++++++++ frontend/package.json | 6 ++- frontend/postcss.config.js | 6 +++ frontend/src/App.jsx | 22 +++++++- frontend/tailwind.config.js | 9 ++++ 7 files changed, 184 insertions(+), 3 deletions(-) create mode 100644 frontend/components/Dashboard.jsx create mode 100644 frontend/components/Register.jsx create mode 100644 frontend/components/SignIn.jsx create mode 100644 frontend/postcss.config.js create mode 100644 frontend/tailwind.config.js diff --git a/frontend/components/Dashboard.jsx b/frontend/components/Dashboard.jsx new file mode 100644 index 000000000..1b91cf2be --- /dev/null +++ b/frontend/components/Dashboard.jsx @@ -0,0 +1,31 @@ +// src/components/Dashboard.js +import { useEffect, useState } from 'react'; + +const Dashboard = () => { + const [content, setContent] = useState(''); + const accessToken = localStorage.getItem('accessToken'); + + useEffect(() => { + if (!accessToken) { + // Redirect to the sign-in page if there is no access token + } else { + // Make a GET request to your authenticated endpoint with the access token + // Update the content state with the response + } + }, [accessToken]); + + const handleSignOut = () => { + // Remove the access token from local storage + // Redirect to the sign-in page + }; + + return ( +
+

Dashboard

+ +
{content}
+
+ ); +}; + +export default Dashboard; diff --git a/frontend/components/Register.jsx b/frontend/components/Register.jsx new file mode 100644 index 000000000..d7722f687 --- /dev/null +++ b/frontend/components/Register.jsx @@ -0,0 +1,86 @@ +// src/components/Register.js +import { useState } from 'react'; +import { useHistory } from 'react-router-dom'; + +const Register = () => { + // State to store form data + const [formData, setFormData] = useState({ username: '', email: '', password: '' }); + // Access the history object to navigate between pages + const history = useHistory(); + + // Handle form submission + const handleSubmit = async (e) => { + e.preventDefault(); + // Make a POST request to your registration endpoint with formData + // Handle success by saving the access token to local storage + // Redirect to the dashboard page + try { + // Make a POST request to the registration endpoint + const response = await fetch('http://your-api-url/auth/register', { + method: 'POST', + headers: { + 'Content-Type': 'application/json', + }, + body: JSON.stringify(formData), + }); + + // Check if the request was successful (status code 2xx) + if (response.ok) { + // Parse the response JSON to get the access token + const data = await response.json(); + + // Save the access token to local storage + localStorage.setItem('accessToken', data.accessToken); + + // Redirect to the dashboard page + history.push('/dashboard'); + } else { + // Handle registration error (status code is not 2xx) + const errorData = await response.json(); + console.error('Registration error:', errorData.error); + // Display an error message to the user, e.g., set a state variable for displaying an error message + } + } catch (error) { + // Handle network errors or other unexpected errors + console.error('Unexpected error during registration:', error); + // Display a generic error message to the user + } + }; + + return ( +
+

Register

+
+ {/* Registration form fields */} + + setFormData({ ...formData, username: e.target.value })} + /> + + + setFormData({ ...formData, email: e.target.value })} + /> + + + setFormData({ ...formData, password: e.target.value })} + /> + + {/* Submit button */} + +
+
+ ); +}; + +export default Register; diff --git a/frontend/components/SignIn.jsx b/frontend/components/SignIn.jsx new file mode 100644 index 000000000..b71659243 --- /dev/null +++ b/frontend/components/SignIn.jsx @@ -0,0 +1,27 @@ +// src/components/SignIn.js +import { useState } from 'react'; +import { useHistory } from 'react-router-dom'; + +const SignIn = () => { + const [formData, setFormData] = useState({ username: '', password: '' }); + const history = useHistory(); + + const handleSubmit = async (e) => { + e.preventDefault(); + // Make a POST request to your login endpoint with formData + // Handle success by saving the access token to local storage + // Redirect to the dashboard page + }; + + return ( +
+

Sign In

+
+ {/* Your sign-in form fields go here */} + +
+
+ ); +}; + +export default SignIn; diff --git a/frontend/package.json b/frontend/package.json index e9c95b79f..556cf8b34 100644 --- a/frontend/package.json +++ b/frontend/package.json @@ -11,7 +11,11 @@ }, "dependencies": { "react": "^18.2.0", - "react-dom": "^18.2.0" + "react-dom": "^18.2.0", + "react-router-dom": "^6.20.1", + "tailwindcss": "^3.3.6", + "tailwindcss-cli": "^0.1.2", + "zustand": "^4.4.7" }, "devDependencies": { "@types/react": "^18.2.15", diff --git a/frontend/postcss.config.js b/frontend/postcss.config.js new file mode 100644 index 000000000..2e7af2b7f --- /dev/null +++ b/frontend/postcss.config.js @@ -0,0 +1,6 @@ +export default { + plugins: { + tailwindcss: {}, + autoprefixer: {}, + }, +} diff --git a/frontend/src/App.jsx b/frontend/src/App.jsx index 1091d4310..2c42849b7 100644 --- a/frontend/src/App.jsx +++ b/frontend/src/App.jsx @@ -1,3 +1,21 @@ -export const App = () => { - return
Find me in src/app.jsx!
; + +// src/App.js +import 'react'; +import { Route, Switch } from 'react-router-dom'; +import Register from '../components/Register'; +import SignIn from '../components/SignIn'; +import Dashboard from '../components/Dashboard'; + +const App = () => { + return ( +
+ + + + + +
+ ); }; + +export default App; diff --git a/frontend/tailwind.config.js b/frontend/tailwind.config.js new file mode 100644 index 000000000..c189a4a51 --- /dev/null +++ b/frontend/tailwind.config.js @@ -0,0 +1,9 @@ +/** @type {import('tailwindcss').Config} */ +export default { + content: [], + theme: { + extend: {}, + }, + plugins: [], +} + From 36dd86edd4db5ad49bdde198f533e5f2bcb53d17 Mon Sep 17 00:00:00 2001 From: EvelynDelCarmen Date: Wed, 13 Dec 2023 17:33:11 +0100 Subject: [PATCH 14/46] frontend + dashboard + register --- frontend/components/Dashboard.jsx | 30 +++++++++++++++++++++++++++++- 1 file changed, 29 insertions(+), 1 deletion(-) diff --git a/frontend/components/Dashboard.jsx b/frontend/components/Dashboard.jsx index 1b91cf2be..dcba9947d 100644 --- a/frontend/components/Dashboard.jsx +++ b/frontend/components/Dashboard.jsx @@ -2,15 +2,43 @@ import { useEffect, useState } from 'react'; const Dashboard = () => { + // State to store the content received from the server const [content, setContent] = useState(''); + // Retrieve the access token from local storage const accessToken = localStorage.getItem('accessToken'); useEffect(() => { + // Check if there is an access token if (!accessToken) { // Redirect to the sign-in page if there is no access token + // You can use the useHistory hook from react-router-dom for navigation + // Example: import { useHistory } from 'react-router-dom'; const history = useHistory(); history.push('/sign-in'); } else { // Make a GET request to your authenticated endpoint with the access token - // Update the content state with the response + fetch('http://your-api-url/auth/protected', { + method: 'GET', + headers: { + Authorization: `Bearer ${accessToken}`, // Include the access token in the Authorization header + }, + }) + .then((response) => { + if (response.ok) { + // If the response is successful (status code 2xx), parse the JSON + return response.json(); + } else { + // If the response is not successful, throw an error + throw new Error('Failed to fetch content'); + } + }) + .then((data) => { + // Update the content state with the response data + setContent(data.message); + }) + .catch((error) => { + // Handle errors, e.g., log the error and display a generic error message to the user + console.error('Error fetching content:', error); + setContent('Error fetching content'); + }); } }, [accessToken]); From 1c1ff1fd61034d0b6694f3e200e094faa803c8bf Mon Sep 17 00:00:00 2001 From: EvelynDelCarmen Date: Wed, 13 Dec 2023 17:42:13 +0100 Subject: [PATCH 15/46] frontend SignIn --- frontend/components/Dashboard.jsx | 6 ++++ frontend/components/SignIn.jsx | 56 ++++++++++++++++++++++++++++--- 2 files changed, 58 insertions(+), 4 deletions(-) diff --git a/frontend/components/Dashboard.jsx b/frontend/components/Dashboard.jsx index dcba9947d..195fd63f8 100644 --- a/frontend/components/Dashboard.jsx +++ b/frontend/components/Dashboard.jsx @@ -42,15 +42,21 @@ const Dashboard = () => { } }, [accessToken]); + // Handle sign-out button click const handleSignOut = () => { // Remove the access token from local storage + localStorage.removeItem('accessToken'); // Redirect to the sign-in page + // You can use the useHistory hook from react-router-dom for navigation + // Example: import { useHistory } from 'react-router-dom'; const history = useHistory(); history.push('/sign-in'); }; return (

Dashboard

+ {/* Sign-out button */} + {/* Display the content */}
{content}
); diff --git a/frontend/components/SignIn.jsx b/frontend/components/SignIn.jsx index b71659243..40e87e982 100644 --- a/frontend/components/SignIn.jsx +++ b/frontend/components/SignIn.jsx @@ -3,21 +3,69 @@ import { useState } from 'react'; import { useHistory } from 'react-router-dom'; const SignIn = () => { + // State to store form data const [formData, setFormData] = useState({ username: '', password: '' }); + // Access the history object to navigate between pages const history = useHistory(); + // Handle form submission const handleSubmit = async (e) => { e.preventDefault(); - // Make a POST request to your login endpoint with formData - // Handle success by saving the access token to local storage - // Redirect to the dashboard page + try { + // Make a POST request to the login endpoint + const response = await fetch('http://your-api-url/auth/login', { + method: 'POST', + headers: { + 'Content-Type': 'application/json', + }, + body: JSON.stringify(formData), + }); + + // Check if the request was successful (status code 2xx) + if (response.ok) { + // Parse the response JSON to get the access token + const data = await response.json(); + + // Save the access token to local storage + localStorage.setItem('accessToken', data.accessToken); + + // Redirect to the dashboard page + history.push('/dashboard'); + } else { + // Handle login error (status code is not 2xx) + const errorData = await response.json(); + console.error('Login error:', errorData.error); + // Display an error message to the user, e.g., set a state variable for displaying an error message + } + } catch (error) { + // Handle network errors or other unexpected errors + console.error('Unexpected error during login:', error); + // Display a generic error message to the user + } }; return (

Sign In

- {/* Your sign-in form fields go here */} + {/* Sign-in form fields */} + + setFormData({ ...formData, username: e.target.value })} + /> + + + setFormData({ ...formData, password: e.target.value })} + /> + + {/* Submit button */}
From 580099fd47168b549fd24747f7680515cedbf05f Mon Sep 17 00:00:00 2001 From: EvelynDelCarmen Date: Wed, 13 Dec 2023 18:36:14 +0100 Subject: [PATCH 16/46] react problem --- frontend/package.json | 4 ++-- frontend/src/App.jsx | 19 ++++++++++--------- frontend/{ => src}/components/Dashboard.jsx | 0 frontend/{ => src}/components/Register.jsx | 6 +++--- frontend/{ => src}/components/SignIn.jsx | 18 +++++------------- frontend/src/main.jsx | 2 +- frontend/tailwind.config.js | 5 ++++- 7 files changed, 25 insertions(+), 29 deletions(-) rename frontend/{ => src}/components/Dashboard.jsx (100%) rename frontend/{ => src}/components/Register.jsx (96%) rename frontend/{ => src}/components/SignIn.jsx (75%) diff --git a/frontend/package.json b/frontend/package.json index 556cf8b34..e2865e591 100644 --- a/frontend/package.json +++ b/frontend/package.json @@ -25,6 +25,6 @@ "eslint-plugin-react": "^7.32.2", "eslint-plugin-react-hooks": "^4.6.0", "eslint-plugin-react-refresh": "^0.4.3", - "vite": "^4.4.5" + "vite": "^4.0.3" } -} +} \ No newline at end of file diff --git a/frontend/src/App.jsx b/frontend/src/App.jsx index 2c42849b7..a328df79d 100644 --- a/frontend/src/App.jsx +++ b/frontend/src/App.jsx @@ -1,19 +1,20 @@ // src/App.js import 'react'; -import { Route, Switch } from 'react-router-dom'; -import Register from '../components/Register'; -import SignIn from '../components/SignIn'; -import Dashboard from '../components/Dashboard'; +import { Route, Routes } from 'react-router-dom'; +import Register from './components/Register'; +import SignIn from './components/SignIn'; +import Dashboard from './components/Dashboard'; const App = () => { return (
- - - - - + + } /> + } /> + } /> + +
); }; diff --git a/frontend/components/Dashboard.jsx b/frontend/src/components/Dashboard.jsx similarity index 100% rename from frontend/components/Dashboard.jsx rename to frontend/src/components/Dashboard.jsx diff --git a/frontend/components/Register.jsx b/frontend/src/components/Register.jsx similarity index 96% rename from frontend/components/Register.jsx rename to frontend/src/components/Register.jsx index d7722f687..890f50fa0 100644 --- a/frontend/components/Register.jsx +++ b/frontend/src/components/Register.jsx @@ -1,12 +1,12 @@ // src/components/Register.js import { useState } from 'react'; -import { useHistory } from 'react-router-dom'; +import { useNavigate } from 'react-router-dom'; const Register = () => { // State to store form data const [formData, setFormData] = useState({ username: '', email: '', password: '' }); // Access the history object to navigate between pages - const history = useHistory(); + const navigate = useNavigate(); // Handle form submission const handleSubmit = async (e) => { @@ -33,7 +33,7 @@ const Register = () => { localStorage.setItem('accessToken', data.accessToken); // Redirect to the dashboard page - history.push('/dashboard'); + navigate.push('/dashboard'); } else { // Handle registration error (status code is not 2xx) const errorData = await response.json(); diff --git a/frontend/components/SignIn.jsx b/frontend/src/components/SignIn.jsx similarity index 75% rename from frontend/components/SignIn.jsx rename to frontend/src/components/SignIn.jsx index 40e87e982..10bbcc6c5 100644 --- a/frontend/components/SignIn.jsx +++ b/frontend/src/components/SignIn.jsx @@ -1,12 +1,12 @@ // src/components/SignIn.js import { useState } from 'react'; -import { useHistory } from 'react-router-dom'; +import { useNavigate } from 'react-router-dom'; const SignIn = () => { // State to store form data const [formData, setFormData] = useState({ username: '', password: '' }); // Access the history object to navigate between pages - const history = useHistory(); + const navigate = useNavigate(); // Handle form submission const handleSubmit = async (e) => { @@ -21,26 +21,18 @@ const SignIn = () => { body: JSON.stringify(formData), }); - // Check if the request was successful (status code 2xx) if (response.ok) { - // Parse the response JSON to get the access token const data = await response.json(); - - // Save the access token to local storage localStorage.setItem('accessToken', data.accessToken); - - // Redirect to the dashboard page - history.push('/dashboard'); + navigate('/dashboard'); // Use navigate to redirect } else { - // Handle login error (status code is not 2xx) const errorData = await response.json(); console.error('Login error:', errorData.error); - // Display an error message to the user, e.g., set a state variable for displaying an error message + // Handle login error } } catch (error) { - // Handle network errors or other unexpected errors console.error('Unexpected error during login:', error); - // Display a generic error message to the user + // Handle unexpected error } }; diff --git a/frontend/src/main.jsx b/frontend/src/main.jsx index 51294f399..b91620d35 100644 --- a/frontend/src/main.jsx +++ b/frontend/src/main.jsx @@ -1,6 +1,6 @@ import React from "react"; import ReactDOM from "react-dom/client"; -import { App } from "./App.jsx"; +import App from "./App.jsx"; import "./index.css"; ReactDOM.createRoot(document.getElementById("root")).render( diff --git a/frontend/tailwind.config.js b/frontend/tailwind.config.js index c189a4a51..ab8ad146d 100644 --- a/frontend/tailwind.config.js +++ b/frontend/tailwind.config.js @@ -1,6 +1,9 @@ /** @type {import('tailwindcss').Config} */ export default { - content: [], + content: [ + "./src/**/*.{js,jsx,ts,tsx}", + "./index.html", + ], theme: { extend: {}, }, From 8d723a31dd4d170b6e4c4293716f85f723181aa4 Mon Sep 17 00:00:00 2001 From: EvelynDelCarmen Date: Thu, 14 Dec 2023 12:08:18 +0100 Subject: [PATCH 17/46] debugged the frontend --- frontend/src/App.jsx | 19 ++++++++++--------- 1 file changed, 10 insertions(+), 9 deletions(-) diff --git a/frontend/src/App.jsx b/frontend/src/App.jsx index a328df79d..9dfa769e4 100644 --- a/frontend/src/App.jsx +++ b/frontend/src/App.jsx @@ -1,7 +1,6 @@ - -// src/App.js +// App.jsx import 'react'; -import { Route, Routes } from 'react-router-dom'; +import { BrowserRouter, Route, Routes } from 'react-router-dom'; import Register from './components/Register'; import SignIn from './components/SignIn'; import Dashboard from './components/Dashboard'; @@ -9,14 +8,16 @@ import Dashboard from './components/Dashboard'; const App = () => { return (
- - } /> - } /> - } /> - - + + + } /> + } /> + } /> + +
); }; export default App; + From c2101389909ba194cb205c045ccd2f143dac5d7a Mon Sep 17 00:00:00 2001 From: EvelynDelCarmen Date: Thu, 14 Dec 2023 12:48:15 +0100 Subject: [PATCH 18/46] test deploying --- backend/package.json | 2 ++ netlify.toml | 11 ++++++++--- 2 files changed, 10 insertions(+), 3 deletions(-) diff --git a/backend/package.json b/backend/package.json index 4aa6dbf02..fd1e674cf 100644 --- a/backend/package.json +++ b/backend/package.json @@ -12,8 +12,10 @@ "@babel/core": "^7.17.9", "@babel/node": "^7.16.8", "@babel/preset-env": "^7.16.11", + "bcrypt": "^5.1.1", "cors": "^2.8.5", "crypto": "^1.0.1", + "dotenv": "^16.3.1", "express": "^4.17.3", "express-list-endpoints": "^6.0.0", "jsonwebtoken": "^9.0.2", diff --git a/netlify.toml b/netlify.toml index 95443a1f3..011616c41 100644 --- a/netlify.toml +++ b/netlify.toml @@ -1,6 +1,11 @@ # This file tells netlify where the code for this project is and # how it should build the JavaScript assets to deploy from. [build] - base = "frontend/" - publish = "build/" - command = "npm run build" +base = "frontend" +publish = "dist" +command = "npm run build" + +[[redirects]] +from = "/*" +to = "/index.html" +status = 200 From 7a260b11806753870764e3b63ddb143223426aff Mon Sep 17 00:00:00 2001 From: EvelynDelCarmen Date: Thu, 14 Dec 2023 14:16:33 +0100 Subject: [PATCH 19/46] render --- frontend/src/components/Dashboard.jsx | 2 +- frontend/src/components/Register.jsx | 3 ++- 2 files changed, 3 insertions(+), 2 deletions(-) diff --git a/frontend/src/components/Dashboard.jsx b/frontend/src/components/Dashboard.jsx index 195fd63f8..690d56343 100644 --- a/frontend/src/components/Dashboard.jsx +++ b/frontend/src/components/Dashboard.jsx @@ -15,7 +15,7 @@ const Dashboard = () => { // Example: import { useHistory } from 'react-router-dom'; const history = useHistory(); history.push('/sign-in'); } else { // Make a GET request to your authenticated endpoint with the access token - fetch('http://your-api-url/auth/protected', { + fetch('https://project-auth-api-mnx9.onrender.com/protected', { method: 'GET', headers: { Authorization: `Bearer ${accessToken}`, // Include the access token in the Authorization header diff --git a/frontend/src/components/Register.jsx b/frontend/src/components/Register.jsx index 890f50fa0..4fb8e6948 100644 --- a/frontend/src/components/Register.jsx +++ b/frontend/src/components/Register.jsx @@ -11,12 +11,13 @@ const Register = () => { // Handle form submission const handleSubmit = async (e) => { e.preventDefault(); + console.log(formData) // Make a POST request to your registration endpoint with formData // Handle success by saving the access token to local storage // Redirect to the dashboard page try { // Make a POST request to the registration endpoint - const response = await fetch('http://your-api-url/auth/register', { + const response = await fetch('https://project-auth-api-mnx9.onrender.com/user/register', { method: 'POST', headers: { 'Content-Type': 'application/json', From 1e6e07ffd76390f0750f87ae392be346b5aef8f2 Mon Sep 17 00:00:00 2001 From: EvelynDelCarmen Date: Thu, 14 Dec 2023 21:25:46 +0100 Subject: [PATCH 20/46] api debugging --- frontend/src/App.jsx | 4 +-- frontend/src/components/Dashboard.jsx | 29 +++++-------------- .../src/components/{SignIn.jsx => LogIn.jsx} | 10 +++---- frontend/src/components/Register.jsx | 2 +- 4 files changed, 16 insertions(+), 29 deletions(-) rename frontend/src/components/{SignIn.jsx => LogIn.jsx} (89%) diff --git a/frontend/src/App.jsx b/frontend/src/App.jsx index 9dfa769e4..901b4fd32 100644 --- a/frontend/src/App.jsx +++ b/frontend/src/App.jsx @@ -2,7 +2,7 @@ import 'react'; import { BrowserRouter, Route, Routes } from 'react-router-dom'; import Register from './components/Register'; -import SignIn from './components/SignIn'; +import LogIn from './components/LogIn'; import Dashboard from './components/Dashboard'; const App = () => { @@ -11,7 +11,7 @@ const App = () => { } /> - } /> + } /> } /> diff --git a/frontend/src/components/Dashboard.jsx b/frontend/src/components/Dashboard.jsx index 690d56343..b5b71253a 100644 --- a/frontend/src/components/Dashboard.jsx +++ b/frontend/src/components/Dashboard.jsx @@ -1,62 +1,49 @@ // src/components/Dashboard.js import { useEffect, useState } from 'react'; +import { useNavigate } from 'react-router-dom'; // Import useNavigate const Dashboard = () => { - // State to store the content received from the server const [content, setContent] = useState(''); - // Retrieve the access token from local storage const accessToken = localStorage.getItem('accessToken'); + const navigate = useNavigate(); // Use useNavigate instead of useHistory useEffect(() => { - // Check if there is an access token if (!accessToken) { - // Redirect to the sign-in page if there is no access token - // You can use the useHistory hook from react-router-dom for navigation - // Example: import { useHistory } from 'react-router-dom'; const history = useHistory(); history.push('/sign-in'); + // Redirect to the sign-in page using useNavigate + navigate('/login'); } else { - // Make a GET request to your authenticated endpoint with the access token - fetch('https://project-auth-api-mnx9.onrender.com/protected', { + fetch('`${import.meta.env.VITE_API_URL}/user/protected`', { method: 'GET', headers: { - Authorization: `Bearer ${accessToken}`, // Include the access token in the Authorization header + Authorization: `Bearer ${accessToken}`, }, }) .then((response) => { if (response.ok) { - // If the response is successful (status code 2xx), parse the JSON return response.json(); } else { - // If the response is not successful, throw an error throw new Error('Failed to fetch content'); } }) .then((data) => { - // Update the content state with the response data setContent(data.message); }) .catch((error) => { - // Handle errors, e.g., log the error and display a generic error message to the user console.error('Error fetching content:', error); setContent('Error fetching content'); }); } - }, [accessToken]); + }, [accessToken, navigate]); // Include navigate in the dependencies - // Handle sign-out button click const handleSignOut = () => { - // Remove the access token from local storage localStorage.removeItem('accessToken'); - // Redirect to the sign-in page - // You can use the useHistory hook from react-router-dom for navigation - // Example: import { useHistory } from 'react-router-dom'; const history = useHistory(); history.push('/sign-in'); + navigate('/login'); // Redirect to the sign-in page }; return (

Dashboard

- {/* Sign-out button */} - {/* Display the content */}
{content}
); diff --git a/frontend/src/components/SignIn.jsx b/frontend/src/components/LogIn.jsx similarity index 89% rename from frontend/src/components/SignIn.jsx rename to frontend/src/components/LogIn.jsx index 10bbcc6c5..6b89c38fd 100644 --- a/frontend/src/components/SignIn.jsx +++ b/frontend/src/components/LogIn.jsx @@ -2,7 +2,7 @@ import { useState } from 'react'; import { useNavigate } from 'react-router-dom'; -const SignIn = () => { +const LogIn = () => { // State to store form data const [formData, setFormData] = useState({ username: '', password: '' }); // Access the history object to navigate between pages @@ -13,7 +13,7 @@ const SignIn = () => { e.preventDefault(); try { // Make a POST request to the login endpoint - const response = await fetch('http://your-api-url/auth/login', { + const response = await fetch('`${import.meta.env.VITE_API_URL}/user/login`', { method: 'POST', headers: { 'Content-Type': 'application/json', @@ -24,7 +24,7 @@ const SignIn = () => { if (response.ok) { const data = await response.json(); localStorage.setItem('accessToken', data.accessToken); - navigate('/dashboard'); // Use navigate to redirect + navigate.push('/dashboard'); // Use navigate to redirect } else { const errorData = await response.json(); console.error('Login error:', errorData.error); @@ -38,7 +38,7 @@ const SignIn = () => { return (
-

Sign In

+

Log In

{/* Sign-in form fields */} @@ -64,4 +64,4 @@ const SignIn = () => { ); }; -export default SignIn; +export default LogIn; diff --git a/frontend/src/components/Register.jsx b/frontend/src/components/Register.jsx index 4fb8e6948..76758fc38 100644 --- a/frontend/src/components/Register.jsx +++ b/frontend/src/components/Register.jsx @@ -17,7 +17,7 @@ const Register = () => { // Redirect to the dashboard page try { // Make a POST request to the registration endpoint - const response = await fetch('https://project-auth-api-mnx9.onrender.com/user/register', { + const response = await fetch('`${import.meta.env.VITE_API_URL}/user/register`', { method: 'POST', headers: { 'Content-Type': 'application/json', From 4a18521f9730a21ac99a0438d1b69de9e6e7450b Mon Sep 17 00:00:00 2001 From: EvelynDelCarmen Date: Thu, 14 Dec 2023 22:50:49 +0100 Subject: [PATCH 21/46] added the store --- frontend/src/App.jsx | 14 +++++++++----- frontend/src/components/Dashboard.jsx | 16 ++++++++++------ frontend/src/components/LogIn.jsx | 8 +++++--- frontend/src/components/Register.jsx | 19 ++++++++----------- frontend/src/store/authStore.jsx | 11 +++++++++++ 5 files changed, 43 insertions(+), 25 deletions(-) create mode 100644 frontend/src/store/authStore.jsx diff --git a/frontend/src/App.jsx b/frontend/src/App.jsx index 901b4fd32..6e3333a72 100644 --- a/frontend/src/App.jsx +++ b/frontend/src/App.jsx @@ -1,19 +1,23 @@ // App.jsx import 'react'; import { BrowserRouter, Route, Routes } from 'react-router-dom'; +import UseAuthStore from './store/authStore'; import Register from './components/Register'; import LogIn from './components/LogIn'; import Dashboard from './components/Dashboard'; + const App = () => { return (
- - } /> - } /> - } /> - + + + } /> + } /> + } /> + +
); diff --git a/frontend/src/components/Dashboard.jsx b/frontend/src/components/Dashboard.jsx index b5b71253a..e76265f6f 100644 --- a/frontend/src/components/Dashboard.jsx +++ b/frontend/src/components/Dashboard.jsx @@ -1,18 +1,20 @@ // src/components/Dashboard.js import { useEffect, useState } from 'react'; -import { useNavigate } from 'react-router-dom'; // Import useNavigate +import { useNavigate } from 'react-router-dom'; +import UseAuthStore from '../store/authStore'; // Adjust the path const Dashboard = () => { const [content, setContent] = useState(''); - const accessToken = localStorage.getItem('accessToken'); - const navigate = useNavigate(); // Use useNavigate instead of useHistory + const accessToken = UseAuthStore((state) => state.accessToken); + const logout = UseAuthStore((state) => state.logout); + const navigate = useNavigate(); useEffect(() => { if (!accessToken) { // Redirect to the sign-in page using useNavigate navigate('/login'); } else { - fetch('`${import.meta.env.VITE_API_URL}/user/protected`', { + fetch(`${import.meta.env.VITE_API_URL}/user/protected`, { method: 'GET', headers: { Authorization: `Bearer ${accessToken}`, @@ -33,10 +35,11 @@ const Dashboard = () => { setContent('Error fetching content'); }); } - }, [accessToken, navigate]); // Include navigate in the dependencies + }, [accessToken, navigate]); const handleSignOut = () => { - localStorage.removeItem('accessToken'); + // Use the logout action from the store + logout(); navigate('/login'); // Redirect to the sign-in page }; @@ -50,3 +53,4 @@ const Dashboard = () => { }; export default Dashboard; + diff --git a/frontend/src/components/LogIn.jsx b/frontend/src/components/LogIn.jsx index 6b89c38fd..229031fe0 100644 --- a/frontend/src/components/LogIn.jsx +++ b/frontend/src/components/LogIn.jsx @@ -1,19 +1,21 @@ // src/components/SignIn.js import { useState } from 'react'; import { useNavigate } from 'react-router-dom'; +import UseAuthStore from '../store/authStore'; const LogIn = () => { // State to store form data const [formData, setFormData] = useState({ username: '', password: '' }); // Access the history object to navigate between pages const navigate = useNavigate(); + const { login } = UseAuthStore(); // Handle form submission const handleSubmit = async (e) => { e.preventDefault(); try { // Make a POST request to the login endpoint - const response = await fetch('`${import.meta.env.VITE_API_URL}/user/login`', { + const response = await fetch(`${import.meta.env.VITE_API_URL}/user/login`, { method: 'POST', headers: { 'Content-Type': 'application/json', @@ -23,8 +25,8 @@ const LogIn = () => { if (response.ok) { const data = await response.json(); - localStorage.setItem('accessToken', data.accessToken); - navigate.push('/dashboard'); // Use navigate to redirect + login(data.accessToken); // Use the login action from the store + navigate('/dashboard'); } else { const errorData = await response.json(); console.error('Login error:', errorData.error); diff --git a/frontend/src/components/Register.jsx b/frontend/src/components/Register.jsx index 76758fc38..f92ac597d 100644 --- a/frontend/src/components/Register.jsx +++ b/frontend/src/components/Register.jsx @@ -1,23 +1,23 @@ // src/components/Register.js import { useState } from 'react'; import { useNavigate } from 'react-router-dom'; +import useAuthStore from '../store/authStore'; const Register = () => { // State to store form data const [formData, setFormData] = useState({ username: '', email: '', password: '' }); // Access the history object to navigate between pages const navigate = useNavigate(); + const { login } = useAuthStore(); // Handle form submission const handleSubmit = async (e) => { e.preventDefault(); - console.log(formData) - // Make a POST request to your registration endpoint with formData - // Handle success by saving the access token to local storage - // Redirect to the dashboard page + console.log(formData); + try { // Make a POST request to the registration endpoint - const response = await fetch('`${import.meta.env.VITE_API_URL}/user/register`', { + const response = await fetch(`${import.meta.env.VITE_API_URL}/user/register`, { method: 'POST', headers: { 'Content-Type': 'application/json', @@ -29,12 +29,8 @@ const Register = () => { if (response.ok) { // Parse the response JSON to get the access token const data = await response.json(); - - // Save the access token to local storage - localStorage.setItem('accessToken', data.accessToken); - - // Redirect to the dashboard page - navigate.push('/dashboard'); + login(data.accessToken); // Use the login action from the store + navigate('/dashboard'); } else { // Handle registration error (status code is not 2xx) const errorData = await response.json(); @@ -85,3 +81,4 @@ const Register = () => { }; export default Register; + diff --git a/frontend/src/store/authStore.jsx b/frontend/src/store/authStore.jsx new file mode 100644 index 000000000..75fff1007 --- /dev/null +++ b/frontend/src/store/authStore.jsx @@ -0,0 +1,11 @@ +// authStore.js +import { createStore } from 'zustand'; + +const UseAuthStore = createStore((set) => ({ + accessToken: localStorage.getItem('accessToken') || null, + login: (token) => set({ accessToken: token }), + logout: () => set({ accessToken: null }), +})); + +export default UseAuthStore; + From fbe53cffc3a6087063002cf0f9e66c505c74cdb1 Mon Sep 17 00:00:00 2001 From: EvelynDelCarmen Date: Fri, 15 Dec 2023 11:57:48 +0100 Subject: [PATCH 22/46] frontend 2.0 --- frontend/src/App.jsx | 14 +++++--------- frontend/src/main.jsx | 3 +++ frontend/src/store/authStore.jsx | 4 ++-- 3 files changed, 10 insertions(+), 11 deletions(-) diff --git a/frontend/src/App.jsx b/frontend/src/App.jsx index 6e3333a72..b0faf4833 100644 --- a/frontend/src/App.jsx +++ b/frontend/src/App.jsx @@ -1,7 +1,5 @@ // App.jsx -import 'react'; import { BrowserRouter, Route, Routes } from 'react-router-dom'; -import UseAuthStore from './store/authStore'; import Register from './components/Register'; import LogIn from './components/LogIn'; import Dashboard from './components/Dashboard'; @@ -11,13 +9,11 @@ const App = () => { return (
- - - } /> - } /> - } /> - - + + } /> + } /> + } /> +
); diff --git a/frontend/src/main.jsx b/frontend/src/main.jsx index b91620d35..a9c89f187 100644 --- a/frontend/src/main.jsx +++ b/frontend/src/main.jsx @@ -8,3 +8,6 @@ ReactDOM.createRoot(document.getElementById("root")).render( ); + + + diff --git a/frontend/src/store/authStore.jsx b/frontend/src/store/authStore.jsx index 75fff1007..5e4251c42 100644 --- a/frontend/src/store/authStore.jsx +++ b/frontend/src/store/authStore.jsx @@ -1,11 +1,11 @@ // authStore.js import { createStore } from 'zustand'; -const UseAuthStore = createStore((set) => ({ +const useAuthStore = createStore((set) => ({ accessToken: localStorage.getItem('accessToken') || null, login: (token) => set({ accessToken: token }), logout: () => set({ accessToken: null }), })); -export default UseAuthStore; +export default useAuthStore; From e58fd6441bf6d59d0245ae69050fad189a515bf0 Mon Sep 17 00:00:00 2001 From: EvelynDelCarmen Date: Fri, 15 Dec 2023 13:49:12 +0100 Subject: [PATCH 23/46] added the new code --- frontend/src/App.jsx | 1 + frontend/src/components/Dashboard.jsx | 25 ++++----- frontend/src/components/LogIn.jsx | 31 ++++++----- frontend/src/components/Register.jsx | 76 ++++++++++++++++----------- frontend/src/store/authStore.jsx | 6 +-- 5 files changed, 80 insertions(+), 59 deletions(-) diff --git a/frontend/src/App.jsx b/frontend/src/App.jsx index b0faf4833..923776bb6 100644 --- a/frontend/src/App.jsx +++ b/frontend/src/App.jsx @@ -1,4 +1,5 @@ // App.jsx +import "react"; import { BrowserRouter, Route, Routes } from 'react-router-dom'; import Register from './components/Register'; import LogIn from './components/LogIn'; diff --git a/frontend/src/components/Dashboard.jsx b/frontend/src/components/Dashboard.jsx index e76265f6f..460636b6b 100644 --- a/frontend/src/components/Dashboard.jsx +++ b/frontend/src/components/Dashboard.jsx @@ -1,21 +1,21 @@ // src/components/Dashboard.js -import { useEffect, useState } from 'react'; -import { useNavigate } from 'react-router-dom'; -import UseAuthStore from '../store/authStore'; // Adjust the path +import { useEffect, useState } from "react"; +import { useNavigate } from "react-router-dom"; +import { authStore } from "../store/authStore"; // Adjust the path const Dashboard = () => { - const [content, setContent] = useState(''); - const accessToken = UseAuthStore((state) => state.accessToken); - const logout = UseAuthStore((state) => state.logout); + const [content, setContent] = useState(""); + const accessToken = authStore((state) => state.accessToken); + const logout = authStore((state) => state.logout); const navigate = useNavigate(); useEffect(() => { if (!accessToken) { // Redirect to the sign-in page using useNavigate - navigate('/login'); + navigate("/login"); } else { fetch(`${import.meta.env.VITE_API_URL}/user/protected`, { - method: 'GET', + method: "GET", headers: { Authorization: `Bearer ${accessToken}`, }, @@ -24,15 +24,15 @@ const Dashboard = () => { if (response.ok) { return response.json(); } else { - throw new Error('Failed to fetch content'); + throw new Error("Failed to fetch content"); } }) .then((data) => { setContent(data.message); }) .catch((error) => { - console.error('Error fetching content:', error); - setContent('Error fetching content'); + console.error("Error fetching content:", error); + setContent("Error fetching content"); }); } }, [accessToken, navigate]); @@ -40,7 +40,7 @@ const Dashboard = () => { const handleSignOut = () => { // Use the logout action from the store logout(); - navigate('/login'); // Redirect to the sign-in page + navigate("/login"); // Redirect to the sign-in page }; return ( @@ -54,3 +54,4 @@ const Dashboard = () => { export default Dashboard; + diff --git a/frontend/src/components/LogIn.jsx b/frontend/src/components/LogIn.jsx index 229031fe0..f2440115d 100644 --- a/frontend/src/components/LogIn.jsx +++ b/frontend/src/components/LogIn.jsx @@ -1,24 +1,24 @@ // src/components/SignIn.js -import { useState } from 'react'; -import { useNavigate } from 'react-router-dom'; -import UseAuthStore from '../store/authStore'; +import { useState } from "react"; +import { useNavigate } from "react-router-dom"; +import { authStore } from "../store/authStore"; const LogIn = () => { // State to store form data - const [formData, setFormData] = useState({ username: '', password: '' }); + const [formData, setFormData] = useState({ username: "", password: "" }); // Access the history object to navigate between pages const navigate = useNavigate(); - const { login } = UseAuthStore(); + const { login } = authStore(); // Handle form submission const handleSubmit = async (e) => { e.preventDefault(); try { // Make a POST request to the login endpoint - const response = await fetch(`${import.meta.env.VITE_API_URL}/user/login`, { - method: 'POST', + const response = await fetch(`http://localhost:3000/user/login`, { + method: "POST", headers: { - 'Content-Type': 'application/json', + "Content-Type": "application/json", }, body: JSON.stringify(formData), }); @@ -26,14 +26,14 @@ const LogIn = () => { if (response.ok) { const data = await response.json(); login(data.accessToken); // Use the login action from the store - navigate('/dashboard'); + navigate("/dashboard"); } else { const errorData = await response.json(); - console.error('Login error:', errorData.error); + console.error("Login error:", errorData.error); // Handle login error } } catch (error) { - console.error('Unexpected error during login:', error); + console.error("Unexpected error during login:", error); // Handle unexpected error } }; @@ -48,7 +48,9 @@ const LogIn = () => { type="text" id="username" value={formData.username} - onChange={(e) => setFormData({ ...formData, username: e.target.value })} + onChange={(e) => + setFormData({ ...formData, username: e.target.value }) + } /> @@ -56,7 +58,9 @@ const LogIn = () => { type="password" id="password" value={formData.password} - onChange={(e) => setFormData({ ...formData, password: e.target.value })} + onChange={(e) => + setFormData({ ...formData, password: e.target.value }) + } /> {/* Submit button */} @@ -67,3 +71,4 @@ const LogIn = () => { }; export default LogIn; + diff --git a/frontend/src/components/Register.jsx b/frontend/src/components/Register.jsx index f92ac597d..21fa9bc37 100644 --- a/frontend/src/components/Register.jsx +++ b/frontend/src/components/Register.jsx @@ -1,45 +1,59 @@ // src/components/Register.js -import { useState } from 'react'; -import { useNavigate } from 'react-router-dom'; -import useAuthStore from '../store/authStore'; +import { useState } from "react"; +import { useNavigate } from "react-router-dom"; +import { authStore } from "../store/authStore"; const Register = () => { // State to store form data - const [formData, setFormData] = useState({ username: '', email: '', password: '' }); + const [formData, setFormData] = useState({ + username: "", + email: "", + password: "", + }); + + const [username, setUsername] = useState(""); + const [email, setEmail] = useState(""); + const [password, setPassword] = useState(""); // Access the history object to navigate between pages const navigate = useNavigate(); - const { login } = useAuthStore(); + const { login } = authStore(); // Handle form submission - const handleSubmit = async (e) => { - e.preventDefault(); - console.log(formData); + const handleSubmit = async () => { + if (!username || !password || !email) { + // Display an alert if any of the required fields are empty. + alert("Please enter email, username, and password"); + return; + } + //e.preventDefault(); + console.log(username, email, password); try { // Make a POST request to the registration endpoint const response = await fetch(`${import.meta.env.VITE_API_URL}/user/register`, { - method: 'POST', + method: "POST", headers: { - 'Content-Type': 'application/json', + "Content-Type": "application/json", }, - body: JSON.stringify(formData), + body: JSON.stringify({ email, username, password }), }); // Check if the request was successful (status code 2xx) - if (response.ok) { + const data = await response.json(); + console.log(data); + if (data.success) { // Parse the response JSON to get the access token - const data = await response.json(); - login(data.accessToken); // Use the login action from the store - navigate('/dashboard'); + // login(data.accessToken); // Use the login action from the store + //navigate("/dashboard"); } else { // Handle registration error (status code is not 2xx) - const errorData = await response.json(); - console.error('Registration error:', errorData.error); + // const errorData = await response.json(); + //console.error("Registration error:", errorData.error); // Display an error message to the user, e.g., set a state variable for displaying an error message } } catch (error) { // Handle network errors or other unexpected errors - console.error('Unexpected error during registration:', error); + console.error("Unexpected error during registration:", error); // Display a generic error message to the user } }; @@ -47,35 +61,35 @@ const Register = () => { return (

Register

- +
{/* Registration form fields */} setFormData({ ...formData, username: e.target.value })} + placeholder="Username" + value={username} + onChange={(e) => setUsername(e.target.value)} /> setFormData({ ...formData, email: e.target.value })} + type="text" + placeholder="Email" + value={email} + onChange={(e) => setEmail(e.target.value)} /> setFormData({ ...formData, password: e.target.value })} + placeholder="Password" + value={password} + onChange={(e) => setPassword(e.target.value)} /> {/* Submit button */} - - + +
); }; diff --git a/frontend/src/store/authStore.jsx b/frontend/src/store/authStore.jsx index 5e4251c42..8c562dd22 100644 --- a/frontend/src/store/authStore.jsx +++ b/frontend/src/store/authStore.jsx @@ -1,11 +1,11 @@ // authStore.js -import { createStore } from 'zustand'; +import { create } from 'zustand'; -const useAuthStore = createStore((set) => ({ +export const authStore = create((set, get) => ({ accessToken: localStorage.getItem('accessToken') || null, login: (token) => set({ accessToken: token }), logout: () => set({ accessToken: null }), })); -export default useAuthStore; + From ddaf2248e1a11ce4e00ed5d8686e2f287c9cd544 Mon Sep 17 00:00:00 2001 From: EvelynDelCarmen Date: Fri, 15 Dec 2023 21:43:01 +0100 Subject: [PATCH 24/46] changed the API --- backend/routes/authUserRoutes.js | 2 +- backend/server.js | 1 - frontend/src/components/Dashboard.jsx | 3 ++- frontend/src/components/LogIn.jsx | 2 +- frontend/src/components/Register.jsx | 19 +++++++++++++------ 5 files changed, 17 insertions(+), 10 deletions(-) diff --git a/backend/routes/authUserRoutes.js b/backend/routes/authUserRoutes.js index ac737d6b8..0a7896f66 100644 --- a/backend/routes/authUserRoutes.js +++ b/backend/routes/authUserRoutes.js @@ -7,7 +7,7 @@ import crypto from 'crypto'; const router = express.Router(); -router.post('/register', async (req, res) => { +router.post('/user/register', async (req, res) => { const { username, email, password } = req.body; try { diff --git a/backend/server.js b/backend/server.js index 830bf77a0..45987407f 100644 --- a/backend/server.js +++ b/backend/server.js @@ -27,7 +27,6 @@ app.use((req, res, next) => { app.use('/auth', authUserMiddleware); app.use('/user', authUserRoutes); -// app.use('/auth', authUserRoutes); app.use('/secret', secretRoute); // Add this line app.get("/", (req, res) => { diff --git a/frontend/src/components/Dashboard.jsx b/frontend/src/components/Dashboard.jsx index 460636b6b..29257449a 100644 --- a/frontend/src/components/Dashboard.jsx +++ b/frontend/src/components/Dashboard.jsx @@ -1,7 +1,8 @@ // src/components/Dashboard.js +import { authStore } from "../store/authStore"; import { useEffect, useState } from "react"; import { useNavigate } from "react-router-dom"; -import { authStore } from "../store/authStore"; // Adjust the path + const Dashboard = () => { const [content, setContent] = useState(""); diff --git a/frontend/src/components/LogIn.jsx b/frontend/src/components/LogIn.jsx index f2440115d..486c6639f 100644 --- a/frontend/src/components/LogIn.jsx +++ b/frontend/src/components/LogIn.jsx @@ -15,7 +15,7 @@ const LogIn = () => { e.preventDefault(); try { // Make a POST request to the login endpoint - const response = await fetch(`http://localhost:3000/user/login`, { + const response = await fetch(`${import.meta.env.VITE_API_URL}/auth/protected`, { method: "POST", headers: { "Content-Type": "application/json", diff --git a/frontend/src/components/Register.jsx b/frontend/src/components/Register.jsx index 21fa9bc37..9e841bc56 100644 --- a/frontend/src/components/Register.jsx +++ b/frontend/src/components/Register.jsx @@ -1,15 +1,16 @@ // src/components/Register.js +import { authStore } from "../store/authStore"; import { useState } from "react"; import { useNavigate } from "react-router-dom"; -import { authStore } from "../store/authStore"; + const Register = () => { // State to store form data - const [formData, setFormData] = useState({ - username: "", - email: "", - password: "", - }); + // const [formData, setFormData] = useState({ + // username: "", + // email: "", + // password: "", + // }); const [username, setUsername] = useState(""); const [email, setEmail] = useState(""); @@ -45,11 +46,17 @@ const Register = () => { // Parse the response JSON to get the access token // login(data.accessToken); // Use the login action from the store //navigate("/dashboard"); + + // If registration is successful, update the accessToken using the login function from the store + login(data.accessToken); + // Optionally, navigate to the dashboard or another page + navigate("/dashboard"); } else { // Handle registration error (status code is not 2xx) // const errorData = await response.json(); //console.error("Registration error:", errorData.error); // Display an error message to the user, e.g., set a state variable for displaying an error message + console.error("Registration error:", data.error); } } catch (error) { // Handle network errors or other unexpected errors From c83d5f1318d2098725130683e05f3f6b9957d5ea Mon Sep 17 00:00:00 2001 From: EvelynDelCarmen Date: Tue, 2 Apr 2024 21:19:52 +0200 Subject: [PATCH 25/46] changed the /user/register --- backend/server.js | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/backend/server.js b/backend/server.js index 45987407f..bde4ed96c 100644 --- a/backend/server.js +++ b/backend/server.js @@ -10,7 +10,7 @@ import listEndpoints from 'express-list-endpoints'; import dotenv from 'dotenv'; dotenv.config(); -const mongoUrl = process.env.MONGO_URI || "mongodb://localhost:27017/project-mongo"; +const mongoUrl = process.env.MONGO_URI || "mongodb://localhost/project-mongo"; mongoose.connect(mongoUrl, { useNewUrlParser: true, useUnifiedTopology: true }); mongoose.Promise = Promise; From a93fdcf094c56734842c0025bb7dfdd6aa6286f0 Mon Sep 17 00:00:00 2001 From: EvelynDelCarmen Date: Tue, 2 Apr 2024 21:22:11 +0200 Subject: [PATCH 26/46] made some changes to backend --- backend/routes/authUserRoutes.js | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/backend/routes/authUserRoutes.js b/backend/routes/authUserRoutes.js index 0a7896f66..ac737d6b8 100644 --- a/backend/routes/authUserRoutes.js +++ b/backend/routes/authUserRoutes.js @@ -7,7 +7,7 @@ import crypto from 'crypto'; const router = express.Router(); -router.post('/user/register', async (req, res) => { +router.post('/register', async (req, res) => { const { username, email, password } = req.body; try { From fec87d388c326bb5e65af364f28bb97b5b71f77b Mon Sep 17 00:00:00 2001 From: EvelynDelCarmen Date: Wed, 3 Apr 2024 20:49:18 +0200 Subject: [PATCH 27/46] changed the logic in the backend --- backend/config/db.js | 35 ++++++ backend/controllers/userController.js | 137 +++++++++++++++++++++ backend/middleware/authMiddleware.js | 97 --------------- backend/middleware/authenticateUser.js | 137 +++++++++++++++++++++ backend/models/userModel.js | 119 +++++++++++++----- backend/package.json | 1 + backend/routes/authUserRoutes.js | 161 ------------------------- backend/routes/secretRoutes.js | 57 --------- backend/routes/userRoutes.js | 20 +++ backend/server.js | 125 ++++++++++++++----- 10 files changed, 511 insertions(+), 378 deletions(-) create mode 100644 backend/config/db.js create mode 100644 backend/controllers/userController.js delete mode 100644 backend/middleware/authMiddleware.js create mode 100644 backend/middleware/authenticateUser.js delete mode 100644 backend/routes/authUserRoutes.js delete mode 100644 backend/routes/secretRoutes.js create mode 100644 backend/routes/userRoutes.js diff --git a/backend/config/db.js b/backend/config/db.js new file mode 100644 index 000000000..2854bd4e8 --- /dev/null +++ b/backend/config/db.js @@ -0,0 +1,35 @@ +// Import the 'mongoose' library to work with MongoDB +import mongoose from "mongoose"; + +// Import the 'dotenv' library to load environment variables from a .env file +import dotenv from "dotenv"; + +// Load environment variables from the .env file +dotenv.config(); + +// Define an asynchronous function 'connectDB' to connect to the MongoDB database +export const connectDB = async () => { + try { + // Attempt to connect to the MongoDB database using the URL from the environment variables + // Mongoose Method: mongoose.connect() + // Description: This line of code serves the crucial purpose of connecting the Node.js application to the MongoDB database specified by the URL provided in the environment variable MONGO_URL. Once this connection is established, the application can perform various database operations, such as querying and modifying data in the MongoDB database. It's a critical step in setting up the database connection for the application to work with MongoDB. + const conn = await mongoose.connect(process.env.MONGO_URI); + + // If the connection is successful, log a message indicating that the MongoDB is connected + console.log(`Mongo DB Connected: ${conn.connection.host}`); + } catch (error) { + // If an error occurs during the connection attempt, log the error message + console.log(error); + + // Exit the Node.js process with an exit code of 1 to indicate an error + process.exit(1); + } +}; + +// In summary, this code does the following: + +// - Imports the necessary libraries (mongoose and dotenv) for working with MongoDB and loading environment variables. +// - Loads environment variables from a .env file, which allows you to store configuration values separately from your code. +// - Defines an asynchronous function named connectDB that attempts to connect to a MongoDB database using the URL specified in the MONGO_URL environment variable. +// - If the connection is successful, it logs a message indicating that the MongoDB database is connected, including the host information. +// - If an error occurs during the connection attempt, it logs the error message and exits the Node.js process with an exit code of 1 to indicate that an error has occurred. \ No newline at end of file diff --git a/backend/controllers/userController.js b/backend/controllers/userController.js new file mode 100644 index 000000000..c0fdd95ce --- /dev/null +++ b/backend/controllers/userController.js @@ -0,0 +1,137 @@ +import { UserModel } from "../models/UserModel"; +//asyncHandler: We use asyncHandler to simplify error handling in asynchronous code. It helps us avoid writing repetitive try-catch blocks by automatically catching errors and passing them to our error handling middleware. This makes our code cleaner and more readable, reducing the risk of unhandled exceptions that could crash the server. +import asyncHandler from "express-async-handler"; +// bcrypt: We use bcrypt to securely hash and store passwords in our database. Storing plain-text passwords is a security risk, as it exposes user credentials in case of a data breach. bcrypt helps us hash passwords in a way that is computationally expensive and time-consuming for potential attackers, making it difficult to crack passwords even if the database is compromised. It enhances the overall security of user authentication in our application. +import bcrypt from "bcrypt"; +// jwt (JSON Web Tokens): We use jwt for authentication and authorization. It allows us to create and verify tokens that contain user identity information, such as user IDs or roles. These tokens are often sent with requests to secure routes and verify that a user has the necessary permissions to access certain resources. JWTs are stateless and efficient, making them a popular choice for secure communication between the client and server. +import jwt from "jsonwebtoken"; + +// Actual Functions here + +// ----------------------- +// ----------------------- + +// @desc Register new user +// @route POST api/register +// @access Public + +export const registerUserController = asyncHandler(async (req, res) => { + // Extract email, username and password from the request body + const { username, password, email } = req.body; + // In this try section of the try catch we will first do some conditional logic and then generate the newUser with a crypted password within the DB. + try { + // 1st Condition + // Check wether all fields of registration logic are NOT [!email] inputted from the request.body object + if (!username || !email || !password) { + // if so, set http status to a 400code + res.status(400); + // and throw new error with some info + throw new Error("Please add all fields"); + } + // 2nd Condition + // Check if the current user trying to register is using an usernam or email that matches with the same username or email in the database, so they would have to choose something diferent + const existingUser = await UserModel.findOne({ + $or: [{ username }, { email }], + }); + if (existingUser) { + res.status(400); + throw new Error( + `User with ${existingUser.username === username ? "username" : "email" + } already exists` + ); + } + + // Generate a salt and hash the user's password + //In this line below, we're using the bcrypt library to create a random value called "salt." The salt is added to the password before hashing it. It adds an extra layer of security by making it more difficult for attackers to use precomputed tables (rainbow tables) to crack passwords. The 10 in genSaltSync(10) represents the cost factor, which determines how computationally intensive the hashing process will be. + const salt = bcrypt.genSaltSync(10); + + const hashedPassword = bcrypt.hashSync(password, salt); + // In this line below, we're using the generated salt to hash the user's password. Hashing transforms the password into a secure and irreversible string of characters. The bcrypt library handles the entire process for us, ensuring that the password is securely hashed. The resulting hashedPassword is what we store in the database to keep the user's password safe. + // Create a new user instance with the hashed password + const newUser = new UserModel({ + username, + email, + password: hashedPassword, + }); + + // Mongoose Method: newUser.save() + // Description: Save the new user instance to the database + await newUser.save(); + + // Respond with a success message, user details, and the JWT token + res.status(201).json({ + success: true, + response: { + username: newUser.username, + email: newUser.email, + id: newUser._id, + accessToken: newUser.accessToken, + }, + }); + } catch (e) { + // Handle any errors that occur during the registration process + res.status(500).json({ success: false, response: e.message }); + } +}); + +// ----------------------- +// ----------------------- +// ----------------------- +// ----------------------- +// ----------------------- + +// @desc Login Existing User +// @route POST api/login +// @access Public + +export const loginUserController = asyncHandler(async (req, res) => { + // Extract username and password from the request body + const { username, password } = req.body; + + try { + + console.log("Username:", username); + console.log("Password:", password); + // Find a user with the provided username in the database + const user = await UserModel.findOne({ username }); + if (!user) { + // If no user is found with the provided username, respond with a 401 Unauthorized and a user not found message + return res + .status(401) + .json({ success: false, response: "User not found" }); + } + + // Compare the provided password with the hashed password in the database + const isMatch = await bcrypt.compare(password, user.password); + if (!isMatch) { + // If the provided password doesn't match the stored password, respond with a 401 Unauthorized and an incorrect password message + return res + .status(401) + .json({ success: false, response: "Incorrect password" }); + } + // Respond with a success message, user details, and the JWT token + res.status(200).json({ + success: true, + response: { + username: user.username, + id: user._id, + accessToken: user.accessToken, // token for the user using the acessToken generated from the model, // Use the generated token here + }, + }); + } catch (e) { + // Handle any errors that occur during the login process + res.status(500).json({ success: false, response: e.message }); + } +}); + +// SUMMARY + +// This file contains controller functions for user-related operations within an Express.js application. Let's provide a summary with additional context: + +// registerUserController: This controller handles user registration. It extracts the user's username, password, and email from the request body. It performs several checks, such as ensuring that all required fields are provided and that the chosen username or email is not already in use by another user. It securely hashes the user's password using the bcrypt library and stores the hashed password in the database. After successfully registering the user, it responds with a success message, user details, and a JSON Web Token (JWT) for authentication. + +// generateToken: This is a utility function used to generate JWT tokens for user authentication. It takes a user object and creates a token containing the user's access token, with an optional secret key and a 24-hour expiration time. + +// loginUserController: This controller manages user login. It extracts the username and password from the request body, then attempts to find a user with the provided username in the database. If the user is found, it compares the provided password with the hashed password stored in the database using bcrypt. If the credentials match, it generates a JWT token for the user and responds with a success message, user details, and the JWT token. In case of authentication failure (wrong password or non-existent user), it responds with appropriate error messages. + +// In summary, this file provides controllers for user registration and login, ensuring that user credentials are securely handled and authenticated using JWT tokens. It also uses bcrypt to hash and store passwords securely in the database, enhancing the overall security of user authentication in the application. diff --git a/backend/middleware/authMiddleware.js b/backend/middleware/authMiddleware.js deleted file mode 100644 index e00ad4418..000000000 --- a/backend/middleware/authMiddleware.js +++ /dev/null @@ -1,97 +0,0 @@ -// authMiddleware.js -import jwt from 'jsonwebtoken'; -import User from '../models/userModel'; - -const authenticateUser = async (req, res, next) => { - const { authorization } = req.headers; - - try { - console.log('Received token:', authorization); - if (!authorization || !authorization.startsWith('Bearer ')) { - return res.status(401).json({ error: 'Unauthorized - Missing or invalid token' }); - } - - const token = authorization.split(' ')[1]; - const decoded = jwt.verify(token, process.env.JWT_SECRET); - - if (!decoded || !decoded.userId) { - return res.status(401).json({ error: 'Unauthorized - Invalid token content' }); - } - - // Attach the user to the request object - req.user = await User.findById(decoded.userId); - - if (!req.user) { - return res.status(401).json({ error: 'Unauthorized - User not found' }); - } - - next(); - } catch (error) { - console.error('Error during token verification:', error); - res.status(401).json({ error: 'Unauthorized - Token verification failed' }); - } -}; - -export default authenticateUser; - - - - - - - - -// authMiddleware.js -// import User from '../models/userModel'; -// import jwt from 'jsonwebtoken'; -// import crypto from 'crypto'; - -// // Middleware to handle user authentication -// const authenticateUser = async (req, res, next) => { -// const { authorization } = req.headers; - -// try { -// if (!authorization || !authorization.startsWith('Bearer ')) { -// return res.status(401).json({ error: 'Unauthorized' }); -// } - -// const token = authorization.split(' ')[1]; -// const secretKey = generateRandomKey(); - -// const decoded = jwt.verify(token, secretKey); - -// const user = await User.findById(decoded.userId); - -// if (!user) { -// return res.status(401).json({ error: 'Unauthorized' }); -// } - -// req.user = user; - -// next(); -// } catch (error) { -// res.status(401).json({ error: 'Unauthorized' }); -// } -// }; - -// // Function to generate a random secret key -// const generateRandomKey = () => { -// return crypto.randomBytes(32).toString('hex'); -// }; - -// export default authenticateUser; - - -// 'authUser.js': -// Uses the comparePassword method from the User model to check if the entered password matches the hashed password stored in the database. -// This middleware is applied to routes where user authentication is required. - - - -//Explanation:This middleware defines a route ('/login') for handling user login. -// It retrieves the username and password from the request body. -// It searches for the user in the database by the provided username. -// If the user is found, it compares the entered password with the hashed password in the database using the comparePassword method from the 'userModel.js'. -// If the credentials are valid, it generates an access token (you need to replace the placeholder with your actual token generation logic) and attaches it to the user in the database. -// The middleware then sends a response with the access token. - diff --git a/backend/middleware/authenticateUser.js b/backend/middleware/authenticateUser.js new file mode 100644 index 000000000..280527586 --- /dev/null +++ b/backend/middleware/authenticateUser.js @@ -0,0 +1,137 @@ +// Import the UserModel from the User model file +import { UserModel } from "../models/UserModel"; +// Define a function called authenticateUser that takes a request (req), response (res), and a next function as parameters +export const authenticateUser = async (req, res, next) => { + // Retrieve the access token from the request header + const accessToken = req.header("Authorization"); + try { + // Find a user in the database using the retrieved access token + // Mongoose Method: UserModel.findOne({ accessToken: accessToken }) + // Description: This line of code serves the purpose of authenticating a user based on the provided access token. It checks if a user with the specified accessToken exists in the database using the UserModel. If a user is found, their user document is stored in the user variable. This allows the middleware to add the user object to the request, making it available for subsequent middleware or routes. If no user is found, it prepares to send a 401 Unauthorized response to indicate that the user needs to log in. This code is an essential part of user authentication in the Node.js application and helps control access to protected routes or endpoints. + const user = await UserModel.findOne({ accessToken: accessToken }); + if (user) { + // If a user is found, add the user object to the request object + req.user = user; // Add user to the request object + next(); // Continue to the next middleware or route + } else { + // If no user is found, send a 401 Unauthorized response + res.status(401).json({ success: false, response: "Please log in" }); + } + } catch (e) { + // Handle any errors that occur during the database query or user authentication + res.status(500).json({ success: false, response: e.message }); + } +}; + +// SUMMARY + +//In this code, we have a function called authenticateUser that is used as middleware in a Node.js application. This middleware is responsible for checking the authorization header of an incoming request, searching for a user with the provided access token in the database using the UserModel, and adding the user object to the request if found. If no user is found or if there are any errors during the process, appropriate responses are sent back to the client. In summary, this code is handling user authentication by checking the access token in the request header and verifying it against the database to grant access to protected routes or endpoints. + + + + + + + + + + + + +// // authMiddleware.js +// import jwt from 'jsonwebtoken'; +// import User from '../models/userModel'; + +// const authenticateUser = async (req, res, next) => { +// const { authorization } = req.headers; + +// try { +// console.log('Received token:', authorization); +// if (!authorization || !authorization.startsWith('Bearer ')) { +// return res.status(401).json({ error: 'Unauthorized - Missing or invalid token' }); +// } + +// const token = authorization.split(' ')[1]; +// const decoded = jwt.verify(token, process.env.JWT_SECRET); + +// if (!decoded || !decoded.userId) { +// return res.status(401).json({ error: 'Unauthorized - Invalid token content' }); +// } + +// // Attach the user to the request object +// req.user = await User.findById(decoded.userId); + +// if (!req.user) { +// return res.status(401).json({ error: 'Unauthorized - User not found' }); +// } + +// next(); +// } catch (error) { +// console.error('Error during token verification:', error); +// res.status(401).json({ error: 'Unauthorized - Token verification failed' }); +// } +// }; + +// export default authenticateUser; + + + + + + + + +// authMiddleware.js +// import User from '../models/userModel'; +// import jwt from 'jsonwebtoken'; +// import crypto from 'crypto'; + +// // Middleware to handle user authentication +// const authenticateUser = async (req, res, next) => { +// const { authorization } = req.headers; + +// try { +// if (!authorization || !authorization.startsWith('Bearer ')) { +// return res.status(401).json({ error: 'Unauthorized' }); +// } + +// const token = authorization.split(' ')[1]; +// const secretKey = generateRandomKey(); + +// const decoded = jwt.verify(token, secretKey); + +// const user = await User.findById(decoded.userId); + +// if (!user) { +// return res.status(401).json({ error: 'Unauthorized' }); +// } + +// req.user = user; + +// next(); +// } catch (error) { +// res.status(401).json({ error: 'Unauthorized' }); +// } +// }; + +// // Function to generate a random secret key +// const generateRandomKey = () => { +// return crypto.randomBytes(32).toString('hex'); +// }; + +// export default authenticateUser; + + +// 'authUser.js': +// Uses the comparePassword method from the User model to check if the entered password matches the hashed password stored in the database. +// This middleware is applied to routes where user authentication is required. + + + +//Explanation:This middleware defines a route ('/login') for handling user login. +// It retrieves the username and password from the request body. +// It searches for the user in the database by the provided username. +// If the user is found, it compares the entered password with the hashed password in the database using the comparePassword method from the 'userModel.js'. +// If the credentials are valid, it generates an access token (you need to replace the placeholder with your actual token generation logic) and attaches it to the user in the database. +// The middleware then sends a response with the access token. + diff --git a/backend/models/userModel.js b/backend/models/userModel.js index c278189f0..4a5c22db2 100644 --- a/backend/models/userModel.js +++ b/backend/models/userModel.js @@ -1,38 +1,93 @@ -// userModel.js -import mongoose from 'mongoose'; -import bcrypt from 'bcrypt'; - -const userSchema = new mongoose.Schema({ - username: { type: String, unique: true, required: true }, - email: { type: String, unique: true, required: true }, - password: { type: String, required: true }, - secretKey: { type: String }, -}); - -userSchema.pre('save', async function (next) { - const user = this; - if (user.isModified('password')) { - try { - const hashedPassword = await bcrypt.hash(user.password, 10); - user.password = hashedPassword; - } catch (error) { - next(error); - } - } - next(); -}); - -userSchema.methods.comparePassword = async function (enteredPassword) { - try { - return await bcrypt.compare(enteredPassword, this.password); - } catch (error) { - throw new Error(error); +import mongoose from "mongoose"; +import crypto from "crypto"; // Imports the Node.js crypto library for generating secure random strings. + +// Import the Schema class from the Mongoose library +// Destructures the Schema class from the Mongoose library, allowing us to create a schema. +const { Schema } = mongoose; + +// Create a new Mongoose schema named 'userSchema' +// Creates a new Mongoose schema named userSchema that defines the structure of a user document in the MongoDB collection. It includes fields like username, password, and accessToken, specifying their data types, validation rules, and default values. +const userSchema = new Schema( + { + // Define the 'username' field with a String data type + username: { + type: String, // Specifies that 'username' should be a string + required: true, // Indicates that 'username' is a required field + unique: true, // Ensures that 'username' values are unique + minlength: 2, // Sets a minimum length of 2 characters for 'username' + }, + // Define the 'password' field with a String data type + password: { + type: String, // Specifies that 'password' should be a string + required: true, // Indicates that 'password' is a required field + minlength: 6, // Sets a minimum length of 6 characters for 'password' + }, + email: { + type: String, + required: true, + unique: true, + }, + //Define the 'accessToken' field with a String data type + accessToken: { + type: String, // Specifies that 'accessToken' should be a string + default: () => crypto.randomBytes(128).toString("hex"), // Sets a default value using a cryptographic random string + }, + }, + { + timestamps: true, } -}; +); + +// Create a Mongoose model named 'UserModel' based on the 'userSchema' for the 'users' collection +// This model is used to interact with the "users" collection in the MongoDB database. It allows you to perform CRUD operations on user documents and provides methods for data validation based on the schema. +export const UserModel = mongoose.model("User", userSchema); + +// In summary, this code defines a Mongoose schema (userSchema) that describes the structure of documents for users in a MongoDB collection. It also creates a Mongoose model (UserModel) associated with the "users" collection, which can be used to interact with the database and perform operations like creating, reading, updating, and deleting user documents. + + + + + + + + + + +// // userModel.js +// import mongoose from 'mongoose'; +// import bcrypt from 'bcrypt'; + +// const userSchema = new mongoose.Schema({ +// username: { type: String, unique: true, required: true }, +// email: { type: String, unique: true, required: true }, +// password: { type: String, required: true }, +// secretKey: { type: String }, +// }); + +// userSchema.pre('save', async function (next) { +// const user = this; +// if (user.isModified('password')) { +// try { +// const hashedPassword = await bcrypt.hash(user.password, 10); +// user.password = hashedPassword; +// } catch (error) { +// next(error); +// } +// } +// next(); +// }); -const User = mongoose.model('User', userSchema); +// userSchema.methods.comparePassword = async function (enteredPassword) { +// try { +// return await bcrypt.compare(enteredPassword, this.password); +// } catch (error) { +// throw new Error(error); +// } +// }; -export default User; +// const User = mongoose.model('User', userSchema); + +// export default User; diff --git a/backend/package.json b/backend/package.json index fd1e674cf..5266c2f64 100644 --- a/backend/package.json +++ b/backend/package.json @@ -17,6 +17,7 @@ "crypto": "^1.0.1", "dotenv": "^16.3.1", "express": "^4.17.3", + "express-async-handler": "^1.2.0", "express-list-endpoints": "^6.0.0", "jsonwebtoken": "^9.0.2", "mongoose": "^8.0.2", diff --git a/backend/routes/authUserRoutes.js b/backend/routes/authUserRoutes.js deleted file mode 100644 index ac737d6b8..000000000 --- a/backend/routes/authUserRoutes.js +++ /dev/null @@ -1,161 +0,0 @@ -// authRoutes.js -import express from 'express'; -import User from '../models/userModel'; -import jwt from 'jsonwebtoken'; -import authenticateUser from '../middleware/authMiddleware'; -import crypto from 'crypto'; - -const router = express.Router(); - -router.post('/register', async (req, res) => { - const { username, email, password } = req.body; - - try { - // Check if the username or email is already taken - const existingUser = await User.findOne({ $or: [{ username }, { email }] }); - - if (existingUser) { - return res.status(400).json({ error: 'Username or email already exists' }); - } - - // Generate a unique secret key for the user - const secretKey = crypto.randomBytes(32).toString('hex'); - - // Create a new user instance with the secret key - const newUser = new User({ username, email, password, secretKey }); - // Save the new user to the database - await newUser.save(); - - // Use jsonwebtoken to create a token - const accessToken = jwt.sign({ userId: newUser._id }, process.env.JWT_SECRET, { expiresIn: '24h' }); - - res.status(201).json({ accessToken }); - } catch (error) { - console.error('Error during registration:', error); - res.status(500).json({ error: 'Internal Server Error' }); - } -}); - -// Route for user login ('/login') -router.post('/login', async (req, res) => { - const { username, password } = req.body; - - try { - const user = await User.findOne({ username }); - - if (!user) { - return res.status(401).json({ error: 'Invalid username or password' }); - } - - const isPasswordValid = await user.comparePassword(password); - - if (!isPasswordValid) { - return res.status(401).json({ error: 'Invalid username or password' }); - } - - // Use jsonwebtoken to create a token - const accessToken = jwt.sign({ userId: user._id }, process.env.JWT_SECRET, { expiresIn: '24h' }); - - res.json({ accessToken }); - } catch (error) { - res.status(500).json({ error: 'Internal Server Error' }); - } -}); - -router.get('/protected', authenticateUser, (req, res) => { - res.json({ message: 'This is a protected route' }); -}); - -export default router; - - - - - -// authUserRoutes.js -// import express from 'express'; -// import User from '../models/userModel'; -// import jwt from 'jsonwebtoken'; -// // import authenticateUser from '../middleware/authMiddleware'; - -// // Create an instance of the Express Router -// const router = express.Router(); - -// // Route for user login ('/login') -// router.post('/login', async (req, res) => { -// const { username, email, password } = req.body; - -// try { -// // Find the user by either username or email -// const user = await User.findOne({ $or: [{ username }, { email }] }); - -// if (!user) { -// return res.status(401).json({ error: 'Invalid username, email, or password' }); -// } - -// const isPasswordValid = await user.comparePassword(password); - -// if (!isPasswordValid) { -// return res.status(401).json({ error: 'Invalid username, email, or password' }); -// } - -// const secretKey = generateRandomKey(); -// const accessToken = jwt.sign({ userId: user._id }, secretKey, { expiresIn: '24h' }); - -// res.json({ accessToken }); -// } catch (error) { -// res.status(500).json({ error: 'Internal Server Error' }); -// } -// }); - -// // Route for user signup ('/signup') -// router.post('/signup', async (req, res) => { -// console.log('Received signup request'); -// const { username, email, password } = req.body; - -// console.log('Received POST request at /user/signup'); - -// try { -// // Check if the username or email is already in use -// const existingUser = await User.findOne({ $or: [{ username }, { email }] }); - -// if (existingUser) { -// return res.status(400).json({ error: 'Username or email already in use' }); -// } - -// // Create a new user instance with the provided username, email, and password -// const newUser = new User({ username, email, password }); -// // Save the new user to the database -// await newUser.save(); - -// console.log('User saved successfully'); - -// // Send a success response -// res.status(201).json({ message: 'User registered successfully' }); -// } catch (error) { -// console.error('Error during registration:', error); -// // Handle validation errors or other issues during registration -// res.status(500).json({ error: 'Internal Server Error' }); -// } -// }); - -// // Export the router for use in other files -// export default router; - - -// Explanation: - -// The '/register' route handles user registration. It creates a new user instance, saves it to the database, and returns a success message. -// The '/signin' route handles user sign-in. It searches for the user by the provided username, compares the entered password with the hashed password in the database, and returns a success message if the credentials are valid. -// Both routes handle potential errors and return appropriate responses. - -// Registration (/register route): - -// When a new user is registered, you create a new instance of the User model using const newUser = new User({ username, password });. -// The userModel.js file contains a pre-save middleware using bcrypt that automatically hashes the password before saving it to the database. This ensures that the actual password is not stored in the database. - -// Sign-In (/signin route): - -// When a user attempts to sign in, you find the user in the database based on the provided username using const user = await User.findOne({ username });. -// If the user is found, you use the comparePassword method from the userModel.js file. This method compares the entered password with the hashed password stored in the database. It internally uses bcrypt to perform this comparison. -// If the password is valid, the user is signed in successfully. \ No newline at end of file diff --git a/backend/routes/secretRoutes.js b/backend/routes/secretRoutes.js deleted file mode 100644 index 052176721..000000000 --- a/backend/routes/secretRoutes.js +++ /dev/null @@ -1,57 +0,0 @@ -// secretRoute.js -import express from 'express'; -import authenticateUser from '../middleware/authMiddleware'; - -const router = express.Router(); - -// Route for accessing the secret content ('/secret') -router.get('/', authenticateUser, async (req, res) => { - try { - // Access the authenticated user from the request object - const authenticatedUser = req.user; - - // You can now use the authenticatedUser to fetch user-specific content from the database - // For simplicity, let's just return a success message - res.json({ message: `Hello, ${authenticatedUser.username}! Welcome to the secret route.` }); - } catch (error) { - // Handle any errors that may occur - res.status(500).json({ error: 'Internal Server Error' }); - } -}); - -export default router; - - - - - -// import express from 'express'; -// import authUserMiddleware from 'backend/middleware/authMiddleware.js'; // Import the authentication middleware - -// // Create an instance of the Express Router -// const router = express.Router(); - -// // Route for accessing the secret content ('/secret') -// router.get('/secret', authUserMiddleware, async (req, res) => { -// try { -// // Access the authenticated user from the request object -// const authenticatedUser = req.user; - -// // You can now use the authenticatedUser to fetch user-specific content from the database -// // For simplicity, let's just return a success message -// res.json({ message: `Hello, ${authenticatedUser.username}! Welcome to the secret route.` }); -// } catch (error) { -// // Handle any errors that may occur -// res.status(500).json({ error: 'Internal Server Error' }); -// } -// }); - -// // Export the router for use in other files -// export default router; - - -// Explanation: - -// The '/secret' route is defined with a router.get method, indicating that it responds to HTTP GET requests. -// authenticateUser is used as middleware for this route. It ensures that only authenticated users can access this route. You can place this middleware on any route that requires authentication. -// Inside the route handler, you can access the authenticated user through req.user. This is possible because the authenticateUser middleware attaches the user to the request object. \ No newline at end of file diff --git a/backend/routes/userRoutes.js b/backend/routes/userRoutes.js new file mode 100644 index 000000000..36cb4bd98 --- /dev/null +++ b/backend/routes/userRoutes.js @@ -0,0 +1,20 @@ +// Import the necessary modules and functions +import express from "express"; +import { + registerUserController, + loginUserController, +} from "../controllers/userController"; // Import controller functions for user registration and login + +// Create an instance of the Express router +const router = express.Router(); + +// REGISTER ROUTE: Handle user registration +router.post("/register", registerUserController); // When a POST request is made to /register, execute the registerUserController function + +// LOGIN ROUTE: Handle user login +router.post("/login", loginUserController); // When a POST request is made to /login, execute the loginUserController function + +// Export the router for use in the main application +export default router; + +// In summary, this file sets up routes using the Express router for user registration and login operations. It associates each route with the corresponding controller function. These routes define the API endpoints for handling user registration and login within the application. diff --git a/backend/server.js b/backend/server.js index bde4ed96c..c97606a62 100644 --- a/backend/server.js +++ b/backend/server.js @@ -1,45 +1,108 @@ -// server.js -import express from 'express'; -import cors from 'cors'; -import mongoose from 'mongoose'; -import authUserMiddleware from './middleware/authMiddleware'; -// import userRoutes from './models/userModel'; -import authUserRoutes from './routes/authUserRoutes'; -import secretRoute from './routes/secretRoutes'; // Add this line -import listEndpoints from 'express-list-endpoints'; -import dotenv from 'dotenv'; -dotenv.config(); - -const mongoUrl = process.env.MONGO_URI || "mongodb://localhost/project-mongo"; -mongoose.connect(mongoUrl, { useNewUrlParser: true, useUnifiedTopology: true }); -mongoose.Promise = Promise; - -const port = process.env.PORT || 8080; -const app = express(); -app.use(cors()); -app.use(express.json()); - -app.use((req, res, next) => { - console.log(`Received ${req.method} request at ${req.url}`); - next(); +// Import necessary libraries and modules +import express from "express"; // Import the Express.js framework +import cors from "cors"; // Import the CORS middleware +import dotenv from "dotenv"; // Import dotenv for environment variables +import expressListEndpoints from "express-list-endpoints"; +dotenv.config(); // Load environment variables from the .env file +import userRoutes from "./routes/userRoutes"; // Import custom user routes +// import contactRoutes from "./routes/contactRoutes" +// import mediaRoutes from "./routes/mediaRoutes" +import { connectDB } from "./config/db"; // Import database connection function (not used here) + +// Defines the port the app will run on. Defaults to 8080, but can be overridden +const port = process.env.PORT; // Set the port number for the server +const app = express(); // Create an instance of the Express application + +// Add middlewares to enable cors and json body parsing +app.use(cors()); // Enable CORS (Cross-Origin Resource Sharing) +app.use(express.json()); // Parse incoming JSON data +app.use(express.urlencoded({ extended: false })); // Parse URL-encoded data + +// Use the routes for handling API requests +// ROUTES - These routes USE controller functions ;) +// Use the task-controlled routes for task-related requests +app.use(userRoutes); // Use the user-controlled routes for user-related requests + +//KANSKE SKA ANVÄNDA DOM HÄR +// app.use(contactRoutes); //for the contactform +// app.use(mediaRoutes); // for the images/film + + +// Connection to the database through Mongoose +connectDB(); +// Create a dedicated endpoint to view endpoints in the browser +app.get("/", (req, res) => { + const endpoints = expressListEndpoints(app); + res.json(endpoints); + console.log("List of Endpoints:"); + console.log(endpoints); }); -app.use('/auth', authUserMiddleware); -app.use('/user', authUserRoutes); -app.use('/secret', secretRoute); // Add this line - -app.get("/", (req, res) => { - res.json(listEndpoints(app)); +app.use((err, req, res, next) => { + res.status(500).send(err); }); +// Start the server and listen for incoming requests on the specified port app.listen(port, () => { - console.log(`Server running on http://localhost:${port}`); + console.log(`Server running on http://localhost:${port}`); // Display a message when the server is successfully started }); + + + + + + + +// // server.js +// import express from 'express'; +// import cors from 'cors'; +// import mongoose from 'mongoose'; +// import authUserMiddleware from './middleware/authMiddleware'; +// // import userRoutes from './models/userModel'; +// import authUserRoutes from './routes/authUserRoutes'; +// import secretRoute from './routes/secretRoutes'; // Add this line +// import listEndpoints from 'express-list-endpoints'; +// import dotenv from 'dotenv'; +// dotenv.config(); + +// console.log('JWT_SECRET:', process.env.JWT_SECRET); + + +// const mongoUrl = process.env.MONGO_URI || "mongodb://localhost/project-mongo"; +// mongoose.connect(mongoUrl, { useNewUrlParser: true, useUnifiedTopology: true }); +// mongoose.Promise = Promise; + +// const port = process.env.PORT || 8080; +// const app = express(); + +// app.use(cors()); +// app.use(express.json()); + +// app.use((req, res, next) => { +// console.log(`Received ${req.method} request at ${req.url}`); +// next(); +// }); + +// app.use('/auth', authUserMiddleware); +// app.use('/user', authUserRoutes); +// app.use('/secret', secretRoute); + +// app.get("/", (req, res) => { +// res.json(listEndpoints(app)); +// }); + +// app.listen(port, () => { +// console.log(`Server running on http://localhost:${port}`); +// }); + + + + // server.js // import express from 'express'; // import cors from 'cors'; From ec6b753c8fe301ce59954ecadfaf2e9050271ba1 Mon Sep 17 00:00:00 2001 From: EvelynDelCarmen Date: Wed, 3 Apr 2024 21:14:30 +0200 Subject: [PATCH 28/46] changed the backend logic --- backend/config/db.js | 2 ++ 1 file changed, 2 insertions(+) diff --git a/backend/config/db.js b/backend/config/db.js index 2854bd4e8..ded83faa7 100644 --- a/backend/config/db.js +++ b/backend/config/db.js @@ -1,6 +1,8 @@ // Import the 'mongoose' library to work with MongoDB import mongoose from "mongoose"; +//det här ska inte vara här + // Import the 'dotenv' library to load environment variables from a .env file import dotenv from "dotenv"; From 8824bc64485909e5ec5ace1e9a1be2134a869949 Mon Sep 17 00:00:00 2001 From: EvelynDelCarmen Date: Wed, 3 Apr 2024 21:59:29 +0200 Subject: [PATCH 29/46] changed frontend --- frontend/src/App.jsx | 26 ++-- frontend/src/components/Dashboard.jsx | 58 -------- frontend/src/components/Header.jsx | 24 ++++ frontend/src/components/LogIn.jsx | 74 ----------- frontend/src/components/Register.jsx | 105 --------------- .../src/components/reusecomponents/Button.jsx | 14 ++ frontend/src/index.css | 4 +- frontend/src/main.jsx | 2 +- frontend/src/pages/Home.jsx | 12 ++ frontend/src/pages/Login.jsx | 98 ++++++++++++++ frontend/src/pages/NotFound.jsx | 8 ++ frontend/src/routes/routes.jsx | 26 ++++ frontend/src/store/authStore.jsx | 11 -- frontend/src/store/userStore.jsx | 125 ++++++++++++++++++ frontend/styles/tailwind.css | 7 + 15 files changed, 328 insertions(+), 266 deletions(-) delete mode 100644 frontend/src/components/Dashboard.jsx create mode 100644 frontend/src/components/Header.jsx delete mode 100644 frontend/src/components/LogIn.jsx delete mode 100644 frontend/src/components/Register.jsx create mode 100644 frontend/src/components/reusecomponents/Button.jsx create mode 100644 frontend/src/pages/Home.jsx create mode 100644 frontend/src/pages/Login.jsx create mode 100644 frontend/src/pages/NotFound.jsx create mode 100644 frontend/src/routes/routes.jsx delete mode 100644 frontend/src/store/authStore.jsx create mode 100644 frontend/src/store/userStore.jsx create mode 100644 frontend/styles/tailwind.css diff --git a/frontend/src/App.jsx b/frontend/src/App.jsx index 923776bb6..f81f97169 100644 --- a/frontend/src/App.jsx +++ b/frontend/src/App.jsx @@ -1,24 +1,18 @@ -// App.jsx -import "react"; -import { BrowserRouter, Route, Routes } from 'react-router-dom'; -import Register from './components/Register'; -import LogIn from './components/LogIn'; -import Dashboard from './components/Dashboard'; +import { BrowserRouter, Routes } from "react-router-dom"; +import routes from "./routes/routes"; +import "./index.css"; - -const App = () => { +export const App = () => { return ( -
+ <> - - } /> - } /> - } /> - +
+ {/* {routes} */} + {routes} +
-
+ ); }; -export default App; diff --git a/frontend/src/components/Dashboard.jsx b/frontend/src/components/Dashboard.jsx deleted file mode 100644 index 29257449a..000000000 --- a/frontend/src/components/Dashboard.jsx +++ /dev/null @@ -1,58 +0,0 @@ -// src/components/Dashboard.js -import { authStore } from "../store/authStore"; -import { useEffect, useState } from "react"; -import { useNavigate } from "react-router-dom"; - - -const Dashboard = () => { - const [content, setContent] = useState(""); - const accessToken = authStore((state) => state.accessToken); - const logout = authStore((state) => state.logout); - const navigate = useNavigate(); - - useEffect(() => { - if (!accessToken) { - // Redirect to the sign-in page using useNavigate - navigate("/login"); - } else { - fetch(`${import.meta.env.VITE_API_URL}/user/protected`, { - method: "GET", - headers: { - Authorization: `Bearer ${accessToken}`, - }, - }) - .then((response) => { - if (response.ok) { - return response.json(); - } else { - throw new Error("Failed to fetch content"); - } - }) - .then((data) => { - setContent(data.message); - }) - .catch((error) => { - console.error("Error fetching content:", error); - setContent("Error fetching content"); - }); - } - }, [accessToken, navigate]); - - const handleSignOut = () => { - // Use the logout action from the store - logout(); - navigate("/login"); // Redirect to the sign-in page - }; - - return ( -
-

Dashboard

- -
{content}
-
- ); -}; - -export default Dashboard; - - diff --git a/frontend/src/components/Header.jsx b/frontend/src/components/Header.jsx new file mode 100644 index 000000000..2c6e49f8e --- /dev/null +++ b/frontend/src/components/Header.jsx @@ -0,0 +1,24 @@ +import 'react'; +import Button from './reusecomponents/Button'; + +const Header = () => { + return ( + // Added flex and flex-col for mobile layout, md:flex-row for desktop layout +
+ {/* Added md:order-2 to move h1 to the middle on desktop */} +

hello world

+ + {/* Wrapped buttons in a div for better control */} +
+
+ + {/* Additional div for spacing on desktop */} +
+
+ ); +}; + +export default Header; \ No newline at end of file diff --git a/frontend/src/components/LogIn.jsx b/frontend/src/components/LogIn.jsx deleted file mode 100644 index 486c6639f..000000000 --- a/frontend/src/components/LogIn.jsx +++ /dev/null @@ -1,74 +0,0 @@ -// src/components/SignIn.js -import { useState } from "react"; -import { useNavigate } from "react-router-dom"; -import { authStore } from "../store/authStore"; - -const LogIn = () => { - // State to store form data - const [formData, setFormData] = useState({ username: "", password: "" }); - // Access the history object to navigate between pages - const navigate = useNavigate(); - const { login } = authStore(); - - // Handle form submission - const handleSubmit = async (e) => { - e.preventDefault(); - try { - // Make a POST request to the login endpoint - const response = await fetch(`${import.meta.env.VITE_API_URL}/auth/protected`, { - method: "POST", - headers: { - "Content-Type": "application/json", - }, - body: JSON.stringify(formData), - }); - - if (response.ok) { - const data = await response.json(); - login(data.accessToken); // Use the login action from the store - navigate("/dashboard"); - } else { - const errorData = await response.json(); - console.error("Login error:", errorData.error); - // Handle login error - } - } catch (error) { - console.error("Unexpected error during login:", error); - // Handle unexpected error - } - }; - - return ( -
-

Log In

-
- {/* Sign-in form fields */} - - - setFormData({ ...formData, username: e.target.value }) - } - /> - - - - setFormData({ ...formData, password: e.target.value }) - } - /> - - {/* Submit button */} - -
-
- ); -}; - -export default LogIn; - diff --git a/frontend/src/components/Register.jsx b/frontend/src/components/Register.jsx deleted file mode 100644 index 9e841bc56..000000000 --- a/frontend/src/components/Register.jsx +++ /dev/null @@ -1,105 +0,0 @@ -// src/components/Register.js -import { authStore } from "../store/authStore"; -import { useState } from "react"; -import { useNavigate } from "react-router-dom"; - - -const Register = () => { - // State to store form data - // const [formData, setFormData] = useState({ - // username: "", - // email: "", - // password: "", - // }); - - const [username, setUsername] = useState(""); - const [email, setEmail] = useState(""); - const [password, setPassword] = useState(""); - // Access the history object to navigate between pages - const navigate = useNavigate(); - const { login } = authStore(); - - // Handle form submission - const handleSubmit = async () => { - if (!username || !password || !email) { - // Display an alert if any of the required fields are empty. - alert("Please enter email, username, and password"); - return; - } - //e.preventDefault(); - console.log(username, email, password); - - try { - // Make a POST request to the registration endpoint - const response = await fetch(`${import.meta.env.VITE_API_URL}/user/register`, { - method: "POST", - headers: { - "Content-Type": "application/json", - }, - body: JSON.stringify({ email, username, password }), - }); - - // Check if the request was successful (status code 2xx) - const data = await response.json(); - console.log(data); - if (data.success) { - // Parse the response JSON to get the access token - // login(data.accessToken); // Use the login action from the store - //navigate("/dashboard"); - - // If registration is successful, update the accessToken using the login function from the store - login(data.accessToken); - // Optionally, navigate to the dashboard or another page - navigate("/dashboard"); - } else { - // Handle registration error (status code is not 2xx) - // const errorData = await response.json(); - //console.error("Registration error:", errorData.error); - // Display an error message to the user, e.g., set a state variable for displaying an error message - console.error("Registration error:", data.error); - } - } catch (error) { - // Handle network errors or other unexpected errors - console.error("Unexpected error during registration:", error); - // Display a generic error message to the user - } - }; - - return ( -
-

Register

-
- {/* Registration form fields */} - - setUsername(e.target.value)} - /> - - - setEmail(e.target.value)} - /> - - - setPassword(e.target.value)} - /> - - {/* Submit button */} - -
-
- ); -}; - -export default Register; - diff --git a/frontend/src/components/reusecomponents/Button.jsx b/frontend/src/components/reusecomponents/Button.jsx new file mode 100644 index 000000000..d5c9f369e --- /dev/null +++ b/frontend/src/components/reusecomponents/Button.jsx @@ -0,0 +1,14 @@ +import 'react'; +import { Link } from 'react-router-dom'; + +const Button = ({ to, text, className }) => { + return ( + + + + ); +}; + +export default Button; \ No newline at end of file diff --git a/frontend/src/index.css b/frontend/src/index.css index 3e560a674..01fa24f9f 100644 --- a/frontend/src/index.css +++ b/frontend/src/index.css @@ -10,4 +10,6 @@ code { font-family: source-code-pro, Menlo, Monaco, Consolas, "Courier New", monospace; -} \ No newline at end of file +} + +@import './styles/tailwind.css'; \ No newline at end of file diff --git a/frontend/src/main.jsx b/frontend/src/main.jsx index a9c89f187..1f685e6b1 100644 --- a/frontend/src/main.jsx +++ b/frontend/src/main.jsx @@ -1,6 +1,6 @@ import React from "react"; import ReactDOM from "react-dom/client"; -import App from "./App.jsx"; +import { App } from "./App.jsx"; import "./index.css"; ReactDOM.createRoot(document.getElementById("root")).render( diff --git a/frontend/src/pages/Home.jsx b/frontend/src/pages/Home.jsx new file mode 100644 index 000000000..5ba09884e --- /dev/null +++ b/frontend/src/pages/Home.jsx @@ -0,0 +1,12 @@ +// pages/Home.jsx +import 'react'; +// import ImageGallery from '../components/ImageGallery'; +import Header from '../components/Header'; + +const Home = () => { + return ( +
+ ); +}; + +export default Home; \ No newline at end of file diff --git a/frontend/src/pages/Login.jsx b/frontend/src/pages/Login.jsx new file mode 100644 index 000000000..86ee41205 --- /dev/null +++ b/frontend/src/pages/Login.jsx @@ -0,0 +1,98 @@ +// Import the 'Logos' component and the 'Link' component from 'react-router-dom'. +import Logos from "../components/Logos"; +import { Link } from "react-router-dom"; +// Import the 'userStore' from the 'userStore' module. +import { userStore } from "../stores/userStore"; // Make sure this is correctly imported +// Import the 'useState' and 'useNavigate' hooks from 'react'. +import { useState } from "react"; +import { useNavigate } from "react-router-dom"; // Import useNavigate + +// Define the 'Login' functional component. +export const Login = () => { + // Create state variables for 'username' and 'password' using 'useState'. + const [username, setUsername] = useState(""); + const [password, setPassword] = useState(""); + // Use the 'useNavigate' hook to programmatically navigate between routes. + const navigate = useNavigate(); + + // Access the 'handleLogin' function from the 'userStore'. + const storeHandleLogin = userStore((state) => state.handleLogin); + + // Function to handle the click event of the login button. + const onLoginClick = async () => { + if (!username || !password) { + // Display an alert if either 'username' or 'password' is empty. + alert("Please enter both username and password"); + return; + } + try { + // Call the 'handleLogin' function from 'userStore' with 'username' and 'password' parameters. + await storeHandleLogin(username, password); + // Get the 'isLoggedIn' state from 'userStore'. + const isLoggedIn = userStore.getState().isLoggedIn; + if (isLoggedIn) { + // If the user is logged in, navigate to the "/home" route. + navigate("/home"); + } + // Additional logic after successful login can be added here. + } catch (error) { + // Handle any errors that occur during login and display an alert. + console.error("Login error:", error); + alert("An error occurred during login"); + } + }; + + // Text content for the heading and paragraphs. + const text = { + heading: "Login Page", + intro: "login here...", + loremIpsum: + "Lorem ipsum dolor sit amet consectetur adipisicing elit. Inventore, vitae fugit ipsam quo accusantium autem officia necessitatibus ullam voluptati", + }; + + // Render the component content. + return ( + <> + + {/* Render the 'Logos' component. */} + +
+ {/* Display the heading and paragraphs. */} +

{text.heading}

+

{text.intro}

+

{text.loremIpsum}

+
+ {/* Create input fields for 'username' and 'password' and associate them with state variables. */} + setUsername(e.target.value)} + /> + setPassword(e.target.value)} + /> + {/* Create a button for logging in and attach the 'onLoginClick' event handler. */} + +
+
+ + ); +}; + +// SUMMARY + +// This code defines the Login component, which handles user login functionality. It imports necessary components, hooks, and the user store, and it defines state variables for username and password. The component also provides a form for entering login credentials, handles the login button click event, and uses React Router to navigate between login and sign-up routes. Additionally, it renders text content and the 'Logos' component. diff --git a/frontend/src/pages/NotFound.jsx b/frontend/src/pages/NotFound.jsx new file mode 100644 index 000000000..9cf374e32 --- /dev/null +++ b/frontend/src/pages/NotFound.jsx @@ -0,0 +1,8 @@ +// Import the 'React' library. +import "react"; + +// Define the 'NotFound' functional component. +export const NotFound = () => { + // Render a div element with a CSS class 'not-found' containing the text 'NotFound'. + return
NotFound
; +}; \ No newline at end of file diff --git a/frontend/src/routes/routes.jsx b/frontend/src/routes/routes.jsx new file mode 100644 index 000000000..1ba6e31da --- /dev/null +++ b/frontend/src/routes/routes.jsx @@ -0,0 +1,26 @@ +// Import the 'Route' component from the 'react-router-dom' library. +import { Route } from "react-router-dom"; +// Import various page components used as route elements. +import Home from "../pages/Home"; +// import Contact from "../pages/Contact" +// import About from "../pages/About" +import { NotFound } from "../pages/NotFound"; + +// Define the 'routes' variable as a JSX expression. +const routes = ( + <> + + + } /> + {/* } /> + } /> */} + } /> + +); + +// Export the 'routes' variable as the default export of this module. +export default routes; + +// SUMMARY + +// This file sets up routing for a React application using React Router. It imports the necessary components and defines routes for different URL paths, associating each path with a specific component to be rendered when that path is accessed. The catch-all route with the path "*" is used to handle routes that do not match any of the specified paths, rendering the "NotFound" component in such cases. diff --git a/frontend/src/store/authStore.jsx b/frontend/src/store/authStore.jsx deleted file mode 100644 index 8c562dd22..000000000 --- a/frontend/src/store/authStore.jsx +++ /dev/null @@ -1,11 +0,0 @@ -// authStore.js -import { create } from 'zustand'; - -export const authStore = create((set, get) => ({ - accessToken: localStorage.getItem('accessToken') || null, - login: (token) => set({ accessToken: token }), - logout: () => set({ accessToken: null }), -})); - - - diff --git a/frontend/src/store/userStore.jsx b/frontend/src/store/userStore.jsx new file mode 100644 index 000000000..335b26fb1 --- /dev/null +++ b/frontend/src/store/userStore.jsx @@ -0,0 +1,125 @@ +// Import the 'create' function from the 'zustand' library. +import { create } from "zustand"; + +// Get the backend API endpoint from the environment variables. +const apiEnv = import.meta.env.VITE_API_URL; + +// Create a Zustand store for user-related state and actions. +export const userStore = create((set, get) => ({ + // Initialize username state. + username: "", + // Define a function to set the username state. + setUsername: (username) => set({ username }), + + // Initialize email state. + email: "", + // Define a function to set the email state. + setEmail: (email) => set({ email }), + + // Initialize password state. + password: "", + // Define a function to set the password state. + setPassword: (password) => set({ password }), + + // Initialize accessToken state with null. + accessToken: null, + // Define a function to set the accessToken state. + setAccessToken: (token) => set({ accessToken: token }), + + // Initialize isLoggedIn state with false. + isLoggedIn: false, + // Define a function to set the isLoggedIn state. + setIsLoggedIn: (isLoggedIn) => set({ isLoggedIn }), + + // FUNCTION TO REGISTER USERS + handleSignup: async (username, password, email) => { + // Check if required fields are provided and display an alert if not. + if (!username || !password || !email) { + alert("Please enter username, email, and password"); + return; + } + + try { + // Send a POST request to the registration endpoint with user data. + const response = await fetch(`${apiEnv}/register`, { + method: "POST", + headers: { + "Content-Type": "application/json", + }, + body: JSON.stringify({ email, username, password }), + }); + + // Parse the response data as JSON. + const data = await response.json(); + if (data.success) { + // Update the username state. + set({ username }); + // Display a success alert. + alert("Signup successful!"); + console.log("Signing up with:", username); + } else { + // Display an error message from the server or a generic message. + alert(data.response || "Signup failed"); + } + } catch (error) { + // Handle and log any signup errors. + console.error("Signup error:", error); + alert("An error occurred during signup"); + } + }, + + // LOGIN + handleLogin: async (username, password) => { + // Check if both username and password are provided and display an alert if not. + if (!username || !password) { + alert("Please enter both username and password"); + return; + } + + try { + // Send a POST request to the login endpoint with user data. + const response = await fetch(`${apiEnv}/login`, { + method: "POST", + headers: { + "Content-Type": "application/json", + }, + body: JSON.stringify({ username, password }), + }); + + // Parse the response data as JSON. + const data = await response.json(); + if (data.success) { + // Update the state with username, accessToken, and set isLoggedIn to true. + set({ + username, + accessToken: data.response.accessToken, + isLoggedIn: true, + }); + // Store the accessToken in the browser's localStorage. + localStorage.setItem("accessToken", data.response.accessToken); + // Display a success alert. + alert("Login successful!"); + console.log("Logging in with:", username, password); + } else { + // Display an error message from the server or a generic message. + alert(data.response || "Login failed"); + } + } catch (error) { + // Handle and log any login errors. + console.error("Login error:", error); + alert("An error occurred during login"); + } + }, + + // Function to handle user logout. + handleLogout: () => { + // Clear user information and set isLoggedIn to false. + set({ username: "", accessToken: null, isLoggedIn: false }); + // Remove the accessToken from localStorage. + localStorage.removeItem("accessToken"); + // Additional logout logic can be added here if needed. + }, +})); + +// SUMMARY +// This file serves as the core of a React application's user authentication and state management system. It utilizes the Zustand library to create a centralized store that handles user-related data and actions. The store includes state variables such as username, email, password, accessToken, and isLoggedIn, each with corresponding functions to modify their values. The handleSignup function allows users to register by sending their information to a server-side registration endpoint, displaying alerts for success or failure. Similarly, the handleLogin function facilitates user login, updating the state with the user's credentials and access token upon success, and storing the token in the browser's local storage. Additionally, it handles the user's logout by clearing user information and local storage data. Overall, this file provides a robust framework for user authentication and state management in the React application, enhancing user registration, login, and logout processes. diff --git a/frontend/styles/tailwind.css b/frontend/styles/tailwind.css new file mode 100644 index 000000000..7805ee89c --- /dev/null +++ b/frontend/styles/tailwind.css @@ -0,0 +1,7 @@ +/* src/styles/tailwind.css */ +@import 'tailwindcss/base'; +@import 'tailwindcss/components'; +@import 'tailwindcss/utilities'; + + +@import url('https://fonts.googleapis.com/css2?family=Archivo+Black&display=swap'); \ No newline at end of file From 75be41571f167e7870f139d38f6e49448b2b102c Mon Sep 17 00:00:00 2001 From: EvelynDelCarmen Date: Wed, 3 Apr 2024 22:00:58 +0200 Subject: [PATCH 30/46] changed the logic --- backend/config/db.js | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/backend/config/db.js b/backend/config/db.js index ded83faa7..52f2f0937 100644 --- a/backend/config/db.js +++ b/backend/config/db.js @@ -1,7 +1,7 @@ // Import the 'mongoose' library to work with MongoDB import mongoose from "mongoose"; -//det här ska inte vara här +//testar // Import the 'dotenv' library to load environment variables from a .env file import dotenv from "dotenv"; From 3797bb7bf0f6223b963b384c62c90e3efb86b963 Mon Sep 17 00:00:00 2001 From: EvelynDelCarmen Date: Thu, 4 Apr 2024 17:14:53 +0200 Subject: [PATCH 31/46] backend logic --- backend/controllers/userController.js | 1 + backend/server.js | 10 +++++++++- 2 files changed, 10 insertions(+), 1 deletion(-) diff --git a/backend/controllers/userController.js b/backend/controllers/userController.js index c0fdd95ce..9381ea81e 100644 --- a/backend/controllers/userController.js +++ b/backend/controllers/userController.js @@ -58,6 +58,7 @@ export const registerUserController = asyncHandler(async (req, res) => { // Description: Save the new user instance to the database await newUser.save(); + // Respond with a success message, user details, and the JWT token res.status(201).json({ success: true, diff --git a/backend/server.js b/backend/server.js index c97606a62..8c910ba8b 100644 --- a/backend/server.js +++ b/backend/server.js @@ -39,8 +39,16 @@ app.get("/", (req, res) => { console.log(endpoints); }); +// app.use((err, req, res, next) => { +// res.status(500).send(err); +// }); + app.use((err, req, res, next) => { - res.status(500).send(err); + const statusCode = err.statusCode || 500; + res.status(statusCode).json({ + message: err.message, + stack: process.env.NODE_ENV === 'production' ? '🥞' : err.stack, + }); }); // Start the server and listen for incoming requests on the specified port From ca079048fdfbf623e6d84347a7a34efa006d59a2 Mon Sep 17 00:00:00 2001 From: EvelynDelCarmen Date: Thu, 4 Apr 2024 18:08:41 +0200 Subject: [PATCH 32/46] changed the logic and path to userModel.js --- backend/controllers/userController.js | 2 +- backend/middleware/authenticateUser.js | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/backend/controllers/userController.js b/backend/controllers/userController.js index 9381ea81e..2accfbbaa 100644 --- a/backend/controllers/userController.js +++ b/backend/controllers/userController.js @@ -1,4 +1,4 @@ -import { UserModel } from "../models/UserModel"; +import { UserModel } from "../models/userModel"; //asyncHandler: We use asyncHandler to simplify error handling in asynchronous code. It helps us avoid writing repetitive try-catch blocks by automatically catching errors and passing them to our error handling middleware. This makes our code cleaner and more readable, reducing the risk of unhandled exceptions that could crash the server. import asyncHandler from "express-async-handler"; // bcrypt: We use bcrypt to securely hash and store passwords in our database. Storing plain-text passwords is a security risk, as it exposes user credentials in case of a data breach. bcrypt helps us hash passwords in a way that is computationally expensive and time-consuming for potential attackers, making it difficult to crack passwords even if the database is compromised. It enhances the overall security of user authentication in our application. diff --git a/backend/middleware/authenticateUser.js b/backend/middleware/authenticateUser.js index 280527586..d4bf23ca8 100644 --- a/backend/middleware/authenticateUser.js +++ b/backend/middleware/authenticateUser.js @@ -1,5 +1,5 @@ // Import the UserModel from the User model file -import { UserModel } from "../models/UserModel"; +import { UserModel } from "../models/userModel"; // Define a function called authenticateUser that takes a request (req), response (res), and a next function as parameters export const authenticateUser = async (req, res, next) => { // Retrieve the access token from the request header From efb46275c31b9da1e6c73b5b9b3c9e0ae6c08cd2 Mon Sep 17 00:00:00 2001 From: EvelynDelCarmen Date: Thu, 4 Apr 2024 22:14:59 +0200 Subject: [PATCH 33/46] ready to deploy --- frontend/src/App.jsx | 24 +- frontend/src/components/Header.jsx | 24 -- .../src/components/reusecomponents/Button.jsx | 24 +- frontend/src/index.css | 14 +- frontend/src/pages/Home.jsx | 52 ++- frontend/src/pages/Login.jsx | 344 ++++++++++++++---- frontend/src/routes/routes.jsx | 26 -- frontend/src/store/userStore.jsx | 3 + 8 files changed, 360 insertions(+), 151 deletions(-) delete mode 100644 frontend/src/components/Header.jsx delete mode 100644 frontend/src/routes/routes.jsx diff --git a/frontend/src/App.jsx b/frontend/src/App.jsx index f81f97169..abb4922e7 100644 --- a/frontend/src/App.jsx +++ b/frontend/src/App.jsx @@ -1,18 +1,20 @@ -import { BrowserRouter, Routes } from "react-router-dom"; -import routes from "./routes/routes"; -import "./index.css"; +import { BrowserRouter, Routes, Route } from "react-router-dom"; +import { Login } from "./pages/Login"; // Adjust the import path as needed +import { NotFound } from "./pages/NotFound"; // Adjust the import path as needed +import { Home } from "./pages/Home"; +// Import other components as needed export const App = () => { return ( - <> - -
- {/* {routes} */} - {routes} -
-
- + + + } /> + } /> + } /> + + ); }; + diff --git a/frontend/src/components/Header.jsx b/frontend/src/components/Header.jsx deleted file mode 100644 index 2c6e49f8e..000000000 --- a/frontend/src/components/Header.jsx +++ /dev/null @@ -1,24 +0,0 @@ -import 'react'; -import Button from './reusecomponents/Button'; - -const Header = () => { - return ( - // Added flex and flex-col for mobile layout, md:flex-row for desktop layout -
- {/* Added md:order-2 to move h1 to the middle on desktop */} -

hello world

- - {/* Wrapped buttons in a div for better control */} -
-
- - {/* Additional div for spacing on desktop */} -
-
- ); -}; - -export default Header; \ No newline at end of file diff --git a/frontend/src/components/reusecomponents/Button.jsx b/frontend/src/components/reusecomponents/Button.jsx index d5c9f369e..d9625a0b7 100644 --- a/frontend/src/components/reusecomponents/Button.jsx +++ b/frontend/src/components/reusecomponents/Button.jsx @@ -1,14 +1,14 @@ -import 'react'; -import { Link } from 'react-router-dom'; +// import 'react'; +// import { Link } from 'react-router-dom'; -const Button = ({ to, text, className }) => { - return ( - - - - ); -}; +// const Button = ({ to, text, className }) => { +// return ( +// +// +// +// ); +// }; -export default Button; \ No newline at end of file +// export default Button; \ No newline at end of file diff --git a/frontend/src/index.css b/frontend/src/index.css index 01fa24f9f..5962ca5cf 100644 --- a/frontend/src/index.css +++ b/frontend/src/index.css @@ -1,5 +1,13 @@ -:root { +@import './styles/tailwind.css'; + +*, +*::before, +*::after { margin: 0; + padding: 0; +} + +body { font-family: -apple-system, BlinkMacSystemFont, "Segoe UI", "Roboto", "Oxygen", "Ubuntu", "Cantarell", "Fira Sans", "Droid Sans", "Helvetica Neue", sans-serif; @@ -10,6 +18,4 @@ code { font-family: source-code-pro, Menlo, Monaco, Consolas, "Courier New", monospace; -} - -@import './styles/tailwind.css'; \ No newline at end of file +} \ No newline at end of file diff --git a/frontend/src/pages/Home.jsx b/frontend/src/pages/Home.jsx index 5ba09884e..0423d32be 100644 --- a/frontend/src/pages/Home.jsx +++ b/frontend/src/pages/Home.jsx @@ -1,12 +1,50 @@ -// pages/Home.jsx -import 'react'; -// import ImageGallery from '../components/ImageGallery'; -import Header from '../components/Header'; +import { useNavigate } from "react-router-dom"; +import { userStore } from "../store/userStore"; // Adjust path as needed + +export const Home = () => { + const navigate = useNavigate(); + const logout = userStore(state => state.handleLogout); + + const onSignOut = () => { + logout(); // This should clear the token and update the isLoggedIn state. + navigate('/'); // Redirect the user to the login page. + }; -const Home = () => { return ( -
+
+

Welcome Home!

+

This is a protected area of the app

+ +
); }; -export default Home; \ No newline at end of file + + + + + + + + + + + + +// pages/Home.jsx +// import 'react'; +// import ImageGallery from '../components/ImageGallery'; +// import Header from '../components/Header'; + +// const Home = () => { +// return ( +//
+// ); +// }; + +// export default Home; \ No newline at end of file diff --git a/frontend/src/pages/Login.jsx b/frontend/src/pages/Login.jsx index 86ee41205..391369c9a 100644 --- a/frontend/src/pages/Login.jsx +++ b/frontend/src/pages/Login.jsx @@ -1,98 +1,308 @@ -// Import the 'Logos' component and the 'Link' component from 'react-router-dom'. -import Logos from "../components/Logos"; + import { Link } from "react-router-dom"; -// Import the 'userStore' from the 'userStore' module. -import { userStore } from "../stores/userStore"; // Make sure this is correctly imported -// Import the 'useState' and 'useNavigate' hooks from 'react'. +import { userStore } from "../store/userStore"; // Adjust the import path as needed import { useState } from "react"; -import { useNavigate } from "react-router-dom"; // Import useNavigate +import { useNavigate } from "react-router-dom"; +// import Logos from "../components/Logos"; // Ensure this is correctly imported -// Define the 'Login' functional component. export const Login = () => { - // Create state variables for 'username' and 'password' using 'useState'. - const [username, setUsername] = useState(""); - const [password, setPassword] = useState(""); - // Use the 'useNavigate' hook to programmatically navigate between routes. + // State for login + const [loginUsername, setLoginUsername] = useState(""); + const [loginPassword, setLoginPassword] = useState(""); + // State for signup + const [signupUsername, setSignupUsername] = useState(""); + const [signupPassword, setSignupPassword] = useState(""); + const [signupEmail, setSignupEmail] = useState(""); + const navigate = useNavigate(); + const [message, setMessage] = useState(""); - // Access the 'handleLogin' function from the 'userStore'. - const storeHandleLogin = userStore((state) => state.handleLogin); - // Function to handle the click event of the login button. - const onLoginClick = async () => { - if (!username || !password) { - // Display an alert if either 'username' or 'password' is empty. - alert("Please enter both username and password"); - return; - } + const { handleLogin, handleSignup } = userStore((state) => ({ + handleLogin: state.handleLogin, + handleSignup: state.handleSignup, + })); + + // Login submission + const onLoginSubmit = async (e) => { + e.preventDefault(); try { - // Call the 'handleLogin' function from 'userStore' with 'username' and 'password' parameters. - await storeHandleLogin(username, password); - // Get the 'isLoggedIn' state from 'userStore'. - const isLoggedIn = userStore.getState().isLoggedIn; - if (isLoggedIn) { - // If the user is logged in, navigate to the "/home" route. - navigate("/home"); - } - // Additional logic after successful login can be added here. + const success = await handleLogin(loginUsername, loginPassword); + if (success) navigate("/home"); + else alert("Login failed."); } catch (error) { - // Handle any errors that occur during login and display an alert. console.error("Login error:", error); - alert("An error occurred during login"); + alert("An error occurred during login."); } }; - // Text content for the heading and paragraphs. - const text = { - heading: "Login Page", - intro: "login here...", - loremIpsum: - "Lorem ipsum dolor sit amet consectetur adipisicing elit. Inventore, vitae fugit ipsam quo accusantium autem officia necessitatibus ullam voluptati", + // Signup submission + const onSignupSubmit = async (e) => { + e.preventDefault(); + try { + await handleSignup(signupUsername, signupPassword, signupEmail); + alert("Signup successful. Please log in."); + // Optionally clear the form or navigate + } catch (error) { + console.error("Signup error:", error); + alert("An error occurred during signup."); + } }; - // Render the component content. return ( - <> - - {/* Render the 'Logos' component. */} - -
- {/* Display the heading and paragraphs. */} -

{text.heading}

-

{text.intro}

-

{text.loremIpsum}

-
- {/* Create input fields for 'username' and 'password' and associate them with state variables. */} +
+
+

+ Sign Up +

+
setUsername(e.target.value)} + value={signupUsername} + onChange={(e) => setSignupUsername(e.target.value)} + /> + setSignupEmail(e.target.value)} /> setPassword(e.target.value)} + value={signupPassword} + onChange={(e) => setSignupPassword(e.target.value)} /> - {/* Create a button for logging in and attach the 'onLoginClick' event handler. */} - -
+ + + +

+ Login +

+
+ setLoginUsername(e.target.value)} + /> + setLoginPassword(e.target.value)} + /> + +
- +
); }; + + + + + + +// export const Login = () => { +// const [username, setUsername] = useState(""); +// const [password, setPassword] = useState(""); +// const [email, setEmail] = useState(""); // Additional state for sign-up +// const [isSignUp, setIsSignUp] = useState(false); // Toggle between login and sign-up + +// const navigate = useNavigate(); + +// const storeHandleLogin = userStore((state) => state.handleLogin); +// const storeHandleSignup = userStore((state) => state.handleSignup); // Access the sign-up function + +// const onSubmit = async () => { +// if (!username || !password || (isSignUp && !email)) { +// alert("Please fill in all fields"); +// return; +// } + +// try { +// let isSuccess = false; +// if (isSignUp) { +// // Sign-up logic +// await storeHandleSignup(username, password, email); +// // Optionally, display a message or handle the UI state change to indicate successful signup. +// alert("Signup successful, please login."); +// isSuccess = true; +// } else { +// // Login logic +// const success = await storeHandleLogin(username, password); +// if (success) { // Assuming handleLogin resolves to true/false based on success +// navigate("/home"); // Navigate to /home only after successful login +// } else { +// alert("Login failed"); +// } +// } +// } catch (error) { +// console.error("Error:", error); +// alert(`An error occurred during ${isSignUp ? "sign up" : "login"}`); +// } +// }; + + +// return ( +// <> +// +// {/* */} +//
+//

{isSignUp ? "Sign Up" : "Login"} Page

+//
+// setUsername(e.target.value)} +// /> +// {isSignUp && ( +// setEmail(e.target.value)} +// /> +// )} +// setPassword(e.target.value)} +// /> +// +//
+//
+// +// ); +// }; + + + + + + + + + +// // Import the 'Logos' component and the 'Link' component from 'react-router-dom'. +// // import Logos from "../components/Logos"; +// import { Link } from "react-router-dom"; +// // Import the 'userStore' from the 'userStore' module. +// import { userStore } from "../store/userStore"; // Make sure this is correctly imported +// // Import the 'useState' and 'useNavigate' hooks from 'react'. +// import { useState } from "react"; +// import { useNavigate } from "react-router-dom"; // Import useNavigate + +// // Define the 'Login' functional component. +// export const Login = () => { +// // Create state variables for 'username' and 'password' using 'useState'. +// const [username, setUsername] = useState(""); +// const [password, setPassword] = useState(""); +// // Use the 'useNavigate' hook to programmatically navigate between routes. +// const navigate = useNavigate(); + +// // Access the 'handleLogin' function from the 'userStore'. +// const storeHandleLogin = userStore((state) => state.handleLogin); + +// // Function to handle the click event of the login button. +// const onLoginClick = async () => { +// if (!username || !password) { +// // Display an alert if either 'username' or 'password' is empty. +// alert("Please enter both username and password"); +// return; +// } +// try { +// // Call the 'handleLogin' function from 'userStore' with 'username' and 'password' parameters. +// await storeHandleLogin(username, password); +// // Get the 'isLoggedIn' state from 'userStore'. +// const isLoggedIn = userStore.getState().isLoggedIn; +// if (isLoggedIn) { +// // If the user is logged in, navigate to the "/home" route. +// navigate("/home"); +// } +// // Additional logic after successful login can be added here. +// } catch (error) { +// // Handle any errors that occur during login and display an alert. +// console.error("Login error:", error); +// alert("An error occurred during login"); +// } +// }; + +// // Text content for the heading and paragraphs. +// const text = { +// heading: "Login Page", +// intro: "login here...", +// loremIpsum: +// "Lorem ipsum dolor sit amet consectetur adipisicing elit. Inventore, vitae fugit ipsam quo accusantium autem officia necessitatibus ullam voluptati", +// }; + +// // Render the component content. +// return ( +// <> +// +// {/* Render the 'Logos' component. */} +// {/* */} +//
+// {/* Display the heading and paragraphs. */} +//

{text.heading}

+//

{text.intro}

+//

{text.loremIpsum}

+//
+// {/* Create input fields for 'username' and 'password' and associate them with state variables. */} +// setUsername(e.target.value)} +// /> +// setPassword(e.target.value)} +// /> +// {/* Create a button for logging in and attach the 'onLoginClick' event handler. */} +// +//
+//
+// +// ); +// }; + // SUMMARY // This code defines the Login component, which handles user login functionality. It imports necessary components, hooks, and the user store, and it defines state variables for username and password. The component also provides a form for entering login credentials, handles the login button click event, and uses React Router to navigate between login and sign-up routes. Additionally, it renders text content and the 'Logos' component. diff --git a/frontend/src/routes/routes.jsx b/frontend/src/routes/routes.jsx deleted file mode 100644 index 1ba6e31da..000000000 --- a/frontend/src/routes/routes.jsx +++ /dev/null @@ -1,26 +0,0 @@ -// Import the 'Route' component from the 'react-router-dom' library. -import { Route } from "react-router-dom"; -// Import various page components used as route elements. -import Home from "../pages/Home"; -// import Contact from "../pages/Contact" -// import About from "../pages/About" -import { NotFound } from "../pages/NotFound"; - -// Define the 'routes' variable as a JSX expression. -const routes = ( - <> - - - } /> - {/* } /> - } /> */} - } /> - -); - -// Export the 'routes' variable as the default export of this module. -export default routes; - -// SUMMARY - -// This file sets up routing for a React application using React Router. It imports the necessary components and defines routes for different URL paths, associating each path with a specific component to be rendered when that path is accessed. The catch-all route with the path "*" is used to handle routes that do not match any of the specified paths, rendering the "NotFound" component in such cases. diff --git a/frontend/src/store/userStore.jsx b/frontend/src/store/userStore.jsx index 335b26fb1..9474f3952 100644 --- a/frontend/src/store/userStore.jsx +++ b/frontend/src/store/userStore.jsx @@ -100,14 +100,17 @@ export const userStore = create((set, get) => ({ // Display a success alert. alert("Login successful!"); console.log("Logging in with:", username, password); + return true; // Indicate success } else { // Display an error message from the server or a generic message. alert(data.response || "Login failed"); + return false; // Indicate failure } } catch (error) { // Handle and log any login errors. console.error("Login error:", error); alert("An error occurred during login"); + return false; // Indicate failure } }, From 84e65e420571e5dcf60800875a575e4ff8ca1e09 Mon Sep 17 00:00:00 2001 From: EvelynDelCarmen Date: Thu, 4 Apr 2024 22:18:50 +0200 Subject: [PATCH 34/46] cleared all the comments, now ready --- frontend/src/App.jsx | 3 +- frontend/src/pages/Login.jsx | 185 ----------------------------------- 2 files changed, 2 insertions(+), 186 deletions(-) diff --git a/frontend/src/App.jsx b/frontend/src/App.jsx index abb4922e7..9968a4201 100644 --- a/frontend/src/App.jsx +++ b/frontend/src/App.jsx @@ -2,7 +2,8 @@ import { BrowserRouter, Routes, Route } from "react-router-dom"; import { Login } from "./pages/Login"; // Adjust the import path as needed import { NotFound } from "./pages/NotFound"; // Adjust the import path as needed import { Home } from "./pages/Home"; -// Import other components as needed +import "./index.css"; + export const App = () => { return ( diff --git a/frontend/src/pages/Login.jsx b/frontend/src/pages/Login.jsx index 391369c9a..120d283db 100644 --- a/frontend/src/pages/Login.jsx +++ b/frontend/src/pages/Login.jsx @@ -121,188 +121,3 @@ export const Login = () => { -// export const Login = () => { -// const [username, setUsername] = useState(""); -// const [password, setPassword] = useState(""); -// const [email, setEmail] = useState(""); // Additional state for sign-up -// const [isSignUp, setIsSignUp] = useState(false); // Toggle between login and sign-up - -// const navigate = useNavigate(); - -// const storeHandleLogin = userStore((state) => state.handleLogin); -// const storeHandleSignup = userStore((state) => state.handleSignup); // Access the sign-up function - -// const onSubmit = async () => { -// if (!username || !password || (isSignUp && !email)) { -// alert("Please fill in all fields"); -// return; -// } - -// try { -// let isSuccess = false; -// if (isSignUp) { -// // Sign-up logic -// await storeHandleSignup(username, password, email); -// // Optionally, display a message or handle the UI state change to indicate successful signup. -// alert("Signup successful, please login."); -// isSuccess = true; -// } else { -// // Login logic -// const success = await storeHandleLogin(username, password); -// if (success) { // Assuming handleLogin resolves to true/false based on success -// navigate("/home"); // Navigate to /home only after successful login -// } else { -// alert("Login failed"); -// } -// } -// } catch (error) { -// console.error("Error:", error); -// alert(`An error occurred during ${isSignUp ? "sign up" : "login"}`); -// } -// }; - - -// return ( -// <> -// -// {/* */} -//
-//

{isSignUp ? "Sign Up" : "Login"} Page

-//
-// setUsername(e.target.value)} -// /> -// {isSignUp && ( -// setEmail(e.target.value)} -// /> -// )} -// setPassword(e.target.value)} -// /> -// -//
-//
-// -// ); -// }; - - - - - - - - - -// // Import the 'Logos' component and the 'Link' component from 'react-router-dom'. -// // import Logos from "../components/Logos"; -// import { Link } from "react-router-dom"; -// // Import the 'userStore' from the 'userStore' module. -// import { userStore } from "../store/userStore"; // Make sure this is correctly imported -// // Import the 'useState' and 'useNavigate' hooks from 'react'. -// import { useState } from "react"; -// import { useNavigate } from "react-router-dom"; // Import useNavigate - -// // Define the 'Login' functional component. -// export const Login = () => { -// // Create state variables for 'username' and 'password' using 'useState'. -// const [username, setUsername] = useState(""); -// const [password, setPassword] = useState(""); -// // Use the 'useNavigate' hook to programmatically navigate between routes. -// const navigate = useNavigate(); - -// // Access the 'handleLogin' function from the 'userStore'. -// const storeHandleLogin = userStore((state) => state.handleLogin); - -// // Function to handle the click event of the login button. -// const onLoginClick = async () => { -// if (!username || !password) { -// // Display an alert if either 'username' or 'password' is empty. -// alert("Please enter both username and password"); -// return; -// } -// try { -// // Call the 'handleLogin' function from 'userStore' with 'username' and 'password' parameters. -// await storeHandleLogin(username, password); -// // Get the 'isLoggedIn' state from 'userStore'. -// const isLoggedIn = userStore.getState().isLoggedIn; -// if (isLoggedIn) { -// // If the user is logged in, navigate to the "/home" route. -// navigate("/home"); -// } -// // Additional logic after successful login can be added here. -// } catch (error) { -// // Handle any errors that occur during login and display an alert. -// console.error("Login error:", error); -// alert("An error occurred during login"); -// } -// }; - -// // Text content for the heading and paragraphs. -// const text = { -// heading: "Login Page", -// intro: "login here...", -// loremIpsum: -// "Lorem ipsum dolor sit amet consectetur adipisicing elit. Inventore, vitae fugit ipsam quo accusantium autem officia necessitatibus ullam voluptati", -// }; - -// // Render the component content. -// return ( -// <> -// -// {/* Render the 'Logos' component. */} -// {/* */} -//
-// {/* Display the heading and paragraphs. */} -//

{text.heading}

-//

{text.intro}

-//

{text.loremIpsum}

-//
-// {/* Create input fields for 'username' and 'password' and associate them with state variables. */} -// setUsername(e.target.value)} -// /> -// setPassword(e.target.value)} -// /> -// {/* Create a button for logging in and attach the 'onLoginClick' event handler. */} -// -//
-//
-// -// ); -// }; - -// SUMMARY - -// This code defines the Login component, which handles user login functionality. It imports necessary components, hooks, and the user store, and it defines state variables for username and password. The component also provides a form for entering login credentials, handles the login button click event, and uses React Router to navigate between login and sign-up routes. Additionally, it renders text content and the 'Logos' component. From 2abe88bed6249b22a18d22fff542214b6511d732 Mon Sep 17 00:00:00 2001 From: EvelynDelCarmen <141672105+EvelynDelCarmen@users.noreply.github.com> Date: Thu, 4 Apr 2024 23:28:03 +0200 Subject: [PATCH 35/46] Update README.md --- README.md | 9 +++++---- 1 file changed, 5 insertions(+), 4 deletions(-) diff --git a/README.md b/README.md index dfa05e177..021640e65 100644 --- a/README.md +++ b/README.md @@ -1,13 +1,14 @@ # Project Auth API -Replace this readme with your own information about your project. +A simple authentication API built with Node.js and Express. It allows users to sign up, log in, and access protected routes with a valid token. -Start by briefly describing the assignment in a sentence or two. Keep it short and to the point. ## The problem -Describe how you approached to problem, and what tools and techniques you used to solve it. How did you plan? What technologies did you use? If you had more time, what would be next? +I didnt understand the first codes that we were provided, I did everything a second time and used videos and lectures that I understood more of. If I had more time I would implement some sort of contact form in the /home directory and work more in the design. + ## View it live -Every project should be deployed somewhere. Be sure to include the link to the deployed project so that the viewer can click around and see what it's all about. +Frontend: https://auth-project-fullstack.netlify.app/ +Backend: https://project-auth-api-mnx9.onrender.com/ From 86db7217f6af4b1736e77a01e99f5c982810b097 Mon Sep 17 00:00:00 2001 From: EvelynDelCarmen <141672105+EvelynDelCarmen@users.noreply.github.com> Date: Thu, 4 Apr 2024 23:28:21 +0200 Subject: [PATCH 36/46] Update README.md --- README.md | 1 + 1 file changed, 1 insertion(+) diff --git a/README.md b/README.md index 021640e65..3f11a707a 100644 --- a/README.md +++ b/README.md @@ -11,4 +11,5 @@ I didnt understand the first codes that we were provided, I did everything a sec ## View it live Frontend: https://auth-project-fullstack.netlify.app/ + Backend: https://project-auth-api-mnx9.onrender.com/ From 528352f5a6a90275d34f8e9387b02b099ba015a2 Mon Sep 17 00:00:00 2001 From: EvelynDelCarmen <141672105+EvelynDelCarmen@users.noreply.github.com> Date: Thu, 4 Apr 2024 23:34:33 +0200 Subject: [PATCH 37/46] Update README.md --- README.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/README.md b/README.md index 3f11a707a..b10ddf7f8 100644 --- a/README.md +++ b/README.md @@ -1,6 +1,6 @@ # Project Auth API -A simple authentication API built with Node.js and Express. It allows users to sign up, log in, and access protected routes with a valid token. +A simple authentication API built with Node.js and Express. It allows users to sign up, log in, and access protected routes with a valid token. The frontend is made with Tailwind CSS. ## The problem From a299f1ad418267851bcee7acf1ef860d23d09c88 Mon Sep 17 00:00:00 2001 From: EvelynDelCarmen Date: Mon, 29 Apr 2024 16:35:25 +0200 Subject: [PATCH 38/46] added the frontend URL --- backend/config/db.js | 4 +- backend/server.js | 112 +++++-------------------------------------- 2 files changed, 12 insertions(+), 104 deletions(-) diff --git a/backend/config/db.js b/backend/config/db.js index 52f2f0937..b6a6acdaa 100644 --- a/backend/config/db.js +++ b/backend/config/db.js @@ -12,9 +12,7 @@ dotenv.config(); // Define an asynchronous function 'connectDB' to connect to the MongoDB database export const connectDB = async () => { try { - // Attempt to connect to the MongoDB database using the URL from the environment variables - // Mongoose Method: mongoose.connect() - // Description: This line of code serves the crucial purpose of connecting the Node.js application to the MongoDB database specified by the URL provided in the environment variable MONGO_URL. Once this connection is established, the application can perform various database operations, such as querying and modifying data in the MongoDB database. It's a critical step in setting up the database connection for the application to work with MongoDB. + const conn = await mongoose.connect(process.env.MONGO_URI); // If the connection is successful, log a message indicating that the MongoDB is connected diff --git a/backend/server.js b/backend/server.js index 8c910ba8b..024b8fbf4 100644 --- a/backend/server.js +++ b/backend/server.js @@ -1,27 +1,23 @@ -// Import necessary libraries and modules -import express from "express"; // Import the Express.js framework -import cors from "cors"; // Import the CORS middleware -import dotenv from "dotenv"; // Import dotenv for environment variables +import express from "express"; +import cors from "cors"; +import dotenv from "dotenv"; import expressListEndpoints from "express-list-endpoints"; -dotenv.config(); // Load environment variables from the .env file -import userRoutes from "./routes/userRoutes"; // Import custom user routes -// import contactRoutes from "./routes/contactRoutes" -// import mediaRoutes from "./routes/mediaRoutes" -import { connectDB } from "./config/db"; // Import database connection function (not used here) -// Defines the port the app will run on. Defaults to 8080, but can be overridden -const port = process.env.PORT; // Set the port number for the server -const app = express(); // Create an instance of the Express application +import userRoutes from "./routes/userRoutes"; +import { connectDB } from "./config/db"; + +dotenv.config(); +const port = process.env.PORT; +const app = express(); // Add middlewares to enable cors and json body parsing app.use(cors()); // Enable CORS (Cross-Origin Resource Sharing) app.use(express.json()); // Parse incoming JSON data app.use(express.urlencoded({ extended: false })); // Parse URL-encoded data -// Use the routes for handling API requests -// ROUTES - These routes USE controller functions ;) -// Use the task-controlled routes for task-related requests + + app.use(userRoutes); // Use the user-controlled routes for user-related requests //KANSKE SKA ANVÄNDA DOM HÄR @@ -66,92 +62,6 @@ app.listen(port, () => { -// // server.js -// import express from 'express'; -// import cors from 'cors'; -// import mongoose from 'mongoose'; -// import authUserMiddleware from './middleware/authMiddleware'; -// // import userRoutes from './models/userModel'; -// import authUserRoutes from './routes/authUserRoutes'; -// import secretRoute from './routes/secretRoutes'; // Add this line -// import listEndpoints from 'express-list-endpoints'; -// import dotenv from 'dotenv'; -// dotenv.config(); - -// console.log('JWT_SECRET:', process.env.JWT_SECRET); - - -// const mongoUrl = process.env.MONGO_URI || "mongodb://localhost/project-mongo"; -// mongoose.connect(mongoUrl, { useNewUrlParser: true, useUnifiedTopology: true }); -// mongoose.Promise = Promise; - -// const port = process.env.PORT || 8080; -// const app = express(); - -// app.use(cors()); -// app.use(express.json()); - -// app.use((req, res, next) => { -// console.log(`Received ${req.method} request at ${req.url}`); -// next(); -// }); - -// app.use('/auth', authUserMiddleware); -// app.use('/user', authUserRoutes); -// app.use('/secret', secretRoute); - -// app.get("/", (req, res) => { -// res.json(listEndpoints(app)); -// }); - -// app.listen(port, () => { -// console.log(`Server running on http://localhost:${port}`); -// }); - - - - -// server.js -// import express from 'express'; -// import cors from 'cors'; -// import mongoose from 'mongoose'; -// import authUserMiddleware from './middleware/authMiddleware'; -// import userRoutes from './models/userModel'; -// import authUserRoutes from './routes/authUserRoutes'; -// import listEndpoints from 'express-list-endpoints'; -// import dotenv from 'dotenv'; -// dotenv.config(); - -// const mongoUrl = process.env.MONGO_URI || "mongodb://localhost:27017/project-mongo"; -// mongoose.connect(mongoUrl, { useNewUrlParser: true, useUnifiedTopology: true }); -// mongoose.Promise = Promise; - -// const port = process.env.PORT || 8080; -// const app = express(); - -// app.use(cors()); -// app.use(express.json()); - -// app.use((req, res, next) => { -// console.log(`Received ${req.method} request at ${req.url}`); -// next(); -// }); - -// app.use('/auth', authUserMiddleware); - -// app.use('/user', userRoutes); -// app.use('/auth', authUserRoutes); - -// app.get("/", (req, res) => { -// res.json(listEndpoints(app)); -// }); - -// app.listen(port, () => { -// console.log(`Server running on http://localhost:${port}`); -// }); - - - From e704c5c3dd007e8dffce1b10d8094585025d4931 Mon Sep 17 00:00:00 2001 From: EvelynDelCarmen Date: Mon, 29 Apr 2024 16:55:34 +0200 Subject: [PATCH 39/46] added the new logic --- backend/config/db.js | 22 +-- backend/controllers/userController.js | 199 ++++++++++++++++++------- backend/middleware/authenticateUser.js | 177 ++++++---------------- backend/models/userModel.js | 124 +-------------- backend/routes/userRoutes.js | 18 +-- backend/server.js | 80 +++++++--- 6 files changed, 264 insertions(+), 356 deletions(-) diff --git a/backend/config/db.js b/backend/config/db.js index b6a6acdaa..456c97556 100644 --- a/backend/config/db.js +++ b/backend/config/db.js @@ -1,35 +1,23 @@ -// Import the 'mongoose' library to work with MongoDB -import mongoose from "mongoose"; - -//testar -// Import the 'dotenv' library to load environment variables from a .env file +import mongoose from "mongoose"; import dotenv from "dotenv"; -// Load environment variables from the .env file dotenv.config(); -// Define an asynchronous function 'connectDB' to connect to the MongoDB database + export const connectDB = async () => { try { const conn = await mongoose.connect(process.env.MONGO_URI); - // If the connection is successful, log a message indicating that the MongoDB is connected + console.log(`Mongo DB Connected: ${conn.connection.host}`); } catch (error) { - // If an error occurs during the connection attempt, log the error message + console.log(error); - // Exit the Node.js process with an exit code of 1 to indicate an error + process.exit(1); } }; -// In summary, this code does the following: - -// - Imports the necessary libraries (mongoose and dotenv) for working with MongoDB and loading environment variables. -// - Loads environment variables from a .env file, which allows you to store configuration values separately from your code. -// - Defines an asynchronous function named connectDB that attempts to connect to a MongoDB database using the URL specified in the MONGO_URL environment variable. -// - If the connection is successful, it logs a message indicating that the MongoDB database is connected, including the host information. -// - If an error occurs during the connection attempt, it logs the error message and exits the Node.js process with an exit code of 1 to indicate that an error has occurred. \ No newline at end of file diff --git a/backend/controllers/userController.js b/backend/controllers/userController.js index 2accfbbaa..ab541b151 100644 --- a/backend/controllers/userController.js +++ b/backend/controllers/userController.js @@ -1,35 +1,147 @@ -import { UserModel } from "../models/userModel"; -//asyncHandler: We use asyncHandler to simplify error handling in asynchronous code. It helps us avoid writing repetitive try-catch blocks by automatically catching errors and passing them to our error handling middleware. This makes our code cleaner and more readable, reducing the risk of unhandled exceptions that could crash the server. +// import { UserModel } from "../models/userModel"; +// //asyncHandler: We use asyncHandler to simplify error handling in asynchronous code. It helps us avoid writing repetitive try-catch blocks by automatically catching errors and passing them to our error handling middleware. This makes our code cleaner and more readable, reducing the risk of unhandled exceptions that could crash the server. +// import asyncHandler from "express-async-handler"; +// // bcrypt: We use bcrypt to securely hash and store passwords in our database. Storing plain-text passwords is a security risk, as it exposes user credentials in case of a data breach. bcrypt helps us hash passwords in a way that is computationally expensive and time-consuming for potential attackers, making it difficult to crack passwords even if the database is compromised. It enhances the overall security of user authentication in our application. +// import bcrypt from "bcrypt"; +// // jwt (JSON Web Tokens): We use jwt for authentication and authorization. It allows us to create and verify tokens that contain user identity information, such as user IDs or roles. These tokens are often sent with requests to secure routes and verify that a user has the necessary permissions to access certain resources. JWTs are stateless and efficient, making them a popular choice for secure communication between the client and server. +// import jwt from "jsonwebtoken"; + +// // Actual Functions here + +// // ----------------------- +// // ----------------------- + +// // @desc Register new user +// // @route POST api/register +// // @access Public + +// export const registerUserController = asyncHandler(async (req, res) => { +// // Extract email, username and password from the request body +// const { username, password, email } = req.body; +// // In this try section of the try catch we will first do some conditional logic and then generate the newUser with a crypted password within the DB. +// try { +// // 1st Condition +// // Check wether all fields of registration logic are NOT [!email] inputted from the request.body object +// if (!username || !email || !password) { +// // if so, set http status to a 400code +// res.status(400); +// // and throw new error with some info +// throw new Error("Please add all fields"); +// } +// // 2nd Condition +// // Check if the current user trying to register is using an usernam or email that matches with the same username or email in the database, so they would have to choose something diferent +// const existingUser = await UserModel.findOne({ +// $or: [{ username }, { email }], +// }); +// if (existingUser) { +// res.status(400); +// throw new Error( +// `User with ${existingUser.username === username ? "username" : "email" +// } already exists` +// ); +// } + +// // Generate a salt and hash the user's password +// //In this line below, we're using the bcrypt library to create a random value called "salt." The salt is added to the password before hashing it. It adds an extra layer of security by making it more difficult for attackers to use precomputed tables (rainbow tables) to crack passwords. The 10 in genSaltSync(10) represents the cost factor, which determines how computationally intensive the hashing process will be. +// const salt = bcrypt.genSaltSync(10); + +// const hashedPassword = bcrypt.hashSync(password, salt); +// // In this line below, we're using the generated salt to hash the user's password. Hashing transforms the password into a secure and irreversible string of characters. The bcrypt library handles the entire process for us, ensuring that the password is securely hashed. The resulting hashedPassword is what we store in the database to keep the user's password safe. +// // Create a new user instance with the hashed password +// const newUser = new UserModel({ +// username, +// email, +// password: hashedPassword, +// }); + +// // Mongoose Method: newUser.save() +// // Description: Save the new user instance to the database +// await newUser.save(); + + +// // Respond with a success message, user details, and the JWT token +// res.status(201).json({ +// success: true, +// response: { +// username: newUser.username, +// email: newUser.email, +// id: newUser._id, +// accessToken: newUser.accessToken, +// }, +// }); +// } catch (e) { +// // Handle any errors that occur during the registration process +// res.status(500).json({ success: false, response: e.message }); +// } +// }); + +// // ----------------------- + + +// export const loginUserController = asyncHandler(async (req, res) => { +// // Extract username and password from the request body +// const { username, password } = req.body; + +// try { + +// console.log("Username:", username); +// console.log("Password:", password); +// // Find a user with the provided username in the database +// const user = await UserModel.findOne({ username }); +// if (!user) { +// // If no user is found with the provided username, respond with a 401 Unauthorized and a user not found message +// return res +// .status(401) +// .json({ success: false, response: "User not found" }); +// } + +// // Compare the provided password with the hashed password in the database +// const isMatch = await bcrypt.compare(password, user.password); +// if (!isMatch) { +// // If the provided password doesn't match the stored password, respond with a 401 Unauthorized and an incorrect password message +// return res +// .status(401) +// .json({ success: false, response: "Incorrect password" }); +// } +// // Respond with a success message, user details, and the JWT token +// res.status(200).json({ +// success: true, +// response: { +// username: user.username, +// id: user._id, +// accessToken: user.accessToken, // token for the user using the acessToken generated from the model, // Use the generated token here +// }, +// }); +// } catch (e) { +// // Handle any errors that occur during the login process +// res.status(500).json({ success: false, response: e.message }); +// } +// }); + +import { UserModel } from "../models/UserModel"; import asyncHandler from "express-async-handler"; -// bcrypt: We use bcrypt to securely hash and store passwords in our database. Storing plain-text passwords is a security risk, as it exposes user credentials in case of a data breach. bcrypt helps us hash passwords in a way that is computationally expensive and time-consuming for potential attackers, making it difficult to crack passwords even if the database is compromised. It enhances the overall security of user authentication in our application. import bcrypt from "bcrypt"; -// jwt (JSON Web Tokens): We use jwt for authentication and authorization. It allows us to create and verify tokens that contain user identity information, such as user IDs or roles. These tokens are often sent with requests to secure routes and verify that a user has the necessary permissions to access certain resources. JWTs are stateless and efficient, making them a popular choice for secure communication between the client and server. import jwt from "jsonwebtoken"; -// Actual Functions here +const generateToken = (user) => { + // Function to generate JWT token + return jwt.sign({ id: user._id }, process.env.JWT_SECRET, { + expiresIn: "24h", + }); +}; -// ----------------------- -// ----------------------- +export const registerUserController = asyncHandler(async (req, res) => { -// @desc Register new user -// @route POST api/register -// @access Public -export const registerUserController = asyncHandler(async (req, res) => { - // Extract email, username and password from the request body const { username, password, email } = req.body; - // In this try section of the try catch we will first do some conditional logic and then generate the newUser with a crypted password within the DB. + try { - // 1st Condition - // Check wether all fields of registration logic are NOT [!email] inputted from the request.body object + if (!username || !email || !password) { - // if so, set http status to a 400code res.status(400); - // and throw new error with some info throw new Error("Please add all fields"); } - // 2nd Condition - // Check if the current user trying to register is using an usernam or email that matches with the same username or email in the database, so they would have to choose something diferent + const existingUser = await UserModel.findOne({ $or: [{ username }, { email }], }); @@ -41,98 +153,69 @@ export const registerUserController = asyncHandler(async (req, res) => { ); } - // Generate a salt and hash the user's password - //In this line below, we're using the bcrypt library to create a random value called "salt." The salt is added to the password before hashing it. It adds an extra layer of security by making it more difficult for attackers to use precomputed tables (rainbow tables) to crack passwords. The 10 in genSaltSync(10) represents the cost factor, which determines how computationally intensive the hashing process will be. - const salt = bcrypt.genSaltSync(10); + const salt = bcrypt.genSaltSync(10); const hashedPassword = bcrypt.hashSync(password, salt); - // In this line below, we're using the generated salt to hash the user's password. Hashing transforms the password into a secure and irreversible string of characters. The bcrypt library handles the entire process for us, ensuring that the password is securely hashed. The resulting hashedPassword is what we store in the database to keep the user's password safe. - // Create a new user instance with the hashed password const newUser = new UserModel({ username, email, password: hashedPassword, }); - // Mongoose Method: newUser.save() - // Description: Save the new user instance to the database await newUser.save(); + const token = generateToken(newUser._id); - // Respond with a success message, user details, and the JWT token res.status(201).json({ success: true, response: { username: newUser.username, email: newUser.email, id: newUser._id, - accessToken: newUser.accessToken, + accessToken: token, }, }); } catch (e) { - // Handle any errors that occur during the registration process res.status(500).json({ success: false, response: e.message }); } }); -// ----------------------- -// ----------------------- -// ----------------------- -// ----------------------- -// ----------------------- - -// @desc Login Existing User -// @route POST api/login -// @access Public export const loginUserController = asyncHandler(async (req, res) => { - // Extract username and password from the request body + const { username, password } = req.body; try { console.log("Username:", username); console.log("Password:", password); - // Find a user with the provided username in the database + const user = await UserModel.findOne({ username }); if (!user) { - // If no user is found with the provided username, respond with a 401 Unauthorized and a user not found message return res .status(401) .json({ success: false, response: "User not found" }); } - // Compare the provided password with the hashed password in the database + const isMatch = await bcrypt.compare(password, user.password); if (!isMatch) { - // If the provided password doesn't match the stored password, respond with a 401 Unauthorized and an incorrect password message + return res .status(401) .json({ success: false, response: "Incorrect password" }); } - // Respond with a success message, user details, and the JWT token + const token = generateToken(user._id); + res.status(200).json({ success: true, response: { username: user.username, id: user._id, - accessToken: user.accessToken, // token for the user using the acessToken generated from the model, // Use the generated token here + accessToken: token, }, }); } catch (e) { - // Handle any errors that occur during the login process res.status(500).json({ success: false, response: e.message }); } }); - -// SUMMARY - -// This file contains controller functions for user-related operations within an Express.js application. Let's provide a summary with additional context: - -// registerUserController: This controller handles user registration. It extracts the user's username, password, and email from the request body. It performs several checks, such as ensuring that all required fields are provided and that the chosen username or email is not already in use by another user. It securely hashes the user's password using the bcrypt library and stores the hashed password in the database. After successfully registering the user, it responds with a success message, user details, and a JSON Web Token (JWT) for authentication. - -// generateToken: This is a utility function used to generate JWT tokens for user authentication. It takes a user object and creates a token containing the user's access token, with an optional secret key and a 24-hour expiration time. - -// loginUserController: This controller manages user login. It extracts the username and password from the request body, then attempts to find a user with the provided username in the database. If the user is found, it compares the provided password with the hashed password stored in the database using bcrypt. If the credentials match, it generates a JWT token for the user and responds with a success message, user details, and the JWT token. In case of authentication failure (wrong password or non-existent user), it responds with appropriate error messages. - -// In summary, this file provides controllers for user registration and login, ensuring that user credentials are securely handled and authenticated using JWT tokens. It also uses bcrypt to hash and store passwords securely in the database, enhancing the overall security of user authentication in the application. diff --git a/backend/middleware/authenticateUser.js b/backend/middleware/authenticateUser.js index d4bf23ca8..7263e1769 100644 --- a/backend/middleware/authenticateUser.js +++ b/backend/middleware/authenticateUser.js @@ -1,137 +1,54 @@ -// Import the UserModel from the User model file -import { UserModel } from "../models/userModel"; -// Define a function called authenticateUser that takes a request (req), response (res), and a next function as parameters -export const authenticateUser = async (req, res, next) => { - // Retrieve the access token from the request header - const accessToken = req.header("Authorization"); - try { - // Find a user in the database using the retrieved access token - // Mongoose Method: UserModel.findOne({ accessToken: accessToken }) - // Description: This line of code serves the purpose of authenticating a user based on the provided access token. It checks if a user with the specified accessToken exists in the database using the UserModel. If a user is found, their user document is stored in the user variable. This allows the middleware to add the user object to the request, making it available for subsequent middleware or routes. If no user is found, it prepares to send a 401 Unauthorized response to indicate that the user needs to log in. This code is an essential part of user authentication in the Node.js application and helps control access to protected routes or endpoints. - const user = await UserModel.findOne({ accessToken: accessToken }); - if (user) { - // If a user is found, add the user object to the request object - req.user = user; // Add user to the request object - next(); // Continue to the next middleware or route - } else { - // If no user is found, send a 401 Unauthorized response - res.status(401).json({ success: false, response: "Please log in" }); - } - } catch (e) { - // Handle any errors that occur during the database query or user authentication - res.status(500).json({ success: false, response: e.message }); - } -}; - -// SUMMARY - -//In this code, we have a function called authenticateUser that is used as middleware in a Node.js application. This middleware is responsible for checking the authorization header of an incoming request, searching for a user with the provided access token in the database using the UserModel, and adding the user object to the request if found. If no user is found or if there are any errors during the process, appropriate responses are sent back to the client. In summary, this code is handling user authentication by checking the access token in the request header and verifying it against the database to grant access to protected routes or endpoints. - - - - - - - - - - - - -// // authMiddleware.js -// import jwt from 'jsonwebtoken'; -// import User from '../models/userModel'; - -// const authenticateUser = async (req, res, next) => { -// const { authorization } = req.headers; - -// try { -// console.log('Received token:', authorization); -// if (!authorization || !authorization.startsWith('Bearer ')) { -// return res.status(401).json({ error: 'Unauthorized - Missing or invalid token' }); -// } - -// const token = authorization.split(' ')[1]; -// const decoded = jwt.verify(token, process.env.JWT_SECRET); - -// if (!decoded || !decoded.userId) { -// return res.status(401).json({ error: 'Unauthorized - Invalid token content' }); -// } - -// // Attach the user to the request object -// req.user = await User.findById(decoded.userId); - -// if (!req.user) { -// return res.status(401).json({ error: 'Unauthorized - User not found' }); -// } - -// next(); -// } catch (error) { -// console.error('Error during token verification:', error); -// res.status(401).json({ error: 'Unauthorized - Token verification failed' }); +// // Import the UserModel from the User model file +// import { UserModel } from "../models/userModel"; +// // Define a function called authenticateUser that takes a request (req), response (res), and a next function as parameters +// export const authenticateUser = async (req, res, next) => { +// // Retrieve the access token from the request header +// const accessToken = req.header("Authorization"); +// try { +// // Find a user in the database using the retrieved access token +// // Mongoose Method: UserModel.findOne({ accessToken: accessToken }) +// // Description: This line of code serves the purpose of authenticating a user based on the provided access token. It checks if a user with the specified accessToken exists in the database using the UserModel. If a user is found, their user document is stored in the user variable. This allows the middleware to add the user object to the request, making it available for subsequent middleware or routes. If no user is found, it prepares to send a 401 Unauthorized response to indicate that the user needs to log in. This code is an essential part of user authentication in the Node.js application and helps control access to protected routes or endpoints. +// const user = await UserModel.findOne({ accessToken: accessToken }); +// if (user) { +// // If a user is found, add the user object to the request object +// req.user = user; // Add user to the request object +// next(); // Continue to the next middleware or route +// } else { +// // If no user is found, send a 401 Unauthorized response +// res.status(401).json({ success: false, response: "Please log in" }); // } +// } catch (e) { +// // Handle any errors that occur during the database query or user authentication +// res.status(500).json({ success: false, response: e.message }); +// } // }; +import { UserModel } from "../models/UserModel"; +import jwt from "jsonwebtoken"; -// export default authenticateUser; - - - - - - - - -// authMiddleware.js -// import User from '../models/userModel'; -// import jwt from 'jsonwebtoken'; -// import crypto from 'crypto'; - -// // Middleware to handle user authentication -// const authenticateUser = async (req, res, next) => { -// const { authorization } = req.headers; - -// try { -// if (!authorization || !authorization.startsWith('Bearer ')) { -// return res.status(401).json({ error: 'Unauthorized' }); -// } - -// const token = authorization.split(' ')[1]; -// const secretKey = generateRandomKey(); - -// const decoded = jwt.verify(token, secretKey); - -// const user = await User.findById(decoded.userId); - -// if (!user) { -// return res.status(401).json({ error: 'Unauthorized' }); -// } - -// req.user = user; - -// next(); -// } catch (error) { -// res.status(401).json({ error: 'Unauthorized' }); -// } -// }; - -// // Function to generate a random secret key -// const generateRandomKey = () => { -// return crypto.randomBytes(32).toString('hex'); -// }; - -// export default authenticateUser; - - -// 'authUser.js': -// Uses the comparePassword method from the User model to check if the entered password matches the hashed password stored in the database. -// This middleware is applied to routes where user authentication is required. +export const authenticateUser = async (req, res, next) => { + // Retrieve the access token from the request header and remove the "Bearer " prefix if present + const authHeader = req.header("Authorization"); + if (!authHeader || !authHeader.startsWith("Bearer ")) { + return res.status(401).json({ success: false, response: "No token provided, please log in" }); + } + const token = authHeader.split(' ')[1]; // Get the token from the header + try { + // Verify token + const decoded = jwt.verify(token, process.env.JWT_SECRET); -//Explanation:This middleware defines a route ('/login') for handling user login. -// It retrieves the username and password from the request body. -// It searches for the user in the database by the provided username. -// If the user is found, it compares the entered password with the hashed password in the database using the comparePassword method from the 'userModel.js'. -// If the credentials are valid, it generates an access token (you need to replace the placeholder with your actual token generation logic) and attaches it to the user in the database. -// The middleware then sends a response with the access token. + // Fetch the user using the ID decoded from the token + const user = await UserModel.findById(decoded.id).select("-password"); // Exclude password from user data + if (!user) { + return res.status(404).json({ success: false, response: "User not found" }); + } + // Attach the user to the request object + req.user = user; + next(); // Continue to the next middleware or route handler + } catch (error) { + console.error(`JWT Error: ${error.message}`); + res.status(401).json({ success: false, response: "Invalid token, please log in again" }); + } +}; diff --git a/backend/models/userModel.js b/backend/models/userModel.js index 4a5c22db2..a388f964c 100644 --- a/backend/models/userModel.js +++ b/backend/models/userModel.js @@ -1,15 +1,11 @@ import mongoose from "mongoose"; -import crypto from "crypto"; // Imports the Node.js crypto library for generating secure random strings. +import crypto from "crypto"; -// Import the Schema class from the Mongoose library -// Destructures the Schema class from the Mongoose library, allowing us to create a schema. const { Schema } = mongoose; -// Create a new Mongoose schema named 'userSchema' -// Creates a new Mongoose schema named userSchema that defines the structure of a user document in the MongoDB collection. It includes fields like username, password, and accessToken, specifying their data types, validation rules, and default values. const userSchema = new Schema( { - // Define the 'username' field with a String data type + username: { type: String, // Specifies that 'username' should be a string required: true, // Indicates that 'username' is a required field @@ -38,131 +34,17 @@ const userSchema = new Schema( } ); -// Create a Mongoose model named 'UserModel' based on the 'userSchema' for the 'users' collection -// This model is used to interact with the "users" collection in the MongoDB database. It allows you to perform CRUD operations on user documents and provides methods for data validation based on the schema. -export const UserModel = mongoose.model("User", userSchema); - -// In summary, this code defines a Mongoose schema (userSchema) that describes the structure of documents for users in a MongoDB collection. It also creates a Mongoose model (UserModel) associated with the "users" collection, which can be used to interact with the database and perform operations like creating, reading, updating, and deleting user documents. - - - - - - - - - -// // userModel.js -// import mongoose from 'mongoose'; -// import bcrypt from 'bcrypt'; - -// const userSchema = new mongoose.Schema({ -// username: { type: String, unique: true, required: true }, -// email: { type: String, unique: true, required: true }, -// password: { type: String, required: true }, -// secretKey: { type: String }, -// }); - -// userSchema.pre('save', async function (next) { -// const user = this; -// if (user.isModified('password')) { -// try { -// const hashedPassword = await bcrypt.hash(user.password, 10); -// user.password = hashedPassword; -// } catch (error) { -// next(error); -// } -// } -// next(); -// }); - -// userSchema.methods.comparePassword = async function (enteredPassword) { -// try { -// return await bcrypt.compare(enteredPassword, this.password); -// } catch (error) { -// throw new Error(error); -// } -// }; - -// const User = mongoose.model('User', userSchema); - -// export default User; +export const UserModel = mongoose.model("User", userSchema); -// userModel.js -// import mongoose from 'mongoose'; -// import bcrypt from 'bcrypt'; -// // Define the user schema -// const userSchema = new mongoose.Schema({ -// username: { -// type: String, -// unique: true, -// required: true, -// trim: true, -// }, -// email: { -// type: String, -// unique: true, -// required: true, -// }, -// password: { -// type: String, -// required: true, -// }, -// accessToken: { -// type: String, -// }, -// }); -// // Mongoose middleware to hash the password before saving to the database -// userSchema.pre('save', async function (next) { -// const user = this; -// if (user.isModified('password')) { -// try { -// const hashedPassword = await bcrypt.hash(user.password, 10); -// user.password = hashedPassword; -// } catch (error) { -// next(error); -// } -// } -// next(); -// }); -// // Create a method to compare entered password with hashed password in the database -// userSchema.methods.comparePassword = async function (enteredPassword) { -// try { -// return await bcrypt.compare(enteredPassword, this.password); -// } catch (error) { -// throw new Error(error); -// } -// }; -// // Create the User model using the user schema -// const User = mongoose.model('User', userSchema); -// // Export the User model for use in other files -// export default User; - - - -// 'userModel.js': -// Contains a Mongoose middleware (pre-save hook) that automatically hashes the password before saving it to the database. -// This middleware is applied to the User schema using userSchema.pre('save', ...). -// The hashed password is stored in the database. - - -//In cryptography, a "salt" is random data that is generated and used as an additional input to a one-way function (in this case, a password hashing function). The primary purpose of using a salt is to defend against dictionary attacks, pre-computed rainbow table attacks, and similar techniques. - - -// Explanation of key points: -// userSchema: This defines the structure of the user document in the MongoDB collection. It includes fields for username, password, and accessToken. -// pre('save') middleware: This middleware runs before saving a new user or updating an existing user. It hashes the password using bcrypt before saving it to the database. -// comparePassword method: This method is added to the user schema to compare entered passwords with the hashed password stored in the database. -// User model: This is created using the mongoose.model function, which takes the model name ('User') and the user schema. diff --git a/backend/routes/userRoutes.js b/backend/routes/userRoutes.js index 36cb4bd98..96c2a5caa 100644 --- a/backend/routes/userRoutes.js +++ b/backend/routes/userRoutes.js @@ -1,20 +1,20 @@ -// Import the necessary modules and functions + import express from "express"; import { registerUserController, loginUserController, -} from "../controllers/userController"; // Import controller functions for user registration and login +} from "../controllers/userController"; + -// Create an instance of the Express router const router = express.Router(); -// REGISTER ROUTE: Handle user registration -router.post("/register", registerUserController); // When a POST request is made to /register, execute the registerUserController function -// LOGIN ROUTE: Handle user login -router.post("/login", loginUserController); // When a POST request is made to /login, execute the loginUserController function +router.post("/register", registerUserController); + + +router.post("/login", loginUserController); + -// Export the router for use in the main application export default router; -// In summary, this file sets up routes using the Express router for user registration and login operations. It associates each route with the corresponding controller function. These routes define the API endpoints for handling user registration and login within the application. + diff --git a/backend/server.js b/backend/server.js index 024b8fbf4..ff1c11b0c 100644 --- a/backend/server.js +++ b/backend/server.js @@ -3,31 +3,75 @@ import express from "express"; import cors from "cors"; import dotenv from "dotenv"; import expressListEndpoints from "express-list-endpoints"; - import userRoutes from "./routes/userRoutes"; import { connectDB } from "./config/db"; +// dotenv.config(); +// const port = process.env.PORT; +// const app = express(); + +// // Add middlewares to enable cors and json body parsing +// app.use(cors()); // Enable CORS (Cross-Origin Resource Sharing) +// app.use(express.json()); // Parse incoming JSON data +// app.use(express.urlencoded({ extended: false })); // Parse URL-encoded data + + + +// app.use(userRoutes); // Use the user-controlled routes for user-related requests + +// //KANSKE SKA ANVÄNDA DOM HÄR +// // app.use(contactRoutes); //for the contactform +// // app.use(mediaRoutes); // for the images/film + + +// // Connection to the database through Mongoose +// connectDB(); +// // Create a dedicated endpoint to view endpoints in the browser +// app.get("/", (req, res) => { +// const endpoints = expressListEndpoints(app); +// res.json(endpoints); +// console.log("List of Endpoints:"); +// console.log(endpoints); +// }); + +// // app.use((err, req, res, next) => { +// // res.status(500).send(err); +// // }); + +// app.use((err, req, res, next) => { +// const statusCode = err.statusCode || 500; +// res.status(statusCode).json({ +// message: err.message, +// stack: process.env.NODE_ENV === 'production' ? : err.stack, +// }); +// }); + +// // Start the server and listen for incoming requests on the specified port +// app.listen(port, () => { +// console.log(`Server running on http://localhost:${port}`); // Display a message when the server is successfully started +// }); + dotenv.config(); -const port = process.env.PORT; const app = express(); +const port = process.env.PORT; + + +app.use(cors({ + origin: process.env.FRONTEND_URL, + credentials: true, +})); + -// Add middlewares to enable cors and json body parsing -app.use(cors()); // Enable CORS (Cross-Origin Resource Sharing) app.use(express.json()); // Parse incoming JSON data app.use(express.urlencoded({ extended: false })); // Parse URL-encoded data -app.use(userRoutes); // Use the user-controlled routes for user-related requests - -//KANSKE SKA ANVÄNDA DOM HÄR -// app.use(contactRoutes); //for the contactform -// app.use(mediaRoutes); // for the images/film +app.use(userRoutes); -// Connection to the database through Mongoose connectDB(); -// Create a dedicated endpoint to view endpoints in the browser + app.get("/", (req, res) => { const endpoints = expressListEndpoints(app); res.json(endpoints); @@ -35,21 +79,15 @@ app.get("/", (req, res) => { console.log(endpoints); }); -// app.use((err, req, res, next) => { -// res.status(500).send(err); -// }); app.use((err, req, res, next) => { - const statusCode = err.statusCode || 500; - res.status(statusCode).json({ - message: err.message, - stack: process.env.NODE_ENV === 'production' ? '🥞' : err.stack, - }); + console.error(`Error: ${err.message}`); + res.status(500).send('Server Error'); }); -// Start the server and listen for incoming requests on the specified port + app.listen(port, () => { - console.log(`Server running on http://localhost:${port}`); // Display a message when the server is successfully started + console.log(`Server running on http://localhost:${port}`); }); From d442dc9d182ddd491c34b8cc1d2ddcec99fcd101 Mon Sep 17 00:00:00 2001 From: EvelynDelCarmen Date: Mon, 29 Apr 2024 17:22:30 +0200 Subject: [PATCH 40/46] git for deploy --- backend/config/db.js | 5 ----- backend/controllers/userController.js | 7 ------- 2 files changed, 12 deletions(-) diff --git a/backend/config/db.js b/backend/config/db.js index 456c97556..a1d061dac 100644 --- a/backend/config/db.js +++ b/backend/config/db.js @@ -4,19 +4,14 @@ import dotenv from "dotenv"; dotenv.config(); - export const connectDB = async () => { try { const conn = await mongoose.connect(process.env.MONGO_URI); - console.log(`Mongo DB Connected: ${conn.connection.host}`); } catch (error) { - console.log(error); - - process.exit(1); } }; diff --git a/backend/controllers/userController.js b/backend/controllers/userController.js index ab541b151..832d629d7 100644 --- a/backend/controllers/userController.js +++ b/backend/controllers/userController.js @@ -6,14 +6,7 @@ // // jwt (JSON Web Tokens): We use jwt for authentication and authorization. It allows us to create and verify tokens that contain user identity information, such as user IDs or roles. These tokens are often sent with requests to secure routes and verify that a user has the necessary permissions to access certain resources. JWTs are stateless and efficient, making them a popular choice for secure communication between the client and server. // import jwt from "jsonwebtoken"; -// // Actual Functions here -// // ----------------------- -// // ----------------------- - -// // @desc Register new user -// // @route POST api/register -// // @access Public // export const registerUserController = asyncHandler(async (req, res) => { // // Extract email, username and password from the request body From 2d3012880709f94ccf5e1727eedf60bddfd4f109 Mon Sep 17 00:00:00 2001 From: EvelynDelCarmen Date: Mon, 29 Apr 2024 17:34:13 +0200 Subject: [PATCH 41/46] updated usermodel + PORT in server --- backend/controllers/userController.js | 2 +- backend/middleware/authenticateUser.js | 2 +- backend/server.js | 2 +- 3 files changed, 3 insertions(+), 3 deletions(-) diff --git a/backend/controllers/userController.js b/backend/controllers/userController.js index 832d629d7..f8912cf2a 100644 --- a/backend/controllers/userController.js +++ b/backend/controllers/userController.js @@ -111,7 +111,7 @@ // } // }); -import { UserModel } from "../models/UserModel"; +import { UserModel } from "../models/userModel"; import asyncHandler from "express-async-handler"; import bcrypt from "bcrypt"; import jwt from "jsonwebtoken"; diff --git a/backend/middleware/authenticateUser.js b/backend/middleware/authenticateUser.js index 7263e1769..6a28b312f 100644 --- a/backend/middleware/authenticateUser.js +++ b/backend/middleware/authenticateUser.js @@ -22,7 +22,7 @@ // res.status(500).json({ success: false, response: e.message }); // } // }; -import { UserModel } from "../models/UserModel"; +import { UserModel } from "../models/userModel"; import jwt from "jsonwebtoken"; export const authenticateUser = async (req, res, next) => { diff --git a/backend/server.js b/backend/server.js index ff1c11b0c..c32df5716 100644 --- a/backend/server.js +++ b/backend/server.js @@ -85,7 +85,7 @@ app.use((err, req, res, next) => { res.status(500).send('Server Error'); }); - +const PORT = process.env.PORT || 8080; app.listen(port, () => { console.log(`Server running on http://localhost:${port}`); }); From 280d155cfe4f8ce48249b16c9ffadff5fc00136f Mon Sep 17 00:00:00 2001 From: EvelynDelCarmen Date: Mon, 29 Apr 2024 17:55:16 +0200 Subject: [PATCH 42/46] git for deploy 2 --- backend/controllers/userController.js | 1 - 1 file changed, 1 deletion(-) diff --git a/backend/controllers/userController.js b/backend/controllers/userController.js index f8912cf2a..c0db88939 100644 --- a/backend/controllers/userController.js +++ b/backend/controllers/userController.js @@ -173,7 +173,6 @@ export const registerUserController = asyncHandler(async (req, res) => { } }); - export const loginUserController = asyncHandler(async (req, res) => { const { username, password } = req.body; From 68903d4db4c94536675044987bb9ab35957f6fd9 Mon Sep 17 00:00:00 2001 From: EvelynDelCarmen Date: Mon, 29 Apr 2024 18:33:15 +0200 Subject: [PATCH 43/46] removed the second PORT --- backend/server.js | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/backend/server.js b/backend/server.js index c32df5716..ff1c11b0c 100644 --- a/backend/server.js +++ b/backend/server.js @@ -85,7 +85,7 @@ app.use((err, req, res, next) => { res.status(500).send('Server Error'); }); -const PORT = process.env.PORT || 8080; + app.listen(port, () => { console.log(`Server running on http://localhost:${port}`); }); From f1576f9989b5c3a69db04e130bc6ac1acf61b0eb Mon Sep 17 00:00:00 2001 From: EvelynDelCarmen Date: Mon, 29 Apr 2024 18:43:07 +0200 Subject: [PATCH 44/46] added the new log in / sign up logic --- frontend/src/pages/Login.jsx | 86 +++++++++--- frontend/src/pages/NotFound.jsx | 7 +- frontend/src/store/userStore.jsx | 183 +++++++++++++++++++++---- frontend/{ => src}/styles/tailwind.css | 0 4 files changed, 224 insertions(+), 52 deletions(-) rename frontend/{ => src}/styles/tailwind.css (100%) diff --git a/frontend/src/pages/Login.jsx b/frontend/src/pages/Login.jsx index 120d283db..ad5d24914 100644 --- a/frontend/src/pages/Login.jsx +++ b/frontend/src/pages/Login.jsx @@ -1,12 +1,55 @@ import { Link } from "react-router-dom"; -import { userStore } from "../store/userStore"; // Adjust the import path as needed +import { userStore } from "../store/userStore"; import { useState } from "react"; import { useNavigate } from "react-router-dom"; -// import Logos from "../components/Logos"; // Ensure this is correctly imported +import { useEffect } from "react"; + export const Login = () => { // State for login + // const [loginUsername, setLoginUsername] = useState(""); + // const [loginPassword, setLoginPassword] = useState(""); + // // State for signup + // const [signupUsername, setSignupUsername] = useState(""); + // const [signupPassword, setSignupPassword] = useState(""); + // const [signupEmail, setSignupEmail] = useState(""); + + // const navigate = useNavigate(); + // const [message, setMessage] = useState(""); + + + // const { handleLogin, handleSignup } = userStore((state) => ({ + // handleLogin: state.handleLogin, + // handleSignup: state.handleSignup, + // })); + + // // Login submission + // const onLoginSubmit = async (e) => { + // e.preventDefault(); + // try { + // const success = await handleLogin(loginUsername, loginPassword); + // if (success) navigate("/home"); + // else alert("Login failed."); + // } catch (error) { + // console.error("Login error:", error); + // alert("An error occurred during login."); + // } + // }; + + // // Signup submission + // const onSignupSubmit = async (e) => { + // e.preventDefault(); + // try { + // await handleSignup(signupUsername, signupPassword, signupEmail); + // alert("Signup successful. Please log in."); + // // Optionally clear the form or navigate + // } catch (error) { + // console.error("Signup error:", error); + // alert("An error occurred during signup."); + // } + // }; + const [loginUsername, setLoginUsername] = useState(""); const [loginPassword, setLoginPassword] = useState(""); // State for signup @@ -18,35 +61,34 @@ export const Login = () => { const [message, setMessage] = useState(""); - const { handleLogin, handleSignup } = userStore((state) => ({ + const { handleLogin, isLoggedIn, handleSignup } = userStore((state) => ({ handleLogin: state.handleLogin, + isLoggedIn: state.isLoggedIn, handleSignup: state.handleSignup, })); - // Login submission + useEffect(() => { + if (isLoggedIn) { + navigate('/home'); + } + }, [isLoggedIn, navigate]); + const onLoginSubmit = async (e) => { e.preventDefault(); - try { - const success = await handleLogin(loginUsername, loginPassword); - if (success) navigate("/home"); - else alert("Login failed."); - } catch (error) { - console.error("Login error:", error); - alert("An error occurred during login."); - } + setMessage(''); + const result = await handleLogin(loginUsername, loginPassword); + setMessage(result.message); }; - // Signup submission + const onSignupSubmit = async (e) => { e.preventDefault(); - try { - await handleSignup(signupUsername, signupPassword, signupEmail); - alert("Signup successful. Please log in."); - // Optionally clear the form or navigate - } catch (error) { - console.error("Signup error:", error); - alert("An error occurred during signup."); - } + setMessage(''); + const result = await handleSignup(signupUsername, signupPassword, signupEmail); + setMessage(result.message); + setSignupUsername(''); + setSignupPassword(''); + setSignupEmail(''); }; return ( @@ -110,6 +152,8 @@ export const Login = () => { Login + {message &&
{message}
}
); diff --git a/frontend/src/pages/NotFound.jsx b/frontend/src/pages/NotFound.jsx index 9cf374e32..ff9c53c4d 100644 --- a/frontend/src/pages/NotFound.jsx +++ b/frontend/src/pages/NotFound.jsx @@ -1,8 +1,9 @@ -// Import the 'React' library. + import "react"; -// Define the 'NotFound' functional component. + export const NotFound = () => { - // Render a div element with a CSS class 'not-found' containing the text 'NotFound'. + + return
NotFound
; }; \ No newline at end of file diff --git a/frontend/src/store/userStore.jsx b/frontend/src/store/userStore.jsx index 9474f3952..f5d834606 100644 --- a/frontend/src/store/userStore.jsx +++ b/frontend/src/store/userStore.jsx @@ -1,8 +1,138 @@ +// // Import the 'create' function from the 'zustand' library. +// import { create } from "zustand"; + +// // Get the backend API endpoint from the environment variables. +// const apiEnv = import.meta.env.VITE_API_URL; + +// // Create a Zustand store for user-related state and actions. +// export const userStore = create((set, get) => ({ +// // Initialize username state. +// username: "", +// // Define a function to set the username state. +// setUsername: (username) => set({ username }), + +// // Initialize email state. +// email: "", +// // Define a function to set the email state. +// setEmail: (email) => set({ email }), + +// // Initialize password state. +// password: "", +// // Define a function to set the password state. +// setPassword: (password) => set({ password }), + +// // Initialize accessToken state with null. +// accessToken: null, +// // Define a function to set the accessToken state. +// setAccessToken: (token) => set({ accessToken: token }), + +// // Initialize isLoggedIn state with false. +// isLoggedIn: false, +// // Define a function to set the isLoggedIn state. +// setIsLoggedIn: (isLoggedIn) => set({ isLoggedIn }), + +// // FUNCTION TO REGISTER USERS +// handleSignup: async (username, password, email) => { +// // Check if required fields are provided and display an alert if not. +// if (!username || !password || !email) { +// alert("Please enter username, email, and password"); +// return; +// } + +// try { +// // Send a POST request to the registration endpoint with user data. +// const response = await fetch(`${apiEnv}/register`, { +// method: "POST", +// headers: { +// "Content-Type": "application/json", +// }, +// body: JSON.stringify({ email, username, password }), +// }); + +// // Parse the response data as JSON. +// const data = await response.json(); +// if (data.success) { +// // Update the username state. +// set({ username }); +// // Display a success alert. +// alert("Signup successful!"); +// console.log("Signing up with:", username); +// } else { +// // Display an error message from the server or a generic message. +// alert(data.response || "Signup failed"); +// } +// } catch (error) { +// // Handle and log any signup errors. +// console.error("Signup error:", error); +// alert("An error occurred during signup"); +// } +// }, + +// // LOGIN +// handleLogin: async (username, password) => { +// // Check if both username and password are provided and display an alert if not. +// if (!username || !password) { +// alert("Please enter both username and password"); +// return; +// } + +// try { +// // Send a POST request to the login endpoint with user data. +// const response = await fetch(`${apiEnv}/login`, { +// method: "POST", +// headers: { +// "Content-Type": "application/json", +// }, +// body: JSON.stringify({ username, password }), +// }); + +// // Parse the response data as JSON. +// const data = await response.json(); +// if (data.success) { +// // Update the state with username, accessToken, and set isLoggedIn to true. +// set({ +// username, +// accessToken: data.response.accessToken, +// isLoggedIn: true, +// }); +// // Store the accessToken in the browser's localStorage. +// localStorage.setItem("accessToken", data.response.accessToken); +// // Display a success alert. +// alert("Login successful!"); +// console.log("Logging in with:", username, password); +// return true; // Indicate success +// } else { +// // Display an error message from the server or a generic message. +// alert(data.response || "Login failed"); +// return false; // Indicate failure +// } +// } catch (error) { +// // Handle and log any login errors. +// console.error("Login error:", error); +// alert("An error occurred during login"); +// return false; // Indicate failure +// } +// }, + +// // Function to handle user logout. +// handleLogout: () => { +// // Clear user information and set isLoggedIn to false. +// set({ username: "", accessToken: null, isLoggedIn: false }); +// // Remove the accessToken from localStorage. +// localStorage.removeItem("accessToken"); +// // Additional logout logic can be added here if needed. +// }, +// })); + // Import the 'create' function from the 'zustand' library. import { create } from "zustand"; // Get the backend API endpoint from the environment variables. const apiEnv = import.meta.env.VITE_API_URL; +if (!apiEnv) { + console.error('VITE_API_URL environment variable is not set.'); +} + // Create a Zustand store for user-related state and actions. export const userStore = create((set, get) => ({ @@ -35,8 +165,11 @@ export const userStore = create((set, get) => ({ handleSignup: async (username, password, email) => { // Check if required fields are provided and display an alert if not. if (!username || !password || !email) { - alert("Please enter username, email, and password"); - return; + + // alert("Please enter username, email, and password"); + // return; + + return { success: false, message: "Please enter username, email, and password" }; } try { @@ -47,33 +180,31 @@ export const userStore = create((set, get) => ({ "Content-Type": "application/json", }, body: JSON.stringify({ email, username, password }), + credentials: 'include', }); // Parse the response data as JSON. const data = await response.json(); + if (data.success) { - // Update the username state. set({ username }); - // Display a success alert. - alert("Signup successful!"); - console.log("Signing up with:", username); + return { success: true, message: "Signup successful! Please log in." }; } else { - // Display an error message from the server or a generic message. - alert(data.response || "Signup failed"); + return { success: false, message: data.response || "Signup failed" }; } } catch (error) { - // Handle and log any signup errors. console.error("Signup error:", error); - alert("An error occurred during signup"); + return { success: false, message: "An error occurred during signup" }; } }, + + // LOGIN handleLogin: async (username, password) => { - // Check if both username and password are provided and display an alert if not. + if (!username || !password) { - alert("Please enter both username and password"); - return; + return { success: false, message: "Please enter both username and password" }; } try { @@ -84,33 +215,31 @@ export const userStore = create((set, get) => ({ "Content-Type": "application/json", }, body: JSON.stringify({ username, password }), + credentials: 'include', }); - // Parse the response data as JSON. + + + if (!response.ok) { + // Handle the non-OK response here + throw new Error(`HTTP error! status: ${response.status}`); + } + const data = await response.json(); if (data.success) { - // Update the state with username, accessToken, and set isLoggedIn to true. set({ username, accessToken: data.response.accessToken, isLoggedIn: true, }); - // Store the accessToken in the browser's localStorage. localStorage.setItem("accessToken", data.response.accessToken); - // Display a success alert. - alert("Login successful!"); - console.log("Logging in with:", username, password); - return true; // Indicate success + return { success: true, message: "Login successful!" }; } else { - // Display an error message from the server or a generic message. - alert(data.response || "Login failed"); - return false; // Indicate failure + return { success: false, message: data.response || "Login failed" }; } } catch (error) { - // Handle and log any login errors. console.error("Login error:", error); - alert("An error occurred during login"); - return false; // Indicate failure + return { success: false, message: "Wrong username or password, try again!" }; } }, @@ -124,5 +253,3 @@ export const userStore = create((set, get) => ({ }, })); -// SUMMARY -// This file serves as the core of a React application's user authentication and state management system. It utilizes the Zustand library to create a centralized store that handles user-related data and actions. The store includes state variables such as username, email, password, accessToken, and isLoggedIn, each with corresponding functions to modify their values. The handleSignup function allows users to register by sending their information to a server-side registration endpoint, displaying alerts for success or failure. Similarly, the handleLogin function facilitates user login, updating the state with the user's credentials and access token upon success, and storing the token in the browser's local storage. Additionally, it handles the user's logout by clearing user information and local storage data. Overall, this file provides a robust framework for user authentication and state management in the React application, enhancing user registration, login, and logout processes. diff --git a/frontend/styles/tailwind.css b/frontend/src/styles/tailwind.css similarity index 100% rename from frontend/styles/tailwind.css rename to frontend/src/styles/tailwind.css From 9d402890c93a7a1b390e96b40a1e24239c0f6ddb Mon Sep 17 00:00:00 2001 From: EvelynDelCarmen Date: Mon, 29 Apr 2024 18:45:40 +0200 Subject: [PATCH 45/46] removed the button --- frontend/src/components/reusecomponents/Button.jsx | 14 -------------- 1 file changed, 14 deletions(-) delete mode 100644 frontend/src/components/reusecomponents/Button.jsx diff --git a/frontend/src/components/reusecomponents/Button.jsx b/frontend/src/components/reusecomponents/Button.jsx deleted file mode 100644 index d9625a0b7..000000000 --- a/frontend/src/components/reusecomponents/Button.jsx +++ /dev/null @@ -1,14 +0,0 @@ -// import 'react'; -// import { Link } from 'react-router-dom'; - -// const Button = ({ to, text, className }) => { -// return ( -// -// -// -// ); -// }; - -// export default Button; \ No newline at end of file From 0584b6bff3f5169946413fa75e189fe942cbea27 Mon Sep 17 00:00:00 2001 From: EvelynDelCarmen Date: Mon, 29 Apr 2024 18:56:22 +0200 Subject: [PATCH 46/46] added the design to the not-found page --- frontend/src/pages/Login.jsx | 42 --------------------------------- frontend/src/pages/NotFound.jsx | 21 ++++++++++++----- 2 files changed, 15 insertions(+), 48 deletions(-) diff --git a/frontend/src/pages/Login.jsx b/frontend/src/pages/Login.jsx index ad5d24914..bcee470fa 100644 --- a/frontend/src/pages/Login.jsx +++ b/frontend/src/pages/Login.jsx @@ -7,48 +7,6 @@ import { useEffect } from "react"; export const Login = () => { - // State for login - // const [loginUsername, setLoginUsername] = useState(""); - // const [loginPassword, setLoginPassword] = useState(""); - // // State for signup - // const [signupUsername, setSignupUsername] = useState(""); - // const [signupPassword, setSignupPassword] = useState(""); - // const [signupEmail, setSignupEmail] = useState(""); - - // const navigate = useNavigate(); - // const [message, setMessage] = useState(""); - - - // const { handleLogin, handleSignup } = userStore((state) => ({ - // handleLogin: state.handleLogin, - // handleSignup: state.handleSignup, - // })); - - // // Login submission - // const onLoginSubmit = async (e) => { - // e.preventDefault(); - // try { - // const success = await handleLogin(loginUsername, loginPassword); - // if (success) navigate("/home"); - // else alert("Login failed."); - // } catch (error) { - // console.error("Login error:", error); - // alert("An error occurred during login."); - // } - // }; - - // // Signup submission - // const onSignupSubmit = async (e) => { - // e.preventDefault(); - // try { - // await handleSignup(signupUsername, signupPassword, signupEmail); - // alert("Signup successful. Please log in."); - // // Optionally clear the form or navigate - // } catch (error) { - // console.error("Signup error:", error); - // alert("An error occurred during signup."); - // } - // }; const [loginUsername, setLoginUsername] = useState(""); const [loginPassword, setLoginPassword] = useState(""); diff --git a/frontend/src/pages/NotFound.jsx b/frontend/src/pages/NotFound.jsx index ff9c53c4d..df87c634f 100644 --- a/frontend/src/pages/NotFound.jsx +++ b/frontend/src/pages/NotFound.jsx @@ -1,9 +1,18 @@ -import "react"; - +import React from "react"; export const NotFound = () => { - - - return
NotFound
; -}; \ No newline at end of file + return ( +
+

404

+

Page Not Found

+

Sorry, the page you are looking for does not exist.

+ + Go Home + +
+ ); +};