From 0358a6025b438295674d8f8e1caf15d909c50967 Mon Sep 17 00:00:00 2001 From: Maieul BOYER Date: Mon, 24 Nov 2025 16:48:07 +0100 Subject: [PATCH] socket fix --- frontend/src/pages/chat/chat.ts | 140 +++++++++++++----------- src/chat/src/app.ts | 187 +++++++++++++++++--------------- 2 files changed, 177 insertions(+), 150 deletions(-) diff --git a/frontend/src/pages/chat/chat.ts b/frontend/src/pages/chat/chat.ts index e0d51ee..d4baf2f 100644 --- a/frontend/src/pages/chat/chat.ts +++ b/frontend/src/pages/chat/chat.ts @@ -3,45 +3,55 @@ import { showError } from "@app/toast"; import authHtml from './chat.html?raw'; import client from '@app/api' import { getUser, updateUser } from "@app/auth"; -import io from 'socket.io-client'; +import io, { Socket } from 'socket.io-client'; + +let __socket: Socket | undefined = undefined; +function getSocket(): Socket { + if (__socket === undefined) + __socket = io("wss://localhost:8888", { + path: "/api/chat/socket.io/", + secure: false, + transports: ["websocket"], + }); + return __socket; +} + + + function handleChat(_url: string, _args: RouteHandlerParams): RouteHandlerReturn { - const socket = io("wss://localhost:8888", { - path: "/api/chat/socket.io/", - secure: false, - transports: ["websocket"], - }); + let socket = getSocket(); // Listen for the 'connect' event socket.on("connect", () => { - console.log("I AM Connected to the server:", socket.id); - const user = getUser()?.name; - // Ensure we have a user AND socket is connected - if (!user || !socket.connected) return; - const message = { - type: "chat", - user, - token: document.cookie ?? "", - text: " has Just ARRIVED in the chat", - timestamp: Date.now(), - SenderWindowID: socket.id, + console.log("I AM Connected to the server:", socket.id); + const user = getUser()?.name; + // Ensure we have a user AND socket is connected + if (!user || !socket.connected) return; + const message = { + type: "chat", + user, + token: document.cookie ?? "", + text: " has Just ARRIVED in the chat", + timestamp: Date.now(), + SenderWindowID: socket.id, }; socket.emit('message', JSON.stringify(message)); }); // Listen for messages from the server "MsgObjectServer" socket.on("MsgObjectServer", (data: any) => { - console.log("Message Obj Recieved:", data.message); - console.log("Recieved data.message.text: ", data.message.text); - console.log("Recieved data.message.user: ", data.message.user); - console.log("Recieved data.message.type: ", data.message.type); - console.log("Recieved data.message.token: ", data.message.token); - console.log("Recieved data.message.timestamp: ", data.message.timestamp); + console.log("Message Obj Recieved:", data.message); + console.log("Recieved data.message.text: ", data.message.text); + console.log("Recieved data.message.user: ", data.message.user); + console.log("Recieved data.message.type: ", data.message.type); + console.log("Recieved data.message.token: ", data.message.token); + console.log("Recieved data.message.timestamp: ", data.message.timestamp); // Display the message in the chat window const chatWindow = document.getElementById("t-chatbox") as HTMLDivElement; if (chatWindow) { const messageElement = document.createElement("div"); // if (getUser()?.id !== `${data.message.id}`) { - console.log('==================> HERE'); - messageElement.textContent = `${data.message.user}: ${data.message.text}`; + console.log('==================> HERE'); + messageElement.textContent = `${data.message.user}: ${data.message.text}`; // } else { // console.log('==================>AND HERE'); // messageElement.textContent = `here`; @@ -49,7 +59,7 @@ function handleChat(_url: string, _args: RouteHandlerParams): RouteHandlerReturn chatWindow.appendChild(messageElement); chatWindow.scrollTop = chatWindow.scrollHeight; } - console.log("Getuser():", getUser()); + console.log("Getuser():", getUser()); }); type Providers = { @@ -62,10 +72,10 @@ function handleChat(_url: string, _args: RouteHandlerParams): RouteHandlerReturn // function handleChat(_url: string, _args: RouteHandlerParams): RouteHandlerReturn { setTitle('Chat Page'); - // Listen for the 'connect' event + // Listen for the 'connect' event return { - html: authHtml, postInsert: async (app) => { + html: authHtml, postInsert: async (app) => { const sendButton = document.getElementById('b-send') as HTMLButtonElement; const chatWindow = document.getElementById('t-chatbox') as HTMLDivElement; const sendtextbox = document.getElementById('t-chat-window') as HTMLButtonElement; @@ -73,8 +83,8 @@ function handleChat(_url: string, _args: RouteHandlerParams): RouteHandlerReturn const bwhoami = document.getElementById('b-whoami') as HTMLButtonElement; const bconnected = document.getElementById('b-help') as HTMLButtonElement; const username = document.getElementById('username') as HTMLDivElement; - - const value = await client.chatTest(); + + const value = await client.chatTest(); if (value.kind === "success") { console.log(value.payload); } else if (value.kind === "notLoggedIn") { @@ -82,67 +92,67 @@ function handleChat(_url: string, _args: RouteHandlerParams): RouteHandlerReturn } else { console.log('unknown response: ', value); } - - - const addMessage = (text: string) => { + + + const addMessage = (text: string) => { if (!chatWindow) return; - const messageElement = document.createElement("div"); + const messageElement = document.createElement("div"); messageElement.textContent = text; chatWindow.appendChild(messageElement); chatWindow.scrollTop = chatWindow.scrollHeight; - }; - + console.log(`Added new message: ${text}`) + }; + // Send button - sendButton?.addEventListener("click",() => { + sendButton?.addEventListener("click", () => { if (sendtextbox && sendtextbox.value.trim()) { const msgText = sendtextbox.value.trim(); - addMessage(msgText); - const user = getUser(); - if (user && socket?.connected) { - const message = { - type: "chat", - user: user.name, - token: document.cookie, - text: msgText, - timestamp: Date.now(), + addMessage(msgText); + const user = getUser(); + if (user && socket?.connected) { + const message = { + type: "chat", + user: user.name, + token: document.cookie, + text: msgText, + timestamp: Date.now(), SenderWindowID: socket.id, - }; - socket.send(JSON.stringify(message)); - } - sendtextbox.value = ""; - } - }); - - + }; + socket.send(JSON.stringify(message)); + } + sendtextbox.value = ""; + } + }); + + // Clear Text button clearText?.addEventListener("click", () => { if (chatWindow) { chatWindow.innerHTML = ''; } }); - - + + // Help Text button bconnected?.addEventListener("click", async () => { if (chatWindow) { addMessage('@list - lists all connected users in the chat'); await socket.emit('list'); - - socket.on('listObj', (list: string) => { - console.log('List chat clients connected ', list); - addMessage(list); - }); } }); - - + socket.on('listObj', (list: string) => { + console.log('List chat clients connected ', list); + addMessage(list); + }); + + // Enter key to send message sendtextbox!.addEventListener('keydown', (event) => { if (event.key === 'Enter') { sendButton?.click(); } }); - + // Whoami button to display user name bwhoami?.addEventListener('click', async () => { try { @@ -167,4 +177,4 @@ function handleChat(_url: string, _args: RouteHandlerParams): RouteHandlerReturn } } }; -addRoute('/chat', handleChat, { bypass_auth: true }); \ No newline at end of file +addRoute('/chat', handleChat, { bypass_auth: true }); diff --git a/src/chat/src/app.ts b/src/chat/src/app.ts index a0867be..bf3ff97 100644 --- a/src/chat/src/app.ts +++ b/src/chat/src/app.ts @@ -10,7 +10,7 @@ import { Server, Socket } from 'socket.io'; declare const __SERVICE_NAME: string; // Global map of clients -const clientChat = new Map(); // key = client name, value = socket +const clientChat = new Map(); // key = client name, value = socket // @ts-expect-error: import.meta.glob is a vite thing. Typescript doesn't know this... const plugins = import.meta.glob('./plugins/**/*.ts', { eager: true }); @@ -26,7 +26,7 @@ const app: FastifyPluginAsync = async (fastify, opts): Promise => { await fastify.register(db.useDatabase as FastifyPluginAsync, {}); await fastify.register(auth.jwtPlugin as FastifyPluginAsync, {}); await fastify.register(auth.authPlugin as FastifyPluginAsync, {}); - + // Place here your custom code! for (const plugin of Object.values(plugins)) { void fastify.register(plugin as FastifyPluginAsync, {}); @@ -73,71 +73,83 @@ declare module 'fastify' { } async function onReady(fastify: FastifyInstance) { - - function connectedUser(io?: Server, target?: string): number { - let count = 0; - const seen = new Set(); // <- only log/count unique usernames + let count = 0; + const seen = new Set(); + // <- only log/count unique usernames - for (const [socketId, username] of clientChat) { - // Basic sanity checks - if (typeof socketId !== "string" || socketId.length === 0) { - clientChat.delete(socketId); - continue; - } - if (typeof username !== "string" || username.length === 0) { - clientChat.delete(socketId); - continue; - } - - // If we have the io instance, attempt to validate the socket is still connected - if (io && typeof io.sockets?.sockets?.get === "function") { - const s = io.sockets.sockets.get(socketId) as Socket | undefined; - // If socket not found or disconnected, remove from map and skip - if (!s || s.disconnected) { - clientChat.delete(socketId); - continue; + for (const [socketId, username] of clientChat) { + // Basic sanity checks + if (typeof socketId !== 'string' || socketId.length === 0) { + clientChat.delete(socketId); + continue; } - - // Skip duplicates (DO NOT delete them — just don't count) - if (seen.has(username)) { - continue; - } - // socket exists and is connected - seen.add(username); + if (typeof username !== 'string' || username.length === 0) { + clientChat.delete(socketId); + continue; + } + + // If we have the io instance, attempt to validate the socket is still connected + if (io && typeof io.sockets?.sockets?.get === 'function') { + const s = io.sockets.sockets.get(socketId) as + | Socket + | undefined; + // If socket not found or disconnected, remove from map and skip + if (!s || s.disconnected) { + clientChat.delete(socketId); + continue; + } + + // Skip duplicates (DO NOT delete them — just don't count) + if (seen.has(username)) { + continue; + } + // socket exists and is connected + seen.add(username); + count++; + // console.log(color.green,"count: ", count); + console.log(color.yellow, 'Client:', color.reset, username); + + const targetSocketId: any = target; + io.to(targetSocketId).emit('listObj', username); + + console.log( + color.yellow, + 'Chat Socket ID:', + color.reset, + socketId, + ); + continue; + } + + // If no io provided, assume entries in the map are valid and count them. count++; - // console.log(color.green,"count: ", count); - console.log(color.yellow, "Client:", color.reset, username); + console.log( + color.red, + 'Client (unverified):', + color.reset, + username, + ); + console.log( + color.red, + 'Chat Socket ID (unverified):', + color.reset, + socketId, + ); + } - const targetSocketId: any = target; - io.to(targetSocketId).emit("listObj", username); - - - console.log(color.yellow, "Chat Socket ID:", color.reset, socketId); - continue; - } - - // If no io provided, assume entries in the map are valid and count them. - count++; - console.log(color.red, "Client (unverified):", color.reset, username); - console.log(color.red, "Chat Socket ID (unverified):", color.reset, socketId); - } - - return count; + return count; } - function broadcast(data: ClientMessage, sender?: string) { fastify.io.fetchSockets().then((sockets) => { - for (const s of sockets) { if (s.id !== sender) { - - // Send REAL JSON object - const clientName = clientChat.get(s.id) || null; - if (clientName !== null) { - s.emit('MsgObjectServer', { message: data }); - } + // Send REAL JSON object + const clientName = clientChat.get(s.id) || null; + if (clientName !== null) { + s.emit('MsgObjectServer', { message: data }); + } console.log(' Target window socket ID:', s.id); console.log(' Target window ID:', [...s.rooms]); console.log(' Sender window ID:', sender ? sender : 'none'); @@ -148,15 +160,19 @@ async function onReady(fastify: FastifyInstance) { fastify.io.on('connection', (socket: Socket) => { socket.on('message', (message: string) => { - console.info(color.blue, 'Socket connected!', color.reset, socket.id); + console.info( + color.blue, + 'Socket connected!', + color.reset, + socket.id, + ); console.log( color.blue, 'Received message from client', color.reset, message, ); - - + const obj: ClientMessage = JSON.parse(message) as ClientMessage; clientChat.set(socket.id, obj.user); console.log( @@ -167,43 +183,44 @@ async function onReady(fastify: FastifyInstance) { ); // Send object directly — DO NOT wrap it in a string broadcast(obj, obj.SenderWindowID); - console.log(color.red, 'connected in the Chat :', connectedUser(fastify.io), color.reset); + console.log( + color.red, + 'connected in the Chat :', + connectedUser(fastify.io), + color.reset, + ); }); - - + socket.on('testend', (sock_id_cl: string) => { console.log('testend received from client socket id:', sock_id_cl); }); - + socket.on('list', () => { console.log(color.red, 'list activated', color.reset, socket.id); connectedUser(fastify.io, socket.id); - - }); - - - - socket.on("disconnecting", (reason) => { - - const clientName = clientChat.get(socket.id) || null; - console.log(color.green, `Client disconnecting: ${clientName} (${socket.id}) reason:`, reason); + socket.on('disconnecting', (reason) => { + const clientName = clientChat.get(socket.id) || null; + console.log( + color.green, + `Client disconnecting: ${clientName} (${socket.id}) reason:`, + reason, + ); if (reason === 'transport error') return; - - - if (clientName !== null) { - const obj = { - type: "chat", - user: clientName, - token: "", - text: `LEFT the chat`, - timestamp: Date.now(), - SenderWindowID: socket.id, - }; - broadcast(obj, obj.SenderWindowID); - // clientChat.delete(obj.user); + if (clientName !== null) { + const obj = { + type: 'chat', + user: clientName, + token: '', + text: 'LEFT the chat', + timestamp: Date.now(), + SenderWindowID: socket.id, + }; + + broadcast(obj, obj.SenderWindowID); + // clientChat.delete(obj.user); } }); });