94 lines
3.0 KiB
JavaScript
94 lines
3.0 KiB
JavaScript
const { WebSocket, WebSocketServer } = require('ws');
|
|
const { z } = require("zod")
|
|
const jwt = require('jsonwebtoken');
|
|
import ForumHandler from "./handlers/ForumHandler.js"
|
|
|
|
export default class Socket {
|
|
wss;
|
|
messageSchema = z.object({
|
|
id: z.string(),
|
|
app: z.string(),
|
|
operation: z.string().optional(),
|
|
msg: z.union([
|
|
z.object({}).passthrough(), // allows any object
|
|
z.array(z.any()) // allows any array
|
|
])
|
|
}).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) throw new Error("No auth token");
|
|
const payload = jwt.verify(token, process.env.JWT_SECRET);
|
|
ws.userEmail = payload.email;
|
|
|
|
ws.on('message', (msg) => {
|
|
this.handleMessage(msg, 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) => {
|
|
try {
|
|
const text = msg.toString();
|
|
const req = JSON.parse(text);
|
|
if(!this.messageSchema.safeParse(req).success) throw new Error("Socket.handleMessage: Incorrectly formatted incoming ws message!")
|
|
|
|
let responseData;
|
|
switch (req.app) {
|
|
case "FORUM":
|
|
responseData = ForumHandler.handle(req.operation, req.msg, ws)
|
|
break;
|
|
|
|
default:
|
|
console.error("unknown ws message")
|
|
}
|
|
|
|
let response = {
|
|
...req
|
|
}
|
|
response.msg = responseData
|
|
|
|
if(!this.messageSchema.safeParse(response).success) throw new Error("Socket.handleMessage: Incorrectly formatted outgoing ws message!")
|
|
ws.send(JSON.stringify(response))
|
|
|
|
} catch (e) {
|
|
console.error("Invalid WS message:", e);
|
|
}
|
|
}
|
|
|
|
broadcast(event) {
|
|
if (!this.wss) return;
|
|
|
|
let message = JSON.stringify(event)
|
|
|
|
this.wss.clients.forEach(ws => {
|
|
if (ws.readyState === WebSocket.OPEN) {
|
|
ws.send(message);
|
|
}
|
|
});
|
|
}
|
|
}
|