Skip to content

Commit

Permalink
Merge branch 'main' into password
Browse files Browse the repository at this point in the history
  • Loading branch information
kushalkumar1362 authored May 31, 2024
2 parents 76a4766 + a95b808 commit d1d9f8f
Show file tree
Hide file tree
Showing 14 changed files with 784 additions and 209 deletions.
289 changes: 289 additions & 0 deletions backend/package-lock.json

Large diffs are not rendered by default.

3 changes: 3 additions & 0 deletions backend/package.json
Original file line number Diff line number Diff line change
Expand Up @@ -21,9 +21,12 @@
"bcrypt": "^5.1.1",
"body-parser": "^1.20.2",
"cors": "^2.8.5",
"crypto": "^1.0.1",
"dotenv": "^16.4.5",
"express": "^4.19.2",
"jsonwebtoken": "^9.0.2",
"nodemailer": "^6.9.13",
"nodemon": "^3.1.1",
"prisma": "^5.14.0",
"ts-node": "^10.9.2",
"typescript": "^5.4.5",
Expand Down
1 change: 1 addition & 0 deletions backend/prisma/schema.prisma
Original file line number Diff line number Diff line change
Expand Up @@ -18,6 +18,7 @@ model User {
username String
email String
passwordHash String
otp Int?
posts Post[] @relation("authorPosts")
createdAt DateTime @default(now())
updatedAt DateTime @updatedAt
Expand Down
93 changes: 93 additions & 0 deletions backend/src/helpers/sendMail.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,93 @@
// import nodemailer from 'nodemailer';
// @ts-nocheck
import nodemailer from "nodemailer";
// nodemailer = require('nodemailer');

export const sendVerificationEmail = async (email: string, otp: number) => {
// console.log("Email: ", process.env.EMAIL_USER, process.env.EMAIL_PASS)
let transporter = nodemailer.createTransport({
service: 'Gmail',
auth: {
user: process.env.EMAIL_USER,
pass: process.env.EMAIL_PASS,
},
});

let info = await transporter.sendMail({
from: '"Style Share" <yourapp@example.com>',
to: email,
subject: "Email Verification",
text: `Your OTP for email verification is ${otp}`,
// This html is a simple html template for the email body
html: `
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<title>OTP Verification</title>
<style>
body {
font-family: Arial, sans-serif;
background-color: #f4f4f4;
margin: 0;
padding: 0;
}
.container {
width: 100%;
max-width: 600px;
margin: 0 auto;
background-color: #ffffff;
padding: 20px;
box-shadow: 0 0 10px rgba(0, 0, 0, 0.1);
border-radius: 8px;
}
.header {
background-color: #4CAF50;
color: white;
padding: 10px 0;
text-align: center;
border-radius: 8px 8px 0 0;
}
.content {
margin: 20px 0;
text-align: center;
}
.otp {
font-size: 24px;
font-weight: bold;
margin: 20px 0;
color: #4CAF50;
}
.footer {
text-align: center;
color: #888888;
font-size: 12px;
margin-top: 20px;
}
</style>
</head>
<body>
<div class="container">
<div class="header">
<h1>OTP Verification</h1>
</div>
<div class="content">
<p>Hello,</p>
<p>Thank you for signing up. To complete your registration, please use the following OTP (One Time Password) to verify your email address:</p>
<div class="otp">${otp}</div>
<p>This OTP is valid for 10 minutes. Please do not share it with anyone.</p>
</div>
<div class="footer">
<p>If you did not request this OTP, please ignore this email.</p>
<p>Thank you!</p>
</div>
</div>
</body>
</html>
`
});

// console.log("Message sent: %s", info.messageId);
};
1 change: 1 addition & 0 deletions backend/src/middleware/auth.ts
Original file line number Diff line number Diff line change
Expand Up @@ -28,6 +28,7 @@ const authMiddleware = (req:UserAuthRequest, res:Response, next: NextFunction) =
}

req.userId = decodedValue.id;
// console.log("IN JWT:'",req.userId)
next();
}

Expand Down
163 changes: 143 additions & 20 deletions backend/src/routes/user/controller.ts
Original file line number Diff line number Diff line change
Expand Up @@ -4,6 +4,9 @@ import { signinBodySchema, signupBodySchema } from "./zodSchema";
import { createHash, validatePassword } from "../../helpers/hash";
import { createJWT } from "../../helpers/jwt";
import { UserAuthRequest } from "../../helpers/types";
import crypto from "crypto";
import { sendVerificationEmail } from "../../helpers/sendMail";
import { z } from "zod";

