diff --git a/src/app.module.ts b/src/app.module.ts index b455988d..275bed44 100644 --- a/src/app.module.ts +++ b/src/app.module.ts @@ -25,6 +25,7 @@ import { ChatModule } from './module/chat/chat.module'; import { AppController } from './app.controller'; import { ImageModule } from './module/image/image.module'; import { PushModule } from './module/push/push.module'; +import { EventModule } from './infrastructure/event/event.module'; @Module({ imports: [ @@ -56,6 +57,7 @@ import { PushModule } from './module/push/push.module'; delimiter: ':', wildcard: true, }), + EventModule, ClsModule.forRoot({ global: true, middleware: { mount: true }, diff --git a/src/database/redis/redis-event.types.ts b/src/database/redis/redis-event.types.ts new file mode 100644 index 00000000..2c410469 --- /dev/null +++ b/src/database/redis/redis-event.types.ts @@ -0,0 +1,43 @@ +export interface RedisEventPayloadMap { + newChatMessage: { + chatId: string; + senderId: string; + chatUsers: { + id: string; + name: string; + image: string | null; + url: string; + unreadCount: number; + }[]; + messages: { + id: string; + content: string | null; + image: string | null; + createdAt: Date; + replyTo: { + id: string; + content: string | null; + image: string | null; + createdAt: Date; + } | null; + }[]; + }; + + likeChatMessage: { + messageId: string; + }; + + unlikeChatMessage: { + messageId: string; + }; + + deleteChat: { + chatIds: string[]; + }; +} + +export type RedisEventName = keyof RedisEventPayloadMap; + +// Redis -> EventEmitter로 전달될 때 targetUserId가 추가됨 +export type RedisToEventPayload = + RedisEventPayloadMap[K] & { targetUserId: string }; diff --git a/src/database/redis/redis.module.ts b/src/database/redis/redis.module.ts index 692af0dc..a5af4773 100644 --- a/src/database/redis/redis.module.ts +++ b/src/database/redis/redis.module.ts @@ -1,9 +1,10 @@ import { Module, Global } from '@nestjs/common'; import { RedisService } from 'src/database/redis/redis.service'; +import { TypedRedisPublisher } from './typed-redis-publisher'; @Global() @Module({ - providers: [RedisService], - exports: [RedisService], + providers: [RedisService, TypedRedisPublisher], + exports: [RedisService, TypedRedisPublisher], }) export class RedisModule {} diff --git a/src/database/redis/redis.service.ts b/src/database/redis/redis.service.ts index 3af72329..1dbd081f 100644 --- a/src/database/redis/redis.service.ts +++ b/src/database/redis/redis.service.ts @@ -1,7 +1,8 @@ import { Injectable, OnModuleDestroy } from '@nestjs/common'; import { ConfigService } from '@nestjs/config'; import Redis from 'ioredis'; -import { EventEmitter2 } from '@nestjs/event-emitter'; +import { TypedEventEmitter } from 'src/infrastructure/event/typed-event-emitter'; +import { RedisEventName, RedisEventPayloadMap } from './redis-event.types'; @Injectable() export class RedisService implements OnModuleDestroy { @@ -10,7 +11,7 @@ export class RedisService implements OnModuleDestroy { constructor( private configService: ConfigService, - private eventEmitter: EventEmitter2, + private eventEmitter: TypedEventEmitter, ) { this.redis = new Redis({ host: this.configService.get('REDIS_HOST'), @@ -18,12 +19,14 @@ export class RedisService implements OnModuleDestroy { this.subRedis = this.redis.duplicate(); this.subRedis.on('message', (channel: string, message: string) => { - const { event, ...payload } = JSON.parse(message); + const { event, ...payload } = JSON.parse(message) as { + event: RedisEventName; + } & RedisEventPayloadMap[RedisEventName]; this.eventEmitter.emit(event, { ...payload, targetUserId: channel.split(':')[1], - }); + } as never); }); } diff --git a/src/database/redis/typed-redis-publisher.ts b/src/database/redis/typed-redis-publisher.ts new file mode 100644 index 00000000..1e54de41 --- /dev/null +++ b/src/database/redis/typed-redis-publisher.ts @@ -0,0 +1,16 @@ +import { Injectable } from '@nestjs/common'; +import { RedisService } from './redis.service'; +import { RedisEventName, RedisEventPayloadMap } from './redis-event.types'; + +@Injectable() +export class TypedRedisPublisher { + constructor(private readonly redisService: RedisService) {} + + async publish( + channel: string, + event: K, + payload: RedisEventPayloadMap[K], + ) { + await this.redisService.publish(channel, { event, ...payload }); + } +} diff --git a/src/infrastructure/event/event-payload.types.ts b/src/infrastructure/event/event-payload.types.ts new file mode 100644 index 00000000..221c4736 --- /dev/null +++ b/src/infrastructure/event/event-payload.types.ts @@ -0,0 +1,51 @@ +import type { RedisToEventPayload } from 'src/database/redis/redis-event.types'; + +export interface EventPayloadMap { + 'notification:FEED_LIKE': { feedId: string; likeCount: number }; + 'notification:FEED_MENTION': { + actorId: string; + feedId: string; + mentionedUserId: string; + }; + 'notification:FEED_REPLY': { + actorId: string; + feedId: string; + parentId: string; + }; + 'notification:FEED_COMMENT': { actorId: string; feedId: string }; + + 'notification:POST_MENTION': { + actorId: string; + postId: string; + mentionedUserId: string; + }; + 'notification:POST_REPLY': { + actorId: string; + postId: string; + parentId: string; + }; + 'notification:POST_COMMENT': { actorId: string; postId: string }; + + 'notification:FOLLOW': { actorId: string; userId: string }; + + push: PushPayload; + + // Redis Pub/Sub -> EventEmitter로 전달되는 채팅 이벤트 + newChatMessage: RedisToEventPayload<'newChatMessage'>; + likeChatMessage: RedisToEventPayload<'likeChatMessage'>; + unlikeChatMessage: RedisToEventPayload<'unlikeChatMessage'>; + deleteChat: RedisToEventPayload<'deleteChat'>; +} + +export interface PushPayload { + userId: string; + title: string; + text: string; + imageUrl?: string | null; + data?: Record; + silent?: boolean; + key?: string; + badge?: number; +} + +export type EventName = keyof EventPayloadMap; diff --git a/src/infrastructure/event/event.module.ts b/src/infrastructure/event/event.module.ts new file mode 100644 index 00000000..3a18f3cd --- /dev/null +++ b/src/infrastructure/event/event.module.ts @@ -0,0 +1,9 @@ +import { Global, Module } from '@nestjs/common'; +import { TypedEventEmitter } from './typed-event-emitter'; + +@Global() +@Module({ + providers: [TypedEventEmitter], + exports: [TypedEventEmitter], +}) +export class EventModule {} diff --git a/src/infrastructure/event/typed-event-emitter.ts b/src/infrastructure/event/typed-event-emitter.ts new file mode 100644 index 00000000..e05c996f --- /dev/null +++ b/src/infrastructure/event/typed-event-emitter.ts @@ -0,0 +1,12 @@ +import { Injectable } from '@nestjs/common'; +import { EventEmitter2 } from '@nestjs/event-emitter'; +import { EventName, EventPayloadMap } from './event-payload.types'; + +@Injectable() +export class TypedEventEmitter { + constructor(private readonly eventEmitter: EventEmitter2) {} + + emit(event: K, payload: EventPayloadMap[K]) { + return this.eventEmitter.emit(event, payload); + } +} diff --git a/src/module/chat/chat-message.service.ts b/src/module/chat/chat-message.service.ts index 9a73a8a8..c0f3b6a4 100644 --- a/src/module/chat/chat-message.service.ts +++ b/src/module/chat/chat-message.service.ts @@ -4,7 +4,8 @@ import { ChatReader } from './repository/chat.reader'; import { getImageUrl } from 'src/shared/util/get-image-url'; import { UserReader } from '../user/repository/user.reader'; import { RedisService } from 'src/database/redis/redis.service'; -import { EventEmitter2 } from '@nestjs/event-emitter'; +import { TypedEventEmitter } from 'src/infrastructure/event/typed-event-emitter'; +import { TypedRedisPublisher } from 'src/database/redis/typed-redis-publisher'; @Injectable() export class ChatMessageService { @@ -14,7 +15,8 @@ export class ChatMessageService { private readonly chatReader: ChatReader, private readonly userReader: UserReader, private readonly redisService: RedisService, - private readonly eventEmitter: EventEmitter2, + private readonly eventEmitter: TypedEventEmitter, + private readonly redisPublisher: TypedRedisPublisher, ) { this.redisClient = this.redisService.pubClient; } @@ -116,7 +118,7 @@ export class ChatMessageService { const chatUsers = await this.chatReader.findUsersByChatId(chatId); - const newMessageEvent = { + const newMessagePayload = { chatId, senderId: userId, chatUsers: chatUsers.map((user) => ({ @@ -144,22 +146,24 @@ export class ChatMessageService { })), }; - await this.redisService.publish(`user:${userId}`, { - ...newMessageEvent, - event: 'newChatMessage', - }); + await this.redisPublisher.publish( + `user:${userId}`, + 'newChatMessage', + newMessagePayload, + ); const myInfo = chatUsers.find((user) => user.id === userId); if (!myInfo) throw new HttpException('USER', 404); if (targetUserIsOnline) { - await this.redisService.publish(`user:${targetUserStatus.userId}`, { - ...newMessageEvent, - event: 'newChatMessage', - }); + await this.redisPublisher.publish( + `user:${targetUserStatus.userId}`, + 'newChatMessage', + newMessagePayload, + ); } if (!targetUserJoinedChat) { - const pushPayload: PushPayload = { + this.eventEmitter.emit(`push`, { userId: targetUserStatus.userId, title: `${myInfo.name}`, text: @@ -170,12 +174,11 @@ export class ChatMessageService { data: { event: 'newChatMessage', deepLink: `/chats/${chatId}`, - data: JSON.stringify(newMessageEvent), + data: JSON.stringify(newMessagePayload), }, key: `chat-message-${chatId}`, badge: targetUserStatus.unreadCount + toCreateMessages.length, - }; - this.eventEmitter.emit(`push`, pushPayload); + }); } return; @@ -193,14 +196,14 @@ export class ChatMessageService { await this.chatWriter.updateMessageLike(messageId, true); - await this.redisService.publish(`user:${userId}`, { + await this.redisPublisher.publish(`user:${userId}`, 'likeChatMessage', { messageId, - event: 'likeChatMessage', - }); - await this.redisService.publish(`user:${targetUser.id}`, { - messageId, - event: 'likeChatMessage', }); + await this.redisPublisher.publish( + `user:${targetUser.id}`, + 'likeChatMessage', + { messageId }, + ); return; } @@ -215,14 +218,14 @@ export class ChatMessageService { await this.chatWriter.updateMessageLike(messageId, false); - await this.redisService.publish(`user:${userId}`, { + await this.redisPublisher.publish(`user:${userId}`, 'unlikeChatMessage', { messageId, - event: 'unlikeChatMessage', - }); - await this.redisService.publish(`user:${targetUser.id}`, { - messageId, - event: 'unlikeChatMessage', }); + await this.redisPublisher.publish( + `user:${targetUser.id}`, + 'unlikeChatMessage', + { messageId }, + ); return; } diff --git a/src/module/chat/chat.listener.ts b/src/module/chat/chat.listener.ts index c5e71f65..edb389be 100644 --- a/src/module/chat/chat.listener.ts +++ b/src/module/chat/chat.listener.ts @@ -1,40 +1,32 @@ import { Injectable } from '@nestjs/common'; import { OnEvent } from '@nestjs/event-emitter'; import { GlobalGateway } from '../websocket/global.gateway'; -import type { NewChatMessageEventResponse } from './dto'; +import type { EventPayloadMap } from 'src/infrastructure/event/event-payload.types'; @Injectable() export class ChatListener { constructor(private readonly gateway: GlobalGateway) {} @OnEvent('newChatMessage') - handleNewChatMessageEvent({ - targetUserId, - ...payload - }: NewChatMessageEventResponse & { targetUserId: string }) { - this.gateway.emitMessageEventToUser(targetUserId, payload); + handleNewChatMessageEvent(payload: EventPayloadMap['newChatMessage']) { + const { targetUserId, ...rest } = payload; + this.gateway.emitMessageEventToUser(targetUserId, rest); } @OnEvent('likeChatMessage') - handleLikeChatMessageEvent(payload: { - targetUserId: string; - messageId: string; - }) { + handleLikeChatMessageEvent(payload: EventPayloadMap['likeChatMessage']) { const { targetUserId, messageId } = payload; this.gateway.emitLikeChatMessageEventToUser(targetUserId, messageId); } @OnEvent('unlikeChatMessage') - handleUnlikeChatMessageEvent(payload: { - targetUserId: string; - messageId: string; - }) { + handleUnlikeChatMessageEvent(payload: EventPayloadMap['unlikeChatMessage']) { const { targetUserId, messageId } = payload; this.gateway.emitUnlikeChatMessageEventToUser(targetUserId, messageId); } @OnEvent('deleteChat') - handleDeleteChatEvent(payload: { targetUserId: string; chatIds: string[] }) { + handleDeleteChatEvent(payload: EventPayloadMap['deleteChat']) { const { targetUserId, chatIds } = payload; this.gateway.emitDeleteChatEventToUser(targetUserId, chatIds); } diff --git a/src/module/chat/chat.service.ts b/src/module/chat/chat.service.ts index 630c929d..692828ce 100644 --- a/src/module/chat/chat.service.ts +++ b/src/module/chat/chat.service.ts @@ -3,6 +3,7 @@ import { ChatReader } from './repository/chat.reader'; import { ChatWriter } from './repository/chat.writer'; import { UserReader } from '../user/repository/user.reader'; import { RedisService } from 'src/database/redis/redis.service'; +import { TypedRedisPublisher } from 'src/database/redis/typed-redis-publisher'; import { getImageUrl } from 'src/shared/util/get-image-url'; @Injectable() @@ -14,6 +15,7 @@ export class ChatService { private readonly chatWriter: ChatWriter, private readonly userReader: UserReader, private readonly redisService: RedisService, + private readonly redisPublisher: TypedRedisPublisher, ) { this.redisClient = this.redisService.pubClient; } @@ -119,18 +121,14 @@ export class ChatService { }); } - this.redisService.publish(`user:${userId}`, { - event: 'deleteChat', + this.redisPublisher.publish(`user:${userId}`, 'deleteChat', { chatIds: [chatId], }); } async deleteChats(userId: string, chatIds: string[]) { await this.chatWriter.exitManyChats(userId, chatIds); - this.redisService.publish(`user:${userId}`, { - event: 'deleteChat', - chatIds, - }); + this.redisPublisher.publish(`user:${userId}`, 'deleteChat', { chatIds }); return; } diff --git a/src/module/feed-comment/feed-comment.service.ts b/src/module/feed-comment/feed-comment.service.ts index a413c7a8..24fdb8d6 100644 --- a/src/module/feed-comment/feed-comment.service.ts +++ b/src/module/feed-comment/feed-comment.service.ts @@ -2,7 +2,7 @@ import { Injectable, HttpException } from '@nestjs/common'; import { FeedCommentReader } from './repository/feed-comment.reader'; import { FeedCommentWriter } from './repository/feed-comment.writer'; import { getImageUrl } from 'src/shared/util/get-image-url'; -import { EventEmitter2 } from '@nestjs/event-emitter'; +import { TypedEventEmitter } from 'src/infrastructure/event/typed-event-emitter'; import { Transactional } from '@nestjs-cls/transactional'; @Injectable() @@ -10,7 +10,7 @@ export class FeedCommentService { constructor( private readonly feedCommentReader: FeedCommentReader, private readonly feedCommentWriter: FeedCommentWriter, - private readonly eventEmitter: EventEmitter2, + private readonly eventEmitter: TypedEventEmitter, ) {} async create(userId: string, input: CreateFeedCommentInput) { diff --git a/src/module/feed/feed.service.ts b/src/module/feed/feed.service.ts index 3bb14c88..31e1834c 100644 --- a/src/module/feed/feed.service.ts +++ b/src/module/feed/feed.service.ts @@ -5,7 +5,7 @@ import { SearchService } from 'src/database/search/search.service'; import { DdbService } from 'src/database/ddb/ddb.service'; import { RedisService } from 'src/database/redis/redis.service'; import { getImageUrl } from 'src/shared/util/get-image-url'; -import { EventEmitter2 } from '@nestjs/event-emitter'; +import { TypedEventEmitter } from 'src/infrastructure/event/typed-event-emitter'; import { Transactional } from '@nestjs-cls/transactional'; import { UserReader } from '../user/repository/user.reader'; @@ -17,7 +17,7 @@ export class FeedService { private ddb: DdbService, private redisService: RedisService, @Inject(SearchService) private searchService: SearchService, - private eventEmitter: EventEmitter2, + private eventEmitter: TypedEventEmitter, private userReader: UserReader, ) {} diff --git a/src/module/notification/notification.listener.ts b/src/module/notification/notification.listener.ts index a17ddda3..80b3fced 100644 --- a/src/module/notification/notification.listener.ts +++ b/src/module/notification/notification.listener.ts @@ -1,12 +1,12 @@ import { Injectable } from '@nestjs/common'; import { OnEvent } from '@nestjs/event-emitter'; -import type * as Event from './types/event'; import { getImageUrl } from 'src/shared/util/get-image-url'; import { PrismaService } from 'src/database/prisma/prisma.service'; import { kyselyUuid } from 'src/shared/util/convert-uuid'; import { GlobalGateway } from '../websocket/global.gateway'; import { RedisService } from 'src/database/redis/redis.service'; import { PushService } from '../push/push.service'; +import { EventPayloadMap } from 'src/infrastructure/event/event-payload.types'; function getProfileLink(url: string) { return `${process.env.SERVICE_URL}/${url}`; @@ -30,7 +30,10 @@ export class NotificationListener { ) {} @OnEvent('notification:FOLLOW') - async handleFollowEvent({ actorId, userId }: Event.FollowEvent) { + async handleFollowEvent({ + actorId, + userId, + }: EventPayloadMap['notification:FOLLOW']) { const user = await this.prisma.user.findUnique({ where: { id: userId }, select: { subscription: true }, @@ -82,7 +85,10 @@ export class NotificationListener { } @OnEvent('notification:FEED_LIKE') - async handleFeedLikeEvent({ feedId, likeCount }: Event.FeedLikeEvent) { + async handleFeedLikeEvent({ + feedId, + likeCount, + }: EventPayloadMap['notification:FEED_LIKE']) { const [result] = await this.prisma.$kysely .selectFrom('Feed') .innerJoin('User', 'Feed.authorId', 'User.id') @@ -135,7 +141,10 @@ export class NotificationListener { } @OnEvent('notification:FEED_COMMENT') - async handleFeedComment({ feedId, actorId }: Event.FeedCommentEvent) { + async handleFeedComment({ + feedId, + actorId, + }: EventPayloadMap['notification:FEED_COMMENT']) { const [result] = await this.prisma.$kysely .selectFrom('Feed') .innerJoin('User', 'Feed.authorId', 'User.id') @@ -198,7 +207,11 @@ export class NotificationListener { } @OnEvent('notification:FEED_REPLY') - async handleFeedReply({ feedId, actorId, parentId }: Event.FeedReplyEvent) { + async handleFeedReply({ + feedId, + actorId, + parentId, + }: EventPayloadMap['notification:FEED_REPLY']) { const [result] = await this.prisma.$kysely .selectFrom('FeedComment') .innerJoin('User', 'FeedComment.writerId', 'User.id') @@ -266,7 +279,7 @@ export class NotificationListener { feedId, actorId, mentionedUserId, - }: Event.FeedMentionEvent) { + }: EventPayloadMap['notification:FEED_MENTION']) { const mentionedUser = await this.prisma.user.findUnique({ where: { id: mentionedUserId }, select: { id: true, subscription: true }, @@ -324,7 +337,10 @@ export class NotificationListener { } @OnEvent('notification:POST_COMMENT') - async handlePostComment({ postId, actorId }: Event.PostCommentEvent) { + async handlePostComment({ + postId, + actorId, + }: EventPayloadMap['notification:POST_COMMENT']) { const [author] = await this.prisma.$kysely .selectFrom('Post') .innerJoin('User', 'Post.authorId', 'User.id') @@ -388,7 +404,11 @@ export class NotificationListener { } @OnEvent('notification:POST_REPLY') - async handlePostReply({ postId, actorId, parentId }: Event.PostReplyEvent) { + async handlePostReply({ + postId, + actorId, + parentId, + }: EventPayloadMap['notification:POST_REPLY']) { const [result] = await this.prisma.$kysely .selectFrom('PostComment') .innerJoin('User', 'PostComment.writerId', 'User.id') @@ -461,7 +481,7 @@ export class NotificationListener { postId, actorId, mentionedUserId, - }: Event.PostMentionEvent) { + }: EventPayloadMap['notification:POST_MENTION']) { if (actorId === mentionedUserId) return; const mentionedUser = await this.prisma.user.findUnique({ diff --git a/src/module/notification/types/event.d.ts b/src/module/notification/types/event.d.ts deleted file mode 100644 index c511ba87..00000000 --- a/src/module/notification/types/event.d.ts +++ /dev/null @@ -1,43 +0,0 @@ -export interface FollowEvent { - actorId: string; - userId: string; -} - -export interface FeedLikeEvent { - feedId: string; - likeCount: number; -} - -export interface FeedCommentEvent { - feedId: string; - actorId: string; -} - -export interface FeedReplyEvent { - feedId: string; - actorId: string; - parentId: string; -} - -export interface FeedMentionEvent { - feedId: string; - actorId: string; - mentionedUserId: string; -} - -export interface PostCommentEvent { - postId: string; - actorId: string; -} - -export interface PostReplyEvent { - postId: string; - actorId: string; - parentId: string; -} - -export interface PostMentionEvent { - postId: string; - actorId: string; - mentionedUserId: string; -} diff --git a/src/module/post-comment/post-comment.service.ts b/src/module/post-comment/post-comment.service.ts index 6f9a810a..7c3124cb 100644 --- a/src/module/post-comment/post-comment.service.ts +++ b/src/module/post-comment/post-comment.service.ts @@ -1,7 +1,7 @@ import { Injectable, HttpException } from '@nestjs/common'; import { PostCommentReader } from './repository/post-comment.reader'; import { PostCommentWriter } from './repository/post-comment.writer'; -import { EventEmitter2 } from '@nestjs/event-emitter'; +import { TypedEventEmitter } from 'src/infrastructure/event/typed-event-emitter'; import { Transactional } from '@nestjs-cls/transactional'; @Injectable() @@ -9,7 +9,7 @@ export class PostCommentService { constructor( private readonly postCommentReader: PostCommentReader, private readonly postCommentWriter: PostCommentWriter, - private eventEmitter: EventEmitter2, + private eventEmitter: TypedEventEmitter, ) {} async create(input: CreateInput) { diff --git a/src/module/push/push.service.ts b/src/module/push/push.service.ts index c091762e..1b2ddf0e 100644 --- a/src/module/push/push.service.ts +++ b/src/module/push/push.service.ts @@ -3,6 +3,7 @@ import { PushRepository } from './push.repository'; import * as admin from 'firebase-admin'; import { OnEvent } from '@nestjs/event-emitter'; import { ConfigService } from '@nestjs/config'; +import { EventPayloadMap } from 'src/infrastructure/event/event-payload.types'; @Injectable() export class PushService implements OnModuleInit { @@ -37,7 +38,7 @@ export class PushService implements OnModuleInit { } @OnEvent('push') - async pushNotification({ userId, ...data }: PushPayload) { + async pushNotification({ userId, ...data }: EventPayloadMap['push']) { if (!this.app) { console.error('Firebase Admin SDK is not initialized'); return; diff --git a/src/module/user/user.service.ts b/src/module/user/user.service.ts index c2fbd1de..c5009abb 100644 --- a/src/module/user/user.service.ts +++ b/src/module/user/user.service.ts @@ -9,7 +9,7 @@ import { RedisService } from 'src/database/redis/redis.service'; import { getImageUrl } from 'src/shared/util/get-image-url'; import { removeHtml } from 'src/shared/util/remove-html'; import { AlbumReader } from '../album/repository/album.reader'; -import { EventEmitter2 } from '@nestjs/event-emitter'; +import { TypedEventEmitter } from 'src/infrastructure/event/typed-event-emitter'; import { Transactional } from '@nestjs-cls/transactional'; const linkSeparator = '|~|'; @@ -24,7 +24,7 @@ export class UserService { private postReader: PostReader, private redisService: RedisService, private albumReader: AlbumReader, - private eventEmitter: EventEmitter2, + private eventEmitter: TypedEventEmitter, ) {} async updateProfileImage(userId: string, imageName: string | null) { @@ -107,7 +107,6 @@ export class UserService { if (targetUser.subscription.includes('FOLLOW')) { this.eventEmitter.emit('notification:FOLLOW', { - type: 'FOLLOW', actorId: userId, userId: targetUserId, }); diff --git a/src/shared/types/push-payload.d.ts b/src/shared/types/push-payload.d.ts deleted file mode 100644 index 81d1b0c4..00000000 --- a/src/shared/types/push-payload.d.ts +++ /dev/null @@ -1,10 +0,0 @@ -interface PushPayload { - userId: string; - title: string; - text: string; - imageUrl?: string | null; - data?: Record; - silent?: boolean; - key?: string; - badge?: number; -} diff --git a/test/e2e/chat-message/get-chat-messages.e2e-spec.ts b/test/e2e/chat-message/get-chat-messages.e2e-spec.ts index 96a4273a..24de8367 100644 --- a/test/e2e/chat-message/get-chat-messages.e2e-spec.ts +++ b/test/e2e/chat-message/get-chat-messages.e2e-spec.ts @@ -272,6 +272,7 @@ describe('GET /chat-messages?chatId - 채팅방 별 메세지 조회', () => { chatId: chat.id, userId: me.id, content: `test`, + createdAt: new Date(Date.now() - 1000), }, });