From df4992876c58debe558df9277b28c3087b6467e2 Mon Sep 17 00:00:00 2001 From: Maybeiley <2784519@gmail.com> Date: Sun, 23 Feb 2025 16:46:54 +0900 Subject: [PATCH 1/3] =?UTF-8?q?Feat:=20#265=20SSE=20=EC=97=B0=EA=B2=B0=20?= =?UTF-8?q?=EB=81=8A=EA=B9=80=20=EC=98=A4=EB=A5=98=20=ED=95=B4=EA=B2=B0?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- src/modules/notification/notification.controller.ts | 12 +++++++++--- 1 file changed, 9 insertions(+), 3 deletions(-) diff --git a/src/modules/notification/notification.controller.ts b/src/modules/notification/notification.controller.ts index 2dba60a..fe1d378 100644 --- a/src/modules/notification/notification.controller.ts +++ b/src/modules/notification/notification.controller.ts @@ -1,8 +1,8 @@ import { Controller, Get, Param, Patch, Post, Sse } from '@nestjs/common'; import NotificationService from './notification.service'; import { UserId } from 'src/common/decorators/user.decorator'; -import { map, Observable } from 'rxjs'; -import { Public } from 'src/common/decorators/public.decorator'; +import { interval, merge, Observable } from 'rxjs'; +import { map } from 'rxjs/operators'; import { NotificationEventName, NotificationProperties } from './types/notification.types'; @Controller('notifications') @@ -41,10 +41,16 @@ export default class NotificationController { @Sse('stream') stream(@UserId() userId: string): Observable<{ data: string }> { console.log(`SSE connection for userId: ${userId}`); - return this.service.stream(userId).pipe( + + // 40초마다 Heartbeat 전송하여 연결 유지 + const heartbeat$ = interval(40000).pipe(map(() => ({ data: 'ping' }))); + const notification$ = this.service.stream(userId).pipe( map((content: string) => { return { data: content }; }) ); + + // Heartbeat와 알림 stream 병합 + return merge(heartbeat$, notification$); } } From 80ae08073aa8f5fb14b1fad5275b682256e897d8 Mon Sep 17 00:00:00 2001 From: Maybeiley <2784519@gmail.com> Date: Sun, 23 Feb 2025 16:58:22 +0900 Subject: [PATCH 2/3] =?UTF-8?q?Feat:=20#266=20User=20=EB=8B=89=EB=84=A4?= =?UTF-8?q?=EC=9E=84=20=EC=88=98=EC=A0=95=20=EC=98=A4=EB=A5=98=20=ED=95=B4?= =?UTF-8?q?=EA=B2=B0?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- src/common/constants/errorMessage.enum.ts | 4 ++-- src/modules/user/domain/user.domain.ts | 4 ++++ src/modules/user/domain/user.interface.ts | 1 + src/modules/user/user.service.ts | 5 +++-- 4 files changed, 10 insertions(+), 4 deletions(-) diff --git a/src/common/constants/errorMessage.enum.ts b/src/common/constants/errorMessage.enum.ts index c6b9366..7324499 100644 --- a/src/common/constants/errorMessage.enum.ts +++ b/src/common/constants/errorMessage.enum.ts @@ -3,9 +3,9 @@ export enum ErrorMessage { USER_NOT_FOUND = '해당 유저를 찾을 수 없습니다.', USER_EXIST = '이미 존재하는 이메일입니다.', USER_OAUTH_EXIST = '이미 존재하는 소셜 로그인 유저입니다.', - USER_NICKNAME_EXIST = '이미 존재하는 닉네임입니다.', + USER_NICKNAME_EXIST = '이미 존재하는 닉네임입니다. 다시 시도해 주세요.', USER_UNAUTHORIZED_ID = '존재하지 않는 email 입니다.', - USER_BAD_REQUEST_PW = '비밀번호가 일치하지 않습니다.', + USER_BAD_REQUEST_PW = '비밀번호가 일치하지 않습니다. 다시 시도해 주세요.', USER_UNAUTHORIZED_TOKEN = '토큰의 유효기간이 만료되었습니다. 로그인이 필요합니다', USER_FORBIDDEN_NOT_OWNER = 'Plan을 등록한 본인만 수정, 삭제할 수 있습니다. 권한을 확인해주세요.', USER_FORBIDDEN_NOT_MAKER = 'Maker 역할을 가진 사용자만 이 리소스에 접근할 수 있습니다. 권한을 확인해 주세요.', diff --git a/src/modules/user/domain/user.domain.ts b/src/modules/user/domain/user.domain.ts index 36b7acb..7d5c6d0 100644 --- a/src/modules/user/domain/user.domain.ts +++ b/src/modules/user/domain/user.domain.ts @@ -159,6 +159,10 @@ export default class User implements IUser { return this.role ?? null; } + getNickName(): string { + return this.nickName; + } + isFollowed(dreamerId: string): boolean { return this.followers?.some((follower) => follower.dreamerId === dreamerId); } diff --git a/src/modules/user/domain/user.interface.ts b/src/modules/user/domain/user.interface.ts index 1b803c2..3d55e7f 100644 --- a/src/modules/user/domain/user.interface.ts +++ b/src/modules/user/domain/user.interface.ts @@ -21,6 +21,7 @@ export interface IUser { OAuthData(): OAuthProperties; getId(): string; getRole(): Role | null; + getNickName(): string; isFollowed(dreamerId: string): boolean; getWithMakerProfile(withDetails?: boolean): Partial; getStats(): UserStatsToClientProperties; diff --git a/src/modules/user/user.service.ts b/src/modules/user/user.service.ts index b6f0b7d..5e352fc 100644 --- a/src/modules/user/user.service.ts +++ b/src/modules/user/user.service.ts @@ -58,8 +58,9 @@ export default class UserService { throw new BadRequestError(ErrorMessage.USER_NOT_FOUND); } - if (data.nickName) { - const existNickName = this.repository.findByNickName(data.nickName); + if (data.nickName && data.nickName !== user.getNickName()) { + const existNickName = await this.repository.findByNickName(data.nickName); + console.log(typeof existNickName, existNickName); if (existNickName) { throw new BadRequestError(ErrorMessage.USER_NICKNAME_EXIST); } From f23b61de412058a77232245704c556379806a60e Mon Sep 17 00:00:00 2001 From: Maybeiley <2784519@gmail.com> Date: Sun, 23 Feb 2025 17:10:03 +0900 Subject: [PATCH 3/3] =?UTF-8?q?Feat:=20#266=20=EC=BD=98=EC=86=94=20?= =?UTF-8?q?=EC=B6=9C=EB=A0=A5=20=EC=82=AD=EC=A0=9C?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- src/modules/user/user.service.ts | 1 - test.json | 13 +++++++++++++ 2 files changed, 13 insertions(+), 1 deletion(-) create mode 100644 test.json diff --git a/src/modules/user/user.service.ts b/src/modules/user/user.service.ts index 5e352fc..7947270 100644 --- a/src/modules/user/user.service.ts +++ b/src/modules/user/user.service.ts @@ -60,7 +60,6 @@ export default class UserService { if (data.nickName && data.nickName !== user.getNickName()) { const existNickName = await this.repository.findByNickName(data.nickName); - console.log(typeof existNickName, existNickName); if (existNickName) { throw new BadRequestError(ErrorMessage.USER_NICKNAME_EXIST); } diff --git a/test.json b/test.json new file mode 100644 index 0000000..3be50e6 --- /dev/null +++ b/test.json @@ -0,0 +1,13 @@ +{ + "Version": "2012-10-17", + "Statement": [ + { + "Effect": "Allow", + "Principal": { + "AWS": "arn:aws:iam::YOUR_EC2_ACCOUNT_ID:role/YOUR_IAM_ROLE_NAME" + }, + "Action": ["s3:GetObject", "s3:PutObject"], + "Resource": "arn:aws:s3:::TARGET_BUCKET_NAME/*" + } + ] +}