import { FreshContext } from "$fresh/server.ts"; const chatConnections = new Set(); // HTML sanitization function sanitizeText(text: string): string { return text .replace(/&/g, "&") .replace(//g, ">") .replace(/"/g, """) .replace(/'/g, "'"); } // Process and sanitize message object function processChatMessage(message: string): string { try { const parsed = JSON.parse(message); if (typeof parsed.text === "string") { parsed.text = sanitizeText(parsed.text.trim().slice(0, 2000)); } if (typeof parsed.sender === "string") { parsed.sender = sanitizeText(parsed.sender.trim().slice(0, 50)); } if (!parsed.timestamp) { parsed.timestamp = new Date().toISOString(); } return JSON.stringify(parsed); } catch (error) { console.error("Invalid message format:", error); return JSON.stringify({ text: "Error: Invalid message format", sender: "System", timestamp: new Date().toISOString(), }); } } // Broadcast current user count to all clients function broadcastUserCount() { const count = chatConnections.size; const message = JSON.stringify({ type: "user_count", count: count, }); for (const client of chatConnections) { if (client.readyState === WebSocket.OPEN) { client.send(message); } } } export const handler = (req: Request, _ctx: FreshContext): Response => { const { socket, response } = Deno.upgradeWebSocket(req); socket.onopen = () => { chatConnections.add(socket); console.log(`New connection: ${chatConnections.size} users connected`); broadcastUserCount(); }; // Handle messages socket.onmessage = (event) => { const sanitizedMessage = processChatMessage(event.data); for (const client of chatConnections) { if (client.readyState === WebSocket.OPEN) { client.send(sanitizedMessage); } } }; socket.onclose = () => { chatConnections.delete(socket); console.log(`Connection closed: ${chatConnections.size} users connected`); broadcastUserCount(); }; return response; };