server: Socket.IO realtime layer

Set up authenticated Socket.IO server; broadcasts market updates,
world events, and mission notifications to connected clients in
real time.

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
This commit is contained in:
2026-06-09 23:42:09 +02:00
parent a998bb9704
commit 0cd396ac1d
+51
View File
@@ -0,0 +1,51 @@
import type { FastifyInstance } from 'fastify';
import { Server } from 'socket.io';
let io: Server | null = null;
/**
* Inizializza Socket.IO sullo stesso server HTTP di Fastify (path /ws).
* L'handshake richiede un JWT valido in `auth.token`.
*/
export function initSocket(app: FastifyInstance): Server {
io = new Server(app.server, {
path: '/ws',
cors: { origin: '*' },
});
io.use((socket, next) => {
const token = socket.handshake.auth?.token as string | undefined;
if (!token) {
next(new Error('UNAUTHORIZED'));
return;
}
try {
const payload = app.jwt.verify<{ sub: string; playerId: string }>(token);
socket.data.playerId = payload.playerId;
next();
} catch {
next(new Error('UNAUTHORIZED'));
}
});
io.on('connection', (socket) => {
const playerId = socket.data.playerId as string;
void socket.join(`player:${playerId}`);
socket.on('client:ping', () => {
socket.emit('player:notification', { type: 'pong', at: new Date().toISOString() });
});
});
return io;
}
/** Notifica un singolo giocatore (room player:{id}). */
export function emitToPlayer(playerId: string, event: string, payload: unknown): void {
io?.to(`player:${playerId}`).emit(event, payload);
}
/** Notifica tutti i client connessi. */
export function emitGlobal(event: string, payload: unknown): void {
io?.emit(event, payload);
}