export const userSignupController = async (req: Request, res: Response) => {
try {
Expand Down Expand Up @@ -36,47 +39,157 @@ export const userSignupController = async (req: Request, res: Response) => {
});

if (existingUser) {
return res.status(411).json({
error: {
message: "Username or email already in use.",
if (!existingUser.otp) {
return res.status(411).json({
error: {
message: "Username or email already in use.",
},
});

}
}



const passwordHash = await createHash(data.password);

const otp = crypto.randomInt(100000, 999999); // Generate a 6-digit OTP

let user;
if(!existingUser){
user = await prisma.user.create({
data: {
email: data.email,
passwordHash,
username: data.username,
// @ts-ignore
otp,
},
select: {
id: true,
email: true,
username: true,
},
});
}
else if(existingUser && existingUser.otp){
user = await prisma.user.update({
where: {
id: existingUser.id
},
data: {
otp: otp,
passwordHash,
username: data.username,
},
select: {
id: true,
email: true,
username: true,
}
})
}


// Send OTP to user's email
await sendVerificationEmail(user!.email, otp);

const passwordHash = await createHash(data.password);


const user = await prisma.user.create({
data: {
email: data.email,
passwordHash,
username: data.username,
res.status(201).json({
message: "User created Successfully.",
user,
});
} catch (error) {
console.log(error)
return res.status(500).json({
error: {
message: "An unexpected exception occurred! Brooo",
display: error
},
});
}
};

// import prisma from "../../db";

const otpVerificationSchema = z.object({
userId: z.string(),
otp: z.number(),
username: z.string(),
});

export const verifyOtpController = async (req: Request, res: Response) => {
try {
const payload = req.body;
const result = otpVerificationSchema.safeParse(payload);

if (!result.success) {
const formattedError: any = {};
result.error.errors.forEach((e) => {
formattedError[e.path[0]] = e.message;
});
return res.status(400).json({
error: { ...formattedError, message: "Validation error." },
});
}

const { userId, otp, username } = result.data;

const user = await prisma.user.findUnique({
where: { id: userId },
select: {
id: true,
email: true,
username: true,
otp: true,
createdAt: true,
},
});

if (!user) {
return res.status(404).json({
error: { message: "User not found." },
});
}

if (user.otp !== otp) {
return res.status(400).json({
error: { message: "Invalid OTP." },
});
}

const otpAge = Date.now() - new Date(user.createdAt).getTime();
const otpExpiry = 10 * 60 * 1000; // 10 minutes

if (otpAge > otpExpiry) {
return res.status(400).json({
error: { message: "OTP has expired." },
});
}
const token = createJWT({
id: user.id,
username: user.username,
id: user!.id,
username: username,
});
await prisma.user.update({
where: { id: userId },
data: {
otp: null,
},
});

res.status(201).json({
message: "User created Successfully.",
user,
token: token,
return res.status(200).json({
message: "Email verified successfully.",
token,
});
} catch (error) {
console.error("OTP verification error:", error);
return res.status(500).json({
error: {
message: "An unexpected exception occurred!",
},
error: { message: "An unexpected error occurred." },
});
}
};



export const userSigninController = async (req: Request, res: Response) => {
try {
const payload = req.body;
Expand All @@ -99,6 +212,7 @@ export const userSigninController = async (req: Request, res: Response) => {
email: data.email,
},
select: {
otp: true,
id: true,
username: true,
email: true,
Expand All @@ -114,6 +228,15 @@ export const userSigninController = async (req: Request, res: Response) => {
});
}

if (user.otp) {
console.log("Email really not verified")
return res.status(411).json({
error: {
message: "Email not verified",
},
});
}

const matchPassword = await validatePassword(
data.password,
user.passwordHash
Expand Down
4 changes: 3 additions & 1 deletion backend/src/routes/user/route.ts
Original file line number Diff line number Diff line change
@@ -1,5 +1,5 @@
import { Router } from "express";
import { userProfileController, userSigninController, userSignupController } from "./controller";
import { userProfileController, userSigninController, userSignupController, verifyOtpController } from "./controller";
import authMiddleware from "../../middleware/auth"

const userRouter = Router();
Expand All @@ -8,6 +8,8 @@ userRouter.post('/signup', userSignupController)

userRouter.post('/signin', userSigninController)

userRouter.post('/verify', verifyOtpController)

userRouter.get('/me', authMiddleware, userProfileController);

export default userRouter;
4 changes: 2 additions & 2 deletions backend/src/routes/user/zodSchema.ts
Original file line number Diff line number Diff line change
Expand Up @@ -5,15 +5,15 @@ export const signupBodySchema = zod.object({
.string()
.min(5, { message: "Username too short!" })
.max(30, { message: "Username too long!" }),
email: zod.string().email().max(30, { message: "Email too long!" }),
email: zod.string().email().max(80, { message: "Email too long!" }),
password: zod
.string()
.min(8, { message: "Password too short!" })
.max(30, { message: "Password too long!" }),
});

export const signinBodySchema = zod.object({
email: zod.string().email().max(30, { message: "Email too long!" }),
email: zod.string().email().max(80, { message: "Email too long!" }),
password: zod
.string()
.min(8, { message: "Password too short!" })
Expand Down
3 changes: 3 additions & 0 deletions frontend/src/App.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -12,6 +12,8 @@ import { RecoilRoot } from "recoil";
import NonAuthenticatedRoute from "./components/NonAuthenticatedRoute";
import AuthenticatedRoute from "./components/AuthenticatedRoute";
import Profile from "./pages/Profile";
// @ts-expect-error
import OTP from "./pages/Otp.jsx";
import React from "react";
import Loader from "./components/Loader";
// import axios from "axios";
Expand All @@ -27,6 +29,7 @@ function App() {
<div className="min-h-[80vh]">
<Routes>
<Route path="/app" element={<Home />} />
<Route path="/app/otp" element={<OTP/>} />
<Route path="/app/posts/:id" element={<Post />} />
<Route path="/app/posts" element={<Posts />} />
<Route
Expand Down
Loading

0 comments on commit d1d9f8f

Please sign in to comment.