df5423511f
- Socket.io Redis adapter pro sdílený stav přes repliky - graceful shutdown serveru - WATCH/MULTI v updateData pro race-condition-safe aktualizace - lease mechanismus pro push reminder (zabrání duplicitnímu odesílání) - k8s/ manifesty pro testovací kind cluster - Dockerfile: opraven EXPOSE port na 3001 - .gitignore: ignorovány Claude pracovní soubory
58 lines
2.0 KiB
TypeScript
58 lines
2.0 KiB
TypeScript
import { DefaultEventsMap, Server } from "socket.io";
|
|
import { createAdapter } from "@socket.io/redis-adapter";
|
|
import { createClient } from "redis";
|
|
|
|
let io: Server<DefaultEventsMap, DefaultEventsMap, DefaultEventsMap, any>;
|
|
let pubClient: ReturnType<typeof createClient>;
|
|
let subClient: ReturnType<typeof createClient>;
|
|
|
|
export const initWebsocket = (server: any) => {
|
|
io = new Server(server, {
|
|
cors: { origin: "*" },
|
|
transports: ["websocket"],
|
|
});
|
|
io.on("connection", (socket) => {
|
|
console.log(`New client connected: ${socket.id}`);
|
|
|
|
socket.on("join", (login: string) => {
|
|
if (login && typeof login === "string") {
|
|
socket.join(`user:${login}`);
|
|
}
|
|
});
|
|
|
|
socket.on("message", (message) => {
|
|
io.emit("message", message);
|
|
});
|
|
|
|
socket.on("disconnect", () => {
|
|
console.log(`Client disconnected: ${socket.id}`);
|
|
});
|
|
});
|
|
return io;
|
|
};
|
|
|
|
/** Připojí Redis adapter pro cross-pod broadcasting. Volat až po inicializaci Redis klienta. */
|
|
export const initRedisAdapter = async () => {
|
|
const HOST = process.env.REDIS_HOST ?? 'localhost';
|
|
const PORT = process.env.REDIS_PORT ?? 6379;
|
|
const url = `redis://${HOST}:${PORT}`;
|
|
pubClient = createClient({ url }) as ReturnType<typeof createClient>;
|
|
subClient = pubClient.duplicate();
|
|
await Promise.all([pubClient.connect(), subClient.connect()]);
|
|
io.adapter(createAdapter(pubClient as any, subClient as any));
|
|
console.log('Socket.io: Redis adapter connected');
|
|
};
|
|
|
|
/** Zavře pub/sub Redis klienty adaptéru při graceful shutdown. */
|
|
export const shutdownWebsocketClients = async () => {
|
|
await Promise.allSettled([pubClient?.quit(), subClient?.quit()]);
|
|
};
|
|
|
|
export const getWebsocket = () => io;
|
|
|
|
/** Pošle event konkrétnímu přihlášenému uživateli (pokud je připojen). */
|
|
export const emitToUser = (login: string, event: string, data: unknown) => {
|
|
if (!io) return;
|
|
io.to(`user:${login}`).emit(event, data);
|
|
};
|