Verified Commit 5a0a4485 authored by Maxime FRIESS's avatar Maxime FRIESS 💙
Browse files

[WS] Basic WebSocket echo server

parents
config/hardconfig.json
config/softconfig.json
node_modules
cache.json
*.log
/**
* Copyright © 2021 Maxime Friess <M4x1me@pm.me>
*
* This file is part of Seb-Gateway.
*
* Seb-Gateway is free software: you can redistribute it and/or modify
* it under the terms of the GNU Affero General Public License as published by
* the Free Software Foundation, either version 3 of the License, or
* (at your option) any later version.
*
* Seb-Gateway is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU Affero General Public License for more details.
*
* You should have received a copy of the GNU Affero General Public License
* along with Seb-Gateway. If not, see <https://www.gnu.org/licenses/>.
*/
import main from './src/main.js';
main();
{
"verbose": true,
"ignore": [
"**.json"
]
}
{
"name": "seb-gateway",
"version": "dev",
"main": "index.js",
"repository": "https://git.unistra.fr/amicale-core/seb-gateway",
"author": "M4x1m3 <M4x1me@protonmail.com>",
"license": "AGPLv3",
"private": false,
"type": "module",
"dependencies": {
"ws": "^8.2.3"
},
"scripts": {
"start": "node index.js",
"debug": "nodemon index.js"
},
"devDependencies": {
"nodemon": "^2.0.13"
}
}
/**
* Copyright © 2021 Maxime Friess <M4x1me@pm.me>
*
* This file is part of Seb-Gateway.
*
* Seb-Gateway is free software: you can redistribute it and/or modify
* it under the terms of the GNU Affero General Public License as published by
* the Free Software Foundation, either version 3 of the License, or
* (at your option) any later version.
*
* Seb-Gateway is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU Affero General Public License for more details.
*
* You should have received a copy of the GNU Affero General Public License
* along with Seb-Gateway. If not, see <https://www.gnu.org/licenses/>.
*/
import Gateway from "./Gateway.js";
import Loggers from "./utils/Logger.js";
let lastId = 0;
class Client {
constructor(ws) {
this.__id = lastId++;
this.__logger = Loggers.getLogger("Client", this.__id);
this.__logger.info("Accepted connection.");
this.__ws = ws;
this.__ws.on("close", this.close.bind(this));
this.__ws.on("pong", this.heartbeat.bind(this));
this.__ws.on("message", this.message.bind(this));
this.__alive = true;
}
getId() {
return this.__id;
}
message(data) {
this.__ws.send(data);
}
heartbeat() {
this.__alive = true;
}
ping() {
if (!this.__alive)
return this.__ws.terminate();
this.__alive = false;
this.__ws.ping();
}
close() {
this.__logger.info("Closed connection.");
Gateway.removeClient(this);
}
}
export default Client;
/**
* Copyright © 2021 Maxime Friess <M4x1me@pm.me>
*
* This file is part of Seb-Gateway.
*
* Seb-Gateway is free software: you can redistribute it and/or modify
* it under the terms of the GNU Affero General Public License as published by
* the Free Software Foundation, either version 3 of the License, or
* (at your option) any later version.
*
* Seb-Gateway is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU Affero General Public License for more details.
*
* You should have received a copy of the GNU Affero General Public License
* along with Seb-Gateway. If not, see <https://www.gnu.org/licenses/>.
*/
import { createServer } from 'http';
import { WebSocketServer } from 'ws';
import Client from './Client.js';
import HardConfig from './config/HardConfig.js';
import Loggers from './utils/Logger.js';
class _Gateway {
constructor() {
this.__server = createServer();
this.__ws = new WebSocketServer({ server: this.__server });
this.__ws.on('connection', this.__connect.bind(this));
this.__logger = Loggers.getLogger("Gateway");
this.__clients = {};
}
__connect(ws, req) {
const ip = HardConfig.getWsProxy() ? req.headers['x-forwarded-for'].split(',')[0].trim() : req.socket.remoteAddress;
this.__logger.info(`Accepted connection from ${ip}.`);
const client = new Client(ws);
this.__clients[client.getId()] = client;
}
start() {
this.__logger.info(`Starting gateway on ${HardConfig.getWsIp()}:${HardConfig.getWsPort()}...`);
this.__server.listen(HardConfig.getWsPort(), HardConfig.getWsIp());
this.__interval = setInterval(this.ping.bind(this), 10000);
}
ping() {
for (let k of Object.keys(this.__clients)) {
this.__clients[k].ping();
}
}
removeClient(client) {
delete this.__clients[client.getId()];
}
}
const Gateway = new _Gateway();
export default Gateway;
/**
* Copyright © 2021 Maxime Friess <M4x1me@pm.me>
*
* This file is part of Seb-Gateway.
*
* Seb-Gateway is free software: you can redistribute it and/or modify
* it under the terms of the GNU Affero General Public License as published by
* the Free Software Foundation, either version 3 of the License, or
* (at your option) any later version.
*
* Seb-Gateway is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU Affero General Public License for more details.
*
* You should have received a copy of the GNU Affero General Public License
* along with Seb-Gateway. If not, see <https://www.gnu.org/licenses/>.
*/
import fs from 'fs';
import Loggers from '../utils/Logger.js';
const HARDCONFIG_FILE = 'config/hardconfig.json';
/**
* Class that parses the hardconfig.json file.
*
* This config is meant to not be editable nor reloadable,
* we thus only load it when constructing.
*/
class _HardConfig {
constructor() {
this.__logger = Loggers.getLogger("HardConfig");
// Check if config dir exists, create if not.
if (!fs.existsSync('config')) {
this.__logger.warn("Config directory doesn't exists, creating...");
try {
fs.mkdirSync('config');
} catch (e) {
this.__logger.fatal("Failed to create config directory!", e, 1);
return;
}
}
if (!fs.existsSync(HARDCONFIG_FILE)) {
try {
fs.writeFileSync(HARDCONFIG_FILE, JSON.stringify(this.__getDefaultConfig()));
} catch (e) {
this.__logger.fatal("Failed to create config file!", e, 1);
return;
}
this.__logger.fatal("Hardconfig created. Please edit it to your need and restart Seb-BOT.");
return;
}
try {
this.__config = JSON.parse(fs.readFileSync(HARDCONFIG_FILE, { encoding: "utf-8" }));
} catch (e) {
this.__logger.fatal("Failed to read config file!", e, 2);
return;
}
}
getWsIp() {
return this.__config.ws.ip;
}
getWsPort() {
return this.__config.ws.port;
}
getWsProxy() {
return this.__config.ws.proxy;
}
__getDefaultConfig() {
return { ws: { ip: "127.0.0.1", port: 8080, proxy: false } }
}
}
const HardConfig = new _HardConfig();
export default HardConfig;
/**
* Copyright © 2021 Maxime Friess <M4x1me@pm.me>
*
* This file is part of Seb-Gateway.
*
* Seb-Gateway is free software: you can redistribute it and/or modify
* it under the terms of the GNU Affero General Public License as published by
* the Free Software Foundation, either version 3 of the License, or
* (at your option) any later version.
*
* Seb-Gateway is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU Affero General Public License for more details.
*
* You should have received a copy of the GNU Affero General Public License
* along with Seb-Gateway. If not, see <https://www.gnu.org/licenses/>.
*/
import Gateway from "./Gateway.js";
import Loggers from "./utils/Logger.js";
const main = () => {
Loggers.getLogger("Main").info("Starting Seb-Gateway");
Gateway.start();
};
export default main;
/**
* Copyright © 2021 Maxime Friess <M4x1me@pm.me>
*
* This file is part of Seb-Gateway.
*
* Seb-Gateway is free software: you can redistribute it and/or modify
* it under the terms of the GNU Affero General Public License as published by
* the Free Software Foundation, either version 3 of the License, or
* (at your option) any later version.
*
* Seb-Gateway is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU Affero General Public License for more details.
*
* You should have received a copy of the GNU Affero General Public License
* along with Seb-Gateway. If not, see <https://www.gnu.org/licenses/>.
*/
import process from "process";
/**
* Logger, used to log things.
*/
class Logger {
/**
* Construct a new logger
*
* @param {string} name Name of the logger
*/
constructor(name) {
this.__name = name;
}
/**
* Get the name of the logger
* @returns Name of the logger
*/
getName() {
return this.__name;
}
/**
* Writes.
*
* @param {string} string String to write
* @param {string} prefix Prefix
* @param {function} logfnc Function to call to write
*/
__write(string, prefix, logfnc) {
const date = new Date().toISOString();
const pref = "[" + date + "][" + this.__name + "][" + prefix + "] ";
for (let line of string.toString().split('\n')) {
logfnc(pref + line);
}
}
info(message) {
this.__write(message, "INFO", console.info);
}
warn(message) {
this.__write(message, "WARN", console.warn);
}
error(message, error) {
this.__write(message, "ERR!", console.error);
if (error !== undefined)
this.__write(error.stack, "ERR!", console.error);
}
fatal(message, error, code) {
this.__write(message, "FTAL", console.error);
if (error !== undefined)
this.__write(error.stack, "FTAL", console.error);
process.exit(code !== undefined ? code : -1);
}
}
class __Loggers {
constructor() {
this.__loggers = {};
}
getLogger(name, id) {
if (id === undefined) {
if (!(name in this.__loggers)) {
this.__loggers[name] = new Logger(name);
}
return this.__loggers[name];
} else {
return new Logger(name + " #" + id);
}
}
}
const Loggers = new __Loggers();
export default Loggers;
This diff is collapsed.
Supports Markdown
0% or .
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment