Skip to content

Commit 642e1d5

Browse files
committed
Add send email to get placeholder reset password page
1 parent 7c8c292 commit 642e1d5

File tree

18 files changed

+169
-10
lines changed

18 files changed

+169
-10
lines changed

backend/user-service/controller/auth-controller.js

Lines changed: 8 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -4,7 +4,6 @@ import { formatUserResponse } from "./user-controller.js";
44
import { jwtConfig, REFRESH_TOKEN_COOKIE_KEY, refreshTokenCookieOptions } from "../config/authConfig.js";
55
import TokenService from "../services/tokenService.js";
66
import { BadRequestError, NotFoundError, UnauthorisedError } from "../utils/httpErrors.js";
7-
import { decode } from "jsonwebtoken";
87

98
export async function handleLogin(req, res, next) {
109
const { email, password } = req.body;
@@ -93,3 +92,11 @@ export async function refresh(req, res, next) {
9392
next(err);
9493
}
9594
}
95+
96+
export async function sendResetPasswordLinkToEmail(req, res, next) {
97+
try {
98+
99+
} catch (err) {
100+
next(err);
101+
}
102+
}

backend/user-service/controller/user-controller.js

Lines changed: 22 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -15,6 +15,8 @@ import {
1515
} from "../model/repository.js";
1616
import { BadRequestError, ConflictError, NotFoundError } from "../utils/httpErrors.js";
1717
import TokenService from "../services/tokenService.js";
18+
import { sendEmail } from "../services/emailService.js";
19+
import redisService from "../services/redisService.js";
1820

1921
export async function createUser(req, res, next) {
2022
try {
@@ -42,12 +44,11 @@ export async function createUser(req, res, next) {
4244
data: { accessToken, user: { ...formatUserResponse(createdUser) } },
4345
});
4446
} catch (err) {
45-
console.error('Error creating user:', err);
47+
console.error("Error creating user:", err);
4648
next(err);
4749
}
4850
}
4951

50-
5152
export async function getUser(req, res, next) {
5253
try {
5354
const userId = req.params.id;
@@ -162,6 +163,25 @@ export async function deleteUser(req, res, next) {
162163
}
163164
}
164165

166+
export async function forgetPassword(req, res, next) {
167+
try {
168+
const { email } = req.body;
169+
const emailToken = TokenService.generateEmailToken(email);
170+
redisService.setKeyWithExpiration(email, emailToken, 300);
171+
172+
const resetPasswordLink = `http://localhost:3000/reset-password/${emailToken}`;
173+
await sendEmail({
174+
to: email,
175+
subject: "Reset password",
176+
html: `Click <a href=${resetPasswordLink}>here</a> to reset your password`,
177+
});
178+
179+
res.sendStatus(204);
180+
} catch (err) {
181+
next(err);
182+
}
183+
}
184+
165185
export function formatUserResponse(user) {
166186
return {
167187
_id: user.id,

backend/user-service/package-lock.json

Lines changed: 9 additions & 0 deletions
Some generated files are not rendered by default. Learn more about customizing how changed files appear on GitHub.

backend/user-service/package.json

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -23,6 +23,7 @@
2323
"express": "^4.19.2",
2424
"jsonwebtoken": "^9.0.2",
2525
"mongoose": "^8.5.4",
26+
"nodemailer": "^6.9.16",
2627
"redis": "^4.7.0",
2728
"swagger-jsdoc": "^6.2.8",
2829
"swagger-ui-express": "^5.0.1",

backend/user-service/routes/user-routes.js

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -2,6 +2,7 @@ import express from "express";
22
import {
33
createUser,
44
deleteUser,
5+
forgetPassword,
56
getAllUsers,
67
getUser,
78
updateUser,
@@ -180,4 +181,6 @@ router.patch("/:id/privilege", verifyAccessToken, verifyIsAdmin, updateUserPrivi
180181
*/
181182
router.delete("/:id", verifyAccessToken, verifyIsOwnerOrAdmin, deleteUser);
182183

184+
router.post("/forgetPassword", forgetPassword);
185+
183186
export default router;
Lines changed: 31 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,31 @@
1+
import nodemailer from "nodemailer";
2+
import dotenv from "dotenv";
3+
4+
dotenv.config();
5+
6+
const transporter = nodemailer.createTransport({
7+
host: process.env.SMTP_HOST,
8+
port: Number(process.env.SMTP_PORT),
9+
auth: {
10+
user: process.env.SMTP_USER,
11+
pass: process.env.SMTP_PASSWORD,
12+
},
13+
});
14+
15+
transporter.verify(function (error, success) {
16+
if (error) {
17+
console.log(error);
18+
} else {
19+
console.log("Email server is ready to take our messages");
20+
}
21+
});
22+
23+
export const sendEmail = async ({ to, subject, text, html }) => {
24+
const emailOptions = { from: process.env.SMTP_USER, to, subject, text, html };
25+
try {
26+
const res = await transporter.sendMail(emailOptions);
27+
} catch (err) {
28+
console.error(err);
29+
throw err;
30+
}
31+
};

backend/user-service/services/redisService.js

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -20,6 +20,7 @@ class RedisService {
2020
async setKeyWithExpiration(key, value, expiration) {
2121
try {
2222
await this.redisClient.set(key, value, { EX: expiration });
23+
console.log(`Set ${key}: ${value}`);
2324
} catch (error) {
2425
console.error("Error setting key in Redis:", error);
2526
}

backend/user-service/services/tokenService.js

Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -14,6 +14,11 @@ class TokenService {
1414
return accessToken;
1515
}
1616

17+
static generateEmailToken(email) {
18+
const emailToken = jwt.sign({ email, jti: uuidv4() }, jwtConfig.accessTokenSecret, { expiresIn: "5m" }); // TODO: change
19+
return emailToken;
20+
}
21+
1722
static generateRefreshToken(user) {
1823
const refreshToken = jwt.sign(
1924
{ id: user.id, jti: uuidv4() },

frontend/src/data/users/UserImpl.ts

Lines changed: 5 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -29,10 +29,14 @@ export class UserImpl implements IUser {
2929
async verifyToken(): Promise<any> {
3030
return this.dataSource.verifyToken();
3131
}
32-
32+
3333
async getUser(userId: string): Promise<any> {
3434
return this.dataSource.getUser(userId);
3535
}
36+
37+
async forgetPassword(email: string): Promise<any> {
38+
return this.dataSource.forgetPassword(email);
39+
}
3640
}
3741

3842
export const userImpl = new UserImpl();

frontend/src/data/users/UserRemoteDataSource.ts

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -51,6 +51,9 @@ export class UserRemoteDataSource extends BaseApi {
5151
return await this.protectedGet<any>(`/users/${userId}`);
5252
}
5353

54+
async forgetPassword(email: string) {
55+
return await this.post<any>("/users/forgetPassword", { email });
56+
}
5457
}
5558

5659
export const userRemoteDataSource = new UserRemoteDataSource();

0 commit comments

Comments
 (0)