diff --git a/.prettierrc b/.prettierrc index dcb7279..982416f 100644 --- a/.prettierrc +++ b/.prettierrc @@ -1,4 +1,10 @@ { + "printWidth": 1000, + "tabWidth": 2, + "useTabs": false, + "semi": true, "singleQuote": true, - "trailingComma": "all" + "trailingComma": "all", + "bracketSpacing": true, + "arrowParens": "always" } \ No newline at end of file diff --git a/src/app.module.ts b/src/app.module.ts index 344c5ea..051a753 100644 --- a/src/app.module.ts +++ b/src/app.module.ts @@ -1,9 +1,4 @@ -import { - MiddlewareConsumer, - Module, - NestModule, - RequestMethod, -} from '@nestjs/common'; +import { MiddlewareConsumer, Module, NestModule, RequestMethod } from '@nestjs/common'; import { GatewayModule } from './gateways/gateway.module'; import { RateMiddleware } from 'middlewares/rate-limit'; import { ChatSocketGateway } from './sockets/chat.gateway'; @@ -13,17 +8,10 @@ import { ChatMessageRoutingService } from './handlers/chat.handler'; @Module({ imports: [GatewayModule], - providers: [ - ChatSocketGateway, - ChatSocketService, - ChatConsumerService, - ChatMessageRoutingService, - ], + providers: [ChatSocketGateway, ChatSocketService, ChatConsumerService, ChatMessageRoutingService], }) export class AppModule implements NestModule { configure(consumer: MiddlewareConsumer) { - consumer - .apply(RateMiddleware) - .forRoutes({ path: '*', method: RequestMethod.ALL }); + consumer.apply(RateMiddleware).forRoutes({ path: '*', method: RequestMethod.ALL }); } } diff --git a/src/consumers/chat.consumer.ts b/src/consumers/chat.consumer.ts index e0e0aba..a7d50a8 100644 --- a/src/consumers/chat.consumer.ts +++ b/src/consumers/chat.consumer.ts @@ -17,9 +17,7 @@ export class ChatConsumerService { }, ); - constructor( - private readonly chatMessageRoutingService: ChatMessageRoutingService, - ) { + constructor(private readonly chatMessageRoutingService: ChatMessageRoutingService) { this.init(); } @@ -34,14 +32,14 @@ export class ChatConsumerService { this.consumer.consume(config.EVENT_STORE_SETTINGS.poolOptions.max); }, 1000); }) - .on('data', data => { + .on('data', (data) => { this.chatMessageRoutingService.register(data); this.consumer.commit(); }) - .on('event.error', err => { + .on('event.error', (err) => { this.logger.error(err.message, '', 'Event_Error'); }) - .on('rebalance.error', err => { + .on('rebalance.error', (err) => { this.logger.error(err.message, '', 'Reblanace_Error'); }); diff --git a/src/gateways/gateway.controller.ts b/src/gateways/gateway.controller.ts index 4cac1a5..58ee92e 100644 --- a/src/gateways/gateway.controller.ts +++ b/src/gateways/gateway.controller.ts @@ -1,15 +1,4 @@ -import { - Controller, - Request, - Get, - UsePipes, - ValidationPipe, - HttpException, - Post, - Put, - Delete, - UseInterceptors, -} from '@nestjs/common'; +import { Controller, Request, Get, UsePipes, ValidationPipe, HttpException, Post, Put, Delete, UseInterceptors } from '@nestjs/common'; import { GatewayService } from './gateway.service'; import * as Express from 'express'; import { FileInterceptor, FilesInterceptor } from '@nestjs/platform-express'; @@ -21,9 +10,7 @@ export class GatewayController { @Get() @UsePipes(ValidationPipe) - getRequest( - @Request() req: Express.Request, - ): Promise { + getRequest(@Request() req: Express.Request): Promise { return this.gatewayService.getRequest(req); } @@ -34,25 +21,19 @@ export class GatewayController { fileFilter: isImageFilter, }), ) - postRequest( - @Request() req: Express.Request, - ): Promise { + postRequest(@Request() req: Express.Request): Promise { return this.gatewayService.postRequest(req); } @Put() @UsePipes(ValidationPipe) - putRequest( - @Request() req: Express.Request, - ): Promise { + putRequest(@Request() req: Express.Request): Promise { return this.gatewayService.putRequest(req); } @Delete() @UsePipes(ValidationPipe) - delRequest( - @Request() req: Express.Request, - ): Promise { + delRequest(@Request() req: Express.Request): Promise { return this.gatewayService.delRequest(req); } } diff --git a/src/gateways/gateway.service.ts b/src/gateways/gateway.service.ts index 7a4858d..cb9f106 100644 --- a/src/gateways/gateway.service.ts +++ b/src/gateways/gateway.service.ts @@ -72,9 +72,7 @@ export class GatewayService { ); // get current server - const service: IGateway.IServerConf | undefined = config.MS_SETTINGS.find( - setting => setting.name === serviceName, - ); + const service: IGateway.IServerConf | undefined = config.MS_SETTINGS.find((setting) => setting.name === serviceName); // check if service is exist or not if (typeof service !== 'object') @@ -87,10 +85,7 @@ export class GatewayService { ); // replace req.url to endpoint - const endpoint: string = req.url.replace( - `/${config.PREFIX}${config.API_EXPLORER_PATH}`, - '', - ); + const endpoint: string = req.url.replace(`/${config.PREFIX}${config.API_EXPLORER_PATH}`, ''); try { return await APIRequestFactory.createRequest('standard').makeRequest({ @@ -133,9 +128,7 @@ export class GatewayService { ); // get current server - const service: IGateway.IServerConf | undefined = config.MS_SETTINGS.find( - setting => setting.name === serviceName, - ); + const service: IGateway.IServerConf | undefined = config.MS_SETTINGS.find((setting) => setting.name === serviceName); // check if service is exist or not if (typeof service !== 'object') @@ -148,10 +141,7 @@ export class GatewayService { ); // replace req.url to endpoint - const endpoint: string = req.url.replace( - `/${config.PREFIX}${config.API_EXPLORER_PATH}`, - '', - ); + const endpoint: string = req.url.replace(`/${config.PREFIX}${config.API_EXPLORER_PATH}`, ''); try { if (req.headers['content-type'].includes('multipart/form-data')) { @@ -217,9 +207,7 @@ export class GatewayService { ); // get current server - const service: IGateway.IServerConf | undefined = config.MS_SETTINGS.find( - setting => setting.name === serviceName, - ); + const service: IGateway.IServerConf | undefined = config.MS_SETTINGS.find((setting) => setting.name === serviceName); // check if service is exist or not if (typeof service !== 'object') @@ -232,10 +220,7 @@ export class GatewayService { ); // replace req.url to endpoint - const endpoint: string = req.url.replace( - `/${config.PREFIX}${config.API_EXPLORER_PATH}`, - '', - ); + const endpoint: string = req.url.replace(`/${config.PREFIX}${config.API_EXPLORER_PATH}`, ''); try { return await APIRequestFactory.createRequest('standard').makeRequest({ @@ -286,9 +271,7 @@ export class GatewayService { ); // get current server - const service: IGateway.IServerConf | undefined = config.MS_SETTINGS.find( - setting => setting.name === serviceName, - ); + const service: IGateway.IServerConf | undefined = config.MS_SETTINGS.find((setting) => setting.name === serviceName); // check if service is exist or not if (typeof service !== 'object') @@ -301,10 +284,7 @@ export class GatewayService { ); // replace req.url to endpoint - const endpoint: string = req.url.replace( - `/${config.PREFIX}${config.API_EXPLORER_PATH}`, - '', - ); + const endpoint: string = req.url.replace(`/${config.PREFIX}${config.API_EXPLORER_PATH}`, ''); try { return await APIRequestFactory.createRequest('standard').makeRequest({ diff --git a/src/handlers/chat.handler.ts b/src/handlers/chat.handler.ts index 2afdd47..3371b69 100644 --- a/src/handlers/chat.handler.ts +++ b/src/handlers/chat.handler.ts @@ -1,8 +1,4 @@ -import { - Injectable, - InternalServerErrorException, - Logger, -} from '@nestjs/common'; +import { Injectable, InternalServerErrorException, Logger } from '@nestjs/common'; import Kafka from 'node-rdkafka'; import { ChatSocketService } from '../sockets/chat.service'; import * as EChatRoom from '../sockets/enums'; @@ -14,28 +10,30 @@ export class ChatMessageRoutingService { constructor(private readonly chatSocketService: ChatSocketService) {} - public register(kafkaMessage: Kafka.Message) { - if (!kafkaMessage) - throw new InternalServerErrorException('Non message is being proecssed'); - const event: IChatRoom.IAggregateResponse< - EChatRoom.EChatRoomSocketEvent, - IChatRoom.IEventData - > = JSON.parse(kafkaMessage.value.toString()); + /** + * @description Register topic event + * @public + * @param {Kafka.Message} kafkaMessage + * @returns {void} + */ + public register(kafkaMessage: Kafka.Message): void { + if (!kafkaMessage) throw new InternalServerErrorException('Non message is being proecssed'); + const event: IChatRoom.IAggregateResponse = JSON.parse(kafkaMessage.value.toString()); return this.handler(event); } - protected handler( - event: IChatRoom.IAggregateResponse< - EChatRoom.EChatRoomSocketEvent, - IChatRoom.IEventData - >, - ) { + /** + * @description Handle message delivery + * @private + * @param {IChatRoom.IAggregateResponse} event + * @returns {void} + */ + private handler(event: IChatRoom.IAggregateResponse) { switch (event.type) { case EChatRoom.EChatRoomSocketEvent.CREATECHATROOM: - return this.chatSocketService.sendNewChatRoom( - event.type, - event.data as IChatRoom.IChatRoomEntity, - ); + return this.chatSocketService.sendNewChatRoom(event.type, event.data as IChatRoom.IChatRoomEntity); + case EChatRoom.EChatRoomSocketEvent.NEWCHATMESSAGE: + return this.chatSocketService.sendNewChatMessage(event.type, event.data as IChatRoom.IChatEntity); } } } diff --git a/src/libs/http.ts b/src/libs/http.ts index 5b0c740..f7e4cc5 100644 --- a/src/libs/http.ts +++ b/src/libs/http.ts @@ -9,8 +9,8 @@ export class StandardRequest { return new Promise((resolve, reject) => { httpRequest .default(options) - .then(res => resolve(res)) - .catch(err => reject(err)); + .then((res) => resolve(res)) + .catch((err) => reject(err)); }); } } diff --git a/src/libs/utils.ts b/src/libs/utils.ts index 41c004d..6b2dbfd 100644 --- a/src/libs/utils.ts +++ b/src/libs/utils.ts @@ -9,13 +9,7 @@ import * as IGateway from '../gateways/interfaces'; * @returns {void} */ export function memInfo(memName: string): void { - Logger.log( - `Function ${memName} used memory: ${Math.round( - (process.memoryUsage().heapUsed / 1024 / 1024) * 100, - ) / 100} MB`, - 'Memory-Info', - true, - ); + Logger.log(`Function ${memName} used memory: ${Math.round((process.memoryUsage().heapUsed / 1024 / 1024) * 100) / 100} MB`, 'Memory-Info', true); } /** @@ -64,8 +58,7 @@ export function isEmptyObj(obj: { [key: string]: any }): boolean { } export function isImageFilter(req, file, cb) { - if (!file.originalname.match(/\.(jpg|jpeg|png|gif)$/)) - return cb(new Error('Not Allowed File'), false); + if (!file.originalname.match(/\.(jpg|jpeg|png|gif)$/)) return cb(new Error('Not Allowed File'), false); cb(null, true); } @@ -79,9 +72,7 @@ export function editFileName(req, file, cb) { cb(null, `${name}-${randomName}${fileExtName}`); } -export function formatErrorMessage( - errorMsg: string, -): IGateway.IErrorStruct | null { +export function formatErrorMessage(errorMsg: string): IGateway.IErrorStruct | null { const errorMsgStr: string = errorMsg.split('-')[1]; if (!errorMsgStr) return null; errorMsgStr.replace(/\/\n/gi, ''); diff --git a/src/main.ts b/src/main.ts index 536e7a5..c1647f7 100644 --- a/src/main.ts +++ b/src/main.ts @@ -26,10 +26,6 @@ async function bootstrap() { }); app.startAllMicroservices(); await app.listen(config.PORT); - Logger.log( - `Server start on ${config.HOST}:${config.PORT}`, - 'Bootstrap', - true, - ); + Logger.log(`Server start on ${config.HOST}:${config.PORT}`, 'Bootstrap', true); } bootstrap(); diff --git a/src/middlewares/auth.service.ts b/src/middlewares/auth.service.ts index 6688155..4f8df76 100644 --- a/src/middlewares/auth.service.ts +++ b/src/middlewares/auth.service.ts @@ -1,11 +1,4 @@ -import { - HttpException, - HttpStatus, - Injectable, - Logger, - NestMiddleware, - UnauthorizedException, -} from '@nestjs/common'; +import { HttpException, HttpStatus, Injectable, Logger, NestMiddleware, UnauthorizedException } from '@nestjs/common'; import { Request, Response, NextFunction } from 'express'; import { config } from '../../config'; import { APIRequestFactory } from '../libs/request-factory'; @@ -21,11 +14,7 @@ export class AuthService implements NestMiddleware { * @param {NextFunction} next * @returns {Promise} */ - public async use( - req: Request, - res: Response, - next: NextFunction, - ): Promise { + public async use(req: Request, res: Response, next: NextFunction): Promise { // check if routes is exception or not if (this.exceptRoutes(req.baseUrl)) return next(); // check token diff --git a/src/middlewares/rate-limit.ts b/src/middlewares/rate-limit.ts index d05e6a9..d3747a6 100644 --- a/src/middlewares/rate-limit.ts +++ b/src/middlewares/rate-limit.ts @@ -47,9 +47,7 @@ export class RateMiddleware implements NestMiddleware { // get current counter const request_count_per_minutes = data.filter((item: IRateLimit) => { - const diff_time = - new Date(current_time).getTime() - - new Date(item.request_time).getTime(); + const diff_time = new Date(current_time).getTime() - new Date(item.request_time).getTime(); if (diff_time >= 60 * 1000) { item.request_time = new Date(current_time); item.counter = 0; @@ -59,22 +57,20 @@ export class RateMiddleware implements NestMiddleware { // data handling to increment threshold let threshold = 0; - request_count_per_minutes.forEach(item => { + request_count_per_minutes.forEach((item) => { threshold += item.counter; }); // rate exception if (threshold >= 100) { Logger.log(token, 'REDIS-RATE-LIMIT-ECEED', true); - return res - .status(429) - .json({ status: 'error', message: 'Throttle Limit Exceeded' }); + return res.status(429).json({ status: 'error', message: 'Throttle Limit Exceeded' }); } let is_found = false; // incrementation - data.forEach(element => { + data.forEach((element) => { if (element.request_time) { is_found = true; element.counter++; diff --git a/src/sockets/chat.gateway.ts b/src/sockets/chat.gateway.ts index a452c22..ea7d814 100644 --- a/src/sockets/chat.gateway.ts +++ b/src/sockets/chat.gateway.ts @@ -23,6 +23,7 @@ export class ChatSocketGateway { this.logger.log('Messaging is on'); }); this.logger.log('Connecting ws success'); + // later add verification here const qs: url.UrlWithParsedQuery = url.parse(req.url, true); ws['uid'] = qs.query.userIds; }); diff --git a/src/sockets/chat.service.ts b/src/sockets/chat.service.ts index dff9341..74fce13 100644 --- a/src/sockets/chat.service.ts +++ b/src/sockets/chat.service.ts @@ -11,17 +11,14 @@ export class ChatSocketService { /** * @description Verify Identity - * @public + * @private * @param {IChatRoom.ISocketWithIdentity} client * @param {IChatRoom.IChatRoomEntity} chatRoom * @returns {boolean} */ - protected isRightClient( - client: IChatRoom.ISocketWithIdentity, - chatRoom: IChatRoom.IChatRoomEntity, - ): boolean { + private isRightClient(client: IChatRoom.ISocketWithIdentity, chatRoom: IChatRoom.IChatRoomEntity | IChatRoom.IChatEntity): boolean { let isClient = false; - chatRoom.participateId.userIds.forEach(user => { + chatRoom.chatParticipate.users.forEach((user) => { if (user.id === client.uid) { isClient = true; } @@ -29,6 +26,22 @@ export class ChatSocketService { return isClient; } + /** + * @description Send event handler + * @private + * @param {IChatRoom.ISocketWithIdentity} client + * @param {T} type + * @param {K} data + * @returns {void} + */ + private sendEvent(client: IChatRoom.ISocketWithIdentity, type: T, data: K): void { + client.send(JSON.stringify({ type, data }), (err) => { + if (err) { + this.logger.error(err.message, '', `${type}SendError`); + } + }); + } + /** * @description send new chat room * @public @@ -36,21 +49,28 @@ export class ChatSocketService { * @param {IChatRoom.IChatRoomEntity} chatRoomEvent * @returns {void} */ - public sendNewChatRoom( - type: EChatRoom.EChatRoomSocketEvent, - chatRoomEvent: IChatRoom.IChatRoomEntity, - ): void { - this.chatSocketGateway.wss.clients.forEach( - (client: IChatRoom.ISocketWithIdentity) => { - const isClient = this.isRightClient(client, chatRoomEvent); - if (isClient) { - client.send(JSON.stringify({ type, data: chatRoomEvent }), err => { - if (err) { - this.logger.error(err.message, '', 'SendNewChatRoom'); - } - }); - } - }, - ); + public sendNewChatRoom(type: EChatRoom.EChatRoomSocketEvent, chatRoomEvent: IChatRoom.IChatRoomEntity): void { + this.chatSocketGateway.wss.clients.forEach((client: IChatRoom.ISocketWithIdentity) => { + const isClient = this.isRightClient(client, chatRoomEvent); + if (isClient) { + this.sendEvent(client, type, chatRoomEvent); + } + }); + } + + /** + * @description send new chat message + * @public + * @param {EChatRoom.EChatRoomSocketEvent} type + * @param {IChatRoom.IChatRoomEntity} msgEvent + * @returns {void} + */ + public sendNewChatMessage(type: EChatRoom.EChatRoomSocketEvent, msgEvent: IChatRoom.IChatEntity): void { + this.chatSocketGateway.wss.clients.forEach((client: IChatRoom.ISocketWithIdentity) => { + const isClient = this.isRightClient(client, msgEvent); + if (isClient) { + this.sendEvent(client, type, msgEvent); + } + }); } } diff --git a/src/sockets/enums/chat-room.enum.ts b/src/sockets/enums/chat-room.enum.ts index 9acdd51..c6d834b 100644 --- a/src/sockets/enums/chat-room.enum.ts +++ b/src/sockets/enums/chat-room.enum.ts @@ -5,7 +5,13 @@ export enum EChatRoomType { } export enum EChatRoomSocketEvent { + // chatroom settings 'CREATECHATROOM' = 'createchatroom', 'UPDATECHATROOM' = 'updatechatroom', 'DELETECHATROOM' = 'deletechatroom', + // participate + 'UPDATEPARTICIPATE' = 'updateparticipate', + 'DELETEPARTICIPATE' = 'deleteparticipate', + // message + 'NEWCHATMESSAGE' = 'newchatmessage', } diff --git a/src/sockets/interfaces/chat.interface.ts b/src/sockets/interfaces/chat.interface.ts index 45ddf45..7bab0eb 100644 --- a/src/sockets/interfaces/chat.interface.ts +++ b/src/sockets/interfaces/chat.interface.ts @@ -19,7 +19,7 @@ export interface IUserEntity extends IBaseTimeArea { age?: number; desc?: string; profileImage?: string; - chatParticipateIds: IChatParticipateEntity[]; + chatParticipates: IChatParticipateEntity[]; followers: IUserEntity[]; followings: IUserEntity[]; blockLists: IUserEntity[]; @@ -30,29 +30,27 @@ export interface IUserEntity extends IBaseTimeArea { export interface IChatEntity extends IBaseTimeArea { id: string; message: string; - chatParticipateId: IChatParticipateEntity; + sendStatus: 'fail' | 'sending' | 'finish'; + readStatus: 'read' | 'unread'; + chatParticipate: IChatParticipateEntity; } export interface IChatParticipateEntity extends IBaseTimeArea { id: string; - chatRoomId: IChatRoomEntity; - messageIds: IChatEntity[]; - userIds: IUserEntity[]; + chatRoom: IChatRoomEntity; + chats: IChatEntity[]; + users: IUserEntity[]; } export interface IChatRoomEntity extends IBaseTimeArea { id: string; name: string; type: EChatRoom.EChatRoomType; - participateId: IChatParticipateEntity; + chatParticipate: IChatParticipateEntity; } export interface ISocketWithIdentity extends WebSocket { uid: string; } -export type IEventData = - | IChatEntity - | IChatParticipateEntity - | IChatRoomEntity - | ISocketWithIdentity; +export type IEventData = IChatEntity | IChatParticipateEntity | IChatRoomEntity | ISocketWithIdentity;