feat: manage user online status and typing indicator in socket gateway
- Added tracking of online users with real-time status updates (online/offline). - Implemented `handleTyping` to broadcast user typing events to recipients. - Added `check_status` handler to query user online status. - Enhanced CORS configuration to support multi-domain deployments with credentials.
This commit is contained in:
@@ -17,8 +17,16 @@ import { JwtService } from "../crypto/services/jwt.service";
|
|||||||
|
|
||||||
@WebSocketGateway({
|
@WebSocketGateway({
|
||||||
cors: {
|
cors: {
|
||||||
origin: "*",
|
origin: (
|
||||||
|
_origin: string,
|
||||||
|
callback: (err: Error | null, allow?: boolean) => void,
|
||||||
|
) => {
|
||||||
|
// En production, on pourrait restreindre ici
|
||||||
|
// Pour l'instant on autorise tout en mode credentials pour faciliter le déploiement multi-domaines
|
||||||
|
callback(null, true);
|
||||||
|
},
|
||||||
credentials: true,
|
credentials: true,
|
||||||
|
methods: ["GET", "POST"],
|
||||||
},
|
},
|
||||||
})
|
})
|
||||||
export class EventsGateway
|
export class EventsGateway
|
||||||
@@ -28,6 +36,7 @@ export class EventsGateway
|
|||||||
server!: Server;
|
server!: Server;
|
||||||
|
|
||||||
private readonly logger = new Logger(EventsGateway.name);
|
private readonly logger = new Logger(EventsGateway.name);
|
||||||
|
private readonly onlineUsers = new Map<string, Set<string>>(); // userId -> Set of socketIds
|
||||||
|
|
||||||
constructor(
|
constructor(
|
||||||
private readonly jwtService: JwtService,
|
private readonly jwtService: JwtService,
|
||||||
@@ -69,6 +78,13 @@ export class EventsGateway
|
|||||||
// Rejoindre une room personnelle pour les notifications
|
// Rejoindre une room personnelle pour les notifications
|
||||||
client.join(`user:${payload.sub}`);
|
client.join(`user:${payload.sub}`);
|
||||||
|
|
||||||
|
// Gérer le statut en ligne
|
||||||
|
if (!this.onlineUsers.has(payload.sub)) {
|
||||||
|
this.onlineUsers.set(payload.sub, new Set());
|
||||||
|
this.server.emit("user_status", { userId: payload.sub, status: "online" });
|
||||||
|
}
|
||||||
|
this.onlineUsers.get(payload.sub)?.add(client.id);
|
||||||
|
|
||||||
this.logger.log(`Client connected: ${client.id} (User: ${payload.sub})`);
|
this.logger.log(`Client connected: ${client.id} (User: ${payload.sub})`);
|
||||||
} catch (error) {
|
} catch (error) {
|
||||||
this.logger.error(`Connection error for client ${client.id}: ${error}`);
|
this.logger.error(`Connection error for client ${client.id}: ${error}`);
|
||||||
@@ -77,6 +93,15 @@ export class EventsGateway
|
|||||||
}
|
}
|
||||||
|
|
||||||
handleDisconnect(client: Socket) {
|
handleDisconnect(client: Socket) {
|
||||||
|
const userId = client.data.user?.sub;
|
||||||
|
if (userId && this.onlineUsers.has(userId)) {
|
||||||
|
const sockets = this.onlineUsers.get(userId);
|
||||||
|
sockets?.delete(client.id);
|
||||||
|
if (sockets?.size === 0) {
|
||||||
|
this.onlineUsers.delete(userId);
|
||||||
|
this.server.emit("user_status", { userId, status: "offline" });
|
||||||
|
}
|
||||||
|
}
|
||||||
this.logger.log(`Client disconnected: ${client.id}`);
|
this.logger.log(`Client disconnected: ${client.id}`);
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -98,6 +123,31 @@ export class EventsGateway
|
|||||||
this.logger.log(`Client ${client.id} left content room: ${contentId}`);
|
this.logger.log(`Client ${client.id} left content room: ${contentId}`);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@SubscribeMessage("typing")
|
||||||
|
handleTyping(
|
||||||
|
@ConnectedSocket() client: Socket,
|
||||||
|
@MessageBody() data: { recipientId: string; isTyping: boolean },
|
||||||
|
) {
|
||||||
|
const userId = client.data.user?.sub;
|
||||||
|
if (!userId) return;
|
||||||
|
|
||||||
|
this.server.to(`user:${data.recipientId}`).emit("user_typing", {
|
||||||
|
userId,
|
||||||
|
isTyping: data.isTyping,
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
@SubscribeMessage("check_status")
|
||||||
|
handleCheckStatus(
|
||||||
|
@ConnectedSocket() _client: Socket,
|
||||||
|
@MessageBody() userId: string,
|
||||||
|
) {
|
||||||
|
return {
|
||||||
|
userId,
|
||||||
|
status: this.onlineUsers.has(userId) ? "online" : "offline",
|
||||||
|
};
|
||||||
|
}
|
||||||
|
|
||||||
// Méthode utilitaire pour envoyer des messages à un utilisateur spécifique
|
// Méthode utilitaire pour envoyer des messages à un utilisateur spécifique
|
||||||
sendToUser(userId: string, event: string, data: any) {
|
sendToUser(userId: string, event: string, data: any) {
|
||||||
this.server.to(`user:${userId}`).emit(event, data);
|
this.server.to(`user:${userId}`).emit(event, data);
|
||||||
|
|||||||
Reference in New Issue
Block a user