Files
scheduler-back/src/modules/account/account.service.ts
Hyang-Dan e4048843e9 issue #41
- 비밀번호 초기화 로직 구현 및 테스트 완료
2025-12-02 22:31:10 +09:00

208 lines
6.3 KiB
TypeScript

import { Inject, Injectable } from "@nestjs/common";
import { AccountRepo } from "./account.repo";
import * as DTO from './dto';
import { MailerService } from "src/util/mailer/mailer.service";
import { Generator } from "src/util/generator";
import Redis from "ioredis";
import { Converter } from "src/util/converter";
import { AuthService } from "src/middleware/auth/auth.service";
@Injectable()
export class AccountService {
constructor(
private readonly accountRepo: AccountRepo
, private readonly mailerService: MailerService
, private readonly authService: AuthService
, @Inject("REDIS") private readonly redis: Redis
) {}
async checkDuplication(data: DTO.CheckDuplicationRequest): Promise<DTO.CheckDuplicationResponse> {
const { type, value } = data;
const count = await this.accountRepo.checkIdExists(type, value);
return { isDuplicated: count > 0, success: true };
}
async sendVerificationCode(data: DTO.SendEmailVerificationCodeRequest): Promise<DTO.SendEmailVerificationCodeResponse> {
const { email } = data;
const code = Generator.getVerificationCode();
const html = `<p>Your verification code is: <strong style="font-size:16px;">${code}</strong></p>`;
const result = await this.mailerService.sendMail(email, "<Scheduler> 이메일 인증 코드", html);
if (result.rejected.length > 0) {
return { success: false, error: result.response }
} else {
await this.redis.set(`verify:${email}`, code, 'EX', 600);
return { success: true, message: "이메일 발송 완료" };
}
}
async verifyCode(data: DTO.VerifyEmailVerificationCodeRequest): Promise<DTO.VerifyEmailVerificationCodeResponse> {
const { email, code } = data;
const storedCode = await this.redis.get(`verify:${email}`);
if (!storedCode) {
return { verified: false, success: true, error: '잘못된 이메일이거나 코드가 만료되었습니다.'};
}
if (storedCode !== code) {
return { verified: false, success: true, error: "잘못된 코드입니다." };
}
await this.redis.del(`verify:${email}`);
return { verified: true, success: true, message: "이메일 인증이 완료되었습니다." };
}
async signup(data: DTO.SignupRequest): Promise<DTO.SignupResponse> {
const { accountId, name, nickname, email, password } = data;
const hashedPassword = Converter.getHashedPassword(password);
const result = await this.accountRepo.signup(accountId, name, nickname, email, hashedPassword);
if (result.rowCount) {
return {
success: true,
message: "회원가입이 완료되었습니다."
};
} else {
return {
success: false,
error: "회원가입에 실패하였습니다."
};
}
}
async login(data: DTO.LoginRequest): Promise<DTO.LoginResponse> {
const { type, id, password } = data;
const queryResult = await this.accountRepo.login(type, id);
const typeValue = type === 'email' ? '이메일' : '아이디';
console.log(queryResult);
if (!queryResult || (queryResult.length < 1)) {
return {
success: false,
message: `존재하지 않는 ${typeValue} 입니다.`
};
}
const hashedPassword = queryResult[0].password;
const isPasswordMatch = Converter.comparePassword(password, hashedPassword);
if (!isPasswordMatch) {
return {
success: false,
message: `비밀번호가 맞지 않습니다.`
};
}
{
const { id, accountId, status, isDeleted, birthday } = queryResult[0];
const payload = {
id, accountId, status, isDeleted, birthday
};
const { accessToken, refreshToken } = this.authService.generateTokens(payload);
return {
success: true,
accessToken: accessToken,
refreshToken: refreshToken
};
}
}
async refreshAccessToken(id: string): Promise<DTO.RefreshAccessTokenResponse> {
const { accessToken, refreshToken } = this.authService.refreshTokens(id);
return {
accessToken: accessToken,
refreshToken: refreshToken,
success: true
};
}
async sendResetPasswordCode(data: DTO.SendResetPasswordCodeRequest): Promise<DTO.SendResetPasswordCodeResponse> {
const { email } = data;
const count = await this.accountRepo.checkIdExists('email', email);
if (count === 0) {
return {
success: false,
error: "찾을 수 없는 사용자"
};
}
const code = Generator.getResetPasswordCode();
const html =
`<p>Your Password Reset Code is: <strong>${code}</strong></p>`
+ `<p>Please Enter this code in 5 minutes.</p>`;
const result = await this.mailerService.sendMail(email, "<Scheduler> 비밀번호 초기화 코드", html);
if (result.rejected.length > 0) {
return {
success: false,
error: result.response
};
}
await this.redis.set(`resetPassword:${email}`, code, 'EX', 300);
return {
success: true,
message: "비밀번호 초기화 코드 발송 완료"
};
}
async verifyResetPasswordCode(data: DTO.VerifyResetPasswordCodeRequest): Promise<DTO.VerifyResetPasswordCodeResponse> {
const { email, code } = data;
const storedCode = await this.redis.get(`resetPassword:${email}`);
if (!storedCode) {
return {
success: false,
verified: false,
error: "잘못된 이메일이거나 코드가 만료되었습니다."
};
}
if (storedCode !== code) {
return {
success: false,
verified: false,
error: "잘못된 코드입니다."
};
}
await this.redis.del(`resetPassword:${email}`);
return {
success: true,
verified: true,
message: "비밀번호 초기화 코드 인증 완료"
};
}
async resetPassword(data: DTO.ResetPasswordRequest): Promise<DTO.ResetPasswordResponse> {
const { email, password } = data;
const hashedPassword = Converter.getHashedPassword(password);
const result = await this.accountRepo.updatePassword('email', email, hashedPassword);
if (!result.rowCount || result.rowCount === 0) {
return {
success: false,
error: "비밀번호 초기화 실패"
};
}
return {
success: true,
message: "비밀번호 초기화 성공"
};
}
}