• Home
  • Features
  • Pricing
  • Docs
  • Announcements
  • Sign In

Bahatisteven / vuga / 6c05cfcc-5aea-4247-a701-b472e8a0eb3a

16 Mar 2026 08:02PM UTC coverage: 59.005% (-11.1%) from 70.113%
6c05cfcc-5aea-4247-a701-b472e8a0eb3a

push

circleci

Bahatisteven
perf: optimize database queries, add security middleware, implement caching improvements

146 of 277 branches covered (52.71%)

Branch coverage included in aggregate %.

14 of 30 new or added lines in 10 files covered. (46.67%)

70 existing lines in 3 files now uncovered.

352 of 567 relevant lines covered (62.08%)

1.47 hits per line

Source File
Press 'n' to go to next uncovered line, 'b' for previous

0.0
/src/websocket/websocket.gateway.ts
1
import {
×
2
  WebSocketGateway,
3
  WebSocketServer,
4
  SubscribeMessage,
5
  MessageBody,
6
  ConnectedSocket,
7
  OnGatewayConnection,
8
  OnGatewayDisconnect,
9
} from '@nestjs/websockets';
UNCOV
10
import { Server, Socket } from 'socket.io';
×
UNCOV
11
import { JoinCallDto, TranscriptionDto } from './dto';
×
UNCOV
12
import { TranslationService } from '../translation/translation.service';
×
UNCOV
13
import { CallService } from '../call/call.service';
×
UNCOV
14
import { WebsocketService } from './websocket.service';
×
UNCOV
15
import { InjectRepository } from '@nestjs/typeorm';
×
UNCOV
16
import { Repository } from 'typeorm';
×
UNCOV
17
import { Call } from '../call/entities';
×
18
@WebSocketGateway({
19
  cors: {
20
    origin: process.env.CORS_ORIGIN?.split(',') || ['http://localhost:3000'],
×
21
    credentials: true,
22
  },
23
})
UNCOV
24
export class WebsocketGateway
×
25
  implements OnGatewayConnection, OnGatewayDisconnect
