bitchat-node 0.1.0 → 0.1.2
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/README.md +94 -23
- package/dist/client.d.ts +1 -0
- package/dist/client.d.ts.map +1 -1
- package/dist/client.js +143 -28
- package/dist/client.js.map +1 -1
- package/dist/debug.d.ts +39 -0
- package/dist/debug.d.ts.map +1 -0
- package/dist/debug.js +89 -0
- package/dist/debug.js.map +1 -0
- package/dist/index.d.ts +2 -0
- package/dist/index.d.ts.map +1 -1
- package/dist/index.js +4 -0
- package/dist/index.js.map +1 -1
- package/dist/mesh/router.d.ts.map +1 -1
- package/dist/mesh/router.js +9 -1
- package/dist/mesh/router.js.map +1 -1
- package/dist/protocol/binary.d.ts +1 -1
- package/dist/protocol/binary.d.ts.map +1 -1
- package/dist/protocol/binary.js +2 -2
- package/dist/protocol/binary.js.map +1 -1
- package/dist/session/manager.d.ts.map +1 -1
- package/dist/session/manager.js +18 -1
- package/dist/session/manager.js.map +1 -1
- package/dist/transport/ble.d.ts +15 -0
- package/dist/transport/ble.d.ts.map +1 -1
- package/dist/transport/ble.js +63 -16
- package/dist/transport/ble.js.map +1 -1
- package/dist/ui/html.d.ts +5 -0
- package/dist/ui/html.d.ts.map +1 -0
- package/dist/ui/html.js +506 -0
- package/dist/ui/html.js.map +1 -0
- package/dist/ui/server.d.ts +5 -1
- package/dist/ui/server.d.ts.map +1 -1
- package/dist/ui/server.js +61 -255
- package/dist/ui/server.js.map +1 -1
- package/package.json +3 -2
- package/src/client.ts +159 -34
- package/src/debug.ts +119 -0
- package/src/index.ts +11 -0
- package/src/mesh/router.ts +11 -1
- package/src/protocol/binary.ts +2 -2
- package/src/session/manager.ts +19 -1
- package/src/transport/ble.ts +70 -16
- package/src/ui/html.ts +506 -0
- package/src/ui/server.ts +78 -258
package/src/ui/server.ts
CHANGED
|
@@ -1,257 +1,14 @@
|
|
|
1
1
|
/**
|
|
2
2
|
* Bitchat Web UI Server
|
|
3
|
-
*
|
|
3
|
+
* Enhanced with debug panel and session monitoring
|
|
4
4
|
*/
|
|
5
5
|
|
|
6
6
|
import { createServer, type IncomingMessage, type ServerResponse } from 'node:http';
|
|
7
7
|
import { WebSocket, WebSocketServer } from 'ws';
|
|
8
8
|
import type { BitchatClient } from '../client.js';
|
|
9
9
|
import type { ChatMessage, PeerInfo } from '../protocol/types.js';
|
|
10
|
-
|
|
11
|
-
|
|
12
|
-
<html>
|
|
13
|
-
<head>
|
|
14
|
-
<meta charset="UTF-8">
|
|
15
|
-
<meta name="viewport" content="width=device-width, initial-scale=1.0">
|
|
16
|
-
<title>Bitchat Node</title>
|
|
17
|
-
<style>
|
|
18
|
-
* { box-sizing: border-box; margin: 0; padding: 0; }
|
|
19
|
-
body {
|
|
20
|
-
font-family: -apple-system, BlinkMacSystemFont, 'Segoe UI', Roboto, sans-serif;
|
|
21
|
-
background: #1a1a2e; color: #eee; height: 100vh; display: flex; flex-direction: column;
|
|
22
|
-
}
|
|
23
|
-
header {
|
|
24
|
-
background: #16213e; padding: 16px 20px; border-bottom: 1px solid #0f3460;
|
|
25
|
-
display: flex; justify-content: space-between; align-items: center;
|
|
26
|
-
}
|
|
27
|
-
header h1 { font-size: 18px; font-weight: 600; }
|
|
28
|
-
.status { font-size: 12px; color: #888; }
|
|
29
|
-
.status.connected { color: #4ade80; }
|
|
30
|
-
.peers { font-size: 12px; color: #60a5fa; margin-left: 16px; }
|
|
31
|
-
main { flex: 1; display: flex; overflow: hidden; }
|
|
32
|
-
.messages {
|
|
33
|
-
flex: 1; overflow-y: auto; padding: 16px; display: flex; flex-direction: column; gap: 8px;
|
|
34
|
-
}
|
|
35
|
-
.message {
|
|
36
|
-
background: #16213e; padding: 12px 16px; border-radius: 12px; max-width: 80%;
|
|
37
|
-
animation: fadeIn 0.2s ease;
|
|
38
|
-
}
|
|
39
|
-
@keyframes fadeIn { from { opacity: 0; transform: translateY(8px); } }
|
|
40
|
-
.message.mine { background: #0f3460; align-self: flex-end; }
|
|
41
|
-
.message.private { border-left: 3px solid #f472b6; }
|
|
42
|
-
.message .meta { font-size: 11px; color: #888; margin-bottom: 4px; }
|
|
43
|
-
.message .meta .nickname { color: #60a5fa; font-weight: 500; }
|
|
44
|
-
.message .meta .private-badge { color: #f472b6; margin-left: 8px; }
|
|
45
|
-
.message .content { line-height: 1.4; word-wrap: break-word; }
|
|
46
|
-
.system {
|
|
47
|
-
text-align: center; font-size: 12px; color: #666; padding: 8px;
|
|
48
|
-
}
|
|
49
|
-
footer { background: #16213e; padding: 16px; border-top: 1px solid #0f3460; }
|
|
50
|
-
.input-row { display: flex; gap: 8px; }
|
|
51
|
-
input[type="text"] {
|
|
52
|
-
flex: 1; background: #1a1a2e; border: 1px solid #0f3460; border-radius: 8px;
|
|
53
|
-
padding: 12px 16px; color: #eee; font-size: 14px; outline: none;
|
|
54
|
-
}
|
|
55
|
-
input[type="text"]:focus { border-color: #3b82f6; }
|
|
56
|
-
input[type="text"]::placeholder { color: #555; }
|
|
57
|
-
button {
|
|
58
|
-
background: #3b82f6; color: white; border: none; border-radius: 8px;
|
|
59
|
-
padding: 12px 24px; font-size: 14px; font-weight: 500; cursor: pointer;
|
|
60
|
-
transition: background 0.2s;
|
|
61
|
-
}
|
|
62
|
-
button:hover { background: #2563eb; }
|
|
63
|
-
button:disabled { background: #374151; cursor: not-allowed; }
|
|
64
|
-
.peers-panel {
|
|
65
|
-
width: 200px; background: #16213e; border-left: 1px solid #0f3460;
|
|
66
|
-
padding: 16px; overflow-y: auto;
|
|
67
|
-
}
|
|
68
|
-
.peers-panel h3 { font-size: 12px; color: #888; margin-bottom: 12px; text-transform: uppercase; }
|
|
69
|
-
.peer {
|
|
70
|
-
padding: 8px 12px; background: #1a1a2e; border-radius: 6px; margin-bottom: 8px;
|
|
71
|
-
font-size: 13px; cursor: pointer; transition: background 0.2s;
|
|
72
|
-
}
|
|
73
|
-
.peer:hover { background: #0f3460; }
|
|
74
|
-
.peer .peer-id { font-size: 10px; color: #666; margin-top: 2px; font-family: monospace; }
|
|
75
|
-
.peer.selected { background: #3b82f6; }
|
|
76
|
-
</style>
|
|
77
|
-
</head>
|
|
78
|
-
<body>
|
|
79
|
-
<header>
|
|
80
|
-
<div>
|
|
81
|
-
<h1>🔗 Bitchat Node</h1>
|
|
82
|
-
<span class="status" id="status">Connecting...</span>
|
|
83
|
-
<span class="peers" id="peer-count"></span>
|
|
84
|
-
</div>
|
|
85
|
-
<div id="my-info" style="font-size: 12px; color: #888;"></div>
|
|
86
|
-
</header>
|
|
87
|
-
<main>
|
|
88
|
-
<div class="messages" id="messages"></div>
|
|
89
|
-
<div class="peers-panel">
|
|
90
|
-
<h3>Peers</h3>
|
|
91
|
-
<div id="peers-list"></div>
|
|
92
|
-
</div>
|
|
93
|
-
</main>
|
|
94
|
-
<footer>
|
|
95
|
-
<div class="input-row">
|
|
96
|
-
<input type="text" id="input" placeholder="Type a message..." autocomplete="off">
|
|
97
|
-
<button id="send">Send</button>
|
|
98
|
-
</div>
|
|
99
|
-
</footer>
|
|
100
|
-
<script>
|
|
101
|
-
const messagesEl = document.getElementById('messages');
|
|
102
|
-
const inputEl = document.getElementById('input');
|
|
103
|
-
const sendBtn = document.getElementById('send');
|
|
104
|
-
const statusEl = document.getElementById('status');
|
|
105
|
-
const peerCountEl = document.getElementById('peer-count');
|
|
106
|
-
const peersListEl = document.getElementById('peers-list');
|
|
107
|
-
const myInfoEl = document.getElementById('my-info');
|
|
108
|
-
|
|
109
|
-
let ws;
|
|
110
|
-
let myPeerID = '';
|
|
111
|
-
let selectedPeer = null;
|
|
112
|
-
const peers = new Map();
|
|
113
|
-
|
|
114
|
-
function connect() {
|
|
115
|
-
ws = new WebSocket('ws://' + location.host + '/ws');
|
|
116
|
-
|
|
117
|
-
ws.onopen = () => {
|
|
118
|
-
statusEl.textContent = 'Connected';
|
|
119
|
-
statusEl.className = 'status connected';
|
|
120
|
-
};
|
|
121
|
-
|
|
122
|
-
ws.onclose = () => {
|
|
123
|
-
statusEl.textContent = 'Disconnected';
|
|
124
|
-
statusEl.className = 'status';
|
|
125
|
-
setTimeout(connect, 2000);
|
|
126
|
-
};
|
|
127
|
-
|
|
128
|
-
ws.onmessage = (e) => {
|
|
129
|
-
const msg = JSON.parse(e.data);
|
|
130
|
-
handleMessage(msg);
|
|
131
|
-
};
|
|
132
|
-
}
|
|
133
|
-
|
|
134
|
-
function handleMessage(msg) {
|
|
135
|
-
switch (msg.type) {
|
|
136
|
-
case 'init':
|
|
137
|
-
myPeerID = msg.peerID;
|
|
138
|
-
myInfoEl.textContent = msg.nickname + ' · ' + msg.peerID.slice(0, 8) + '...';
|
|
139
|
-
break;
|
|
140
|
-
|
|
141
|
-
case 'message':
|
|
142
|
-
addMessage(msg.message);
|
|
143
|
-
break;
|
|
144
|
-
|
|
145
|
-
case 'peer:connected':
|
|
146
|
-
peers.set(msg.peer.peerID, msg.peer);
|
|
147
|
-
updatePeersList();
|
|
148
|
-
addSystem(msg.peer.nickname + ' joined the mesh');
|
|
149
|
-
break;
|
|
150
|
-
|
|
151
|
-
case 'peer:disconnected':
|
|
152
|
-
const peer = peers.get(msg.peerID);
|
|
153
|
-
peers.delete(msg.peerID);
|
|
154
|
-
updatePeersList();
|
|
155
|
-
if (peer) addSystem(peer.nickname + ' left the mesh');
|
|
156
|
-
break;
|
|
157
|
-
|
|
158
|
-
case 'peers':
|
|
159
|
-
peers.clear();
|
|
160
|
-
msg.peers.forEach(p => peers.set(p.peerID, p));
|
|
161
|
-
updatePeersList();
|
|
162
|
-
break;
|
|
163
|
-
|
|
164
|
-
case 'sent':
|
|
165
|
-
// Could update delivery status
|
|
166
|
-
break;
|
|
167
|
-
|
|
168
|
-
case 'error':
|
|
169
|
-
addSystem('Error: ' + msg.error);
|
|
170
|
-
break;
|
|
171
|
-
}
|
|
172
|
-
}
|
|
173
|
-
|
|
174
|
-
function addMessage(msg) {
|
|
175
|
-
const div = document.createElement('div');
|
|
176
|
-
div.className = 'message' + (msg.sender === myPeerID ? ' mine' : '') + (msg.isPrivate ? ' private' : '');
|
|
177
|
-
div.innerHTML =
|
|
178
|
-
'<div class="meta">' +
|
|
179
|
-
'<span class="nickname">' + escapeHtml(msg.senderNickname) + '</span> · ' +
|
|
180
|
-
new Date(msg.timestamp).toLocaleTimeString() +
|
|
181
|
-
(msg.isPrivate ? '<span class="private-badge">private</span>' : '') +
|
|
182
|
-
'</div>' +
|
|
183
|
-
'<div class="content">' + escapeHtml(msg.content) + '</div>';
|
|
184
|
-
messagesEl.appendChild(div);
|
|
185
|
-
messagesEl.scrollTop = messagesEl.scrollHeight;
|
|
186
|
-
}
|
|
187
|
-
|
|
188
|
-
function addSystem(text) {
|
|
189
|
-
const div = document.createElement('div');
|
|
190
|
-
div.className = 'system';
|
|
191
|
-
div.textContent = text;
|
|
192
|
-
messagesEl.appendChild(div);
|
|
193
|
-
messagesEl.scrollTop = messagesEl.scrollHeight;
|
|
194
|
-
}
|
|
195
|
-
|
|
196
|
-
function updatePeersList() {
|
|
197
|
-
peerCountEl.textContent = peers.size + ' peer' + (peers.size !== 1 ? 's' : '');
|
|
198
|
-
peersListEl.innerHTML = '';
|
|
199
|
-
peers.forEach((peer, id) => {
|
|
200
|
-
const div = document.createElement('div');
|
|
201
|
-
div.className = 'peer' + (selectedPeer === id ? ' selected' : '');
|
|
202
|
-
div.innerHTML =
|
|
203
|
-
'<div>' + escapeHtml(peer.nickname) + '</div>' +
|
|
204
|
-
'<div class="peer-id">' + id.slice(0, 12) + '...</div>';
|
|
205
|
-
div.onclick = () => {
|
|
206
|
-
selectedPeer = selectedPeer === id ? null : id;
|
|
207
|
-
updatePeersList();
|
|
208
|
-
inputEl.placeholder = selectedPeer ? 'Private message to ' + peer.nickname + '...' : 'Type a message...';
|
|
209
|
-
};
|
|
210
|
-
peersListEl.appendChild(div);
|
|
211
|
-
});
|
|
212
|
-
}
|
|
213
|
-
|
|
214
|
-
function send() {
|
|
215
|
-
const text = inputEl.value.trim();
|
|
216
|
-
if (!text) return;
|
|
217
|
-
|
|
218
|
-
if (!ws || ws.readyState !== WebSocket.OPEN) {
|
|
219
|
-
addSystem('Not connected - please wait');
|
|
220
|
-
return;
|
|
221
|
-
}
|
|
222
|
-
|
|
223
|
-
// Show our own message immediately
|
|
224
|
-
addMessage({
|
|
225
|
-
id: Date.now().toString(),
|
|
226
|
-
sender: myPeerID,
|
|
227
|
-
senderNickname: 'me',
|
|
228
|
-
content: text,
|
|
229
|
-
timestamp: new Date().toISOString(),
|
|
230
|
-
isPrivate: !!selectedPeer
|
|
231
|
-
});
|
|
232
|
-
|
|
233
|
-
ws.send(JSON.stringify({
|
|
234
|
-
type: 'send',
|
|
235
|
-
text: text,
|
|
236
|
-
to: selectedPeer || null
|
|
237
|
-
}));
|
|
238
|
-
|
|
239
|
-
inputEl.value = '';
|
|
240
|
-
}
|
|
241
|
-
|
|
242
|
-
function escapeHtml(text) {
|
|
243
|
-
const div = document.createElement('div');
|
|
244
|
-
div.textContent = text;
|
|
245
|
-
return div.innerHTML;
|
|
246
|
-
}
|
|
247
|
-
|
|
248
|
-
sendBtn.onclick = send;
|
|
249
|
-
inputEl.onkeydown = (e) => { if (e.key === 'Enter') send(); };
|
|
250
|
-
|
|
251
|
-
connect();
|
|
252
|
-
</script>
|
|
253
|
-
</body>
|
|
254
|
-
</html>`;
|
|
10
|
+
import { HTML } from './html.js';
|
|
11
|
+
import { debugLog, type DebugEvent, type SessionState } from '../debug.js';
|
|
255
12
|
|
|
256
13
|
export interface UIServerConfig {
|
|
257
14
|
port: number;
|
|
@@ -268,10 +25,17 @@ interface StoredMessage {
|
|
|
268
25
|
timestamp: number;
|
|
269
26
|
}
|
|
270
27
|
|
|
28
|
+
// Re-export for convenience
|
|
29
|
+
export type { DebugEvent, SessionState } from '../debug.js';
|
|
30
|
+
|
|
271
31
|
/**
|
|
272
32
|
* Start a web UI server for the Bitchat client
|
|
273
33
|
*/
|
|
274
|
-
export function startUIServer(client: BitchatClient, config: UIServerConfig): {
|
|
34
|
+
export function startUIServer(client: BitchatClient, config: UIServerConfig): {
|
|
35
|
+
stop: () => void;
|
|
36
|
+
emitDebug: (event: DebugEvent) => void;
|
|
37
|
+
updateSession: (state: SessionState) => void;
|
|
38
|
+
} {
|
|
275
39
|
const { port } = config;
|
|
276
40
|
const clients = new Set<WebSocket>();
|
|
277
41
|
|
|
@@ -279,6 +43,13 @@ export function startUIServer(client: BitchatClient, config: UIServerConfig): {
|
|
|
279
43
|
const messageStore: StoredMessage[] = [];
|
|
280
44
|
const MAX_MESSAGES = 100;
|
|
281
45
|
|
|
46
|
+
// Debug log store (keep last 500 entries)
|
|
47
|
+
const debugStore: DebugEvent[] = [];
|
|
48
|
+
const MAX_DEBUG = 500;
|
|
49
|
+
|
|
50
|
+
// Session states
|
|
51
|
+
const sessionStates = new Map<string, SessionState>();
|
|
52
|
+
|
|
282
53
|
// Registered webhooks
|
|
283
54
|
const webhooks: string[] = [];
|
|
284
55
|
if (config.webhookUrl) {
|
|
@@ -322,6 +93,35 @@ export function startUIServer(client: BitchatClient, config: UIServerConfig): {
|
|
|
322
93
|
}
|
|
323
94
|
};
|
|
324
95
|
|
|
96
|
+
// Broadcast to all connected WebSocket clients
|
|
97
|
+
const broadcast = (msg: object) => {
|
|
98
|
+
const data = JSON.stringify(msg);
|
|
99
|
+
clients.forEach((ws) => {
|
|
100
|
+
if (ws.readyState === WebSocket.OPEN) {
|
|
101
|
+
ws.send(data);
|
|
102
|
+
}
|
|
103
|
+
});
|
|
104
|
+
};
|
|
105
|
+
|
|
106
|
+
// Emit debug event to UI
|
|
107
|
+
const emitDebug = (event: DebugEvent) => {
|
|
108
|
+
debugStore.push(event);
|
|
109
|
+
if (debugStore.length > MAX_DEBUG) {
|
|
110
|
+
debugStore.shift();
|
|
111
|
+
}
|
|
112
|
+
broadcast({ type: 'debug', ...event });
|
|
113
|
+
};
|
|
114
|
+
|
|
115
|
+
// Update session state in UI
|
|
116
|
+
const updateSession = (state: SessionState) => {
|
|
117
|
+
sessionStates.set(state.peerID, state);
|
|
118
|
+
broadcast({ type: 'session', ...state });
|
|
119
|
+
};
|
|
120
|
+
|
|
121
|
+
// Connect to global debug logger
|
|
122
|
+
debugLog.on('debug', emitDebug);
|
|
123
|
+
debugLog.on('session', updateSession);
|
|
124
|
+
|
|
325
125
|
// HTTP server with REST API
|
|
326
126
|
const server = createServer(async (req: IncomingMessage, res: ServerResponse) => {
|
|
327
127
|
const url = new URL(req.url ?? '/', `http://localhost:${port}`);
|
|
@@ -357,7 +157,7 @@ export function startUIServer(client: BitchatClient, config: UIServerConfig): {
|
|
|
357
157
|
peerID: p.peerID.toHex(),
|
|
358
158
|
nickname: p.nickname,
|
|
359
159
|
isConnected: p.isConnected,
|
|
360
|
-
lastSeen: Date.now(),
|
|
160
|
+
lastSeen: Date.now(),
|
|
361
161
|
}));
|
|
362
162
|
sendJson(res, 200, peers);
|
|
363
163
|
return;
|
|
@@ -370,6 +170,18 @@ export function startUIServer(client: BitchatClient, config: UIServerConfig): {
|
|
|
370
170
|
return;
|
|
371
171
|
}
|
|
372
172
|
|
|
173
|
+
if (path === '/api/debug' && method === 'GET') {
|
|
174
|
+
const since = parseInt(url.searchParams.get('since') ?? '0', 10);
|
|
175
|
+
const events = debugStore.filter((e) => e.time > since);
|
|
176
|
+
sendJson(res, 200, events);
|
|
177
|
+
return;
|
|
178
|
+
}
|
|
179
|
+
|
|
180
|
+
if (path === '/api/sessions' && method === 'GET') {
|
|
181
|
+
sendJson(res, 200, Array.from(sessionStates.values()));
|
|
182
|
+
return;
|
|
183
|
+
}
|
|
184
|
+
|
|
373
185
|
if (path === '/api/send' && method === 'POST') {
|
|
374
186
|
const body = await parseBody(req);
|
|
375
187
|
const { type, text, recipientPeerID } = body as {
|
|
@@ -460,6 +272,11 @@ export function startUIServer(client: BitchatClient, config: UIServerConfig): {
|
|
|
460
272
|
})
|
|
461
273
|
);
|
|
462
274
|
|
|
275
|
+
// Send current sessions
|
|
276
|
+
sessionStates.forEach((state) => {
|
|
277
|
+
ws.send(JSON.stringify({ type: 'session', ...state }));
|
|
278
|
+
});
|
|
279
|
+
|
|
463
280
|
ws.on('message', async (data: Buffer) => {
|
|
464
281
|
console.log('[WS] Received:', data.toString().slice(0, 100));
|
|
465
282
|
try {
|
|
@@ -481,6 +298,11 @@ export function startUIServer(client: BitchatClient, config: UIServerConfig): {
|
|
|
481
298
|
} catch (error) {
|
|
482
299
|
console.error('[WS] Send error:', (error as Error).message);
|
|
483
300
|
ws.send(JSON.stringify({ type: 'error', error: (error as Error).message }));
|
|
301
|
+
emitDebug({
|
|
302
|
+
category: 'error',
|
|
303
|
+
text: (error as Error).message,
|
|
304
|
+
time: Date.now(),
|
|
305
|
+
});
|
|
484
306
|
}
|
|
485
307
|
});
|
|
486
308
|
|
|
@@ -491,15 +313,6 @@ export function startUIServer(client: BitchatClient, config: UIServerConfig): {
|
|
|
491
313
|
});
|
|
492
314
|
|
|
493
315
|
// Forward client events to WebSocket clients
|
|
494
|
-
const broadcast = (msg: object) => {
|
|
495
|
-
const data = JSON.stringify(msg);
|
|
496
|
-
clients.forEach((ws) => {
|
|
497
|
-
if (ws.readyState === WebSocket.OPEN) {
|
|
498
|
-
ws.send(data);
|
|
499
|
-
}
|
|
500
|
-
});
|
|
501
|
-
};
|
|
502
|
-
|
|
503
316
|
client.on('message', (message: ChatMessage) => {
|
|
504
317
|
// Store message for polling
|
|
505
318
|
const storedMessage: StoredMessage = {
|
|
@@ -513,7 +326,7 @@ export function startUIServer(client: BitchatClient, config: UIServerConfig): {
|
|
|
513
326
|
|
|
514
327
|
messageStore.push(storedMessage);
|
|
515
328
|
if (messageStore.length > MAX_MESSAGES) {
|
|
516
|
-
messageStore.shift();
|
|
329
|
+
messageStore.shift();
|
|
517
330
|
}
|
|
518
331
|
|
|
519
332
|
// Notify webhooks
|
|
@@ -554,16 +367,23 @@ export function startUIServer(client: BitchatClient, config: UIServerConfig): {
|
|
|
554
367
|
type: 'peer:disconnected',
|
|
555
368
|
peerID: peerID.toHex(),
|
|
556
369
|
});
|
|
370
|
+
// Remove session state
|
|
371
|
+
sessionStates.delete(peerID.toHex());
|
|
557
372
|
});
|
|
558
373
|
|
|
559
374
|
server.listen(port, () => {
|
|
560
375
|
console.log(`Bitchat UI: http://localhost:${port}`);
|
|
376
|
+
console.log(`Debug panel: http://localhost:${port} → Debug tab`);
|
|
561
377
|
});
|
|
562
378
|
|
|
563
379
|
return {
|
|
564
380
|
stop: () => {
|
|
381
|
+
debugLog.off('debug', emitDebug);
|
|
382
|
+
debugLog.off('session', updateSession);
|
|
565
383
|
wss.close();
|
|
566
384
|
server.close();
|
|
567
385
|
},
|
|
386
|
+
emitDebug,
|
|
387
|
+
updateSession,
|
|
568
388
|
};
|
|
569
389
|
}
|