From 1e89617a89a23d96c99c1f6bb7bb1f087ecb94d3 Mon Sep 17 00:00:00 2001 From: Maybeiley <2784519@gmail.com> Date: Mon, 24 Feb 2025 02:57:56 +0900 Subject: [PATCH 1/2] =?UTF-8?q?Feat:=20#261=20Profile=20=EB=AA=A8=EB=93=88?= =?UTF-8?q?=20=EB=B6=84=EB=A6=AC?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- src/app.module.ts | 2 + src/modules/auth/auth.controller.ts | 10 +-- src/modules/auth/auth.module.ts | 2 + src/modules/auth/auth.repository.ts | 32 ------- src/modules/auth/auth.service.ts | 15 ++-- src/modules/chatRoom/chatRoom.module.ts | 4 +- src/modules/chatRoom/chatRoom.service.ts | 7 +- src/modules/plan/plan.module.ts | 3 +- src/modules/plan/plan.service.ts | 6 +- .../domain/profile.domain.ts | 0 .../domain/profile.interface.ts | 2 +- .../domain/profile.mapper.ts | 2 +- src/modules/profile/profile.controller.ts | 49 +++++++++++ src/modules/profile/profile.module.ts | 12 +++ src/modules/profile/profile.repository.ts | 85 +++++++++++++++++++ src/modules/profile/profile.service.ts | 62 ++++++++++++++ .../profile/types/profile.response.dto.ts | 0 .../{user => profile}/types/profile.types.ts | 0 .../types/updateProfile.dto.ts | 2 +- src/modules/user/domain/user.domain.ts | 2 +- src/modules/user/domain/user.interface.ts | 2 +- src/modules/user/types/user.types.ts | 2 +- src/modules/user/user.controller.ts | 51 +---------- src/modules/user/user.e2e.spec.ts | 20 ++--- src/modules/user/user.repository.ts | 51 +---------- src/modules/user/user.service.ts | 35 +------- 26 files changed, 256 insertions(+), 202 deletions(-) rename src/modules/{user => profile}/domain/profile.domain.ts (100%) rename src/modules/{user => profile}/domain/profile.interface.ts (89%) rename src/modules/{user => profile}/domain/profile.mapper.ts (92%) create mode 100644 src/modules/profile/profile.controller.ts create mode 100644 src/modules/profile/profile.module.ts create mode 100644 src/modules/profile/profile.repository.ts create mode 100644 src/modules/profile/profile.service.ts create mode 100644 src/modules/profile/types/profile.response.dto.ts rename src/modules/{user => profile}/types/profile.types.ts (100%) rename src/modules/{user => profile}/types/updateProfile.dto.ts (64%) diff --git a/src/app.module.ts b/src/app.module.ts index 4637972..d90331a 100644 --- a/src/app.module.ts +++ b/src/app.module.ts @@ -24,6 +24,7 @@ import S3Module from './providers/storage/s3/s3.module'; import PointLogModule from './modules/pointLog/pointLog.module'; import { LoggerModule } from './common/logger/winston/logger.module'; import TransactionModule from './providers/database/transaction/transaction.module'; +import ProfileModule from './modules/profile/profile.module'; @Module({ imports: [ @@ -39,6 +40,7 @@ import TransactionModule from './providers/database/transaction/transaction.modu PrismaModule, AuthModule, UserModule, + ProfileModule, NotificationModule, FollowModule, PlanModule, diff --git a/src/modules/auth/auth.controller.ts b/src/modules/auth/auth.controller.ts index 691723f..2a6da1b 100644 --- a/src/modules/auth/auth.controller.ts +++ b/src/modules/auth/auth.controller.ts @@ -50,7 +50,7 @@ export default class AuthController { const { accessToken, refreshToken } = this.service.createTokens(tokenPayload); res.cookie('refreshToken', refreshToken, { - path: '/user/token/refresh', + path: '/auth/refresh/token', httpOnly: true, sameSite: 'lax', secure: true, @@ -80,7 +80,7 @@ export default class AuthController { // 기존 회원의 경우 토큰 반환 res.cookie('refreshToken', tokens.refreshToken, { - path: '/user/token/refresh', + path: '/auth/refresh/token', httpOnly: true, sameSite: 'lax', secure: true, @@ -108,7 +108,7 @@ export default class AuthController { } res.cookie('refreshToken', tokens.refreshToken, { - path: '/user/token/refresh', + path: '/auth/refresh/token', httpOnly: true, sameSite: 'lax', secure: true, @@ -138,7 +138,7 @@ export default class AuthController { } res.cookie('refreshToken', tokens.refreshToken, { - path: '/user/token/refresh', + path: '/auth/refresh/token', httpOnly: true, sameSite: 'lax', secure: true, @@ -162,7 +162,7 @@ export default class AuthController { const { accessToken, refreshToken: newRefreshToken } = this.service.createNewToken(refreshToken); res.cookie('refreshToken', newRefreshToken, { - path: '/user/token/refresh', + path: '/auth/refresh/token', httpOnly: true, sameSite: 'lax', secure: true, diff --git a/src/modules/auth/auth.module.ts b/src/modules/auth/auth.module.ts index 44b9f68..86fb411 100644 --- a/src/modules/auth/auth.module.ts +++ b/src/modules/auth/auth.module.ts @@ -9,6 +9,7 @@ import { GoogleStrategy } from './strategy/google.strategy'; import AuthController from './auth.controller'; import { KakaoStrategy } from './strategy/kakao.strategy'; import { NaverStrategy } from './strategy/naver.strategy'; +import ProfileModule from '../profile/profile.module'; @Module({ imports: [ @@ -16,6 +17,7 @@ import { NaverStrategy } from './strategy/naver.strategy'; JwtModule.register({ secret: process.env.JWT_SECRET }), + ProfileModule, UserStatsModule ], controllers: [AuthController], diff --git a/src/modules/auth/auth.repository.ts b/src/modules/auth/auth.repository.ts index 5ed0be9..5716aec 100644 --- a/src/modules/auth/auth.repository.ts +++ b/src/modules/auth/auth.repository.ts @@ -1,9 +1,6 @@ import { Injectable } from '@nestjs/common'; -import { IDreamerProfile, IMakerProfile } from 'src/modules/user/domain/profile.interface'; -import { DreamerProfileMapper, MakerProfileMapper } from 'src/modules/user/domain/profile.mapper'; import { IUser } from 'src/modules/user/domain/user.interface'; import UserMapper from 'src/modules/user/domain/user.mapper'; -import { DreamerProfileProperties, MakerProfileProperties } from 'src/modules/user/types/profile.types'; import { OAuthProperties, SignupProperties } from 'src/modules/user/types/user.types'; import DBClient from 'src/providers/database/prisma/DB.client'; @@ -40,33 +37,4 @@ export default class AuthRepository { return new UserMapper(data).toDomain(); } - - async createDreamer(user: Partial): Promise { - const profile = await this.db.dreamerProfile.create({ - data: { - user: { connect: { id: user.userId } }, - tripTypes: user.tripTypes, - serviceArea: user.serviceArea, - image: user.image - } - }); - - return new DreamerProfileMapper(profile).toDomain(); - } - - async createMaker(user: Partial): Promise { - const profile = await this.db.makerProfile.create({ - data: { - user: { connect: { id: user.userId } }, - serviceArea: user.serviceArea, - serviceTypes: user.serviceTypes, - gallery: user.gallery, - description: user.description, - detailDescription: user.detailDescription, - image: user.image - } - }); - - return new MakerProfileMapper(profile).toDomain(); - } } diff --git a/src/modules/auth/auth.service.ts b/src/modules/auth/auth.service.ts index 08e0a6d..b7ecc9f 100644 --- a/src/modules/auth/auth.service.ts +++ b/src/modules/auth/auth.service.ts @@ -3,20 +3,22 @@ import BadRequestError from 'src/common/errors/badRequestError'; import { JwtService } from '@nestjs/jwt'; import { Injectable } from '@nestjs/common'; import User from 'src/modules/user/domain/user.domain'; -import { DreamerProfile, MakerProfile } from 'src/modules/user/domain/profile.domain'; +import { DreamerProfile, MakerProfile } from 'src/modules/profile/domain/profile.domain'; import UserStatsService from '../userStats/userStats.service'; import { FilteredUserProperties, OAuthProperties, UserProperties } from 'src/modules/user/types/user.types'; import AuthRepository from './auth.repository'; import { Role, RoleValues } from 'src/common/constants/role.type'; -import { DreamerProfileProperties, MakerProfileProperties } from 'src/modules/user/types/profile.types'; +import { DreamerProfileProperties, MakerProfileProperties } from 'src/modules/profile/types/profile.types'; import { OAuthProvider } from 'src/common/constants/oauth.type'; import UnauthorizedError from 'src/common/errors/unauthorizedError'; +import ProfileService from '../profile/profile.service'; @Injectable() export default class AuthService { constructor( private readonly repository: AuthRepository, private readonly jwt: JwtService, + private readonly profile: ProfileService, private readonly userStats: UserStatsService ) {} @@ -44,14 +46,7 @@ export default class AuthService { const userData = await User.create(user); const savedUser = await this.repository.create(userData.signupData()); - // 역할에 따라 프로필 등록 - if (savedUser.getRole() === RoleValues.DREAMER) { - const profileData = DreamerProfile.create({ ...profile, userId: savedUser.getId() }); - await this.repository.createDreamer(profileData); - } else { - const profileData = MakerProfile.create({ ...profile, userId: savedUser.getId() }); - await this.repository.createMaker(profileData.get()); - } + await this.profile.createProfile(savedUser.getId(), savedUser.getRole(), profile); // 유저 생성시 기본값으로 UserStats 생성 await this.userStats.create(savedUser.getId(), {}); diff --git a/src/modules/chatRoom/chatRoom.module.ts b/src/modules/chatRoom/chatRoom.module.ts index c4d2019..f2e67f5 100644 --- a/src/modules/chatRoom/chatRoom.module.ts +++ b/src/modules/chatRoom/chatRoom.module.ts @@ -9,6 +9,7 @@ import ChatModule from '../chat/chat.module'; import ChatRoomGateway from './chatRoom.gateway'; import WebSocketJwtGuard from 'src/common/guards/webSocket.guard'; import UserModule from '../user/user.module'; +import ProfileModule from '../profile/profile.module'; @Module({ imports: [ @@ -17,7 +18,8 @@ import UserModule from '../user/user.module'; }), MongooseModule.forFeature([{ name: ChatRoom.name, schema: ChatRoomSchema }]), ChatModule, - UserModule + UserModule, + ProfileModule ], controllers: [ChatRoomController], diff --git a/src/modules/chatRoom/chatRoom.service.ts b/src/modules/chatRoom/chatRoom.service.ts index a2de95b..bf955ef 100644 --- a/src/modules/chatRoom/chatRoom.service.ts +++ b/src/modules/chatRoom/chatRoom.service.ts @@ -13,7 +13,7 @@ import { ChatReference, FileUploadData, FindChatRoomByIdOptions } from 'src/modu import NotFoundError from 'src/common/errors/notFoundError'; import IChatRoom from './domain/chatRoom.interface'; import BadRequestError from 'src/common/errors/badRequestError'; -import Transactional from 'src/common/decorators/transaction.decorator'; +import ProfileService from '../profile/profile.service'; @Injectable() export default class ChatRoomService { @@ -21,7 +21,8 @@ export default class ChatRoomService { constructor( private readonly chatRoomRepository: ChatRoomRepository, private readonly chatService: ChatService, - private readonly userService: UserService + private readonly userService: UserService, + private readonly profileService: ProfileService ) {} registerClient(userId: string, client: Socket) { @@ -147,7 +148,7 @@ export default class ChatRoomService { const users = await Promise.all( userIds?.map(async (userId) => { const userData = await this.userService.getUser(userId); - const userProfile = await this.userService.getProfile(userData.role, userId); + const userProfile = await this.profileService.getProfile(userData.role, userId); const user = { id: userId, diff --git a/src/modules/plan/plan.module.ts b/src/modules/plan/plan.module.ts index 9045a54..06c0367 100644 --- a/src/modules/plan/plan.module.ts +++ b/src/modules/plan/plan.module.ts @@ -6,9 +6,10 @@ import PlanRepository from './plan.repository'; import PlanService from './plan.service'; import ChatRoomModule from '../chatRoom/chatRoom.module'; import { BullModule } from '@nestjs/bullmq'; +import ProfileModule from '../profile/profile.module'; @Module({ - imports: [BullModule.registerQueue({ name: 'points' }), UserModule, QuoteModule, ChatRoomModule], + imports: [BullModule.registerQueue({ name: 'points' }), UserModule, ProfileModule, QuoteModule, ChatRoomModule], controllers: [PlanController], providers: [PlanRepository, PlanService], exports: [PlanRepository, PlanService] diff --git a/src/modules/plan/plan.service.ts b/src/modules/plan/plan.service.ts index 3df2ebf..ef67595 100644 --- a/src/modules/plan/plan.service.ts +++ b/src/modules/plan/plan.service.ts @@ -24,6 +24,7 @@ import { Queue } from 'bullmq'; import { PointEventEnum } from 'src/common/constants/pointEvent.type'; import TransactionManager from 'src/providers/database/transaction/transaction.manager'; import Transactional from 'src/common/decorators/transaction.decorator'; +import ProfileService from '../profile/profile.service'; @Injectable() export default class PlanService { @@ -32,6 +33,7 @@ export default class PlanService { private readonly repository: PlanRepository, private readonly quoteService: QuoteService, private readonly userService: UserService, + private readonly profileService: ProfileService, private readonly chatRoomService: ChatRoomService, private readonly transactionManager: TransactionManager, private readonly eventEmitter: EventEmitter2 @@ -54,7 +56,7 @@ export default class PlanService { userId: string, options: PlanQueryOptions ): Promise<{ totalCount: number; groupByCount: GroupByCount; list: PlanToClientProperties[] }> { - const makerProfile = await this.userService.getProfile(RoleValues.MAKER, userId); + const makerProfile = await this.profileService.getProfile(RoleValues.MAKER, userId); const serviceArea: ServiceArea[] = options.isAssigned === true ? undefined : makerProfile.serviceArea; options.serviceArea = serviceArea; //NOTE. 메이커의 서비스지역 필터링 @@ -179,7 +181,7 @@ export default class PlanService { throw new BadRequestError(ErrorMessage.PLAN_ASSIGN_NOT_MAKER); } - const assigneeServiceArea = (await this.userService.getProfile(RoleValues.MAKER, assigneeId)).serviceArea; + const assigneeServiceArea = (await this.profileService.getProfile(RoleValues.MAKER, assigneeId)).serviceArea; if (!assigneeServiceArea.includes(plan.getServiceArea())) { throw new BadRequestError(ErrorMessage.PLAN_MAKER_NOT_IN_SERVICE_AREA); } diff --git a/src/modules/user/domain/profile.domain.ts b/src/modules/profile/domain/profile.domain.ts similarity index 100% rename from src/modules/user/domain/profile.domain.ts rename to src/modules/profile/domain/profile.domain.ts diff --git a/src/modules/user/domain/profile.interface.ts b/src/modules/profile/domain/profile.interface.ts similarity index 89% rename from src/modules/user/domain/profile.interface.ts rename to src/modules/profile/domain/profile.interface.ts index bef8524..ab7d6a4 100644 --- a/src/modules/user/domain/profile.interface.ts +++ b/src/modules/profile/domain/profile.interface.ts @@ -1,4 +1,4 @@ -import { DreamerProfileProperties, MakerProfileProperties } from 'src/modules/user/types/profile.types'; +import { DreamerProfileProperties, MakerProfileProperties } from 'src/modules/profile/types/profile.types'; export interface IDreamerProfile { update(data: Partial): DreamerProfileProperties; diff --git a/src/modules/user/domain/profile.mapper.ts b/src/modules/profile/domain/profile.mapper.ts similarity index 92% rename from src/modules/user/domain/profile.mapper.ts rename to src/modules/profile/domain/profile.mapper.ts index 30bd9ef..7f86875 100644 --- a/src/modules/user/domain/profile.mapper.ts +++ b/src/modules/profile/domain/profile.mapper.ts @@ -1,5 +1,5 @@ import { DreamerProfileProperties, MakerProfileProperties } from '../types/profile.types'; -import { DreamerProfile, MakerProfile } from './profile.domain'; +import { DreamerProfile, MakerProfile } from '../../profile/domain/profile.domain'; export class DreamerProfileMapper { constructor(private readonly dreamer: DreamerProfileProperties) {} diff --git a/src/modules/profile/profile.controller.ts b/src/modules/profile/profile.controller.ts new file mode 100644 index 0000000..fb15be7 --- /dev/null +++ b/src/modules/profile/profile.controller.ts @@ -0,0 +1,49 @@ +import { Body, Controller, Get, Patch } from '@nestjs/common'; +import ProfileService from './profile.service'; +import { + ApiBearerAuth, + ApiBody, + ApiOkResponse, + ApiOperation, + ApiResponse, + ApiUnauthorizedResponse +} from '@nestjs/swagger'; +import { DreamerProfileResponseDTO, MakerProfileResponseDTO } from '../user/types/user.response.dto'; +import { UserRole } from 'src/common/decorators/role.decorator'; +import { UserId } from 'src/common/decorators/user.decorator'; +import { DreamerProfileProperties, MakerProfileProperties } from './types/profile.types'; +import UpdateProfileDTO from './types/updateProfile.dto'; + +@Controller('profile') +export default class ProfileController { + constructor(private readonly service: ProfileService) {} + + @Get() + @ApiBearerAuth('accessToken') + @ApiOperation({ summary: '프로필 정보 조회', description: '로그인한 유저의 프로필을 조회합니다' }) + @ApiOkResponse({ type: MakerProfileResponseDTO || DreamerProfileResponseDTO }) + @ApiUnauthorizedResponse({ description: 'Access Token이 없거나 만료되었습니다' }) + async getProfile( + @UserRole() role: string, + @UserId() userId: string + ): Promise { + return await this.service.getProfile(role, userId); + } + + @Patch('update') + @ApiBearerAuth('accessToken') + @ApiOperation({ summary: '프로필 수정', description: '로그인한 유저의 프로필을 수정합니다' }) + @ApiBody({ type: UpdateProfileDTO }) + @ApiResponse({ type: MakerProfileResponseDTO || DreamerProfileResponseDTO }) + @ApiUnauthorizedResponse({ description: 'Access Token이 없거나 만료되었습니다' }) + async updateProfile( + @UserRole() role: string, + @Body() data: Partial, + @UserId() userId: string + ) { + if (role === 'DREAMER') { + return await this.service.updateDreamerProfile(userId, data); + } + return await this.service.updateMakerProfile(userId, data); + } +} diff --git a/src/modules/profile/profile.module.ts b/src/modules/profile/profile.module.ts new file mode 100644 index 0000000..85ae424 --- /dev/null +++ b/src/modules/profile/profile.module.ts @@ -0,0 +1,12 @@ +import { Module } from '@nestjs/common'; +import ProfileController from './profile.controller'; +import ProfileService from './profile.service'; +import ProfileRepository from './profile.repository'; + +@Module({ + imports: [], + controllers: [ProfileController], + providers: [ProfileService, ProfileRepository], + exports: [ProfileService] +}) +export default class ProfileModule {} diff --git a/src/modules/profile/profile.repository.ts b/src/modules/profile/profile.repository.ts new file mode 100644 index 0000000..4667e85 --- /dev/null +++ b/src/modules/profile/profile.repository.ts @@ -0,0 +1,85 @@ +import { Injectable } from '@nestjs/common'; +import DBClient from 'src/providers/database/prisma/DB.client'; +import { IDreamerProfile, IMakerProfile } from './domain/profile.interface'; +import { DreamerProfileMapper, MakerProfileMapper } from './domain/profile.mapper'; +import { DreamerProfileProperties, MakerProfileProperties } from './types/profile.types'; + +@Injectable() +export default class ProfileRepository { + constructor(private readonly db: DBClient) {} + + async findDreamerProfile(userId: string): Promise { + const data = await this.db.dreamerProfile.findUnique({ + where: { + userId + } + }); + + if (data) { + return new DreamerProfileMapper(data).toDomain(); + } + } + + async findMakerProfile(userId: string): Promise { + const data = await this.db.makerProfile.findUnique({ + where: { + userId + } + }); + + if (data) { + return new MakerProfileMapper(data).toDomain(); + } + } + + async createDreamer(user: Partial): Promise { + const profile = await this.db.dreamerProfile.create({ + data: { + user: { connect: { id: user.userId } }, + tripTypes: user.tripTypes, + serviceArea: user.serviceArea, + image: user.image + } + }); + + return new DreamerProfileMapper(profile).toDomain(); + } + + async createMaker(user: Partial): Promise { + const profile = await this.db.makerProfile.create({ + data: { + user: { connect: { id: user.userId } }, + serviceArea: user.serviceArea, + serviceTypes: user.serviceTypes, + gallery: user.gallery, + description: user.description, + detailDescription: user.detailDescription, + image: user.image + } + }); + + return new MakerProfileMapper(profile).toDomain(); + } + + async updateDreamerProfile(userId: string, data: Partial): Promise { + const profile = await this.db.dreamerProfile.update({ + where: { + userId + }, + data + }); + + return new DreamerProfileMapper(profile).toDomain(); + } + + async updateMakerProfile(userId: string, data: Partial): Promise { + const profile = await this.db.makerProfile.update({ + where: { + userId + }, + data + }); + + return new MakerProfileMapper(profile).toDomain(); + } +} diff --git a/src/modules/profile/profile.service.ts b/src/modules/profile/profile.service.ts new file mode 100644 index 0000000..3da2a8c --- /dev/null +++ b/src/modules/profile/profile.service.ts @@ -0,0 +1,62 @@ +import { Injectable } from '@nestjs/common'; +import ProfileRepository from './profile.repository'; +import { DreamerProfileProperties, MakerProfileProperties } from './types/profile.types'; +import BadRequestError from 'src/common/errors/badRequestError'; +import ErrorMessage from 'src/common/constants/errorMessage.enum'; +import { Role, RoleValues } from 'src/common/constants/role.type'; +import { DreamerProfile, MakerProfile } from './domain/profile.domain'; + +@Injectable() +export default class ProfileService { + constructor(private readonly repository: ProfileRepository) {} + + async getProfile(role: string, userId: string): Promise { + if (role === 'DREAMER') { + const profile = await this.repository.findDreamerProfile(userId); + return profile.get(); + } + + const profile = await this.repository.findMakerProfile(userId); + return profile.get(); + } + + async createProfile( + userId: string, + role: Role, + data: DreamerProfileProperties | MakerProfileProperties + ): Promise { + // 역할에 따라 프로필 등록 + if (role === RoleValues.DREAMER) { + const profileData = DreamerProfile.create({ ...data, userId }); + await this.repository.createDreamer(profileData); + } else { + const profileData = MakerProfile.create({ ...data, userId }); + await this.repository.createMaker(profileData.get()); + } + + return; + } + + async updateDreamerProfile( + userId: string, + data: Partial + ): Promise { + const profile = await this.repository.findDreamerProfile(userId); + if (!profile) { + throw new BadRequestError(ErrorMessage.USER_NOT_FOUND); + } + + const newProfile = await this.repository.updateDreamerProfile(userId, profile.update(data)); + return newProfile.get(); + } + + async updateMakerProfile(userId: string, data: Partial): Promise { + const profile = await this.repository.findMakerProfile(userId); + if (!profile) { + throw new BadRequestError(ErrorMessage.USER_NOT_FOUND); + } + + const newProfile = await this.repository.updateMakerProfile(userId, profile.update(data)); + return newProfile.get(); + } +} diff --git a/src/modules/profile/types/profile.response.dto.ts b/src/modules/profile/types/profile.response.dto.ts new file mode 100644 index 0000000..e69de29 diff --git a/src/modules/user/types/profile.types.ts b/src/modules/profile/types/profile.types.ts similarity index 100% rename from src/modules/user/types/profile.types.ts rename to src/modules/profile/types/profile.types.ts diff --git a/src/modules/user/types/updateProfile.dto.ts b/src/modules/profile/types/updateProfile.dto.ts similarity index 64% rename from src/modules/user/types/updateProfile.dto.ts rename to src/modules/profile/types/updateProfile.dto.ts index 9647686..c8fd63a 100644 --- a/src/modules/user/types/updateProfile.dto.ts +++ b/src/modules/profile/types/updateProfile.dto.ts @@ -1,4 +1,4 @@ import { PartialType } from '@nestjs/swagger'; -import { SignupProfileDTO } from './signup.dto'; +import { SignupProfileDTO } from 'src/modules/user/types/signup.dto'; export default class UpdateProfileDTO extends PartialType(SignupProfileDTO) {} diff --git a/src/modules/user/domain/user.domain.ts b/src/modules/user/domain/user.domain.ts index 7d5c6d0..88063bd 100644 --- a/src/modules/user/domain/user.domain.ts +++ b/src/modules/user/domain/user.domain.ts @@ -11,7 +11,7 @@ import { ComparePassword, HashingPassword } from '../../../common/utilities/hash import { IUser } from './user.interface'; import BadRequestError from 'src/common/errors/badRequestError'; import ErrorMessage from 'src/common/constants/errorMessage.enum'; -import { MakerInfoAndProfileProperties, MakerProfileProperties } from 'src/modules/user/types/profile.types'; +import { MakerInfoAndProfileProperties, MakerProfileProperties } from 'src/modules/profile/types/profile.types'; import { UserStatsProperties, UserStatsToClientProperties } from 'src/modules/userStats/types/userStats.types'; import { OAuthProvider } from 'src/common/constants/oauth.type'; diff --git a/src/modules/user/domain/user.interface.ts b/src/modules/user/domain/user.interface.ts index 3d55e7f..b2aa1be 100644 --- a/src/modules/user/domain/user.interface.ts +++ b/src/modules/user/domain/user.interface.ts @@ -1,5 +1,5 @@ import { Role } from 'src/common/constants/role.type'; -import { MakerInfoAndProfileProperties } from 'src/modules/user/types/profile.types'; +import { MakerInfoAndProfileProperties } from 'src/modules/profile/types/profile.types'; import { FilteredUserProperties, OAuthProperties, diff --git a/src/modules/user/types/user.types.ts b/src/modules/user/types/user.types.ts index 5e77fcb..dbd2e1f 100644 --- a/src/modules/user/types/user.types.ts +++ b/src/modules/user/types/user.types.ts @@ -1,7 +1,7 @@ import { ProfileImage } from 'src/common/constants/image.type'; import { Role } from 'src/common/constants/role.type'; import { TripType } from 'src/common/constants/tripType.type'; -import { MakerProfileProperties } from './profile.types'; +import { MakerProfileProperties } from '../../profile/types/profile.types'; import SortOrder from 'src/common/constants/sortOrder.enum'; import { UserStatsProperties } from '../../userStats/types/userStats.types'; import { OAuthProvider } from 'src/common/constants/oauth.type'; diff --git a/src/modules/user/user.controller.ts b/src/modules/user/user.controller.ts index d7066d8..5b5a9e2 100644 --- a/src/modules/user/user.controller.ts +++ b/src/modules/user/user.controller.ts @@ -2,26 +2,10 @@ import { Body, Controller, Get, Param, Patch, Query } from '@nestjs/common'; import UserService from './user.service'; import { Public } from 'src/common/decorators/public.decorator'; import { UserId } from 'src/common/decorators/user.decorator'; -import { - ApiBearerAuth, - ApiBody, - ApiOkResponse, - ApiOperation, - ApiResponse, - ApiUnauthorizedResponse -} from '@nestjs/swagger'; -import { - DreamerProfileResponseDTO, - FilteredUserResponseDTO, - followResponseDTO, - MakerProfileResponseDTO, - UserResponseDTO -} from './types/user.response.dto'; -import UpdateProfileDTO from './types/updateProfile.dto'; -import { DreamerProfileProperties, MakerProfileProperties } from './types/profile.types'; +import { ApiBearerAuth, ApiBody, ApiOkResponse, ApiOperation, ApiUnauthorizedResponse } from '@nestjs/swagger'; +import { FilteredUserResponseDTO, followResponseDTO, UserResponseDTO } from './types/user.response.dto'; import { FilteredUserProperties, UserProperties } from './types/user.types'; import UpdateUserDTO from './types/updateUser.dto'; -import { UserRole } from 'src/common/decorators/role.decorator'; import { Role } from 'src/common/decorators/roleGuard.decorator'; import { GetMakerListQueryDTO, PaginationQueryDTO } from 'src/modules/user/types/query.dto'; @@ -38,20 +22,8 @@ export default class UserController { return await this.service.getUser(userId); } - @Get('profile') - @ApiBearerAuth('accessToken') - @ApiOperation({ summary: '프로필 정보 조회', description: '로그인한 유저의 프로필을 조회합니다' }) - @ApiOkResponse({ type: MakerProfileResponseDTO || DreamerProfileResponseDTO }) - @ApiUnauthorizedResponse({ description: 'Access Token이 없거나 만료되었습니다' }) - async getProfile( - @UserRole() role: string, - @UserId() userId: string - ): Promise { - return await this.service.getProfile(role, userId); - } - @Public() - @Get('profile/:makerId') + @Get('maker/:makerId') async getProfileById(@Param('makerId') makerId: string, @UserId() dreamerId: string) { return await this.service.getProfileCardData(makerId, dreamerId, true); } @@ -80,21 +52,4 @@ export default class UserController { async updateUser(@Body() data: UpdateUserDTO, @UserId() userId: string): Promise { return await this.service.updateUser(userId, data); } - - @Patch('update/profile') - @ApiBearerAuth('accessToken') - @ApiOperation({ summary: '프로필 수정', description: '로그인한 유저의 프로필을 수정합니다' }) - @ApiBody({ type: UpdateProfileDTO }) - @ApiResponse({ type: MakerProfileResponseDTO || DreamerProfileResponseDTO }) - @ApiUnauthorizedResponse({ description: 'Access Token이 없거나 만료되었습니다' }) - async updateProfile( - @UserRole() role: string, - @Body() data: Partial, - @UserId() userId: string - ) { - if (role === 'DREAMER') { - return await this.service.updateDreamerProfile(userId, data); - } - return await this.service.updateMakerProfile(userId, data); - } } diff --git a/src/modules/user/user.e2e.spec.ts b/src/modules/user/user.e2e.spec.ts index 1becbea..3dd2267 100644 --- a/src/modules/user/user.e2e.spec.ts +++ b/src/modules/user/user.e2e.spec.ts @@ -61,10 +61,10 @@ describe('Review Test (e2e)', () => { }); }); - describe('[GET /users/profile]', () => { + describe('[GET /profile]', () => { it('DREAMER의 프로필 조회', async () => { const { body, statusCode } = await request(app.getHttpServer()) - .get('/users/profile') + .get('/profile') .set('authorization', `Bearer ${dreamerToken}`); expect(statusCode).toBe(HttpStatus.OK); @@ -73,7 +73,7 @@ describe('Review Test (e2e)', () => { it('MAKER의 프로필 조회', async () => { const { body, statusCode } = await request(app.getHttpServer()) - .get('/users/profile') + .get('/profile') .set('authorization', `Bearer ${makerToken}`); expect(statusCode).toBe(HttpStatus.OK); @@ -81,10 +81,10 @@ describe('Review Test (e2e)', () => { }); }); - describe('[GET /users/profile/{makerId}]', () => { + describe('[GET /users/maker/{makerId}]', () => { it('메이커 정보 조회', async () => { const { body, statusCode } = await request(app.getHttpServer()) - .get(`/users/profile/${makerId}`) + .get(`/users/maker/${makerId}`) .set('authorization', `Bearer ${dreamerToken}`); expect(statusCode).toBe(HttpStatus.OK); @@ -92,7 +92,7 @@ describe('Review Test (e2e)', () => { }); it('메이커 정보 조회, 존재하지 않는 MAKER인 경우 400에러', async () => { - const { statusCode } = await request(app.getHttpServer()).get(`/users/profile/1`); + const { statusCode } = await request(app.getHttpServer()).get(`/users/maker/1`); expect(statusCode).toBe(HttpStatus.BAD_REQUEST); }); @@ -186,12 +186,12 @@ describe('Review Test (e2e)', () => { }); }); - describe('[PATCH /users/update/profile]', () => { + describe('[PATCH /profile/update]', () => { const dto = { image: ProfileImageValues.DEFAULT_4 }; it('나의 프로필 수정-DREAMER', async () => { const { body, statusCode } = await request(app.getHttpServer()) - .patch('/users/update/profile') + .patch('/profile/update') .set('authorization', `Bearer ${dreamerToken}`) .send(dto); @@ -201,7 +201,7 @@ describe('Review Test (e2e)', () => { it('나의 프로필 수정-MAKER', async () => { const { body, statusCode } = await request(app.getHttpServer()) - .patch('/users/update/profile') + .patch('/profile/update') .set('authorization', `Bearer ${makerToken}`) .send(dto); @@ -211,7 +211,7 @@ describe('Review Test (e2e)', () => { it('나의 프로필 수정-DREAMER, 유저 프로필이 없는 경우 400에러', async () => { const { body, statusCode } = await request(app.getHttpServer()) - .patch('/users/update/profile') + .patch('/profile/update') .set('authorization', `Bearer ${noUserToken}`) .send(dto); diff --git a/src/modules/user/user.repository.ts b/src/modules/user/user.repository.ts index 7283edb..c5d2724 100644 --- a/src/modules/user/user.repository.ts +++ b/src/modules/user/user.repository.ts @@ -1,11 +1,8 @@ import { Injectable } from '@nestjs/common'; import DBClient from 'src/providers/database/prisma/DB.client'; import UserMapper from './domain/user.mapper'; -import { MakerOrderBy, MakerOrderByField, UserProperties } from './types/user.types'; -import { DreamerProfileProperties, MakerProfileProperties } from './types/profile.types'; -import { DreamerProfileMapper, MakerProfileMapper } from './domain/profile.mapper'; +import { MakerOrderBy, MakerOrderByField } from './types/user.types'; import { IUser } from './domain/user.interface'; -import { IDreamerProfile, IMakerProfile } from './domain/profile.interface'; import { RoleValues } from 'src/common/constants/role.type'; import SortOrder from 'src/common/constants/sortOrder.enum'; import { GetMakerListQueryDTO } from 'src/modules/user/types/query.dto'; @@ -104,50 +101,4 @@ export default class UserRepository { return new UserMapper(user).toDomain(); } - - async findDreamerProfile(userId: string): Promise { - const data = await this.db.dreamerProfile.findUnique({ - where: { - userId - } - }); - - if (data) { - return new DreamerProfileMapper(data).toDomain(); - } - } - - async findMakerProfile(userId: string): Promise { - const data = await this.db.makerProfile.findUnique({ - where: { - userId - } - }); - - if (data) { - return new MakerProfileMapper(data).toDomain(); - } - } - - async updateDreamerProfile(userId: string, data: Partial): Promise { - const profile = await this.db.dreamerProfile.update({ - where: { - userId - }, - data - }); - - return new DreamerProfileMapper(profile).toDomain(); - } - - async updateMakerProfile(userId: string, data: Partial): Promise { - const profile = await this.db.makerProfile.update({ - where: { - userId - }, - data - }); - - return new MakerProfileMapper(profile).toDomain(); - } } diff --git a/src/modules/user/user.service.ts b/src/modules/user/user.service.ts index 7947270..71183db 100644 --- a/src/modules/user/user.service.ts +++ b/src/modules/user/user.service.ts @@ -3,7 +3,7 @@ import UserRepository from './user.repository'; import BadRequestError from 'src/common/errors/badRequestError'; import ErrorMessage from 'src/common/constants/errorMessage.enum'; import { FilteredUserProperties, PasswordProperties, UserProperties } from './types/user.types'; -import { DreamerProfileProperties, MakerInfoAndProfileProperties, MakerProfileProperties } from './types/profile.types'; +import { MakerInfoAndProfileProperties } from '../profile/types/profile.types'; import UserStatsService from '../userStats/userStats.service'; import FollowService from '../follow/follow.service'; import { GetMakerListQueryDTO, PaginationQueryDTO } from 'src/modules/user/types/query.dto'; @@ -23,16 +23,6 @@ export default class UserService { return user?.toClientAll(); } - async getProfile(role: string, userId: string): Promise { - if (role === 'DREAMER') { - const profile = await this.repository.findDreamerProfile(userId); - return profile.get(); - } - - const profile = await this.repository.findMakerProfile(userId); - return profile.get(); - } - async getMakers( options: GetMakerListQueryDTO, userId?: string @@ -70,29 +60,6 @@ export default class UserService { return newUser.toClient(); } - async updateDreamerProfile( - userId: string, - data: Partial - ): Promise { - const profile = await this.repository.findDreamerProfile(userId); - if (!profile) { - throw new BadRequestError(ErrorMessage.USER_NOT_FOUND); - } - - const newProfile = await this.repository.updateDreamerProfile(userId, profile.update(data)); - return newProfile.get(); - } - - async updateMakerProfile(userId: string, data: Partial): Promise { - const profile = await this.repository.findMakerProfile(userId); - if (!profile) { - throw new BadRequestError(ErrorMessage.USER_NOT_FOUND); - } - - const newProfile = await this.repository.updateMakerProfile(userId, profile.update(data)); - return newProfile.get(); - } - async getProfileCardData(makerId: string, dreamerId: string, withDetails?: boolean): Promise { const user = await this.repository.findByIdWithProfileAndFollow(makerId); From 025123bd6fd0bd94d68e54470ead137f764d3214 Mon Sep 17 00:00:00 2001 From: Maybeiley <2784519@gmail.com> Date: Mon, 24 Feb 2025 03:35:35 +0900 Subject: [PATCH 2/2] =?UTF-8?q?Feat:=20#262=20Auth=20=EB=8F=84=EB=A9=94?= =?UTF-8?q?=EC=9D=B8=20=EA=B5=AC=ED=98=84?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- src/modules/auth/auth.controller.ts | 82 +++++------ src/modules/auth/auth.repository.ts | 26 ++-- src/modules/auth/auth.service.ts | 13 +- src/modules/auth/domain/auth.domain.ts | 127 ++++++++++++++++++ src/modules/auth/domain/auth.interface.ts | 19 +++ src/modules/auth/domain/auth.mapper.ts | 22 +++ src/modules/auth/types/auth.types.ts | 59 ++++++++ src/modules/{user => auth}/types/login.dto.ts | 0 .../{user => auth}/types/signup.dto.ts | 0 .../profile/types/updateProfile.dto.ts | 2 +- src/modules/user/domain/user.domain.ts | 36 +---- src/modules/user/domain/user.interface.ts | 10 +- src/modules/user/types/user.types.ts | 21 +-- 13 files changed, 293 insertions(+), 124 deletions(-) create mode 100644 src/modules/auth/domain/auth.domain.ts create mode 100644 src/modules/auth/domain/auth.interface.ts create mode 100644 src/modules/auth/domain/auth.mapper.ts create mode 100644 src/modules/auth/types/auth.types.ts rename src/modules/{user => auth}/types/login.dto.ts (100%) rename src/modules/{user => auth}/types/signup.dto.ts (100%) diff --git a/src/modules/auth/auth.controller.ts b/src/modules/auth/auth.controller.ts index 2a6da1b..89d205c 100644 --- a/src/modules/auth/auth.controller.ts +++ b/src/modules/auth/auth.controller.ts @@ -9,16 +9,16 @@ import { ApiOperation, ApiUnauthorizedResponse } from '@nestjs/swagger'; -import SignupDTO from 'src/modules/user/types/signup.dto'; -import LoginDTO from 'src/modules/user/types/login.dto'; import { Cookies } from 'src/common/decorators/cookie.decorator'; import UnauthorizedError from 'src/common/errors/unauthorizedError'; import ErrorMessage from 'src/common/constants/errorMessage.enum'; import { Response } from 'express'; import { AuthGuard } from '@nestjs/passport'; import { User } from 'src/common/decorators/user.decorator'; -import { OAuthProperties } from 'src/modules/user/types/user.types'; +import { OAuthProperties } from 'src/modules/auth/types/auth.types'; import { OAuthProvider } from 'src/common/constants/oauth.type'; +import SignupDTO from './types/signup.dto'; +import LoginDTO from './types/login.dto'; @Controller('auth') export default class AuthController { @@ -60,6 +60,44 @@ export default class AuthController { return res.json({ accessToken }); } + @Public() + @Post('refresh/token') + @ApiCookieAuth('refreshToken') + @ApiOperation({ summary: '토큰 재발급', description: '유저의 Refresh Token을 확인하여 토큰을 재발급합니다' }) + @ApiCreatedResponse({ description: '{ accessToken }' }) + @ApiUnauthorizedResponse({ description: 'Refresh Token이 없거나 만료되었습니다' }) + async getNewToken(@Cookies('refreshToken') refreshToken: string, @Res() res: Response): Promise { + if (!refreshToken) { + throw new UnauthorizedError(ErrorMessage.REFRESH_TOKEN_NOT_FOUND); + } + + const { accessToken, refreshToken: newRefreshToken } = this.service.createNewToken(refreshToken); + + res.cookie('refreshToken', newRefreshToken, { + path: '/auth/refresh/token', + httpOnly: true, + sameSite: 'lax', + secure: true, + maxAge: 7 * 24 * 60 * 60 * 1000 + }); + + return res.json({ accessToken }); + } + + @Public() + @HttpCode(HttpStatus.OK) + @Post('check/email') + async checkEmail(@Body() body: { email: string }): Promise { + return await this.service.checkEmail(body.email); + } + + @Public() + @HttpCode(HttpStatus.OK) + @Post('check/nickname') + async checkNickName(@Body() body: { nickName: string }): Promise { + return await this.service.checkNickName(body.nickName); + } + @Public() @Get('google') toGoogle(): { redirectUrl: string } { @@ -147,42 +185,4 @@ export default class AuthController { return res.redirect(`${process.env.CLIENT_REDIRECT}?auth=${tokens.accessToken}`); } - - @Public() - @Post('refresh/token') - @ApiCookieAuth('refreshToken') - @ApiOperation({ summary: '토큰 재발급', description: '유저의 Refresh Token을 확인하여 토큰을 재발급합니다' }) - @ApiCreatedResponse({ description: '{ accessToken }' }) - @ApiUnauthorizedResponse({ description: 'Refresh Token이 없거나 만료되었습니다' }) - async getNewToken(@Cookies('refreshToken') refreshToken: string, @Res() res: Response): Promise { - if (!refreshToken) { - throw new UnauthorizedError(ErrorMessage.REFRESH_TOKEN_NOT_FOUND); - } - - const { accessToken, refreshToken: newRefreshToken } = this.service.createNewToken(refreshToken); - - res.cookie('refreshToken', newRefreshToken, { - path: '/auth/refresh/token', - httpOnly: true, - sameSite: 'lax', - secure: true, - maxAge: 7 * 24 * 60 * 60 * 1000 - }); - - return res.json({ accessToken }); - } - - @Public() - @HttpCode(HttpStatus.OK) - @Post('check/email') - async checkEmail(@Body() body: { email: string }): Promise { - return await this.service.checkEmail(body.email); - } - - @Public() - @HttpCode(HttpStatus.OK) - @Post('check/nickname') - async checkNickName(@Body() body: { nickName: string }): Promise { - return await this.service.checkNickName(body.nickName); - } } diff --git a/src/modules/auth/auth.repository.ts b/src/modules/auth/auth.repository.ts index 5716aec..3b21a92 100644 --- a/src/modules/auth/auth.repository.ts +++ b/src/modules/auth/auth.repository.ts @@ -1,40 +1,40 @@ import { Injectable } from '@nestjs/common'; -import { IUser } from 'src/modules/user/domain/user.interface'; -import UserMapper from 'src/modules/user/domain/user.mapper'; -import { OAuthProperties, SignupProperties } from 'src/modules/user/types/user.types'; +import { IAuth } from 'src/modules/auth/domain/auth.interface'; +import AuthMapper from 'src/modules/auth/domain/auth.mapper'; import DBClient from 'src/providers/database/prisma/DB.client'; +import { OAuthProperties, SignupProperties } from './types/auth.types'; @Injectable() export default class AuthRepository { constructor(private readonly db: DBClient) {} - async findByEmail(email: string): Promise { + async findByEmail(email: string): Promise { const data = await this.db.user.findUnique({ where: { email } }); - return new UserMapper(data).toDomain(); + return new AuthMapper(data).toDomain(); } - async findByNickName(nickName: string): Promise { + async findByNickName(nickName: string): Promise { const data = await this.db.user.findUnique({ where: { nickName } }); - return new UserMapper(data).toDomain(); + return new AuthMapper(data).toDomain(); } - async findById(id: string): Promise { + async findById(id: string): Promise { const data = await this.db.user.findUnique({ where: { id } }); - return new UserMapper(data).toDomain(); + return new AuthMapper(data).toDomain(); } - async findByProvider(providerData: OAuthProperties): Promise { + async findByProvider(providerData: OAuthProperties): Promise { const data = await this.db.user.findUnique({ where: { provider_providerId: providerData } }); - return new UserMapper(data).toDomain(); + return new AuthMapper(data).toDomain(); } - async create(user: SignupProperties): Promise { + async create(user: SignupProperties): Promise { const data = await this.db.user.create({ data: user }); - return new UserMapper(data).toDomain(); + return new AuthMapper(data).toDomain(); } } diff --git a/src/modules/auth/auth.service.ts b/src/modules/auth/auth.service.ts index b7ecc9f..c65cda1 100644 --- a/src/modules/auth/auth.service.ts +++ b/src/modules/auth/auth.service.ts @@ -2,16 +2,15 @@ import ErrorMessage from 'src/common/constants/errorMessage.enum'; import BadRequestError from 'src/common/errors/badRequestError'; import { JwtService } from '@nestjs/jwt'; import { Injectable } from '@nestjs/common'; -import User from 'src/modules/user/domain/user.domain'; -import { DreamerProfile, MakerProfile } from 'src/modules/profile/domain/profile.domain'; import UserStatsService from '../userStats/userStats.service'; -import { FilteredUserProperties, OAuthProperties, UserProperties } from 'src/modules/user/types/user.types'; +import { FilteredAuthProperties, OAuthProperties, AuthProperties } from 'src/modules/auth/types/auth.types'; import AuthRepository from './auth.repository'; -import { Role, RoleValues } from 'src/common/constants/role.type'; +import { Role } from 'src/common/constants/role.type'; import { DreamerProfileProperties, MakerProfileProperties } from 'src/modules/profile/types/profile.types'; import { OAuthProvider } from 'src/common/constants/oauth.type'; import UnauthorizedError from 'src/common/errors/unauthorizedError'; import ProfileService from '../profile/profile.service'; +import Auth from './domain/auth.domain'; @Injectable() export default class AuthService { @@ -22,7 +21,7 @@ export default class AuthService { private readonly userStats: UserStatsService ) {} - async createUser(user: UserProperties, profile: DreamerProfileProperties | MakerProfileProperties): Promise { + async createUser(user: AuthProperties, profile: DreamerProfileProperties | MakerProfileProperties): Promise { // 유저 등록: 소셜 로그인의 경우 이메일이 없어 중복 확인 패스 const { provider, providerId } = user; @@ -43,7 +42,7 @@ export default class AuthService { throw new BadRequestError(ErrorMessage.USER_NICKNAME_EXIST); } - const userData = await User.create(user); + const userData = await Auth.create(user); const savedUser = await this.repository.create(userData.signupData()); await this.profile.createProfile(savedUser.getId(), savedUser.getRole(), profile); @@ -54,7 +53,7 @@ export default class AuthService { return; } - async login(email: string, password: string): Promise { + async login(email: string, password: string): Promise { const user = await this.repository.findByEmail(email); if (!user) { throw new BadRequestError(ErrorMessage.USER_UNAUTHORIZED_ID); diff --git a/src/modules/auth/domain/auth.domain.ts b/src/modules/auth/domain/auth.domain.ts new file mode 100644 index 0000000..1eca870 --- /dev/null +++ b/src/modules/auth/domain/auth.domain.ts @@ -0,0 +1,127 @@ +import { Role } from 'src/common/constants/role.type'; +import { + FilteredAuthProperties, + OAuthProperties, + SignupProperties, + AuthProperties, + AuthPropertiesFromDB +} from '../types/auth.types'; +import { ComparePassword, HashingPassword } from '../../../common/utilities/hashingPassword'; +import { IAuth } from './auth.interface'; +import { OAuthProvider } from 'src/common/constants/oauth.type'; + +export default class Auth implements IAuth { + private readonly id?: string; + private role?: Role; + private nickName?: string; + private readonly email: string; + private password?: string; + private phoneNumber?: string; + private coconut: number; + private readonly provider: OAuthProvider; + private readonly providerId: string; + private readonly createdAt?: Date; + private readonly updatedAt?: Date; + + constructor(user: AuthPropertiesFromDB) { + this.id = user?.id; + this.role = user?.role; + this.nickName = user?.nickName; + this.email = user.email; + this.password = user?.password; + this.phoneNumber = user?.phoneNumber; + this.coconut = user.coconut ?? 0; + this.provider = user?.provider; + this.providerId = user?.providerId; + this.createdAt = user?.createdAt; + this.updatedAt = user?.updatedAt; + } + async validatePassword(password: string): Promise { + return ComparePassword(password, this.password); + } + + static async create(data: AuthPropertiesFromDB): Promise { + let hashedPassword: string | null = null; + if (data.password) { + hashedPassword = await HashingPassword(data.password); + } + return new Auth({ ...data, password: hashedPassword }); + } + + get(): AuthProperties { + return { + id: this.id, + role: this.role, + nickName: this.nickName, + email: this.email, + password: this.password, + phoneNumber: this.phoneNumber, + coconut: this.coconut, + createdAt: this.createdAt, + updatedAt: this.updatedAt + }; + } + + toClientAll(): Omit { + return { + id: this.id, + role: this.role, + nickName: this.nickName, + email: this.email, + phoneNumber: this.phoneNumber, + coconut: this.coconut + }; + } + + toClient(): FilteredAuthProperties { + return { + id: this.id, + role: this.role, + nickName: this.nickName, + coconut: this.coconut + }; + } + + toDB(): AuthProperties { + return { + id: this.id, + role: this.role, + nickName: this.nickName, + email: this.email, + password: this.password, + phoneNumber: this.phoneNumber, + coconut: this.coconut + }; + } + + signupData(): SignupProperties { + return { + role: this.role, + email: this?.email, + nickName: this.nickName, + password: this?.password, + phoneNumber: this.phoneNumber, + provider: this?.provider, + providerId: this?.providerId + }; + } + + OAuthData(): OAuthProperties { + return { + provider: this.provider, + providerId: this.providerId + }; + } + + getId(): string { + return this.id; + } + + getRole(): Role | null { + return this.role ?? null; + } + + getNickName(): string { + return this.nickName; + } +} diff --git a/src/modules/auth/domain/auth.interface.ts b/src/modules/auth/domain/auth.interface.ts new file mode 100644 index 0000000..f2056c5 --- /dev/null +++ b/src/modules/auth/domain/auth.interface.ts @@ -0,0 +1,19 @@ +import { Role } from 'src/common/constants/role.type'; +import { + FilteredAuthProperties, + OAuthProperties, + SignupProperties, + AuthProperties +} from 'src/modules/auth/types/auth.types'; + +export interface IAuth { + validatePassword(password: string): Promise; + get(): AuthProperties; + toClient(): FilteredAuthProperties; + toDB(): AuthProperties; + signupData(): SignupProperties; + OAuthData(): OAuthProperties; + getId(): string; + getRole(): Role | null; + getNickName(): string; +} diff --git a/src/modules/auth/domain/auth.mapper.ts b/src/modules/auth/domain/auth.mapper.ts new file mode 100644 index 0000000..d55cc5f --- /dev/null +++ b/src/modules/auth/domain/auth.mapper.ts @@ -0,0 +1,22 @@ +import { AuthPropertiesFromDB } from '../types/auth.types'; +import User from './auth.domain'; + +export default class AuthMapper { + constructor(private readonly user: AuthPropertiesFromDB) {} + + toDomain() { + if (!this.user) return null; + + return new User({ + id: this.user.id, + role: this.user.role, + nickName: this.user.nickName, + email: this.user.email, + password: this.user.password, + phoneNumber: this.user.phoneNumber, + coconut: this.user.coconut, + createdAt: this.user.createdAt, + updatedAt: this.user.updatedAt + }); + } +} diff --git a/src/modules/auth/types/auth.types.ts b/src/modules/auth/types/auth.types.ts new file mode 100644 index 0000000..18322d6 --- /dev/null +++ b/src/modules/auth/types/auth.types.ts @@ -0,0 +1,59 @@ +import { Role } from 'src/common/constants/role.type'; +import { MakerProfileProperties } from '../../profile/types/profile.types'; +import { UserStatsProperties } from '../../userStats/types/userStats.types'; +import { OAuthProvider } from 'src/common/constants/oauth.type'; + +export interface AuthProperties { + id?: string; + role?: Role; + nickName?: string; + email: string; + password?: string; + phoneNumber?: string; + coconut?: number; + provider?: OAuthProvider; + providerId?: string; + createdAt?: Date; + updatedAt?: Date; +} + +export interface SignupProperties { + role: Role; + email?: string; + nickName: string; + password?: string; + phoneNumber: string; + provider?: OAuthProvider; + providerId?: string; +} + +export interface OAuthProperties { + provider: OAuthProvider; + providerId: string; +} + +export interface AuthPropertiesFromDB { + id?: string; + role?: Role; + nickName?: string; + email: string; + password?: string; + phoneNumber?: string; + coconut?: number; + provider?: OAuthProvider; + providerId?: string; + createdAt?: Date; + updatedAt?: Date; +} + +export interface FilteredAuthProperties { + id?: string; + role: Role; + nickName: string; + coconut: number; +} + +export interface PasswordProperties { + password: string; + newPassword: string; +} diff --git a/src/modules/user/types/login.dto.ts b/src/modules/auth/types/login.dto.ts similarity index 100% rename from src/modules/user/types/login.dto.ts rename to src/modules/auth/types/login.dto.ts diff --git a/src/modules/user/types/signup.dto.ts b/src/modules/auth/types/signup.dto.ts similarity index 100% rename from src/modules/user/types/signup.dto.ts rename to src/modules/auth/types/signup.dto.ts diff --git a/src/modules/profile/types/updateProfile.dto.ts b/src/modules/profile/types/updateProfile.dto.ts index c8fd63a..abb48bd 100644 --- a/src/modules/profile/types/updateProfile.dto.ts +++ b/src/modules/profile/types/updateProfile.dto.ts @@ -1,4 +1,4 @@ import { PartialType } from '@nestjs/swagger'; -import { SignupProfileDTO } from 'src/modules/user/types/signup.dto'; +import { SignupProfileDTO } from 'src/modules/auth/types/signup.dto'; export default class UpdateProfileDTO extends PartialType(SignupProfileDTO) {} diff --git a/src/modules/user/domain/user.domain.ts b/src/modules/user/domain/user.domain.ts index 88063bd..ce448d2 100644 --- a/src/modules/user/domain/user.domain.ts +++ b/src/modules/user/domain/user.domain.ts @@ -1,12 +1,5 @@ import { Role } from 'src/common/constants/role.type'; -import { - FilteredUserProperties, - OAuthProperties, - PasswordProperties, - SignupProperties, - UserProperties, - UserPropertiesFromDB -} from '../types/user.types'; +import { FilteredUserProperties, PasswordProperties, UserProperties, UserPropertiesFromDB } from '../types/user.types'; import { ComparePassword, HashingPassword } from '../../../common/utilities/hashingPassword'; import { IUser } from './user.interface'; import BadRequestError from 'src/common/errors/badRequestError'; @@ -48,14 +41,6 @@ export default class User implements IUser { this.updatedAt = user?.updatedAt; } - static async create(data: UserPropertiesFromDB): Promise { - let hashedPassword: string | null = null; - if (data.password) { - hashedPassword = await HashingPassword(data.password); - } - return new User({ ...data, password: hashedPassword }); - } - async validatePassword(password: string): Promise { return ComparePassword(password, this.password); } @@ -132,25 +117,6 @@ export default class User implements IUser { }; } - signupData(): SignupProperties { - return { - role: this.role, - email: this?.email, - nickName: this.nickName, - password: this?.password, - phoneNumber: this.phoneNumber, - provider: this?.provider, - providerId: this?.providerId - }; - } - - OAuthData(): OAuthProperties { - return { - provider: this.provider, - providerId: this.providerId - }; - } - getId(): string { return this.id; } diff --git a/src/modules/user/domain/user.interface.ts b/src/modules/user/domain/user.interface.ts index b2aa1be..bf9a908 100644 --- a/src/modules/user/domain/user.interface.ts +++ b/src/modules/user/domain/user.interface.ts @@ -1,12 +1,6 @@ import { Role } from 'src/common/constants/role.type'; import { MakerInfoAndProfileProperties } from 'src/modules/profile/types/profile.types'; -import { - FilteredUserProperties, - OAuthProperties, - PasswordProperties, - SignupProperties, - UserProperties -} from 'src/modules/user/types/user.types'; +import { FilteredUserProperties, PasswordProperties, UserProperties } from 'src/modules/user/types/user.types'; import { UserStatsToClientProperties } from 'src/modules/userStats/types/userStats.types'; export interface IUser { @@ -17,8 +11,6 @@ export interface IUser { toClient(): FilteredUserProperties; toClientAll(): Omit; toDB(): UserProperties; - signupData(): SignupProperties; - OAuthData(): OAuthProperties; getId(): string; getRole(): Role | null; getNickName(): string; diff --git a/src/modules/user/types/user.types.ts b/src/modules/user/types/user.types.ts index dbd2e1f..137ddfe 100644 --- a/src/modules/user/types/user.types.ts +++ b/src/modules/user/types/user.types.ts @@ -1,10 +1,10 @@ -import { ProfileImage } from 'src/common/constants/image.type'; import { Role } from 'src/common/constants/role.type'; -import { TripType } from 'src/common/constants/tripType.type'; import { MakerProfileProperties } from '../../profile/types/profile.types'; -import SortOrder from 'src/common/constants/sortOrder.enum'; import { UserStatsProperties } from '../../userStats/types/userStats.types'; import { OAuthProvider } from 'src/common/constants/oauth.type'; +import { ProfileImage } from 'src/common/constants/image.type'; +import { TripType } from 'src/common/constants/tripType.type'; +import SortOrder from 'src/common/constants/sortOrder.enum'; export interface UserProperties { id?: string; @@ -20,21 +20,6 @@ export interface UserProperties { updatedAt?: Date; } -export interface SignupProperties { - role: Role; - email?: string; - nickName: string; - password?: string; - phoneNumber: string; - provider?: OAuthProvider; - providerId?: string; -} - -export interface OAuthProperties { - provider: OAuthProvider; - providerId: string; -} - export interface UserPropertiesFromDB { id?: string; role?: Role;