sonic-ws 1.3.0-min → 1.3.0
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/dist/ws/server/SonicWSServer.js +395 -396
- package/package.json +1 -1
|
@@ -337,405 +337,404 @@ class SonicWSServer {
|
|
|
337
337
|
data.password ??= "";
|
|
338
338
|
if (data.port < 0 || data.port >= 65536)
|
|
339
339
|
throw new Error("Port out of range!");
|
|
340
|
-
|
|
341
|
-
|
|
342
|
-
|
|
343
|
-
|
|
344
|
-
|
|
345
|
-
|
|
346
|
-
|
|
347
|
-
|
|
348
|
-
|
|
349
|
-
|
|
350
|
-
|
|
351
|
-
|
|
352
|
-
|
|
353
|
-
|
|
354
|
-
|
|
355
|
-
|
|
356
|
-
|
|
357
|
-
|
|
358
|
-
|
|
359
|
-
|
|
360
|
-
|
|
361
|
-
|
|
362
|
-
|
|
363
|
-
|
|
364
|
-
|
|
365
|
-
|
|
366
|
-
|
|
367
|
-
|
|
368
|
-
|
|
369
|
-
|
|
370
|
-
|
|
371
|
-
|
|
372
|
-
|
|
373
|
-
|
|
374
|
-
|
|
375
|
-
|
|
376
|
-
|
|
377
|
-
|
|
378
|
-
|
|
379
|
-
|
|
380
|
-
|
|
381
|
-
|
|
382
|
-
|
|
383
|
-
|
|
384
|
-
|
|
385
|
-
|
|
386
|
-
|
|
387
|
-
|
|
388
|
-
|
|
389
|
-
|
|
390
|
-
|
|
391
|
-
|
|
392
|
-
|
|
393
|
-
|
|
394
|
-
|
|
395
|
-
|
|
396
|
-
|
|
397
|
-
|
|
398
|
-
|
|
399
|
-
|
|
400
|
-
|
|
401
|
-
|
|
402
|
-
|
|
403
|
-
|
|
404
|
-
|
|
405
|
-
|
|
406
|
-
|
|
407
|
-
|
|
408
|
-
|
|
409
|
-
|
|
410
|
-
|
|
411
|
-
|
|
412
|
-
|
|
413
|
-
|
|
414
|
-
|
|
415
|
-
|
|
416
|
-
|
|
417
|
-
|
|
418
|
-
|
|
419
|
-
|
|
420
|
-
|
|
421
|
-
|
|
422
|
-
|
|
423
|
-
|
|
424
|
-
|
|
425
|
-
|
|
426
|
-
|
|
427
|
-
|
|
428
|
-
|
|
429
|
-
|
|
430
|
-
|
|
431
|
-
|
|
432
|
-
|
|
433
|
-
|
|
434
|
-
|
|
435
|
-
|
|
436
|
-
|
|
437
|
-
|
|
438
|
-
|
|
439
|
-
|
|
440
|
-
|
|
441
|
-
|
|
442
|
-
|
|
443
|
-
|
|
444
|
-
|
|
445
|
-
|
|
446
|
-
|
|
447
|
-
|
|
448
|
-
|
|
449
|
-
|
|
450
|
-
|
|
451
|
-
|
|
452
|
-
|
|
453
|
-
|
|
454
|
-
|
|
455
|
-
|
|
456
|
-
|
|
457
|
-
|
|
458
|
-
|
|
459
|
-
|
|
460
|
-
|
|
461
|
-
|
|
462
|
-
|
|
463
|
-
|
|
464
|
-
|
|
465
|
-
|
|
466
|
-
|
|
467
|
-
|
|
468
|
-
|
|
469
|
-
|
|
470
|
-
|
|
471
|
-
|
|
472
|
-
|
|
473
|
-
|
|
474
|
-
|
|
475
|
-
|
|
476
|
-
|
|
477
|
-
|
|
478
|
-
|
|
479
|
-
|
|
480
|
-
|
|
481
|
-
</
|
|
482
|
-
<
|
|
483
|
-
</
|
|
484
|
-
</
|
|
485
|
-
|
|
486
|
-
|
|
340
|
+
// there's no `` inside of it because i wanna use lib-htnml or whatever to make it hihglihted
|
|
341
|
+
const server = http_1.default.createServer((req, res) => {
|
|
342
|
+
res.writeHead(200, { 'Content-Type': 'text/html' });
|
|
343
|
+
const html = String.raw;
|
|
344
|
+
res.end(`
|
|
345
|
+
<!DOCTYPE html>
|
|
346
|
+
<html>
|
|
347
|
+
<head>
|
|
348
|
+
<meta charset="UTF-8">
|
|
349
|
+
<script src="https://cdn.jsdelivr.net/gh/liwybloc/sonic-ws/bundled/SonicWS_bundle.js"></script>
|
|
350
|
+
<title>SonicWS Debug Menu</title>
|
|
351
|
+
<style>
|
|
352
|
+
body {
|
|
353
|
+
margin: 0;
|
|
354
|
+
font-family: Inter, Arial, sans-serif;
|
|
355
|
+
background: #0f1115;
|
|
356
|
+
color: #e6e6e6;
|
|
357
|
+
height: 100vh;
|
|
358
|
+
display: flex;
|
|
359
|
+
}
|
|
360
|
+
|
|
361
|
+
#sidebar {
|
|
362
|
+
width: 260px;
|
|
363
|
+
background: #141821;
|
|
364
|
+
border-right: 1px solid #1f2533;
|
|
365
|
+
display: flex;
|
|
366
|
+
flex-direction: column;
|
|
367
|
+
}
|
|
368
|
+
|
|
369
|
+
#sidebar-header {
|
|
370
|
+
padding: 16px;
|
|
371
|
+
font-weight: 600;
|
|
372
|
+
font-size: 18px;
|
|
373
|
+
border-bottom: 1px solid #1f2533;
|
|
374
|
+
cursor: pointer;
|
|
375
|
+
transition: color 0.2s;
|
|
376
|
+
}
|
|
377
|
+
#sidebar-header:hover {
|
|
378
|
+
color: #dddddd;
|
|
379
|
+
}
|
|
380
|
+
|
|
381
|
+
#socket-list {
|
|
382
|
+
flex: 1;
|
|
383
|
+
overflow-y: auto;
|
|
384
|
+
}
|
|
385
|
+
|
|
386
|
+
.socket-item {
|
|
387
|
+
padding: 10px 14px;
|
|
388
|
+
cursor: pointer;
|
|
389
|
+
border-bottom: 1px solid #1f2533;
|
|
390
|
+
}
|
|
391
|
+
|
|
392
|
+
.socket-item:hover {
|
|
393
|
+
background: #1b2030;
|
|
394
|
+
}
|
|
395
|
+
|
|
396
|
+
.socket-item.active {
|
|
397
|
+
background: #22294a;
|
|
398
|
+
}
|
|
399
|
+
|
|
400
|
+
.socket-id {
|
|
401
|
+
font-size: 12px;
|
|
402
|
+
opacity: 0.7;
|
|
403
|
+
}
|
|
404
|
+
|
|
405
|
+
#main {
|
|
406
|
+
flex: 1;
|
|
407
|
+
display: flex;
|
|
408
|
+
flex-direction: column;
|
|
409
|
+
}
|
|
410
|
+
|
|
411
|
+
#main-header {
|
|
412
|
+
padding: 17px;
|
|
413
|
+
border-bottom: 1px solid #1f2533;
|
|
414
|
+
display: flex;
|
|
415
|
+
justify-content: space-between;
|
|
416
|
+
align-items: center;
|
|
417
|
+
}
|
|
418
|
+
|
|
419
|
+
#stats {
|
|
420
|
+
display: flex;
|
|
421
|
+
gap: 20px;
|
|
422
|
+
font-size: 13px;
|
|
423
|
+
}
|
|
424
|
+
|
|
425
|
+
#packets {
|
|
426
|
+
flex: 1;
|
|
427
|
+
overflow-y: auto;
|
|
428
|
+
padding: 12px;
|
|
429
|
+
}
|
|
430
|
+
|
|
431
|
+
.packet {
|
|
432
|
+
background: #1a1f2e;
|
|
433
|
+
border-radius: 6px;
|
|
434
|
+
padding: 6px 8px;
|
|
435
|
+
margin-bottom: 4px;
|
|
436
|
+
font-size: 12px;
|
|
437
|
+
cursor: pointer;
|
|
438
|
+
}
|
|
439
|
+
|
|
440
|
+
.packet.sent { border-left: 3px solid #3cff7a; }
|
|
441
|
+
.packet.recv { border-left: 3px solid #ff5a5a; }
|
|
442
|
+
|
|
443
|
+
.packet-details {
|
|
444
|
+
display: none;
|
|
445
|
+
margin-top: 4px;
|
|
446
|
+
opacity: 0.75;
|
|
447
|
+
font-size: 11px;
|
|
448
|
+
}
|
|
449
|
+
|
|
450
|
+
.packet.expanded .packet-details {
|
|
451
|
+
display: block;
|
|
452
|
+
}
|
|
453
|
+
|
|
454
|
+
button {
|
|
455
|
+
background: none;
|
|
456
|
+
}
|
|
457
|
+
</style>
|
|
458
|
+
</head>
|
|
459
|
+
<body>
|
|
460
|
+
<div id="sidebar">
|
|
461
|
+
<div id="sidebar-header">Sonic WS Debug Menu</div>
|
|
462
|
+
<div id="socket-list"></div>
|
|
463
|
+
</div>
|
|
464
|
+
|
|
465
|
+
<div id="main">
|
|
466
|
+
<div id="main-header">
|
|
467
|
+
<button id="close-socket" style="display:none;">❌</button>
|
|
468
|
+
<div id="socket-title">Server Home</div>
|
|
469
|
+
<div id="stats"></div>
|
|
470
|
+
</div>
|
|
471
|
+
<div id="home" style="display: block; padding: 16px;">
|
|
472
|
+
<h2>Server Dashboard</h2>
|
|
473
|
+
<div id="global-stats" style="margin-bottom:16px;"></div>
|
|
474
|
+
<h3>Connection Logs</h3>
|
|
475
|
+
<ul id="connection-logs" style="max-height:200px; overflow-y:auto; padding-left:16px;"></ul>
|
|
476
|
+
<div style="margin-top:16px;">
|
|
477
|
+
<table style="width:100%; border-collapse:collapse;">
|
|
478
|
+
<thead>
|
|
479
|
+
<tr>
|
|
480
|
+
<th style="border-bottom:1px solid #444; text-align:left;">Socket ID</th>
|
|
481
|
+
<th style="border-bottom:1px solid #444; text-align:left;">Name</th>
|
|
482
|
+
<th style="border-bottom:1px solid #444; text-align:left;">Status</th>
|
|
483
|
+
</tr>
|
|
484
|
+
</thead>
|
|
485
|
+
<tbody id="connection-table"></tbody>
|
|
486
|
+
</table>
|
|
487
487
|
</div>
|
|
488
|
-
|
|
489
|
-
<
|
|
490
|
-
|
|
491
|
-
|
|
492
|
-
|
|
493
|
-
|
|
494
|
-
|
|
495
|
-
|
|
496
|
-
|
|
497
|
-
|
|
498
|
-
|
|
499
|
-
|
|
500
|
-
|
|
501
|
-
|
|
502
|
-
|
|
503
|
-
|
|
504
|
-
|
|
505
|
-
|
|
506
|
-
|
|
507
|
-
|
|
508
|
-
|
|
509
|
-
|
|
510
|
-
|
|
511
|
-
|
|
512
|
-
|
|
513
|
-
|
|
514
|
-
|
|
515
|
-
|
|
516
|
-
|
|
517
|
-
|
|
518
|
-
|
|
519
|
-
|
|
520
|
-
|
|
521
|
-
|
|
522
|
-
|
|
523
|
-
|
|
524
|
-
|
|
525
|
-
|
|
526
|
-
|
|
527
|
-
|
|
528
|
-
|
|
529
|
-
|
|
530
|
-
|
|
531
|
-
|
|
532
|
-
|
|
533
|
-
|
|
534
|
-
|
|
535
|
-
|
|
536
|
-
|
|
537
|
-
|
|
538
|
-
if (ms > 0 || parts.length === 0) {
|
|
539
|
-
const seconds = (ms / 1000).toFixed(1);
|
|
540
|
-
parts.push(seconds + 's');
|
|
541
|
-
}
|
|
542
|
-
|
|
543
|
-
if (parts.length === 1) return parts[0];
|
|
544
|
-
const last = parts.pop();
|
|
545
|
-
return parts.join(', ') + ' and ' + last;
|
|
546
|
-
}
|
|
547
|
-
|
|
548
|
-
|
|
549
|
-
const globalStatsDiv = document.getElementById('global-stats');
|
|
550
|
-
function renderGlobalStats() {
|
|
551
|
-
const stats = globalStats;
|
|
552
|
-
const uptime = Date.now() - stats.startTime;
|
|
553
|
-
const formattedUptime = formatMilliseconds(uptime);
|
|
554
|
-
|
|
555
|
-
globalStatsDiv.innerHTML = '<div><strong>Total Sockets:</strong> ' + stats.totalSockets + '</div><div><strong>Total Sent Packets:</strong> ' + stats.totalSent + '</div><div><strong>Total Received Packets:</strong> ' + stats.totalRecv + '</div><div><strong>Total Sent Bytes:</strong> ' + stats.totalSentBytes + ' B</div><div><strong>Total Received Bytes:</strong> ' + stats.totalRecvBytes + ' B</div><div><strong>Total Bandwidth Saved:</strong> ' + stats.totalSaved + ' B</div><div><strong>Uptime:</strong> ' + formattedUptime + '</div>';
|
|
556
|
-
}
|
|
557
|
-
setInterval(renderGlobalStats, 50);
|
|
558
|
-
|
|
559
|
-
function updateConnectionTable() {
|
|
560
|
-
const tbody = document.getElementById('connection-table');
|
|
561
|
-
tbody.innerHTML = '';
|
|
562
|
-
sockets.forEach(s => {
|
|
563
|
-
const row = document.createElement('tr');
|
|
564
|
-
row.innerHTML = '<td>' + s.id + '</td><td>' + s.name + '</td><td style="color:' + (s.el.style.color || '#0f0') + '">' + (s.el.style.color === '#f00' ? 'Disconnected' : 'Connected') + '</td>';
|
|
565
|
-
tbody.appendChild(row);
|
|
566
|
-
});
|
|
567
|
-
}
|
|
568
|
-
setInterval(updateConnectionTable, 1000);
|
|
569
|
-
|
|
570
|
-
const sockets = new Map();
|
|
571
|
-
let activeId = null;
|
|
572
|
-
|
|
573
|
-
function selectSocket(id) {
|
|
574
|
-
activeId = id;
|
|
575
|
-
[...socketList.children].forEach(e => e.classList.toggle('active', e.dataset.id == id));
|
|
576
|
-
|
|
577
|
-
const s = sockets.get(id);
|
|
578
|
-
socketTitle.textContent = s.name;
|
|
579
|
-
packetsDiv.innerHTML = '';
|
|
580
|
-
s.packets.forEach(p => packetsDiv.appendChild(p.el));
|
|
581
|
-
renderStats(s);
|
|
582
|
-
home.style.display = 'none';
|
|
583
|
-
packetsDiv.style.display = 'block';
|
|
584
|
-
closeSocketBtn.style.display = 'block';
|
|
585
|
-
}
|
|
586
|
-
|
|
587
|
-
closeSocketBtn.onclick = () => {
|
|
588
|
-
if(activeId === null) return;
|
|
589
|
-
ws.send("close", Number(activeId));
|
|
590
|
-
}
|
|
591
|
-
|
|
592
|
-
function renderStats(s) {
|
|
593
|
-
statsDiv.innerHTML = "<div>Sent: " + s.sent + "</div><div>Recv: " + s.recv + "</div><div>Sent bytes: " + s.sentBytes + "</div><div>Recv bytes: " + s.recvBytes + "</div><div>Saved: " + s.saved + "</div>";
|
|
594
|
-
}
|
|
595
|
-
|
|
596
|
-
function addSocket(id, name) {
|
|
597
|
-
const el = document.createElement('div');
|
|
598
|
-
el.className = 'socket-item';
|
|
599
|
-
el.dataset.id = id;
|
|
600
|
-
el.innerHTML = "<div>" + name + "</div><div class=\\"socket-id\\">#" + id + "</div>";
|
|
601
|
-
el.onclick = () => selectSocket(id);
|
|
602
|
-
socketList.appendChild(el);
|
|
603
|
-
|
|
604
|
-
sockets.set(id, {
|
|
605
|
-
id,
|
|
606
|
-
name,
|
|
607
|
-
el,
|
|
608
|
-
packets: [],
|
|
609
|
-
sent: 0,
|
|
610
|
-
recv: 0,
|
|
611
|
-
sentBytes: 0,
|
|
612
|
-
recvBytes: 0,
|
|
613
|
-
saved: 0
|
|
614
|
-
});
|
|
615
|
-
}
|
|
616
|
-
|
|
617
|
-
function removeSocket(id, code, reason, codeReason) {
|
|
618
|
-
const s = sockets.get(id);
|
|
619
|
-
if (!s) return console.error("Unknown socket!!", id);
|
|
620
|
-
|
|
621
|
-
s.el.dataset.id = id + "-closed";
|
|
622
|
-
s.el.onclick = () => selectSocket(id + "-closed");
|
|
623
|
-
sockets.set(id + "-closed", s);
|
|
624
|
-
sockets.delete(id);
|
|
625
|
-
|
|
626
|
-
if(activeId == id) activeId = id + "-closed";
|
|
627
|
-
|
|
628
|
-
const nameNode = s.el.childNodes[0];
|
|
629
|
-
nameNode.style.color = "#f00";
|
|
630
|
-
|
|
631
|
-
let circle = document.createElement('span');
|
|
632
|
-
circle.style.display = 'inline-block';
|
|
633
|
-
circle.style.width = '10px';
|
|
634
|
-
circle.style.height = '10px';
|
|
635
|
-
circle.style.borderRadius = '50%';
|
|
636
|
-
circle.style.background = '#ff5a5a';
|
|
637
|
-
circle.style.marginLeft = '8px';
|
|
638
|
-
nameNode.appendChild(circle);
|
|
639
|
-
|
|
640
|
-
// add disconnection info to home page logs
|
|
641
|
-
const logItem = document.createElement('li');
|
|
642
|
-
logItem.textContent = 'Socket #' + id + ' disconnected — Code: ' + code + ', Reason: ' + reason + ', Closure Cause: ' + codeReason;
|
|
643
|
-
document.getElementById('connection-logs').appendChild(logItem);
|
|
644
|
-
|
|
645
|
-
requestAnimationFrame(() => circle.style.width = '100%');
|
|
646
|
-
setTimeout(() => {
|
|
647
|
-
circle.remove();
|
|
648
|
-
s.el.remove();
|
|
649
|
-
if(activeId == id + "-closed") {
|
|
650
|
-
packetsDiv.style.display = 'none';
|
|
651
|
-
home.style.display = 'block';
|
|
652
|
-
closeSocketBtn.style.display = 'none';
|
|
653
|
-
}
|
|
654
|
-
}, 30000);
|
|
655
|
-
}
|
|
656
|
-
|
|
657
|
-
function addPacket(id, dir, tag, rawSize, saved, info, date, processTime) {
|
|
658
|
-
const s = sockets.get(id);
|
|
659
|
-
if (!s) return console.error("Unknown socket!!", id);
|
|
660
|
-
|
|
661
|
-
const el = document.createElement('div');
|
|
662
|
-
el.className = 'packet ' + (dir === 'sent' ? 'sent' : 'recv');
|
|
663
|
-
el.innerHTML = '<div>' + tag + (info !== "undefined" ? ' — ' + info : '') + '</div><div class="packet-details">Raw Bytes: ' + rawSize + 'b (saved: ~' + saved + 'b)<br>Processed At: ' + new Date(date).toISOString() + '<br>Processing Time: ' + processTime.toFixed(2) + 'ms</div>';
|
|
664
|
-
|
|
665
|
-
el.onclick = () => el.classList.toggle('expanded');
|
|
666
|
-
|
|
667
|
-
s.packets.push({ el });
|
|
668
|
-
if (dir === 'sent') {
|
|
669
|
-
s.sent++;
|
|
670
|
-
s.sentBytes += rawSize;
|
|
671
|
-
} else {
|
|
672
|
-
s.recv++;
|
|
673
|
-
s.recvBytes += rawSize;
|
|
674
|
-
}
|
|
675
|
-
s.saved += saved;
|
|
676
|
-
|
|
677
|
-
if (activeId === id) {
|
|
678
|
-
packetsDiv.appendChild(el);
|
|
679
|
-
renderStats(s);
|
|
680
|
-
}
|
|
488
|
+
</div>
|
|
489
|
+
<div id="packets"></div>
|
|
490
|
+
</div>
|
|
491
|
+
|
|
492
|
+
<script>
|
|
493
|
+
const socketList = document.getElementById('socket-list');
|
|
494
|
+
const packetsDiv = document.getElementById('packets');
|
|
495
|
+
const socketTitle = document.getElementById('socket-title');
|
|
496
|
+
const debugTitle = document.getElementById('sidebar-header');
|
|
497
|
+
const statsDiv = document.getElementById('stats');
|
|
498
|
+
const home = document.getElementById('home');
|
|
499
|
+
const closeSocketBtn = document.getElementById('close-socket');
|
|
500
|
+
|
|
501
|
+
debugTitle.onclick = () => {
|
|
502
|
+
if(activeId !== null) {
|
|
503
|
+
activeId = null;
|
|
504
|
+
packetsDiv.style.display = 'none';
|
|
505
|
+
home.style.display = 'block';
|
|
506
|
+
closeSocketBtn.style.display = 'none';
|
|
507
|
+
renderGlobalStats();
|
|
508
|
+
}
|
|
509
|
+
};
|
|
510
|
+
|
|
511
|
+
const globalStats = {
|
|
512
|
+
totalSockets: 0,
|
|
513
|
+
totalSent: 0,
|
|
514
|
+
totalRecv: 0,
|
|
515
|
+
totalSentBytes: 0,
|
|
516
|
+
totalRecvBytes: 0,
|
|
517
|
+
totalSaved: 0,
|
|
518
|
+
startTime: 0,
|
|
519
|
+
};
|
|
520
|
+
|
|
521
|
+
function formatMilliseconds(ms) {
|
|
522
|
+
if (ms < 1) return '0.0s';
|
|
523
|
+
|
|
524
|
+
const units = [
|
|
525
|
+
{ label: 'week', value: 7 * 24 * 60 * 60 * 1000 },
|
|
526
|
+
{ label: 'day', value: 24 * 60 * 60 * 1000 },
|
|
527
|
+
{ label: 'hour', value: 60 * 60 * 1000 },
|
|
528
|
+
{ label: 'minute', value: 60 * 1000 },
|
|
529
|
+
];
|
|
530
|
+
|
|
531
|
+
const parts = [];
|
|
532
|
+
|
|
533
|
+
for (const { label, value } of units) {
|
|
534
|
+
const amount = Math.floor(ms / value);
|
|
535
|
+
if (amount > 0) {
|
|
536
|
+
parts.push(amount + ' ' + label + (amount !== 1 ? 's' : ''));
|
|
537
|
+
ms -= amount * value;
|
|
681
538
|
}
|
|
682
|
-
|
|
683
|
-
|
|
684
|
-
|
|
685
|
-
|
|
539
|
+
}
|
|
540
|
+
|
|
541
|
+
if (ms > 0 || parts.length === 0) {
|
|
542
|
+
const seconds = (ms / 1000).toFixed(1);
|
|
543
|
+
parts.push(seconds + 's');
|
|
544
|
+
}
|
|
545
|
+
|
|
546
|
+
if (parts.length === 1) return parts[0];
|
|
547
|
+
const last = parts.pop();
|
|
548
|
+
return parts.join(', ') + ' and ' + last;
|
|
549
|
+
}
|
|
550
|
+
|
|
551
|
+
|
|
552
|
+
const globalStatsDiv = document.getElementById('global-stats');
|
|
553
|
+
function renderGlobalStats() {
|
|
554
|
+
const stats = globalStats;
|
|
555
|
+
const uptime = Date.now() - stats.startTime;
|
|
556
|
+
const formattedUptime = formatMilliseconds(uptime);
|
|
557
|
+
|
|
558
|
+
globalStatsDiv.innerHTML = '<div><strong>Total Sockets:</strong> ' + stats.totalSockets + '</div><div><strong>Total Sent Packets:</strong> ' + stats.totalSent + '</div><div><strong>Total Received Packets:</strong> ' + stats.totalRecv + '</div><div><strong>Total Sent Bytes:</strong> ' + stats.totalSentBytes + ' B</div><div><strong>Total Received Bytes:</strong> ' + stats.totalRecvBytes + ' B</div><div><strong>Total Bandwidth Saved:</strong> ' + stats.totalSaved + ' B</div><div><strong>Uptime:</strong> ' + formattedUptime + '</div>';
|
|
559
|
+
}
|
|
560
|
+
setInterval(renderGlobalStats, 50);
|
|
561
|
+
|
|
562
|
+
function updateConnectionTable() {
|
|
563
|
+
const tbody = document.getElementById('connection-table');
|
|
564
|
+
tbody.innerHTML = '';
|
|
565
|
+
sockets.forEach(s => {
|
|
566
|
+
const row = document.createElement('tr');
|
|
567
|
+
row.innerHTML = '<td>' + s.id + '</td><td>' + s.name + '</td><td style="color:' + (s.el.style.color || '#0f0') + '">' + (s.el.style.color === '#f00' ? 'Disconnected' : 'Connected') + '</td>';
|
|
568
|
+
tbody.appendChild(row);
|
|
569
|
+
});
|
|
570
|
+
}
|
|
571
|
+
setInterval(updateConnectionTable, 1000);
|
|
572
|
+
|
|
573
|
+
const sockets = new Map();
|
|
574
|
+
let activeId = null;
|
|
575
|
+
|
|
576
|
+
function selectSocket(id) {
|
|
577
|
+
activeId = id;
|
|
578
|
+
[...socketList.children].forEach(e => e.classList.toggle('active', e.dataset.id == id));
|
|
579
|
+
|
|
580
|
+
const s = sockets.get(id);
|
|
581
|
+
socketTitle.textContent = s.name;
|
|
582
|
+
packetsDiv.innerHTML = '';
|
|
583
|
+
s.packets.forEach(p => packetsDiv.appendChild(p.el));
|
|
584
|
+
renderStats(s);
|
|
585
|
+
home.style.display = 'none';
|
|
586
|
+
packetsDiv.style.display = 'block';
|
|
587
|
+
closeSocketBtn.style.display = 'block';
|
|
588
|
+
}
|
|
589
|
+
|
|
590
|
+
closeSocketBtn.onclick = () => {
|
|
591
|
+
if(activeId === null) return;
|
|
592
|
+
ws.send("close", Number(activeId));
|
|
593
|
+
}
|
|
594
|
+
|
|
595
|
+
function renderStats(s) {
|
|
596
|
+
statsDiv.innerHTML = "<div>Sent: " + s.sent + "</div><div>Recv: " + s.recv + "</div><div>Sent bytes: " + s.sentBytes + "</div><div>Recv bytes: " + s.recvBytes + "</div><div>Saved: " + s.saved + "</div>";
|
|
597
|
+
}
|
|
598
|
+
|
|
599
|
+
function addSocket(id, name) {
|
|
600
|
+
const el = document.createElement('div');
|
|
601
|
+
el.className = 'socket-item';
|
|
602
|
+
el.dataset.id = id;
|
|
603
|
+
el.innerHTML = "<div>" + name + "</div><div class=\\"socket-id\\">#" + id + "</div>";
|
|
604
|
+
el.onclick = () => selectSocket(id);
|
|
605
|
+
socketList.appendChild(el);
|
|
606
|
+
|
|
607
|
+
sockets.set(id, {
|
|
608
|
+
id,
|
|
609
|
+
name,
|
|
610
|
+
el,
|
|
611
|
+
packets: [],
|
|
612
|
+
sent: 0,
|
|
613
|
+
recv: 0,
|
|
614
|
+
sentBytes: 0,
|
|
615
|
+
recvBytes: 0,
|
|
616
|
+
saved: 0
|
|
617
|
+
});
|
|
618
|
+
}
|
|
619
|
+
|
|
620
|
+
function removeSocket(id, code, reason, codeReason) {
|
|
621
|
+
const s = sockets.get(id);
|
|
622
|
+
if (!s) return console.error("Unknown socket!!", id);
|
|
623
|
+
|
|
624
|
+
s.el.dataset.id = id + "-closed";
|
|
625
|
+
s.el.onclick = () => selectSocket(id + "-closed");
|
|
626
|
+
sockets.set(id + "-closed", s);
|
|
627
|
+
sockets.delete(id);
|
|
628
|
+
|
|
629
|
+
if(activeId == id) activeId = id + "-closed";
|
|
630
|
+
|
|
631
|
+
const nameNode = s.el.childNodes[0];
|
|
632
|
+
nameNode.style.color = "#f00";
|
|
633
|
+
|
|
634
|
+
let circle = document.createElement('span');
|
|
635
|
+
circle.style.display = 'inline-block';
|
|
636
|
+
circle.style.width = '10px';
|
|
637
|
+
circle.style.height = '10px';
|
|
638
|
+
circle.style.borderRadius = '50%';
|
|
639
|
+
circle.style.background = '#ff5a5a';
|
|
640
|
+
circle.style.marginLeft = '8px';
|
|
641
|
+
nameNode.appendChild(circle);
|
|
642
|
+
|
|
643
|
+
// add disconnection info to home page logs
|
|
644
|
+
const logItem = document.createElement('li');
|
|
645
|
+
logItem.textContent = 'Socket #' + id + ' disconnected — Code: ' + code + ', Reason: ' + reason + ', Closure Cause: ' + codeReason;
|
|
646
|
+
document.getElementById('connection-logs').appendChild(logItem);
|
|
647
|
+
|
|
648
|
+
requestAnimationFrame(() => circle.style.width = '100%');
|
|
649
|
+
setTimeout(() => {
|
|
650
|
+
circle.remove();
|
|
651
|
+
s.el.remove();
|
|
652
|
+
if(activeId == id + "-closed") {
|
|
653
|
+
packetsDiv.style.display = 'none';
|
|
654
|
+
home.style.display = 'block';
|
|
655
|
+
closeSocketBtn.style.display = 'none';
|
|
686
656
|
}
|
|
687
|
-
|
|
688
|
-
|
|
689
|
-
|
|
690
|
-
|
|
691
|
-
|
|
692
|
-
|
|
693
|
-
|
|
694
|
-
|
|
695
|
-
|
|
696
|
-
|
|
697
|
-
|
|
698
|
-
|
|
699
|
-
|
|
700
|
-
|
|
701
|
-
|
|
702
|
-
|
|
703
|
-
|
|
704
|
-
|
|
705
|
-
|
|
706
|
-
|
|
707
|
-
|
|
708
|
-
|
|
709
|
-
|
|
710
|
-
|
|
711
|
-
|
|
712
|
-
|
|
713
|
-
|
|
714
|
-
|
|
715
|
-
|
|
716
|
-
|
|
717
|
-
|
|
718
|
-
|
|
719
|
-
|
|
720
|
-
|
|
721
|
-
|
|
722
|
-
|
|
723
|
-
|
|
724
|
-
|
|
725
|
-
|
|
726
|
-
|
|
727
|
-
|
|
728
|
-
|
|
729
|
-
|
|
730
|
-
|
|
731
|
-
|
|
732
|
-
|
|
733
|
-
|
|
734
|
-
|
|
735
|
-
|
|
736
|
-
|
|
737
|
-
|
|
738
|
-
|
|
657
|
+
}, 30000);
|
|
658
|
+
}
|
|
659
|
+
|
|
660
|
+
function addPacket(id, dir, tag, rawSize, saved, info, date, processTime) {
|
|
661
|
+
const s = sockets.get(id);
|
|
662
|
+
if (!s) return console.error("Unknown socket!!", id);
|
|
663
|
+
|
|
664
|
+
const el = document.createElement('div');
|
|
665
|
+
el.className = 'packet ' + (dir === 'sent' ? 'sent' : 'recv');
|
|
666
|
+
el.innerHTML = '<div>' + tag + (info !== "undefined" ? ' — ' + info : '') + '</div><div class="packet-details">Raw Bytes: ' + rawSize + 'b (saved: ~' + saved + 'b)<br>Processed At: ' + new Date(date).toISOString() + '<br>Processing Time: ' + processTime.toFixed(2) + 'ms</div>';
|
|
667
|
+
|
|
668
|
+
el.onclick = () => el.classList.toggle('expanded');
|
|
669
|
+
|
|
670
|
+
s.packets.push({ el });
|
|
671
|
+
if (dir === 'sent') {
|
|
672
|
+
s.sent++;
|
|
673
|
+
s.sentBytes += rawSize;
|
|
674
|
+
} else {
|
|
675
|
+
s.recv++;
|
|
676
|
+
s.recvBytes += rawSize;
|
|
677
|
+
}
|
|
678
|
+
s.saved += saved;
|
|
679
|
+
|
|
680
|
+
if (activeId === id) {
|
|
681
|
+
packetsDiv.appendChild(el);
|
|
682
|
+
renderStats(s);
|
|
683
|
+
}
|
|
684
|
+
}
|
|
685
|
+
|
|
686
|
+
function setStat(i, v) {
|
|
687
|
+
globalStats[Object.keys(globalStats)[i]] = v;
|
|
688
|
+
if (activeId === null) renderGlobalStats();
|
|
689
|
+
}
|
|
690
|
+
|
|
691
|
+
const ws = new SonicWS('ws://' + location.host);
|
|
692
|
+
|
|
693
|
+
ws.on("connection", id => addSocket(id, "Socket " + id));
|
|
694
|
+
ws.on("disconnection", ([id, code], [reason, codeReason]) => removeSocket(id, code, reason, codeReason));
|
|
695
|
+
ws.on("nameChange", ([id], [name]) => {
|
|
696
|
+
const s = sockets.get(id);
|
|
697
|
+
if (!s) return console.error("Unknown socket!!", id);
|
|
698
|
+
s.name = name;
|
|
699
|
+
s.el.firstChild.textContent = name;
|
|
700
|
+
if (activeId === id) socketTitle.textContent = name;
|
|
701
|
+
});
|
|
702
|
+
ws.on("packet", ([id, size, saved], [dir], [tag], [values], [time, processTime]) => {
|
|
703
|
+
console.log("Received packet", { id, size, saved, dir, tag, values, time, processTime });
|
|
704
|
+
addPacket(id, dir, tag, size, saved, values, time, processTime);
|
|
705
|
+
});
|
|
706
|
+
ws.on("stats", (stats) => {
|
|
707
|
+
console.log("Received stats", stats);
|
|
708
|
+
stats.forEach((v, i) => setStat(i, v));
|
|
709
|
+
});
|
|
710
|
+
ws.on("stat", (i, v) => setStat(i, v));
|
|
711
|
+
|
|
712
|
+
const lastKnownPassword = localStorage.getItem("password");
|
|
713
|
+
const empty = !localStorage.getItem("req");
|
|
714
|
+
let usedPass = "";
|
|
715
|
+
ws.on_ready(() => {
|
|
716
|
+
if(empty) ws.send("password", "");
|
|
717
|
+
else ws.send("password", usedPass = (lastKnownPassword ?? prompt("Please enter password")));
|
|
718
|
+
});
|
|
719
|
+
|
|
720
|
+
ws.on_close(() => {
|
|
721
|
+
localStorage.setItem("req", true);
|
|
722
|
+
localStorage.removeItem("password");
|
|
723
|
+
setTimeout(() => window.location.reload(), 1000);
|
|
724
|
+
});
|
|
725
|
+
|
|
726
|
+
ws.on("authenticated", (success) => {
|
|
727
|
+
console.log("Auth status:", success);
|
|
728
|
+
if(!success) {
|
|
729
|
+
} else {
|
|
730
|
+
localStorage.setItem("req", usedPass.length > 0);
|
|
731
|
+
localStorage.setItem("password", usedPass);
|
|
732
|
+
}
|
|
733
|
+
})
|
|
734
|
+
</script>
|
|
735
|
+
</body>
|
|
736
|
+
</html>
|
|
737
|
+
`);
|
|
739
738
|
});
|
|
740
739
|
const wss = new SonicWSServer({
|
|
741
740
|
clientPackets: [
|