Skip to content
Closed

Glm #5473

Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
48 changes: 48 additions & 0 deletions src/realtime/events/connection.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,48 @@
const roomService = require("../services/roomService");
const notificationService = require("../services/notificationService");

function registerConnectionHandler(io) {
io.on("connection", (socket) => {
console.log(`client connected: ${socket.id}`);

socket.on("join-room", ({ roomId, userId }) => {
if (!roomId || !userId) {
socket.emit("event-error", {
message: "roomId and userId are required",
});
return;
}

roomService.joinRoom(socket, roomId, userId);

socket.emit("room-joined", {
roomId,
userId,
socketId: socket.id,
});
});

socket.on("broadcast-notification", ({ roomId, message, type }) => {
if (!roomId || !message) {
socket.emit("event-error", {
message: "roomId and message are required",
});
return;
}

notificationService.broadcastToRoom(io, roomId, {
type: type || "info",
message,
senderId: socket.id,
timestamp: new Date().toISOString(),
});
});

socket.on("disconnect", () => {
roomService.removeSocket(socket.id);
console.log(`client disconnected: ${socket.id}`);
});
});
}

module.exports = registerConnectionHandler;
15 changes: 15 additions & 0 deletions src/realtime/services/notificationService.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,15 @@
class NotificationService {
broadcastToRoom(io, roomId, payload) {
io.to(roomId).emit("receive-notification", payload);
}

broadcastToAll(io, payload) {
io.emit("receive-notification", payload);
}

sendToClient(socket, payload) {
socket.emit("receive-notification", payload);
}
}

module.exports = new NotificationService();
45 changes: 45 additions & 0 deletions src/realtime/services/roomService.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,45 @@
class RoomService {
constructor() {
this.rooms = new Map();
}

joinRoom(socket, roomId, userId) {
socket.join(roomId);

if (!this.rooms.has(roomId)) {
this.rooms.set(roomId, new Set());
}

this.rooms.get(roomId).add(
JSON.stringify({
socketId: socket.id,
userId,
})
);
}

removeSocket(socketId) {
for (const [roomId, members] of this.rooms.entries()) {
const filteredMembers = [...members].filter((member) => {
const parsed = JSON.parse(member);
return parsed.socketId !== socketId;
});

if (filteredMembers.length === 0) {
this.rooms.delete(roomId);
} else {
this.rooms.set(roomId, new Set(filteredMembers));
}
}
}

getRoomMembers(roomId) {
if (!this.rooms.has(roomId)) {
return [];
}

return [...this.rooms.get(roomId)].map((member) => JSON.parse(member));
}
}

module.exports = new RoomService();
16 changes: 16 additions & 0 deletions src/realtime/socketServer.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,16 @@
const { Server } = require("socket.io");
const registerConnectionHandler = require("./events/connection");

function createSocketServer(httpServer) {
const io = new Server(httpServer, {
cors: {
origin: "*",
},
});

registerConnectionHandler(io);

return io;
}

module.exports = createSocketServer;
38 changes: 38 additions & 0 deletions tests/realtime.test.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,38 @@
const roomService = require("../src/realtime/services/roomService");

describe("Realtime notification system", () => {
beforeEach(() => {
roomService.rooms = new Map();
});

it("should allow client to join room", () => {
const mockSocket = {
id: "socket-1",
join: jest.fn(),
};

roomService.joinRoom(mockSocket, "room-a", "user-1");

const members = roomService.getRoomMembers("room-a");

expect(mockSocket.join).toHaveBeenCalledWith("room-a");
expect(members).toHaveLength(1);
expect(members[0]).toEqual({
socketId: "socket-1",
userId: "user-1",
});
});

it("should clean up room members on disconnect", () => {
const mockSocket = {
id: "socket-2",
join: jest.fn(),
};

roomService.joinRoom(mockSocket, "room-b", "user-2");
roomService.removeSocket("socket-2");

const members = roomService.getRoomMembers("room-b");
expect(members).toHaveLength(0);
});
});