Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Websocket gateway is registered two times, every events are duplicated. #14787

Closed
7 of 15 tasks
SquirrelDeveloper opened this issue Mar 14, 2025 · 2 comments
Closed
7 of 15 tasks

Comments

@SquirrelDeveloper
Copy link

Is there an existing issue for this?

  • I have searched the existing issues

Current behavior

Hi everyone.

I am following a discussion I had on the NestJS discord server.

I am currently refactoring and migrating my open source project, SquirrelServersManager, to NestJS!
However, I am encountering a problem I cannot solve:

I have one module, called "ssh", that has a websocket gateway. (https://github.com/SquirrelCorporation/SquirrelServersManager/tree/chore-refacto-nestjs/server/src/modules/ssh ; https://github.com/SquirrelCorporation/SquirrelServersManager/blob/chore-refacto-nestjs/server/src/modules/ssh/presentation/gateways/ssh.gateway.ts)

This module is "standalone", its not injected in any other module

However, I am seeing in the logs that the handleConnection methods is called twice (but the client only connect once):

server-1        | SSH Gateway - Connection details: {
server-1        |   id: 'lgFSRJ1pQeQgUDfpAAAL',
server-1        |   nsp: '/ssh',
server-1        |   connected: true,
server-1        |   rooms: [ 'lgFSRJ1pQeQgUDfpAAAL' ]
server-1        | }
server-1        | SSH Gateway - Connection details: {
server-1        |   id: 'lgFSRJ1pQeQgUDfpAAAL',
server-1        |   nsp: '/ssh',
server-1        |   connected: true,
server-1        |   rooms: [ 'lgFSRJ1pQeQgUDfpAAAL' ]
server-1        | }

I tried a lots of stuff:

  • Putting the module Global (didn't change anything)
  • Having a different chain dependency, no luck either
  • Having the gateway in a separate module

What am I missing? I am pretty sure that the module is not explicitely imported two times, however, the logs say the contrary

Minimum reproduction code

https://github.com/SquirrelCorporation/SquirrelServersManager/blob/chore-refacto-nestjs/server/src/modules/ssh/presentation/gateways/ssh.gateway.ts

Steps to reproduce

No response

Expected behavior

Gateway should only be registered once.

Package

  • I don't know. Or some 3rd-party package
  • @nestjs/common
  • @nestjs/core
  • @nestjs/microservices
  • @nestjs/platform-express
  • @nestjs/platform-fastify
  • @nestjs/platform-socket.io
  • @nestjs/platform-ws
  • @nestjs/testing
  • @nestjs/websockets
  • Other (see below)

Other package

No response

NestJS version

11.0.11

Packages versions

{
  "name": "ssm-server",
  "description": "SSM Server - A simple way to manage all your servers",
  "main": "main.ts",
  "license": "AGPL-3.0 license",
  "scripts": {
    "dev": "nodemon",
    "dev:nest": "nest start --watch",
    "dev:fast": "NODE_ENV=development ts-node-dev --respawn --transpile-only --require tsconfig-paths/register --watch src --clear src/main.ts",
    "start": "npm run build-shared && npm run build && cross-env NODE_ENV=production node -r tsconfig-paths/register dist/index.js",
    "build": "nest build",
    "test": "vitest run",
    "test:python:install": "PIP_BREAK_SYSTEM_PACKAGES=1 pip install -r ./src/ansible/requirements.txt",
    "test:python": "npm run test:python:install && npm run test:python:run",
    "test:python:run": "cd ./src/ansible && python3 -m unittest discover -s . -p \"*.py\"",
    "test:dev": "vitest --disable-console-intercept",
    "coverage": "vitest run --coverage"
  },
  "version": "0.1.31",
  "author": "Squirrel Team",
  "dependencies": {
    "@aws-sdk/client-ecr": "^3.750.0",
    "@nestjs/axios": "^4.0.0",
    "@nestjs/cache-manager": "^3.0.0",
    "@nestjs/common": "^11.0.11",
    "@nestjs/config": "^4.0.1",
    "@nestjs/core": "^11.0.11",
    "@nestjs/event-emitter": "^3.0.1",
    "@nestjs/mongoose": "^11.0.1",
    "@nestjs/passport": "^11.0.5",
    "@nestjs/platform-express": "^11.0.11",
    "@nestjs/platform-socket.io": "^11.0.11",
    "@nestjs/schedule": "^5.0.1",
    "@nestjs/websockets": "^11.0.11",
    "@nestjs/jwt": "11.0.0",
    "axios": "^1.7.9",
    "bcrypt": "^5.1.1",
    "bull": "^4.16.5",
    "cache-manager": "^6.4.1",
    "cache-manager-redis-store": "^3.0.1",
    "class-transformer": "^0.5.1",
    "class-validator": "^0.14.0",
    "connect-mongo": "^5.1.0",
    "cookie-parser": "^1.4.7",
    "debounce": "^2.2.0",
    "dockerode": "^4.0.4",
    "dockerode-compose": "^1.4.0",
    "dotenv": "^16.4.7",
    "dugite": "^2.7.1",
    "express": "^5.0.1",
    "express-validator": "^7.2.1",
    "fs-extra": "^11.3.0",
    "isomorphic-git": "^1.29.0",
    "joi": "^17.13.3",
    "jsonwebtoken": "^9.0.2",
    "lodash": "^4.17.21",
    "luxon": "^3.5.0",
    "mongoose": "^8.10.1",
    "mongoose-autopopulate": "^1.1.0",
    "multer": "^1.4.5-lts.1",
    "nestjs-pino": "^4.3.1",
    "node-cron": "^3.0.3",
    "node-ssh": "^13.2.0",
    "parse-docker-image-name": "^3.0.0",
    "passport": "^0.7.0",
    "passport-anonymous": "^1.0.1",
    "passport-http": "^0.3.0",
    "passport-http-bearer": "^1.0.1",
    "passport-jwt": "^4.0.1",
    "passport-mock-strategy": "^2.0.0",
    "pino": "^9.6.0",
    "pino-http": "^10.4.0",
    "pino-mongodb": "^4.3.0",
    "pino-pretty": "^13.0.0",
    "posthog-node": "^4.7.0",
    "prom-client": "^15.1.3",
    "prometheus-query": "^3.4.1",
    "redis": "^4.7.0",
    "reflect-metadata": "^0.2.1",
    "rewire": "^7.0.0",
    "rxjs": "^7.8.1",
    "semver": "^7.7.1",
    "shelljs": "^0.8.5",
    "socket.io": "^4.8.1",
    "ssh2": "^1.16.0",
    "ssm-shared-lib": "file:../shared-lib/",
    "yaml": "^2.7.0"
  },
  "overrides": {
    "minimatch": "5.1.2",
    "glob": "8.1.0"
  },
  "devDependencies": {
    "@eslint/compat": "^1.2.7",
    "@eslint/eslintrc": "^3.3.0",
    "@eslint/js": "^9.21.0",
    "@nestjs/testing": "^11.0.11",
    "@stylistic/eslint-plugin": "^4.0.1",
    "@types/bcrypt": "^5.0.2",
    "@types/cookie-parser": "^1.4.8",
    "@types/dockerode": "^3.3.35",
    "@types/dockerode-compose": "^1.4.1",
    "@types/express": "^5.0.0",
    "@types/fs-extra": "^11.0.4",
    "@types/js-yaml": "^4.0.9",
    "@types/jsonwebtoken": "^9.0.9",
    "@types/lodash": "^4.17.15",
    "@types/luxon": "^3.4.2",
    "@types/multer": "^1.4.12",
    "@types/node": "^22.13.9",
    "@types/node-cron": "^3.0.11",
    "@types/node-os-utils": "^1.3.4",
    "@types/passport": "^1.0.17",
    "@types/passport-anonymous": "^1.0.5",
    "@types/passport-http": "^0.3.11",
    "@types/passport-http-bearer": "^1.0.41",
    "@types/passport-jwt": "^4.0.1",
    "@types/shelljs": "^0.8.15",
    "@types/ssh2": "^1.15.4",
    "@types/supertest": "^6.0.2",
    "@types/uuid": "^10.0.0",
    "@typescript-eslint/eslint-plugin": "^8.25.0",
    "@typescript-eslint/parser": "^8.25.0",
    "@vitest/coverage-v8": "^3.0.8",
    "eslint": "^9.21.0",
    "eslint-config-prettier": "^10.0.1",
    "eslint-plugin-import-x": "^4.6.1",
    "eslint-plugin-prettier": "^5.2.3",
    "globals": "^16.0.0",
    "memfs": "^4.17.0",
    "mongodb-memory-server": "^10.1.4",
    "node-mocks-http": "^1.16.2",
    "nodemon": "^3.1.9",
    "prettier": "^3.5.2",
    "supertest": "^7.0.0",
    "ts-node": "^10.9.2",
    "ts-node-dev": "^2.0.0",
    "typescript": "^5.7.3",
    "vitest": "^3.0.8",
    "tsconfig-paths": "^4.0.0"
  }
}

Node.js version

23.8.0

In which operating systems have you tested?

  • macOS
  • Windows
  • Linux

Other

No response

@SquirrelDeveloper SquirrelDeveloper added the needs triage This issue has not been looked into label Mar 14, 2025
@kamilmysliwiec
Copy link
Member

Please provide a minimum reproduction repository (Git repository/StackBlitz/CodeSandbox project).

@kamilmysliwiec kamilmysliwiec added needs clarification and removed needs triage This issue has not been looked into labels Mar 18, 2025
@kamilmysliwiec
Copy link
Member

kamilmysliwiec commented Mar 19, 2025

I tried to reproduce your issue with this very simple setup (following what you have in your repository):

@Injectable()
export class SshTerminalService {
  private readonly logger = new Logger(SshTerminalService.name);

  constructor(
    @Inject(forwardRef(() => EventsGateway))
    private readonly sshGateway,
  ) {}
}

@WebSocketGateway({
  cors: {
    origin: '*',
  },
})
export class EventsGateway {
  private readonly logger = new Logger(EventsGateway.name);

  @WebSocketServer()
  server: Server;

  constructor(
    @Inject(forwardRef(() => SshTerminalService))
    private readonly terminalSvc: SshTerminalService,
  ) {}

  afterInit() {
    this.logger.log(`SSH WebSocket Gateway initialized`);
  }

  handleConnection(client: Socket) {
    // Detailed debugging information
    console.log('SSH Gateway - Connection details:', {
      id: client.id,
      nsp: client.nsp?.name,
      connected: client.connected,
      rooms: Array.from(client.rooms || []),
    });
  }

  handleDisconnect(client: Socket) {
    this.logger.log(`Client disconnected (ssh): ${client.id}`);
  }

  @SubscribeMessage('events')
  findAll(@MessageBody() data: any): Observable<WsResponse<number>> {
    return from([1, 2, 3]).pipe(map(item => ({ event: 'events', data: item })));
  }

  @SubscribeMessage('identity')
  async identity(@MessageBody() data: number): Promise<number> {
    return data;
  }
}

and using 02-gateways sample as the base setup, and I was unable to reproduce your issue

Image

Image

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Projects
None yet
Development

No branches or pull requests

2 participants