From aee0a395fd1d944152a921afb5c15418c192ab6c Mon Sep 17 00:00:00 2001 From: Conut-1 <1mim1@naver.com> Date: Tue, 30 Apr 2024 15:07:28 +0900 Subject: [PATCH 01/17] =?UTF-8?q?fix:=20PrismaServiceMongoDB=20=EC=A0=9C?= =?UTF-8?q?=EA=B1=B0?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- nestjs-BE/server/src/auth/auth.service.ts | 4 +-- nestjs-BE/server/src/base/base.service.ts | 9 ++--- .../src/invite-codes/invite-codes.service.ts | 4 +-- nestjs-BE/server/src/prisma/prisma.module.ts | 7 ++-- nestjs-BE/server/src/prisma/prisma.service.ts | 18 ++-------- .../profile-space/profile-space.service.ts | 4 +-- .../server/src/profiles/profiles.service.ts | 4 +-- nestjs-BE/server/src/spaces/spaces.service.ts | 4 +-- .../temporary-database.service.ts | 33 ++++++------------- nestjs-BE/server/src/users/users.service.ts | 4 +-- 10 files changed, 30 insertions(+), 61 deletions(-) diff --git a/nestjs-BE/server/src/auth/auth.service.ts b/nestjs-BE/server/src/auth/auth.service.ts index 98f7adb9..98035968 100644 --- a/nestjs-BE/server/src/auth/auth.service.ts +++ b/nestjs-BE/server/src/auth/auth.service.ts @@ -2,7 +2,7 @@ import { Injectable, UnauthorizedException, HttpStatus } from '@nestjs/common'; import { JwtService } from '@nestjs/jwt'; import { jwtConstants, kakaoOauthConstants } from './constants'; import { stringify } from 'qs'; -import { PrismaServiceMySQL } from 'src/prisma/prisma.service'; +import { PrismaService } from 'src/prisma/prisma.service'; import { TemporaryDatabaseService } from 'src/temporary-database/temporary-database.service'; import { BaseService } from 'src/base/base.service'; import { @@ -27,7 +27,7 @@ export interface TokenData { export class AuthService extends BaseService { constructor( private jwtService: JwtService, - protected prisma: PrismaServiceMySQL, + protected prisma: PrismaService, protected temporaryDatabaseService: TemporaryDatabaseService, ) { super({ diff --git a/nestjs-BE/server/src/base/base.service.ts b/nestjs-BE/server/src/base/base.service.ts index 5c9394c8..2045dbed 100644 --- a/nestjs-BE/server/src/base/base.service.ts +++ b/nestjs-BE/server/src/base/base.service.ts @@ -1,15 +1,12 @@ import { HttpException, HttpStatus } from '@nestjs/common'; -import { - PrismaServiceMySQL, - PrismaServiceMongoDB, -} from '../prisma/prisma.service'; +import { PrismaService } from '../prisma/prisma.service'; import { TemporaryDatabaseService } from '../temporary-database/temporary-database.service'; import LRUCache from '../utils/lru-cache'; import generateUuid from '../utils/uuid'; import { ResponseUtils } from 'src/utils/response'; interface BaseServiceOptions { - prisma: PrismaServiceMySQL | PrismaServiceMongoDB; + prisma: PrismaService; temporaryDatabaseService: TemporaryDatabaseService; cacheSize: number; className: string; @@ -24,7 +21,7 @@ export abstract class BaseService { protected cache: LRUCache; protected className: string; protected field: string; - protected prisma: PrismaServiceMySQL | PrismaServiceMongoDB; + protected prisma: PrismaService; protected temporaryDatabaseService: TemporaryDatabaseService; constructor(options: BaseServiceOptions) { diff --git a/nestjs-BE/server/src/invite-codes/invite-codes.service.ts b/nestjs-BE/server/src/invite-codes/invite-codes.service.ts index 8c8f54e7..5a690b15 100644 --- a/nestjs-BE/server/src/invite-codes/invite-codes.service.ts +++ b/nestjs-BE/server/src/invite-codes/invite-codes.service.ts @@ -1,7 +1,7 @@ import { Injectable, HttpException, HttpStatus } from '@nestjs/common'; import { CreateInviteCodeDto } from './dto/create-invite-code.dto'; import { BaseService } from 'src/base/base.service'; -import { PrismaServiceMySQL } from 'src/prisma/prisma.service'; +import { PrismaService } from 'src/prisma/prisma.service'; import { TemporaryDatabaseService } from 'src/temporary-database/temporary-database.service'; import { INVITE_CODE_CACHE_SIZE, @@ -20,7 +20,7 @@ export interface InviteCodeData extends CreateInviteCodeDto { @Injectable() export class InviteCodesService extends BaseService { constructor( - protected prisma: PrismaServiceMySQL, + protected prisma: PrismaService, protected temporaryDatabaseService: TemporaryDatabaseService, protected spacesService: SpacesService, ) { diff --git a/nestjs-BE/server/src/prisma/prisma.module.ts b/nestjs-BE/server/src/prisma/prisma.module.ts index 01b27f2d..7207426f 100644 --- a/nestjs-BE/server/src/prisma/prisma.module.ts +++ b/nestjs-BE/server/src/prisma/prisma.module.ts @@ -1,10 +1,9 @@ import { Global, Module } from '@nestjs/common'; -import { PrismaServiceMySQL } from './prisma.service'; -import { PrismaServiceMongoDB } from './prisma.service'; +import { PrismaService } from './prisma.service'; @Global() @Module({ - providers: [PrismaServiceMySQL, PrismaServiceMongoDB], - exports: [PrismaServiceMySQL, PrismaServiceMongoDB], + providers: [PrismaService], + exports: [PrismaService], }) export class PrismaModule {} diff --git a/nestjs-BE/server/src/prisma/prisma.service.ts b/nestjs-BE/server/src/prisma/prisma.service.ts index 5559b4ea..c2fef447 100644 --- a/nestjs-BE/server/src/prisma/prisma.service.ts +++ b/nestjs-BE/server/src/prisma/prisma.service.ts @@ -1,22 +1,8 @@ import { Injectable, OnModuleInit } from '@nestjs/common'; -import { PrismaClient as PrismaClientMySQL } from '../../prisma/generated/mysql'; -import { PrismaClient as PrismaClientMongoDB } from '../../prisma/generated/mongodb'; +import { PrismaClient } from '../../prisma/generated/mysql'; @Injectable() -export class PrismaServiceMySQL - extends PrismaClientMySQL - implements OnModuleInit -{ - async onModuleInit() { - await this.$connect(); - } -} - -@Injectable() -export class PrismaServiceMongoDB - extends PrismaClientMongoDB - implements OnModuleInit -{ +export class PrismaService extends PrismaClient implements OnModuleInit { async onModuleInit() { await this.$connect(); } diff --git a/nestjs-BE/server/src/profile-space/profile-space.service.ts b/nestjs-BE/server/src/profile-space/profile-space.service.ts index a24f791a..305f8d32 100644 --- a/nestjs-BE/server/src/profile-space/profile-space.service.ts +++ b/nestjs-BE/server/src/profile-space/profile-space.service.ts @@ -1,7 +1,7 @@ import { Injectable, HttpStatus } from '@nestjs/common'; import { UpdateProfileSpaceDto } from './dto/update-profile-space.dto'; import { BaseService } from 'src/base/base.service'; -import { PrismaServiceMySQL } from 'src/prisma/prisma.service'; +import { PrismaService } from 'src/prisma/prisma.service'; import { TemporaryDatabaseService } from 'src/temporary-database/temporary-database.service'; import { PROFILE_SPACE_CACHE_SIZE, @@ -25,7 +25,7 @@ export class ProfileSpaceService extends BaseService { private readonly userCache: LRUCache; private readonly spaceCache: LRUCache; constructor( - protected prisma: PrismaServiceMySQL, + protected prisma: PrismaService, protected temporaryDatabaseService: TemporaryDatabaseService, private readonly profilesService: ProfilesService, ) { diff --git a/nestjs-BE/server/src/profiles/profiles.service.ts b/nestjs-BE/server/src/profiles/profiles.service.ts index 0a1027fb..26849c8a 100644 --- a/nestjs-BE/server/src/profiles/profiles.service.ts +++ b/nestjs-BE/server/src/profiles/profiles.service.ts @@ -1,5 +1,5 @@ import { Injectable } from '@nestjs/common'; -import { PrismaServiceMySQL } from '../prisma/prisma.service'; +import { PrismaService } from '../prisma/prisma.service'; import { TemporaryDatabaseService } from '../temporary-database/temporary-database.service'; import { BaseService } from '../base/base.service'; import { PROFILE_CACHE_SIZE } from 'src/config/magic-number'; @@ -8,7 +8,7 @@ import { UpdateProfileDto } from './dto/update-profile.dto'; @Injectable() export class ProfilesService extends BaseService { constructor( - protected prisma: PrismaServiceMySQL, + protected prisma: PrismaService, protected temporaryDatabaseService: TemporaryDatabaseService, ) { super({ diff --git a/nestjs-BE/server/src/spaces/spaces.service.ts b/nestjs-BE/server/src/spaces/spaces.service.ts index 4eba8ff0..7ddbc276 100644 --- a/nestjs-BE/server/src/spaces/spaces.service.ts +++ b/nestjs-BE/server/src/spaces/spaces.service.ts @@ -1,5 +1,5 @@ import { Injectable } from '@nestjs/common'; -import { PrismaServiceMySQL } from '../prisma/prisma.service'; +import { PrismaService } from '../prisma/prisma.service'; import { TemporaryDatabaseService } from '../temporary-database/temporary-database.service'; import { BaseService } from '../base/base.service'; import { SPACE_CACHE_SIZE } from 'src/config/magic-number'; @@ -9,7 +9,7 @@ import { UpdateProfileDto } from 'src/profiles/dto/update-profile.dto'; @Injectable() export class SpacesService extends BaseService { constructor( - protected prisma: PrismaServiceMySQL, + protected prisma: PrismaService, protected temporaryDatabaseService: TemporaryDatabaseService, ) { super({ diff --git a/nestjs-BE/server/src/temporary-database/temporary-database.service.ts b/nestjs-BE/server/src/temporary-database/temporary-database.service.ts index 873c8b82..e712272c 100644 --- a/nestjs-BE/server/src/temporary-database/temporary-database.service.ts +++ b/nestjs-BE/server/src/temporary-database/temporary-database.service.ts @@ -1,8 +1,5 @@ import { Injectable } from '@nestjs/common'; -import { - PrismaServiceMySQL, - PrismaServiceMongoDB, -} from '../prisma/prisma.service'; +import { PrismaService } from '../prisma/prisma.service'; import { Cron } from '@nestjs/schedule'; import { promises as fs } from 'fs'; import { join } from 'path'; @@ -46,10 +43,7 @@ export class TemporaryDatabaseService { private database: Map>> = new Map(); private readonly FOLDER_NAME = CSV_FOLDER; - constructor( - private readonly prismaMysql: PrismaServiceMySQL, - private readonly prismaMongoDB: PrismaServiceMongoDB, - ) { + constructor(private readonly prismaMysql: PrismaService) { this.init(); } @@ -143,8 +137,7 @@ export class TemporaryDatabaseService { private async executeBulkOperations() { for (const service of this.database.keys()) { const serviceMap = this.database.get(service); - const prisma = - service === 'BoardCollection' ? this.prismaMongoDB : this.prismaMysql; + const prisma = this.prismaMysql; await this.performInsert(service, serviceMap.get('insert'), prisma); await this.performUpdate(service, serviceMap.get('update'), prisma); await this.performDelete(service, serviceMap.get('delete'), prisma); @@ -154,26 +147,20 @@ export class TemporaryDatabaseService { private async performInsert( service: string, dataMap: Map, - prisma: PrismaServiceMongoDB | PrismaServiceMySQL, + prisma: PrismaService, ) { const data = this.prepareData(service, 'insert', dataMap); if (!data.length) return; - if (prisma instanceof PrismaServiceMySQL) { - await prisma[service].createMany({ - data: data, - skipDuplicates: true, - }); - } else { - await prisma[service].createMany({ - data: data, - }); - } + await prisma[service].createMany({ + data: data, + skipDuplicates: true, + }); } private async performUpdate( service: string, dataMap: Map, - prisma: PrismaServiceMongoDB | PrismaServiceMySQL, + prisma: PrismaService, ) { const data = this.prepareData(service, 'update', dataMap); if (!data.length) return; @@ -195,7 +182,7 @@ export class TemporaryDatabaseService { private async performDelete( service: string, dataMap: Map, - prisma: PrismaServiceMongoDB | PrismaServiceMySQL, + prisma: PrismaService, ) { const data = this.prepareData(service, 'delete', dataMap); if (!data.length) return; diff --git a/nestjs-BE/server/src/users/users.service.ts b/nestjs-BE/server/src/users/users.service.ts index be26c750..519923d6 100644 --- a/nestjs-BE/server/src/users/users.service.ts +++ b/nestjs-BE/server/src/users/users.service.ts @@ -1,5 +1,5 @@ import { Injectable } from '@nestjs/common'; -import { PrismaServiceMySQL } from '../prisma/prisma.service'; +import { PrismaService } from '../prisma/prisma.service'; import { TemporaryDatabaseService } from '../temporary-database/temporary-database.service'; import { BaseService } from '../base/base.service'; import { USER_CACHE_SIZE } from '../config/magic-number'; @@ -8,7 +8,7 @@ import { UpdateUserDto } from './dto/update-user.dto'; @Injectable() export class UsersService extends BaseService { constructor( - protected prisma: PrismaServiceMySQL, + protected prisma: PrismaService, protected temporaryDatabaseService: TemporaryDatabaseService, ) { super({ From 59f49cf5a71944e751c52453ca41dbf27b720113 Mon Sep 17 00:00:00 2001 From: Conut-1 <1mim1@naver.com> Date: Tue, 30 Apr 2024 15:18:58 +0900 Subject: [PATCH 02/17] =?UTF-8?q?fix:=20mongodb=20=EC=8A=A4=ED=82=A4?= =?UTF-8?q?=EB=A7=88=20=EC=A0=9C=EA=B1=B0?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- nestjs-BE/server/Dockerfile | 4 +--- nestjs-BE/server/prisma/mongodb.schema.prisma | 14 -------------- .../prisma/{mysql.schema.prisma => schema.prisma} | 1 - nestjs-BE/server/src/prisma/prisma.service.ts | 2 +- 4 files changed, 2 insertions(+), 19 deletions(-) delete mode 100644 nestjs-BE/server/prisma/mongodb.schema.prisma rename nestjs-BE/server/prisma/{mysql.schema.prisma => schema.prisma} (98%) diff --git a/nestjs-BE/server/Dockerfile b/nestjs-BE/server/Dockerfile index 8ebcd69f..9ba8cdb5 100644 --- a/nestjs-BE/server/Dockerfile +++ b/nestjs-BE/server/Dockerfile @@ -10,9 +10,7 @@ RUN npm ci COPY ./ ./ -RUN npx prisma generate --schema=./prisma/mysql.schema.prisma - -RUN npx prisma generate --schema=./prisma/mongodb.schema.prisma +RUN npx prisma migrate dev EXPOSE 3000 diff --git a/nestjs-BE/server/prisma/mongodb.schema.prisma b/nestjs-BE/server/prisma/mongodb.schema.prisma deleted file mode 100644 index 7fbb0264..00000000 --- a/nestjs-BE/server/prisma/mongodb.schema.prisma +++ /dev/null @@ -1,14 +0,0 @@ -generator client { - provider = "prisma-client-js" - output = "./generated/mongodb" -} - -datasource db { - provider = "mongodb" - url = env("MONGODB_DATABASE_URL") -} - -model BoardCollection { - uuid String @id @map("_id") - data Json -} \ No newline at end of file diff --git a/nestjs-BE/server/prisma/mysql.schema.prisma b/nestjs-BE/server/prisma/schema.prisma similarity index 98% rename from nestjs-BE/server/prisma/mysql.schema.prisma rename to nestjs-BE/server/prisma/schema.prisma index 89eae437..cc497160 100644 --- a/nestjs-BE/server/prisma/mysql.schema.prisma +++ b/nestjs-BE/server/prisma/schema.prisma @@ -1,6 +1,5 @@ generator client { provider = "prisma-client-js" - output = "./generated/mysql" } datasource db { diff --git a/nestjs-BE/server/src/prisma/prisma.service.ts b/nestjs-BE/server/src/prisma/prisma.service.ts index c2fef447..359f950b 100644 --- a/nestjs-BE/server/src/prisma/prisma.service.ts +++ b/nestjs-BE/server/src/prisma/prisma.service.ts @@ -1,5 +1,5 @@ import { Injectable, OnModuleInit } from '@nestjs/common'; -import { PrismaClient } from '../../prisma/generated/mysql'; +import { PrismaClient } from '@prisma/client'; @Injectable() export class PrismaService extends PrismaClient implements OnModuleInit { From 7d005fe9ed76d7622f0f972c2b02325333945b49 Mon Sep 17 00:00:00 2001 From: Conut-1 <1mim1@naver.com> Date: Tue, 30 Apr 2024 16:52:48 +0900 Subject: [PATCH 03/17] =?UTF-8?q?refactor:=20=EB=B6=88=ED=95=84=EC=9A=94?= =?UTF-8?q?=ED=95=9C=20=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 --- nestjs-BE/server/src/users/users.module.ts | 3 +-- 1 file changed, 1 insertion(+), 2 deletions(-) diff --git a/nestjs-BE/server/src/users/users.module.ts b/nestjs-BE/server/src/users/users.module.ts index 153ff941..8fa904f1 100644 --- a/nestjs-BE/server/src/users/users.module.ts +++ b/nestjs-BE/server/src/users/users.module.ts @@ -1,9 +1,8 @@ import { Module } from '@nestjs/common'; import { UsersService } from './users.service'; -import { SpacesService } from 'src/spaces/spaces.service'; @Module({ - providers: [UsersService, SpacesService], + providers: [UsersService], exports: [UsersService], }) export class UsersModule {} From d8b3831ddb86db2650191756eb2dc5a8f11a044d Mon Sep 17 00:00:00 2001 From: Conut-1 <1mim1@naver.com> Date: Tue, 30 Apr 2024 16:53:50 +0900 Subject: [PATCH 04/17] =?UTF-8?q?fix:=20User=20=EB=AA=A8=EB=8D=B8=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 --- .../20240430062129_set_max_length/migration.sql | 12 ++++++++++++ .../20240430064956_rename_user_model/migration.sql | 5 +++++ nestjs-BE/server/prisma/schema.prisma | 7 +++---- 3 files changed, 20 insertions(+), 4 deletions(-) create mode 100644 nestjs-BE/server/prisma/migrations/20240430062129_set_max_length/migration.sql create mode 100644 nestjs-BE/server/prisma/migrations/20240430064956_rename_user_model/migration.sql diff --git a/nestjs-BE/server/prisma/migrations/20240430062129_set_max_length/migration.sql b/nestjs-BE/server/prisma/migrations/20240430062129_set_max_length/migration.sql new file mode 100644 index 00000000..1d5ef029 --- /dev/null +++ b/nestjs-BE/server/prisma/migrations/20240430062129_set_max_length/migration.sql @@ -0,0 +1,12 @@ +/* + Warnings: + + - You are about to alter the column `nickname` on the `PROFILE_TB` table. The data in that column could be lost. The data in that column will be cast from `VarChar(191)` to `VarChar(20)`. + - You are about to alter the column `name` on the `SPACE_TB` table. The data in that column could be lost. The data in that column will be cast from `VarChar(191)` to `VarChar(20)`. + +*/ +-- AlterTable +ALTER TABLE `PROFILE_TB` MODIFY `nickname` VARCHAR(20) NOT NULL; + +-- AlterTable +ALTER TABLE `SPACE_TB` MODIFY `name` VARCHAR(20) NOT NULL; diff --git a/nestjs-BE/server/prisma/migrations/20240430064956_rename_user_model/migration.sql b/nestjs-BE/server/prisma/migrations/20240430064956_rename_user_model/migration.sql new file mode 100644 index 00000000..b03db2d1 --- /dev/null +++ b/nestjs-BE/server/prisma/migrations/20240430064956_rename_user_model/migration.sql @@ -0,0 +1,5 @@ +-- RenameIndex +ALTER TABLE `USER_TB` RENAME INDEX `USER_TB_email_provider_key` TO `User_email_provider_key`; + +-- RenameTable +ALTER TABLE `USER_TB` RENAME `User`; diff --git a/nestjs-BE/server/prisma/schema.prisma b/nestjs-BE/server/prisma/schema.prisma index cc497160..01baa5ab 100644 --- a/nestjs-BE/server/prisma/schema.prisma +++ b/nestjs-BE/server/prisma/schema.prisma @@ -7,7 +7,7 @@ datasource db { url = env("MYSQL_DATABASE_URL") } -model USER_TB { +model User { uuid String @id @db.VarChar(32) email String provider String @@ -20,7 +20,7 @@ model REFRESH_TOKEN_TB { uuid String @id @db.VarChar(32) expiry_date DateTime user_id String - user USER_TB @relation(fields: [user_id], references: [uuid], onDelete: Cascade) + user User @relation(fields: [user_id], references: [uuid], onDelete: Cascade) } model PROFILE_TB { @@ -28,7 +28,7 @@ model PROFILE_TB { user_id String @unique @db.VarChar(32) image String nickname String @db.VarChar(20) - user USER_TB @relation(fields: [user_id], references: [uuid], onDelete: Cascade) + user User @relation(fields: [user_id], references: [uuid], onDelete: Cascade) spaces PROFILE_SPACE_TB[] } @@ -55,4 +55,3 @@ model INVITE_CODE_TB { expiry_date DateTime space SPACE_TB @relation(fields: [space_uuid], references: [uuid], onDelete: Cascade) } - From 843a1a0321a2018c63a9bcbade2078ab4ea6b2b6 Mon Sep 17 00:00:00 2001 From: Conut-1 <1mim1@naver.com> Date: Tue, 30 Apr 2024 16:55:12 +0900 Subject: [PATCH 05/17] =?UTF-8?q?fix:=20UsersService=20=EC=88=98=EC=A0=95?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- nestjs-BE/server/src/auth/auth.service.ts | 12 ++++--- nestjs-BE/server/src/users/users.service.ts | 37 +++++++++++---------- 2 files changed, 27 insertions(+), 22 deletions(-) diff --git a/nestjs-BE/server/src/auth/auth.service.ts b/nestjs-BE/server/src/auth/auth.service.ts index 98035968..b9daa5dd 100644 --- a/nestjs-BE/server/src/auth/auth.service.ts +++ b/nestjs-BE/server/src/auth/auth.service.ts @@ -125,9 +125,11 @@ export class AuthService extends BaseService { } async findUser(usersService: UsersService, email: string, provider: string) { - const key = usersService.generateKey({ email, provider }); - const findUserData = await usersService.getDataFromCacheOrDB(key); - return findUserData?.uuid; + const userData = await usersService.findUserByEmailAndProvider( + email, + provider, + ); + return userData?.uuid; } async createUser( @@ -135,8 +137,8 @@ export class AuthService extends BaseService { usersService: UsersService, profilesService: ProfilesService, ) { - const createdData = await usersService.create(data); - const userUuid = createdData.data.uuid; + const createdUser = await usersService.createUser(data); + const userUuid = createdUser.uuid; const profileData = { user_id: userUuid, image: BASE_IMAGE_URL, diff --git a/nestjs-BE/server/src/users/users.service.ts b/nestjs-BE/server/src/users/users.service.ts index 519923d6..66110712 100644 --- a/nestjs-BE/server/src/users/users.service.ts +++ b/nestjs-BE/server/src/users/users.service.ts @@ -1,26 +1,29 @@ import { Injectable } from '@nestjs/common'; import { PrismaService } from '../prisma/prisma.service'; -import { TemporaryDatabaseService } from '../temporary-database/temporary-database.service'; -import { BaseService } from '../base/base.service'; -import { USER_CACHE_SIZE } from '../config/magic-number'; -import { UpdateUserDto } from './dto/update-user.dto'; +import { CreateUserDto } from './dto/create-user.dto'; +import { User } from '@prisma/client'; +import generateUuid from '../utils/uuid'; @Injectable() -export class UsersService extends BaseService { - constructor( - protected prisma: PrismaService, - protected temporaryDatabaseService: TemporaryDatabaseService, - ) { - super({ - prisma, - temporaryDatabaseService, - cacheSize: USER_CACHE_SIZE, - className: 'USER_TB', - field: 'email_provider', +export class UsersService { + constructor(protected prisma: PrismaService) {} + + async findUserByEmailAndProvider( + email: string, + provider: string, + ): Promise { + return this.prisma.user.findUnique({ + where: { email_provider: { email, provider } }, }); } - generateKey(data: UpdateUserDto) { - return `email:${data.email}+provider:${data.provider}`; + async createUser(data: CreateUserDto): Promise { + return this.prisma.user.create({ + data: { + uuid: generateUuid(), + email: data.email, + provider: data.provider, + }, + }); } } From c5e04fa3e2f1ea92db3eb751fd0917ac9fb59ad9 Mon Sep 17 00:00:00 2001 From: Conut-1 <1mim1@naver.com> Date: Tue, 30 Apr 2024 17:18:06 +0900 Subject: [PATCH 06/17] =?UTF-8?q?refactor:=20User=20=EB=AA=A8=EB=8D=B8=20?= =?UTF-8?q?=EA=B4=80=EB=A0=A8=20=EB=8F=99=EC=9E=91=20controller=EB=A1=9C?= =?UTF-8?q?=20=EC=9D=B4=EB=8F=99?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- nestjs-BE/server/src/auth/auth.controller.ts | 18 +++++++----- nestjs-BE/server/src/auth/auth.service.ts | 29 -------------------- 2 files changed, 11 insertions(+), 36 deletions(-) diff --git a/nestjs-BE/server/src/auth/auth.controller.ts b/nestjs-BE/server/src/auth/auth.controller.ts index aad67c36..27cde51b 100644 --- a/nestjs-BE/server/src/auth/auth.controller.ts +++ b/nestjs-BE/server/src/auth/auth.controller.ts @@ -6,6 +6,7 @@ import { UsersService } from 'src/users/users.service'; import { RefreshTokenDto } from './dto/refresh-token.dto'; import { ProfilesService } from 'src/profiles/profiles.service'; import { ApiTags, ApiResponse, ApiOperation } from '@nestjs/swagger'; +import customEnv from 'src/config/env'; @Controller('auth') @ApiTags('auth') @@ -33,18 +34,21 @@ export class AuthController { ); if (!kakaoUserAccount) throw new NotFoundException(); const email = kakaoUserAccount.email; - let userUuid = await this.authService.findUser( - this.usersService, + const user = await this.usersService.findUserByEmailAndProvider( email, 'kakao', ); + let userUuid = user?.uuid; if (!userUuid) { const data = { email, provider: 'kakao' }; - userUuid = await this.authService.createUser( - data, - this.usersService, - this.profilesService, - ); + const createdUser = await this.usersService.createUser(data); + userUuid = createdUser.uuid; + const profileData = { + user_id: createdUser.uuid, + image: customEnv.BASE_IMAGE_URL, + nickname: '익명의 사용자', + }; + await this.profilesService.create(profileData); } return this.authService.login(userUuid); } diff --git a/nestjs-BE/server/src/auth/auth.service.ts b/nestjs-BE/server/src/auth/auth.service.ts index b9daa5dd..f1306fe3 100644 --- a/nestjs-BE/server/src/auth/auth.service.ts +++ b/nestjs-BE/server/src/auth/auth.service.ts @@ -10,12 +10,7 @@ import { REFRESH_TOKEN_EXPIRY_DAYS, } from 'src/config/magic-number'; import generateUuid from 'src/utils/uuid'; -import { UsersService } from 'src/users/users.service'; -import { ProfilesService } from 'src/profiles/profiles.service'; -import { CreateUserDto } from 'src/users/dto/create-user.dto'; -import customEnv from 'src/config/env'; import { ResponseUtils } from 'src/utils/response'; -const { BASE_IMAGE_URL } = customEnv; export interface TokenData { uuid: string; @@ -124,30 +119,6 @@ export class AuthService extends BaseService { } } - async findUser(usersService: UsersService, email: string, provider: string) { - const userData = await usersService.findUserByEmailAndProvider( - email, - provider, - ); - return userData?.uuid; - } - - async createUser( - data: CreateUserDto, - usersService: UsersService, - profilesService: ProfilesService, - ) { - const createdUser = await usersService.createUser(data); - const userUuid = createdUser.uuid; - const profileData = { - user_id: userUuid, - image: BASE_IMAGE_URL, - nickname: '익명의 사용자', - }; - profilesService.create(profileData); - return userUuid; - } - remove(refreshToken: string) { const decodedToken = this.jwtService.decode(refreshToken); const uuid = decodedToken?.uuid; From 91157a74b96adb4ea8869330335ed695be9313b3 Mon Sep 17 00:00:00 2001 From: Conut-1 <1mim1@naver.com> Date: Sat, 4 May 2024 17:52:55 +0900 Subject: [PATCH 07/17] =?UTF-8?q?fix:=20Profile=20=EB=AA=A8=EB=8D=B8=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 --- .../20240430082846_rename_profile_model/migration.sql | 11 +++++++++++ nestjs-BE/server/prisma/schema.prisma | 6 +++--- 2 files changed, 14 insertions(+), 3 deletions(-) create mode 100644 nestjs-BE/server/prisma/migrations/20240430082846_rename_profile_model/migration.sql diff --git a/nestjs-BE/server/prisma/migrations/20240430082846_rename_profile_model/migration.sql b/nestjs-BE/server/prisma/migrations/20240430082846_rename_profile_model/migration.sql new file mode 100644 index 00000000..67df03ad --- /dev/null +++ b/nestjs-BE/server/prisma/migrations/20240430082846_rename_profile_model/migration.sql @@ -0,0 +1,11 @@ +-- DropForeignKey +ALTER TABLE `PROFILE_TB` DROP FOREIGN KEY `PROFILE_TB_user_id_fkey`; + +-- RenameIndex +ALTER TABLE `PROFILE_TB` RENAME INDEX `PROFILE_TB_user_id_key` TO `Profile_user_id_key`; + +-- RenameTable +ALTER TABLE `PROFILE_TB` RENAME `Profile`; + +-- AddForeignKey +ALTER TABLE `Profile` ADD CONSTRAINT `Profile_user_id_fkey` FOREIGN KEY (`user_id`) REFERENCES `User`(`uuid`) ON DELETE CASCADE ON UPDATE CASCADE; diff --git a/nestjs-BE/server/prisma/schema.prisma b/nestjs-BE/server/prisma/schema.prisma index 01baa5ab..e5819c9a 100644 --- a/nestjs-BE/server/prisma/schema.prisma +++ b/nestjs-BE/server/prisma/schema.prisma @@ -11,7 +11,7 @@ model User { uuid String @id @db.VarChar(32) email String provider String - profiles PROFILE_TB[] + profiles Profile[] refresh_tokens REFRESH_TOKEN_TB[] @@unique([email, provider]) } @@ -23,7 +23,7 @@ model REFRESH_TOKEN_TB { user User @relation(fields: [user_id], references: [uuid], onDelete: Cascade) } -model PROFILE_TB { +model Profile { uuid String @id @db.VarChar(32) user_id String @unique @db.VarChar(32) image String @@ -44,7 +44,7 @@ model PROFILE_SPACE_TB { space_uuid String @db.VarChar(32) profile_uuid String @db.VarChar(32) space SPACE_TB @relation(fields: [space_uuid], references: [uuid], onDelete: Cascade) - profile PROFILE_TB @relation(fields: [profile_uuid], references: [uuid], onDelete: Cascade) + profile Profile @relation(fields: [profile_uuid], references: [uuid], onDelete: Cascade) @@unique([space_uuid, profile_uuid]) } From d49f9e9b4f8741b929fd3963b8d68eaca70135c6 Mon Sep 17 00:00:00 2001 From: Conut-1 <1mim1@naver.com> Date: Thu, 9 May 2024 18:15:51 +0900 Subject: [PATCH 08/17] =?UTF-8?q?fix:=20Profile=5Fspace,=20Space=20?= =?UTF-8?q?=EB=AA=A8=EB=8D=B8=20=EC=88=98=EC=A0=95?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../migration.sql | 26 +++++++++++++++++++ nestjs-BE/server/prisma/schema.prisma | 12 ++++----- 2 files changed, 32 insertions(+), 6 deletions(-) create mode 100644 nestjs-BE/server/prisma/migrations/20240509025953_rename_models/migration.sql diff --git a/nestjs-BE/server/prisma/migrations/20240509025953_rename_models/migration.sql b/nestjs-BE/server/prisma/migrations/20240509025953_rename_models/migration.sql new file mode 100644 index 00000000..0c76981a --- /dev/null +++ b/nestjs-BE/server/prisma/migrations/20240509025953_rename_models/migration.sql @@ -0,0 +1,26 @@ +-- DropForeignKey +ALTER TABLE `INVITE_CODE_TB` DROP FOREIGN KEY `INVITE_CODE_TB_space_uuid_fkey`; + +-- DropForeignKey +ALTER TABLE `PROFILE_SPACE_TB` DROP FOREIGN KEY `PROFILE_SPACE_TB_profile_uuid_fkey`; + +-- DropForeignKey +ALTER TABLE `PROFILE_SPACE_TB` DROP FOREIGN KEY `PROFILE_SPACE_TB_space_uuid_fkey`; + +-- RenameIndex +ALTER TABLE `PROFILE_SPACE_TB` RENAME INDEX `PROFILE_SPACE_TB_space_uuid_profile_uuid_key` TO `Profile_space_space_uuid_profile_uuid_key`; + +-- RenameTable +ALTER TABLE `PROFILE_SPACE_TB` RENAME `Profile_space`; + +-- RenameTable +ALTER TABLE `SPACE_TB` RENAME `Space`; + +-- AddForeignKey +ALTER TABLE `Profile_space` ADD CONSTRAINT `Profile_space_space_uuid_fkey` FOREIGN KEY (`space_uuid`) REFERENCES `Space`(`uuid`) ON DELETE CASCADE ON UPDATE CASCADE; + +-- AddForeignKey +ALTER TABLE `Profile_space` ADD CONSTRAINT `Profile_space_profile_uuid_fkey` FOREIGN KEY (`profile_uuid`) REFERENCES `Profile`(`uuid`) ON DELETE CASCADE ON UPDATE CASCADE; + +-- AddForeignKey +ALTER TABLE `INVITE_CODE_TB` ADD CONSTRAINT `INVITE_CODE_TB_space_uuid_fkey` FOREIGN KEY (`space_uuid`) REFERENCES `Space`(`uuid`) ON DELETE CASCADE ON UPDATE CASCADE; diff --git a/nestjs-BE/server/prisma/schema.prisma b/nestjs-BE/server/prisma/schema.prisma index e5819c9a..06bbe9dd 100644 --- a/nestjs-BE/server/prisma/schema.prisma +++ b/nestjs-BE/server/prisma/schema.prisma @@ -29,21 +29,21 @@ model Profile { image String nickname String @db.VarChar(20) user User @relation(fields: [user_id], references: [uuid], onDelete: Cascade) - spaces PROFILE_SPACE_TB[] + spaces Profile_space[] } -model SPACE_TB { +model Space { uuid String @id @db.VarChar(32) name String @db.VarChar(20) icon String - profiles PROFILE_SPACE_TB[] + profiles Profile_space[] invite_codes INVITE_CODE_TB[] } -model PROFILE_SPACE_TB { +model Profile_space { space_uuid String @db.VarChar(32) profile_uuid String @db.VarChar(32) - space SPACE_TB @relation(fields: [space_uuid], references: [uuid], onDelete: Cascade) + space Space @relation(fields: [space_uuid], references: [uuid], onDelete: Cascade) profile Profile @relation(fields: [profile_uuid], references: [uuid], onDelete: Cascade) @@unique([space_uuid, profile_uuid]) } @@ -53,5 +53,5 @@ model INVITE_CODE_TB { invite_code String @unique @db.VarChar(10) space_uuid String @db.VarChar(32) expiry_date DateTime - space SPACE_TB @relation(fields: [space_uuid], references: [uuid], onDelete: Cascade) + space Space @relation(fields: [space_uuid], references: [uuid], onDelete: Cascade) } From 1d9f8e58382ab9670b99d1f0053c92ce324d51fc Mon Sep 17 00:00:00 2001 From: Conut-1 <1mim1@naver.com> Date: Fri, 10 May 2024 15:31:41 +0900 Subject: [PATCH 09/17] =?UTF-8?q?fix:=20Profile=20controller,=20service=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 --- nestjs-BE/server/src/auth/auth.controller.ts | 2 +- .../src/profiles/dto/update-profile.dto.ts | 3 ++ .../src/profiles/profiles.controller.ts | 18 +++++-- .../server/src/profiles/profiles.service.ts | 50 +++++++++++++------ 4 files changed, 52 insertions(+), 21 deletions(-) diff --git a/nestjs-BE/server/src/auth/auth.controller.ts b/nestjs-BE/server/src/auth/auth.controller.ts index 27cde51b..125bfc0b 100644 --- a/nestjs-BE/server/src/auth/auth.controller.ts +++ b/nestjs-BE/server/src/auth/auth.controller.ts @@ -48,7 +48,7 @@ export class AuthController { image: customEnv.BASE_IMAGE_URL, nickname: '익명의 사용자', }; - await this.profilesService.create(profileData); + await this.profilesService.createProfile(profileData); } return this.authService.login(userUuid); } diff --git a/nestjs-BE/server/src/profiles/dto/update-profile.dto.ts b/nestjs-BE/server/src/profiles/dto/update-profile.dto.ts index c6e4c50e..5d9d39fd 100644 --- a/nestjs-BE/server/src/profiles/dto/update-profile.dto.ts +++ b/nestjs-BE/server/src/profiles/dto/update-profile.dto.ts @@ -1,8 +1,11 @@ import { PartialType } from '@nestjs/mapped-types'; import { CreateProfileDto } from './create-profile.dto'; import { ApiProperty } from '@nestjs/swagger'; +import { MaxLength } from 'class-validator'; +import { MAX_NAME_LENGTH } from 'src/config/magic-number'; export class UpdateProfileDto extends PartialType(CreateProfileDto) { + @MaxLength(MAX_NAME_LENGTH) @ApiProperty({ example: 'new nickname', description: 'Updated nickname of the profile', diff --git a/nestjs-BE/server/src/profiles/profiles.controller.ts b/nestjs-BE/server/src/profiles/profiles.controller.ts index c59bff3b..0b391493 100644 --- a/nestjs-BE/server/src/profiles/profiles.controller.ts +++ b/nestjs-BE/server/src/profiles/profiles.controller.ts @@ -6,6 +6,8 @@ import { UseInterceptors, UploadedFile, Request as Req, + ValidationPipe, + NotFoundException, } from '@nestjs/common'; import { FileInterceptor } from '@nestjs/platform-express'; import { ProfilesService } from './profiles.service'; @@ -32,8 +34,10 @@ export class ProfilesController { status: 401, description: 'Unauthorized.', }) - findOne(@Req() req: RequestWithUser) { - return this.profilesService.findOne(req.user.uuid); + async findProfile(@Req() req: RequestWithUser) { + const profile = await this.profilesService.findProfile(req.user.uuid); + if (!profile) throw new NotFoundException(); + return { statusCode: 200, message: 'Success', data: profile }; } @Patch() @@ -50,11 +54,17 @@ export class ProfilesController { async update( @UploadedFile() image: Express.Multer.File, @Req() req: RequestWithUser, - @Body() updateProfileDto: UpdateProfileDto, + @Body(new ValidationPipe({ whitelist: true })) + updateProfileDto: UpdateProfileDto, ) { if (image) { updateProfileDto.image = await this.uploadService.uploadFile(image); } - return this.profilesService.update(req.user.uuid, updateProfileDto); + const profile = await this.profilesService.updateProfile( + req.user.uuid, + updateProfileDto, + ); + if (!profile) throw new NotFoundException(); + return { statusCode: 200, message: 'Success', data: profile }; } } diff --git a/nestjs-BE/server/src/profiles/profiles.service.ts b/nestjs-BE/server/src/profiles/profiles.service.ts index 26849c8a..446ae121 100644 --- a/nestjs-BE/server/src/profiles/profiles.service.ts +++ b/nestjs-BE/server/src/profiles/profiles.service.ts @@ -1,26 +1,44 @@ import { Injectable } from '@nestjs/common'; import { PrismaService } from '../prisma/prisma.service'; -import { TemporaryDatabaseService } from '../temporary-database/temporary-database.service'; -import { BaseService } from '../base/base.service'; -import { PROFILE_CACHE_SIZE } from 'src/config/magic-number'; import { UpdateProfileDto } from './dto/update-profile.dto'; +import { CreateProfileDto } from './dto/create-profile.dto'; +import { Profile, Prisma } from '@prisma/client'; +import generateUuid from 'src/utils/uuid'; @Injectable() -export class ProfilesService extends BaseService { - constructor( - protected prisma: PrismaService, - protected temporaryDatabaseService: TemporaryDatabaseService, - ) { - super({ - prisma, - temporaryDatabaseService, - cacheSize: PROFILE_CACHE_SIZE, - className: 'PROFILE_TB', - field: 'user_id', +export class ProfilesService { + constructor(protected prisma: PrismaService) {} + + async findProfile(userUuid: string): Promise { + return this.prisma.profile.findUnique({ where: { user_id: userUuid } }); + } + + async createProfile(data: CreateProfileDto): Promise { + return this.prisma.profile.create({ + data: { + uuid: generateUuid(), + user_id: data.user_id, + image: data.image, + nickname: data.nickname, + }, }); } - generateKey(data: UpdateProfileDto): string { - return data.user_id; + async updateProfile( + userUuid: string, + updateProfileDto: UpdateProfileDto, + ): Promise { + try { + return await this.prisma.profile.update({ + where: { user_id: userUuid }, + data: { ...updateProfileDto }, + }); + } catch (err) { + if (err instanceof Prisma.PrismaClientKnownRequestError) { + return null; + } else { + throw err; + } + } } } From b8c91e8f542c676d4af541281dbdc4204368dc57 Mon Sep 17 00:00:00 2001 From: Conut-1 <1mim1@naver.com> Date: Sat, 11 May 2024 15:06:00 +0900 Subject: [PATCH 10/17] =?UTF-8?q?fix:=20Space,=20Profile=5Fspace=20?= =?UTF-8?q?=EB=AA=A8=EB=8D=B8=20controller,=20service=20=EC=88=98=EC=A0=95?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- nestjs-BE/server/.eslintrc.js | 3 +- .../invite-codes/invite-codes.controller.ts | 5 +- .../src/invite-codes/invite-codes.service.ts | 7 +- .../profile-space/profile-space.controller.ts | 75 ++++--- .../profile-space/profile-space.service.ts | 188 +++++------------- .../server/src/profiles/profiles.service.ts | 6 + .../server/src/spaces/dto/update-space.dto.ts | 15 +- .../server/src/spaces/spaces.controller.ts | 35 ++-- nestjs-BE/server/src/spaces/spaces.module.ts | 3 +- nestjs-BE/server/src/spaces/spaces.service.ts | 61 ++++-- .../temporary-database.service.ts | 12 +- 11 files changed, 186 insertions(+), 224 deletions(-) diff --git a/nestjs-BE/server/.eslintrc.js b/nestjs-BE/server/.eslintrc.js index 5d07f54b..3b453c1b 100644 --- a/nestjs-BE/server/.eslintrc.js +++ b/nestjs-BE/server/.eslintrc.js @@ -23,7 +23,6 @@ module.exports = { '@typescript-eslint/no-explicit-any': 'off', 'max-depth': ['error', 3], 'no-magic-numbers': ['error', { ignore: [-1, 0, 1] }], - 'curly': ['error', 'multi-line', 'consistent'], - 'max-params': ['error', 3], + curly: ['error', 'multi-line', 'consistent'], }, }; diff --git a/nestjs-BE/server/src/invite-codes/invite-codes.controller.ts b/nestjs-BE/server/src/invite-codes/invite-codes.controller.ts index f6e10e3d..b0bc2fd4 100644 --- a/nestjs-BE/server/src/invite-codes/invite-codes.controller.ts +++ b/nestjs-BE/server/src/invite-codes/invite-codes.controller.ts @@ -40,7 +40,8 @@ export class InviteCodesController { status: 410, description: 'Invite code has expired', }) - findSpace(@Param('inviteCode') inviteCode: string) { - return this.inviteCodesService.findSpace(inviteCode); + async findSpace(@Param('inviteCode') inviteCode: string) { + const space = await this.inviteCodesService.findSpace(inviteCode); + return { statusCode: 200, message: 'Success', data: space }; } } diff --git a/nestjs-BE/server/src/invite-codes/invite-codes.service.ts b/nestjs-BE/server/src/invite-codes/invite-codes.service.ts index 5a690b15..8fdaa94d 100644 --- a/nestjs-BE/server/src/invite-codes/invite-codes.service.ts +++ b/nestjs-BE/server/src/invite-codes/invite-codes.service.ts @@ -39,7 +39,7 @@ export class InviteCodesService extends BaseService { async createCode(createInviteCodeDto: CreateInviteCodeDto) { const { space_uuid: spaceUuid } = createInviteCodeDto; - await this.spacesService.findOne(spaceUuid); + await this.spacesService.findSpace(spaceUuid); const inviteCodeData = await this.generateInviteCode(createInviteCodeDto); super.create(inviteCodeData); const { invite_code } = inviteCodeData; @@ -49,10 +49,7 @@ export class InviteCodesService extends BaseService { async findSpace(inviteCode: string) { const inviteCodeData = await this.getInviteCodeData(inviteCode); this.checkExpiry(inviteCode, inviteCodeData.expiry_date); - const spaceResponse = await this.spacesService.findOne( - inviteCodeData.space_uuid, - ); - return spaceResponse; + return this.spacesService.findSpace(inviteCodeData.space_uuid); } private async generateInviteCode(createInviteCodeDto: CreateInviteCodeDto) { diff --git a/nestjs-BE/server/src/profile-space/profile-space.controller.ts b/nestjs-BE/server/src/profile-space/profile-space.controller.ts index 06a83064..f4df1f0b 100644 --- a/nestjs-BE/server/src/profile-space/profile-space.controller.ts +++ b/nestjs-BE/server/src/profile-space/profile-space.controller.ts @@ -6,12 +6,17 @@ import { Delete, Param, Request as Req, + NotFoundException, + HttpException, + HttpStatus, + ConflictException, } from '@nestjs/common'; import { ProfileSpaceService } from './profile-space.service'; import { CreateProfileSpaceDto } from './dto/create-profile-space.dto'; import { RequestWithUser } from 'src/utils/interface'; import { SpacesService } from 'src/spaces/spaces.service'; import { ApiTags, ApiResponse, ApiOperation } from '@nestjs/swagger'; +import { ProfilesService } from 'src/profiles/profiles.service'; @Controller('profileSpace') @ApiTags('profileSpace') @@ -19,6 +24,7 @@ export class ProfileSpaceController { constructor( private readonly profileSpaceService: ProfileSpaceService, private readonly spacesService: SpacesService, + private readonly profilesService: ProfilesService, ) {} @Post('join') @@ -35,14 +41,16 @@ export class ProfileSpaceController { @Body() createProfileSpaceDto: CreateProfileSpaceDto, @Req() req: RequestWithUser, ) { - const userUuid = req.user.uuid; - const { space_uuid } = createProfileSpaceDto; - const { joinData, profileData } = - await this.profileSpaceService.processData(userUuid, space_uuid); - const responseData = await this.profileSpaceService.create(joinData, false); - const data = await this.spacesService.processData(space_uuid, profileData); - await this.profileSpaceService.put(userUuid, space_uuid, data); - return responseData; + const profile = await this.profilesService.findProfile(req.user.uuid); + if (!profile) throw new NotFoundException(); + const profileSpace = await this.profileSpaceService.joinSpace( + profile.uuid, + createProfileSpaceDto.space_uuid, + ); + if (!profileSpace) { + throw new HttpException('Data already exists.', HttpStatus.CONFLICT); + } + return { statusCode: 201, message: 'Created', data: profileSpace }; } @Delete('leave/:space_uuid') @@ -58,29 +66,40 @@ export class ProfileSpaceController { @Param('space_uuid') spaceUuid: string, @Req() req: RequestWithUser, ) { - const userUuid = req.user.uuid; - const { joinData, profileData } = - await this.profileSpaceService.processData(userUuid, spaceUuid); - await this.spacesService.processData(spaceUuid, profileData); - const isSpaceEmpty = await this.profileSpaceService.delete( - userUuid, + const profile = await this.profilesService.findProfile(req.user.uuid); + if (!profile) throw new NotFoundException(); + const space = await this.spacesService.findSpace(spaceUuid); + if (!space) throw new NotFoundException(); + const profileSpace = await this.profileSpaceService.leaveSpace( + profile.uuid, spaceUuid, - profileData, ); - if (isSpaceEmpty) return this.spacesService.remove(spaceUuid); - const key = this.profileSpaceService.generateKey(joinData); - return this.profileSpaceService.remove(key); + if (!profileSpace) throw new ConflictException(); + const isSpaceEmpty = await this.profileSpaceService.isSpaceEmpty(spaceUuid); + if (isSpaceEmpty) { + await this.spacesService.deleteSpace(spaceUuid); + } + return { statusCode: 204, message: 'No Content' }; } @Get('spaces') - @ApiOperation({ summary: 'Get user’s spaces' }) + @ApiOperation({ summary: "Get user's spaces" }) @ApiResponse({ status: 200, description: 'Returns a list of spaces.', }) - getSpaces(@Req() req: RequestWithUser) { - const userUuid = req.user.uuid; - return this.profileSpaceService.retrieveUserSpaces(userUuid); + async getSpaces(@Req() req: RequestWithUser) { + const profile = await this.profilesService.findProfile(req.user.uuid); + if (!profile) throw new NotFoundException(); + const profileSpaces = + await this.profileSpaceService.findProfileSpacesByProfileUuid( + profile.uuid, + ); + const spaceUuids = profileSpaces.map( + (profileSpace) => profileSpace.space_uuid, + ); + const spaces = await this.spacesService.findSpaces(spaceUuids); + return { statusCode: 200, message: 'Success', data: spaces }; } @Get('users/:space_uuid') @@ -93,7 +112,15 @@ export class ProfileSpaceController { status: 404, description: 'Space not found.', }) - getUsers(@Param('space_uuid') spaceUuid: string) { - return this.profileSpaceService.retrieveSpaceUsers(spaceUuid); + async getProfiles(@Param('space_uuid') spaceUuid: string) { + const space = await this.spacesService.findSpace(spaceUuid); + if (!space) throw new NotFoundException(); + const profileSpaces = + await this.profileSpaceService.findProfileSpacesBySpaceUuid(space.uuid); + const profileUuids = profileSpaces.map( + (profileSpace) => profileSpace.profile_uuid, + ); + const profiles = await this.profilesService.findProfiles(profileUuids); + return { statusCode: 200, message: 'Success', data: profiles }; } } diff --git a/nestjs-BE/server/src/profile-space/profile-space.service.ts b/nestjs-BE/server/src/profile-space/profile-space.service.ts index 305f8d32..844cc542 100644 --- a/nestjs-BE/server/src/profile-space/profile-space.service.ts +++ b/nestjs-BE/server/src/profile-space/profile-space.service.ts @@ -1,160 +1,76 @@ -import { Injectable, HttpStatus } from '@nestjs/common'; -import { UpdateProfileSpaceDto } from './dto/update-profile-space.dto'; -import { BaseService } from 'src/base/base.service'; +import { Injectable } from '@nestjs/common'; import { PrismaService } from 'src/prisma/prisma.service'; -import { TemporaryDatabaseService } from 'src/temporary-database/temporary-database.service'; -import { - PROFILE_SPACE_CACHE_SIZE, - SPACE_USER_CACHE_SIZE, - USER_SPACE_CACHE_SIZE, -} from 'src/config/magic-number'; -import { CreateProfileSpaceDto } from './dto/create-profile-space.dto'; import { ProfilesService } from 'src/profiles/profiles.service'; -import { UpdateProfileDto } from 'src/profiles/dto/update-profile.dto'; -import { UpdateSpaceDto } from 'src/spaces/dto/update-space.dto'; -import LRUCache from 'src/utils/lru-cache'; -import { ResponseUtils } from 'src/utils/response'; - -interface UpdateProfileAndSpaceDto { - profileData: UpdateProfileDto; - spaceData: UpdateSpaceDto; -} +import { Prisma, Profile_space } from '@prisma/client'; @Injectable() -export class ProfileSpaceService extends BaseService { - private readonly userCache: LRUCache; - private readonly spaceCache: LRUCache; +export class ProfileSpaceService { constructor( - protected prisma: PrismaService, - protected temporaryDatabaseService: TemporaryDatabaseService, + private readonly prisma: PrismaService, private readonly profilesService: ProfilesService, - ) { - super({ - prisma, - temporaryDatabaseService, - cacheSize: PROFILE_SPACE_CACHE_SIZE, - className: 'PROFILE_SPACE_TB', - field: 'space_uuid_profile_uuid', - }); - this.userCache = new LRUCache(USER_SPACE_CACHE_SIZE); - this.spaceCache = new LRUCache(SPACE_USER_CACHE_SIZE); - } - - generateKey(data: CreateProfileSpaceDto) { - return `space_uuid:${data.space_uuid}+profile_uuid:${data.profile_uuid}`; - } + ) {} - async processData(userUuid: string, spaceUuid: string) { - const profileResponse = await this.profilesService.findOne(userUuid); - const profileUuid = profileResponse.data?.uuid; - const joinData = { - profile_uuid: profileUuid, - space_uuid: spaceUuid, - }; - return { joinData, profileData: profileResponse.data }; + async findProfileSpacesByProfileUuid( + profileUuid: string, + ): Promise { + return this.prisma.profile_space.findMany({ + where: { profile_uuid: profileUuid }, + }); } - async put( - userUuid: string, + async findProfileSpacesBySpaceUuid( spaceUuid: string, - data: UpdateProfileAndSpaceDto, - ) { - const { spaceData, profileData } = data; - const userSpaces = await this.fetchUserSpacesFromCacheOrDB( - userUuid, - profileData.uuid, - ); - userSpaces.push(spaceData); - this.userCache.put(userUuid, userSpaces); - const spaceProfiles = await this.fetchSpaceUsersFromCacheOrDB(spaceUuid); - spaceProfiles.push(profileData); - this.spaceCache.put(spaceUuid, spaceProfiles); + ): Promise { + return this.prisma.profile_space.findMany({ + where: { space_uuid: spaceUuid }, + }); } - async delete( - userUuid: string, + async joinSpace( + profileUuid: string, spaceUuid: string, - profileData: UpdateProfileDto, - ) { - const userSpaces = await this.fetchUserSpacesFromCacheOrDB( - userUuid, - profileData.uuid, - ); - const filterUserSpaces = userSpaces.filter( - (space) => space.uuid !== spaceUuid, - ); - this.userCache.put(userUuid, filterUserSpaces); - const spaceUsers = await this.fetchSpaceUsersFromCacheOrDB(spaceUuid); - const filterSpaceUsers = spaceUsers.filter( - (profile) => profile.uuid !== profileData.uuid, - ); - this.spaceCache.put(spaceUuid, filterSpaceUsers); - return filterSpaceUsers.length === 0; + ): Promise { + try { + return await this.prisma.profile_space.create({ + data: { space_uuid: spaceUuid, profile_uuid: profileUuid }, + }); + } catch (err) { + if (err instanceof Prisma.PrismaClientKnownRequestError) { + return null; + } else { + throw err; + } + } } - private async fetchUserSpacesFromCacheOrDB( - userUuid: string, + async leaveSpace( profileUuid: string, - ): Promise { - const cacheUserSpaces = this.userCache.get(userUuid); - if (cacheUserSpaces) return cacheUserSpaces; - const profileResponse = await this.prisma['PROFILE_TB'].findUnique({ - where: { uuid: profileUuid }, - include: { - spaces: { - include: { - space: true, + spaceUuid: string, + ): Promise { + try { + return await this.prisma.profile_space.delete({ + where: { + space_uuid_profile_uuid: { + space_uuid: spaceUuid, + profile_uuid: profileUuid, }, }, - }, - }); - const storeUserSpaces = - profileResponse?.spaces.map((profileSpace) => profileSpace.space) || []; - return storeUserSpaces; + }); + } catch (err) { + if (err instanceof Prisma.PrismaClientKnownRequestError) { + return null; + } else { + throw err; + } + } } - private async fetchSpaceUsersFromCacheOrDB( - spaceUuid: string, - ): Promise { - const cacheSpaceProfiles = this.spaceCache.get(spaceUuid); - if (cacheSpaceProfiles) return cacheSpaceProfiles; - - const spaceResponse = await this.prisma['SPACE_TB'].findUnique({ - where: { uuid: spaceUuid }, - include: { - profiles: { - include: { - profile: true, - }, - }, + async isSpaceEmpty(spaceUuid: string) { + const first = await this.prisma.profile_space.findFirst({ + where: { + space_uuid: spaceUuid, }, }); - - const storeSpaceProfiles = - spaceResponse?.profiles.map((profileSpace) => profileSpace.profile) || []; - return storeSpaceProfiles; - } - - async retrieveUserSpaces(userUuid: string) { - const profileResponse = await this.profilesService.findOne(userUuid); - const profileUuid = profileResponse.data?.uuid; - const spaces = await this.fetchUserSpacesFromCacheOrDB( - userUuid, - profileUuid, - ); - this.userCache.put(userUuid, spaces); - return ResponseUtils.createResponse(HttpStatus.OK, spaces); - } - - async retrieveSpaceUsers(spaceUuid: string) { - const users = await this.fetchSpaceUsersFromCacheOrDB(spaceUuid); - const usersData = await Promise.all( - users.map(async (user) => { - const profile = await this.profilesService.findOne(user.user_id); - return profile.data; - }), - ); - this.spaceCache.put(spaceUuid, usersData); - return ResponseUtils.createResponse(HttpStatus.OK, usersData); + return first ? false : true; } } diff --git a/nestjs-BE/server/src/profiles/profiles.service.ts b/nestjs-BE/server/src/profiles/profiles.service.ts index 446ae121..da930eec 100644 --- a/nestjs-BE/server/src/profiles/profiles.service.ts +++ b/nestjs-BE/server/src/profiles/profiles.service.ts @@ -13,6 +13,12 @@ export class ProfilesService { return this.prisma.profile.findUnique({ where: { user_id: userUuid } }); } + async findProfiles(profileUuids: string[]): Promise { + return this.prisma.profile.findMany({ + where: { uuid: { in: profileUuids } }, + }); + } + async createProfile(data: CreateProfileDto): Promise { return this.prisma.profile.create({ data: { diff --git a/nestjs-BE/server/src/spaces/dto/update-space.dto.ts b/nestjs-BE/server/src/spaces/dto/update-space.dto.ts index 017ec463..4f6b7efd 100644 --- a/nestjs-BE/server/src/spaces/dto/update-space.dto.ts +++ b/nestjs-BE/server/src/spaces/dto/update-space.dto.ts @@ -1,21 +1,22 @@ -import { PartialType } from '@nestjs/mapped-types'; -import { CreateSpaceDto } from './create-space.dto'; import { ApiProperty } from '@nestjs/swagger'; +import { IsNotEmpty, IsString, MaxLength } from 'class-validator'; +import { MAX_NAME_LENGTH } from 'src/config/magic-number'; -export class UpdateSpaceDto extends PartialType(CreateSpaceDto) { +export class UpdateSpaceDto { + @IsString() + @IsNotEmpty() + @MaxLength(MAX_NAME_LENGTH) @ApiProperty({ example: 'new space', description: 'Updated space name', required: false, }) - name?: string; + name: string; @ApiProperty({ example: 'new image', description: 'Updated space icon', required: false, }) - icon?: string; - - uuid?: string; + icon: string; } diff --git a/nestjs-BE/server/src/spaces/spaces.controller.ts b/nestjs-BE/server/src/spaces/spaces.controller.ts index a6f83380..cf706e9b 100644 --- a/nestjs-BE/server/src/spaces/spaces.controller.ts +++ b/nestjs-BE/server/src/spaces/spaces.controller.ts @@ -8,6 +8,8 @@ import { UseInterceptors, UploadedFile, Request as Req, + NotFoundException, + ValidationPipe, } from '@nestjs/common'; import { FileInterceptor } from '@nestjs/platform-express'; import { SpacesService } from './spaces.service'; @@ -18,6 +20,7 @@ import { UploadService } from 'src/upload/upload.service'; import { ProfileSpaceService } from 'src/profile-space/profile-space.service'; import { RequestWithUser } from 'src/utils/interface'; import customEnv from 'src/config/env'; +import { ProfilesService } from 'src/profiles/profiles.service'; const { APP_ICON_URL } = customEnv; @Controller('spaces') @@ -27,6 +30,7 @@ export class SpacesController { private readonly spacesService: SpacesService, private readonly uploadService: UploadService, private readonly profileSpaceService: ProfileSpaceService, + private readonly profilesService: ProfilesService, ) {} @Post() @@ -41,20 +45,15 @@ export class SpacesController { @Body() createSpaceDto: CreateSpaceDto, @Req() req: RequestWithUser, ) { + const profile = await this.profilesService.findProfile(req.user.uuid); + if (!profile) throw new NotFoundException(); const iconUrl = icon ? await this.uploadService.uploadFile(icon) : APP_ICON_URL; createSpaceDto.icon = iconUrl; - const response = await this.spacesService.create(createSpaceDto); - const { uuid: spaceUuid } = response.data; - const userUuid = req.user.uuid; - const { joinData, profileData } = - await this.profileSpaceService.processData(userUuid, spaceUuid); - this.profileSpaceService.create(joinData, false); - const spaceData = response.data; - const data = { profileData, spaceData }; - await this.profileSpaceService.put(userUuid, spaceUuid, data); - return response; + const space = await this.spacesService.createSpace(createSpaceDto); + await this.profileSpaceService.joinSpace(profile.uuid, space.uuid); + return { statusCode: 201, message: 'Created', data: space }; } @Get(':space_uuid') @@ -67,8 +66,10 @@ export class SpacesController { status: 404, description: 'Space not found.', }) - findOne(@Param('space_uuid') spaceUuid: string) { - return this.spacesService.findOne(spaceUuid); + async findOne(@Param('space_uuid') spaceUuid: string) { + const space = await this.spacesService.findSpace(spaceUuid); + if (!space) throw new NotFoundException(); + return { statusCode: 200, message: 'Success', data: space }; } @Patch(':space_uuid') @@ -89,11 +90,17 @@ export class SpacesController { async update( @UploadedFile() icon: Express.Multer.File, @Param('space_uuid') spaceUuid: string, - @Body() updateSpaceDto: UpdateSpaceDto, + @Body(new ValidationPipe({ whitelist: true })) + updateSpaceDto: UpdateSpaceDto, ) { if (icon) { updateSpaceDto.icon = await this.uploadService.uploadFile(icon); } - return this.spacesService.update(spaceUuid, updateSpaceDto); + const space = await this.spacesService.updateSpace( + spaceUuid, + updateSpaceDto, + ); + if (!space) throw new NotFoundException(); + return { statusCode: 200, message: 'Success', data: space }; } } diff --git a/nestjs-BE/server/src/spaces/spaces.module.ts b/nestjs-BE/server/src/spaces/spaces.module.ts index 2964682c..d208085c 100644 --- a/nestjs-BE/server/src/spaces/spaces.module.ts +++ b/nestjs-BE/server/src/spaces/spaces.module.ts @@ -3,9 +3,10 @@ import { SpacesService } from './spaces.service'; import { SpacesController } from './spaces.controller'; import { UploadService } from 'src/upload/upload.service'; import { ProfileSpaceModule } from 'src/profile-space/profile-space.module'; +import { ProfilesModule } from 'src/profiles/profiles.module'; @Module({ - imports: [forwardRef(() => ProfileSpaceModule)], + imports: [forwardRef(() => ProfileSpaceModule), ProfilesModule], controllers: [SpacesController], providers: [SpacesService, UploadService], exports: [SpacesService], diff --git a/nestjs-BE/server/src/spaces/spaces.service.ts b/nestjs-BE/server/src/spaces/spaces.service.ts index 7ddbc276..e6144d51 100644 --- a/nestjs-BE/server/src/spaces/spaces.service.ts +++ b/nestjs-BE/server/src/spaces/spaces.service.ts @@ -1,34 +1,51 @@ import { Injectable } from '@nestjs/common'; import { PrismaService } from '../prisma/prisma.service'; -import { TemporaryDatabaseService } from '../temporary-database/temporary-database.service'; -import { BaseService } from '../base/base.service'; -import { SPACE_CACHE_SIZE } from 'src/config/magic-number'; import { UpdateSpaceDto } from './dto/update-space.dto'; -import { UpdateProfileDto } from 'src/profiles/dto/update-profile.dto'; +import { Prisma, Space } from '@prisma/client'; +import { CreateSpaceDto } from './dto/create-space.dto'; +import generateUuid from 'src/utils/uuid'; @Injectable() -export class SpacesService extends BaseService { - constructor( - protected prisma: PrismaService, - protected temporaryDatabaseService: TemporaryDatabaseService, - ) { - super({ - prisma, - temporaryDatabaseService, - cacheSize: SPACE_CACHE_SIZE, - className: 'SPACE_TB', - field: 'uuid', +export class SpacesService { + constructor(protected prisma: PrismaService) {} + + async findSpace(spaceUuid: string): Promise { + return this.prisma.space.findUnique({ where: { uuid: spaceUuid } }); + } + + async findSpaces(spaceUuids: string[]): Promise { + return this.prisma.space.findMany({ where: { uuid: { in: spaceUuids } } }); + } + + async createSpace(createSpaceDto: CreateSpaceDto): Promise { + return this.prisma.space.create({ + data: { + uuid: generateUuid(), + name: createSpaceDto.name, + icon: createSpaceDto.icon, + }, }); } - generateKey(data: UpdateSpaceDto): string { - return data.uuid; + async updateSpace( + spaceUuid: string, + updateSpaceDto: UpdateSpaceDto, + ): Promise { + try { + return await this.prisma.space.update({ + where: { uuid: spaceUuid }, + data: { ...updateSpaceDto }, + }); + } catch (err) { + if (err instanceof Prisma.PrismaClientKnownRequestError) { + return null; + } else { + throw err; + } + } } - async processData(spaceUuid: string, profileData: UpdateProfileDto) { - const spaceResponseData = await super.findOne(spaceUuid); - const spaceData = spaceResponseData.data; - const data = { profileData, spaceData }; - return data; + async deleteSpace(spaceUuid: string): Promise { + return this.prisma.space.delete({ where: { uuid: spaceUuid } }); } } diff --git a/nestjs-BE/server/src/temporary-database/temporary-database.service.ts b/nestjs-BE/server/src/temporary-database/temporary-database.service.ts index e712272c..55815147 100644 --- a/nestjs-BE/server/src/temporary-database/temporary-database.service.ts +++ b/nestjs-BE/server/src/temporary-database/temporary-database.service.ts @@ -7,7 +7,6 @@ import { TokenData } from 'src/auth/auth.service'; import { InviteCodeData } from 'src/invite-codes/invite-codes.service'; import { CreateProfileSpaceDto } from 'src/profile-space/dto/create-profile-space.dto'; import { UpdateProfileDto } from 'src/profiles/dto/update-profile.dto'; -import { UpdateSpaceDto } from 'src/spaces/dto/update-space.dto'; import { UpdateUserDto } from 'src/users/dto/update-user.dto'; import costomEnv from 'src/config/env'; const { CSV_FOLDER } = costomEnv; @@ -22,7 +21,6 @@ export type InsertDataType = | InviteCodeData | CreateProfileSpaceDto | UpdateProfileDto - | UpdateSpaceDto | UpdateUserDto; type UpdateDataType = { @@ -54,15 +52,7 @@ export class TemporaryDatabaseService { } private initializeDatabase() { - const services = [ - 'USER_TB', - 'PROFILE_TB', - 'SPACE_TB', - 'BoardCollection', - 'PROFILE_SPACE_TB', - 'REFRESH_TOKEN_TB', - 'INVITE_CODE_TB', - ]; + const services = ['REFRESH_TOKEN_TB', 'INVITE_CODE_TB']; const operations = ['insert', 'update', 'delete']; services.forEach((service) => { From e94882c5e4e7984cc1fc4f876d7effc8bbb61317 Mon Sep 17 00:00:00 2001 From: Conut-1 <1mim1@naver.com> Date: Sat, 11 May 2024 15:23:18 +0900 Subject: [PATCH 11/17] =?UTF-8?q?fix:=20RefreshToken,=20InviteCode=20?= =?UTF-8?q?=EB=AA=A8=EB=8D=B8=20=EC=88=98=EC=A0=95?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../migration.sql | 20 +++++++++++++++++++ nestjs-BE/server/prisma/schema.prisma | 8 ++++---- 2 files changed, 24 insertions(+), 4 deletions(-) create mode 100644 nestjs-BE/server/prisma/migrations/20240511061416_rename_refresh_token_invite_code/migration.sql diff --git a/nestjs-BE/server/prisma/migrations/20240511061416_rename_refresh_token_invite_code/migration.sql b/nestjs-BE/server/prisma/migrations/20240511061416_rename_refresh_token_invite_code/migration.sql new file mode 100644 index 00000000..56982598 --- /dev/null +++ b/nestjs-BE/server/prisma/migrations/20240511061416_rename_refresh_token_invite_code/migration.sql @@ -0,0 +1,20 @@ +-- DropForeignKey +ALTER TABLE `INVITE_CODE_TB` DROP FOREIGN KEY `INVITE_CODE_TB_space_uuid_fkey`; + +-- DropForeignKey +ALTER TABLE `REFRESH_TOKEN_TB` DROP FOREIGN KEY `REFRESH_TOKEN_TB_user_id_fkey`; + +-- RenameIndex +ALTER TABLE `INVITE_CODE_TB` RENAME INDEX `INVITE_CODE_TB_invite_code_key` TO `InviteCode_invite_code_key`; + +-- RenameTable +ALTER TABLE `INVITE_CODE_TB` RENAME `InviteCode`; + +-- RenameTable +ALTER TABLE `REFRESH_TOKEN_TB` RENAME `RefreshToken`; + +-- AddForeignKey +ALTER TABLE `RefreshToken` ADD CONSTRAINT `RefreshToken_user_id_fkey` FOREIGN KEY (`user_id`) REFERENCES `User`(`uuid`) ON DELETE CASCADE ON UPDATE CASCADE; + +-- AddForeignKey +ALTER TABLE `InviteCode` ADD CONSTRAINT `InviteCode_space_uuid_fkey` FOREIGN KEY (`space_uuid`) REFERENCES `Space`(`uuid`) ON DELETE CASCADE ON UPDATE CASCADE; diff --git a/nestjs-BE/server/prisma/schema.prisma b/nestjs-BE/server/prisma/schema.prisma index 06bbe9dd..9aeab2e0 100644 --- a/nestjs-BE/server/prisma/schema.prisma +++ b/nestjs-BE/server/prisma/schema.prisma @@ -12,11 +12,11 @@ model User { email String provider String profiles Profile[] - refresh_tokens REFRESH_TOKEN_TB[] + refresh_tokens RefreshToken[] @@unique([email, provider]) } -model REFRESH_TOKEN_TB { +model RefreshToken { uuid String @id @db.VarChar(32) expiry_date DateTime user_id String @@ -37,7 +37,7 @@ model Space { name String @db.VarChar(20) icon String profiles Profile_space[] - invite_codes INVITE_CODE_TB[] + invite_codes InviteCode[] } model Profile_space { @@ -48,7 +48,7 @@ model Profile_space { @@unique([space_uuid, profile_uuid]) } -model INVITE_CODE_TB { +model InviteCode { uuid String @id @db.VarChar(32) invite_code String @unique @db.VarChar(10) space_uuid String @db.VarChar(32) From 0b32d3fb212a0c8f2f36a71ce52719870c14971f Mon Sep 17 00:00:00 2001 From: Conut-1 <1mim1@naver.com> Date: Mon, 13 May 2024 13:55:45 +0900 Subject: [PATCH 12/17] =?UTF-8?q?fix:=20InviteCode=20controller,=20service?= =?UTF-8?q?=20=EC=88=98=EC=A0=95?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../invite-codes/invite-codes.controller.ts | 39 ++++++- .../src/invite-codes/invite-codes.service.ts | 104 +++++++----------- .../temporary-database.service.ts | 4 +- 3 files changed, 72 insertions(+), 75 deletions(-) diff --git a/nestjs-BE/server/src/invite-codes/invite-codes.controller.ts b/nestjs-BE/server/src/invite-codes/invite-codes.controller.ts index b0bc2fd4..1a125b1c 100644 --- a/nestjs-BE/server/src/invite-codes/invite-codes.controller.ts +++ b/nestjs-BE/server/src/invite-codes/invite-codes.controller.ts @@ -1,12 +1,25 @@ -import { Controller, Get, Post, Body, Param } from '@nestjs/common'; +import { + Controller, + Get, + Post, + Body, + Param, + NotFoundException, + HttpException, + HttpStatus, +} from '@nestjs/common'; import { InviteCodesService } from './invite-codes.service'; import { CreateInviteCodeDto } from './dto/create-invite-code.dto'; import { ApiTags, ApiOperation, ApiResponse } from '@nestjs/swagger'; +import { SpacesService } from 'src/spaces/spaces.service'; @Controller('inviteCodes') @ApiTags('inviteCodes') export class InviteCodesController { - constructor(private readonly inviteCodesService: InviteCodesService) {} + constructor( + private readonly inviteCodesService: InviteCodesService, + private readonly spacesService: SpacesService, + ) {} @Post() @ApiOperation({ summary: 'Create invite code' }) @@ -22,8 +35,17 @@ export class InviteCodesController { status: 404, description: 'Space not found.', }) - create(@Body() createInviteCodeDto: CreateInviteCodeDto) { - return this.inviteCodesService.createCode(createInviteCodeDto); + async create(@Body() createInviteCodeDto: CreateInviteCodeDto) { + const spaceUuid = createInviteCodeDto.space_uuid; + const space = await this.spacesService.findSpace(spaceUuid); + if (!space) throw new NotFoundException(); + const inviteCode = + await this.inviteCodesService.createInviteCode(spaceUuid); + return { + statusCode: 201, + message: 'Created', + data: { invite_code: inviteCode.invite_code }, + }; } @Get(':inviteCode') @@ -41,7 +63,14 @@ export class InviteCodesController { description: 'Invite code has expired', }) async findSpace(@Param('inviteCode') inviteCode: string) { - const space = await this.inviteCodesService.findSpace(inviteCode); + const inviteCodeData = + await this.inviteCodesService.findInviteCode(inviteCode); + if (!inviteCodeData) throw new NotFoundException(); + if (this.inviteCodesService.checkExpiry(inviteCodeData.expiry_date)) { + this.inviteCodesService.deleteInviteCode(inviteCode); + throw new HttpException('Invite code has expired.', HttpStatus.GONE); + } + const space = await this.spacesService.findSpace(inviteCodeData.space_uuid); return { statusCode: 200, message: 'Success', data: space }; } } diff --git a/nestjs-BE/server/src/invite-codes/invite-codes.service.ts b/nestjs-BE/server/src/invite-codes/invite-codes.service.ts index 8fdaa94d..c48722bf 100644 --- a/nestjs-BE/server/src/invite-codes/invite-codes.service.ts +++ b/nestjs-BE/server/src/invite-codes/invite-codes.service.ts @@ -1,67 +1,47 @@ -import { Injectable, HttpException, HttpStatus } from '@nestjs/common'; -import { CreateInviteCodeDto } from './dto/create-invite-code.dto'; -import { BaseService } from 'src/base/base.service'; +import { Injectable } from '@nestjs/common'; import { PrismaService } from 'src/prisma/prisma.service'; -import { TemporaryDatabaseService } from 'src/temporary-database/temporary-database.service'; import { - INVITE_CODE_CACHE_SIZE, INVITE_CODE_EXPIRY_HOURS, INVITE_CODE_LENGTH, } from 'src/config/magic-number'; -import { SpacesService } from 'src/spaces/spaces.service'; -import { ResponseUtils } from 'src/utils/response'; - -export interface InviteCodeData extends CreateInviteCodeDto { - uuid?: string; - invite_code: string; - expiry_date: Date; -} +import { InviteCode, Prisma } from '@prisma/client'; +import generateUuid from 'src/utils/uuid'; @Injectable() -export class InviteCodesService extends BaseService { - constructor( - protected prisma: PrismaService, - protected temporaryDatabaseService: TemporaryDatabaseService, - protected spacesService: SpacesService, - ) { - super({ - prisma, - temporaryDatabaseService, - cacheSize: INVITE_CODE_CACHE_SIZE, - className: 'INVITE_CODE_TB', - field: 'invite_code', - }); - } +export class InviteCodesService { + constructor(protected prisma: PrismaService) {} - generateKey(data: InviteCodeData): string { - return data.invite_code; - } - - async createCode(createInviteCodeDto: CreateInviteCodeDto) { - const { space_uuid: spaceUuid } = createInviteCodeDto; - await this.spacesService.findSpace(spaceUuid); - const inviteCodeData = await this.generateInviteCode(createInviteCodeDto); - super.create(inviteCodeData); - const { invite_code } = inviteCodeData; - return ResponseUtils.createResponse(HttpStatus.CREATED, { invite_code }); + async findInviteCode(inviteCode: string): Promise { + return this.prisma.inviteCode.findUnique({ + where: { invite_code: inviteCode }, + }); } - async findSpace(inviteCode: string) { - const inviteCodeData = await this.getInviteCodeData(inviteCode); - this.checkExpiry(inviteCode, inviteCodeData.expiry_date); - return this.spacesService.findSpace(inviteCodeData.space_uuid); + async createInviteCode(spaceUuid: string): Promise { + return this.prisma.inviteCode.create({ + data: { + uuid: generateUuid(), + invite_code: await this.generateUniqueInviteCode(INVITE_CODE_LENGTH), + space_uuid: spaceUuid, + expiry_date: this.calculateExpiryDate(), + }, + }); } - private async generateInviteCode(createInviteCodeDto: CreateInviteCodeDto) { - const uniqueInviteCode = - await this.generateUniqueInviteCode(INVITE_CODE_LENGTH); - const expiryDate = this.calculateExpiryDate(); - - return { - ...createInviteCodeDto, - invite_code: uniqueInviteCode, - expiry_date: expiryDate, - }; + async deleteInviteCode(inviteCode: string): Promise { + try { + return await this.prisma.inviteCode.delete({ + where: { + invite_code: inviteCode, + }, + }); + } catch (err) { + if (err instanceof Prisma.PrismaClientKnownRequestError) { + return null; + } else { + throw err; + } + } } private calculateExpiryDate(): Date { @@ -71,19 +51,9 @@ export class InviteCodesService extends BaseService { return expiryDate; } - private async getInviteCodeData(inviteCode: string) { - const inviteCodeResponse = await super.findOne(inviteCode); - const { data: inviteCodeData } = inviteCodeResponse; - return inviteCodeData; - } - - private checkExpiry(inviteCode: string, expiryDate: Date) { - const currentTimestamp = new Date().getTime(); - const expiryTimestamp = new Date(expiryDate).getTime(); - if (expiryTimestamp < currentTimestamp) { - super.remove(inviteCode); - throw new HttpException('Invite code has expired.', HttpStatus.GONE); - } + checkExpiry(expiryDate: Date) { + const currentTimestamp = new Date(); + return expiryDate < currentTimestamp ? true : false; } private generateShortInviteCode(length: number) { @@ -100,11 +70,11 @@ export class InviteCodesService extends BaseService { private async generateUniqueInviteCode(length: number): Promise { let inviteCode: string; - let inviteCodeData: InviteCodeData; + let inviteCodeData: InviteCode; do { inviteCode = this.generateShortInviteCode(length); - inviteCodeData = await super.getDataFromCacheOrDB(inviteCode); + inviteCodeData = await this.findInviteCode(inviteCode); } while (inviteCodeData !== null); return inviteCode; diff --git a/nestjs-BE/server/src/temporary-database/temporary-database.service.ts b/nestjs-BE/server/src/temporary-database/temporary-database.service.ts index 55815147..30cc523d 100644 --- a/nestjs-BE/server/src/temporary-database/temporary-database.service.ts +++ b/nestjs-BE/server/src/temporary-database/temporary-database.service.ts @@ -4,7 +4,6 @@ import { Cron } from '@nestjs/schedule'; import { promises as fs } from 'fs'; import { join } from 'path'; import { TokenData } from 'src/auth/auth.service'; -import { InviteCodeData } from 'src/invite-codes/invite-codes.service'; import { CreateProfileSpaceDto } from 'src/profile-space/dto/create-profile-space.dto'; import { UpdateProfileDto } from 'src/profiles/dto/update-profile.dto'; import { UpdateUserDto } from 'src/users/dto/update-user.dto'; @@ -18,7 +17,6 @@ type DeleteDataType = { export type InsertDataType = | TokenData - | InviteCodeData | CreateProfileSpaceDto | UpdateProfileDto | UpdateUserDto; @@ -52,7 +50,7 @@ export class TemporaryDatabaseService { } private initializeDatabase() { - const services = ['REFRESH_TOKEN_TB', 'INVITE_CODE_TB']; + const services = ['REFRESH_TOKEN_TB']; const operations = ['insert', 'update', 'delete']; services.forEach((service) => { From 4e633683199477497512f588d1ad455c47243357 Mon Sep 17 00:00:00 2001 From: Conut-1 <1mim1@naver.com> Date: Mon, 13 May 2024 18:23:11 +0900 Subject: [PATCH 13/17] =?UTF-8?q?fix:=20RefreshToken=20=EB=AA=A8=EB=8D=B8?= =?UTF-8?q?=20=EC=88=98=EC=A0=95?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../migration.sql | 17 +++++++++++++++++ .../20240513082711_alter_index/migration.sql | 11 +++++++++++ nestjs-BE/server/prisma/schema.prisma | 4 +++- 3 files changed, 31 insertions(+), 1 deletion(-) create mode 100644 nestjs-BE/server/prisma/migrations/20240513072450_fix_refresh_token/migration.sql create mode 100644 nestjs-BE/server/prisma/migrations/20240513082711_alter_index/migration.sql diff --git a/nestjs-BE/server/prisma/migrations/20240513072450_fix_refresh_token/migration.sql b/nestjs-BE/server/prisma/migrations/20240513072450_fix_refresh_token/migration.sql new file mode 100644 index 00000000..b533af71 --- /dev/null +++ b/nestjs-BE/server/prisma/migrations/20240513072450_fix_refresh_token/migration.sql @@ -0,0 +1,17 @@ +/* + Warnings: + + - The primary key for the `RefreshToken` table will be changed. If it partially fails, the table could be left without primary key constraint. + - Added the required column `id` to the `RefreshToken` table without a default value. This is not possible if the table is not empty. + +*/ +-- AlterTable +ALTER TABLE `RefreshToken` DROP PRIMARY KEY, + ADD COLUMN `id` INTEGER NOT NULL AUTO_INCREMENT, + RENAME COLUMN `uuid` TO `token`, + ADD PRIMARY KEY (`id`); + +ALTER TABLE `RefreshToken` MODIFY `token` VARCHAR(210) NOT NULL; + +-- CreateIndex +CREATE INDEX `RefreshToken_token_idx` ON `RefreshToken`(`token`); diff --git a/nestjs-BE/server/prisma/migrations/20240513082711_alter_index/migration.sql b/nestjs-BE/server/prisma/migrations/20240513082711_alter_index/migration.sql new file mode 100644 index 00000000..0ef3796d --- /dev/null +++ b/nestjs-BE/server/prisma/migrations/20240513082711_alter_index/migration.sql @@ -0,0 +1,11 @@ +/* + Warnings: + + - A unique constraint covering the columns `[token]` on the table `RefreshToken` will be added. If there are existing duplicate values, this will fail. + +*/ +-- DropIndex +DROP INDEX `RefreshToken_token_idx` ON `RefreshToken`; + +-- CreateIndex +CREATE UNIQUE INDEX `RefreshToken_token_key` ON `RefreshToken`(`token`); diff --git a/nestjs-BE/server/prisma/schema.prisma b/nestjs-BE/server/prisma/schema.prisma index 9aeab2e0..cd971414 100644 --- a/nestjs-BE/server/prisma/schema.prisma +++ b/nestjs-BE/server/prisma/schema.prisma @@ -17,10 +17,12 @@ model User { } model RefreshToken { - uuid String @id @db.VarChar(32) + id Int @id @default(autoincrement()) + token String @db.VarChar(210) expiry_date DateTime user_id String user User @relation(fields: [user_id], references: [uuid], onDelete: Cascade) + @@unique([token]) } model Profile { From 37cc0d047823837875a7c2c898e5ad3b009a155a Mon Sep 17 00:00:00 2001 From: Conut-1 <1mim1@naver.com> Date: Mon, 13 May 2024 18:35:01 +0900 Subject: [PATCH 14/17] =?UTF-8?q?feat:=20RefreshToken=20service=20?= =?UTF-8?q?=EC=B6=94=EA=B0=80?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- nestjs-BE/server/src/auth/auth.controller.ts | 29 ++++-- nestjs-BE/server/src/auth/auth.module.ts | 2 + nestjs-BE/server/src/auth/auth.service.ts | 89 +++---------------- .../server/src/auth/refresh-tokens.service.ts | 60 +++++++++++++ .../temporary-database.service.ts | 4 +- 5 files changed, 100 insertions(+), 84 deletions(-) create mode 100644 nestjs-BE/server/src/auth/refresh-tokens.service.ts diff --git a/nestjs-BE/server/src/auth/auth.controller.ts b/nestjs-BE/server/src/auth/auth.controller.ts index 125bfc0b..066e03fb 100644 --- a/nestjs-BE/server/src/auth/auth.controller.ts +++ b/nestjs-BE/server/src/auth/auth.controller.ts @@ -1,4 +1,10 @@ -import { Controller, Post, Body, NotFoundException } from '@nestjs/common'; +import { + Controller, + Post, + Body, + NotFoundException, + BadRequestException, +} from '@nestjs/common'; import { AuthService } from './auth.service'; import { Public } from './public.decorator'; import { KakaoUserDto } from './dto/kakao-user.dto'; @@ -7,6 +13,7 @@ import { RefreshTokenDto } from './dto/refresh-token.dto'; import { ProfilesService } from 'src/profiles/profiles.service'; import { ApiTags, ApiResponse, ApiOperation } from '@nestjs/swagger'; import customEnv from 'src/config/env'; +import { RefreshTokensService } from './refresh-tokens.service'; @Controller('auth') @ApiTags('auth') @@ -15,6 +22,7 @@ export class AuthController { private authService: AuthService, private usersService: UsersService, private profilesService: ProfilesService, + private refreshTokensService: RefreshTokensService, ) {} @Post('kakao-oauth') @@ -50,7 +58,8 @@ export class AuthController { }; await this.profilesService.createProfile(profileData); } - return this.authService.login(userUuid); + const tokenData = await this.authService.login(userUuid); + return { statusCode: 200, message: 'Success', data: tokenData }; } @Post('token') @@ -64,15 +73,23 @@ export class AuthController { status: 401, description: 'Refresh token expired. Please log in again.', }) - renewAccessToken(@Body() refreshTokenDto: RefreshTokenDto) { + async renewAccessToken(@Body() refreshTokenDto: RefreshTokenDto) { const refreshToken = refreshTokenDto.refresh_token; - return this.authService.renewAccessToken(refreshToken); + const accessToken = await this.authService.renewAccessToken(refreshToken); + return { + statusCode: 200, + message: 'Success', + data: { access_token: accessToken }, + }; } @Post('logout') @Public() - logout(@Body() refreshTokenDto: RefreshTokenDto) { + async logout(@Body() refreshTokenDto: RefreshTokenDto) { const refreshToken = refreshTokenDto.refresh_token; - return this.authService.remove(refreshToken); + const token = + await this.refreshTokensService.deleteRefreshToken(refreshToken); + if (!token) throw new BadRequestException(); + return { statusCode: 204, message: 'No Content' }; } } diff --git a/nestjs-BE/server/src/auth/auth.module.ts b/nestjs-BE/server/src/auth/auth.module.ts index 14b7cccd..027a13dc 100644 --- a/nestjs-BE/server/src/auth/auth.module.ts +++ b/nestjs-BE/server/src/auth/auth.module.ts @@ -8,6 +8,7 @@ import { JwtStrategy } from './jwt.strategy'; import { APP_GUARD } from '@nestjs/core'; import { JwtAuthGuard } from './jwt-auth.guard'; import { ProfilesModule } from 'src/profiles/profiles.module'; +import { RefreshTokensService } from './refresh-tokens.service'; @Module({ imports: [UsersModule, PassportModule, JwtModule, ProfilesModule], @@ -16,6 +17,7 @@ import { ProfilesModule } from 'src/profiles/profiles.module'; AuthService, JwtStrategy, { provide: APP_GUARD, useClass: JwtAuthGuard }, + RefreshTokensService, ], exports: [AuthService], }) diff --git a/nestjs-BE/server/src/auth/auth.service.ts b/nestjs-BE/server/src/auth/auth.service.ts index f1306fe3..b1401979 100644 --- a/nestjs-BE/server/src/auth/auth.service.ts +++ b/nestjs-BE/server/src/auth/auth.service.ts @@ -1,42 +1,17 @@ -import { Injectable, UnauthorizedException, HttpStatus } from '@nestjs/common'; +import { Injectable, UnauthorizedException } from '@nestjs/common'; import { JwtService } from '@nestjs/jwt'; import { jwtConstants, kakaoOauthConstants } from './constants'; import { stringify } from 'qs'; import { PrismaService } from 'src/prisma/prisma.service'; -import { TemporaryDatabaseService } from 'src/temporary-database/temporary-database.service'; -import { BaseService } from 'src/base/base.service'; -import { - REFRESH_TOKEN_CACHE_SIZE, - REFRESH_TOKEN_EXPIRY_DAYS, -} from 'src/config/magic-number'; -import generateUuid from 'src/utils/uuid'; -import { ResponseUtils } from 'src/utils/response'; - -export interface TokenData { - uuid: string; - expiry_date: Date; - user_id: string; -} +import { RefreshTokensService } from './refresh-tokens.service'; @Injectable() -export class AuthService extends BaseService { +export class AuthService { constructor( private jwtService: JwtService, + private refreshTokensService: RefreshTokensService, protected prisma: PrismaService, - protected temporaryDatabaseService: TemporaryDatabaseService, - ) { - super({ - prisma, - temporaryDatabaseService, - cacheSize: REFRESH_TOKEN_CACHE_SIZE, - className: 'REFRESH_TOKEN_TB', - field: 'uuid', - }); - } - - generateKey(data: TokenData): string { - return data.uuid; - } + ) {} async getKakaoAccount(kakaoUserId: number) { const url = `https://kapi.kakao.com/v2/user/me`; @@ -63,65 +38,29 @@ export class AuthService extends BaseService { return accessToken; } - private async createRefreshToken(): Promise> { - const refreshTokenUuid = generateUuid(); - const refreshToken = await this.jwtService.signAsync( - { uuid: refreshTokenUuid }, - { secret: jwtConstants.refreshSecret, expiresIn: '14d' }, - ); - return { refreshToken, refreshTokenUuid }; - } - - private createRefreshTokenData(refreshTokenUuid: string, userUuid: string) { - const currentDate = new Date(); - const expiryDate = new Date(currentDate); - expiryDate.setDate(currentDate.getDate() + REFRESH_TOKEN_EXPIRY_DAYS); - const refreshTokenData: TokenData = { - uuid: refreshTokenUuid, - expiry_date: expiryDate, - user_id: userUuid, - }; - return refreshTokenData; - } - async login(userUuid: string) { - const { refreshToken, refreshTokenUuid } = await this.createRefreshToken(); const accessToken = await this.createAccessToken(userUuid); - const refreshTokenData = this.createRefreshTokenData( - refreshTokenUuid, - userUuid, - ); - super.create(refreshTokenData, false); - const tokenData = { + const refreshToken = + await this.refreshTokensService.createRefreshToken(userUuid); + return { access_token: accessToken, - refresh_token: refreshToken, + refresh_token: refreshToken.token, }; - return ResponseUtils.createResponse(HttpStatus.OK, tokenData); } - async renewAccessToken(refreshToken: string) { - const decodedToken = this.jwtService.decode(refreshToken); - const uuid = decodedToken?.uuid; + async renewAccessToken(refreshToken: string): Promise { try { this.jwtService.verify(refreshToken, { secret: jwtConstants.refreshSecret, }); - const { data: tokenData } = await super.findOne(uuid); - const accessToken = await this.createAccessToken(tokenData.user_id); - return ResponseUtils.createResponse(HttpStatus.OK, { - access_token: accessToken, - }); + const token = + await this.refreshTokensService.findRefreshToken(refreshToken); + const accessToken = await this.createAccessToken(token.user_id); + return accessToken; } catch (error) { - super.remove(uuid); throw new UnauthorizedException( 'Refresh token expired. Please log in again.', ); } } - - remove(refreshToken: string) { - const decodedToken = this.jwtService.decode(refreshToken); - const uuid = decodedToken?.uuid; - return super.remove(uuid); - } } diff --git a/nestjs-BE/server/src/auth/refresh-tokens.service.ts b/nestjs-BE/server/src/auth/refresh-tokens.service.ts new file mode 100644 index 00000000..e026c9bc --- /dev/null +++ b/nestjs-BE/server/src/auth/refresh-tokens.service.ts @@ -0,0 +1,60 @@ +import { Injectable } from '@nestjs/common'; +import { JwtService } from '@nestjs/jwt'; +import { PrismaService } from 'src/prisma/prisma.service'; +import generateUuid from 'src/utils/uuid'; +import { jwtConstants } from './constants'; +import { Prisma, RefreshToken } from '@prisma/client'; +import { REFRESH_TOKEN_EXPIRY_DAYS } from 'src/config/magic-number'; + +@Injectable() +export class RefreshTokensService { + constructor( + private prisma: PrismaService, + private jwtService: JwtService, + ) {} + + async createRefreshToken(userUuid: string): Promise { + return this.prisma.refreshToken.create({ + data: { + token: this.createToken(), + expiry_date: this.getExpiryDate(), + user_id: userUuid, + }, + }); + } + + async findRefreshToken(refreshToken: string): Promise { + return this.prisma.refreshToken.findUnique({ + where: { token: refreshToken }, + }); + } + + async deleteRefreshToken(refreshToken: string): Promise { + try { + return await this.prisma.refreshToken.delete({ + where: { token: refreshToken }, + }); + } catch (err) { + if (err instanceof Prisma.PrismaClientKnownRequestError) { + return null; + } else { + throw err; + } + } + } + + createToken(): string { + const refreshToken = this.jwtService.sign( + { uuid: generateUuid() }, + { secret: jwtConstants.refreshSecret, expiresIn: '14d' }, + ); + return refreshToken; + } + + getExpiryDate(): Date { + const currentDate = new Date(); + const expiryDate = new Date(currentDate); + expiryDate.setDate(currentDate.getDate() + REFRESH_TOKEN_EXPIRY_DAYS); + return expiryDate; + } +} diff --git a/nestjs-BE/server/src/temporary-database/temporary-database.service.ts b/nestjs-BE/server/src/temporary-database/temporary-database.service.ts index 30cc523d..9de0d0c3 100644 --- a/nestjs-BE/server/src/temporary-database/temporary-database.service.ts +++ b/nestjs-BE/server/src/temporary-database/temporary-database.service.ts @@ -3,7 +3,6 @@ import { PrismaService } from '../prisma/prisma.service'; import { Cron } from '@nestjs/schedule'; import { promises as fs } from 'fs'; import { join } from 'path'; -import { TokenData } from 'src/auth/auth.service'; import { CreateProfileSpaceDto } from 'src/profile-space/dto/create-profile-space.dto'; import { UpdateProfileDto } from 'src/profiles/dto/update-profile.dto'; import { UpdateUserDto } from 'src/users/dto/update-user.dto'; @@ -16,7 +15,6 @@ type DeleteDataType = { }; export type InsertDataType = - | TokenData | CreateProfileSpaceDto | UpdateProfileDto | UpdateUserDto; @@ -50,7 +48,7 @@ export class TemporaryDatabaseService { } private initializeDatabase() { - const services = ['REFRESH_TOKEN_TB']; + const services = []; const operations = ['insert', 'update', 'delete']; services.forEach((service) => { From 7ae23c5a56edc67013f0ac5a74d3ea8bd5adf8e1 Mon Sep 17 00:00:00 2001 From: Conut-1 <1mim1@naver.com> Date: Mon, 13 May 2024 18:40:29 +0900 Subject: [PATCH 15/17] =?UTF-8?q?remove:=20=EB=B6=88=ED=95=84=EC=9A=94?= =?UTF-8?q?=ED=95=9C=20=EC=BD=94=EB=93=9C=20=EC=A0=9C=EA=B1=B0?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- nestjs-BE/server/src/app.module.ts | 2 - nestjs-BE/server/src/base/base.service.ts | 169 --------------- .../temporary-database.module.ts | 9 - .../temporary-database.service.spec.ts | 18 -- .../temporary-database.service.ts | 202 ------------------ nestjs-BE/server/src/utils/response.ts | 8 +- 6 files changed, 2 insertions(+), 406 deletions(-) delete mode 100644 nestjs-BE/server/src/base/base.service.ts delete mode 100644 nestjs-BE/server/src/temporary-database/temporary-database.module.ts delete mode 100644 nestjs-BE/server/src/temporary-database/temporary-database.service.spec.ts delete mode 100644 nestjs-BE/server/src/temporary-database/temporary-database.service.ts diff --git a/nestjs-BE/server/src/app.module.ts b/nestjs-BE/server/src/app.module.ts index 59b27d08..bb479ceb 100644 --- a/nestjs-BE/server/src/app.module.ts +++ b/nestjs-BE/server/src/app.module.ts @@ -4,7 +4,6 @@ import { AppService } from './app.service'; import { AuthModule } from './auth/auth.module'; import { UsersModule } from './users/users.module'; import { PrismaModule } from './prisma/prisma.module'; -import { TemporaryDatabaseModule } from './temporary-database/temporary-database.module'; import { ProfilesModule } from './profiles/profiles.module'; import { SpacesModule } from './spaces/spaces.module'; import { BoardsModule } from './boards/boards.module'; @@ -21,7 +20,6 @@ import customEnv from './config/env'; AuthModule, UsersModule, PrismaModule, - TemporaryDatabaseModule, ScheduleModule.forRoot(), ProfilesModule, SpacesModule, diff --git a/nestjs-BE/server/src/base/base.service.ts b/nestjs-BE/server/src/base/base.service.ts deleted file mode 100644 index 2045dbed..00000000 --- a/nestjs-BE/server/src/base/base.service.ts +++ /dev/null @@ -1,169 +0,0 @@ -import { HttpException, HttpStatus } from '@nestjs/common'; -import { PrismaService } from '../prisma/prisma.service'; -import { TemporaryDatabaseService } from '../temporary-database/temporary-database.service'; -import LRUCache from '../utils/lru-cache'; -import generateUuid from '../utils/uuid'; -import { ResponseUtils } from 'src/utils/response'; - -interface BaseServiceOptions { - prisma: PrismaService; - temporaryDatabaseService: TemporaryDatabaseService; - cacheSize: number; - className: string; - field: string; -} - -export interface HasUuid { - uuid?: string; -} - -export abstract class BaseService { - protected cache: LRUCache; - protected className: string; - protected field: string; - protected prisma: PrismaService; - protected temporaryDatabaseService: TemporaryDatabaseService; - - constructor(options: BaseServiceOptions) { - this.cache = new LRUCache(options.cacheSize); - this.className = options.className; - this.field = options.field; - this.prisma = options.prisma; - this.temporaryDatabaseService = options.temporaryDatabaseService; - } - - abstract generateKey(data: T): string; - - async create(data: T, generateUuidFlag: boolean = true) { - if (generateUuidFlag) data.uuid = generateUuid(); - const key = this.generateKey(data); - const deleteCommand = this.temporaryDatabaseService.get( - this.className, - key, - 'delete', - ); - if (deleteCommand) { - this.temporaryDatabaseService.delete(this.className, key, 'delete'); - } else { - const storeData = await this.getDataFromCacheOrDB(key); - if (storeData) { - throw new HttpException('Data already exists.', HttpStatus.CONFLICT); - } - this.temporaryDatabaseService.create(this.className, key, data); - } - this.cache.put(key, data); - return ResponseUtils.createResponse(HttpStatus.CREATED, data); - } - - async findOne(key: string) { - const data = await this.getDataFromCacheOrDB(key); - const deleteCommand = this.temporaryDatabaseService.get( - this.className, - key, - 'delete', - ); - if (deleteCommand) { - throw new HttpException('Not Found', HttpStatus.NOT_FOUND); - } - if (data) { - const mergedData = this.mergeWithUpdateCommand(data, key); - this.cache.put(key, mergedData); - return ResponseUtils.createResponse(HttpStatus.OK, mergedData); - } else { - throw new HttpException('Not Found', HttpStatus.NOT_FOUND); - } - } - - async update(key: string, updateData: T) { - const data = await this.getDataFromCacheOrDB(key); - if (data) { - const updatedData = { - field: this.field, - value: { ...data, ...updateData }, - }; - if (this.temporaryDatabaseService.get(this.className, key, 'insert')) { - this.temporaryDatabaseService.create( - this.className, - key, - updatedData.value, - ); - } else { - this.temporaryDatabaseService.update(this.className, key, updatedData); - } - this.cache.put(key, updatedData.value); - return ResponseUtils.createResponse(HttpStatus.OK, updatedData.value); - } else { - return ResponseUtils.createResponse(HttpStatus.NOT_FOUND); - } - } - - async remove(key: string) { - const storeData = await this.getDataFromCacheOrDB(key); - if (!storeData) return ResponseUtils.createResponse(HttpStatus.NO_CONTENT); - this.cache.delete(key); - const insertTemporaryData = this.temporaryDatabaseService.get( - this.className, - key, - 'insert', - ); - const updateTemporaryData = this.temporaryDatabaseService.get( - this.className, - key, - 'update', - ); - if (updateTemporaryData) { - this.temporaryDatabaseService.delete(this.className, key, 'update'); - } - if (insertTemporaryData) { - this.temporaryDatabaseService.delete(this.className, key, 'insert'); - } else { - const value = key.includes('+') ? this.stringToObject(key) : key; - this.temporaryDatabaseService.remove(this.className, key, { - field: this.field, - value, - }); - } - return ResponseUtils.createResponse(HttpStatus.NO_CONTENT); - } - - async getDataFromCacheOrDB(key: string): Promise { - if (!key) throw new HttpException('Bad Request', HttpStatus.BAD_REQUEST); - const cacheData = this.cache.get(key); - if (cacheData) return cacheData; - const temporaryDatabaseData = this.temporaryDatabaseService.get( - this.className, - key, - 'insert', - ); - if (temporaryDatabaseData) return temporaryDatabaseData; - const databaseData = await this.prisma[this.className].findUnique({ - where: { - [this.field]: key.includes('+') ? this.stringToObject(key) : key, - }, - }); - return databaseData; - } - - private stringToObject(key: string) { - const obj = {}; - const keyValuePairs = key.split('+'); - - keyValuePairs.forEach((keyValue) => { - const [key, value] = keyValue.split(':'); - obj[key] = value; - }); - - return obj; - } - - private mergeWithUpdateCommand(data: T, key: string): T { - const updateCommand = this.temporaryDatabaseService.get( - this.className, - key, - 'update', - ); - if (updateCommand) return { ...data, ...updateCommand.value }; - - return data; - } -} diff --git a/nestjs-BE/server/src/temporary-database/temporary-database.module.ts b/nestjs-BE/server/src/temporary-database/temporary-database.module.ts deleted file mode 100644 index d3a2f58a..00000000 --- a/nestjs-BE/server/src/temporary-database/temporary-database.module.ts +++ /dev/null @@ -1,9 +0,0 @@ -import { Global, Module } from '@nestjs/common'; -import { TemporaryDatabaseService } from './temporary-database.service'; - -@Global() -@Module({ - providers: [TemporaryDatabaseService], - exports: [TemporaryDatabaseService], -}) -export class TemporaryDatabaseModule {} diff --git a/nestjs-BE/server/src/temporary-database/temporary-database.service.spec.ts b/nestjs-BE/server/src/temporary-database/temporary-database.service.spec.ts deleted file mode 100644 index b76bc582..00000000 --- a/nestjs-BE/server/src/temporary-database/temporary-database.service.spec.ts +++ /dev/null @@ -1,18 +0,0 @@ -import { Test, TestingModule } from '@nestjs/testing'; -import { TemporaryDatabaseService } from './temporary-database.service'; - -describe('TemporaryDatabaseService', () => { - let service: TemporaryDatabaseService; - - beforeEach(async () => { - const module: TestingModule = await Test.createTestingModule({ - providers: [TemporaryDatabaseService], - }).compile(); - - service = module.get(TemporaryDatabaseService); - }); - - it('should be defined', () => { - expect(service).toBeDefined(); - }); -}); diff --git a/nestjs-BE/server/src/temporary-database/temporary-database.service.ts b/nestjs-BE/server/src/temporary-database/temporary-database.service.ts deleted file mode 100644 index 9de0d0c3..00000000 --- a/nestjs-BE/server/src/temporary-database/temporary-database.service.ts +++ /dev/null @@ -1,202 +0,0 @@ -import { Injectable } from '@nestjs/common'; -import { PrismaService } from '../prisma/prisma.service'; -import { Cron } from '@nestjs/schedule'; -import { promises as fs } from 'fs'; -import { join } from 'path'; -import { CreateProfileSpaceDto } from 'src/profile-space/dto/create-profile-space.dto'; -import { UpdateProfileDto } from 'src/profiles/dto/update-profile.dto'; -import { UpdateUserDto } from 'src/users/dto/update-user.dto'; -import costomEnv from 'src/config/env'; -const { CSV_FOLDER } = costomEnv; - -type DeleteDataType = { - field: string; - value: string | Record; -}; - -export type InsertDataType = - | CreateProfileSpaceDto - | UpdateProfileDto - | UpdateUserDto; - -type UpdateDataType = { - field: string; - value: InsertDataType; -}; -type DataType = InsertDataType | UpdateDataType | DeleteDataType; - -interface OperationData { - service: string; - uniqueKey: string; - command: string; - data: DataType; -} - -@Injectable() -export class TemporaryDatabaseService { - private database: Map>> = new Map(); - private readonly FOLDER_NAME = CSV_FOLDER; - - constructor(private readonly prismaMysql: PrismaService) { - this.init(); - } - - private async init() { - this.initializeDatabase(); - await this.readDataFromFiles(); - await this.executeBulkOperations(); - } - - private initializeDatabase() { - const services = []; - const operations = ['insert', 'update', 'delete']; - - services.forEach((service) => { - const serviceMap = new Map(); - this.database.set(service, serviceMap); - operations.forEach((operation) => { - serviceMap.set(operation, new Map()); - }); - }); - } - - private async readDataFromFiles() { - const files = await fs.readdir(this.FOLDER_NAME); - return Promise.all( - files - .filter((file) => file.endsWith('.csv')) - .map((file) => this.readDataFromFile(file)), - ); - } - - private async readDataFromFile(file: string) { - const [service, commandWithExtension] = file.split('-'); - const command = commandWithExtension.replace('.csv', ''); - const fileData = await fs.readFile(join(this.FOLDER_NAME, file), 'utf8'); - fileData.split('\n').forEach((line) => { - if (line.trim() !== '') { - const [uniqueKey, ...dataParts] = line.split(','); - const data = dataParts.join(','); - this.database - .get(service) - .get(command) - .set(uniqueKey, JSON.parse(data)); - } - }); - } - - get(service: string, uniqueKey: string, command: string): any { - return this.database.get(service).get(command).get(uniqueKey); - } - - create(service: string, uniqueKey: string, data: InsertDataType) { - this.operation({ service, uniqueKey, command: 'insert', data }); - } - - update(service: string, uniqueKey: string, data: UpdateDataType) { - this.operation({ service, uniqueKey, command: 'update', data }); - } - - remove(service: string, uniqueKey: string, data: DeleteDataType) { - this.operation({ service, uniqueKey, command: 'delete', data }); - } - - delete(service: string, uniqueKey: string, command: string) { - this.database.get(service).get(command).delete(uniqueKey); - const filePath = join(this.FOLDER_NAME, `${service}-${command}.csv`); - fs.readFile(filePath, 'utf8').then((fileData) => { - const lines = fileData.split('\n'); - const updatedFileData = lines - .filter((line) => !line.startsWith(`${uniqueKey},`)) - .join('\n'); - fs.writeFile(filePath, updatedFileData); - }); - } - - private operation({ service, uniqueKey, command, data }: OperationData) { - const filePath = join(this.FOLDER_NAME, `${service}-${command}.csv`); - fs.appendFile(filePath, `${uniqueKey},${JSON.stringify(data)}\n`, 'utf8'); - this.database.get(service).get(command).set(uniqueKey, data); - } - - @Cron('0 */10 * * * *') - private async executeBulkOperations() { - for (const service of this.database.keys()) { - const serviceMap = this.database.get(service); - const prisma = this.prismaMysql; - await this.performInsert(service, serviceMap.get('insert'), prisma); - await this.performUpdate(service, serviceMap.get('update'), prisma); - await this.performDelete(service, serviceMap.get('delete'), prisma); - } - } - - private async performInsert( - service: string, - dataMap: Map, - prisma: PrismaService, - ) { - const data = this.prepareData(service, 'insert', dataMap); - if (!data.length) return; - await prisma[service].createMany({ - data: data, - skipDuplicates: true, - }); - } - - private async performUpdate( - service: string, - dataMap: Map, - prisma: PrismaService, - ) { - const data = this.prepareData(service, 'update', dataMap); - if (!data.length) return; - await Promise.all( - data.map((item) => { - const keyField = item.field; - const keyValue = item.value[keyField]; - const updatedValue = Object.fromEntries( - Object.entries(item.value).filter(([key]) => key !== 'uuid'), - ); - return prisma[service].update({ - where: { [keyField]: keyValue }, - data: updatedValue, - }); - }), - ); - } - - private async performDelete( - service: string, - dataMap: Map, - prisma: PrismaService, - ) { - const data = this.prepareData(service, 'delete', dataMap); - if (!data.length) return; - await Promise.all( - data.map(async (item) => { - try { - await prisma[service].delete({ - where: { [item.field]: item.value }, - }); - } finally { - return; - } - }), - ); - } - - private prepareData( - service: string, - operation: string, - dataMap: Map, - ) { - const data = Array.from(dataMap.values()); - this.clearFile(`${service}-${operation}.csv`); - dataMap.clear(); - return data; - } - - private clearFile(filename: string) { - fs.writeFile(join(this.FOLDER_NAME, filename), '', 'utf8'); - } -} diff --git a/nestjs-BE/server/src/utils/response.ts b/nestjs-BE/server/src/utils/response.ts index a3c01ef4..291d40b9 100644 --- a/nestjs-BE/server/src/utils/response.ts +++ b/nestjs-BE/server/src/utils/response.ts @@ -1,5 +1,5 @@ import { HttpStatus } from '@nestjs/common'; -import { InsertDataType } from 'src/temporary-database/temporary-database.service'; + type TokenDataType = { access_token: string; refresh_token?: string; @@ -8,11 +8,7 @@ type InviteDataType = { invite_code: string; }; -type ExtendedDataType = - | InsertDataType - | TokenDataType - | InviteDataType - | InsertDataType[]; +type ExtendedDataType = TokenDataType | InviteDataType; export class ResponseUtils { private static messages = new Map([ From a88e0d2e551773318cc0ad7b67104e424e1fdda1 Mon Sep 17 00:00:00 2001 From: Conut-1 <1mim1@naver.com> Date: Mon, 13 May 2024 18:41:03 +0900 Subject: [PATCH 16/17] =?UTF-8?q?chore:=20.gitignore=20=EC=88=98=EC=A0=95?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- nestjs-BE/server/.gitignore | 4 ---- 1 file changed, 4 deletions(-) diff --git a/nestjs-BE/server/.gitignore b/nestjs-BE/server/.gitignore index 4af1e623..2d2fb1cc 100644 --- a/nestjs-BE/server/.gitignore +++ b/nestjs-BE/server/.gitignore @@ -32,7 +32,3 @@ lerna-debug.log* # Environment Variable File .env - -# csv, prisma -/operations -/prisma/generated From 3c62c587fe07e19b4d573ef487aa34f0ea1212f9 Mon Sep 17 00:00:00 2001 From: Conut-1 <1mim1@naver.com> Date: Mon, 20 May 2024 16:32:35 +0900 Subject: [PATCH 17/17] =?UTF-8?q?chore:=20git=20action=20=EC=88=98?= =?UTF-8?q?=EC=A0=95?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .github/workflows/BE-deploy.yml | 3 --- 1 file changed, 3 deletions(-) diff --git a/.github/workflows/BE-deploy.yml b/.github/workflows/BE-deploy.yml index 0d42d518..5891b8af 100644 --- a/.github/workflows/BE-deploy.yml +++ b/.github/workflows/BE-deploy.yml @@ -31,7 +31,6 @@ jobs: echo "BASE_IMAGE_URL=$BASE_IMAGE_URL" >> ./nestjs-BE/server/.env echo "BUCKET_NAME=$BUCKET_NAME" >> ./nestjs-BE/server/.env echo "APP_ICON_URL=$APP_ICON_URL" >> ./nestjs-BE/server/.env - echo "CSV_FOLDER=$CSV_FOLDER" >> ./nestjs-BE/server/.env docker build -t ghcr.io/${{ secrets.PACKAGE_USERNAME }}/mindsync ./nestjs-BE/server docker push ghcr.io/${{ secrets.PACKAGE_USERNAME }}/mindsync:latest env: @@ -49,7 +48,6 @@ jobs: BASE_IMAGE_URL: ${{ secrets.BASE_IMAGE_URL }} BUCKET_NAME: ${{ secrets.BUCKET_NAME }} APP_ICON_URL: ${{ secrets.APP_ICON_URL }} - CSV_FOLDER: ${{ secrets.CSV_FOLDER }} deploy: needs: build @@ -70,6 +68,5 @@ jobs: sudo docker run -d \ --name mindsync_server \ -p ${{ secrets.SERVER_PORT }}:${{ secrets.CONTAINER_PORT }} \ - -v temporary-volume:${{ secrets.CSV_FOLDER }} \ --net mybridge \ ghcr.io/${{ secrets.PACKAGE_USERNAME }}/mindsync