import express from "express"; import { createServer } from "http"; import { Server } from "socket.io"; import path from "path"; import { fileURLToPath } from "url"; const __filename = fileURLToPath(import.meta.url); const __dirname = path.dirname(__filename); async function startServer() { const app = express(); const httpServer = createServer(app); const io = new Server(httpServer, { cors: { origin: "*", methods: ["GET", "POST"], }, // FIX: Force websocket only - prevents the rapid connect/disconnect loop // that happens when Socket.io keeps falling back to HTTP polling on HF Spaces transports: ["websocket"], pingTimeout: 60000, pingInterval: 25000, }); const PORT = parseInt(process.env.PORT || "7860"); let videoQueue: string[] = []; let textQueue: string[] = []; const partners = new Map(); let activeCount = 0; io.on("connection", (socket) => { console.log("User connected:", socket.id); socket.on("join-queue", ({ type }) => { // FIX: If already in queue, do nothing - prevents spam joining on reconnect if (videoQueue.includes(socket.id) || textQueue.includes(socket.id)) { console.log(`${socket.id} already in queue, ignoring duplicate`); socket.emit("waiting"); return; } // If already matched with someone, do nothing if (partners.has(socket.id)) { console.log(`${socket.id} already matched, ignoring join-queue`); return; } console.log(`${socket.id} joining ${type} queue`); activeCount++; io.emit("online_count", activeCount); const queue = type === "video" ? videoQueue : textQueue; if (queue.length > 0) { const partnerId = queue.shift()!; partners.set(socket.id, partnerId); partners.set(partnerId, socket.id); console.log(`✅ Matched ${socket.id} with ${partnerId}`); io.to(socket.id).emit("matched", { partnerId, type, role: "initiator" }); io.to(partnerId).emit("matched", { partnerId: socket.id, type, role: "receiver" }); } else { queue.push(socket.id); socket.emit("waiting"); console.log(`${socket.id} waiting in ${type} queue (queue size: ${queue.length})`); } }); socket.on("send-message", (text) => { const partnerId = partners.get(socket.id); if (partnerId) { io.to(partnerId).emit("receive-message", text); } }); socket.on("signal", (data) => { const partnerId = partners.get(socket.id); if (partnerId) { io.to(partnerId).emit("signal", data); } }); socket.on("leave-chat", () => { handleLeave(socket.id); }); socket.on("disconnect", () => { console.log("User disconnected:", socket.id); handleLeave(socket.id); }); function handleLeave(socketId: string) { const wasActive = videoQueue.includes(socketId) || textQueue.includes(socketId) || partners.has(socketId); if (wasActive) { activeCount = Math.max(0, activeCount - 1); io.emit("online_count", activeCount); } const partnerId = partners.get(socketId); if (partnerId) { io.to(partnerId).emit("partner-disconnected"); partners.delete(partnerId); } partners.delete(socketId); videoQueue = videoQueue.filter((id) => id !== socketId); textQueue = textQueue.filter((id) => id !== socketId); } }); const distPath = path.join(process.cwd(), "dist"); app.use(express.static(distPath)); app.get("*", (req, res) => { res.sendFile(path.join(distPath, "index.html")); }); httpServer.listen(PORT, "0.0.0.0", () => { console.log(`MingleHub server running on port ${PORT}`); }); } startServer();