From 904b65c4a5c50d0290c2bf15c7e15564d7450a81 Mon Sep 17 00:00:00 2001 From: Son Daehyeon Date: Fri, 13 Sep 2024 17:47:34 +0900 Subject: [PATCH 1/8] refactor: remove unused import --- test/mock/module/auth.mock.ts | 1 - 1 file changed, 1 deletion(-) diff --git a/test/mock/module/auth.mock.ts b/test/mock/module/auth.mock.ts index 64903b5..039e314 100644 --- a/test/mock/module/auth.mock.ts +++ b/test/mock/module/auth.mock.ts @@ -15,7 +15,6 @@ import { AuthService } from '@wink/auth/service'; import { MemberRepository } from '@wink/member/repository'; import { Member } from '@wink/member/schema'; -import { RedisService } from '@wink/redis'; import { MailService } from '@wink/mail'; import { ConfigService } from '@nestjs/config'; From 546309480321f66c7a389a26535d9bcde81dda1d Mon Sep 17 00:00:00 2001 From: Son Daehyeon Date: Fri, 13 Sep 2024 17:47:47 +0900 Subject: [PATCH 2/8] =?UTF-8?q?refactor(eslint):=20=EB=AC=B8=EB=B2=95=20?= =?UTF-8?q?=EC=88=98=EC=A0=95?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- eslint.config.mjs | 20 +++++++------------- 1 file changed, 7 insertions(+), 13 deletions(-) diff --git a/eslint.config.mjs b/eslint.config.mjs index 7f1cc2f..e5aa415 100644 --- a/eslint.config.mjs +++ b/eslint.config.mjs @@ -4,17 +4,11 @@ import tseslint from 'typescript-eslint'; import prettierConfig from 'eslint-config-prettier'; import prettierRecommended from 'eslint-plugin-prettier/recommended'; -export default tseslint.config( - { - files: ['**/*.js', '**/*.mjs', '**/*.ts'], - extends: [eslint.configs.recommended, ...tseslint.configs.recommended], - rules: { - 'no-console': 'warn', - }, +export default tseslint.config({ + files: ['**/*.js', '**/*.mjs', '**/*.ts'], + extends: [eslint.configs.recommended, ...tseslint.configs.recommended, prettierRecommended], + rules: { + ...prettierConfig.rules, + 'no-console': 'warn', }, - { - files: ['**/*.js', '**/*.mjs', '**/*.ts'], - extends: [prettierRecommended], - rules: prettierConfig.rules, - }, -); +}); From 766b983e9adba16e410c4843b7ad758800784f30 Mon Sep 17 00:00:00 2001 From: Son Daehyeon Date: Fri, 13 Sep 2024 17:48:19 +0900 Subject: [PATCH 3/8] =?UTF-8?q?refactor(auth):=20=EC=A4=91=EB=B3=B5=20?= =?UTF-8?q?=EC=BD=94=EB=93=9C=20=EC=82=AD=EC=A0=9C?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- src/domain/member/constant/Role.ts | 34 ----------------------------- src/domain/member/constant/index.ts | 1 - 2 files changed, 35 deletions(-) delete mode 100644 src/domain/member/constant/Role.ts delete mode 100644 src/domain/member/constant/index.ts diff --git a/src/domain/member/constant/Role.ts b/src/domain/member/constant/Role.ts deleted file mode 100644 index 00b7e5b..0000000 --- a/src/domain/member/constant/Role.ts +++ /dev/null @@ -1,34 +0,0 @@ -export enum Role { - PRESIDENT = 'PRESIDENT', - VICE_PRESIDENT = 'VICE_PRESIDENT', - TREASURY_HEAD = 'TREASURY_HEAD', - TREASURY_ASSISTANT = 'TREASURY_ASSISTANT', - PUBLIC_RELATIONS_HEAD = 'PUBLIC_RELATIONS_HEAD', - PUBLIC_RELATIONS_ASSISTANT = 'PUBLIC_RELATIONS_ASSISTANT', - PLANNING_HEAD = 'PLANNING_HEAD', - PLANNING_ASSISTANT = 'PLANNING_ASSISTANT', - MEMBER = 'MEMBER', -} - -const roleHierarchy: { [key: string]: number } = { - [Role.PRESIDENT]: 1, - - [Role.VICE_PRESIDENT]: 2, - - [Role.TREASURY_HEAD]: 3, - [Role.PUBLIC_RELATIONS_HEAD]: 3, - [Role.PLANNING_HEAD]: 3, - - [Role.TREASURY_ASSISTANT]: 4, - [Role.PUBLIC_RELATIONS_ASSISTANT]: 4, - [Role.PLANNING_ASSISTANT]: 4, - - [Role.MEMBER]: 5, -}; - -export const checkRoleHierarchy = (myRole: Role, targetRole: Role): boolean => { - const myRoleIndex = roleHierarchy[myRole]; - const targetRoleIndex = roleHierarchy[targetRole]; - - return myRoleIndex < targetRoleIndex; -}; diff --git a/src/domain/member/constant/index.ts b/src/domain/member/constant/index.ts deleted file mode 100644 index 940d536..0000000 --- a/src/domain/member/constant/index.ts +++ /dev/null @@ -1 +0,0 @@ -export * from './Role'; From f0d6d797512fe53e60973652d0d489ee0a29bc5c Mon Sep 17 00:00:00 2001 From: Son Daehyeon Date: Fri, 13 Sep 2024 17:49:52 +0900 Subject: [PATCH 4/8] =?UTF-8?q?refactor(study):=20=EC=82=AC=EC=9A=A9?= =?UTF-8?q?=ED=95=98=EC=A7=80=20=EC=95=8A=EB=8A=94=20=EB=A9=94=EC=86=8C?= =?UTF-8?q?=EB=93=9C=20=EC=82=AD=EC=A0=9C?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- src/domain/activity/study/repository/category.repository.ts | 4 ---- src/domain/activity/study/repository/study.repository.ts | 4 ---- 2 files changed, 8 deletions(-) diff --git a/src/domain/activity/study/repository/category.repository.ts b/src/domain/activity/study/repository/category.repository.ts index b54496e..0591803 100644 --- a/src/domain/activity/study/repository/category.repository.ts +++ b/src/domain/activity/study/repository/category.repository.ts @@ -22,10 +22,6 @@ export class CategoryRepository { return this.categoryModel.find().exec(); } - async findById(id: string): Promise { - return this.categoryModel.findById(id).exec(); - } - async findByName(name: string): Promise { return this.categoryModel.findOne({ name }).exec(); } diff --git a/src/domain/activity/study/repository/study.repository.ts b/src/domain/activity/study/repository/study.repository.ts index 26feb0d..ef2e8b9 100644 --- a/src/domain/activity/study/repository/study.repository.ts +++ b/src/domain/activity/study/repository/study.repository.ts @@ -28,10 +28,6 @@ export class StudyRepository { .exec(); } - async findById(id: string): Promise { - return this.studyModel.findById(id).exec(); - } - // Delete async deleteById(id: string): Promise { await this.studyModel.deleteOne({ _id: id }).exec(); From 1b2ace34fd50d79414775083b20feef769fb920b Mon Sep 17 00:00:00 2001 From: Son Daehyeon Date: Fri, 13 Sep 2024 17:51:38 +0900 Subject: [PATCH 5/8] =?UTF-8?q?refactor(member):=20repository=20=EB=A9=94?= =?UTF-8?q?=EC=86=8C=EB=93=9C=20=EB=84=A4=EC=9D=B4=EB=B0=8D=20=ED=86=B5?= =?UTF-8?q?=EC=9D=BC?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- src/domain/member/repository/member.repository.ts | 12 ++++++------ src/domain/member/service/member.service.ts | 14 +++++++------- 2 files changed, 13 insertions(+), 13 deletions(-) diff --git a/src/domain/member/repository/member.repository.ts b/src/domain/member/repository/member.repository.ts index d163ad9..92ed673 100644 --- a/src/domain/member/repository/member.repository.ts +++ b/src/domain/member/repository/member.repository.ts @@ -36,27 +36,27 @@ export class MemberRepository { } // Update - async updatePassword(id: string, password: string): Promise { + async updatePasswordById(id: string, password: string): Promise { await this.memberModel.updateOne({ _id: id }, { password }).exec(); } - async updateDescription(id: string, description: string | null): Promise { + async updateDescriptionById(id: string, description: string | null): Promise { await this.memberModel.updateOne({ _id: id }, { description }).exec(); } - async updateGithub(id: string, githubUrl: string | null): Promise { + async updateGithubUrlById(id: string, githubUrl: string | null): Promise { await this.memberModel.updateOne({ _id: id }, { 'link.github': githubUrl }).exec(); } - async updateInstagram(id: string, instagramUrl: string | null): Promise { + async updateInstagramUrlById(id: string, instagramUrl: string | null): Promise { await this.memberModel.updateOne({ _id: id }, { 'link.instagram': instagramUrl }).exec(); } - async updateBlog(id: string, blog: string | null): Promise { + async updateBlogById(id: string, blog: string | null): Promise { await this.memberModel.updateOne({ _id: id }, { 'link.blog': blog }).exec(); } - async updateAvatar(id: string, avatar: string | null): Promise { + async updateAvatarById(id: string, avatar: string | null): Promise { await this.memberModel.updateOne({ _id: id }, { avatar }).exec(); } diff --git a/src/domain/member/service/member.service.ts b/src/domain/member/service/member.service.ts index c489120..c27de16 100644 --- a/src/domain/member/service/member.service.ts +++ b/src/domain/member/service/member.service.ts @@ -48,10 +48,10 @@ export class MemberService { ): Promise { const { _id: id } = member; - await this.memberRepository.updateDescription(id, description); - await this.memberRepository.updateGithub(id, github); - await this.memberRepository.updateInstagram(id, instagram); - await this.memberRepository.updateBlog(id, blog); + await this.memberRepository.updateDescriptionById(id, description); + await this.memberRepository.updateGithubUrlById(id, github); + await this.memberRepository.updateInstagramUrlById(id, instagram); + await this.memberRepository.updateBlogById(id, blog); this.eventEmitter.emit( UpdateMyInfoEvent.EVENT_NAME, @@ -73,7 +73,7 @@ export class MemberService { const salt = await bcrypt.genSalt(10); const hash = await bcrypt.hash(newPassword, salt); - await this.memberRepository.updatePassword(id, hash); + await this.memberRepository.updatePasswordById(id, hash); this.eventEmitter.emit(UpdateMyPasswordEvent.EVENT_NAME, new UpdateMyPasswordEvent(member)); } @@ -85,7 +85,7 @@ export class MemberService { const { _id: id, avatar: original } = member; const avatar = await this.avatarService.upload(file); - await this.memberRepository.updateAvatar(id, avatar); + await this.memberRepository.updateAvatarById(id, avatar); if (original) { const key = this.avatarService.extractKeyFromUrl(original); @@ -105,7 +105,7 @@ export class MemberService { const key = this.avatarService.extractKeyFromUrl(avatar); await this.avatarService.delete(key); - await this.memberRepository.updateAvatar(id, null); + await this.memberRepository.updateAvatarById(id, null); this.eventEmitter.emit(DeleteMyAvatarEvent.EVENT_NAME, new DeleteMyAvatarEvent(member)); } From 9ad7d99bcff113f386ed465760307e2971198d47 Mon Sep 17 00:00:00 2001 From: Son Daehyeon Date: Fri, 13 Sep 2024 17:54:49 +0900 Subject: [PATCH 6/8] =?UTF-8?q?fix(activity/study):=20author=20=EC=8B=9C?= =?UTF-8?q?=EA=B7=B8=EB=8B=88=EC=B2=98=20=EB=B3=80=EA=B2=BD?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- src/domain/activity/study/service/study.admin.service.ts | 9 +++++---- 1 file changed, 5 insertions(+), 4 deletions(-) diff --git a/src/domain/activity/study/service/study.admin.service.ts b/src/domain/activity/study/service/study.admin.service.ts index 62026cc..bc69854 100644 --- a/src/domain/activity/study/service/study.admin.service.ts +++ b/src/domain/activity/study/service/study.admin.service.ts @@ -68,13 +68,14 @@ export class StudyAdminService { const title = $('meta[property="og:title"]').attr('content')!; const content = $('meta[property="og:description"]').attr('content')!; + const author = $('meta[property="og.article.author"]').attr('content')!; const image = $('meta[property="og:image"]').attr('content')!; - const uploadedAt = $('meta[property="article:published_time"]').attr('content')!; + const rawUploadedAt = $('meta[property="article:published_time"]').attr('content')!; + const uploadedAt = new Date(new Date(rawUploadedAt).getTime() + 9 * 60 * 60 * 1000); const entryInfoMatch = html.match(/window\.T\.entryInfo\s*=\s*({[^}]*});/); const entryInfo = entryInfoMatch ? JSON.parse(entryInfoMatch[1]) : null; const categoryLabel = entryInfo['categoryLabel']; - const category = await this.categoryRepository.findByName(categoryLabel); if (!category) { @@ -82,11 +83,11 @@ export class StudyAdminService { } const study: Partial = { - author: member, title, content, + author, image, - uploadedAt: new Date(uploadedAt), + uploadedAt, link, category, }; From fbd4a9ec730e97fbeb06ba0ac39aaf1af21b8245 Mon Sep 17 00:00:00 2001 From: Son Daehyeon Date: Fri, 13 Sep 2024 17:56:02 +0900 Subject: [PATCH 7/8] =?UTF-8?q?fix(activity/study):=20controller=EC=97=90?= =?UTF-8?q?=EC=84=9C=20member=20=EA=B7=B8=EB=A7=8C=20=EB=B0=9B=EA=B8=B0?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../study/controller/study.admin.controller.ts | 11 +++-------- .../activity/study/service/study.admin.service.ts | 7 +------ 2 files changed, 4 insertions(+), 14 deletions(-) diff --git a/src/domain/activity/study/controller/study.admin.controller.ts b/src/domain/activity/study/controller/study.admin.controller.ts index 90c84c8..3708200 100644 --- a/src/domain/activity/study/controller/study.admin.controller.ts +++ b/src/domain/activity/study/controller/study.admin.controller.ts @@ -1,9 +1,7 @@ import { Body, Controller, Delete, Patch, Put } from '@nestjs/common'; import { ApiOperation, ApiProperty, ApiTags } from '@nestjs/swagger'; -import { AuthAdminAccount, AuthAdminAccountException, ReqMember } from '@wink/auth/guard'; - -import { Member } from '@wink/member/schema'; +import { AuthAdminAccount, AuthAdminAccountException } from '@wink/auth/guard'; import { CreateCategoryRequestDto, @@ -66,11 +64,8 @@ export class StudyAdminController { @ApiProperty({ type: CreateStudyRequestDto }) @ApiCustomResponse(CreateStudyResponseDto) @ApiCustomErrorResponse([...AuthAdminAccountException, CategoryNotFoundException]) - async createStudy( - @ReqMember() member: Member, - @Body() request: CreateStudyRequestDto, - ): Promise { - return this.studyAdminService.createStudy(member, request); + async createStudy(@Body() request: CreateStudyRequestDto): Promise { + return this.studyAdminService.createStudy(request); } @Delete() diff --git a/src/domain/activity/study/service/study.admin.service.ts b/src/domain/activity/study/service/study.admin.service.ts index bc69854..8c5c207 100644 --- a/src/domain/activity/study/service/study.admin.service.ts +++ b/src/domain/activity/study/service/study.admin.service.ts @@ -1,7 +1,5 @@ import { Injectable } from '@nestjs/common'; -import { Member } from '@wink/member/schema'; - import { CreateCategoryRequestDto, CreateCategoryResponseDto, @@ -59,10 +57,7 @@ export class StudyAdminService { await this.categoryRepository.deleteById(categoryId); } - async createStudy( - member: Member, - { link }: CreateStudyRequestDto, - ): Promise { + async createStudy({ link }: CreateStudyRequestDto): Promise { const { data: html } = await axios.get(link); const $ = cheerio.load(html); From dc1a399589949611d4f8ed24151c02cb32c383a2 Mon Sep 17 00:00:00 2001 From: Son Daehyeon Date: Fri, 13 Sep 2024 17:58:37 +0900 Subject: [PATCH 8/8] =?UTF-8?q?fix(activity/study):=20=EC=A4=91=EB=B3=B5?= =?UTF-8?q?=EB=90=9C=20study=EA=B0=80=20=EC=83=9D=EC=84=B1=EB=90=98?= =?UTF-8?q?=EC=A7=80=20=EC=95=8A=EB=8F=84=EB=A1=9D=20=EC=88=98=EC=A0=95?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../study/controller/study.admin.controller.ts | 7 ++++++- .../exception/already-exists-study.exception.ts | 13 +++++++++++++ src/domain/activity/study/exception/index.ts | 2 +- .../activity/study/repository/study.repository.ts | 4 ++++ .../activity/study/service/study.admin.service.ts | 5 +++++ 5 files changed, 29 insertions(+), 2 deletions(-) create mode 100644 src/domain/activity/study/exception/already-exists-study.exception.ts diff --git a/src/domain/activity/study/controller/study.admin.controller.ts b/src/domain/activity/study/controller/study.admin.controller.ts index 3708200..54be335 100644 --- a/src/domain/activity/study/controller/study.admin.controller.ts +++ b/src/domain/activity/study/controller/study.admin.controller.ts @@ -14,6 +14,7 @@ import { } from '@wink/activity/dto'; import { AlreadyExistsCategoryException, + AlreadyExistsStudyException, CategoryNotFoundException, StudyNotFoundException, } from '@wink/activity/exception'; @@ -63,7 +64,11 @@ export class StudyAdminController { @ApiOperation({ summary: '스터디 생성' }) @ApiProperty({ type: CreateStudyRequestDto }) @ApiCustomResponse(CreateStudyResponseDto) - @ApiCustomErrorResponse([...AuthAdminAccountException, CategoryNotFoundException]) + @ApiCustomErrorResponse([ + ...AuthAdminAccountException, + CategoryNotFoundException, + AlreadyExistsStudyException, + ]) async createStudy(@Body() request: CreateStudyRequestDto): Promise { return this.studyAdminService.createStudy(request); } diff --git a/src/domain/activity/study/exception/already-exists-study.exception.ts b/src/domain/activity/study/exception/already-exists-study.exception.ts new file mode 100644 index 0000000..c316d0c --- /dev/null +++ b/src/domain/activity/study/exception/already-exists-study.exception.ts @@ -0,0 +1,13 @@ +import { HttpStatus } from '@nestjs/common'; + +import { ApiException } from '@wink/swagger'; + +export class AlreadyExistsStudyException extends ApiException { + constructor() { + super({ + swagger: '스터디가 이미 존재하는 경우', + message: '스터디가 이미 존재합니다.', + code: HttpStatus.CONFLICT, + }); + } +} diff --git a/src/domain/activity/study/exception/index.ts b/src/domain/activity/study/exception/index.ts index 00950df..25a8e11 100644 --- a/src/domain/activity/study/exception/index.ts +++ b/src/domain/activity/study/exception/index.ts @@ -1,4 +1,4 @@ export * from './already-exists-category.exception'; +export * from './already-exists-study.exception'; export * from './category-not-found.exception'; - export * from './study-not-found.exception'; diff --git a/src/domain/activity/study/repository/study.repository.ts b/src/domain/activity/study/repository/study.repository.ts index ef2e8b9..051a62f 100644 --- a/src/domain/activity/study/repository/study.repository.ts +++ b/src/domain/activity/study/repository/study.repository.ts @@ -37,4 +37,8 @@ export class StudyRepository { async existsById(id: string): Promise { return !!(await this.studyModel.exists({ _id: id }).exec()); } + + async existsByLink(link: string): Promise { + return !!(await this.studyModel.exists({ link }).exec()); + } } diff --git a/src/domain/activity/study/service/study.admin.service.ts b/src/domain/activity/study/service/study.admin.service.ts index 8c5c207..4668ff5 100644 --- a/src/domain/activity/study/service/study.admin.service.ts +++ b/src/domain/activity/study/service/study.admin.service.ts @@ -11,6 +11,7 @@ import { } from '@wink/activity/dto'; import { AlreadyExistsCategoryException, + AlreadyExistsStudyException, CategoryNotFoundException, StudyNotFoundException, } from '@wink/activity/exception'; @@ -58,6 +59,10 @@ export class StudyAdminService { } async createStudy({ link }: CreateStudyRequestDto): Promise { + if (await this.studyRepository.existsByLink(link)) { + throw new AlreadyExistsStudyException(); + } + const { data: html } = await axios.get(link); const $ = cheerio.load(html);