Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
4 changes: 2 additions & 2 deletions src/common/constants/errorMessage.enum.ts
Original file line number Diff line number Diff line change
Expand Up @@ -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 역할을 가진 사용자만 이 리소스에 접근할 수 있습니다. 권한을 확인해 주세요.',
Expand Down
12 changes: 9 additions & 3 deletions src/modules/notification/notification.controller.ts
Original file line number Diff line number Diff line change
@@ -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')
Expand Down Expand Up @@ -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$);
}
}
4 changes: 4 additions & 0 deletions src/modules/user/domain/user.domain.ts
Original file line number Diff line number Diff line change
Expand Up @@ -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);
}
Expand Down
1 change: 1 addition & 0 deletions src/modules/user/domain/user.interface.ts
Original file line number Diff line number Diff line change
Expand Up @@ -21,6 +21,7 @@ export interface IUser {
OAuthData(): OAuthProperties;
getId(): string;
getRole(): Role | null;
getNickName(): string;
isFollowed(dreamerId: string): boolean;
getWithMakerProfile(withDetails?: boolean): Partial<MakerInfoAndProfileProperties>;
getStats(): UserStatsToClientProperties;
Expand Down
4 changes: 2 additions & 2 deletions src/modules/user/user.service.ts
Original file line number Diff line number Diff line change
Expand Up @@ -58,8 +58,8 @@ 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);
if (existNickName) {
throw new BadRequestError(ErrorMessage.USER_NICKNAME_EXIST);
}
Expand Down
13 changes: 13 additions & 0 deletions test.json
Original file line number Diff line number Diff line change
@@ -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/*"
}
]
}