26
{
27
  @WebSocketServer()
UNCOV
28
  server: Server;
×
29

30
  // which clients are in which rooms
UNCOV
31
  private callRooms: Map<string, Set<string>> = new Map();
×
32

33
  // language preferences
UNCOV
34
  private userLanguages: Map<string, string> = new Map();
×
35

36
  constructor(
UNCOV
37
    private translationService: TranslationService,
×
UNCOV
38
    private callService: CallService,
×
UNCOV
39
    private readonly websocketService: WebsocketService,
×
40
    @InjectRepository(Call)
UNCOV
41
    private callRepository: Repository<Call>,
×
42
  ) {}
43

44
  handleConnection(client: Socket) {
UNCOV
45
    console.log(`Client connected: ${client.id}`);
×
46
  }
47

48
  handleDisconnect(client: Socket) {
UNCOV
49
    console.log(`Client disconnected: ${client.id}`);
×
50

UNCOV
51
    this.callRooms.forEach((clients, callId) => {
×
UNCOV
52
      if (clients.has(client.id)) {
×
UNCOV
53
        clients.delete(client.id);
×
54

55
        // notify other user left
UNCOV
56
        this.server.to(callId).emit('user-left', {
×
57
          userId: client.id,
58
          timestamp: new Date(),
59
        });
60

UNCOV
61
        this.userLanguages.delete(client.id);
×
62
      }
63
    });
64
  }
65

66
  /** join call event */
67

68
  @SubscribeMessage('join-call')
UNCOV
69
  async handleJoinCall(
×
70
    @MessageBody() data: JoinCallDto,
×
71
    @ConnectedSocket() client: Socket,
72
  ) {
UNCOV
73
    const { callId, language } = data;
×
74

UNCOV
75
    try {
×
76
      // verify call exists
UNCOV
77
      const call = await this.callRepository.findOne({
×
78
        where: { id: callId },
79
      });
UNCOV
80
      if (!call) {
×
UNCOV
81
        throw new Error('Call not found');
×
82
      }
83
    } catch {
UNCOV
84
      client.emit('error', { message: 'Call not found' });
×
UNCOV
85
      return;
×
86
    }
87

88
    // join socket.io room
NEW
89
    void client.join(callId);
×
90

91
    // track user in room
UNCOV
92
    if (!this.callRooms.has(callId)) {
×
UNCOV
93
      this.callRooms.set(callId, new Set());
×
94
    }
UNCOV
95
    this.callRooms.get(callId)!.add(client.id);
×
96

97
    // store user language
UNCOV
98
    this.userLanguages.set(client.id, language);
×
99

100
    // notify other someone joined
UNCOV
101
    client.to(callId).emit('user-joined', {
×
102
      userId: client.id,
103
      language,
104
      timestamp: new Date(),
105
    });
106

107
    // send confirmation
UNCOV
108
    client.emit('joined-call', {
×
109
      callId,
110
      language,
111
      participants: this.callRooms.get(callId)?.size,
112
      timestamp: new Date(),
113
    });
114

UNCOV
115
    console.log(
×
116
      `Client ${client.id} joined call ${callId} with language ${language}`,
117
    );
118
  }
119

120
  /** handle leave call event */
121

122
  @SubscribeMessage('leave-call')
UNCOV
123
  handleLeaveCall(
×
124
    @MessageBody() data: { callId: string },
125
    @ConnectedSocket() client: Socket,
×
126
  ) {
UNCOV
127
    const { callId } = data;
×
128

129
    //leave socket.io
NEW
130
    void client.leave(callId);
×
131

132
    //remove from tacking
UNCOV
133
    const room = this.callRooms.get(callId);
×
UNCOV
134
    if (room) {
×
UNCOV
135
      room.delete(client.id);
×
UNCOV
136
      if (room.size === 0) {
×
UNCOV
137
        this.callRooms.delete(callId);
×
138
      }
139
    }
140

141
    //remove language tracking
UNCOV
142
    this.userLanguages.delete(client.id);
×
143

144
    //notify others
UNCOV
145
    client.to(callId).emit('user-left', {
×
146
      userId: client.id,
147
      timestamp: new Date(),
148
    });
149

UNCOV
150
    console.log(`Client ${client.id} left call ${callId}`);
×
151
  }
152

153
  /** transcription event */
154

155
  @SubscribeMessage('transcription')
UNCOV
156
  async handleTranscription(
×
157
    @MessageBody() data: TranscriptionDto,
×
158
    @ConnectedSocket() client: Socket,
159
  ) {
UNCOV
160
    const { callId, text, language } = data;
×
161

162
    // broadcast original transcription to all in rooms
UNCOV
163
    this.server.to(callId).emit('transcription', {
×
164
      userId: client.id,
165
      text,
166
      language,
167
      timestamp: new Date(),
168
    });
169

170
    // translate for each participants with diff language
171

UNCOV
172
    const room = this.callRooms.get(callId);
×
UNCOV
173
    if (room) {
×
UNCOV
174
      for (const participantId of room) {
×
175
        // skip sender
UNCOV
176
        if (participantId !== client.id) {
×
UNCOV
177
          const targetLanguage = this.userLanguages.get(participantId);
×
178

179
          //only translate if target lang is diff
UNCOV
180
          if (targetLanguage && targetLanguage !== language) {
×
UNCOV
181
            try {
×
UNCOV
182
              const translatedText = await this.translationService.translate(
×
183
                text,
184
                language,
185
                targetLanguage,
186
              );
187
              // send translated text to specific partcipant
UNCOV
188
              this.server.to(participantId).emit('translated-text', {
×
189
                originalText: text,
190
                translatedText: translatedText,
191
                sourceLanguage: language,
192
                targetLanguage,
193
                userId: client.id,
194
                timestamp: new Date(),
195
              });
196
            } catch (error) {
UNCOV
197
              console.error('Translation error:', error);
×
198

199
              //send error to participants
UNCOV
200
              this.server.to(participantId).emit('translation-error', {
×
201
                message: 'Translation failed',
202
                originalText: text,
203
              });
204
            }
205
          }
206
        }
207
      }
208
    }
209
  }
210

211
  /**handle ping event(for connection testing) */
212
  @SubscribeMessage('ping')
UNCOV
213
  handlePing(@ConnectedSocket() client: Socket) {
×
UNCOV
214
    client.emit('pong', { timestamp: new Date() });
×
215
  }
216
}
STATUS · Troubleshooting · Open an Issue · Sales · Support · CAREERS · ENTERPRISE · START FREE · SCHEDULE DEMO
ANNOUNCEMENTS · TWITTER · TOS & SLA · Supported CI Services · What's a CI service? · Automated Testing

© 2026 Coveralls, Inc