);
};
diff --git a/frontend/src/components/authPageContent.jsx b/frontend/src/components/authPageContent.jsx
new file mode 100644
index 000000000..fff9c8c14
--- /dev/null
+++ b/frontend/src/components/authPageContent.jsx
@@ -0,0 +1,62 @@
+// components/AuthPageContent.jsx
+import { useEffect, useState } from 'react';
+import { useAuthStore } from './authStore';
+
+export const AuthPageContent = () => {
+ // Access the accessToken from Zustand state
+ const { accessToken } = useAuthStore();
+
+ // State to store the data fetched from the authenticated endpoint
+ const [contentData, setContentData] = useState(null);
+
+ // useEffect to run the fetch operation when the component mounts or accessToken changes
+ useEffect(() => {
+ // Define an asynchronous function to fetch authenticated content
+ const fetchAuthenticatedContent = async () => {
+ try {
+ // Make a GET request to the authenticated endpoint
+ const response = await fetch('http://localhost:8081/secrets', {
+ headers: {
+ Authorization: accessToken,
+ },
+ });
+
+ // Check if the response is successful
+ if (response.ok) {
+ // Parse the JSON response
+ const data = await response.json();
+ // Update the state with the fetched data
+ setContentData(data);
+ } else {
+ // Handle errors if the response is not successful
+ console.error('Error fetching authenticated content');
+ }
+ } catch (error) {
+ // Handle network or other errors
+ console.error('Error:', error.message);
+ }
+ };
+
+ // Check if there is a valid accessToken before making the request
+ if (accessToken) {
+ // Call the fetchAuthenticatedContent function
+ fetchAuthenticatedContent();
+ }
+ }, [accessToken]); // useEffect will re-run if accessToken changes
+
+ return (
+
+
Authenticated Content Page
+ {contentData === null ? (
+ // Loading state
+
Loading...
+ ) : contentData ? (
+ // If contentData is available, display the page content
+
{contentData.secret}
+ ) : (
+ // If contentData is not available, display an error message
+
Content not found
+ )}
+
+ );
+};
\ No newline at end of file
diff --git a/frontend/src/components/loginForm.jsx b/frontend/src/components/loginForm.jsx
index 1fa4714e1..a922e0d1c 100644
--- a/frontend/src/components/loginForm.jsx
+++ b/frontend/src/components/loginForm.jsx
@@ -1,8 +1,12 @@
+import { useHistory } from 'react-router-dom';
import { useState } from 'react';
-import useAuthStore from './authStore';
+import { useAuthStore } from './authStore';
export const LoginForm = () => {
+ const history = useHistory(); // Initialize useHistory
+
const { setUser } = useAuthStore();
+
// State variables for form inputs and errors
const [email, setEmail] = useState('');
const [password, setPassword] = useState('');
@@ -36,6 +40,10 @@ export const LoginForm = () => {
password,
}),
});
+ // Check if the login was successful
+ if (!response.ok) {
+ throw new Error('Login failed. Please try again.');
+ }
// Parse the JSON response
const data = await response.json();
@@ -47,8 +55,13 @@ export const LoginForm = () => {
setUser({ id: userId, name, email }, accessToken);
console.log('Login successful:', data);
// Handle successful login (update global state, redirect, etc.)
+
+ history.push('/secrets'); // // Redirect to /secrets after successful login)
+
} catch (error) {
console.error('Error during login:', error.message);
+ setErrors({ general: 'Login failed. Please try again.' });
+
// Handle login error (display error message, etc.)
}
} else {
diff --git a/frontend/src/components/logoutButton.jsx b/frontend/src/components/logoutButton.jsx
index d11624036..49984c50a 100644
--- a/frontend/src/components/logoutButton.jsx
+++ b/frontend/src/components/logoutButton.jsx
@@ -1,13 +1,16 @@
-import useAuthStore from './authStore';
+import { useHistory } from 'react-router-dom';
+import { useAuthStore } from './authStore';
export const LogoutButton = () => {
+ const history = useHistory(); // Initialize useHistory
+
const logout = useAuthStore((state) => state.logout);
const handleLogout = () => {
logout();
// Redirect to login form after signing out
- window.location.href = '/login';
+ history.push('/login');
};
return (
diff --git a/frontend/src/components/pagecontent.jsx b/frontend/src/components/pagecontent.jsx
deleted file mode 100644
index e69de29bb..000000000
diff --git a/frontend/src/components/registrationForm.jsx b/frontend/src/components/registrationForm.jsx
index 4f32fd4a3..80bf89cae 100644
--- a/frontend/src/components/registrationForm.jsx
+++ b/frontend/src/components/registrationForm.jsx
@@ -1,7 +1,11 @@
import { useState } from 'react';
-import useAuthStore from './authStore';
+import { useHistory } from 'react-router-dom';
+import { useAuthStore } from './authStore';
export const RegistrationForm = () => {
+
+ const history = useHistory(); // Initialize useHistory
+
// State variables for form inputs and errors
const [name, setName] = useState('');
const [email, setEmail] = useState('');
@@ -37,14 +41,23 @@ export const RegistrationForm = () => {
password,
}),
});
+ // Check if the registration was successful
+ if (!response.ok) {
+ throw new Error('Registration failed. Please try again.');
+ }
// Parse the JSON response
const data = await response.json();
// Update user and authentication state on successful registration
useAuthStore.setState({ user: data.user, accessToken: data.accessToken, isAuthenticated: true });
console.log('Form submitted successfully:', data);
// Handle successful registration
+
+ history.push('/login');
+ // Redirect to /login after successful registration
+
} catch (error) {
console.error('Error submitting form:', error.message);
+ setErrors({ general: 'Registration failed. Please try again.' });
// Handle registration error (display error message, etc.)
}
} else {
diff --git a/frontend/src/main.jsx b/frontend/src/main.jsx
index 16db3b0ab..72066602b 100644
--- a/frontend/src/main.jsx
+++ b/frontend/src/main.jsx
@@ -3,7 +3,7 @@ import ReactDOM from "react-dom/client";
import { App } from "./App.jsx";
import "./index.css";
import { Provider } from 'zustand';
-import useAuthStore from './authStore'; //
+import { useAuthStore } from './components/authStore';
ReactDOM.createRoot(document.getElementById('root')).render(
diff --git a/frontend/src/routes/routes.jsx b/frontend/src/routes/routes.jsx
index e69de29bb..732e79a68 100644
--- a/frontend/src/routes/routes.jsx
+++ b/frontend/src/routes/routes.jsx
@@ -0,0 +1,17 @@
+import { BrowserRouter as Router, Route, Switch } from 'react-router-dom';
+import { RegistrationForm } from '../components/registrationForm';
+import { LoginForm } from '../components/loginForm';
+import { AuthPageContent } from '../components/authPageContent';
+
+
+export const Routes = () => {
+ return (
+
+
+
+
+
+
+
+ );
+};
From 52728d022e3fa8a3a5f4b982027bcf203b88e2d3 Mon Sep 17 00:00:00 2001
From: mirelcac <141237662+mirelcac@users.noreply.github.com>
Date: Thu, 18 Jan 2024 23:14:37 +0100
Subject: [PATCH 09/32] backend fixed
---
backend/.gitignore | 3 +-
backend/db.js | 64 ++++++++++++++++++
backend/package.json | 5 +-
backend/server.js | 110 +++++++------------------------
backend/userLogin/controller.js | 113 ++++++++++++++++++++++++++++++++
backend/userLogin/middleware.js | 30 +++++++++
backend/userLogin/model.js | 52 +++++++++++++++
backend/userLogin/routes.js | 30 +++++++++
8 files changed, 320 insertions(+), 87 deletions(-)
create mode 100644 backend/db.js
create mode 100644 backend/userLogin/controller.js
create mode 100644 backend/userLogin/middleware.js
create mode 100644 backend/userLogin/model.js
create mode 100644 backend/userLogin/routes.js
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/db.js b/backend/db.js
new file mode 100644
index 000000000..54049cb20
--- /dev/null
+++ b/backend/db.js
@@ -0,0 +1,64 @@
+// Configuration file for connecting to MongoDB using Mongoose
+
+// Importing the Mongoose library for MongoDB interactions
+import mongoose from "mongoose";
+
+// Importing the dotenv library to manage environment variables
+import dotenv from "dotenv";
+// Execute the config function to load variables from .env file into process.env
+dotenv.config();
+
+// Configuration options for Mongoose (with deprecated options removed)
+const mongooseOptions = {
+ useNewUrlParser: true, // Use the new URL string parser
+ useUnifiedTopology: true // Use the new Server Discover and Monitoring engine
+};
+
+/**
+ * Asynchronous function to connect to MongoDB.
+ * This function tries to establish a connection with the MongoDB server
+ * using the connection string provided in the .env file.
+ */
+
+// CONNECT TO LOCAL MONGO DB
+export const connectDB = async () => {
+ // Check if the MONGO_URL environment variable is set
+ if (!process.env.MONGO_URL) {
+ console.error("MONGO_URL is not set in .env");
+ // Exit the application if MONGO_URL is not set
+ process.exit(1);
+ }
+
+ try {
+ // Attempting to connect to MongoDB using the provided URL and options
+ const conn = await mongoose.connect(process.env.MONGO_URL, mongooseOptions);
+ // Logging a success message with the connected database host
+ console.log(`Local MongoDB Connected: ${conn.connection.host}`);
+ } catch (error) {
+ // Logging the error if the connection fails
+ console.error("Local MongoDB connection error:", error);
+ // Exiting the application in case of connection failure
+ process.exit(1);
+ }
+};
+
+// CONNECT TO ATLAS DB
+export const connectAtlasDB = async () => {
+ // Check if the ATLAS_MONGO_URL environment variable is set
+ if (!process.env.ATLAS_MONGO_URL) {
+ console.error("ATLAS_MONGO_URL is not set in .env");
+ process.exit(1);
+ }
+
+ try {
+ // Attempting to connect to MongoDB Atlas using the provided URL and options
+ const conn = await mongoose.connect(process.env.ATLAS_MONGO_URL, mongooseOptions);
+ // Logging a success message with the connected database host
+ console.log(`MongoDB Atlas Connected: ${conn.connection.host}`);
+ } catch (error) {
+ // Logging the error if the connection fails
+ console.error("MongoDB Atlas connection error:", error);
+ // Exiting the application in case of connection failure
+ process.exit(1);
+ }
+};
diff --git a/backend/package.json b/backend/package.json
index 6815dbc4d..a82dfd352 100644
--- a/backend/package.json
+++ b/backend/package.json
@@ -13,12 +13,15 @@
"@babel/node": "^7.16.8",
"@babel/preset-env": "^7.16.11",
"bcrypt": "^5.1.1",
+ "bcrypt-nodejs": "^0.0.3",
"cors": "^2.8.5",
"crypto": "^1.0.1",
+ "dotenv": "^16.3.1",
"express": "^4.17.3",
"express-async-handler": "^1.2.0",
"jsonwebtoken": "^9.0.2",
"mongoose": "^8.0.0",
+ "node.js": "^0.0.1-security",
"nodemon": "^3.0.1"
}
-}
+}
\ No newline at end of file
diff --git a/backend/server.js b/backend/server.js
index 51f799e2c..5bbe63fa0 100644
--- a/backend/server.js
+++ b/backend/server.js
@@ -1,101 +1,41 @@
-import express from 'express';
-import cors from 'cors'; // Security mechanism - defines how web pages in one domain interact with resources from another domain
-import mongoose from 'mongoose';
-import crypto from 'crypto';
-import bcrypt from 'bcrypt';
+// Importing necessary libraries and modules
+import express from "express";
+import cors from "cors";
+import dotenv from "dotenv";
+import userRoutes from "./userLogin/routes";
-// CONNECT TO DATABASE
-const mongoUrl = process.env.MONGO_URL || 'mongodb://localhost/auth';
-mongoose.connect(mongoUrl, { useNewUrlParser: true, useUnifiedTopology: true });
-mongoose.Promise = Promise;
+// Import database connection functions
+import { connectDB } from "./db";
+dotenv.config(); // Load and parse environment variables from the .env file
-// 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 || 8081;
-const app = express();
-
-
-// CREATE MONGOOSE MODEL //
-const User = mongoose.model('User', {
- name: {
- type: String,
- unique: true
- },
- email: {
- type: String,
- required: true
- },
- password: {
- type: String,
- required: true
- },
- accessToken: {
- type: String,
- default: () => crypto.randomBytes(128).toString('hex') // Convert to hex desimal value to store in our database
- }
-});
+// Retrieve the port number from environment variables or set default
+const port = process.env.PORT || 3000;
+// Create an Express application instance
+const app = express();
-// MIDDLEWARE //
-// Middleware for authenticating users based on access token
-const authenticateUser = async (req, res, next) => {
- const user = await User.findOne({ accessToken: req.header('Authorization') }); // Find a user in the database using the access token
- if (user) {
- // If a user is found, set the user in the request object and proceed to the next middleware
- req.user = user;
- next();
- } else {
- // If no user is found, respond with a 401 Unauthorized status and a JSON indicating logout
- res.status(401).json({ error: 'Unauthorized access. Please log in.' });
- }
-}
-
-// Middlewares to enable cors and json body parsing
+// Middlewares setup
app.use(cors());
app.use(express.json());
+app.use(express.urlencoded({ extended: false }));
+// Registering API routes with the Express application
+app.use('/', userRoutes);
-// DEFINING ROUTES //
-// Endpoint (edit later)
-app.get('/', (req, res) => {
- res.send('Hello Technigo!');
+// Error handling middleware
+app.use(function (err, req, res, next) {
+ console.error(err.stack);
+ res.status(500).send('Something broke!');
});
+// Connecting to Mongo DB Atlas Instance
+connectDB(); // Connects to MongoDB Atlas
-// Endpoint to create a new user
-app.post('/users', async (req, res) => {
- try {
- const { name, email, password } = req.body // Extract user details from request body
- const user = new User({ name, email, password: bcrypt.hashSync(password) }); // Create a new user with hashed(!) password
- await user.save(); // Save the user to the database
- res.status(201).json({ id: user._id, accessToken: user.accessToken }) // Respond with user details and access token
- } catch (err) {
- res.status(400).json({ message: 'Error creating user. Please check your input and try again.', errors: err.errors }) // Handle errors during user creation
- }
-})
-
-// (!) authenticateUser - Ensures that the '/secrets' endpoint is accessible only to authenticated users by utilizing the authenticateUser middleware.
-// Endpoint Secret message
-app.get('/secrets', authenticateUser, (req, res) => {
- res.json({ secret: 'Secret message!' });
-});
-
-// Endpoint for user authentication (to find the user)
-app.post('/sessions', async (req, res) => {
- // Find a user in the database with the provided email
- const user = await User.findOne({ email: req.body.email });
- // Check if a user is found and if the provided password matches the stored hashed password
- if (user && bcrypt.compareSync(req.body.password, user.password)) {
- res.json({ userId: user._id, accessToken: user.accessToken }); // Respond with the user's ID and access token if authentication is successful
- } else {
- res.status(401).json({ error: 'Invalid email or password. Please try again.' }); // Respond with a JSON indicating that the user was not found
- }
+// Start the server and listen for incoming requests
+app.listen(port, () => {
+ console.log(`Server running on http://localhost:${port}`);
});
-// START THE SERVER //
-app.listen(port, () => {
- console.log(`Server running on http://localhost:${port}`);
-});
diff --git a/backend/userLogin/controller.js b/backend/userLogin/controller.js
new file mode 100644
index 000000000..26c4a6113
--- /dev/null
+++ b/backend/userLogin/controller.js
@@ -0,0 +1,113 @@
+import { UserModel } from "./model";
+import asyncHandler from "express-async-handler";
+import bcrypt from "bcrypt";
+import jwt from "jsonwebtoken";
+
+const generateToken = (id) => {
+ return jwt.sign({ id }, process.env.JWT_SECRET, {
+ expiresIn: '24h' // Token expires in 24 hours
+ });
+};
+
+// FUNCTION FOR USER REGISTRATION
+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
+ const salt = bcrypt.genSaltSync(10);
+
+ const hashedPassword = bcrypt.hashSync(password, salt);
+ // Create a new user instance with the hashed password
+ const newUser = new UserModel({
+ username,
+ email,
+ password: hashedPassword,
+ });
+
+ // Description: Save the new user instance to the database
+ await newUser.save();
+
+ // Generate a JWT token for the new user
+ const token = generateToken(newUser._id);
+
+ // Respond with a success message, user details, and the token
+ res.status(201).json({
+ success: true,
+ response: {
+ username: newUser.username,
+ email: newUser.email,
+ id: newUser._id,
+ token // Send the token to the user
+ },
+ });
+ } catch (e) {
+ // Handle any errors that occur during the registration process
+ res.status(500).json({ success: false, response: e.message });
+ }
+});
+
+
+// FUNCTION FOR USER LOGIN
+export const loginUserController = asyncHandler(async (req, res) => {
+ // Extract username and password from the request body
+ const { username, password } = req.body;
+
+ try {
+ // 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" });
+ }
+ // Generate a JWT token for the user
+ const token = generateToken(user._id);
+
+ // Respond with a success message, user details, and the token
+ res.status(200).json({
+ success: true,
+ response: {
+ username: user.username,
+ id: user._id,
+ token // 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 });
+ }
+});
+
diff --git a/backend/userLogin/middleware.js b/backend/userLogin/middleware.js
new file mode 100644
index 000000000..f8a571557
--- /dev/null
+++ b/backend/userLogin/middleware.js
@@ -0,0 +1,30 @@
+
+import jwt from 'jsonwebtoken';
+import { UserModel } from './model';
+import dotenv from 'dotenv';
+dotenv.config();
+
+export const authenticateUser = async (req, res, next) => {
+ const token = req.header('Authorization')?.split(' ')[1];
+ if (!token) {
+ return res.status(401).json({ success: false, message: 'No token, authorization denied' });
+ }
+
+ try {
+ const decoded = jwt.verify(token, process.env.JWT_SECRET);
+ const user = await UserModel.findById(decoded.id).select('-password');
+
+ if (!user) {
+ return res.status(401).json({ success: false, message: 'Token is not valid' });
+ }
+
+ if (user.role !== 'user') {
+ return res.status(403).json({ success: false, message: 'Access denied: requires user role' });
+ }
+
+ req.user = user;
+ next();
+ } catch (e) {
+ res.status(401).json({ success: false, message: 'Token is not valid' });
+ }
+};
diff --git a/backend/userLogin/model.js b/backend/userLogin/model.js
new file mode 100644
index 000000000..710a36c94
--- /dev/null
+++ b/backend/userLogin/model.js
@@ -0,0 +1,52 @@
+import mongoose from "mongoose";
+
+// 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;
+
+// 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
+ minlength: 5, // Sets a minimum length
+ unique: true, // Make sure the username is unique in the database
+ },
+ email: {
+ type: String,
+ required: true,
+ unique: true,
+ },
+ // Define the 'password' field with a String data type
+ password: {
+ type: String,
+ required: true,
+ minlength: 6,
+ validate: {
+ // Password requirements added
+ validator: function (password) {
+ const hasNumber = /[0-9]/.test(password);
+ const hasCapitalLetter = /[A-Z]/.test(password);
+ const hasSpecialSign = /[!@#\$%\^&\*]/.test(password);
+ return hasNumber && hasCapitalLetter && hasSpecialSign;
+ },
+ message: 'Password must contain at least one number, one capital letter, and one special character.'
+ }
+ },
+ role: {
+ type: String,
+ default: 'user',
+ },
+ },
+ {
+ 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);
+
+
diff --git a/backend/userLogin/routes.js b/backend/userLogin/routes.js
new file mode 100644
index 000000000..837972fcd
--- /dev/null
+++ b/backend/userLogin/routes.js
@@ -0,0 +1,30 @@
+// Import the necessary modules and functions
+import express from "express";
+import { authenticateUser } from "./middleware";
+import {
+ registerUserController,
+ loginUserController,
+} from "./controller"; // 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
+
+// AUTHENTICATED USER ROUTE: Display a secret message
+router.get('/userpage', authenticateUser, (req, res) => {
+ // Send a secret message to the authenticated user
+ res.json({
+ success: true,
+ secretMessage: "This is a secret message only for authenticated users!"
+ });
+});
+
+// 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.
From 742be672325d9ce007c6a50d53085ee7709ae8bd Mon Sep 17 00:00:00 2001
From: mirelcac <141237662+mirelcac@users.noreply.github.com>
Date: Fri, 19 Jan 2024 01:22:16 +0100
Subject: [PATCH 10/32] backend criteria added
---
backend/package.json | 5 +--
backend/userLogin/controller.js | 55 +++++++++++++++++----------------
backend/userLogin/model.js | 16 +++-------
3 files changed, 37 insertions(+), 39 deletions(-)
diff --git a/backend/package.json b/backend/package.json
index a82dfd352..556d6bf04 100644
--- a/backend/package.json
+++ b/backend/package.json
@@ -22,6 +22,7 @@
"jsonwebtoken": "^9.0.2",
"mongoose": "^8.0.0",
"node.js": "^0.0.1-security",
- "nodemon": "^3.0.1"
+ "nodemon": "^3.0.1",
+ "validator": "^13.11.0"
}
-}
\ No newline at end of file
+}
diff --git a/backend/userLogin/controller.js b/backend/userLogin/controller.js
index 26c4a6113..04c7eff46 100644
--- a/backend/userLogin/controller.js
+++ b/backend/userLogin/controller.js
@@ -9,46 +9,45 @@ const generateToken = (id) => {
});
};
+// Password validation function
+const isValidPassword = (password) => {
+ const hasNumber = /[0-9]/.test(password);
+ const hasCapitalLetter = /[A-Z]/.test(password);
+ const hasSpecialSign = /[!@#$%^&*]/.test(password);
+ return hasNumber && hasCapitalLetter && hasSpecialSign && password.length >= 6;
+};
+
// FUNCTION FOR USER REGISTRATION
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
+ // Check whether all fields of registration are inputted
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 }],
- });
+
+ // Check if password meets criteria
+ if (!isValidPassword(password)) {
+ return res.status(400).json({ success: false, message: "Password must contain at least one number, one capital letter, and one special character, and be at least 6 characters long." });
+ }
+
+ // Check if the username or email already exists in the database
+ 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`
- );
+ throw new Error(`User with ${existingUser.username === username ? "username" : "email"} already exists`);
}
// Generate a salt and hash the user's password
const salt = bcrypt.genSaltSync(10);
-
const hashedPassword = bcrypt.hashSync(password, salt);
- // Create a new user instance with the hashed password
- const newUser = new UserModel({
- username,
- email,
- password: hashedPassword,
- });
- // Description: Save the new user instance to the database
- await newUser.save();
+ // Create a new user instance with the hashed password
+ const newUser = new UserModel({ username, email, password: hashedPassword });
+ await newUser.save(); // Save the new user instance to the database
// Generate a JWT token for the new user
const token = generateToken(newUser._id);
@@ -60,11 +59,15 @@ export const registerUserController = asyncHandler(async (req, res) => {
username: newUser.username,
email: newUser.email,
id: newUser._id,
- token // Send the token to the user
- },
+ token
+ }
});
} catch (e) {
- // Handle any errors that occur during the registration process
+ if (e.name === 'ValidationError') {
+ // Handle Mongoose validation errors
+ return res.status(400).json({ success: false, message: e.message });
+ }
+ // Handle other types of errors
res.status(500).json({ success: false, response: e.message });
}
});
diff --git a/backend/userLogin/model.js b/backend/userLogin/model.js
index 710a36c94..9ffbb8442 100644
--- a/backend/userLogin/model.js
+++ b/backend/userLogin/model.js
@@ -1,4 +1,5 @@
import mongoose from "mongoose";
+import validator from 'validator';
// Import the Schema class from the Mongoose library
// Destructures the Schema class from the Mongoose library, allowing us to create a schema.
@@ -16,24 +17,16 @@ const userSchema = new Schema(
},
email: {
type: String,
+ minlength: 4,
required: true,
unique: true,
+ validate: [validator.isEmail, 'Invalid email address']
},
// Define the 'password' field with a String data type
password: {
type: String,
required: true,
- minlength: 6,
- validate: {
- // Password requirements added
- validator: function (password) {
- const hasNumber = /[0-9]/.test(password);
- const hasCapitalLetter = /[A-Z]/.test(password);
- const hasSpecialSign = /[!@#\$%\^&\*]/.test(password);
- return hasNumber && hasCapitalLetter && hasSpecialSign;
- },
- message: 'Password must contain at least one number, one capital letter, and one special character.'
- }
+ minlength: 6
},
role: {
type: String,
@@ -45,6 +38,7 @@ 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);
From ea8b406bd2b01a173ba14825619ec5e3d3a4b0c9 Mon Sep 17 00:00:00 2001
From: mirelcac <141237662+mirelcac@users.noreply.github.com>
Date: Fri, 19 Jan 2024 01:22:41 +0100
Subject: [PATCH 11/32] frontend set up
---
frontend/src/App.jsx | 7 +-
frontend/src/components/authPageContent.jsx | 62 -----------
frontend/src/components/authStore.jsx | 66 ++++++++++--
frontend/src/components/homePage.jsx | 66 ++++++++++++
frontend/src/components/loginForm.jsx | 100 -----------------
frontend/src/components/logoutButton.jsx | 21 ----
frontend/src/components/mainWrapper.jsx | 14 ---
frontend/src/components/registerPage.jsx | 58 ++++++++++
frontend/src/components/registrationForm.jsx | 106 -------------------
frontend/src/components/userPage.jsx | 26 +++++
frontend/src/main.jsx | 25 ++++-
frontend/src/routes/protectRoute.jsx | 15 +++
frontend/src/routes/routes.jsx | 24 ++---
13 files changed, 258 insertions(+), 332 deletions(-)
delete mode 100644 frontend/src/components/authPageContent.jsx
create mode 100644 frontend/src/components/homePage.jsx
delete mode 100644 frontend/src/components/loginForm.jsx
delete mode 100644 frontend/src/components/logoutButton.jsx
delete mode 100644 frontend/src/components/mainWrapper.jsx
create mode 100644 frontend/src/components/registerPage.jsx
delete mode 100644 frontend/src/components/registrationForm.jsx
create mode 100644 frontend/src/components/userPage.jsx
create mode 100644 frontend/src/routes/protectRoute.jsx
diff --git a/frontend/src/App.jsx b/frontend/src/App.jsx
index 360e852cd..168be5d41 100644
--- a/frontend/src/App.jsx
+++ b/frontend/src/App.jsx
@@ -1,13 +1,10 @@
-import { MainWrapper } from '/src/components/mainWrapper';
-import { Routes } from './routes/routes';
+import { AppRoutes } from './routes/routes';
export const App = () => {
return (
-
-
+
);
};
-
diff --git a/frontend/src/components/authPageContent.jsx b/frontend/src/components/authPageContent.jsx
deleted file mode 100644
index fff9c8c14..000000000
--- a/frontend/src/components/authPageContent.jsx
+++ /dev/null
@@ -1,62 +0,0 @@
-// components/AuthPageContent.jsx
-import { useEffect, useState } from 'react';
-import { useAuthStore } from './authStore';
-
-export const AuthPageContent = () => {
- // Access the accessToken from Zustand state
- const { accessToken } = useAuthStore();
-
- // State to store the data fetched from the authenticated endpoint
- const [contentData, setContentData] = useState(null);
-
- // useEffect to run the fetch operation when the component mounts or accessToken changes
- useEffect(() => {
- // Define an asynchronous function to fetch authenticated content
- const fetchAuthenticatedContent = async () => {
- try {
- // Make a GET request to the authenticated endpoint
- const response = await fetch('http://localhost:8081/secrets', {
- headers: {
- Authorization: accessToken,
- },
- });
-
- // Check if the response is successful
- if (response.ok) {
- // Parse the JSON response
- const data = await response.json();
- // Update the state with the fetched data
- setContentData(data);
- } else {
- // Handle errors if the response is not successful
- console.error('Error fetching authenticated content');
- }
- } catch (error) {
- // Handle network or other errors
- console.error('Error:', error.message);
- }
- };
-
- // Check if there is a valid accessToken before making the request
- if (accessToken) {
- // Call the fetchAuthenticatedContent function
- fetchAuthenticatedContent();
- }
- }, [accessToken]); // useEffect will re-run if accessToken changes
-
- return (
-
-
Authenticated Content Page
- {contentData === null ? (
- // Loading state
-
Loading...
- ) : contentData ? (
- // If contentData is available, display the page content
-
{contentData.secret}
- ) : (
- // If contentData is not available, display an error message
-