Skip to content

Commit

Permalink
Merge branch 'backend/dev' of https://github.com/Advanced-computer-la…
Browse files Browse the repository at this point in the history
  • Loading branch information
Sawy03 committed Nov 29, 2024
2 parents 0d38d4a + e0e278c commit 602d4ba
Show file tree
Hide file tree
Showing 13 changed files with 377 additions and 2 deletions.
21 changes: 21 additions & 0 deletions backend/package-lock.json

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

2 changes: 2 additions & 0 deletions backend/package.json
Original file line number Diff line number Diff line change
Expand Up @@ -27,6 +27,7 @@
"mongoose": "^8.6.3",
"multer": "^1.4.5-lts.1",
"multer-storage-cloudinary": "^4.0.0",
"nodemailer": "^6.9.16",
"winston": "^3.14.2"
},
"devDependencies": {
Expand All @@ -40,6 +41,7 @@
"@types/jsonwebtoken": "^9.0.7",
"@types/multer": "^1.4.12",
"@types/node": "^22.7.2",
"@types/nodemailer": "^6.4.17",
"@types/supertest": "^6.0.2",
"jest": "^29.7.0",
"nodemon": "^3.1.7",
Expand Down
49 changes: 49 additions & 0 deletions backend/src/controllers/users/user.controller.ts
Original file line number Diff line number Diff line change
Expand Up @@ -6,7 +6,9 @@ import bookingRepo from '../../database/repositories/booking.repo';
import activityRepo from '../../database/repositories/activity.repo';
import itineraryRepo from '../../database/repositories/itinerary.repo';
import bcrypt from 'bcrypt';
import emailService from '../../services/email/email.service';
import { accountType } from '../../types/User.types';
import authService from '../../services/auth.service';

const getUsers = async (req: Request, res: Response) => {
try {
Expand Down Expand Up @@ -206,6 +208,51 @@ const rejectUser = async (req: Request, res: Response) => {
}
};

const forgotPassword = async (req: Request, res: Response) => {
try {
const email = req.body.email;
const user = await userRepo.findUserByEmail(email);
if (!user) {
res.status(ResponseStatusCodes.NOT_FOUND).json({ message: 'User not found' });
return;
}

user.OTP = Math.floor(100000 + Math.random() * 900000).toString();
await userRepo.updateUser(user._id, user);

console.log(user);

await emailService.sendForgotPasswordEmail(email, user.OTP);

res.status(ResponseStatusCodes.OK).json({ message: 'OTP sent to email' });
} catch (error: any) {
logger.error(`Error sending OTP: ${error.message}`);
res.status(ResponseStatusCodes.INTERNAL_SERVER_ERROR).json({ message: error.message });
}
};

const verifyOTP = async (req: Request, res: Response) => {
try {
const email = req.body.email;
const OTP = req.body.OTP;
const user = await userRepo.findUserByEmail(email);
if (!user) {
res.status(ResponseStatusCodes.NOT_FOUND).json({ message: 'User not found' });
return;
}

if (user.OTP === OTP) {
const token = authService.generateAccessToken({ userId: user._id, accountType: user.account_type });
res.status(ResponseStatusCodes.OK).json({ message: 'OTP verified', data: { token: token } });
} else {
res.status(ResponseStatusCodes.UNAUTHORIZED).json({ message: 'Invalid OTP' });
}
} catch (error: any) {
logger.error(`Error verifying OTP: ${error.message}`);
res.status(ResponseStatusCodes.INTERNAL_SERVER_ERROR).json({ message: error.message });
}
};

const getItinerary = async (req: Request, res: Response) => {
try {
const itineraries = await userRepo.getItinerary(req.user.userId);
Expand Down Expand Up @@ -363,6 +410,8 @@ export {
ChangeUserPassword,
acceptTerms,
rejectUser,
forgotPassword,
verifyOTP,
getItinerary,
getActivity,
cancelActivityBooking,
Expand Down
29 changes: 29 additions & 0 deletions backend/src/database/models/user.model.ts
Original file line number Diff line number Diff line change
Expand Up @@ -54,6 +54,29 @@ const companyProfileSchema = new mongoose.Schema({
},
});

const notificationSchema = new mongoose.Schema({
title: {
type: String,
},
message: {
type: String,
},
notificationType: {
type: String,
enum: ['error', 'warning', 'information', 'success'],
default: 'information',
required: true,
},
read: {
type: Boolean,
default: false,
},
createdAt: {
type: Date,
default: Date.now,
},
});

const userSchema = new mongoose.Schema(
{
account_type: {
Expand Down Expand Up @@ -154,6 +177,12 @@ const userSchema = new mongoose.Schema(
purchased_products: {
type: [{ type: Schema.Types.ObjectId, ref: 'product' }],
},
notifications: {
type: [notificationSchema],
},
OTP: {
type: String,
},
cart: {
type: [cartItemSchema],
default: [],
Expand Down
4 changes: 3 additions & 1 deletion backend/src/middlewares/auth.middleware.ts
Original file line number Diff line number Diff line change
Expand Up @@ -48,9 +48,11 @@ const openPaths = [
{ path: '/api/museums/getall', methods: ['GET'] },
{ path: '/api/activities', methods: ['GET'] },
{ path: '/api/attachments', methods: ['POST'] },
{ path: '/api/termsAndConditions', methods: ['GET'] }, // Add /terms to the openPaths list
{ path: '/api/termsAndConditions', methods: ['GET'] },
{ path: '/api/tags', methods: ['GET'] },
{ path: '/api/categories', methods: ['GET'] },
{ path: '/api/users/forgotPassword', methods: ['POST'] },
{ path: '/api/users/verifyOTP', methods: ['POST'] },
];

const authenticateUnlessOpen = (req: Request, res: Response, next: NextFunction) => {
Expand Down
6 changes: 6 additions & 0 deletions backend/src/routes/user.route.ts
Original file line number Diff line number Diff line change
Expand Up @@ -23,11 +23,17 @@ import {
getPurchasedProducts,
getHowManyUsers,
getHowManyUsersByMonth,
forgotPassword,
verifyOTP,
} from '../controllers/users/user.controller';
import cartController from '../controllers/cart.controller';

const router = Router();

// Open routes
router.post('/forgotPassword', forgotPassword);
router.post('/verifyOTP', verifyOTP);

// User-specific routes
router.use('/advertisers', advertiserRouter);
router.use('/tourGuides', tourGuideRouter);
Expand Down
2 changes: 1 addition & 1 deletion backend/src/services/auth.service.ts
Original file line number Diff line number Diff line change
@@ -1,7 +1,7 @@
import jwt from 'jsonwebtoken';

class AuthService {
public generateAccessToken(user: any) {
public generateAccessToken(user: { userId: string; accountType: string }) {
return jwt.sign(user, process.env.TOKEN_SECRET as string, { expiresIn: '30d' });
}
}
Expand Down
59 changes: 59 additions & 0 deletions backend/src/services/email/email-templates/forgotPassword.html
Original file line number Diff line number Diff line change
@@ -0,0 +1,59 @@
<!doctype html>
<html lang="en">
<head>
<meta charset="UTF-8" />
<meta name="viewport" content="width=device-width, initial-scale=1.0" />
<title>Forgot Password</title>
<style>
body {
font-family: Arial, sans-serif;
background-color: #f4f4f4;
display: flex;
justify-content: center;
align-items: center;
height: 100vh;
}
.container {
background-color: #fff;
padding: 20px;
border-radius: 8px;
box-shadow: 0 0 10px rgba(0, 0, 0, 0.1);
}
.container h2 {
margin-bottom: 20px;
}
.container input {
width: 100%;
padding: 10px;
margin: 10px 0;
border: 1px solid #ccc;
border-radius: 4px;
}
.container button {
width: 100%;
padding: 10px;
background-color: #007bff;
color: #fff;
border: none;
border-radius: 4px;
cursor: pointer;
}
.container button:hover {
background-color: #0056b3;
}
</style>
</head>
<body>
<div class="container">
<h2>Forgot Password</h2>
<form action="/reset-password" method="POST">
<input type="hidden" name="otp" value="{{otp}}" />
<label for="password">New Password:</label>
<input type="password" id="password" name="password" required />
<label for="confirm-password">Confirm New Password:</label>
<input type="password" id="confirm-password" name="confirm-password" required />
<button type="submit">Reset Password</button>
</form>
</div>
</body>
</html>
49 changes: 49 additions & 0 deletions backend/src/services/email/email-templates/notification.html
Original file line number Diff line number Diff line change
@@ -0,0 +1,49 @@
<!doctype html>
<html lang="en">
<head>
<meta charset="UTF-8" />
<meta name="viewport" content="width=device-width, initial-scale=1.0" />
<title>Notification</title>
<style>
body {
font-family: Arial, sans-serif;
background-color: #f4f4f4;
display: flex;
justify-content: center;
align-items: center;
height: 100vh;
}
.container {
background-color: #fff;
padding: 20px;
border-radius: 8px;
box-shadow: 0 0 10px rgba(0, 0, 0, 0.1);
max-width: 600px;
width: 100%;
}
.container h2 {
margin-bottom: 20px;
color: #333;
}
.container p {
font-size: 16px;
line-height: 1.5;
color: #555;
}
.container .footer {
margin-top: 20px;
font-size: 14px;
color: #999;
}
</style>
</head>
<body>
<div class="container">
<h2>{{title}}</h2>
<p>{{message}}</p>
<div class="footer">
<p>Thank you for using our service.</p>
</div>
</div>
</body>
</html>
Loading

0 comments on commit 602d4ba

Please sign in to comment.