Skip to content

Commit

Permalink
Merge branch 'main' into feat/225-nickname-sidebar
Browse files Browse the repository at this point in the history
  • Loading branch information
dbwhdtjr0457 committed Dec 6, 2023
2 parents a5bee8f + 36cde8c commit 8f3626c
Show file tree
Hide file tree
Showing 13 changed files with 420 additions and 231 deletions.
10 changes: 5 additions & 5 deletions BE/src/auth/auth.controller.ts
Original file line number Diff line number Diff line change
Expand Up @@ -9,7 +9,7 @@ import {
} from "@nestjs/common";
import { AuthService } from "./auth.service";
import { AuthCredentialsDto } from "./dto/auth-credential.dto";
import { AccessTokenDto } from "./dto/auth-access-token.dto";
import { LoginResponseDto } from "./dto/login-response.dto";
import {
ExpiredOrNotGuard,
NoDuplicateLoginGuard,
Expand All @@ -30,7 +30,7 @@ export class AuthController {
async kakaoSignIn(
@GetUser() user: User,
@Req() request: Request,
): Promise<AccessTokenDto> {
): Promise<LoginResponseDto> {
return await this.authService.kakaoSignIn(user, request);
}

Expand All @@ -40,7 +40,7 @@ export class AuthController {
async naverSignIn(
@GetUser() user: User,
@Req() request: Request,
): Promise<AccessTokenDto> {
): Promise<LoginResponseDto> {
return await this.authService.naverSignIn(user, request);
}

Expand All @@ -56,7 +56,7 @@ export class AuthController {
async signIn(
@Body() authCredentialsDto: AuthCredentialsDto,
@Req() request: Request,
): Promise<AccessTokenDto> {
): Promise<LoginResponseDto> {
return await this.authService.signIn(authCredentialsDto, request);
}

Expand All @@ -70,7 +70,7 @@ export class AuthController {
@Post("/reissue")
@UseGuards(ExpiredOrNotGuard)
@HttpCode(201)
async reissueAccessToken(@Req() request: Request): Promise<AccessTokenDto> {
async reissueAccessToken(@Req() request: Request): Promise<LoginResponseDto> {
return await this.authService.reissueAccessToken(request);
}
}
37 changes: 21 additions & 16 deletions BE/src/auth/auth.service.ts
Original file line number Diff line number Diff line change
Expand Up @@ -3,7 +3,7 @@ import { JwtService } from "@nestjs/jwt";
import { UsersRepository } from "src/auth/users.repository";
import { AuthCredentialsDto } from "./dto/auth-credential.dto";
import * as bcrypt from "bcryptjs";
import { AccessTokenDto } from "./dto/auth-access-token.dto";
import { LoginResponseDto } from "./dto/login-response.dto";
import { CreateUserDto } from "./dto/users.dto";
import { User } from "./users.entity";
import { Redis } from "ioredis";
Expand All @@ -26,7 +26,7 @@ export class AuthService {
async signIn(
authCredentialsDto: AuthCredentialsDto,
request: Request,
): Promise<AccessTokenDto> {
): Promise<LoginResponseDto> {
const { userId, password } = authCredentialsDto;
const user = await this.usersRepository.getUserByUserId(userId);
if (!user) {
Expand All @@ -37,14 +37,16 @@ export class AuthService {
throw new NotFoundException("올바르지 않은 비밀번호입니다.");
}

return this.createUserTokens(userId, user.nickname, request.ip);
const { nickname, premium } = user;
const accessToken = await this.createUserTokens(userId, request.ip);
return new LoginResponseDto(accessToken, nickname, premium);
}

async signOut(user: User): Promise<void> {
await this.redisClient.del(user.userId);
}

async reissueAccessToken(request: Request): Promise<AccessTokenDto> {
async reissueAccessToken(request: Request): Promise<LoginResponseDto> {
const expiredAccessToken = request.headers.authorization.split(" ")[1];

// 만료된 액세스 토큰을 직접 디코딩
Expand All @@ -54,38 +56,41 @@ export class AuthService {

const userId = expiredResult.userId;

const userNickname = (await User.findOne({ where: { userId: userId } }))
.nickname;
return this.createUserTokens(userId, userNickname, request.ip);
const { nickname, premium } = await User.findOne({
where: { userId },
});
const accessToken = await this.createUserTokens(userId, request.ip);
return new LoginResponseDto(accessToken, nickname, premium);
}

async naverSignIn(user: User, request: Request): Promise<AccessTokenDto> {
const userId = user.userId;
async naverSignIn(user: User, request: Request): Promise<LoginResponseDto> {
const { userId, nickname, premium } = user;
const provider = providerEnum.NAVER;

if (!(await User.findOne({ where: { userId, provider } }))) {
await user.save();
}

return this.createUserTokens(userId, user.nickname, request.ip);
const accessToken = await this.createUserTokens(userId, request.ip);
return new LoginResponseDto(accessToken, nickname, premium);
}

async kakaoSignIn(user: User, request: Request): Promise<AccessTokenDto> {
const userId = user.userId;
async kakaoSignIn(user: User, request: Request): Promise<LoginResponseDto> {
const { userId, nickname, premium } = user;
const provider = providerEnum.KAKAO;

if (!(await User.findOne({ where: { userId, provider } }))) {
await user.save();
}

return this.createUserTokens(userId, user.nickname, request.ip);
const accessToken = await this.createUserTokens(userId, request.ip);
return new LoginResponseDto(accessToken, nickname, premium);
}

private async createUserTokens(
userId: string,
nickname: string,
requestIp: string,
): Promise<AccessTokenDto> {
): Promise<string> {
const accessTokenPayload = { userId };
const accessToken = await this.jwtService.sign(accessTokenPayload, {
expiresIn: "1h",
Expand All @@ -102,6 +107,6 @@ export class AuthService {
// 86000s = 24h
await this.redisClient.set(userId, refreshToken, "EX", 86400);

return new AccessTokenDto(accessToken, nickname);
return accessToken;
}
}
9 changes: 0 additions & 9 deletions BE/src/auth/dto/auth-access-token.dto.ts

This file was deleted.

13 changes: 13 additions & 0 deletions BE/src/auth/dto/login-response.dto.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,13 @@
import { premiumStatus } from "src/utils/enum";

export class LoginResponseDto {
accessToken: string;
nickname: string;
premium: premiumStatus;

constructor(accessToken: string, nickname: string, premium: premiumStatus) {
this.accessToken = accessToken;
this.nickname = nickname;
this.premium = premium;
}
}
7 changes: 7 additions & 0 deletions BE/src/purchase/dto/purchase.credit.dto.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,7 @@
export class CreditDto {
credit: number;

constructor(credit: number) {
this.credit = credit;
}
}
11 changes: 8 additions & 3 deletions BE/src/purchase/purchase.controller.ts
Original file line number Diff line number Diff line change
Expand Up @@ -11,24 +11,29 @@ import { PurchaseService } from "./purchase.service";
import { PurchaseDesignDto, PurchaseListDto } from "./dto/purchase.design.dto";
import { GetUser } from "src/auth/get-user.decorator";
import { User } from "src/auth/users.entity";
import { CreditDto } from "./dto/purchase.credit.dto";

@Controller("purchase")
@UseGuards(JwtAuthGuard)
export class PurchaseController {
constructor(private purchaseService: PurchaseService) {}

@Post("/design")
@HttpCode(204)
async purchaseDesign(
@GetUser() user: User,
@Body() purchaseDesignDto: PurchaseDesignDto,
): Promise<void> {
await this.purchaseService.purchaseDesign(user, purchaseDesignDto);
): Promise<CreditDto> {
return this.purchaseService.purchaseDesign(user, purchaseDesignDto);
}

@Get("/design")
@HttpCode(200)
async getDesignPurchaseList(@GetUser() user: User): Promise<PurchaseListDto> {
return await this.purchaseService.getDesignPurchaseList(user);
}

@Post("/premium")
async purchasePremium(@GetUser() user: User): Promise<CreditDto> {
return this.purchaseService.purchasePremium(user);
}
}
2 changes: 1 addition & 1 deletion BE/src/purchase/purchase.repository.ts
Original file line number Diff line number Diff line change
Expand Up @@ -15,7 +15,7 @@ export class PurchaseRepository {
purchase.design = design;
purchase.user = user;

purchase.save();
await purchase.save();
}

async getDesignPurchaseList(user: User) {
Expand Down
62 changes: 45 additions & 17 deletions BE/src/purchase/purchase.service.ts
Original file line number Diff line number Diff line change
Expand Up @@ -3,7 +3,8 @@ import { PurchaseDesignDto, PurchaseListDto } from "./dto/purchase.design.dto";
import { User } from "src/auth/users.entity";
import { PurchaseRepository } from "./purchase.repository";
import { Purchase } from "./purchase.entity";
import { designEnum, domainEnum } from "src/utils/enum";
import { designEnum, domainEnum, premiumStatus } from "src/utils/enum";
import { CreditDto } from "./dto/purchase.credit.dto";

@Injectable()
export class PurchaseService {
Expand All @@ -12,32 +13,59 @@ export class PurchaseService {
async purchaseDesign(
user: User,
purchaseDesignDto: PurchaseDesignDto,
): Promise<void> {
): Promise<CreditDto> {
const DESIGN_PRICE = 500;
const domain = domainEnum[purchaseDesignDto.domain];
const design = designEnum[purchaseDesignDto.design];

if (user.credit < 500) {
if (await this.isDesignAlreadyPurchased(user.userId, design, design)) {
throw new BadRequestException(`이미 구매한 디자인입니다.`);
}

if (user.credit < DESIGN_PRICE) {
throw new BadRequestException(
`보유한 별가루가 부족합니다. 현재 ${user.credit} 별가루`,
);
}

if (
await Purchase.findOne({
where: {
user: { userId: user.userId },
domain: domain,
design: design,
},
})
) {
throw new BadRequestException(`이미 구매한 디자인입니다.`);
user.credit -= DESIGN_PRICE;
await user.save();

await this.purchaseRepository.purchaseDesign(user, domain, design);

return new CreditDto(user.credit);
}

private async isDesignAlreadyPurchased(
userId: string,
domain: domainEnum,
design: designEnum,
): Promise<boolean> {
const found = await Purchase.findOne({
where: { design, domain, user: { userId } },
});

return !!found;
}

async purchasePremium(user: User): Promise<CreditDto> {
const PREMIUM_VERSION_PRICE = 350;

if (user.premium === premiumStatus.TRUE) {
throw new BadRequestException(`이미 프리미엄 사용자입니다.`);
}

if (user.credit < PREMIUM_VERSION_PRICE) {
throw new BadRequestException(
`보유한 별가루가 부족합니다. 현재 ${user.credit} 별가루`,
);
}

user.credit -= 500;
user.save();
user.credit -= PREMIUM_VERSION_PRICE;
user.premium = premiumStatus.TRUE;
await user.save();

await this.purchaseRepository.purchaseDesign(user, domain, design);
return new CreditDto(user.credit);
}

async getDesignPurchaseList(user: User): Promise<PurchaseListDto> {
Expand All @@ -46,7 +74,7 @@ export class PurchaseService {

const groundPurchase = [];
const skyPurchase = [];
await purchaseList.forEach((purchase) => {
purchaseList.forEach((purchase) => {
if (purchase.domain === "ground") {
groundPurchase.push(purchase.design);
}
Expand Down
8 changes: 4 additions & 4 deletions BE/test/int/auth.service.int-spec.ts
Original file line number Diff line number Diff line change
Expand Up @@ -11,7 +11,7 @@ import { clearUserDb } from "src/utils/clearDb";
import { CreateUserDto } from "src/auth/dto/users.dto";
import { AuthCredentialsDto } from "src/auth/dto/auth-credential.dto";
import { Request } from "express";
import { AccessTokenDto } from "src/auth/dto/auth-access-token.dto";
import { LoginResponseDto } from "src/auth/dto/login-response.dto";
import { NotFoundException } from "@nestjs/common";
import { providerEnum } from "src/utils/enum";

Expand Down Expand Up @@ -71,7 +71,7 @@ describe("AuthService 통합 테스트", () => {

const result = await authService.signIn(authCredentialsDto, request);

expect(result).toBeInstanceOf(AccessTokenDto);
expect(result).toBeInstanceOf(LoginResponseDto);
});

it("존재하지 않는 아이디로 요청 시 실패", async () => {
Expand Down Expand Up @@ -130,7 +130,7 @@ describe("AuthService 통합 테스트", () => {
await authService.signIn(authCredentialsDto, request);
const result = await authService.reissueAccessToken(user, request);

expect(result).toBeInstanceOf(AccessTokenDto);
expect(result).toBeInstanceOf(LoginResponseDto);
});
});

Expand All @@ -147,7 +147,7 @@ describe("AuthService 통합 테스트", () => {

const result = await authService.naverSignIn(user, request);

expect(result).toBeInstanceOf(AccessTokenDto);
expect(result).toBeInstanceOf(LoginResponseDto);
});
});
});
Loading

0 comments on commit 8f3626c

Please sign in to comment.