diff --git a/README.md b/README.md
index 1e4a84a..ebcf613 100644
--- a/README.md
+++ b/README.md
@@ -1 +1,3 @@
-Change example.env to .env
\ No newline at end of file
+Change example.env to .env
+
+>> Join Postman Workspace by following [link](https://app.getpostman.com/join-team?invite_code=2d2ef31819c048b241f82bd7f85ac6dd332460290748fd1f36bee2aedb38200e&target_code=ca1aae0c6efdc09edabb6d99534b9323)
\ No newline at end of file
diff --git a/backend/controllers/auth.controller.js b/backend/controllers/auth.controller.js
index e69de29..374dc80 100644
--- a/backend/controllers/auth.controller.js
+++ b/backend/controllers/auth.controller.js
@@ -0,0 +1,92 @@
+import { User } from "../models/user.model.js";
+import bcryptjs from 'bcryptjs';
+
+import {generateTokenAndSetCookie} from "../utils/generateTokenAndSetCookie.js"
+import { sendVerificationEmail, sendWelcomeEmail } from "../mailtrap/emails.js";
+
+export const signup = async (req, res) => {
+ const { email, password, name } = req.body;
+
+ try {
+ if (!email || !password || !name) {
+ throw new Error("All fields are required");
+ }
+
+ const userAlreadyExists = await User.findOne({ email });
+ console.log("userAlreadyExists", userAlreadyExists);
+
+ if (userAlreadyExists) {
+ return res.status(400).json({ success: false, message: "User already exists" });
+ }
+
+ const hashedPassword = await bcryptjs.hash(password, 10);
+ const verificationToken = Math.floor(100000 + Math.random() * 900000).toString();
+
+ const user = new User({
+ email,
+ password: hashedPassword,
+ name,
+ verificationToken,
+ verificationTokenExpiresAt: Date.now() + 24 * 60 * 60 * 1000, // 24 hours
+ });
+
+ await user.save();
+
+ // jwt
+ generateTokenAndSetCookie(res, user._id);
+ await sendVerificationEmail(user.email, verificationToken);
+
+
+ res.status(201).json({
+ success: true,
+ message: "User created successfully",
+ user: {
+ ...user._doc,
+ password: undefined,
+ },
+ });
+ } catch (error) {
+ res.status(400).json({ success: false, message: error.message });
+ }
+};
+
+export const verifyEmail = async (req, res) => {
+ const { code } = req.body;
+ try {
+ const user = await User.findOne({
+ verificationToken: code,
+ verificationTokenExpiresAt: { $gt: Date.now() },
+ });
+
+ if (!user) {
+ return res.status(400).json({ success: false, message: "Invalid or expired verification code" });
+ }
+
+ user.isVerified = true;
+ user.verificationToken = undefined;
+ user.verificationTokenExpiresAt = undefined;
+ await user.save();
+
+ await sendWelcomeEmail(user.email, user.name);
+
+ res.status(200).json({
+ success: true,
+ message: "Email verified successfully",
+ user: {
+ ...user._doc,
+ password: undefined,
+ },
+ });
+ } catch (error) {
+ console.log("error in verifyEmail ", error);
+ res.status(500).json({ success: false, message: "Server error" });
+ }
+};
+
+
+export const login = async (req, res) => {
+ res.send("login route");
+};
+export const logout = async (req, res) => {
+ res.send("logout route")
+};
\ No newline at end of file
diff --git a/backend/mailtrap/emailTemplates.js b/backend/mailtrap/emailTemplates.js
new file mode 100644
index 0000000..4282bbd
--- /dev/null
+++ b/backend/mailtrap/emailTemplates.js
@@ -0,0 +1,95 @@
+export const VERIFICATION_EMAIL_TEMPLATE = `
+
+
+
+
+
+ Verify Your Email
+
+
+
+
Verify Your Email
+
+
+
Hello,
+
Thank you for signing up! Your verification code is:
+
+ {verificationCode}
+
+
Enter this code on the verification page to complete your registration.
+
This code will expire in 15 minutes for security reasons.
+
If you didn't create an account with us, please ignore this email.
+
Best regards,
Your App Team
+
+
+
This is an automated message, please do not reply to this email.
+
+
+
+`;
+
+export const PASSWORD_RESET_SUCCESS_TEMPLATE = `
+
+
+
+
+
+ Password Reset Successful
+
+
+
+
Password Reset Successful
+
+
+
Hello,
+
We're writing to confirm that your password has been successfully reset.
+
+
If you did not initiate this password reset, please contact our support team immediately.
+
For security reasons, we recommend that you:
+
+ - Use a strong, unique password
+ - Enable two-factor authentication if available
+ - Avoid using the same password across multiple sites
+
+
Thank you for helping us keep your account secure.
+
Best regards,
Your App Team
+
+
+
This is an automated message, please do not reply to this email.
+
+
+
+`;
+
+export const PASSWORD_RESET_REQUEST_TEMPLATE = `
+
+
+
+
+
+ Reset Your Password
+
+
+
+
Password Reset
+
+
+
Hello,
+
We received a request to reset your password. If you didn't make this request, please ignore this email.
+
To reset your password, click the button below:
+
+
This link will expire in 1 hour for security reasons.
+
Best regards,
Your App Team
+
+
+
This is an automated message, please do not reply to this email.
+
+
+
+`;
\ No newline at end of file
diff --git a/backend/mailtrap/emails.js b/backend/mailtrap/emails.js
new file mode 100644
index 0000000..c17f0b0
--- /dev/null
+++ b/backend/mailtrap/emails.js
@@ -0,0 +1,44 @@
+import { VERIFICATION_EMAIL_TEMPLATE } from "./emailTemplates.js"
+import { mailtrapClient, sender} from "./mailtrap.config.js"
+
+export const sendVerificationEmail = async (email, verificationToken) => {
+ const recepient = [{ email }]
+
+ try {
+ const response = await mailtrapClient.send({
+ from: sender,
+ to: recepient,
+ subject: "Verify your email",
+ html: VERIFICATION_EMAIL_TEMPLATE.replace("{verificationCode}", verificationToken),
+ category: "Email Verification"
+ })
+ } catch (error) {
+ console.error(`Error sending verification`, error);
+ throw new Error(`Error sending verification email: ${error}`);
+
+ }
+}
+
+export const sendWelcomeEmail = async (email, name) => {
+ const recipients = [{email}];
+ try {
+ const response = await mailtrapClient.send({
+ from: sender,
+ to: recipients,
+ template_uuid: "eee83ead-5e6d-4784-bd67-c7296f4649b5",
+ template_variables: {
+ "company_info_name": "Ai Laplace Lab",
+ "name": name,
+ "company_info_address": "Rinstraße 19C",
+ "company_info_city": "Schwabhausen",
+ "company_info_zip_code": "85247",
+ "company_info_country": "Germany"
+ },
+ });
+ console.log("Welcome email sent successfully", response);
+ } catch (error) {
+ console.error(`Error sending welcome email`, error);
+
+ throw new Error(`Error sending welcome email: ${error}`);
+ }
+};
\ No newline at end of file
diff --git a/backend/mailtrap/mailtrap.config.js b/backend/mailtrap/mailtrap.config.js
new file mode 100644
index 0000000..1218269
--- /dev/null
+++ b/backend/mailtrap/mailtrap.config.js
@@ -0,0 +1,13 @@
+import { MailtrapClient } from "mailtrap"
+import dotenv from "dotenv";
+
+dotenv.config();
+
+export const mailtrapClient = new MailtrapClient({
+ token: process.env.MAILTRAP_TOKEN,
+});
+
+export const sender = {
+ email: "hello@demomailtrap.co",
+ name: "Mailtrap Test",
+};
\ No newline at end of file
diff --git a/backend/models/user.model.js b/backend/models/user.model.js
index e69de29..d801747 100644
--- a/backend/models/user.model.js
+++ b/backend/models/user.model.js
@@ -0,0 +1,34 @@
+import mongoose from "mongoose";
+
+const userSchema = new mongoose.Schema(
+ {
+ email: {
+ type: String,
+ required: true,
+ unique: true,
+ },
+ password: {
+ type: String,
+ required: true,
+ },
+ name: {
+ type: String,
+ required: true,
+ },
+ lastLogin: {
+ type: Date,
+ default: Date.now,
+ },
+ isVerified: {
+ type: Boolean,
+ default: false,
+ },
+ resetPasswordToken: String,
+ resetPasswordExpiresAt: Date,
+ verificationToken: String,
+ verificationTokenExpiresAt: Date,
+ },
+ { timestamps: true }
+);
+
+export const User = mongoose.model("User", userSchema);
\ No newline at end of file
diff --git a/backend/package-lock.json b/backend/package-lock.json
index a58c586..b487107 100644
--- a/backend/package-lock.json
+++ b/backend/package-lock.json
@@ -9,8 +9,11 @@
"version": "1.0.0",
"license": "ISC",
"dependencies": {
+ "bcryptjs": "^3.0.2",
"dotenv": "^16.5.0",
"express": "^5.1.0",
+ "jsonwebtoken": "^9.0.2",
+ "mailtrap": "^4.1.0",
"mongoose": "^8.14.1",
"nodemon": "^3.1.10"
}
@@ -65,12 +68,38 @@
"node": ">= 8"
}
},
+ "node_modules/asynckit": {
+ "version": "0.4.0",
+ "resolved": "https://registry.npmjs.org/asynckit/-/asynckit-0.4.0.tgz",
+ "integrity": "sha512-Oei9OH4tRh0YqU3GxhX79dM/mwVgvbZJaSNaRk+bshkj0S5cfHcgYakreBjrHwatXKbz+IoIdYLxrKim2MjW0Q==",
+ "license": "MIT"
+ },
+ "node_modules/axios": {
+ "version": "1.9.0",
+ "resolved": "https://registry.npmjs.org/axios/-/axios-1.9.0.tgz",
+ "integrity": "sha512-re4CqKTJaURpzbLHtIi6XpDv20/CnpXOtjRY5/CU32L8gU8ek9UIivcfvSWvmKEngmVbrUtPpdDwWDWL7DNHvg==",
+ "license": "MIT",
+ "dependencies": {
+ "follow-redirects": "^1.15.6",
+ "form-data": "^4.0.0",
+ "proxy-from-env": "^1.1.0"
+ }
+ },
"node_modules/balanced-match": {
"version": "1.0.2",
"resolved": "https://registry.npmjs.org/balanced-match/-/balanced-match-1.0.2.tgz",
"integrity": "sha512-3oSeUO0TMV67hN1AmbXsK4yaqU7tjiHlbxRDZOpH0KW9+CeX4bRAaX0Anxt0tx2MrpRpWwQaPwIlISEJhYU5Pw==",
"license": "MIT"
},
+ "node_modules/bcryptjs": {
+ "version": "3.0.2",
+ "resolved": "https://registry.npmjs.org/bcryptjs/-/bcryptjs-3.0.2.tgz",
+ "integrity": "sha512-k38b3XOZKv60C4E2hVsXTolJWfkGRMbILBIe2IBITXciy5bOsTKot5kDrf3ZfufQtQOUN5mXceUEpU1rTl9Uog==",
+ "license": "BSD-3-Clause",
+ "bin": {
+ "bcrypt": "bin/bcrypt"
+ }
+ },
"node_modules/binary-extensions": {
"version": "2.3.0",
"resolved": "https://registry.npmjs.org/binary-extensions/-/binary-extensions-2.3.0.tgz",
@@ -134,6 +163,12 @@
"node": ">=16.20.1"
}
},
+ "node_modules/buffer-equal-constant-time": {
+ "version": "1.0.1",
+ "resolved": "https://registry.npmjs.org/buffer-equal-constant-time/-/buffer-equal-constant-time-1.0.1.tgz",
+ "integrity": "sha512-zRpUiDwd/xk6ADqPMATG8vc9VPrkck7T07OIx0gnjmJAnHnTVXNQG3vfvWNuiZIkwu9KrKdA1iJKfsfTVxE6NA==",
+ "license": "BSD-3-Clause"
+ },
"node_modules/bytes": {
"version": "3.1.2",
"resolved": "https://registry.npmjs.org/bytes/-/bytes-3.1.2.tgz",
@@ -196,6 +231,18 @@
"fsevents": "~2.3.2"
}
},
+ "node_modules/combined-stream": {
+ "version": "1.0.8",
+ "resolved": "https://registry.npmjs.org/combined-stream/-/combined-stream-1.0.8.tgz",
+ "integrity": "sha512-FQN4MRfuJeHf7cBbBMJFXhKSDq+2kAArBlmRBvcvFE5BB1HZKXtSFASDhdlz9zOYwxh8lDdnvmMOe/+5cdoEdg==",
+ "license": "MIT",
+ "dependencies": {
+ "delayed-stream": "~1.0.0"
+ },
+ "engines": {
+ "node": ">= 0.8"
+ }
+ },
"node_modules/concat-map": {
"version": "0.0.1",
"resolved": "https://registry.npmjs.org/concat-map/-/concat-map-0.0.1.tgz",
@@ -258,6 +305,15 @@
}
}
},
+ "node_modules/delayed-stream": {
+ "version": "1.0.0",
+ "resolved": "https://registry.npmjs.org/delayed-stream/-/delayed-stream-1.0.0.tgz",
+ "integrity": "sha512-ZySD7Nf91aLB0RxL4KGrKHBXl7Eds1DAmEdcoVawXnLD7SDhpNgtuII2aAkg7a7QS41jxPSZ17p4VdGnMHk3MQ==",
+ "license": "MIT",
+ "engines": {
+ "node": ">=0.4.0"
+ }
+ },
"node_modules/depd": {
"version": "2.0.0",
"resolved": "https://registry.npmjs.org/depd/-/depd-2.0.0.tgz",
@@ -293,6 +349,15 @@
"node": ">= 0.4"
}
},
+ "node_modules/ecdsa-sig-formatter": {
+ "version": "1.0.11",
+ "resolved": "https://registry.npmjs.org/ecdsa-sig-formatter/-/ecdsa-sig-formatter-1.0.11.tgz",
+ "integrity": "sha512-nagl3RYrbNv6kQkeJIpt6NJZy8twLB/2vtz6yN9Z4vRKHN4/QZJIEbqohALSgwKdnksuY3k5Addp5lg8sVoVcQ==",
+ "license": "Apache-2.0",
+ "dependencies": {
+ "safe-buffer": "^5.0.1"
+ }
+ },
"node_modules/ee-first": {
"version": "1.1.1",
"resolved": "https://registry.npmjs.org/ee-first/-/ee-first-1.1.1.tgz",
@@ -338,6 +403,21 @@
"node": ">= 0.4"
}
},
+ "node_modules/es-set-tostringtag": {
+ "version": "2.1.0",
+ "resolved": "https://registry.npmjs.org/es-set-tostringtag/-/es-set-tostringtag-2.1.0.tgz",
+ "integrity": "sha512-j6vWzfrGVfyXxge+O0x5sh6cvxAog0a/4Rdd2K36zCMV5eJ+/+tOAngRO8cODMNWbVRdVlmGZQL2YS3yR8bIUA==",
+ "license": "MIT",
+ "dependencies": {
+ "es-errors": "^1.3.0",
+ "get-intrinsic": "^1.2.6",
+ "has-tostringtag": "^1.0.2",
+ "hasown": "^2.0.2"
+ },
+ "engines": {
+ "node": ">= 0.4"
+ }
+ },
"node_modules/escape-html": {
"version": "1.0.3",
"resolved": "https://registry.npmjs.org/escape-html/-/escape-html-1.0.3.tgz",
@@ -424,6 +504,62 @@
"node": ">= 0.8"
}
},
+ "node_modules/follow-redirects": {
+ "version": "1.15.9",
+ "resolved": "https://registry.npmjs.org/follow-redirects/-/follow-redirects-1.15.9.tgz",
+ "integrity": "sha512-gew4GsXizNgdoRyqmyfMHyAmXsZDk6mHkSxZFCzW9gwlbtOW44CDtYavM+y+72qD/Vq2l550kMF52DT8fOLJqQ==",
+ "funding": [
+ {
+ "type": "individual",
+ "url": "https://github.com/sponsors/RubenVerborgh"
+ }
+ ],
+ "license": "MIT",
+ "engines": {
+ "node": ">=4.0"
+ },
+ "peerDependenciesMeta": {
+ "debug": {
+ "optional": true
+ }
+ }
+ },
+ "node_modules/form-data": {
+ "version": "4.0.2",
+ "resolved": "https://registry.npmjs.org/form-data/-/form-data-4.0.2.tgz",
+ "integrity": "sha512-hGfm/slu0ZabnNt4oaRZ6uREyfCj6P4fT/n6A1rGV+Z0VdGXjfOhVUpkn6qVQONHGIFwmveGXyDs75+nr6FM8w==",
+ "license": "MIT",
+ "dependencies": {
+ "asynckit": "^0.4.0",
+ "combined-stream": "^1.0.8",
+ "es-set-tostringtag": "^2.1.0",
+ "mime-types": "^2.1.12"
+ },
+ "engines": {
+ "node": ">= 6"
+ }
+ },
+ "node_modules/form-data/node_modules/mime-db": {
+ "version": "1.52.0",
+ "resolved": "https://registry.npmjs.org/mime-db/-/mime-db-1.52.0.tgz",
+ "integrity": "sha512-sPU4uV7dYlvtWJxwwxHD0PuihVNiE7TyAbQ5SWxDCB9mUYvOgroQOwYQQOKPJ8CIbE+1ETVlOoK1UC2nU3gYvg==",
+ "license": "MIT",
+ "engines": {
+ "node": ">= 0.6"
+ }
+ },
+ "node_modules/form-data/node_modules/mime-types": {
+ "version": "2.1.35",
+ "resolved": "https://registry.npmjs.org/mime-types/-/mime-types-2.1.35.tgz",
+ "integrity": "sha512-ZDY+bPm5zTTF+YpCrAU9nK0UgICYPT0QtT1NZWFv4s++TNkcgVaT0g6+4R2uI4MjQjzysHB1zxuWL50hzaeXiw==",
+ "license": "MIT",
+ "dependencies": {
+ "mime-db": "1.52.0"
+ },
+ "engines": {
+ "node": ">= 0.6"
+ }
+ },
"node_modules/forwarded": {
"version": "0.2.0",
"resolved": "https://registry.npmjs.org/forwarded/-/forwarded-0.2.0.tgz",
@@ -547,6 +683,21 @@
"url": "https://github.com/sponsors/ljharb"
}
},
+ "node_modules/has-tostringtag": {
+ "version": "1.0.2",
+ "resolved": "https://registry.npmjs.org/has-tostringtag/-/has-tostringtag-1.0.2.tgz",
+ "integrity": "sha512-NqADB8VjPFLM2V0VvHUewwwsw0ZWBaIdgo+ieHtK3hasLz4qeCRjYcqfB6AQrBggRKppKF8L52/VqdVsO47Dlw==",
+ "license": "MIT",
+ "dependencies": {
+ "has-symbols": "^1.0.3"
+ },
+ "engines": {
+ "node": ">= 0.4"
+ },
+ "funding": {
+ "url": "https://github.com/sponsors/ljharb"
+ }
+ },
"node_modules/hasown": {
"version": "2.0.2",
"resolved": "https://registry.npmjs.org/hasown/-/hasown-2.0.2.tgz",
@@ -656,6 +807,49 @@
"integrity": "sha512-hvpoI6korhJMnej285dSg6nu1+e6uxs7zG3BYAm5byqDsgJNWwxzM6z6iZiAgQR4TJ30JmBTOwqZUw3WlyH3AQ==",
"license": "MIT"
},
+ "node_modules/jsonwebtoken": {
+ "version": "9.0.2",
+ "resolved": "https://registry.npmjs.org/jsonwebtoken/-/jsonwebtoken-9.0.2.tgz",
+ "integrity": "sha512-PRp66vJ865SSqOlgqS8hujT5U4AOgMfhrwYIuIhfKaoSCZcirrmASQr8CX7cUg+RMih+hgznrjp99o+W4pJLHQ==",
+ "license": "MIT",
+ "dependencies": {
+ "jws": "^3.2.2",
+ "lodash.includes": "^4.3.0",
+ "lodash.isboolean": "^3.0.3",
+ "lodash.isinteger": "^4.0.4",
+ "lodash.isnumber": "^3.0.3",
+ "lodash.isplainobject": "^4.0.6",
+ "lodash.isstring": "^4.0.1",
+ "lodash.once": "^4.0.0",
+ "ms": "^2.1.1",
+ "semver": "^7.5.4"
+ },
+ "engines": {
+ "node": ">=12",
+ "npm": ">=6"
+ }
+ },
+ "node_modules/jwa": {
+ "version": "1.4.1",
+ "resolved": "https://registry.npmjs.org/jwa/-/jwa-1.4.1.tgz",
+ "integrity": "sha512-qiLX/xhEEFKUAJ6FiBMbes3w9ATzyk5W7Hvzpa/SLYdxNtng+gcurvrI7TbACjIXlsJyr05/S1oUhZrc63evQA==",
+ "license": "MIT",
+ "dependencies": {
+ "buffer-equal-constant-time": "1.0.1",
+ "ecdsa-sig-formatter": "1.0.11",
+ "safe-buffer": "^5.0.1"
+ }
+ },
+ "node_modules/jws": {
+ "version": "3.2.2",
+ "resolved": "https://registry.npmjs.org/jws/-/jws-3.2.2.tgz",
+ "integrity": "sha512-YHlZCB6lMTllWDtSPHz/ZXTsi8S00usEV6v1tjq8tOUZzw7DpSDWVXjXDre6ed1w/pd495ODpHZYSdkRTsa0HA==",
+ "license": "MIT",
+ "dependencies": {
+ "jwa": "^1.4.1",
+ "safe-buffer": "^5.0.1"
+ }
+ },
"node_modules/kareem": {
"version": "2.6.3",
"resolved": "https://registry.npmjs.org/kareem/-/kareem-2.6.3.tgz",
@@ -665,6 +859,73 @@
"node": ">=12.0.0"
}
},
+ "node_modules/lodash.includes": {
+ "version": "4.3.0",
+ "resolved": "https://registry.npmjs.org/lodash.includes/-/lodash.includes-4.3.0.tgz",
+ "integrity": "sha512-W3Bx6mdkRTGtlJISOvVD/lbqjTlPPUDTMnlXZFnVwi9NKJ6tiAk6LVdlhZMm17VZisqhKcgzpO5Wz91PCt5b0w==",
+ "license": "MIT"
+ },
+ "node_modules/lodash.isboolean": {
+ "version": "3.0.3",
+ "resolved": "https://registry.npmjs.org/lodash.isboolean/-/lodash.isboolean-3.0.3.tgz",
+ "integrity": "sha512-Bz5mupy2SVbPHURB98VAcw+aHh4vRV5IPNhILUCsOzRmsTmSQ17jIuqopAentWoehktxGd9e/hbIXq980/1QJg==",
+ "license": "MIT"
+ },
+ "node_modules/lodash.isinteger": {
+ "version": "4.0.4",
+ "resolved": "https://registry.npmjs.org/lodash.isinteger/-/lodash.isinteger-4.0.4.tgz",
+ "integrity": "sha512-DBwtEWN2caHQ9/imiNeEA5ys1JoRtRfY3d7V9wkqtbycnAmTvRRmbHKDV4a0EYc678/dia0jrte4tjYwVBaZUA==",
+ "license": "MIT"
+ },
+ "node_modules/lodash.isnumber": {
+ "version": "3.0.3",
+ "resolved": "https://registry.npmjs.org/lodash.isnumber/-/lodash.isnumber-3.0.3.tgz",
+ "integrity": "sha512-QYqzpfwO3/CWf3XP+Z+tkQsfaLL/EnUlXWVkIk5FUPc4sBdTehEqZONuyRt2P67PXAk+NXmTBcc97zw9t1FQrw==",
+ "license": "MIT"
+ },
+ "node_modules/lodash.isplainobject": {
+ "version": "4.0.6",
+ "resolved": "https://registry.npmjs.org/lodash.isplainobject/-/lodash.isplainobject-4.0.6.tgz",
+ "integrity": "sha512-oSXzaWypCMHkPC3NvBEaPHf0KsA5mvPrOPgQWDsbg8n7orZ290M0BmC/jgRZ4vcJ6DTAhjrsSYgdsW/F+MFOBA==",
+ "license": "MIT"
+ },
+ "node_modules/lodash.isstring": {
+ "version": "4.0.1",
+ "resolved": "https://registry.npmjs.org/lodash.isstring/-/lodash.isstring-4.0.1.tgz",
+ "integrity": "sha512-0wJxfxH1wgO3GrbuP+dTTk7op+6L41QCXbGINEmD+ny/G/eCqGzxyCsh7159S+mgDDcoarnBw6PC1PS5+wUGgw==",
+ "license": "MIT"
+ },
+ "node_modules/lodash.once": {
+ "version": "4.1.1",
+ "resolved": "https://registry.npmjs.org/lodash.once/-/lodash.once-4.1.1.tgz",
+ "integrity": "sha512-Sb487aTOCr9drQVL8pIxOzVhafOjZN9UU54hiN8PU3uAiSV7lx1yYNpbNmex2PK6dSJoNTSJUUswT651yww3Mg==",
+ "license": "MIT"
+ },
+ "node_modules/mailtrap": {
+ "version": "4.1.0",
+ "resolved": "https://registry.npmjs.org/mailtrap/-/mailtrap-4.1.0.tgz",
+ "integrity": "sha512-rCuumv0ZcLvxMukV8Pn9sh5hmk2TL23THrpwgE/yXwDfeJQdJGVENa2rNzGR9zcGsUK3LSXuyvjKrTwc21RBOQ==",
+ "license": "MIT",
+ "dependencies": {
+ "axios": ">=0.27"
+ },
+ "engines": {
+ "node": ">=16.20.1",
+ "yarn": ">=1.22.17"
+ },
+ "peerDependencies": {
+ "@types/nodemailer": "^6.4.9",
+ "nodemailer": "^6.9.4"
+ },
+ "peerDependenciesMeta": {
+ "@types/nodemailer": {
+ "optional": true
+ },
+ "nodemailer": {
+ "optional": true
+ }
+ }
+ },
"node_modules/math-intrinsics": {
"version": "1.1.0",
"resolved": "https://registry.npmjs.org/math-intrinsics/-/math-intrinsics-1.1.0.tgz",
@@ -961,6 +1222,12 @@
"node": ">= 0.10"
}
},
+ "node_modules/proxy-from-env": {
+ "version": "1.1.0",
+ "resolved": "https://registry.npmjs.org/proxy-from-env/-/proxy-from-env-1.1.0.tgz",
+ "integrity": "sha512-D+zkORCbA9f1tdWRK0RaCR3GPv50cMxcrz4X8k5LTSUD1Dkw47mKJEZQNunItRTkWwgtaUSo1RVFRIG9ZXiFYg==",
+ "license": "MIT"
+ },
"node_modules/pstree.remy": {
"version": "1.1.8",
"resolved": "https://registry.npmjs.org/pstree.remy/-/pstree.remy-1.1.8.tgz",
diff --git a/backend/package.json b/backend/package.json
index 60634a9..13d5372 100644
--- a/backend/package.json
+++ b/backend/package.json
@@ -13,8 +13,11 @@
"license": "ISC",
"description": "",
"dependencies": {
+ "bcryptjs": "^3.0.2",
"dotenv": "^16.5.0",
"express": "^5.1.0",
+ "jsonwebtoken": "^9.0.2",
+ "mailtrap": "^4.1.0",
"mongoose": "^8.14.1",
"nodemon": "^3.1.10"
}
diff --git a/backend/routes/auth.route.js b/backend/routes/auth.route.js
index 217744c..90d1988 100644
--- a/backend/routes/auth.route.js
+++ b/backend/routes/auth.route.js
@@ -1,18 +1,13 @@
import express from "express";
-
+import {login, signup, logout, verifyEmail} from "../controllers/auth.controller.js";
const router = express.Router();
-router.get("/signup", (req, res) => {
- res.send("Signup route");
-})
+router.post("/signup", signup);
+router.post("/login", login);
+router.post("/logout", logout);
-router.get("/login", (req, res) => {
- res.send("Signup route");
-})
+router.post("/verify-email", verifyEmail);
-router.get("/logout", (req, res) => {
- res.send("Signup route");
-})
export default router;
diff --git a/backend/server.js b/backend/server.js
index 0d33c72..d1e07a4 100644
--- a/backend/server.js
+++ b/backend/server.js
@@ -1,22 +1,18 @@
import express from "express";
import dotenv from "dotenv";
-import {connectDB} from "./db/connectDB.js";
+import { connectDB } from "./db/connectDB.js";
-import authRoutes from "./routes/auth.route.js"
+import authRoutes from "./routes/auth.route.js";
dotenv.config();
const app = express();
-
-app.get("/", (req, res) => {
- res.send("Hello World!");
- });
-
-
- app.use("/api/auth", authRoutes)
+const PORT = process.env.PORT || 8080;
+app.use(express.json()); // allows us to parse incoming requests:req.body
+app.use("/api/auth", authRoutes);
-app.listen(8080, () => {
+app.listen(PORT, () => {
connectDB();
- console.log("Server Started at 0.0.0.0:8080");
+ console.log("Server Started at 0.0.0.0: ", PORT);
});
diff --git a/backend/utils/generateTokenAndSetCookie.js b/backend/utils/generateTokenAndSetCookie.js
new file mode 100644
index 0000000..8ef2b37
--- /dev/null
+++ b/backend/utils/generateTokenAndSetCookie.js
@@ -0,0 +1,16 @@
+import jwt from "jsonwebtoken";
+
+export const generateTokenAndSetCookie = (res, userId) => {
+ const token = jwt.sign({ userId}, process.env.JWT_SECRET, {
+ expiresIn: "7d",
+ });
+
+ res.cookie("token", token, {
+ httpOnly: true,
+ secure: process.env.NODE_ENV === "production",
+ sameSite: "strict",
+ maxAge: 7 * 24 * 60 * 60 * 1000,
+ });
+
+ return token;
+}
\ No newline at end of file
diff --git a/docker-compose.mongo.yaml b/docker-compose.mongo.yaml
new file mode 100644
index 0000000..f5b262a
--- /dev/null
+++ b/docker-compose.mongo.yaml
@@ -0,0 +1,10 @@
+services:
+
+ mongo:
+ image: mongo
+ restart: always
+ ports:
+ - "27017:27017"
+ environment:
+ MONGO_INITDB_ROOT_USERNAME: admin
+ MONGO_INITDB_ROOT_PASSWORD: test
\ No newline at end of file