version checks, improved secure/insecure handling, bugfixes, and proxy support!
This commit is contained in:
parent
5ddac62717
commit
863b445a17
15
README.md
15
README.md
|
@ -1,4 +1,5 @@
|
||||||
# OpenTerminal
|
# OpenTerminal
|
||||||
|
|
||||||
An online terminal and communal text buffer
|
An online terminal and communal text buffer
|
||||||
|
|
||||||
![openterminal thumbnail image](public/img/thumbnail.png)
|
![openterminal thumbnail image](public/img/thumbnail.png)
|
||||||
|
@ -15,13 +16,23 @@ jokes aside- while i do absolutely see how an open, self-moderated text buffer i
|
||||||
|
|
||||||
...or maybe it'll just become a garbage-posting haven. regardless, it's a fun little idea, so i made it anyway.
|
...or maybe it'll just become a garbage-posting haven. regardless, it's a fun little idea, so i made it anyway.
|
||||||
|
|
||||||
|
## hosting
|
||||||
|
|
||||||
|
- `git clone` this repo and `cd` into it
|
||||||
|
- `npm ci` to install dependencies
|
||||||
|
- `npm run start`, and you should be good to do!
|
||||||
|
|
||||||
|
OpenTerminal also makes use of environment variables for server hosts who want a bit more control of their setup:
|
||||||
|
|
||||||
|
- `OPENTERM_HOST` - the address to bind OpenTerminal's http and websocket server. (default 0.0.0.0)
|
||||||
|
- `OPENTERM_POST` - the port to bind OpenTerminal's http and websocket server. (default 8080)
|
||||||
|
- `OPENTERM_TRUSTED_PROXIES` - a comma-separated list of addresses to check for `X-Forwarded-For` headers from. handy if you run OpenTerminal behind a reverse proxy! (default none, example `"127.0.0.1,192.168.0.100"`)
|
||||||
|
|
||||||
## roadmap
|
## roadmap
|
||||||
|
|
||||||
- rewrite backend in go/rust (me no like javascript raaaahhh)
|
- rewrite backend in go/rust (me no like javascript raaaahhh)
|
||||||
- multiple "channels" (at least if one gets flooded, there's somewhere else you can go)
|
|
||||||
- master server (anyone can host a channel and post to the MS)
|
- master server (anyone can host a channel and post to the MS)
|
||||||
|
|
||||||
### "maybe" roadmap
|
### "maybe" roadmap
|
||||||
|
|
||||||
- channel logs (for recovery in the event of a crash, as an optional feature)
|
- channel logs (for recovery in the event of a crash, as an optional feature)
|
||||||
|
|
||||||
|
|
|
@ -5,10 +5,9 @@ services:
|
||||||
image: openterminal
|
image: openterminal
|
||||||
container_name: openterminal
|
container_name: openterminal
|
||||||
ports:
|
ports:
|
||||||
- 443:443
|
- 8080:8080
|
||||||
volumes:
|
|
||||||
- ./certs/cert.crt:/srv/openterminal/certs/cert.crt
|
|
||||||
- ./certs/cert.key:/srv/openterminal/certs/cert.key
|
|
||||||
environment:
|
environment:
|
||||||
PORT: 443
|
OPENTERM_HOST: 0.0.0.0
|
||||||
|
OPENTERM_PORT: 8080
|
||||||
|
# OPENTERM_TRUSTED_PROXIES: 127.0.0.1,192.168.0.100
|
||||||
restart: unless-stopped
|
restart: unless-stopped
|
||||||
|
|
|
@ -49,6 +49,7 @@
|
||||||
<ul>
|
<ul>
|
||||||
<li><a href="https://github.com/mellodoot/openterminal/issues" target="_blank">issues</a></li>
|
<li><a href="https://github.com/mellodoot/openterminal/issues" target="_blank">issues</a></li>
|
||||||
<li><a href="https://github.com/mellodoot/openterminal" target="_blank">source</a></li>
|
<li><a href="https://github.com/mellodoot/openterminal" target="_blank">source</a></li>
|
||||||
|
<li id="version">loading...</li>
|
||||||
</ul>
|
</ul>
|
||||||
</footer>
|
</footer>
|
||||||
<div id="dialog-backdrop"></div>
|
<div id="dialog-backdrop"></div>
|
||||||
|
|
|
@ -2,6 +2,8 @@ import * as Terminal from "./terminal.js";
|
||||||
import * as Visual from "./visual.js";
|
import * as Visual from "./visual.js";
|
||||||
|
|
||||||
document.addEventListener("DOMContentLoaded", () => {
|
document.addEventListener("DOMContentLoaded", () => {
|
||||||
|
document.getElementById("version").textContent = Terminal.VERSION;
|
||||||
|
|
||||||
Visual.bind();
|
Visual.bind();
|
||||||
Terminal.start();
|
Terminal.start();
|
||||||
|
|
||||||
|
@ -56,3 +58,37 @@ document.addEventListener("DOMContentLoaded", () => {
|
||||||
});
|
});
|
||||||
});
|
});
|
||||||
|
|
||||||
|
/**
|
||||||
|
* requests that the user confirm they wish to connect to an insecure (ws://) server.
|
||||||
|
* @returns a promise returning `true` or `false` based on user input.
|
||||||
|
*/
|
||||||
|
export async function user_confirm_insecure() {
|
||||||
|
const warn_dialog = document.getElementById("warn-dialog");
|
||||||
|
const warn_close = warn_dialog.getElementsByClassName("dialog-close").item(0);
|
||||||
|
const dialog_backdrop = document.getElementById("dialog-backdrop");
|
||||||
|
const warn_proceed = document.getElementById("warn-proceed");
|
||||||
|
const warn_cancel = document.getElementById("warn-cancel");
|
||||||
|
|
||||||
|
warn_dialog.classList.add("show");
|
||||||
|
dialog_backdrop.classList.add("show");
|
||||||
|
|
||||||
|
const user_input = await new Promise((Resolve, Reject) => {
|
||||||
|
warn_close.addEventListener('click', () => {
|
||||||
|
Resolve(false);
|
||||||
|
});
|
||||||
|
warn_cancel.addEventListener('click', () => {
|
||||||
|
warn_close.click();
|
||||||
|
});
|
||||||
|
dialog_backdrop.addEventListener("click", () => {
|
||||||
|
warn_close.click();
|
||||||
|
});
|
||||||
|
warn_proceed.addEventListener('click', () => {
|
||||||
|
Resolve(true);
|
||||||
|
});
|
||||||
|
});
|
||||||
|
|
||||||
|
warn_dialog.classList.remove("show");
|
||||||
|
dialog_backdrop.classList.remove("show");
|
||||||
|
|
||||||
|
return user_input;
|
||||||
|
}
|
||||||
|
|
|
@ -1,12 +1,18 @@
|
||||||
var recv_buffer = [];
|
import { user_confirm_insecure } from "./main.js";
|
||||||
var send_buffer = [];
|
|
||||||
|
|
||||||
var content;
|
export const VERSION = "1.1.0";
|
||||||
var server_indicator;
|
|
||||||
var mobile_input;
|
const foreground = localStorage.getItem("foreground");
|
||||||
|
const background = localStorage.getItem("background");
|
||||||
|
const content = document.getElementById("content");
|
||||||
|
const server_indicator = document.getElementById("server-url");
|
||||||
|
const mobile_input = document.getElementById("mobile-input");
|
||||||
|
|
||||||
var client;
|
var client;
|
||||||
|
|
||||||
|
var recv_buffer = [];
|
||||||
|
var send_buffer = [];
|
||||||
|
|
||||||
var my_colour = false;
|
var my_colour = false;
|
||||||
var pre_buffer_chars = 0;
|
var pre_buffer_chars = 0;
|
||||||
var server_url = "";
|
var server_url = "";
|
||||||
|
@ -20,6 +26,7 @@ const DATA_TYPES = {
|
||||||
backspace: 4,
|
backspace: 4,
|
||||||
backword: 5,
|
backword: 5,
|
||||||
arrow: 6,
|
arrow: 6,
|
||||||
|
version: 7,
|
||||||
};
|
};
|
||||||
|
|
||||||
/**
|
/**
|
||||||
|
@ -49,8 +56,6 @@ to help you feel a little more comfortable, i've prepared some commands for you:
|
||||||
|
|
||||||
server_url = new URL(window.location).searchParams.get("server") || window.location.host;
|
server_url = new URL(window.location).searchParams.get("server") || window.location.host;
|
||||||
|
|
||||||
const foreground = localStorage.getItem("foreground");
|
|
||||||
const background = localStorage.getItem("background");
|
|
||||||
if (foreground && background) {
|
if (foreground && background) {
|
||||||
window.set_colours(foreground, background);
|
window.set_colours(foreground, background);
|
||||||
}
|
}
|
||||||
|
@ -59,10 +64,6 @@ to help you feel a little more comfortable, i've prepared some commands for you:
|
||||||
document.body.classList.add("lcd");
|
document.body.classList.add("lcd");
|
||||||
}
|
}
|
||||||
|
|
||||||
content = document.getElementById("content");
|
|
||||||
server_indicator = document.getElementById("server-url");
|
|
||||||
mobile_input = document.getElementById("mobile-input");
|
|
||||||
|
|
||||||
document.addEventListener("keydown", handle_input);
|
document.addEventListener("keydown", handle_input);
|
||||||
document.addEventListener("paste", handle_paste);
|
document.addEventListener("paste", handle_paste);
|
||||||
|
|
||||||
|
@ -88,8 +89,6 @@ to help you feel a little more comfortable, i've prepared some commands for you:
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* closes any existing websocket connection and attempts to create a new one.
|
* closes any existing websocket connection and attempts to create a new one.
|
||||||
* `wss://` is prefixed automatically if not present.
|
|
||||||
* insecure websockets are not recommended and support is not planned.
|
|
||||||
* @param {string} server_url - the server websocket url to connect to.
|
* @param {string} server_url - the server websocket url to connect to.
|
||||||
*/
|
*/
|
||||||
export async function connect(server_url) {
|
export async function connect(server_url) {
|
||||||
|
@ -111,68 +110,46 @@ export async function connect(server_url) {
|
||||||
|
|
||||||
add_system_message("Connecting to the server...\n");
|
add_system_message("Connecting to the server...\n");
|
||||||
|
|
||||||
let protocol = false;
|
if (server_url.startsWith("wss://") || server_url.startsWith("ws://")) {
|
||||||
|
client = new WebSocket(server_url);
|
||||||
// check if user explicitly stated secure/insecure protocol
|
add_client_events(client);
|
||||||
if (server_url.startsWith("wss://")) {
|
client.addEventListener('error', () => {
|
||||||
protocol = "wss://";
|
add_system_message(`\nConnection failed!\n`);
|
||||||
server_url = server_url.split(6);
|
add_system_message("Ensure you entered the correct server URL, or check the console for more details.\n");
|
||||||
} else if (server_url.startsWith("ws://")) {
|
|
||||||
server_url = server_url.split(5);
|
|
||||||
protocol = "ws://";
|
|
||||||
}
|
|
||||||
// otherwise, probe the url!
|
|
||||||
else protocol = await get_available_socket_protocol(server_url);
|
|
||||||
|
|
||||||
// no server was found!
|
|
||||||
if (!protocol) {
|
|
||||||
return add_system_message(`\n[NO SERVER FOUND AT ${server_url}!]\n`);
|
|
||||||
}
|
|
||||||
|
|
||||||
// server was found, but it's insecure!
|
|
||||||
if (protocol === "ws://") {
|
|
||||||
const warn_dialog = document.getElementById("warn-dialog");
|
|
||||||
const warn_close = warn_dialog.getElementsByClassName("dialog-close").item(0);
|
|
||||||
const user_wants_insecure = await new Promise((Resolve, Reject) => {
|
|
||||||
const dialog_backdrop = document.getElementById("dialog-backdrop");
|
|
||||||
const warn_proceed = document.getElementById("warn-proceed");
|
|
||||||
const warn_cancel = document.getElementById("warn-cancel");
|
|
||||||
|
|
||||||
warn_dialog.classList.add("show");
|
|
||||||
dialog_backdrop.classList.add("show");
|
|
||||||
|
|
||||||
set_enable_input(false);
|
|
||||||
|
|
||||||
warn_close.addEventListener('click', () => {
|
|
||||||
Resolve(false);
|
|
||||||
});
|
});
|
||||||
warn_cancel.addEventListener('click', () => {
|
} else {
|
||||||
Resolve(false);
|
try {
|
||||||
});
|
client = await find_socket_at_url(server_url);
|
||||||
warn_proceed.addEventListener('click', () => {
|
} catch (error) {
|
||||||
Resolve(true);
|
if (error === 'deny-insecure') {
|
||||||
});
|
|
||||||
});
|
|
||||||
|
|
||||||
warn_close.click();
|
|
||||||
|
|
||||||
set_enable_input(true);
|
|
||||||
|
|
||||||
if (!user_wants_insecure) {
|
|
||||||
server_indicator.innerText = "not connected";
|
server_indicator.innerText = "not connected";
|
||||||
add_system_message(`\n[CONNECTION CLOSED]\n`);
|
add_system_message(`\n[CONNECTION CLOSED]\n`);
|
||||||
return;
|
return
|
||||||
|
}
|
||||||
|
add_system_message(`\nConnection failed!\n`);
|
||||||
|
add_system_message("Ensure you entered the correct server URL, or check the console for more details.\n");
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
client = new WebSocket(protocol + server_url);
|
set_enable_input(true);
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* attaches initial client events (open, message, close)
|
||||||
|
* @param {WebSocket} client the client to bind events to.
|
||||||
|
*/
|
||||||
|
function add_client_events(client) {
|
||||||
|
client.addEventListener('open', async () => {
|
||||||
|
console.log(`Successfully connected to ${client.url}.`);
|
||||||
|
|
||||||
client.addEventListener('open', () => {
|
|
||||||
server_indicator.innerText = server_url;
|
server_indicator.innerText = server_url;
|
||||||
add_system_message(`Connection successful.\n\n`);
|
add_system_message(`Connection successful.\n\n`);
|
||||||
add_system_message(`=== BEGIN SESSION ===\n\n`);
|
add_system_message(`=== BEGIN SESSION ===\n\n`);
|
||||||
new_caret();
|
|
||||||
});
|
client.send(JSON.stringify({
|
||||||
|
type: DATA_TYPES.version,
|
||||||
|
text: VERSION
|
||||||
|
}));
|
||||||
|
|
||||||
client.addEventListener('message', handle_message);
|
client.addEventListener('message', handle_message);
|
||||||
|
|
||||||
|
@ -181,36 +158,43 @@ export async function connect(server_url) {
|
||||||
add_system_message(`\n[CONNECTION CLOSED]\n`);
|
add_system_message(`\n[CONNECTION CLOSED]\n`);
|
||||||
});
|
});
|
||||||
|
|
||||||
client.addEventListener('error', () => {
|
new_caret();
|
||||||
add_system_message(`\nConnection failed!\n`);
|
|
||||||
add_system_message("Ensure you entered the correct server URL, or check the console for more details.\n");
|
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* probes the `server_url` for a secure websocket connection first, an insecure websocket second, or resolves to `false` on failure.
|
* probes the `server_url` for a secure websocket connection first, an insecure websocket second, or resolves to `false` on failure.
|
||||||
* @param {string} server_url
|
* @param {string} server_url
|
||||||
* @returns a promise either resolving to the discovered protocol, or false on failure.
|
* @returns a promise either resolving to the created socket, or rejects to a string on failure.
|
||||||
*/
|
*/
|
||||||
function get_available_socket_protocol(server_url) {
|
function find_socket_at_url(server_url) {
|
||||||
return new Promise((Resolve, Reject) => {
|
return new Promise((Resolve, Reject) => {
|
||||||
const secure_client = new WebSocket("wss://" + server_url);
|
const secure_client = new WebSocket("wss://" + server_url);
|
||||||
|
add_client_events(secure_client);
|
||||||
|
|
||||||
secure_client.addEventListener('open', () => {
|
secure_client.addEventListener('open', () => {
|
||||||
Resolve("wss://");
|
Resolve(secure_client);
|
||||||
});
|
});
|
||||||
|
|
||||||
secure_client.addEventListener('error', () => {
|
secure_client.addEventListener('error', async () => {
|
||||||
|
set_enable_input(false);
|
||||||
|
if (!await user_confirm_insecure()) {
|
||||||
|
Reject('deny-insecure');
|
||||||
|
set_enable_input(true);
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
set_enable_input(true);
|
||||||
|
|
||||||
const insecure_client = new WebSocket("ws://" + server_url);
|
const insecure_client = new WebSocket("ws://" + server_url);
|
||||||
|
add_client_events(insecure_client);
|
||||||
|
|
||||||
insecure_client.addEventListener('open', () => {
|
insecure_client.addEventListener('open', () => {
|
||||||
Resolve("ws://");
|
Resolve(insecure_client);
|
||||||
});
|
});
|
||||||
|
|
||||||
insecure_client.addEventListener('error', () => {
|
insecure_client.addEventListener('error', () => {
|
||||||
Reject(false);
|
Reject('error');
|
||||||
});
|
});
|
||||||
|
|
||||||
});
|
});
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
@ -229,7 +213,7 @@ function add_system_message(text) {
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* the message handler for the websocket.
|
* handles incoming messages on the websocket.
|
||||||
*/
|
*/
|
||||||
function handle_message(event) {
|
function handle_message(event) {
|
||||||
var data;
|
var data;
|
||||||
|
|
|
@ -3,6 +3,8 @@ const http = require('http');
|
||||||
const path = require('path');
|
const path = require('path');
|
||||||
const Websocket = require('ws');
|
const Websocket = require('ws');
|
||||||
|
|
||||||
|
const VERSION = "1.1.0";
|
||||||
|
|
||||||
const MIME_TYPES = {
|
const MIME_TYPES = {
|
||||||
default: "application/octet-stream",
|
default: "application/octet-stream",
|
||||||
html: "text/html; charset=UTF-8",
|
html: "text/html; charset=UTF-8",
|
||||||
|
@ -23,6 +25,7 @@ const DATA_TYPES = {
|
||||||
backspace: 4,
|
backspace: 4,
|
||||||
backword: 5,
|
backword: 5,
|
||||||
arrow: 6,
|
arrow: 6,
|
||||||
|
version: 7,
|
||||||
};
|
};
|
||||||
|
|
||||||
const MOTDS = [
|
const MOTDS = [
|
||||||
|
@ -43,7 +46,7 @@ const MOTDS = [
|
||||||
];
|
];
|
||||||
|
|
||||||
const STATIC_PATH = path.join(process.cwd(), "public");
|
const STATIC_PATH = path.join(process.cwd(), "public");
|
||||||
const CACHE_MAX_AGE = 86400 // 1 day
|
const CACHE_MAX_AGE = 14400 // 4 hours
|
||||||
|
|
||||||
const BANNER =
|
const BANNER =
|
||||||
`Welcome to OpenTerminal!
|
`Welcome to OpenTerminal!
|
||||||
|
@ -58,9 +61,15 @@ Please acquire a genuine copy.
|
||||||
This connection will now terminate.
|
This connection will now terminate.
|
||||||
=========================================
|
=========================================
|
||||||
`;
|
`;
|
||||||
|
const VERSION_ERROR =
|
||||||
|
`Your client does not match this server's version of OpenTerminal (${VERSION})!
|
||||||
|
If you are connecting to this site's own OpenTerminal server, please refresh the page or clear your browser's cache to update.
|
||||||
|
`
|
||||||
|
|
||||||
|
console.log("using env vars: " + Object.keys(process.env).filter(value => { return value.startsWith("OPENTERM_") }).join(', '));
|
||||||
const PORT = process.env.PORT || 8080;
|
const HOST = process.env.OPENTERM_HOST || '0.0.0.0';
|
||||||
|
const PORT = process.env.OPENTERM_PORT || 8080;
|
||||||
|
const TRUSTED_PROXIES = process.env.OPENTERM_TRUSTED_PROXIES ? process.env.OPENTERM_TRUSTED_PROXIES.split(',') : [];
|
||||||
const PING_INTERVAL = 10000;
|
const PING_INTERVAL = 10000;
|
||||||
let sockets = [];
|
let sockets = [];
|
||||||
|
|
||||||
|
@ -81,20 +90,28 @@ async function get_file(url) {
|
||||||
|
|
||||||
// check for path traversal. path traversal is...bad.
|
// check for path traversal. path traversal is...bad.
|
||||||
const path_traversal = !file_path.startsWith(STATIC_PATH);
|
const path_traversal = !file_path.startsWith(STATIC_PATH);
|
||||||
const exists = await fs.promises.access(file_path).then(...[() => true, () => false]);
|
const exists = fs.existsSync(file_path) && fs.statSync(file_path).isFile();
|
||||||
if (path_traversal || !exists) return false;
|
if (path_traversal || !exists) return false;
|
||||||
|
|
||||||
const ext = path.extname(file_path).substring(1).toLowerCase();
|
const ext = path.extname(file_path).substring(1).toLowerCase();
|
||||||
const stream = fs.createReadStream(file_path);
|
|
||||||
return { stream, ext };
|
try {
|
||||||
|
const data = await fs.promises.readFile(file_path, { encoding: 'utf8' });
|
||||||
|
return { data, ext };
|
||||||
|
} catch (error) {
|
||||||
|
console.error(error);
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
const server = http.createServer(async (req, res) => {
|
const server = http.createServer(async (req, res) => {
|
||||||
|
const request_time = new Date().getTime();
|
||||||
const file = await get_file(req.url);
|
const file = await get_file(req.url);
|
||||||
if (!file) {
|
if (file === false) {
|
||||||
res.writeHead(404);
|
res.writeHead(404);
|
||||||
res.end();
|
res.end("404 not found!");
|
||||||
return;
|
return log_request(req, res, request_time);
|
||||||
}
|
}
|
||||||
const mime_type = MIME_TYPES[file.ext] || MIME_TYPES.default;
|
const mime_type = MIME_TYPES[file.ext] || MIME_TYPES.default;
|
||||||
res.writeHead(200, {
|
res.writeHead(200, {
|
||||||
|
@ -102,12 +119,26 @@ const server = http.createServer(async (req, res) => {
|
||||||
"Cache-Control": `max-age=${CACHE_MAX_AGE}`,
|
"Cache-Control": `max-age=${CACHE_MAX_AGE}`,
|
||||||
"Server": "OpenTerminal",
|
"Server": "OpenTerminal",
|
||||||
});
|
});
|
||||||
file.stream.pipe(res);
|
res.write(file.data);
|
||||||
// console.log(`${req.method} - ${req.url}`);
|
res.end();
|
||||||
|
return log_request(req, res, request_time);
|
||||||
});
|
});
|
||||||
|
|
||||||
|
function log_request(req, res, time) {
|
||||||
|
const elapsed = new Date().getTime() - time;
|
||||||
|
console.log(`${new Date().toISOString()} - ${req.method} ${req.url} - ${res.statusCode} - ${get_real_address(req)} in ${elapsed}ms`);
|
||||||
|
}
|
||||||
|
|
||||||
|
function get_real_address(req) {
|
||||||
|
if (TRUSTED_PROXIES.indexOf(req.socket.localAddress) !== -1 && req.headers['x-forwarded-for']) {
|
||||||
|
return req.headers['x-forwarded-for'];
|
||||||
|
}
|
||||||
|
return req.socket.localAddress;
|
||||||
|
}
|
||||||
|
|
||||||
const wss = new Websocket.Server({ server });
|
const wss = new Websocket.Server({ server });
|
||||||
wss.on('connection', socket => {
|
wss.on('connection', (socket, req) => {
|
||||||
|
console.log(`${new Date().toISOString()} - WS OPEN - ${get_real_address(req)} (active connections: ${sockets.length + 1})`);
|
||||||
/*
|
/*
|
||||||
socket.colour = generate_colour();
|
socket.colour = generate_colour();
|
||||||
socket.send(JSON.stringify({
|
socket.send(JSON.stringify({
|
||||||
|
@ -128,18 +159,16 @@ wss.on('connection', socket => {
|
||||||
}));
|
}));
|
||||||
}
|
}
|
||||||
|
|
||||||
const ping_interval = setInterval(
|
const ping_routine = setInterval(
|
||||||
function() {
|
function() {
|
||||||
socket.send(JSON.stringify({
|
socket.send(JSON.stringify({
|
||||||
type: DATA_TYPES.ping,
|
type: DATA_TYPES.ping,
|
||||||
}))
|
}))
|
||||||
}, PING_INTERVAL);
|
}, PING_INTERVAL);
|
||||||
socket.ping_interval = ping_interval;
|
socket.ping_routine = ping_routine;
|
||||||
|
|
||||||
sockets.push(socket);
|
sockets.push(socket);
|
||||||
|
|
||||||
// console.log(`new connection.\n\tcurrent connections: ${sockets.length}`);
|
|
||||||
|
|
||||||
socket.on('message', event => {
|
socket.on('message', event => {
|
||||||
try {
|
try {
|
||||||
handle_message(JSON.parse(event), socket)
|
handle_message(JSON.parse(event), socket)
|
||||||
|
@ -153,7 +182,7 @@ wss.on('connection', socket => {
|
||||||
});
|
});
|
||||||
|
|
||||||
socket.on('close', () => {
|
socket.on('close', () => {
|
||||||
clearInterval(socket.ping_interval);
|
clearInterval(socket.ping_routine);
|
||||||
sockets = sockets.filter(s => s !== socket);
|
sockets = sockets.filter(s => s !== socket);
|
||||||
// console.log(`connection closed.\n\tcurrent connections: ${sockets.length}`);
|
// console.log(`connection closed.\n\tcurrent connections: ${sockets.length}`);
|
||||||
});
|
});
|
||||||
|
@ -163,6 +192,27 @@ wss.on('connection', socket => {
|
||||||
* handles parsed JSON data sent by the client.
|
* handles parsed JSON data sent by the client.
|
||||||
*/
|
*/
|
||||||
function handle_message(data, user) {
|
function handle_message(data, user) {
|
||||||
|
if (user.version === undefined) {
|
||||||
|
if (data.type !== DATA_TYPES.version) {
|
||||||
|
user.send(JSON.stringify({
|
||||||
|
type: DATA_TYPES.text,
|
||||||
|
text: VERSION_ERROR
|
||||||
|
}));
|
||||||
|
user.close();
|
||||||
|
return;
|
||||||
|
} else if (data.text !== VERSION) {
|
||||||
|
user.send(JSON.stringify({
|
||||||
|
type: DATA_TYPES.text,
|
||||||
|
text: VERSION_ERROR
|
||||||
|
}));
|
||||||
|
user.close();
|
||||||
|
return;
|
||||||
|
} else {
|
||||||
|
user.version = data.text;
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
switch (data.type) {
|
switch (data.type) {
|
||||||
case DATA_TYPES.backword:
|
case DATA_TYPES.backword:
|
||||||
var break_point = buffer.lastIndexOf(" ");
|
var break_point = buffer.lastIndexOf(" ");
|
||||||
|
@ -220,8 +270,9 @@ function generate_colour() {
|
||||||
return result;
|
return result;
|
||||||
}
|
}
|
||||||
|
|
||||||
server.listen(PORT, () => {
|
server.listen(PORT, HOST, () => {
|
||||||
console.log(`OpenTerminal is now LIVE on http://127.0.0.1:${PORT}`);
|
console.log(`OpenTerminal is now LIVE on http://${HOST === '0.0.0.0' ? '127.0.0.1' : HOST}:${PORT}`);
|
||||||
|
if (TRUSTED_PROXIES.length > 0) console.log(`Using X-Forwarded-For headers for hosts: ${TRUSTED_PROXIES.join(", ")}`);
|
||||||
});
|
});
|
||||||
|
|
||||||
/**
|
/**
|
||||||
|
|
Loading…
Reference in a new issue