From eec27ce2e6b613473cb52ffa9b1e76233ffe2b79 Mon Sep 17 00:00:00 2001 From: Maieul BOYER Date: Thu, 20 Nov 2025 17:06:39 +0100 Subject: [PATCH] feat(chat): updated to work with socket.io - Chore: ran `make npm@update` to update deps and lockfile - Chat: new plugin: Socket.ts that allow the use of socket.io with fastify (fastify-socket.io is not updated) - Chat: Put everything from `src/socket.ts` that needed to be saved into `src/app.ts` --- src/@shared/package.json | 4 +- src/auth/package.json | 4 +- src/chat/package.json | 4 +- src/chat/src/app.ts | 108 +++++++++++++++++++++++++++++---- src/chat/src/plugins/socket.ts | 53 ++++++++-------- src/chat/src/socket.ts | 90 --------------------------- src/icons/package.json | 2 +- src/package.json | 6 +- src/user/package.json | 4 +- 9 files changed, 132 insertions(+), 143 deletions(-) delete mode 100644 src/chat/src/socket.ts diff --git a/src/@shared/package.json b/src/@shared/package.json index fd25288..edcf6a8 100644 --- a/src/@shared/package.json +++ b/src/@shared/package.json @@ -18,9 +18,9 @@ "better-sqlite3": "^11.10.0", "fastify": "^5.6.2", "fastify-plugin": "^5.1.0", - "joi": "^18.0.1", + "joi": "^18.0.2", "otp": "^1.1.2", - "typebox": "^1.0.53", + "typebox": "^1.0.55", "uuidv7": "^1.0.2" }, "devDependencies": { diff --git a/src/auth/package.json b/src/auth/package.json index d41774c..855e7f8 100644 --- a/src/auth/package.json +++ b/src/auth/package.json @@ -27,12 +27,12 @@ "fastify": "^5.6.2", "fastify-cli": "^7.4.1", "fastify-plugin": "^5.1.0", - "typebox": "^1.0.53" + "typebox": "^1.0.55" }, "devDependencies": { "@types/node": "^22.19.1", "rollup-plugin-node-externals": "^8.1.2", - "vite": "^7.2.2", + "vite": "^7.2.4", "vite-tsconfig-paths": "^5.1.4" } } diff --git a/src/chat/package.json b/src/chat/package.json index 16f3b6c..7a504d7 100644 --- a/src/chat/package.json +++ b/src/chat/package.json @@ -26,15 +26,13 @@ "@fastify/websocket": "^11.2.0", "@sinclair/typebox": "^0.34.41", "fastify": "^5.6.2", - "fastify-cli": "^7.4.1", "fastify-plugin": "^5.1.0", - "fastify-socket.io": "^5.1.0", "socket.io": "^4.8.1" }, "devDependencies": { "@types/node": "^22.19.1", "rollup-plugin-node-externals": "^8.1.2", - "vite": "^7.2.2", + "vite": "^7.2.4", "vite-tsconfig-paths": "^5.1.4" } } diff --git a/src/chat/src/app.ts b/src/chat/src/app.ts index e8f198b..eaf01c1 100644 --- a/src/chat/src/app.ts +++ b/src/chat/src/app.ts @@ -1,14 +1,11 @@ -import { FastifyPluginAsync } from 'fastify'; +import { FastifyInstance, FastifyPluginAsync } from 'fastify'; import fastifyFormBody from '@fastify/formbody'; import fastifyMultipart from '@fastify/multipart'; import * as db from '@shared/database'; import * as auth from '@shared/auth'; import * as swagger from '@shared/swagger'; import * as utils from '@shared/utils'; -// import useSocketIo from 'fastify-socket.io'; -import { setupSocketIo } from './socket'; -import.meta.glob('/plugins/**.ts') - +import { Server, Socket } from 'socket.io'; declare const __SERVICE_NAME: string; @@ -19,6 +16,8 @@ const routes = import.meta.glob('./routes/**/*.ts', { eager: true }); const app: FastifyPluginAsync = async (fastify, opts): Promise => { void opts; + + await fastify.register(utils.useMonitoring); await fastify.register(utils.useMakeResponse); await fastify.register(swagger.useSwagger, { service: __SERVICE_NAME }); await fastify.register(db.useDatabase as FastifyPluginAsync, {}); @@ -38,13 +37,100 @@ const app: FastifyPluginAsync = async (fastify, opts): Promise => { void fastify.register(fastifyFormBody, {}); void fastify.register(fastifyMultipart, {}); - fastify.get('/monitoring', () => 'Ok'); - - - - // Setup Socket.io - setupSocketIo(); + fastify.ready((err) => { + if (err) throw err; + onReady(fastify); + }); }; export default app; export { app }; + +export const color = { + red: 'x1b[31m', + green: 'x1b[32m', + yellow: 'x1b[33m', + blue: 'x1b[34m', + reset: 'x1b[0m', +}; + +type ClientMessage = { + userID: string; + text: string; + SenderWindowID: string; +}; + +// When using .decorate you have to specify added properties for Typescript +declare module 'fastify' { + interface FastifyInstance { + io: Server<{ + hello: (message: string) => string; + MsgObjectServer: (data: { message: ClientMessage }) => void; + message: (msg: string) => void; + testend: (sock_id_client: string) => void; + }>; + } +} + +async function onReady(fastify: FastifyInstance) { + // Broadcast function to send messages to all connected clients except the sender + function broadcast(data: ClientMessage, sender?: string) { + fastify.io.fetchSockets().then((sockets) => { + console.log('Connected clients:', sockets.length); + + for (const s of sockets) { + if (s.id !== sender) { + // Send REAL JSON object + s.emit('MsgObjectServer', { message: data }); + + console.log(' emit window socket ID:', s.id); + console.log(' emit window ID:', [...s.rooms]); + console.log(' Sender window ID:', sender ? sender : 'none'); + console.log( + ' text recieved:', + data.text ? data.text : 'none', + ); + console.log( + color.red, + 'data:', + color.reset, + data ? data : 'none', + ); + } + } + }); + } + fastify.io.on('connection', (socket: Socket) => { + console.info(color.blue, 'Socket connected!', color.reset, socket.id); + socket.on('message', (message: string) => { + console.log( + color.blue, + 'Received message from client', + color.reset, + message, + ); + const obj: ClientMessage = JSON.parse(message) as ClientMessage; + console.log( + color.green, + 'Message from client', + color.reset, + `${obj.userID}: ${obj.text}`, + ); + + // Send object directly — DO NOT wrap it in a string + broadcast(obj, obj.SenderWindowID); + }); + socket.on('testend', (sock_id_cl: string) => { + console.log('testend received from client socket id:', sock_id_cl); + }); + socket.on('disconnecting', (reason) => { + console.log( + 'Client is disconnecting:', + socket.id, + 'reason:', + reason, + ); + console.log('Socket AAAAAAAActing because:', socket.connected); + }); + }); +} diff --git a/src/chat/src/plugins/socket.ts b/src/chat/src/plugins/socket.ts index 59905d2..f32dec8 100644 --- a/src/chat/src/plugins/socket.ts +++ b/src/chat/src/plugins/socket.ts @@ -1,36 +1,31 @@ import type { - FastifyInstance, - FastifyPluginAsync, - HookHandlerDoneFunction, + FastifyInstance, + FastifyPluginAsync, + HookHandlerDoneFunction, } from 'fastify'; import fp from 'fastify-plugin'; -import { Server, type ServerOptions } from 'socket.io'; +import { Server } from 'socket.io'; -export type FastifySocketioOptions = Partial & { - preClose?: (done: HookHandlerDoneFunction) => void; -}; +const F: ( + f: FastifyInstance, +) => Omit & { io: Server } = (f) => + f as Omit & { io: Server }; -const F: (f: FastifyInstance) => (Omit & { io: Server }) = f => (f as Omit & { io: Server }); +const fastifySocketIO: FastifyPluginAsync = fp(async (fastify) => { + function defaultPreClose(done: HookHandlerDoneFunction) { + F(fastify).io.local.disconnectSockets(true); + done(); + } + fastify.decorate( + 'io', + new Server(fastify.server, { path: '/api/chat/socket.io' }), + ); + fastify.addHook('preClose', defaultPreClose); + fastify.addHook('onClose', (instance: FastifyInstance, done) => { + F(instance).io.close(); + done(); + }); +}); -const fastifySocketIO: FastifyPluginAsync = fp( - async (fastify, opts: FastifySocketioOptions) => { - function defaultPreClose(done: HookHandlerDoneFunction) { - F(fastify).io.local.disconnectSockets(true); - done(); - } - fastify.decorate('io', new Server(fastify.server, opts)); - fastify.addHook('preClose', (done) => { - if (opts.preClose) { - return opts.preClose(done); - } - return defaultPreClose(done); - }); - fastify.addHook('onClose', (instance: FastifyInstance, done) => { - F(instance).io.close(); - done(); - }); - }, -); +export default fastifySocketIO; - -export default fastifySocketIO; \ No newline at end of file diff --git a/src/chat/src/socket.ts b/src/chat/src/socket.ts deleted file mode 100644 index dda5f3d..0000000 --- a/src/chat/src/socket.ts +++ /dev/null @@ -1,90 +0,0 @@ -import Fastify from "fastify"; -import { Server, Socket } from 'socket.io'; - -export const color = { - red: 'x1b[31m', - green: 'x1b[32m', - yellow: 'x1b[33m', - blue: 'x1b[34m', - reset: 'x1b[0m', -}; - -type ClientMessage = { - userID: string; - text: string; - SenderWindowID: string; -}; - -// When using .decorate you have to specify added properties for Typescript -declare module 'fastify' { - interface FastifyInstance { - io: Server<{ - hello: (message: string) => string, - MsgObjectServer: (data: { message: ClientMessage }) => void, - message: (msg: string) => void, - testend: (sock_id_client: string) => void, - }> - } -}; - -const fastify = Fastify({ - logger: true -}); - - -export async function setupSocketIo() { - // Wait for Fastify to be ready so .server exists - await fastify.ready(); - - const io = new Server(fastify.server, { - cors: { - origin: "*" - } - }); - - -// export function setupSocketIo(fastify: import('fastify').FastifyInstance): void { - -// fastify.ready((err) => { -// if (err) throw err; - - // Broadcast function to send messages to all connected clients except the sender - function broadcast(data: ClientMessage, sender?: string) { - fastify.io.fetchSockets().then((sockets) => { - console.log('Connected clients:', sockets.length); - - for (const s of sockets) { - if (s.id !== sender) { - // Send REAL JSON object - s.emit('MsgObjectServer', { message: data }); - - console.log(' emit window socket ID:', s.id); - console.log(' emit window ID:', [...s.rooms]); - console.log(' Sender window ID:', sender ? sender : 'none'); - console.log(' text recieved:', data.text ? data.text : 'none'); - console.log(color.red, 'data:', color.reset, data ? data : 'none'); - } - } - }); - }; - // console.log(Object.getOwnPropertyNames(Object.getPrototypeOf(fastify.io))); - io.on('connection', (socket : Socket) => { - console.info(color.blue, 'Socket connected!', color.reset, socket.id); - socket.on('message', (message: string) => { - console.log(color.blue, 'Received message from client', color.reset, message); - const obj: ClientMessage = JSON.parse(message) as ClientMessage; - console.log(color.green, 'Message from client', color.reset, `${obj.userID}: ${obj.text}`); - - // Send object directly — DO NOT wrap it in a string - broadcast(obj, obj.SenderWindowID); - }); - socket.on('testend', (sock_id_cl : string) => { - console.log('testend received from client socket id:', sock_id_cl); - }); - socket.on('disconnecting', (reason) => { - console.log('Client is disconnecting:', socket.id, 'reason:', reason); - console.log('Socket AAAAAAAActing because:', socket.connected); - }); - }); -// }); -}; \ No newline at end of file diff --git a/src/icons/package.json b/src/icons/package.json index f161e2c..cf5b0ed 100644 --- a/src/icons/package.json +++ b/src/icons/package.json @@ -31,7 +31,7 @@ "devDependencies": { "@types/node": "^22.19.1", "rollup-plugin-node-externals": "^8.1.2", - "vite": "^7.2.2", + "vite": "^7.2.4", "vite-tsconfig-paths": "^5.1.4" } } diff --git a/src/package.json b/src/package.json index b29376e..9bd2ba9 100644 --- a/src/package.json +++ b/src/package.json @@ -23,17 +23,17 @@ }, "devDependencies": { "@eslint/js": "^9.39.1", - "@openapitools/openapi-generator-cli": "^2.25.1", + "@openapitools/openapi-generator-cli": "^2.25.2", "@typescript-eslint/eslint-plugin": "^8.47.0", "@typescript-eslint/parser": "^8.47.0", "eslint": "^9.39.1", "husky": "^9.1.7", - "lint-staged": "^16.2.6", + "lint-staged": "^16.2.7", "openapi-generator-cli": "^1.0.0", "openapi-typescript": "^7.10.1", "typescript": "^5.9.3", "typescript-eslint": "^8.47.0", - "vite": "^7.2.2" + "vite": "^7.2.4" }, "dependencies": { "@redocly/cli": "^2.11.1", diff --git a/src/user/package.json b/src/user/package.json index 57a06c5..4f197a3 100644 --- a/src/user/package.json +++ b/src/user/package.json @@ -26,12 +26,12 @@ "fastify": "^5.6.2", "fastify-cli": "^7.4.1", "fastify-plugin": "^5.1.0", - "typebox": "^1.0.53" + "typebox": "^1.0.55" }, "devDependencies": { "@types/node": "^22.19.1", "rollup-plugin-node-externals": "^8.1.2", - "vite": "^7.2.2", + "vite": "^7.2.4", "vite-tsconfig-paths": "^5.1.4" } }