89 lines
2.8 KiB
JavaScript
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);
|
|
}
|
|
});
|
|
}
|
|
}
|