From c98be8a767002b9ca53873cd3ca09e8b490204b8 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. --- apps/ws/package.json | 2 + apps/ws/src/.env.development | 3 ++ apps/ws/src/app.module.ts | 11 +++++ apps/ws/src/socket/ws.gateway.ts | 17 +++++++- pnpm-lock.yaml | 75 +++++++++++++++++++++++++++----- 5 files changed, 95 insertions(+), 13 deletions(-) diff --git a/apps/ws/package.json b/apps/ws/package.json index b07212bc1a0b..d6c81e947176 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 bc250ca4443a..7ac395922556 100644 --- a/apps/ws/src/.env.development +++ b/apps/ws/src/.env.development @@ -16,3 +16,6 @@ NEW_RELIC_APPLICATION_LOGGING_FORWARDING_ENABLED=true LOGGING_LEVEL=info AUTO_CREATE_INDEXES=true + +SOCKET_IO_ADMIN_USERNAME=admin +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 1cc7423e305b..8f0e7e2cd8bc 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 bbc4121e3000..0fd81206e6ac 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,18 @@ 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) { + 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 9a73594a5279..c8c12538b7af 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 @@ -21189,6 +21195,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: @@ -28012,6 +28039,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==} @@ -31595,6 +31635,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 @@ -32861,7 +32905,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) @@ -35983,6 +36027,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 @@ -39046,6 +39094,7 @@ packages: dependencies: ms: 2.1.3 supports-color: 5.5.0 + dev: true /debug@3.2.7(supports-color@8.1.1): resolution: {integrity: sha512-CFjzYYAi4ThfiQvizrFQevTTXHtnCqWfe7x1AhgEscTz6ZbLbfoLRLPugTQyBth6f8ZERVUSyWHFD/7Wu4t1XQ==} @@ -40340,7 +40389,7 @@ packages: /eslint-import-resolver-node@0.3.7: resolution: {integrity: sha512-gozW2blMLJCeFpBwugLTGyvVjNoeo1knonXAcatC6bjPBZitotxdWf7Gimr25N4c0AAOo4eOUfaG82IJPDpqCA==} dependencies: - debug: 3.2.7(supports-color@5.5.0) + debug: 3.2.7(supports-color@8.1.1) is-core-module: 2.13.0 resolve: 1.22.2 transitivePeerDependencies: @@ -40355,7 +40404,7 @@ packages: webpack: '>=1.11.0' dependencies: array.prototype.find: 2.2.2 - debug: 3.2.7(supports-color@5.5.0) + debug: 3.2.7(supports-color@8.1.1) enhanced-resolve: 0.9.1 eslint-plugin-import: 2.28.1(@typescript-eslint/parser@5.58.0)(eslint-import-resolver-webpack@0.13.7)(eslint@8.38.0) find-root: 1.1.0 @@ -40393,7 +40442,7 @@ packages: optional: true dependencies: '@typescript-eslint/parser': 5.58.0(eslint@8.38.0)(typescript@4.9.5) - debug: 3.2.7(supports-color@5.5.0) + debug: 3.2.7(supports-color@8.1.1) eslint: 8.38.0 eslint-import-resolver-node: 0.3.7 eslint-import-resolver-webpack: 0.13.7(eslint-plugin-import@2.28.1)(webpack@5.78.0) @@ -40423,7 +40472,7 @@ packages: optional: true dependencies: '@typescript-eslint/parser': 5.58.0(eslint@8.51.0)(typescript@4.9.5) - debug: 3.2.7(supports-color@5.5.0) + debug: 3.2.7(supports-color@8.1.1) eslint: 8.51.0 eslint-import-resolver-node: 0.3.7 eslint-import-resolver-webpack: 0.13.7(eslint-plugin-import@2.28.1)(webpack@5.78.0) @@ -40504,7 +40553,7 @@ packages: array.prototype.findlastindex: 1.2.3 array.prototype.flat: 1.3.1 array.prototype.flatmap: 1.3.1 - debug: 3.2.7(supports-color@5.5.0) + debug: 3.2.7(supports-color@8.1.1) doctrine: 2.1.0 eslint: 8.38.0 eslint-import-resolver-node: 0.3.7 @@ -40539,7 +40588,7 @@ packages: array.prototype.findlastindex: 1.2.3 array.prototype.flat: 1.3.1 array.prototype.flatmap: 1.3.1 - debug: 3.2.7(supports-color@5.5.0) + debug: 3.2.7(supports-color@8.1.1) doctrine: 2.1.0 eslint: 8.51.0 eslint-import-resolver-node: 0.3.7 @@ -45358,7 +45407,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 @@ -47547,8 +47596,6 @@ packages: peerDependenciesMeta: webpack: optional: true - webpack-sources: - optional: true dependencies: webpack: 5.88.2(esbuild@0.18.17) webpack-sources: 3.2.3 @@ -49892,7 +49939,7 @@ packages: hasBin: true requiresBuild: true dependencies: - debug: 3.2.7(supports-color@5.5.0) + debug: 3.2.7(supports-color@8.1.1) iconv-lite: 0.6.3 sax: 1.2.4 transitivePeerDependencies: @@ -51910,6 +51957,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: @@ -52284,7 +52335,7 @@ packages: engines: {node: '>= 0.12.0'} dependencies: async: 2.6.4 - debug: 3.2.7(supports-color@5.5.0) + debug: 3.2.7(supports-color@8.1.1) mkdirp: 0.5.6 transitivePeerDependencies: - supports-color