add : forget password
This commit is contained in:
@@ -654,6 +654,7 @@ export const enum EmailMessage {
|
||||
ANNOUNCEMENT = "اعلان",
|
||||
PAYMENT_REMINDER = "یادآوری پرداخت",
|
||||
PAYMENT_CANCELLATION = "لغو پرداخت",
|
||||
VERIFY_OTP = "تایید کد یکبار مصرف",
|
||||
}
|
||||
|
||||
export const enum FinancialMessage {
|
||||
|
||||
Executable
+11
@@ -0,0 +1,11 @@
|
||||
import { ApiProperty } from "@nestjs/swagger";
|
||||
import { IsEmail, IsNotEmpty } from "class-validator";
|
||||
|
||||
import { AuthMessage } from "../../../common/enums/message.enum";
|
||||
|
||||
export class ForgetPasswordDto {
|
||||
@IsNotEmpty({ message: AuthMessage.EMAIL_NOT_EMPTY })
|
||||
@IsEmail(undefined, { message: AuthMessage.INVALID_EMAIL_FORMAT })
|
||||
@ApiProperty({ description: "email of user", default: "admin-dsc@gmail.com" })
|
||||
email: string;
|
||||
}
|
||||
+22
@@ -0,0 +1,22 @@
|
||||
import { ApiProperty, ApiPropertyOptional } from "@nestjs/swagger";
|
||||
import { IsEmail, IsNotEmpty, IsNumberString, IsString, Length } from "class-validator";
|
||||
|
||||
import { AuthMessage } from "../../../common/enums/message.enum";
|
||||
|
||||
export class VerifyForgotPasswordOtpDto {
|
||||
@IsNotEmpty({ message: AuthMessage.EMAIL_NOT_EMPTY })
|
||||
@ApiProperty()
|
||||
@IsEmail()
|
||||
email: string;
|
||||
|
||||
@ApiProperty({ description: "OTP code received via SMS", example: "56893" })
|
||||
@IsNotEmpty({ message: AuthMessage.OTP_NOT_EMPTY })
|
||||
@IsNumberString(undefined, { message: AuthMessage.OTP_FORMAT_INVALID })
|
||||
@Length(5, 5, { message: AuthMessage.OTP_FORMAT_INVALID })
|
||||
code: string;
|
||||
|
||||
@IsString()
|
||||
@IsNotEmpty()
|
||||
@ApiPropertyOptional({ description: "new password", example: "abc123" })
|
||||
newPassword: string;
|
||||
}
|
||||
@@ -14,6 +14,8 @@ import { AuthGuards } from "../../common/decorators/auth-guard.decorator";
|
||||
import { RefreshTokenRateLimit, StrictRateLimit } from "../../common/decorators/rate-limit.decorator";
|
||||
import { UserDec } from "../../common/decorators/user.decorator";
|
||||
import { User } from "../users/entities/user.entity";
|
||||
import { ForgetPasswordDto } from "./DTO/forget-password.dto";
|
||||
import { VerifyForgotPasswordOtpDto } from "./DTO/verify-forgot-otp.dto";
|
||||
|
||||
@ApiTags("Auth")
|
||||
@Controller("auth")
|
||||
@@ -22,7 +24,7 @@ export class AuthController {
|
||||
constructor(
|
||||
private readonly authService: AuthService,
|
||||
private readonly ssoService: SSOService,
|
||||
) {}
|
||||
) { }
|
||||
|
||||
@ApiOperation({ summary: "request to login with otp" })
|
||||
@HttpCode(HttpStatus.OK)
|
||||
@@ -92,15 +94,17 @@ export class AuthController {
|
||||
}
|
||||
//***************************** */
|
||||
|
||||
// @Post("forgot-password")
|
||||
// async forgotPassword(@Body() forgotPasswordDto: ForgotPasswordDto) {
|
||||
// return this.authService.forgotPassword(forgotPasswordDto);
|
||||
// }
|
||||
@ApiOperation({ summary: "Generate otp and send vial email" })
|
||||
@Post("forgot-password")
|
||||
async forgotPassword(@Body() dto: ForgetPasswordDto) {
|
||||
return this.authService.requestForgotPasswordOtp(dto);
|
||||
}
|
||||
|
||||
// @Post("reset-password")
|
||||
// async resetPassword(@Body() resetPasswordDto: ResetPasswordDto) {
|
||||
// return this.authService.resetPassword(resetPasswordDto);
|
||||
// }
|
||||
@ApiOperation({ summary: "verify otp and update password" })
|
||||
@Post("reset-password")
|
||||
async resetPassword(@Body() resetPasswordDto: VerifyForgotPasswordOtpDto) {
|
||||
return this.authService.verifyForgotPasswordOtp(resetPasswordDto);
|
||||
}
|
||||
|
||||
@ApiOperation({ summary: "Generate SSO token for client application" })
|
||||
@HttpCode(HttpStatus.OK)
|
||||
|
||||
@@ -17,6 +17,10 @@ import { ChangePasswordDto } from "../DTO/change-password.dto";
|
||||
import { CheckUserExistDto, LoginPasswordDTO } from "../DTO/loginPassword.dto";
|
||||
import { RequestOtpDto } from "../DTO/request-otp.dto";
|
||||
import { VerifyOtpDto } from "../DTO/verify-otp.dto";
|
||||
import { ForgetPasswordDto } from "../DTO/forget-password.dto";
|
||||
import { VerifyForgotPasswordOtpDto } from "../DTO/verify-forgot-otp.dto";
|
||||
import { EmailService } from "../../utils/providers/email.service";
|
||||
|
||||
@Injectable()
|
||||
export class AuthService {
|
||||
private readonly logger = new Logger(AuthService.name);
|
||||
@@ -29,7 +33,8 @@ export class AuthService {
|
||||
private readonly dataSource: DataSource,
|
||||
private readonly notificationQueue: NotificationQueue,
|
||||
private readonly referralsService: ReferralsService,
|
||||
) {}
|
||||
private readonly emailService: EmailService,
|
||||
) { }
|
||||
|
||||
//****************** */
|
||||
//****************** */
|
||||
@@ -99,7 +104,7 @@ export class AuthService {
|
||||
await this.notificationQueue.addLoginOtpNotification({ phone, code: otpCode });
|
||||
|
||||
return {
|
||||
message: AuthMessage.OTP_SENT+otpCode,
|
||||
message: AuthMessage.OTP_SENT + otpCode,
|
||||
};
|
||||
}
|
||||
|
||||
@@ -244,7 +249,58 @@ export class AuthService {
|
||||
|
||||
return user;
|
||||
}
|
||||
//****************** */
|
||||
//****************** */
|
||||
async requestForgotPasswordOtp(dto: ForgetPasswordDto) {
|
||||
const { email } = dto;
|
||||
|
||||
const user = await this.usersService.findOneWithEmail(email)
|
||||
|
||||
if (!user) {
|
||||
throw new BadRequestException("کاربری با این ایمیل یافت نشد")
|
||||
}
|
||||
|
||||
const existCode = await this.otpService.checkExistOtp(email, "FORGOT_PASSWORD");
|
||||
|
||||
if (existCode) {
|
||||
return {
|
||||
message: AuthMessage.OTP_ALREADY_SENT,
|
||||
ttlSecond: existCode,
|
||||
};
|
||||
}
|
||||
|
||||
const otpCode = await this.otpService.generateAndSetInCache(email, "FORGOT_PASSWORD");
|
||||
|
||||
await this.emailService.sendOtpEmail({ userEmail: email, otp: otpCode });
|
||||
|
||||
this.logger.log(`otp code: ${otpCode} for email: ${email}`);
|
||||
|
||||
return { message: 'success' }
|
||||
}
|
||||
|
||||
async verifyForgotPasswordOtp(dto: VerifyForgotPasswordOtpDto) {
|
||||
const { code, email, newPassword } = dto;
|
||||
|
||||
const isValid = await this.otpService.verifyOtp(email, code, "FORGOT_PASSWORD");
|
||||
|
||||
if (!isValid) throw new BadRequestException(AuthMessage.INVALID_OTP);
|
||||
|
||||
let user = await this.usersService.findOneWithEmail(email);
|
||||
|
||||
if (!user) {
|
||||
throw new BadRequestException('کاربر پیدا نشد .')
|
||||
}
|
||||
|
||||
const hashedPassword = await this.passwordService.hashPassword(newPassword);
|
||||
|
||||
await this.otpService.delOtpFormCache(email, "FORGOT_PASSWORD");
|
||||
|
||||
await this.usersService.updateUserPassword(user.id, hashedPassword);
|
||||
|
||||
return {
|
||||
message: AuthMessage.PASSWORD_CHANGED_SUCCESSFULLY
|
||||
};
|
||||
}
|
||||
//****************** */
|
||||
//****************** */
|
||||
private checkUserIsAdmin(roles: Role[]) {
|
||||
|
||||
@@ -1,23 +1,25 @@
|
||||
<html dir="rtl" lang="fa">
|
||||
<head>
|
||||
<meta charset="UTF-8" />
|
||||
<meta name="viewport" content="width=device-width, initial-scale=1.0" />
|
||||
<title>ورود به سیستم</title>
|
||||
</head>
|
||||
<body
|
||||
style="font-family: 'IRANSans', Arial, sans-serif; line-height: 1.6; color: #333; max-width: 600px; margin: 0 auto; padding: 20px; text-align: center;"
|
||||
>
|
||||
<div style="text-align: center; margin-bottom: 30px;">
|
||||
<h1>ورود به سیستم</h1>
|
||||
</div>
|
||||
<div style="background-color: #f9f9f9; padding: 20px; border-radius: 5px;">
|
||||
<p>سلام،</p>
|
||||
<p>ورود به سیستم در تاریخ و ساعت زیر انجام شد:</p>
|
||||
<div style="font-size: 20px; color: #2c3e50; text-align: center; margin: 20px 0;">{{date}}</div>
|
||||
<p>با تشکر</p>
|
||||
</div>
|
||||
<div style="text-align: center; margin-top: 30px; font-size: 12px; color: #666;">
|
||||
<p>این ایمیل به صورت خودکار ارسال شده است. لطفاً به آن پاسخ ندهید.</p>
|
||||
</div>
|
||||
</body>
|
||||
|
||||
<head>
|
||||
<meta charset="UTF-8" />
|
||||
<meta name="viewport" content="width=device-width, initial-scale=1.0" />
|
||||
<title>ورود به سیستم</title>
|
||||
</head>
|
||||
|
||||
<body
|
||||
style="font-family: 'IRANSans', Arial, sans-serif; line-height: 1.6; color: #333; max-width: 600px; margin: 0 auto; padding: 20px; text-align: center;">
|
||||
<div style="text-align: center; margin-bottom: 30px;">
|
||||
<h1>ورود به سیستم</h1>
|
||||
</div>
|
||||
<div style="background-color: #f9f9f9; padding: 20px; border-radius: 5px;">
|
||||
<p>سلام،</p>
|
||||
<p>کد تایید :</p>
|
||||
<div style="font-size: 20px; color: #2c3e50; text-align: center; margin: 20px 0;">{{otp}}</div>
|
||||
|
||||
</div>
|
||||
<div style="text-align: center; margin-top: 30px; font-size: 12px; color: #666;">
|
||||
<p>این ایمیل به صورت خودکار ارسال شده است. لطفاً به آن پاسخ ندهید.</p>
|
||||
</div>
|
||||
</body>
|
||||
|
||||
</html>
|
||||
@@ -0,0 +1,23 @@
|
||||
<html dir="rtl" lang="fa">
|
||||
<head>
|
||||
<meta charset="UTF-8" />
|
||||
<meta name="viewport" content="width=device-width, initial-scale=1.0" />
|
||||
<title>کد تایید تغییر رمز عبور</title>
|
||||
</head>
|
||||
<body
|
||||
style="font-family: 'IRANSans', Arial, sans-serif; line-height: 1.6; color: #333; max-width: 600px; margin: 0 auto; padding: 20px; text-align: center; dir:rtl;"
|
||||
>
|
||||
<div style="text-align: center; margin-bottom: 30px;">
|
||||
<h1>ورود به سیستم</h1>
|
||||
</div>
|
||||
<div style="background-color: #f9f9f9; padding: 20px; border-radius: 5px;">
|
||||
<p>سلام،</p>
|
||||
<p>ورود به سیستم در تاریخ و ساعت زیر انجام شد:</p>
|
||||
<div style="font-size: 20px; color: #2c3e50; text-align: center; margin: 20px 0;">{{date}}</div>
|
||||
<p>با تشکر</p>
|
||||
</div>
|
||||
<div style="text-align: center; margin-top: 30px; font-size: 12px; color: #666;">
|
||||
<p>این ایمیل به صورت خودکار ارسال شده است. لطفاً به آن پاسخ ندهید.</p>
|
||||
</div>
|
||||
</body>
|
||||
</html>
|
||||
@@ -16,7 +16,7 @@ import { IBaseNotificationData } from "../../notifications/interfaces/ISendNotif
|
||||
@Injectable()
|
||||
export class EmailService {
|
||||
private readonly logger = new Logger(EmailService.name);
|
||||
constructor(private readonly mailerService: MailerService) {}
|
||||
constructor(private readonly mailerService: MailerService) { }
|
||||
|
||||
async sendVerificationEmail(to: string, link: string, fullName: string): Promise<SentMessageInfo> {
|
||||
try {
|
||||
@@ -174,4 +174,19 @@ export class EmailService {
|
||||
// throw new InternalServerErrorException("error in sending admin notification email");
|
||||
}
|
||||
}
|
||||
|
||||
async sendOtpEmail({ userEmail, otp }: { userEmail: string, otp: string }) {
|
||||
try {
|
||||
await this.mailerService.sendMail({
|
||||
to: userEmail,
|
||||
subject: EmailMessage.VERIFY_OTP,
|
||||
template: "otp",
|
||||
context: {
|
||||
otp,
|
||||
},
|
||||
});
|
||||
} catch (error) {
|
||||
this.logger.error("error in sending otp email", error);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user