diff --git a/package.json b/package.json index bf3b4361..89d27c43 100644 --- a/package.json +++ b/package.json @@ -4,10 +4,10 @@ "description": "NestJS + MikroORM blog example with batteries included", "license": "MIT", "author": { - "name": "Rubin Bhandari", - "email": "roobin.bhandari@gmail.com", - "url": "https://rubiin.ml" - }, + "name": "Rubin Bhandari", + "email": "roobin.bhandari@gmail.com", + "url": "https://rubiin.ml" + }, "repository": "https://github.com/rubiin/ultimate-nest", "homepage": "https://github.com/rubiin/ultimate-nest#readme", "bugs": "https://github.com/rubiin/ultimate-nest/issues", @@ -28,10 +28,10 @@ "jest", "casl", "nestjs-boilerplate", - "nestjs-mikroorm", - "nestjs-starter-template", - "nestjs-template" -], + "nestjs-mikroorm", + "nestjs-starter-template", + "nestjs-template" + ], "scripts": { "prebuild": "rimraf dist", "build": "nest build", @@ -78,6 +78,7 @@ "@sentry/hub": "^7.30.0", "@sentry/node": "^7.30.0", "@sentry/types": "^7.30.0", + "@socket.io/redis-adapter": "^8.0.1", "@supercharge/request-ip": "^1.2.0", "argon2": "^0.30.3", "cache-manager": "5.1.4", @@ -110,9 +111,11 @@ "preview-email": "^3.0.7", "prom-client": "^14.1.1", "pug": "^3.0.2", + "redis": "^4.5.1", "reflect-metadata": "0.1.13", "rxjs": "^7.8.0", "sharp": "^0.31.3", + "socket.io": "^4.5.4", "swagger-stats": "^0.99.5", "twilio": "3.84.1", "unprofane": "^1.0.3", @@ -222,8 +225,8 @@ } }, "pnpm": { - "overrides": { - "jsonwebtoken": "^9.0.0" - } - } + "overrides": { + "jsonwebtoken": "^9.0.0" + } + } } diff --git a/pnpm-lock.yaml b/pnpm-lock.yaml index 1fcc8465..6074e9d6 100644 --- a/pnpm-lock.yaml +++ b/pnpm-lock.yaml @@ -40,6 +40,7 @@ specifiers: '@sentry/node': ^7.30.0 '@sentry/types': ^7.30.0 '@side/jest-runtime': ^1.1.0 + '@socket.io/redis-adapter': ^8.0.1 '@supercharge/request-ip': ^1.2.0 '@swc/core': ^1.3.26 '@swc/jest': ^0.2.24 @@ -104,11 +105,13 @@ specifiers: preview-email: ^3.0.7 prom-client: ^14.1.1 pug: ^3.0.2 + redis: ^4.5.1 reflect-metadata: 0.1.13 run-script-webpack-plugin: ^0.1.1 rxjs: ^7.8.0 sample-env: ^1.0.3 sharp: ^0.31.3 + socket.io: ^4.5.4 supertest: 6.3.3 swagger-stats: ^0.99.5 ts-loader: 9.4.2 @@ -149,6 +152,7 @@ dependencies: '@sentry/hub': 7.30.0 '@sentry/node': 7.30.0 '@sentry/types': 7.30.0 + '@socket.io/redis-adapter': 8.0.1 '@supercharge/request-ip': 1.2.0 argon2: 0.30.3 cache-manager: 5.1.4 @@ -181,9 +185,11 @@ dependencies: preview-email: 3.0.7 prom-client: 14.1.1 pug: 3.0.2 + redis: 4.5.1 reflect-metadata: 0.1.13 rxjs: 7.8.0 sharp: 0.31.3 + socket.io: 4.5.4 swagger-stats: 0.99.5_prom-client@14.1.1 twilio: 3.84.1 unprofane: 1.0.3 @@ -3084,6 +3090,18 @@ packages: /@socket.io/component-emitter/3.1.0: resolution: {integrity: sha512-+9jVqKhRSpsc591z5vX+X5Yyw+he/HCB4iQ/RYxw35CEPaY1gnsNE43nf9n9AaYjAQrTiI/mOwKUKdUs9vf7Xg==} + /@socket.io/redis-adapter/8.0.1: + resolution: {integrity: sha512-qjYRaf+Xp/OEkQDqWwEXnCjcuBUtVCrV1loXTIqICF4D+HHAv4T4jgPHUagyzJQ9M/RmJL25GlG15wLjV2O37g==} + engines: {node: '>=10.0.0'} + dependencies: + debug: 4.3.4 + notepack.io: 3.0.1 + socket.io-adapter: 2.4.0 + uid2: 1.0.0 + transitivePeerDependencies: + - supports-color + dev: false + /@supercharge/request-ip/1.2.0: resolution: {integrity: sha512-wlt6JW69MHqLY2M6Sm/jVyCojNRKq2CBvwH0Hbx24SFhDQQGkgEjeKxVutDxHSyrWixFaOSLXC27euzxijhyMQ==} dev: false @@ -9081,6 +9099,10 @@ packages: resolution: {integrity: sha512-6eZs5Ls3WtCisHWp9S2GUy8dqkpGi4BVSz3GaqiE6ezub0512ESztXUwUB6C6IKbQkY2Pnb/mD4WYojCRwcwLA==} engines: {node: '>=0.10.0'} + /notepack.io/3.0.1: + resolution: {integrity: sha512-TKC/8zH5pXIAMVQio2TvVDTtPRX+DJPHDqjRbxogtFiByHyzKmy96RA0JtCQJ+WouyyL4A10xomQzgbUT+1jCg==} + dev: false + /npm-run-path/2.0.2: resolution: {integrity: sha512-lJxZYlT4DW/bRUtFh1MQIWqmLwQfAxnqWG4HhEdjMlkrJYnJn0Jrr2u3mgxqaWsdiBc76TYkTG/mhrnYTuzfHw==} engines: {node: '>=4'} @@ -10652,6 +10674,22 @@ packages: - supports-color - utf-8-validate + /socket.io/4.5.4: + resolution: {integrity: sha512-m3GC94iK9MfIEeIBfbhJs5BqFibMtkRk8ZpKwG2QwxV0m/eEhPIV4ara6XCF1LWNAus7z58RodiZlAH71U3EhQ==} + engines: {node: '>=10.0.0'} + dependencies: + accepts: 1.3.8 + base64id: 2.0.0 + debug: 4.3.4 + engine.io: 6.2.1 + socket.io-adapter: 2.4.0 + socket.io-parser: 4.2.1 + transitivePeerDependencies: + - bufferutil + - supports-color + - utf-8-validate + dev: false + /socks-proxy-agent/5.0.1: resolution: {integrity: sha512-vZdmnjb9a2Tz6WEQVIurybSwElwPxMZaIc7PzqbJTrezcKNznv6giT7J7tZDZ1BojVaa1jvO/UiUdhDVB0ACoQ==} engines: {node: '>= 6'} @@ -11455,6 +11493,11 @@ packages: resolution: {integrity: sha512-IevTus0SbGwQzYh3+fRsAMTVVPOoIVufzacXcHPmdlle1jUpq7BRL+mw3dgeLanvGZdwwbWhRV6XrcFNdBmjWA==} dev: false + /uid2/1.0.0: + resolution: {integrity: sha512-+I6aJUv63YAcY9n4mQreLUt0d4lvwkkopDNmpomkAUz0fAkEMV9pRWxN0EjhW1YfRhcuyHg2v3mwddCDW1+LFQ==} + engines: {node: '>= 4.0.0'} + dev: false + /umzug/3.2.1: resolution: {integrity: sha512-XyWQowvP9CKZycKc/Zg9SYWrAWX/gJCE799AUTFqk8yC3tp44K1xWr3LoFF0MNEjClKOo1suCr5ASnoy+KltdA==} engines: {node: '>=12'} diff --git a/src/main.ts b/src/main.ts index 7c6c6f25..c2185b7d 100644 --- a/src/main.ts +++ b/src/main.ts @@ -60,7 +60,15 @@ const bootstrap = async () => { app.useGlobalFilters(new I18nValidationExceptionFilter({ detailedErrors: false })); app.setGlobalPrefix(globalPrefix); - app.useWebSocketAdapter(new SocketIOAdapter(app)); + + // ========================================================= + // configureWebSocket + // ========================================================= + + const redisIoAdapter = new SocketIOAdapter(app, configService); + + await redisIoAdapter.connectToRedis(); + app.useWebSocketAdapter(redisIoAdapter); // ========================================================= // configureNestSwagger diff --git a/src/modules/chat/chat.gateway.ts b/src/modules/chat/chat.gateway.ts index 2a0ab0f1..485e0d45 100644 --- a/src/modules/chat/chat.gateway.ts +++ b/src/modules/chat/chat.gateway.ts @@ -1,3 +1,4 @@ +import { Logger } from "@nestjs/common"; import { ConnectedSocket, MessageBody, @@ -15,9 +16,14 @@ import { CreateChatDto } from "./dto/create-chat.dto"; }) export class ChatGateway { @WebSocketServer() server: Namespace; + private readonly logger = new Logger(ChatGateway.name); constructor(private readonly chatService: ChatService) {} + afterInit(): void { + this.logger.log(`💬 Websocket Gateway initialized.`); + } + @SubscribeMessage("createChat") async create(@MessageBody() createChatDto: CreateChatDto, @ConnectedSocket() client: Socket) { const message = this.chatService.create(createChatDto, client.id); diff --git a/src/socket-io.adapter.ts b/src/socket-io.adapter.ts index f7de0c39..4cd270af 100644 --- a/src/socket-io.adapter.ts +++ b/src/socket-io.adapter.ts @@ -1,19 +1,32 @@ import { INestApplicationContext, Logger } from "@nestjs/common"; +import { ConfigService } from "@nestjs/config"; import { IoAdapter } from "@nestjs/platform-socket.io"; +import { createAdapter } from "@socket.io/redis-adapter"; +import { createClient } from "redis"; import { Server, ServerOptions } from "socket.io"; export class SocketIOAdapter extends IoAdapter { private readonly logger = new Logger(SocketIOAdapter.name); + private adapterConstructor: ReturnType; - constructor(private app: INestApplicationContext) { + constructor(private app: INestApplicationContext, private readonly config: ConfigService) { super(app); } + async connectToRedis(): Promise { + const pubClient = createClient({ url: this.config.get("redis.uri") }); + const subClient = pubClient.duplicate(); + + await Promise.all([pubClient.connect(), subClient.connect()]); + + this.adapterConstructor = createAdapter(pubClient, subClient); + } + createIOServer(port: number, options?: ServerOptions) { - const _clientPort = 8000; + const clientUrl = this.config.get("app.clientUrl"); const cors = { - origin: [`http://localhost:${_clientPort}`], + origin: [clientUrl], }; const optionsWithCORS: ServerOptions = { @@ -23,6 +36,8 @@ export class SocketIOAdapter extends IoAdapter { const server: Server = super.createIOServer(port, optionsWithCORS); + server.adapter(this.adapterConstructor); + return server; } }