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/authenticateUser.js b/backend/authenticateUser.js new file mode 100644 index 000000000..753250ea4 --- /dev/null +++ b/backend/authenticateUser.js @@ -0,0 +1,21 @@ +import UserModel from "./models/userModel.js"; + +export const authenticateUser = async (req, res, next) => { + const accessToken = req.header("Authorization"); + + if ( + req.headers.authorization && + req.headers.authorization.startsWith("Bearer") + ) + try { + const user = await UserModel.findOne({ accessToken: accessToken }); + if (user) { + req.user = user; + next(); + } else { + res.status(401).json({ success: false, response: "please log in" }); + } + } catch (e) { + res.status(500).json({ success: false, response: e.message }); + } +}; diff --git a/backend/models/userModel.js b/backend/models/userModel.js new file mode 100644 index 000000000..02b66ea09 --- /dev/null +++ b/backend/models/userModel.js @@ -0,0 +1,37 @@ +import mongoose from "mongoose"; +import crypto from "crypto"; + +const { Schema } = mongoose; + +const userSchema = new Schema( + { + username: { + type: String, + required: true, + unique: true, + minlength: 2, // corrected typo: minlenght -> minlength + }, + password: { + type: String, + required: true, + minlength: 6, + }, + email: { + type: String, + required: true, + unique: true, + }, + accessToken: { + type: String, + default: () => crypto.randomBytes(128).toString("hex"), + required: false, + }, + }, + { + timestamps: true, + } +); + +const UserModel = mongoose.model("User", userSchema); + +export default UserModel; diff --git a/backend/package.json b/backend/package.json index 8de5c4ce0..a42b3c455 100644 --- a/backend/package.json +++ b/backend/package.json @@ -4,17 +4,25 @@ "description": "Starter project to get up and running with express quickly", "scripts": { "start": "babel-node server.js", - "dev": "nodemon server.js --exec babel-node" + "dev": "nodemon server.js --exec babel-node", + "build": "babel src -d dist" }, "author": "", "license": "ISC", + "type": "module", "dependencies": { "@babel/core": "^7.17.9", "@babel/node": "^7.16.8", "@babel/preset-env": "^7.16.11", + "bcrypt": "^5.0.0", "cors": "^2.8.5", + "dotenv": "^16.4.5", "express": "^4.17.3", - "mongoose": "^8.0.0", + "express-async-handler": "^1.2.0", + "express-list-endpoints": "^7.1.0", + "jsonwebtoken": "^9.0.2", + "mongodb": "^6.5.0", + "mongoose": "^8.3.2", "nodemon": "^3.0.1" } } diff --git a/backend/routes/userRoutes.js b/backend/routes/userRoutes.js new file mode 100644 index 000000000..89ab9943a --- /dev/null +++ b/backend/routes/userRoutes.js @@ -0,0 +1,244 @@ +import express from "express"; +import bcrypt from "bcrypt"; +import jwt from "jsonwebtoken"; + +import UserModel from "../models/userModel.js"; // Make sure to import your user model here + +import asyncHandler from "express-async-handler"; +import dotenv from "dotenv"; +import { authenticateUser } from "../authenticateUser.js"; // If you have an authentication middleware, import it here + +dotenv.config(); + +const router = express.Router(); + +const generateToken = (user) => { + // Function to generate JWT token + return jwt.sign({ id: user._id }, process.env.JWT_SECRET, { + expiresIn: "24h", + }); +}; + +// Route for user registration +router.post( + "/register", + asyncHandler(async (req, res) => { + const { username, password, email } = req.body; + try { + // Check if all required fields are provided + if (!username || !password || !email) { + res.status(400); + throw new Error("Please provide all fields"); + } + // Check if user with the same username or email already exists + 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` + ); + } + // Hash the password + const salt = bcrypt.genSaltSync(10); + const hashedPassword = bcrypt.hashSync(password, salt); + // Create a new user + const newUser = new UserModel({ + username, + email, + password: hashedPassword, + }); + await newUser.save(); + // Respond with success message and token + res.status(201).json({ + success: true, + response: { + username: newUser.username, + email: newUser.email, + id: newUser._id, + accessToken: generateToken(newUser), + }, + }); + } catch (e) { + // Handle any errors + res.status(500).json({ success: false, response: e.message }); + } + }) +); + +// Route for user login +router.post( + "/login", + asyncHandler(async (req, res) => { + const { username, password } = req.body; + try { + // Find user by username + const user = await UserModel.findOne({ username }); + if (!user) { + return res + .status(401) + .json({ success: false, response: "User not found" }); + } + // Compare passwords + const isMatch = await bcrypt.compare(password, user.password); + if (!isMatch) { + return res + .status(401) + .json({ success: false, response: "Incorrect password" }); + } + // Respond with success message and token + res.status(200).json({ + success: true, + response: { + username: user.username, + email: user.email, + id: user._id, + accessToken: generateToken(user), + }, + }); + } catch (error) { + // Handle any errors + res.status(500).json({ + success: false, + response: { + message: error.message, + }, + }); + } + }) +); + +// Sample route accessible only to logged-in users +router.get( + "/logged-in", + authenticateUser, // Authentication middleware to verify JWT token + asyncHandler(async (req, res) => { + res.status(200).json({ + success: true, + response: { + message: "User is logged in", + }, + }); + }) +); + +export default router; + +/*import express from "express"; +import bcrypt from "bcrypt"; +import jwt from "jsonwebtoken"; + +import UserModel from "../models/userModel.js"; + +import asyncHandler from "express-async-handler"; +import dotenv from "dotenv"; +import { authenticateUser } from "../authenticateUser.js"; + +dotenv.config(); + +const router = express.Router(); + +const generateToken = (user) => { + return jwt.sign({ id: user._id }, process.env.JWT_SECRET, { + expiresIn: "24h", + }); +}; + +router.post( + "/register", + asyncHandler(async (req, res) => { + const { username, password, email } = req.body; + try { + if (!username || !password || !email) { + res.status(400); + throw new Error("please add all fields"); + } + 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` + ); + } + const salt = bcrypt.genSaltSync(10); + const hashedPassword = bcrypt.hashSync(password, salt); + const newUser = new UserModel({ + username, + email, + password: hashedPassword, + }); + await newUser.save(); + res.status(201).json({ + success: true, + response: { + username: newUser.username, + email: newUser.email, + id: newUser._id, + accessToken: geneerateToken(newUser), + }, + }); + } catch (e) { + res.status(500).json({ success: false, response: e.message }); + } + }) +); + +router.post( + "/login", + asyncHandler(async (req, res) => { + const { username, password } = req.body; + try { + const user = await UserModel.findOne({ username }); + if (!user) { + return res + .status(401) + .json({ success: false, response: "user not found" }); + } + const isMatch = await bcrypt.compare(password, user.password); + if (!isMatch) { + return res + .status(401) + .json({ success: false, response: "incorrect password" }); + } + res.status(200).json({ + success: true, + response: { + username: user.username, + email: user.email, + id: user._id, + accessToken: geneerateToken(user), + }, + }); + } catch (error) { + res.status(500).json({ + success: false, + response: { + message: error.message, + }, + }); + } + }) +); + +router.get( + "/logged-in", + authenticateUser, + asyncHandler(async (req, res) => { + res.status(200).json({ + success: true, + response: { + message: "user is logged in", + }, + }); + }) +); + +export default router; +*/ diff --git a/backend/server.js b/backend/server.js index 2d7ae8aa1..35049bfca 100644 --- a/backend/server.js +++ b/backend/server.js @@ -1,27 +1,44 @@ import express from "express"; +import expressListEndpoints from "express-list-endpoints"; import cors from "cors"; import mongoose from "mongoose"; +import dotenv from "dotenv"; +import userRoutes from "./routes/userRoutes.js"; -const mongoUrl = process.env.MONGO_URL || "mongodb://localhost/project-mongo"; -mongoose.connect(mongoUrl, { useNewUrlParser: true, useUnifiedTopology: true }); -mongoose.Promise = Promise; +dotenv.config(); -// 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(); +const port = process.env.PORT || 9090; -// Add middlewares to enable cors and json body parsing +// Middleware app.use(cors()); app.use(express.json()); +app.use(express.urlencoded({ extended: true })); +app.use(userRoutes); -// Start defining your routes here +// Routes app.get("/", (req, res) => { - res.send("Hello Technigo!"); + const endpoints = expressListEndpoints(app); + res.json(endpoints); + console.log("List of Endpoints:"); + console.log(endpoints); }); -// Start the server -app.listen(port, () => { - console.log(`Server running on http://localhost:${port}`); -}); +// Database connection + +mongoose + .connect(process.env.MONGODB_URI, { + //useNewUrlParser: true, + //useUnifiedTopology: true, + }) + .then(() => { + console.log("Connected to MongoDB Atlas"); + // Start the server after successfully connecting to the database + app.listen(port, () => { + console.log(`Server running on http://localhost:${port}`); + }); + }) + .catch((error) => { + console.error("Error connecting to MongoDB Atlas:", error); + process.exit(1); + }); diff --git a/bash.exe.stackdump b/bash.exe.stackdump new file mode 100644 index 000000000..15faa3ea6 --- /dev/null +++ b/bash.exe.stackdump @@ -0,0 +1,41 @@ +Stack trace: +Frame Function Args +0007FFFFCD30 00021006118E (0002102A6010, 000210272B51, 00000000005C, 0007FFFFB6E0) msys-2.0.dll+0x2118E +0007FFFFCD30 0002100469BA (0007FFFFC790, 000200000000, 000700000000, 000700000001) msys-2.0.dll+0x69BA +0007FFFFCD30 0002100469F2 (000000000000, 000000000000, 00000000005C, 000700000003) msys-2.0.dll+0x69F2 +0007FFFFCD30 000210101C61 (000210177A63, 000800007568, 000000000000, 0002102752E0) msys-2.0.dll+0xC1C61 +0007FFFFCD30 000210101C79 (0002101778B0, 0007FFFFC8F8, 000000000004, 0007FFFFC910) msys-2.0.dll+0xC1C79 +0007FFFFCD30 000210104644 (0007FFFFCD30, 000000000000, 0007FFFFC910, 0002102752E0) msys-2.0.dll+0xC4644 +0007FFFFCD30 000210059095 (000000000004, 000000000004, 0007FFFFCA68, 0000FFFFFFFF) msys-2.0.dll+0x19095 +0007FFFFCD30 000210059865 (000800000000, 000800006F28, 000000000000, 000000030201) msys-2.0.dll+0x19865 +0007FFFFCD30 000210059D77 (00021017DA35, 000000000000, 000000000000, 000000000000) msys-2.0.dll+0x19D77 +0007FFFFCD30 00021005A0B6 (000000000000, 0007FFFFCD30, FFFFFFFFFFFFFFC6, 000700000000) msys-2.0.dll+0x1A0B6 +0007FFFFCD30 000210047151 (000000000000, 000000000000, 000000000000, 000000000000) msys-2.0.dll+0x7151 +0007FFFFFFF0 000210045C86 (000000000000, 000000000000, 000000000000, 000000000000) msys-2.0.dll+0x5C86 +0007FFFFFFF0 000210045D34 (000000000000, 000000000000, 000000000000, 000000000000) msys-2.0.dll+0x5D34 +End of stack trace +Loaded modules: +000100400000 bash.exe +7FF997AF0000 ntdll.dll +7FF9967C0000 KERNEL32.DLL +7FF9950C0000 KERNELBASE.dll +7FF991C30000 apphelp.dll +7FF996B40000 USER32.dll +7FF9954A0000 win32u.dll +7FF995AA0000 GDI32.dll +7FF9956B0000 gdi32full.dll +7FF994EB0000 msvcp_win.dll +7FF995590000 ucrtbase.dll +000210040000 msys-2.0.dll +7FF996890000 advapi32.dll +7FF9959F0000 msvcrt.dll +7FF995940000 sechost.dll +7FF995470000 bcrypt.dll +7FF995E10000 RPCRT4.dll +7FF994560000 CRYPTBASE.DLL +7FF994E30000 bcryptPrimitives.dll +7FF995F30000 IMM32.DLL +7FF980520000 netapi32.dll +7FF980500000 SAMCLI.DLL +7FF9888E0000 SAMLIB.dll +7FF9938E0000 NETUTILS.DLL diff --git a/frontend/.gitignore b/frontend/.gitignore index 265f50c92..6254213ab 100644 --- a/frontend/.gitignore +++ b/frontend/.gitignore @@ -23,4 +23,5 @@ dist-ssr *.sln *.sw? -package-lock.json \ No newline at end of file +package-lock.json +.env \ No newline at end of file diff --git a/frontend/index.html b/frontend/index.html index 0c589eccd..79c470191 100644 --- a/frontend/index.html +++ b/frontend/index.html @@ -1,4 +1,4 @@ - +
diff --git a/frontend/package.json b/frontend/package.json index e9c95b79f..ea51431de 100644 --- a/frontend/package.json +++ b/frontend/package.json @@ -10,13 +10,19 @@ "preview": "vite preview" }, "dependencies": { + "axios": "^1.6.8", + "lottie-react": "^2.4.0", "react": "^18.2.0", - "react-dom": "^18.2.0" + "react-dom": "^18.2.0", + "react-router-dom": "^6.23.0", + "styled-components": "^6.1.8", + "zustand": "^4.5.2" }, "devDependencies": { - "@types/react": "^18.2.15", + "@types/react": "^18.2.79", "@types/react-dom": "^18.2.7", "@vitejs/plugin-react": "^4.0.3", + "@vitejs/plugin-react-refresh": "^1.3.6", "eslint": "^8.45.0", "eslint-plugin-react": "^7.32.2", "eslint-plugin-react-hooks": "^4.6.0", diff --git a/frontend/src/App.jsx b/frontend/src/App.jsx index 1091d4310..2cd115875 100644 --- a/frontend/src/App.jsx +++ b/frontend/src/App.jsx @@ -1,3 +1,17 @@ +// eslint-disable-next-line no-unused-vars +import React from "react"; +import { BrowserRouter, Routes } from "react-router-dom"; +import { routes } from "./routes/RoutesComp"; +import "./index.css"; + export const App = () => { - returnYAY! You are logged in!
+
+ Congratulations! You are logged in. Here is a cute puppy for you!
+Please login:
+No account?
+Please sign up:
+