From fddc8372978f93254158b3ad240bf8223128272a Mon Sep 17 00:00:00 2001 From: Sokratis Vidros Date: Fri, 19 Apr 2024 00:42:59 +0300 Subject: [PATCH] feat(ws): Mount Socket.io Admin UI This simple dashboard should be able to increase our visibility on WS connection status. --- .cspell.json | 8 ++++- .source | 2 +- apps/ws/package.json | 2 ++ apps/ws/src/.env.development | 4 +++ apps/ws/src/app.module.ts | 11 ++++++ apps/ws/src/socket/ws.gateway.ts | 18 +++++++++- pnpm-lock.yaml | 58 +++++++++++++++++++++++++++++--- 7 files changed, 96 insertions(+), 7 deletions(-) diff --git a/.cspell.json b/.cspell.json index 34067ed1883..01479d16363 100644 --- a/.cspell.json +++ b/.cspell.json @@ -718,6 +718,12 @@ "angular.json", "ng-package.json", "libs/shared/src/types/timezones/timezones.types.ts", - "*.riv" + "*.riv", + "websockets", + ".env", + ".env.development", + ".env.local", + ".env.production", + ".env.test", ] } diff --git a/.source b/.source index 2278736ec10..3390b7da795 160000 --- a/.source +++ b/.source @@ -1 +1 @@ -Subproject commit 2278736ec10d09e5aa5f4c1f5338ab659884573f +Subproject commit 3390b7da79577ed9d5412c82e1079e93556e1282 diff --git a/apps/ws/package.json b/apps/ws/package.json index b07212bc1a0..d6c81e94717 100644 --- a/apps/ws/package.json +++ b/apps/ws/package.json @@ -25,6 +25,7 @@ "@nestjs/jwt": "10.2.0", "@nestjs/platform-express": "^10.2.2", "@nestjs/platform-socket.io": "^10.2.2", + "@nestjs/serve-static": "^4.0.2", "@nestjs/swagger": "^7.1.9", "@nestjs/terminus": "^10.0.1", "@nestjs/websockets": "^10.2.2", @@ -33,6 +34,7 @@ "@novu/shared": "^0.24.1", "@novu/testing": "^0.24.1", "@sentry/node": "^7.30.0", + "@socket.io/admin-ui": "^0.5.1", "@socket.io/redis-adapter": "^7.2.0", "class-transformer": "^0.5.1", "class-validator": "^0.14.0", diff --git a/apps/ws/src/.env.development b/apps/ws/src/.env.development index bc250ca4443..5ccf33ea5d4 100644 --- a/apps/ws/src/.env.development +++ b/apps/ws/src/.env.development @@ -16,3 +16,7 @@ NEW_RELIC_APPLICATION_LOGGING_FORWARDING_ENABLED=true LOGGING_LEVEL=info AUTO_CREATE_INDEXES=true + +SOCKET_IO_ADMIN_USERNAME=admin +# "changeit" encrypted with bcrypt +SOCKET_IO_ADMIN_PASSWORD_HASH=$2b$10$heqvAkYMez.Va6Et2uXInOnkCT6/uQj1brkrbyG3LpopDklcq7ZOS diff --git a/apps/ws/src/app.module.ts b/apps/ws/src/app.module.ts index 1cc7423e305..8f0e7e2cd8b 100644 --- a/apps/ws/src/app.module.ts +++ b/apps/ws/src/app.module.ts @@ -1,6 +1,8 @@ +import { join } from 'path'; import { Module } from '@nestjs/common'; import { RavenInterceptor, RavenModule } from 'nest-raven'; import { APP_INTERCEPTOR } from '@nestjs/core'; +import { ServeStaticModule } from '@nestjs/serve-static'; import { createNestLoggingModuleOptions, LoggerModule, @@ -40,6 +42,15 @@ if (process.env.SENTRY_DSN) { useValue: new RavenInterceptor(), }); } +if (!!process.env.SOCKET_IO_ADMIN_USERNAME && !!process.env.SOCKET_IO_ADMIN_PASSWORD_HASH) { + modules.push( + ServeStaticModule.forRoot({ + rootPath: join(__dirname, '../node_modules/@socket.io/admin-ui/ui/dist'), + serveRoot: '/admin', + exclude: ['/api/(.*)'], + }) + ); +} @Module({ imports: modules, diff --git a/apps/ws/src/socket/ws.gateway.ts b/apps/ws/src/socket/ws.gateway.ts index bbc4121e300..20157c96403 100644 --- a/apps/ws/src/socket/ws.gateway.ts +++ b/apps/ws/src/socket/ws.gateway.ts @@ -3,6 +3,7 @@ import { Server, Socket } from 'socket.io'; import { JwtService } from '@nestjs/jwt'; import { OnGatewayConnection, OnGatewayDisconnect, WebSocketGateway, WebSocketServer } from '@nestjs/websockets'; import { Logger } from '@nestjs/common'; +import { instrument } from '@socket.io/admin-ui'; import { ISubscriberJwt, ObservabilityBackgroundTransactionEnum } from '@novu/shared'; import { IDestroy } from '@novu/application-generic'; @@ -18,7 +19,7 @@ export class WSGateway implements OnGatewayConnection, OnGatewayDisconnect, IDes constructor(private jwtService: JwtService, private subscriberOnlineService: SubscriberOnlineService) {} @WebSocketServer() - server: Server | null; + server: Server; async handleDisconnect(connection: Socket) { Logger.log(`New disconnect received from ${connection.id}`, LOG_CONTEXT); @@ -199,4 +200,19 @@ export class WSGateway implements OnGatewayConnection, OnGatewayDisconnect, IDes this.isShutdown = true; await this.gracefulShutdown(); } + + afterInit() { + if (!!process.env.SOCKET_IO_ADMIN_USERNAME && !!process.env.SOCKET_IO_ADMIN_PASSWORD_HASH) { + // For more information on how to use the admin UI, see https://socket.io/docs/v4/admin-ui/ + instrument(this.server, { + auth: { + type: 'basic', + username: process.env.SOCKET_IO_ADMIN_USERNAME, + password: process.env.SOCKET_IO_ADMIN_PASSWORD_HASH, + }, + mode: process.env.NODE_ENV === 'production' ? 'production' : 'development', + namespaceName: '/admin', + }); + } + } } diff --git a/pnpm-lock.yaml b/pnpm-lock.yaml index c8f904cf086..998e9a6c0a1 100644 --- a/pnpm-lock.yaml +++ b/pnpm-lock.yaml @@ -1554,6 +1554,9 @@ importers: '@nestjs/platform-socket.io': specifier: ^10.2.2 version: 10.2.2(@nestjs/common@10.2.2)(@nestjs/websockets@10.2.2)(rxjs@7.8.1) + '@nestjs/serve-static': + specifier: ^4.0.2 + version: 4.0.2(@nestjs/common@10.2.2)(@nestjs/core@10.2.2) '@nestjs/swagger': specifier: ^7.1.9 version: 7.1.9(@nestjs/common@10.2.2)(@nestjs/core@10.2.2)(class-transformer@0.5.1)(class-validator@0.14.0)(reflect-metadata@0.1.13) @@ -1578,6 +1581,9 @@ importers: '@sentry/node': specifier: ^7.30.0 version: 7.47.0 + '@socket.io/admin-ui': + specifier: ^0.5.1 + version: 0.5.1(socket.io@4.7.2) '@socket.io/redis-adapter': specifier: ^7.2.0 version: 7.2.0 @@ -21265,6 +21271,27 @@ packages: - chokidar dev: true + /@nestjs/serve-static@4.0.2(@nestjs/common@10.2.2)(@nestjs/core@10.2.2): + resolution: {integrity: sha512-cT0vdWN5ar7jDI2NKbhf4LcwJzU4vS5sVpMkVrHuyLcltbrz6JdGi1TfIMMatP2pNiq5Ie/uUdPSFDVaZX/URQ==} + peerDependencies: + '@fastify/static': ^6.5.0 || ^7.0.0 + '@nestjs/common': ^9.0.0 || ^10.0.0 + '@nestjs/core': ^9.0.0 || ^10.0.0 + express: ^4.18.1 + fastify: ^4.7.0 + peerDependenciesMeta: + '@fastify/static': + optional: true + express: + optional: true + fastify: + optional: true + dependencies: + '@nestjs/common': 10.2.2(class-transformer@0.5.1)(class-validator@0.14.0)(reflect-metadata@0.1.13)(rxjs@7.8.1) + '@nestjs/core': 10.2.2(@nestjs/common@10.2.2)(@nestjs/platform-express@10.2.2)(@nestjs/websockets@10.2.2)(reflect-metadata@0.1.13)(rxjs@7.8.1) + path-to-regexp: 0.2.5 + dev: false + /@nestjs/swagger@7.1.9(@nestjs/common@10.2.2)(@nestjs/core@10.2.2)(class-transformer@0.5.1)(class-validator@0.14.0)(reflect-metadata@0.1.13): resolution: {integrity: sha512-mKaNlN6uJpLh8itIF1EHK87j8GXFnwW8ldG4pQPqgeaBC8M6E9vH+GdRhxyRgAPrAkay4dL3lSOQS119tGrEHA==} peerDependencies: @@ -28088,6 +28115,19 @@ packages: tslib: 2.6.2 dev: false + /@socket.io/admin-ui@0.5.1(socket.io@4.7.2): + resolution: {integrity: sha512-1dlGL2FGm6T+uL1e6iDvbo2eCINwvW7iVbjIblwh5kPPRM1SP8lmZrbFZf4QNJ/cqQ+JLcx49eXGM9WAB4TK7w==} + peerDependencies: + socket.io: '>=3.1.0' + dependencies: + '@types/bcryptjs': 2.4.6 + bcryptjs: 2.4.3 + debug: 4.3.4(supports-color@8.1.1) + socket.io: 4.7.2 + transitivePeerDependencies: + - supports-color + dev: false + /@socket.io/component-emitter@3.1.0: resolution: {integrity: sha512-+9jVqKhRSpsc591z5vX+X5Yyw+he/HCB4iQ/RYxw35CEPaY1gnsNE43nf9n9AaYjAQrTiI/mOwKUKdUs9vf7Xg==} @@ -31671,6 +31711,10 @@ packages: resolution: {integrity: sha512-SwBrq5wb6jXP0o3O3jStdPWbKpimTImfdFD/OZE3uW+jhGpds/l5wMX9lfYOTDOa5Bod2QmOgo9ln+tMp2XP/w==} dev: true + /@types/bcryptjs@2.4.6: + resolution: {integrity: sha512-9xlo6R2qDs5uixm0bcIqCeMCE6HiQsIyel9KQySStiyqNl2tnj2mP3DX1Nf56MD6KMenNNlBBsy3LJ7gUEQPXQ==} + dev: false + /@types/bluebird@3.5.38: resolution: {integrity: sha512-yR/Kxc0dd4FfwtEoLZMoqJbM/VE/W7hXn/MIjb+axcwag0iFmSPK7OBUZq1YWLynJUoWQkfUrI7T0HDqGApNSg==} dev: true @@ -32937,7 +32981,7 @@ packages: typescript: optional: true dependencies: - '@eslint-community/regexpp': 4.9.1 + '@eslint-community/regexpp': 4.5.0 '@typescript-eslint/parser': 5.58.0(eslint@8.51.0)(typescript@4.9.5) '@typescript-eslint/scope-manager': 5.58.0 '@typescript-eslint/type-utils': 5.58.0(eslint@8.51.0)(typescript@4.9.5) @@ -36071,6 +36115,10 @@ packages: - supports-color dev: false + /bcryptjs@2.4.3: + resolution: {integrity: sha512-V/Hy/X9Vt7f3BbPJEi8BdVFMByHi+jNXrYkW3huaybV/kQ0KJg0Y6PkEMbn+zeT+i+SiKZ/HMqJGIIt4LZDqNQ==} + dev: false + /before-after-hook@2.2.3: resolution: {integrity: sha512-NzUnlZexiaH/46WDhANlyR2bXRopNg4F/zuSA3OpZnllCUgRaOF2znDioDWrmbNVsuZk6l9pMquQB38cfBZwkQ==} dev: true @@ -45447,7 +45495,7 @@ packages: pretty-format: 27.5.1 slash: 3.0.0 strip-json-comments: 3.1.1 - ts-node: 10.9.1(@types/node@14.18.42)(typescript@4.9.5) + ts-node: 10.9.1(@types/node@16.11.7)(typescript@4.9.5) transitivePeerDependencies: - bufferutil - canvas @@ -47633,8 +47681,6 @@ packages: peerDependenciesMeta: webpack: optional: true - webpack-sources: - optional: true dependencies: webpack: 5.88.2(esbuild@0.18.17) webpack-sources: 3.2.3 @@ -51996,6 +52042,10 @@ packages: /path-to-regexp@0.1.7: resolution: {integrity: sha512-5DFkuoqlv1uYQKxy8omFBeJPQcdoE07Kv2sferDCrAq1ohOU+MSDswDIbnx3YAM60qIOnYa53wBhXW0EbMonrQ==} + /path-to-regexp@0.2.5: + resolution: {integrity: sha512-l6qtdDPIkmAmzEO6egquYDfqQGPMRNGjYtrU13HAXb3YSRrt7HSb1sJY0pKp6o2bAa86tSB6iwaW2JbthPKr7Q==} + dev: false + /path-to-regexp@1.8.0: resolution: {integrity: sha512-n43JRhlUKUAlibEJhPeir1ncUID16QnEjNpwzNdO3Lm4ywrBpBZ5oLD0I6br9evr1Y9JTqwRtAh7JLoOzAQdVA==} dependencies: