Pepguy's picture
Update app.js
7c9ff5c verified
// 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));