Spaces:
Running
Running
| // server.js (Bun) | |
| const rooms = new Map(); // roomId β Set<ServerWebSocket> | |
| const VALID_TOKEN = "mysecrettoken"; // β your hard-coded secret | |
| Bun.serve({ | |
| port: Number(Bun.env.PORT) || 7860, | |
| fetch(req, server) { | |
| const url = new URL(req.url); | |
| // Handle CORS preflight | |
| if (req.method === "OPTIONS") { | |
| return new Response(null, { status: 204, headers: { | |
| "Access-Control-Allow-Origin": "*", | |
| "Access-Control-Allow-Methods": "GET, POST, OPTIONS", | |
| "Access-Control-Allow-Headers": "Content-Type, Upgrade", | |
| "Access-Control-Allow-Credentials": "true", | |
| }}); | |
| } | |
| // Upgrade to WebSocket if requested | |
| if (req.headers.get("upgrade")?.toLowerCase() === "websocket") { | |
| // **AUTH CHECK**: require ?token=VALID_TOKEN | |
| const clientToken = url.searchParams.get("token"); | |
| if (clientToken !== VALID_TOKEN) { | |
| return new Response("Unauthorized", { status: 401 }); | |
| } | |
| // Accept the upgrade with CORS headers on the handshake | |
| const upgradeRes = server.upgrade(req, { | |
| headers: { | |
| "Access-Control-Allow-Origin": "*", | |
| "Access-Control-Allow-Credentials": "true", | |
| }, | |
| }); | |
| return upgradeRes ?? new Response("Upgrade failed", { status: 500 }); | |
| } | |
| // β¦ your existing HTML serve code unchanged β¦ | |
| if (url.pathname === "/") { | |
| return new Response(`<!doctype html> | |
| <html><head><title>Dubem Realtime Rooms</title><meta charset="utf-8"/></head><body> | |
| <h2>Join a Room & Send Messages</h2> | |
| <label><input type="checkbox" id="authToggle" checked> Send Auth Token?</label><br/> | |
| <input id="room" placeholder="Room ID"/><button onclick="joinRoom()">Join Room</button> | |
| <div id="log"></div> | |
| <input id="msg" placeholder="Type a message" style="width:80%;"/><button onclick="sendMsg()">Send</button> | |
| <script> | |
| const VALID_TOKEN = "mysecrettoken"; // must match backend | |
| let includeAuth = true; //false; | |
| document.getElementById("authToggle").addEventListener("change", e => { | |
| includeAuth = e.target.checked; | |
| connect(); // reconnect on toggle | |
| }); | |
| let ws, currentRoom; | |
| function connect() { | |
| if (ws) ws.close(); | |
| const scheme = location.protocol === 'https:' ? 'wss' : 'ws'; | |
| let socketUrl = scheme + '://' + location.host + '/'; | |
| if (includeAuth) socketUrl += "?token=" + VALID_TOKEN; | |
| ws = new WebSocket(socketUrl); | |
| ws.onopen = () => log('π Connected'); | |
| ws.onmessage = ev => { const m = JSON.parse(ev.data); log('['+m.roomId+'] '+m.message); }; | |
| ws.onerror = () => log('β οΈ WebSocket error'); | |
| ws.onclose = c => log('β Disconnected (code='+c.code+')'); | |
| } | |
| window.addEventListener('load', connect); | |
| function joinRoom(){ | |
| const id = document.getElementById('room').value.trim(); | |
| if(!id) return alert('Enter room ID'); | |
| ws.send(JSON.stringify({ action:'join', roomId:id })); | |
| currentRoom = id; | |
| log('β‘οΈ Joined '+id); | |
| } | |
| function sendMsg(){ | |
| const t = document.getElementById('msg').value.trim(); | |
| if(!t) return; | |
| if(!currentRoom) return alert('Join a room first'); | |
| ws.send(JSON.stringify({ action:'post', roomId:currentRoom, message:t })); | |
| document.getElementById('msg').value = ''; | |
| } | |
| function log(txt){ | |
| const e = document.getElementById('log'); | |
| e.innerHTML += '<div>'+txt+'</div>'; | |
| e.scrollTop = e.scrollHeight; | |
| } | |
| </script> | |
| </body></html>`, { | |
| headers: { | |
| "Content-Type": "text/html; charset=utf-8", | |
| "Access-Control-Allow-Origin": "*", | |
| } | |
| }); | |
| } | |
| return new Response("Not Found", { | |
| status: 404, | |
| headers: { "Access-Control-Allow-Origin": "*" }, | |
| }); | |
| }, | |
| websocket: { | |
| message(ws, raw) { | |
| let msg; | |
| try { msg = JSON.parse(raw); } catch { return; } | |
| if (msg.action === "join" && msg.roomId) { | |
| for (const set of rooms.values()) set.delete(ws); | |
| let set = rooms.get(msg.roomId); | |
| if (!set) { set = new Set(); rooms.set(msg.roomId, set); } | |
| set.add(ws); | |
| } | |
| if (msg.action === "post" && msg.roomId && msg.message) { | |
| const set = rooms.get(msg.roomId); | |
| if (!set) return; | |
| const payload = JSON.stringify({ | |
| roomId: msg.roomId, | |
| message: msg.message, | |
| timestamp: Date.now(), | |
| }); | |
| for (const client of set) { | |
| if (client.readyState === 1) client.send(payload); | |
| } | |
| } | |
| }, | |
| close(ws) { | |
| for (const set of rooms.values()) set.delete(ws); | |
| } | |
| } | |
| }); | |
| console.log("β Bun realtime server running on port " + (Bun.env.PORT || 7860)); |