import express, { Request, Response } from "express"; import { createServer, Server as HTTPServer } from "http"; import { Server as SocketIOServer, Socket } from "socket.io"; import cors from "cors"; import { activeCallMapType, callRequestAReject, ConnectRequest, OnlineUser, rtcAnswer, rtcCandidate, rtcOffer, UserDetails, } from "./types/sType.js"; import { getSecsFromDates, saveCallLogs } from "./helper"; // Initialize Express const app = express(); // Set up CORS middleware for Express app.use( cors({ origin: "*", }) ); // Create an HTTP server const httpServer: HTTPServer = createServer(app); // Initialize Socket.io on the server const io = new SocketIOServer(httpServer, { path: "/socket", // Define the WebSocket path cors: { origin: "*", // Update this to the origin of your client methods: ["GET", "POST"], }, }); // Store online users const onlineUsers: Map = new Map(); // Remap the socket ID to the user ID const socketToUserId: Map = new Map(); //call request const activeCalls: activeCallMapType = new Map(); // Handle WebSocket connections io.on("connection", (socket: Socket) => { console.log("User connected", socket.id); // Handle user-online event socket.on("user-online", (user: UserDetails) => { const { userId, userName, image } = user; // Now we store the socket ID along with the user details onlineUsers.set(userId, { userId: userId, userName, image, socketId: socket.id, }); socketToUserId.set(socket.id, userId); }); // Handle connect request event socket.on("connect-req", (user: ConnectRequest) => { const { caller, callee, userName, image } = user; // Now we store the socket ID along with the user details const details = onlineUsers.get(String(callee)); console.log("connect-req", details, onlineUsers, user); if (details) { socket.to(details.socketId).emit("connect-res", { userId: caller, userName, image, time: new Date(), }); } }); // Handle user-online event socket.on("connect-reject-req", (data: { users: number[] }) => { const { users } = data; console.log("connect-reject-req", data); // Now we store the socket ID along with the user details for (const user of users) { const details = onlineUsers.get(String(user)); console.log("connect-reject-req", details); if (details) { socket.to(details.socketId).emit("connect-reject-res", { reject: true, }); } } }); // Handle user disconnect event socket.on("disconnect", async () => { const user = activeCalls.get(socket.id); console.log("disconnect", user); if (user) { if (user?.from) { const calleeU = socketToUserId.get(socket.id); const callerU = socketToUserId.get(user.from); if (calleeU && callerU) { const caller = onlineUsers.get(callerU); const callee = onlineUsers.get(calleeU); if (caller && callee) { const meetTime = getSecsFromDates(user.callTime); const callLog = { to: callee.userId, from: caller.userId, time: meetTime, }; await saveCallLogs(callLog); socket.to(caller.socketId).emit("call-rejected"); } } } if (user?.to) { const callerU = socketToUserId.get(socket.id); const calleeU = socketToUserId.get(user.to); if (calleeU && callerU) { const caller = onlineUsers.get(callerU); const callee = onlineUsers.get(calleeU); if (caller && callee) { const meetTime = getSecsFromDates(user.callTime); const callLog = { to: callee.userId, from: caller.userId, time: meetTime, }; await saveCallLogs(callLog); socket.to(callee.socketId).emit("call-rejected"); } } } const secUser = user?.to || user?.from; if (secUser) { activeCalls.delete(socket.id); activeCalls.delete(secUser); } } const userId = socketToUserId.get(socket.id); if (userId) { onlineUsers.delete(userId); socketToUserId.delete(socket.id); } }); // Handle user ice candidate event socket.on("ice-candidate", (data: rtcCandidate) => { const { candidate, to } = data; const calledTo = onlineUsers.get(to); if (calledTo) { console.log("get-ice-candidate", to); // Emit to the recipient's socket ID socket.to(calledTo.socketId).emit("get-ice-candidate", candidate); } }); // // Handle call-offer events socket.on("offer", (data: rtcOffer) => { const { offer, to, sender } = data; const calledFrom = onlineUsers.get(sender); const calledTo = onlineUsers.get(to); if (calledTo && calledFrom) { // Emit to the recipient's socket ID socket.to(calledTo.socketId).emit("get-offer", { offer, sender: sender, senderName: calledFrom.userName, senderImage: calledFrom.image, }); } }); // // Handle call-request events socket.on("call-request", (data: rtcAnswer) => { const { answer, to } = data; const calledTo = onlineUsers.get(to); if (calledTo) { socket.to(calledTo.socketId).emit("get-answer", answer); } }); // // Handle call-accept events socket.on("call-accept", (data: callRequestAReject) => { const { to, from } = data; const calledTo = onlineUsers.get(to); const calledFrom = onlineUsers.get(from); if (calledTo && calledFrom) { activeCalls.set(calledFrom.socketId, { to: calledTo.socketId, callTime: new Date(), }); activeCalls.set(calledTo.socketId, { from: calledFrom.socketId, callTime: new Date(), }); console.log("call-accept", activeCalls); socket.to(calledTo.socketId).emit("call-accepted"); } }); // // Handle call-reject events socket.on("call-reject", async () => { const user = activeCalls.get(socket.id); if (user) { if (user?.from) { const calleeU = socketToUserId.get(socket.id); const callerU = socketToUserId.get(user.from); if (calleeU && callerU) { const caller = onlineUsers.get(callerU); const callee = onlineUsers.get(calleeU); if (caller && callee) { const meetTime = getSecsFromDates(user.callTime); const callLog = { to: callee.userId, from: caller.userId, time: meetTime, }; await saveCallLogs(callLog); socket.to(caller.socketId).emit("call-rejected"); } } } if (user?.to) { const callerU = socketToUserId.get(socket.id); const calleeU = socketToUserId.get(user.to); if (calleeU && callerU) { const caller = onlineUsers.get(callerU); const callee = onlineUsers.get(calleeU); if (caller && callee) { const meetTime = getSecsFromDates(user.callTime); const callLog = { to: callee.userId, from: caller.userId, time: meetTime, }; await saveCallLogs(callLog); socket.to(callee.socketId).emit("call-rejected"); } } } const secUser = user?.to || user?.from; if (secUser) { activeCalls.delete(socket.id); activeCalls.delete(secUser); } } }); }); app.use(express.json()); // Define a basic route app.get("/", (req: Request, res: Response) => { res.send("WebSocket server is running"); }); // Define a basic route app.post("/get-onlines", (req: Request, res: Response) => { const { users }: { users: number[] } = req.body; const onlineUsersData = users.filter((user: number) => onlineUsers.has(String(user)) ); res.json({ users: onlineUsersData }); }); // Start the server on port 3000 const PORT = process.env.PORT || 5007; httpServer.listen(PORT, () => { console.log(`Server is running on port ${PORT}`); });