Files
Console/server/ws/Clients.js
2025-12-26 06:55:32 -06:00

89 lines
2.8 KiB
JavaScript

import { WebSocket, WebSocketServer } from 'ws'
import { z } from "zod"
import jwt from 'jsonwebtoken'
import handler from "./handler.js"
import chalk from 'chalk'
export default class Clients {
wss;
messageSchema = z.object({
id: z.string(),
op: z.string().optional(),
msg: z.union([
z.object({}).passthrough(), // allows any object
z.array(z.any()) // allows any array
]).optional()
})
.superRefine((data, ctx) => {
if (data.op !== "GET" && data.msg === undefined) {
ctx.addIssue({
code: z.ZodIssueCode.custom,
path: ["msg"],
message: "msg is required when operation is not GET"
})
}
})
.strict()
constructor(server) {
this.wss = new WebSocketServer({ server });
this.wss.on('connection', (ws, req) => {
console.log('✅ New WebSocket client connected');
function parseCookies(cookieHeader = "") {
return Object.fromEntries(
cookieHeader.split(";").map(c => {
const [key, ...v] = c.trim().split("=");
return [key, decodeURIComponent(v.join("="))];
})
);
}
const cookies = parseCookies(req.headers.cookie);
const token = cookies.auth_token;
if (!token) {
console.error("No auth token");
return
}
const payload = jwt.verify(token, process.env.JWT_SECRET);
ws.userEmail = payload.email;
ws.on('message', (msg) => {
const text = msg.toString("utf8");
const data = JSON.parse(text);
this.handleMessage(data, ws);
});
ws.on('close', () => {
console.log('Client disconnected');
});
});
console.log('WebSocket server initialized');
}
// Build a system where the ws obj is updated every time on navigate, so it already has context
// this way, we can only send broadcast messages to clients that actually have that app / subapp open
handleMessage = (msg, ws) => {
if(!this.messageSchema.safeParse(msg).success) {
console.log(chalk.red("Socket.handleMessage: Incoming ws message has incorrect format! ", this.messageSchema.safeParse(msg).error))
return
}
console.log("websocket message received: ", msg)
handler.handle(msg)
}
broadcast(event) {
if (!this.wss) return;
let message = JSON.stringify(event)
this.wss.clients.forEach(ws => {
if (ws.readyState === WebSocket.OPEN) {
ws.send(message);
}
});
}
}