add : forget password
This commit is contained in:
@@ -654,6 +654,7 @@ export const enum EmailMessage {
|
|||||||
ANNOUNCEMENT = "اعلان",
|
ANNOUNCEMENT = "اعلان",
|
||||||
PAYMENT_REMINDER = "یادآوری پرداخت",
|
PAYMENT_REMINDER = "یادآوری پرداخت",
|
||||||
PAYMENT_CANCELLATION = "لغو پرداخت",
|
PAYMENT_CANCELLATION = "لغو پرداخت",
|
||||||
|
VERIFY_OTP = "تایید کد یکبار مصرف",
|
||||||
}
|
}
|
||||||
|
|
||||||
export const enum FinancialMessage {
|
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 { RefreshTokenRateLimit, StrictRateLimit } from "../../common/decorators/rate-limit.decorator";
|
||||||
import { UserDec } from "../../common/decorators/user.decorator";
|
import { UserDec } from "../../common/decorators/user.decorator";
|
||||||
import { User } from "../users/entities/user.entity";
|
import { User } from "../users/entities/user.entity";
|
||||||
|
import { ForgetPasswordDto } from "./DTO/forget-password.dto";
|
||||||
|
import { VerifyForgotPasswordOtpDto } from "./DTO/verify-forgot-otp.dto";
|
||||||
|
|
||||||
@ApiTags("Auth")
|
@ApiTags("Auth")
|
||||||
@Controller("auth")
|
@Controller("auth")
|
||||||
@@ -22,7 +24,7 @@ export class AuthController {
|
|||||||
constructor(
|
constructor(
|
||||||
private readonly authService: AuthService,
|
private readonly authService: AuthService,
|
||||||
private readonly ssoService: SSOService,
|
private readonly ssoService: SSOService,
|
||||||
) {}
|
) { }
|
||||||
|
|
||||||
@ApiOperation({ summary: "request to login with otp" })
|
@ApiOperation({ summary: "request to login with otp" })
|
||||||
@HttpCode(HttpStatus.OK)
|
@HttpCode(HttpStatus.OK)
|
||||||
@@ -92,15 +94,17 @@ export class AuthController {
|
|||||||
}
|
}
|
||||||
//***************************** */
|
//***************************** */
|
||||||
|
|
||||||
// @Post("forgot-password")
|
@ApiOperation({ summary: "Generate otp and send vial email" })
|
||||||
// async forgotPassword(@Body() forgotPasswordDto: ForgotPasswordDto) {
|
@Post("forgot-password")
|
||||||
// return this.authService.forgotPassword(forgotPasswordDto);
|
async forgotPassword(@Body() dto: ForgetPasswordDto) {
|
||||||
// }
|
return this.authService.requestForgotPasswordOtp(dto);
|
||||||
|
}
|
||||||
|
|
||||||
// @Post("reset-password")
|
@ApiOperation({ summary: "verify otp and update password" })
|
||||||
// async resetPassword(@Body() resetPasswordDto: ResetPasswordDto) {
|
@Post("reset-password")
|
||||||
// return this.authService.resetPassword(resetPasswordDto);
|
async resetPassword(@Body() resetPasswordDto: VerifyForgotPasswordOtpDto) {
|
||||||
// }
|
return this.authService.verifyForgotPasswordOtp(resetPasswordDto);
|
||||||
|
}
|
||||||
|
|
||||||
@ApiOperation({ summary: "Generate SSO token for client application" })
|
@ApiOperation({ summary: "Generate SSO token for client application" })
|
||||||
@HttpCode(HttpStatus.OK)
|
@HttpCode(HttpStatus.OK)
|
||||||
|
|||||||
@@ -17,6 +17,10 @@ import { ChangePasswordDto } from "../DTO/change-password.dto";
|
|||||||
import { CheckUserExistDto, LoginPasswordDTO } from "../DTO/loginPassword.dto";
|
import { CheckUserExistDto, LoginPasswordDTO } from "../DTO/loginPassword.dto";
|
||||||
import { RequestOtpDto } from "../DTO/request-otp.dto";
|
import { RequestOtpDto } from "../DTO/request-otp.dto";
|
||||||
import { VerifyOtpDto } from "../DTO/verify-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()
|
@Injectable()
|
||||||
export class AuthService {
|
export class AuthService {
|
||||||
private readonly logger = new Logger(AuthService.name);
|
private readonly logger = new Logger(AuthService.name);
|
||||||
@@ -29,7 +33,8 @@ export class AuthService {
|
|||||||
private readonly dataSource: DataSource,
|
private readonly dataSource: DataSource,
|
||||||
private readonly notificationQueue: NotificationQueue,
|
private readonly notificationQueue: NotificationQueue,
|
||||||
private readonly referralsService: ReferralsService,
|
private readonly referralsService: ReferralsService,
|
||||||
) {}
|
private readonly emailService: EmailService,
|
||||||
|
) { }
|
||||||
|
|
||||||
//****************** */
|
//****************** */
|
||||||
//****************** */
|
//****************** */
|
||||||
@@ -99,7 +104,7 @@ export class AuthService {
|
|||||||
await this.notificationQueue.addLoginOtpNotification({ phone, code: otpCode });
|
await this.notificationQueue.addLoginOtpNotification({ phone, code: otpCode });
|
||||||
|
|
||||||
return {
|
return {
|
||||||
message: AuthMessage.OTP_SENT+otpCode,
|
message: AuthMessage.OTP_SENT + otpCode,
|
||||||
};
|
};
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -244,7 +249,58 @@ export class AuthService {
|
|||||||
|
|
||||||
return user;
|
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[]) {
|
private checkUserIsAdmin(roles: Role[]) {
|
||||||
|
|||||||
@@ -1,23 +1,25 @@
|
|||||||
<html dir="rtl" lang="fa">
|
<html dir="rtl" lang="fa">
|
||||||
<head>
|
|
||||||
<meta charset="UTF-8" />
|
<head>
|
||||||
<meta name="viewport" content="width=device-width, initial-scale=1.0" />
|
<meta charset="UTF-8" />
|
||||||
<title>ورود به سیستم</title>
|
<meta name="viewport" content="width=device-width, initial-scale=1.0" />
|
||||||
</head>
|
<title>ورود به سیستم</title>
|
||||||
<body
|
</head>
|
||||||
style="font-family: 'IRANSans', Arial, sans-serif; line-height: 1.6; color: #333; max-width: 600px; margin: 0 auto; padding: 20px; text-align: center;"
|
|
||||||
>
|
<body
|
||||||
<div style="text-align: center; margin-bottom: 30px;">
|
style="font-family: 'IRANSans', Arial, sans-serif; line-height: 1.6; color: #333; max-width: 600px; margin: 0 auto; padding: 20px; text-align: center;">
|
||||||
<h1>ورود به سیستم</h1>
|
<div style="text-align: center; margin-bottom: 30px;">
|
||||||
</div>
|
<h1>ورود به سیستم</h1>
|
||||||
<div style="background-color: #f9f9f9; padding: 20px; border-radius: 5px;">
|
</div>
|
||||||
<p>سلام،</p>
|
<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>
|
||||||
<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;">
|
</div>
|
||||||
<p>این ایمیل به صورت خودکار ارسال شده است. لطفاً به آن پاسخ ندهید.</p>
|
<div style="text-align: center; margin-top: 30px; font-size: 12px; color: #666;">
|
||||||
</div>
|
<p>این ایمیل به صورت خودکار ارسال شده است. لطفاً به آن پاسخ ندهید.</p>
|
||||||
</body>
|
</div>
|
||||||
|
</body>
|
||||||
|
|
||||||
</html>
|
</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()
|
@Injectable()
|
||||||
export class EmailService {
|
export class EmailService {
|
||||||
private readonly logger = new Logger(EmailService.name);
|
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> {
|
async sendVerificationEmail(to: string, link: string, fullName: string): Promise<SentMessageInfo> {
|
||||||
try {
|
try {
|
||||||
@@ -174,4 +174,19 @@ export class EmailService {
|
|||||||
// throw new InternalServerErrorException("error in sending admin notification email");
|
// 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