File size: 4,690 Bytes
072db80
67d31ad
072db80
7fe989f
67d31ad
 
7fe989f
67d31ad
d3bdf51
 
b977bfa
 
072db80
 
 
 
 
 
b977bfa
 
67d31ad
be81bd0
072db80
 
 
 
 
 
b977bfa
 
 
 
 
 
 
 
be81bd0
 
072db80
be81bd0
 
072db80
be81bd0
7c9ff5c
072db80
7fe989f
072db80
7fe989f
072db80
7c9ff5c
072db80
 
 
2fe2d50
072db80
 
 
 
 
 
 
 
 
 
 
 
 
 
 
be81bd0
072db80
be81bd0
 
072db80
 
7fe989f
be81bd0
072db80
be81bd0
 
 
 
7fe989f
be81bd0
072db80
b977bfa
 
be81bd0
 
 
b977bfa
 
 
 
be81bd0
904caf7
d3bdf51
b977bfa
 
 
 
d3bdf51
67d31ad
 
 
 
 
 
 
 
072db80
67d31ad
 
 
 
 
 
 
 
 
 
 
072db80
67d31ad
 
 
 
 
 
 
7fe989f
6d4d78a
072db80
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
// 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));