Continue write code of connect area
This commit is contained in:
parent
643ea3efa5
commit
e7000c9abf
@ -9,6 +9,5 @@ module.exports = {
|
||||
name: global.config.server.info.name,
|
||||
tag: global.config.server.info.tag,
|
||||
authMode: authModes[global.config.server["auth-mode"] ?? 1],
|
||||
secureMode: global.config.server.secureMode,
|
||||
...(global.config.server.secureMode ? {cert: global.openSSH.cert.toString("utf8")} : {})
|
||||
secureMode: global.config.server.secureMode ?? false,
|
||||
};
|
@ -69,12 +69,12 @@
|
||||
<p><b class="translate" id="addressCustomConnect">Enter address of server (without ws(s):// and port)</b></p>
|
||||
<p><input class="bg-dark text-white form-control" type="text" placeholder="ai.example.org" id="custom-conn-address"></p>
|
||||
<p><b class="translate" id="portCustomConnect">Enter port of server</b></p>
|
||||
<p><input class="bg-dark text-white form-control" type="number" value="80" min="1" max="25565" placeholder="80" id="custom-conn-port"></p>
|
||||
<p><input class="bg-dark text-white form-control" type="number" value="80" min="0" max="65535" placeholder="80" id="custom-conn-port"></p>
|
||||
<p><b class="translate" id="tlsCustomConnect">Check this if server use TLS (wss:// connection):</b>
|
||||
<input class="bg-dark text-white form-check-input" type="checkbox" id="custom-conn-encryption"></p>
|
||||
</div>
|
||||
<div class="modal-footer"><center>
|
||||
<button type="button" class="btn btn-primary translate" data-bs-dismiss="modal" id="accept-denial-btn">Accept</button>
|
||||
<button type="button" class="btn btn-primary translate" data-bs-dismiss="modal" id="custom-connect-btn" disabled>Connect</button>
|
||||
<!-- <button type="button" class="btn btn-primary">Сохранить изменения</button> -->
|
||||
</center></div>
|
||||
</div>
|
||||
|
11768
server/frontend/public/bootstrap/bootstrap.min.css
vendored
11768
server/frontend/public/bootstrap/bootstrap.min.css
vendored
File diff suppressed because one or more lines are too long
112
server/frontend/public/css/connect/main.css
Normal file
112
server/frontend/public/css/connect/main.css
Normal file
@ -0,0 +1,112 @@
|
||||
.carousel > .carousel-inner > .carousel-item > img {
|
||||
width : 100vh;
|
||||
height : calc(100vh - 56px);
|
||||
/* height : 10vh; */
|
||||
}
|
||||
|
||||
/*.main-window-alt {
|
||||
width : 100vh;
|
||||
height : calc(100vh - 56px);
|
||||
}*/
|
||||
|
||||
.centered-el {
|
||||
padding-top : 45vh;
|
||||
}
|
||||
|
||||
.bg {
|
||||
background-color : #212529;
|
||||
}
|
||||
|
||||
.release-menu {
|
||||
/* Margin */
|
||||
margin-left : 35vh;
|
||||
margin-right : 35vh;
|
||||
margin-top : 5vh;
|
||||
/* Padding */
|
||||
padding-left : 5vh;
|
||||
padding-right : 5vh;
|
||||
padding-top : 5vh;
|
||||
padding-bottom : 5vh;
|
||||
/* Style */
|
||||
border-radius : 2.0vh;
|
||||
background-color : #212529;
|
||||
}
|
||||
|
||||
.release-item {
|
||||
/* Margin */
|
||||
margin-left : 5vh;
|
||||
margin-right : 45vh;
|
||||
margin-top : 5vh;
|
||||
/* Padding */
|
||||
padding-left : 5vh;
|
||||
padding-right : 5vh;
|
||||
padding-top : 5vh;
|
||||
padding-bottom : 5vh;
|
||||
/* Style */
|
||||
border-radius : 2.0vh;
|
||||
background-color : #252525;
|
||||
}
|
||||
|
||||
.sys-win-img {
|
||||
margin-top: 24vh;
|
||||
margin-bottom: 1vh;
|
||||
width : 25vh;
|
||||
height : 25vh;
|
||||
}
|
||||
|
||||
.blog-post-card {
|
||||
border-radius : 25px;
|
||||
background-color: #212121;
|
||||
min-width: 250px;
|
||||
min-height : 250px;
|
||||
margin-left : 25%;
|
||||
margin-right : 25%;
|
||||
}
|
||||
|
||||
.blog-post-image-carousel {
|
||||
margin-left: 10%;
|
||||
margin-right: 10%;
|
||||
max-height: 512px;
|
||||
overflow: hidden;
|
||||
}
|
||||
|
||||
.blog-post-image-carousel img {
|
||||
object-fit: cover;
|
||||
}
|
||||
|
||||
.modal-img-view {
|
||||
width: 1920px;
|
||||
margin: auto;
|
||||
}
|
||||
|
||||
.password-manger-form {
|
||||
margin-left : 30%;
|
||||
margin-right : 30%;
|
||||
padding : 10px;
|
||||
padding-left : 65px;
|
||||
padding-right : 65px;
|
||||
border-radius : 15px;
|
||||
}
|
||||
|
||||
.navbar-connect-dark {
|
||||
color: #ffffff;
|
||||
--bs-bg-opacity: 1;
|
||||
background-color: #09638A !important;
|
||||
}
|
||||
|
||||
.text-navbar-connect-dark {
|
||||
color: #fff !important;
|
||||
background-color: #09638A !important;
|
||||
}
|
||||
|
||||
/* $accordion-color:green; */
|
||||
/* $accordion-padding-y:1.3rem; */
|
||||
/* $accordion-padding-x:2.5rem; */
|
||||
/* $accordion-border-color:black; */
|
||||
/* $accordion-border-width:3.5px; */
|
||||
/* $accordion-border-radius: 3rem; */
|
||||
/* $accordion-button-color:white; */
|
||||
/* $accordion-button-bg:green; */
|
||||
/* $accordion-button-active-bg: white; */
|
||||
/* $accordion-button-active-color:green; */
|
||||
/* @import "./node_modules/bootstrap/scss/bootstrap" */
|
46
server/frontend/public/js/connect/api.js
Normal file
46
server/frontend/public/js/connect/api.js
Normal file
@ -0,0 +1,46 @@
|
||||
import { ServerAuth } from "/js/connect/auth.js";
|
||||
|
||||
class ApiMethods {
|
||||
constructor (api) {
|
||||
this.api = api;
|
||||
}
|
||||
|
||||
async info () {
|
||||
const socket = this.api.socket;
|
||||
const promise = new Promise((rs, rj) => {
|
||||
socket.onmessage = (ev) => {
|
||||
const data = JSON.parse(ev.data).result;
|
||||
rs(data);
|
||||
};
|
||||
socket.onerror = (err) => rj(err);
|
||||
});
|
||||
socket.send(
|
||||
JSON.stringify({
|
||||
method: "info",
|
||||
}),
|
||||
);
|
||||
return await promise;
|
||||
}
|
||||
}
|
||||
|
||||
export class ApiSocket {
|
||||
constructor ({
|
||||
isTLSmode, address, port
|
||||
}) {
|
||||
this.tlsMode = isTLSmode;
|
||||
this.address = address;
|
||||
this.port = port;
|
||||
|
||||
this.socket = new WebSocket(`${!isTLSmode ? "ws" : "wss"}://${address}:${port}`);
|
||||
this.methods = new ApiMethods(this);
|
||||
}
|
||||
|
||||
async run () {
|
||||
const socket = this.socket;
|
||||
const promise = new Promise((rs, rj) => {
|
||||
socket.onopen = () => this.methods.info().then(data => rs(data));
|
||||
socket.onerror = (err) => rj(err);
|
||||
});
|
||||
return await promise;
|
||||
}
|
||||
}
|
19
server/frontend/public/js/connect/auth.js
Normal file
19
server/frontend/public/js/connect/auth.js
Normal file
@ -0,0 +1,19 @@
|
||||
export class ServerAuth {
|
||||
constructor (address, port, tlsEnabled) {
|
||||
this.address = address;
|
||||
this.port = port;
|
||||
this.isTLSenabled = !!tlsEnabled;
|
||||
}
|
||||
|
||||
getAuth () {
|
||||
const serverdata = JSON.parse(localStorage.getItem(`server:${
|
||||
!!this.isTLSenabled ? "wss" : "ws"
|
||||
}:${address}:${port}`) ?? "null");
|
||||
if (!serverdata) return null;
|
||||
|
||||
}
|
||||
|
||||
renderAuth (type) {
|
||||
|
||||
}
|
||||
}
|
@ -1,52 +1,32 @@
|
||||
import { ApiSocket } from "/js/connect/api.js";
|
||||
|
||||
// load styles
|
||||
document.head.innerHTML += '\\n <link href="/bootstrap/bootstrap.min.css" rel="stylesheet">';
|
||||
document.head.innerHTML += '\\n <link href="/css/main.css" rel="stylesheet">';
|
||||
document.head.innerHTML += '\\n <link href="/css/connect/main.css" rel="stylesheet">';
|
||||
document.head.innerHTML += '\\n <link href="/font-awesome/css/all.css" rel="stylesheet">';
|
||||
document.head.innerHTML += '\\n <link rel="icon" href="/favicon.png">'
|
||||
|
||||
// load main page
|
||||
document.body.innerHTML = `<nav id="navbar-main" class="navbar navbar-dark bg-dark navbar-expand-lg noselect" style="background-color: #e3f2fd;">
|
||||
<a style="padding-left:10px" class="navbar-brand linked-btn" onclick="fl.go('/');">
|
||||
Подключение...
|
||||
document.body.innerHTML = `<nav id="navbar-main" class="navbar navbar-connect-dark navbar-dark navbar-expand-lg noselect" style="background-color: #e3f2fd;">
|
||||
<a style="padding-left:10px" class="navbar-brand linked-btn" id="server-name" onclick="fl.go('/');">
|
||||
Connection...
|
||||
</a>
|
||||
<div class="collapse navbar-collapse noselect" id="navbarNav"><ul class="navbar-nav"><ul class="navbar-nav">
|
||||
<div class="vr"></div>
|
||||
<li class="nav-item">
|
||||
<a class="nav-link translate linked-btn noselect" aria-current="page" onclick="fl.go('/');" id="nav-home-btn"><i class="fas fa-home"></i> Главная</a>
|
||||
<a class="nav-link translate linked-btn noselect" aria-current="page" id="nav-home-btn"><i class="fas fa-home"></i> Home</a>
|
||||
</li>
|
||||
<li class="nav-item">
|
||||
<a class="nav-link translate linked-btn noselect" aria-current="page" onclick="fl.go('/blog');" id="nav-docs-btn"><i class="fab fa-microblog"></i> Мой блог</a>
|
||||
</li>
|
||||
<li class="nav-item">
|
||||
<a class="nav-link translate linked-btn noselect" aria-current="page" onclick="fl.go('/contacts');" id="nav-docs-btn"><i class="fas fa-address-book"></i> Контакты</a>
|
||||
</li>
|
||||
<li class="nav-item">
|
||||
<a class="nav-link translate linked-btn noselect" aria-current="page" onclick="fl.go('/projects');" id="nav-docs-btn"><i class="fas fa-folder-open"></i> Проекты</a>
|
||||
</li>
|
||||
<li class="nav-item">
|
||||
<a class="nav-link translate linked-btn noselect" aria-current="page" onclick="fl.go('/knowledge-base');" id="nav-docs-btn"><i class="fas fa-book"></i> База знаний</a>
|
||||
</li>
|
||||
<li class="nav-item">
|
||||
<a class="nav-link translate linked-btn noselect" aria-current="page" onclick="fl.goToLocation('https://git.fullgream.tech/fullgream?tab=activity');" id="nav-docs-btn"><i class="fab fa-git-alt"></i> Мой git</a>
|
||||
</li>
|
||||
<li class="nav-item">
|
||||
<a class="nav-link translate linked-btn noselect" aria-current="page" onclick="fl.go('/price');" id="nav-docs-btn"><i class="fas fa-coins"></i> Услуги</a>
|
||||
</li>
|
||||
<li class="nav-item">
|
||||
<a class="nav-link translate linked-btn noselect" aria-current="page" onclick="fl.go('/dnd');" id="nav-docs-btn"><i class="fas fa-dice-d20"></i> D&D</a>
|
||||
<a class="nav-link translate linked-btn noselect" aria-current="page" id="nav-docs-btn"><i class="fas fa-dice-d20"></i> Games</a>
|
||||
</li>
|
||||
</ul></div>
|
||||
|
||||
<div class="vr" style="margin-right:5px"></div>
|
||||
<div class="d-flex" style="margin-right:10px" id="navbar-user-menu">
|
||||
<div id="fp-condition-div">
|
||||
<button id="fp-condition" class="btn btn-outline-secondary" type="button" data-toggle="collapse" data-target="#navbarNav" aria-controls="navbarNav" aria-expanded="false" aria-label="Toggle navigation"><i class="fas fa-fingerprint"></i></button>
|
||||
</div>
|
||||
<div class="vr" style="margin-right:5px; margin-left: 5px"></div>
|
||||
<button onclick="fl.go('/auth');" class="btn btn-outline-secondary" type="button" data-toggle="collapse" data-target="#navbarNav" aria-controls="navbarNav" aria-expanded="false" aria-label="Toggle navigation"><i class="fas fa-key"></i> Авторизация</button>
|
||||
<button id="toggle-theme" class="btn btn-outline-light" type="button" data-toggle="collapse" data-target="#navbarNav" aria-controls="navbarNav" aria-expanded="false" aria-label="Toggle navigation"><i class="far fa-eye"></i> Light theme</button>
|
||||
</div>
|
||||
</nav>
|
||||
<div id="fl.area">
|
||||
<div id="server.area">
|
||||
<div style='margin-top: 30vh'>
|
||||
<center>
|
||||
<h1>Connection to server...</h1>
|
||||
@ -56,3 +36,28 @@ document.body.innerHTML = `<nav id="navbar-main" class="navbar navbar-dark bg-da
|
||||
</div>`;
|
||||
|
||||
//document <i class="fas fa-sync-alt"></i>
|
||||
|
||||
const curUrl = new URL(location.href);
|
||||
const [address, port] = curUrl.searchParams.get("server").split(/:/);
|
||||
const isTLSmode = curUrl.searchParams.get("encrypted") === "true";
|
||||
|
||||
const socket = new ApiSocket({
|
||||
isTLSmode, address, port
|
||||
});
|
||||
socket.run()
|
||||
.then(data => {
|
||||
console.log("socket sends:", data);
|
||||
document.getElementById("server-name").innerHTML = data.name;
|
||||
document.title = data.name;
|
||||
})
|
||||
.catch(err => {
|
||||
console.error(err);
|
||||
document.title = "Connection error";
|
||||
document.getElementById("server-name").innerHTML = "Error";
|
||||
document.getElementById("server.area").innerHTML = `<div style='margin-top: 30vh'>
|
||||
<center>
|
||||
<h1>Connection errored</h1>
|
||||
<p>Connection errored</p>
|
||||
</center>
|
||||
</div>`;
|
||||
});
|
@ -65,6 +65,62 @@ function checkContentWarning (successfulHandler) {
|
||||
fl.bindLoad("/connect", () => {
|
||||
translate("/connect");
|
||||
|
||||
function initNewConnectionOpen () {
|
||||
if (initNewConnectionOpen.interval)
|
||||
clearInterval(initNewConnectionOpen.interval);
|
||||
document.getElementById("custom-conn-address").value = "";
|
||||
document.getElementById("custom-conn-port").value = 80;
|
||||
document.getElementById("custom-conn-encryption").checked = false;
|
||||
document.getElementById("custom-connect-btn").disabled = true;
|
||||
|
||||
const connButton = document.getElementById("custom-connect-btn");
|
||||
|
||||
initNewConnectionOpen.interval = setInterval(() => {
|
||||
const address = document.getElementById("custom-conn-address");
|
||||
// address.value
|
||||
const port = document.getElementById("custom-conn-port");
|
||||
// port.value
|
||||
const isWSS = document.getElementById("custom-conn-encryption");
|
||||
// isWSS.checked
|
||||
// connButton.disabled
|
||||
|
||||
if (address.value.match(/^wss:\/\//)) {
|
||||
address.value = address.value.slice(6);
|
||||
isWSS.checked = true;
|
||||
}
|
||||
if (address.value.match(/^ws:\/\//)) {
|
||||
address.value = address.value.slice(5);
|
||||
isWSS.checked = false;
|
||||
}
|
||||
if (address.value.match(/:[0-9]{1,5}$/)) {
|
||||
const portSelected = +(address.value.match(/:[0-9]{1,5}$/).at(0).slice(1,));
|
||||
address.value = address.value.slice(0, -1 * (":"+portSelected).length);
|
||||
port.value = portSelected;
|
||||
}
|
||||
|
||||
if (!port.value) {
|
||||
port.value = 80;
|
||||
}
|
||||
if (+port.value < 0)
|
||||
port.value = 0;
|
||||
if (+port.value > 65535)
|
||||
port.value = 65535;
|
||||
|
||||
connButton.disabled = !address.value;
|
||||
}, 50);
|
||||
|
||||
connButton.onclick = () => {
|
||||
const serverlist = JSON.parse(localStorage.getItem("savedServers") ?? "null") ?? [];
|
||||
serverlist.push({
|
||||
address: document.getElementById("custom-conn-address").value,
|
||||
port: +document.getElementById("custom-conn-port").value,
|
||||
tls: document.getElementById("custom-conn-encryption").checked,
|
||||
});
|
||||
localStorage.setItem("savedServers", JSON.stringify(serverlist));
|
||||
connectServers();
|
||||
}
|
||||
}
|
||||
|
||||
function connectServers () {
|
||||
document.getElementById("loading-servers").innerHTML = `<center><p>${tr("/connect", "loading") ?? "Loading..."}</p></center>`;
|
||||
|
||||
@ -81,33 +137,33 @@ fl.bindLoad("/connect", () => {
|
||||
|
||||
xhr.onload = async () => {
|
||||
if (xhr.status === 200) {
|
||||
const localServers = JSON.parse(localStorage.getItem("savedServers") ?? "null") ?? [];
|
||||
const localServers = (JSON.parse(localStorage.getItem("savedServers") ?? "null") ?? []).map(x => { x.fromLocal = true; return x });
|
||||
|
||||
const servers = localServers.concat(JSON.parse(xhr.response).result);
|
||||
const servers = localServers.concat(JSON.parse(xhr.response).result.map(x => { x.fromLocal = false; return x }));
|
||||
const serversByItemID = new Object();
|
||||
const items = [];
|
||||
const itemIDs = [];
|
||||
const mp = servers.map(async serverItem => {
|
||||
const promise = new Promise((resolve, reject) => {
|
||||
console.log("serverItem:", serverItem);
|
||||
const socket = new WebSocket(`${!serverItem.secureMode ? "ws" : "wss"}://${serverItem.address}:${serverItem.port}`);
|
||||
const socket = new WebSocket(`${!serverItem.tls ? "ws" : "wss"}://${serverItem.address}:${serverItem.port}`);
|
||||
let ping = new Date();
|
||||
socket.onopen = () => {
|
||||
ping = (new Date()) - ping;
|
||||
socket.onmessage = (ev) => {
|
||||
const data = JSON.parse(ev.data).result;
|
||||
data.secureMode = serverItem.secureMode;
|
||||
data.tls = serverItem.tls;
|
||||
itemIDs.push({
|
||||
...data,
|
||||
itemID: `con-btn-to:${data.tag}:${serverItem.address}:${serverItem.port}`
|
||||
});
|
||||
serversByItemID[`con-btn-to:${data.tag}:${serverItem.address}:${serverItem.port}`] = { data, serverItem };
|
||||
resolve(items.push(`<tr${!data.secureMode ? ' style="background-color: #7F6003"' : ""}>
|
||||
<td${!data.secureMode ? ' style="background-color: #7F6003"' : ""}>${data.name}</td>
|
||||
<td${!data.secureMode ? ' style="background-color: #7F6003"' : ""}>${serverItem.address}:${serverItem.port}</td>
|
||||
<td${!data.secureMode ? ' style="background-color: #7F6003"' : ""}>${!data.secureMode ? '<font color="red"><i class="fas fa-exclamation-triangle"></i></font>' : '<font color="green"><i class="fas fa-lock"></i></font>'}</td>
|
||||
<td${!data.secureMode ? ' style="background-color: #7F6003"' : ""}>${ping}</td>
|
||||
<td${!data.secureMode ? ' style="background-color: #7F6003"' : ""}><button class="btn btn-secondary" id="con-btn-to:${data.tag}:${serverItem.address}:${serverItem.port}">${tr("/connect", "connect-to-server-btn") ?? "Connect"}</button></td>
|
||||
resolve(items.push(`<tr${!data.tls ? ' style="background-color: #7F6003"' : ""}>
|
||||
<td${!data.tls ? ' style="background-color: #7F6003"' : ""}>${data.name}</td>
|
||||
<td${!data.tls ? ' style="background-color: #7F6003"' : ""}>${serverItem.address}:${serverItem.port}</td>
|
||||
<td${!data.tls ? ' style="background-color: #7F6003"' : ""}>${!data.tls ? '<font color="red"><i class="fas fa-exclamation-triangle"></i></font>' : '<font color="green"><i class="fas fa-lock"></i></font>'}</td>
|
||||
<td${!data.tls ? ' style="background-color: #7F6003"' : ""}>${ping}</td>
|
||||
<td${!data.tls ? ' style="background-color: #7F6003"' : ""}><button class="btn btn-secondary" id="con-btn-to:${data.tag}:${serverItem.address}:${serverItem.port}">${tr("/connect", "connect-to-server-btn") ?? "Connect"}</button></td>
|
||||
</tr>`));
|
||||
};
|
||||
socket.send(JSON.stringify({
|
||||
@ -147,13 +203,14 @@ fl.bindLoad("/connect", () => {
|
||||
document.getElementById(btn.itemID).onclick = () => {
|
||||
checkContentWarning(() => {
|
||||
const serverInfo = serversByItemID[btn.itemID].serverItem;
|
||||
window.open(`/connect-area?server=${serverInfo.address}:${serverInfo.port}&encrypted=${serverInfo.secureMode ? "true" : "false"}`);
|
||||
window.open(`/connect-area?server=${serverInfo.address}:${serverInfo.port}&encrypted=${serverInfo.tls ? "true" : "false"}`);
|
||||
});
|
||||
};
|
||||
});
|
||||
|
||||
document.getElementById("conn-to-custom").onclick = function () {
|
||||
const connectModal = new bootstrap.Modal(document.getElementById('connectModal'), {});
|
||||
initNewConnectionOpen();
|
||||
connectModal.show();
|
||||
}
|
||||
}
|
||||
|
@ -61,6 +61,7 @@ const translationTree = {
|
||||
addressCustomConnect: 'Enter address of server (without ws(s):// and port)',
|
||||
portCustomConnect: 'Enter port of server',
|
||||
tlsCustomConnect: 'Check this if server use TLS (wss:// connection):',
|
||||
"custom-connect-btn": "Connect",
|
||||
|
||||
"server-name-tbl": "Name",
|
||||
"server-address": "Address",
|
||||
@ -93,6 +94,7 @@ const translationTree = {
|
||||
addressCustomConnect: 'Адрес сервера (без порта и ws(s)://)',
|
||||
portCustomConnect: 'Порт сервера',
|
||||
tlsCustomConnect: 'Использовать защищённое соединение (TLS) (wss://):',
|
||||
"custom-connect-btn": "Подключиться",
|
||||
|
||||
"server-name-tbl": "Имя сервера",
|
||||
"server-address": "Адрес",
|
||||
|
@ -3,11 +3,7 @@ module.exports = async function (req, res) {
|
||||
result: [{
|
||||
address: global.config.server.address === "0.0.0.0" ? req.headers.host.split(":").at(0) : global.config.server.address,
|
||||
port: global.config.server.port,
|
||||
secureMode: false
|
||||
}, {
|
||||
address: "offline.example.org",
|
||||
port: 8891,
|
||||
secureMode: false,
|
||||
tls: false
|
||||
}]
|
||||
});
|
||||
}
|
||||
|
@ -20,7 +20,7 @@ module.exports = async (bytes) => {
|
||||
const packet = bytes.toString("utf8");
|
||||
|
||||
const isHTTP = packet.match(/(GET|POST|PUT|DELETE|OPTIONS|HEAD|PATCH|TRACE|CONNECT)\s{1,}\/[a-zA-Z0-9_\-\.\/?=&%:]{0,}\s{1,}HTTP(S|)\/(1\.0|1\.1|2\.0)(\n|\r)/gmu)?.length > 0 ?? false;
|
||||
console.log({ isHTTP });
|
||||
//console.log({ isHTTP });
|
||||
let isWebSocket = false;
|
||||
if (isHTTP) {
|
||||
const headers = simpleParseHeaders(packet);
|
||||
|
Loading…
Reference in New Issue
Block a user