aboutsummaryrefslogtreecommitdiffstats
path: root/server/src
diff options
context:
space:
mode:
authormarzavec <admin@marzavec.com>2019-04-08 02:04:10 +0200
committermarzavec <admin@marzavec.com>2019-04-08 02:04:10 +0200
commitf353ecbd9e9e8e409303bb4f5c5590e95f579869 (patch)
tree3345967ae59a46f59fe86c0d6fda432115594927 /server/src
parentrefactoring 1 of 2 (diff)
downloadhackchat-f353ecbd9e9e8e409303bb4f5c5590e95f579869.tar.gz
hackchat-f353ecbd9e9e8e409303bb4f5c5590e95f579869.zip
refactoring 2 of 2
Diffstat (limited to 'server/src')
-rw-r--r--server/src/commands/admin/addmod.js2
-rw-r--r--server/src/commands/admin/listusers.js2
-rw-r--r--server/src/commands/admin/reload.js8
-rw-r--r--server/src/commands/admin/removemod.js2
-rw-r--r--server/src/commands/admin/saveconfig.js2
-rw-r--r--server/src/commands/admin/shout.js2
-rw-r--r--server/src/commands/core/changenick.js4
-rw-r--r--server/src/commands/core/chat.js4
-rw-r--r--server/src/commands/core/emote.js4
-rw-r--r--server/src/commands/core/help.js4
-rw-r--r--server/src/commands/core/invite.js2
-rw-r--r--server/src/commands/core/join.js2
-rw-r--r--server/src/commands/core/move.js2
-rw-r--r--server/src/commands/core/whisper.js4
-rw-r--r--server/src/commands/internal/disconnect.js4
-rw-r--r--server/src/commands/internal/socketreply.js4
-rw-r--r--server/src/commands/mod/ban.js4
-rw-r--r--server/src/commands/mod/dumb.js4
-rw-r--r--server/src/commands/mod/kick.js2
-rw-r--r--server/src/commands/mod/moveuser.js2
-rw-r--r--server/src/commands/mod/speak.js2
-rw-r--r--server/src/commands/mod/unban.js4
-rw-r--r--server/src/commands/mod/unbanall.js4
-rw-r--r--server/src/scripts/setupSchema/Footer.js2
-rw-r--r--server/src/serverLib/CommandManager.js95
-rw-r--r--server/src/serverLib/ConfigManager.js15
-rw-r--r--server/src/serverLib/CoreApp.js1
-rw-r--r--server/src/serverLib/ImportsManager.js63
-rw-r--r--server/src/serverLib/MainServer.js138
-rw-r--r--server/src/serverLib/RateLimiter.js34
-rw-r--r--server/src/serverLib/StatsManager.js2
31 files changed, 242 insertions, 182 deletions
diff --git a/server/src/commands/admin/addmod.js b/server/src/commands/admin/addmod.js
index a30b175..6853a42 100644
--- a/server/src/commands/admin/addmod.js
+++ b/server/src/commands/admin/addmod.js
@@ -6,7 +6,7 @@
exports.run = async (core, server, socket, data) => {
// increase rate limit chance and ignore if not admin
if (socket.uType != 'admin') {
- return server._police.frisk(socket.remoteAddress, 20);
+ return server.police.frisk(socket.remoteAddress, 20);
}
// add new trip to config
diff --git a/server/src/commands/admin/listusers.js b/server/src/commands/admin/listusers.js
index 472b89d..5ff350f 100644
--- a/server/src/commands/admin/listusers.js
+++ b/server/src/commands/admin/listusers.js
@@ -6,7 +6,7 @@
exports.run = async (core, server, socket, data) => {
// increase rate limit chance and ignore if not admin
if (socket.uType != 'admin') {
- return server._police.frisk(socket.remoteAddress, 20);
+ return server.police.frisk(socket.remoteAddress, 20);
}
// find all users currently in a channel
diff --git a/server/src/commands/admin/reload.js b/server/src/commands/admin/reload.js
index 27be19c..588d1c5 100644
--- a/server/src/commands/admin/reload.js
+++ b/server/src/commands/admin/reload.js
@@ -6,11 +6,11 @@
exports.run = async (core, server, socket, data) => {
// increase rate limit chance and ignore if not admin
if (socket.uType != 'admin') {
- return server._police.frisk(socket.remoteAddress, 20);
+ return server.police.frisk(socket.remoteAddress, 20);
}
// do command reload and store results
- let loadResult = core.dynamicImports.reloadDirCache('src/commands');
+ let loadResult = core.dynamicImports.reloadDirCache();
loadResult += core.commands.loadCommands();
// clear and rebuild all module hooks
@@ -18,9 +18,9 @@ exports.run = async (core, server, socket, data) => {
// build reply based on reload results
if (loadResult == '') {
- loadResult = `Reloaded ${core.commands._commands.length} commands, 0 errors`;
+ loadResult = `Reloaded ${core.commands.commands.length} commands, 0 errors`;
} else {
- loadResult = `Reloaded ${core.commands._commands.length} commands, error(s):
+ loadResult = `Reloaded ${core.commands.commands.length} commands, error(s):
${loadResult}`;
}
diff --git a/server/src/commands/admin/removemod.js b/server/src/commands/admin/removemod.js
index 87f4124..a2d862c 100644
--- a/server/src/commands/admin/removemod.js
+++ b/server/src/commands/admin/removemod.js
@@ -6,7 +6,7 @@
exports.run = async (core, server, socket, data) => {
// increase rate limit chance and ignore if not admin
if (socket.uType != 'admin') {
- return server._police.frisk(socket.remoteAddress, 20);
+ return server.police.frisk(socket.remoteAddress, 20);
}
// remove trip from config
diff --git a/server/src/commands/admin/saveconfig.js b/server/src/commands/admin/saveconfig.js
index a95a39a..65fff0e 100644
--- a/server/src/commands/admin/saveconfig.js
+++ b/server/src/commands/admin/saveconfig.js
@@ -6,7 +6,7 @@
exports.run = async (core, server, socket, data) => {
// increase rate limit chance and ignore if not admin
if (socket.uType != 'admin') {
- return server._police.frisk(socket.remoteAddress, 20);
+ return server.police.frisk(socket.remoteAddress, 20);
}
// attempt save, notify of failure
diff --git a/server/src/commands/admin/shout.js b/server/src/commands/admin/shout.js
index e9c69e0..821a22a 100644
--- a/server/src/commands/admin/shout.js
+++ b/server/src/commands/admin/shout.js
@@ -6,7 +6,7 @@
exports.run = async (core, server, socket, data) => {
// increase rate limit chance and ignore if not admin
if (socket.uType != 'admin') {
- return server._police.frisk(socket.remoteAddress, 20);
+ return server.police.frisk(socket.remoteAddress, 20);
}
// send text to all channels
diff --git a/server/src/commands/core/changenick.js b/server/src/commands/core/changenick.js
index ae057b3..cb6d1d0 100644
--- a/server/src/commands/core/changenick.js
+++ b/server/src/commands/core/changenick.js
@@ -7,7 +7,7 @@ const verifyNickname = (nick) => /^[a-zA-Z0-9_]{1,24}$/.test(nick);
// module main
exports.run = async (core, server, socket, data) => {
- if (server._police.frisk(socket.remoteAddress, 6)) {
+ if (server.police.frisk(socket.remoteAddress, 6)) {
return server.reply({
cmd: 'warn',
text: 'You are changing nicknames too fast. Wait a moment before trying again.'
@@ -31,7 +31,7 @@ exports.run = async (core, server, socket, data) => {
// prevent admin impersonation
// TODO: prevent mod impersonation
if (newNick.toLowerCase() == core.config.adminName.toLowerCase()) {
- server._police.frisk(socket.remoteAddress, 4);
+ server.police.frisk(socket.remoteAddress, 4);
return server.reply({
cmd: 'warn',
diff --git a/server/src/commands/core/chat.js b/server/src/commands/core/chat.js
index bb9584e..7b7e79e 100644
--- a/server/src/commands/core/chat.js
+++ b/server/src/commands/core/chat.js
@@ -24,12 +24,12 @@ exports.run = async (core, server, socket, data) => {
if (!text) {
// lets not send objects or empty text, yea?
- return server._police.frisk(socket.remoteAddress, 13);
+ return server.police.frisk(socket.remoteAddress, 13);
}
// check for spam
let score = text.length / 83 / 4;
- if (server._police.frisk(socket.remoteAddress, score)) {
+ if (server.police.frisk(socket.remoteAddress, score)) {
return server.reply({
cmd: 'warn',
text: 'You are sending too much text. Wait a moment and try again.\nPress the up arrow key to restore your last message.'
diff --git a/server/src/commands/core/emote.js b/server/src/commands/core/emote.js
index 40fcff2..f51f854 100644
--- a/server/src/commands/core/emote.js
+++ b/server/src/commands/core/emote.js
@@ -24,12 +24,12 @@ exports.run = async (core, server, socket, payload) => {
if (!text) {
// lets not send objects or empty text, yea?
- return server._police.frisk(socket.remoteAddress, 8);
+ return server.police.frisk(socket.remoteAddress, 8);
}
// check for spam
let score = text.length / 83 / 4;
- if (server._police.frisk(socket.remoteAddress, score)) {
+ if (server.police.frisk(socket.remoteAddress, score)) {
return server.reply({
cmd: 'warn',
text: 'You are sending too much text. Wait a moment and try again.\nPress the up arrow key to restore your last message.'
diff --git a/server/src/commands/core/help.js b/server/src/commands/core/help.js
index 51f6d87..8eccdb6 100644
--- a/server/src/commands/core/help.js
+++ b/server/src/commands/core/help.js
@@ -8,7 +8,7 @@ const stripIndents = require('common-tags').stripIndents;
// module main
exports.run = async (core, server, socket, payload) => {
// check for spam
- if (server._police.frisk(socket.remoteAddress, 2)) {
+ if (server.police.frisk(socket.remoteAddress, 2)) {
return server.reply({
cmd: 'warn',
text: 'You are sending too much text. Wait a moment and try again.\nPress the up arrow key to restore your last message.'
@@ -27,7 +27,7 @@ exports.run = async (core, server, socket, payload) => {
API: {cmd: 'help', command: '<command name>'}`;
reply += '\n\n-------------------------------------\n\n';
- let categories = core.commands.categories().sort();
+ let categories = core.commands.categoriesList.sort();
for (let i = 0, j = categories.length; i < j; i++) {
reply += `${categories[i].replace('../src/commands/', '').replace(/^\w/, c => c.toUpperCase())} Commands:\n`;
let catCommands = core.commands.all(categories[i]).sort((a, b) => a.info.name.localeCompare(b.info.name));
diff --git a/server/src/commands/core/invite.js b/server/src/commands/core/invite.js
index 046e47a..70393b1 100644
--- a/server/src/commands/core/invite.js
+++ b/server/src/commands/core/invite.js
@@ -8,7 +8,7 @@ const verifyNickname = (nick) => /^[a-zA-Z0-9_]{1,24}$/.test(nick);
// module main
exports.run = async (core, server, socket, data) => {
// check for spam
- if (server._police.frisk(socket.remoteAddress, 2)) {
+ if (server.police.frisk(socket.remoteAddress, 2)) {
return server.reply({
cmd: 'warn',
text: 'You are sending invites too fast. Wait a moment before trying again.'
diff --git a/server/src/commands/core/join.js b/server/src/commands/core/join.js
index ce900db..965a8bc 100644
--- a/server/src/commands/core/join.js
+++ b/server/src/commands/core/join.js
@@ -55,7 +55,7 @@ exports.parseNickname = (core, data) => {
// module main
exports.run = async (core, server, socket, data) => {
// check for spam
- if (server._police.frisk(socket.remoteAddress, 3)) {
+ if (server.police.frisk(socket.remoteAddress, 3)) {
return server.reply({
cmd: 'warn',
text: 'You are joining channels too fast. Wait a moment and try again.'
diff --git a/server/src/commands/core/move.js b/server/src/commands/core/move.js
index e85f481..8e97a06 100644
--- a/server/src/commands/core/move.js
+++ b/server/src/commands/core/move.js
@@ -5,7 +5,7 @@
// module main
exports.run = async (core, server, socket, data) => {
// check for spam
- if (server._police.frisk(socket.remoteAddress, 6)) {
+ if (server.police.frisk(socket.remoteAddress, 6)) {
return server.reply({
cmd: 'warn',
text: 'You are changing channels too fast. Wait a moment before trying again.'
diff --git a/server/src/commands/core/whisper.js b/server/src/commands/core/whisper.js
index c515d43..109889d 100644
--- a/server/src/commands/core/whisper.js
+++ b/server/src/commands/core/whisper.js
@@ -26,12 +26,12 @@ exports.run = async (core, server, socket, payload) => {
if (!text) {
// lets not send objects or empty text, yea?
- return server._police.frisk(socket.remoteAddress, 13);
+ return server.police.frisk(socket.remoteAddress, 13);
}
// check for spam
let score = text.length / 83 / 4;
- if (server._police.frisk(socket.remoteAddress, score)) {
+ if (server.police.frisk(socket.remoteAddress, score)) {
return server.reply({
cmd: 'warn',
text: 'You are sending too much text. Wait a moment and try again.\nPress the up arrow key to restore your last message.'
diff --git a/server/src/commands/internal/disconnect.js b/server/src/commands/internal/disconnect.js
index 8656779..520f8cb 100644
--- a/server/src/commands/internal/disconnect.js
+++ b/server/src/commands/internal/disconnect.js
@@ -5,9 +5,9 @@
// module main
exports.run = async (core, server, socket, data) => {
- if (data.cmdKey !== server._cmdKey) {
+ if (data.cmdKey !== server.cmdKey) {
// internal command attempt by client, increase rate limit chance and ignore
- return server._police.frisk(socket.remoteAddress, 20);
+ return server.police.frisk(socket.remoteAddress, 20);
}
// send leave notice to client peers
diff --git a/server/src/commands/internal/socketreply.js b/server/src/commands/internal/socketreply.js
index fa3b8cf..5dadaf6 100644
--- a/server/src/commands/internal/socketreply.js
+++ b/server/src/commands/internal/socketreply.js
@@ -4,9 +4,9 @@
// module main
exports.run = async (core, server, socket, data) => {
- if (data.cmdKey !== server._cmdKey) {
+ if (data.cmdKey !== server.cmdKey) {
// internal command attempt by client, increase rate limit chance and ignore
- return server._police.frisk(socket.remoteAddress, 20);
+ return server.police.frisk(socket.remoteAddress, 20);
}
// send warning to target socket
diff --git a/server/src/commands/mod/ban.js b/server/src/commands/mod/ban.js
index 93c536b..9c8eb4f 100644
--- a/server/src/commands/mod/ban.js
+++ b/server/src/commands/mod/ban.js
@@ -6,7 +6,7 @@
exports.run = async (core, server, socket, data) => {
// increase rate limit chance and ignore if not admin or mod
if (socket.uType === 'user') {
- return server._police.frisk(socket.remoteAddress, 10);
+ return server.police.frisk(socket.remoteAddress, 10);
}
// check user input
@@ -36,7 +36,7 @@ exports.run = async (core, server, socket, data) => {
}
// commit arrest record
- server._police.arrest(badClient.remoteAddress, badClient.hash);
+ server.police.arrest(badClient.remoteAddress, badClient.hash);
console.log(`${socket.nick} [${socket.trip}] banned ${targetNick} in ${socket.channel}`);
diff --git a/server/src/commands/mod/dumb.js b/server/src/commands/mod/dumb.js
index d64c746..d5e8fee 100644
--- a/server/src/commands/mod/dumb.js
+++ b/server/src/commands/mod/dumb.js
@@ -14,7 +14,7 @@ exports.init = (core) => {
exports.run = async (core, server, socket, data) => {
// increase rate limit chance and ignore if not admin or mod
if (socket.uType === 'user') {
- return server._police.frisk(socket.remoteAddress, 10);
+ return server.police.frisk(socket.remoteAddress, 10);
}
// check user input
@@ -93,7 +93,7 @@ exports.chatCheck = (core, server, socket, payload) => {
}
// blanket "spam" protection, may expose the ratelimiting lines from `chat` and use that, TODO: one day #lazydev
- server._police.frisk(socket.remoteAddress, 9);
+ server.police.frisk(socket.remoteAddress, 9);
return false;
}
diff --git a/server/src/commands/mod/kick.js b/server/src/commands/mod/kick.js
index 808defa..f3bc7ca 100644
--- a/server/src/commands/mod/kick.js
+++ b/server/src/commands/mod/kick.js
@@ -6,7 +6,7 @@
exports.run = async (core, server, socket, data) => {
// increase rate limit chance and ignore if not admin or mod
if (socket.uType === 'user') {
- return server._police.frisk(socket.remoteAddress, 10);
+ return server.police.frisk(socket.remoteAddress, 10);
}
// check user input
diff --git a/server/src/commands/mod/moveuser.js b/server/src/commands/mod/moveuser.js
index e4f6c22..c7fc4bf 100644
--- a/server/src/commands/mod/moveuser.js
+++ b/server/src/commands/mod/moveuser.js
@@ -6,7 +6,7 @@
exports.run = async (core, server, socket, data) => {
// increase rate limit chance and ignore if not admin or mod
if (socket.uType === 'user') {
- return server._police.frisk(socket.remoteAddress, 10);
+ return server.police.frisk(socket.remoteAddress, 10);
}
// check user input
diff --git a/server/src/commands/mod/speak.js b/server/src/commands/mod/speak.js
index e2a3ef7..23fc4de 100644
--- a/server/src/commands/mod/speak.js
+++ b/server/src/commands/mod/speak.js
@@ -14,7 +14,7 @@
exports.run = async (core, server, socket, data) => {
// increase rate limit chance and ignore if not admin or mod
if (socket.uType === 'user') {
- return server._police.frisk(socket.remoteAddress, 10);
+ return server.police.frisk(socket.remoteAddress, 10);
}
// check user input
diff --git a/server/src/commands/mod/unban.js b/server/src/commands/mod/unban.js
index 71be9bb..6744d9d 100644
--- a/server/src/commands/mod/unban.js
+++ b/server/src/commands/mod/unban.js
@@ -6,7 +6,7 @@
exports.run = async (core, server, socket, data) => {
// increase rate limit chance and ignore if not admin or mod
if (socket.uType === 'user') {
- return server._police.frisk(socket.remoteAddress, 10);
+ return server.police.frisk(socket.remoteAddress, 10);
}
// check user input
@@ -28,7 +28,7 @@ exports.run = async (core, server, socket, data) => {
}
// remove arrest record
- server._police.pardon(target);
+ server.police.pardon(target);
// mask ip if used
if (mode === 'ip') {
diff --git a/server/src/commands/mod/unbanall.js b/server/src/commands/mod/unbanall.js
index a9cf682..c285b80 100644
--- a/server/src/commands/mod/unbanall.js
+++ b/server/src/commands/mod/unbanall.js
@@ -6,11 +6,11 @@
exports.run = async (core, server, socket, data) => {
// increase rate limit chance and ignore if not admin or mod
if (socket.uType === 'user') {
- return server._police.frisk(socket.remoteAddress, 10);
+ return server.police.frisk(socket.remoteAddress, 10);
}
// remove arrest records
- server._police._records = {};
+ server.police.records = {};
console.log(`${socket.nick} [${socket.trip}] unbanned all`);
diff --git a/server/src/scripts/setupSchema/Footer.js b/server/src/scripts/setupSchema/Footer.js
index d2ee14f..5fb627b 100644
--- a/server/src/scripts/setupSchema/Footer.js
+++ b/server/src/scripts/setupSchema/Footer.js
@@ -4,4 +4,6 @@
*
*/
+console.log('');
console.log('Config generated! You may now start the server normally.');
+console.log('');
diff --git a/server/src/serverLib/CommandManager.js b/server/src/serverLib/CommandManager.js
index 71c8884..a99d0a9 100644
--- a/server/src/serverLib/CommandManager.js
+++ b/server/src/serverLib/CommandManager.js
@@ -10,6 +10,9 @@
const path = require('path');
const didYouMean = require('didyoumean2').default;
+// default command modules path
+const CmdDir = 'src/commands';
+
class CommandManager {
/**
* Create a `CommandManager` instance for handling commands/protocol
@@ -18,26 +21,25 @@ class CommandManager {
*/
constructor (core) {
this.core = core;
- this._commands = [];
- this._categories = [];
+ this.commands = [];
+ this.categories = [];
}
/**
* (Re)initializes name spaces for commands and starts load routine
*
+ * @return {String} Module errors or empty if none
*/
loadCommands () {
- this._commands = [];
- this._categories = [];
-
- const core = this.core;
+ this.commands = [];
+ this.categories = [];
- const commandImports = core.dynamicImports.getImport('src/commands');
+ const commandImports = this.core.dynamicImports.getImport(CmdDir);
let cmdErrors = '';
Object.keys(commandImports).forEach(file => {
let command = commandImports[file];
let name = path.basename(file);
- cmdErrors += this._validateAndLoad(command, file, name);
+ cmdErrors += this.validateAndLoad(command, file, name);
});
return cmdErrors;
@@ -49,9 +51,11 @@ class CommandManager {
* @param {Object} command reference to the newly loaded object
* @param {String} file file path to the module
* @param {String} name command (`cmd`) name
+ *
+ * @return {String} Module errors or empty if none
*/
- _validateAndLoad (command, file, name) {
- let error = this._validateCommand(command);
+ validateAndLoad (command, file, name) {
+ let error = this.validateCommand(command);
if (error) {
let errText = `Failed to load '${name}': ${error}`;
@@ -70,8 +74,8 @@ class CommandManager {
command.info.category = category;
- if (this._categories.indexOf(category) === -1) {
- this._categories.push(category);
+ if (this.categories.indexOf(category) === -1) {
+ this.categories.push(category);
}
}
@@ -85,7 +89,7 @@ class CommandManager {
}
}
- this._commands.push(command);
+ this.commands.push(command);
return '';
}
@@ -94,8 +98,10 @@ class CommandManager {
* Checks the module after having been `require()`ed in and reports errors
*
* @param {Object} object reference to the newly loaded object
+ *
+ * @return {String} Module errors or null if none
*/
- _validateCommand (object) {
+ validateCommand (object) {
if (typeof object !== 'object')
return 'command setup is invalid';
@@ -115,27 +121,37 @@ class CommandManager {
* Pulls all command names from a passed `category`
*
* @param {String} category [Optional] filter return results by this category
+ *
+ * @return {Array} Array of command modules matching the category
*/
all (category) {
- return !category ? this._commands : this._commands.filter(c => c.info.category.toLowerCase() === category.toLowerCase());
+ return !category ? this.commands : this.commands.filter(
+ c => c.info.category.toLowerCase() === category.toLowerCase()
+ );
}
/**
* Pulls all category names
*
+ * @return {Array} Array of sub directories under CmdDir
*/
- categories () {
- return this._categories;
+ get categoriesList () {
+ return this.categories;
}
/**
* Pulls command by name or alia(s)
*
* @param {String} name name or alias of command
+ *
+ * @return {Object} Target command module object
*/
get (name) {
return this.findBy('name', name)
- || this._commands.find(command => command.info.aliases instanceof Array && command.info.aliases.indexOf(name) > -1);
+ || this.commands.find(
+ command => command.info.aliases instanceof Array &&
+ command.info.aliases.indexOf(name) > -1
+ );
}
/**
@@ -143,9 +159,11 @@ class CommandManager {
*
* @param {String} key name or alias of command
* @param {String} value name or alias of command
+ *
+ * @return {Object} Target command module object
*/
findBy (key, value) {
- return this._commands.find(c => c.info[key] === value);
+ return this.commands.find(c => c.info[key] === value);
}
/**
@@ -154,7 +172,9 @@ class CommandManager {
* @param {Object} server main server object
*/
initCommandHooks (server) {
- this._commands.filter(c => typeof c.initHooks !== 'undefined').forEach(c => c.initHooks(server));
+ this.commands.filter(c => typeof c.initHooks !== 'undefined').forEach(
+ c => c.initHooks(server)
+ );
}
/**
@@ -163,6 +183,8 @@ class CommandManager {
* @param {Object} server main server reference
* @param {Object} socket calling socket reference
* @param {Object} data command structure passed by socket (client)
+ *
+ * @return {*} Arbitrary module return data
*/
handleCommand (server, socket, data) {
// Try to find command first
@@ -172,7 +194,7 @@ class CommandManager {
return this.execute(command, server, socket, data);
} else {
// Then fail with helpful (sorta) message
- return this._handleFail(server, socket, data);
+ return this.handleFail(server, socket, data);
}
}
@@ -182,8 +204,10 @@ class CommandManager {
* @param {Object} server main server reference
* @param {Object} socket calling socket reference
* @param {Object} data command structure passed by socket (client)
+ *
+ * @return {*} Arbitrary module return data
*/
- _handleFail(server, socket, data) {
+ handleFail (server, socket, data) {
const maybe = didYouMean(data.cmd, this.all().map(c => c.info.name), {
threshold: 5,
thresholdType: 'edit-distance'
@@ -193,13 +217,17 @@ class CommandManager {
// Found a suggestion, pass it on to their dyslexic self
return this.handleCommand(server, socket, {
cmd: 'socketreply',
- cmdKey: server._cmdKey,
+ cmdKey: server.cmdKey,
text: `Command not found, did you mean: \`${maybe}\`?`
});
}
- // Request so mangled that I don't even, silently fail
- return;
+ // Request so mangled that I don't even. . .
+ return this.handleCommand(server, socket, {
+ cmd: 'socketreply',
+ cmdKey: server.cmdKey,
+ text: 'Unknown command'
+ });
}
/**
@@ -209,8 +237,10 @@ class CommandManager {
* @param {Object} server main server reference
* @param {Object} socket calling socket reference
* @param {Object} data command structure passed by socket (client)
+ *
+ * @return {*} Arbitrary module return data
*/
- async execute(command, server, socket, data) {
+ async execute (command, server, socket, data) {
if (typeof command.requiredData !== 'undefined') {
let missing = [];
for (let i = 0, len = command.requiredData.length; i < len; i++) {
@@ -219,11 +249,16 @@ class CommandManager {
}
if (missing.length > 0) {
- console.log(`Failed to execute '${command.info.name}': missing required ${missing.join(', ')}\n\n`);
+ console.log(`Failed to execute '${
+ command.info.name
+ }': missing required ${missing.join(', ')}\n\n`);
+
this.handleCommand(server, socket, {
cmd: 'socketreply',
- cmdKey: server._cmdKey,
- text: `Failed to execute '${command.info.name}': missing required ${missing.join(', ')}\n\n`
+ cmdKey: server.cmdKey,
+ text: `Failed to execute '${
+ command.info.name
+ }': missing required ${missing.join(', ')}\n\n`
});
return null;
@@ -238,7 +273,7 @@ class CommandManager {
this.handleCommand(server, socket, {
cmd: 'socketreply',
- cmdKey: server._cmdKey,
+ cmdKey: server.cmdKey,
text: errText
});
diff --git a/server/src/serverLib/ConfigManager.js b/server/src/serverLib/ConfigManager.js
index ebec050..36af1ec 100644
--- a/server/src/serverLib/ConfigManager.js
+++ b/server/src/serverLib/ConfigManager.js
@@ -15,10 +15,10 @@ class ConfigManager {
/**
* Create a `ConfigManager` instance for managing application settings
*
- * @param {String} base executing directory name; __dirname
+ * @param {String} basePath executing directory name; __dirname
*/
- constructor (base = __dirname) {
- this.configPath = path.resolve(base, 'config/config.json');
+ constructor (basePath = __dirname) {
+ this.configPath = path.resolve(basePath, 'config/config.json');
if (!fse.existsSync(this.configPath)){
fse.ensureFileSync(this.configPath);
@@ -26,10 +26,9 @@ class ConfigManager {
}
/**
- * (Re)builds the config.json (main server config), or loads the config into mem
- * if rebuilding, process will exit- this is to allow a process manager to take over
+ * Loads config.json (main server config) into mem
*
- * @param {Boolean} reconfiguring set to true by `scripts/configure.js`, will exit if true
+ * @return {Object || Boolean} False if the config.json could not be loaded
*/
async load () {
try {
@@ -44,6 +43,7 @@ class ConfigManager {
/**
* Creates backup of current config into configPath
*
+ * @return {String} Backed up config.json path
*/
async backup () {
const backupPath = `${this.configPath}.${dateFormat('dd-mm-yy-HH-MM-ss')}.bak`;
@@ -56,6 +56,7 @@ class ConfigManager {
* First makes a backup of the current `config.json`, then writes current config
* to disk
*
+ * @return {Boolean} False on failure
*/
async save () {
const backupPath = await this.backup();
@@ -77,6 +78,8 @@ class ConfigManager {
*
* @param {*} key arbitrary configuration key
* @param {*} value new value to change `key` to
+ *
+ * @return {Boolean} False on failure
*/
async set (key, value) {
const realKey = `${key}`;
diff --git a/server/src/serverLib/CoreApp.js b/server/src/serverLib/CoreApp.js
index 012ae44..6bab090 100644
--- a/server/src/serverLib/CoreApp.js
+++ b/server/src/serverLib/CoreApp.js
@@ -45,7 +45,6 @@ class CoreApp {
buildImportManager () {
this.dynamicImports = new ImportsManager(path.join(__dirname, '../..'));
- this.dynamicImports.init();
}
buildCommandsManager () {
diff --git a/server/src/serverLib/ImportsManager.js b/server/src/serverLib/ImportsManager.js
index a049d5c..8db7b68 100644
--- a/server/src/serverLib/ImportsManager.js
+++ b/server/src/serverLib/ImportsManager.js
@@ -14,12 +14,11 @@ class ImportsManager {
/**
* Create a `ImportsManager` instance for (re)loading classes and config
*
- * @param {String} base executing directory name; __dirname
+ * @param {String} basePath executing directory name; default __dirname
*/
- constructor (base) {
- this._base = base;
-
- this._imports = {};
+ constructor (basePath) {
+ this.basePath = basePath;
+ this.imports = {};
}
/**
@@ -28,29 +27,18 @@ class ImportsManager {
* @type {String} readonly
*/
get base () {
- return this._base;
- }
-
- /**
- * Initialize this class and start loading target directories
- *
- */
- init () {
- let errorText = '';
- ImportsManager.load_dirs.forEach(dir => {
- errorText += this.loadDir(dir);
- });
-
- return errorText;
+ return this.basePath;
}
/**
* Gather all js files from target directory, then verify and load
*
- * @param {String} dirName The name of the dir to load, relative to the _base path.
+ * @param {String} dirName The name of the dir to load, relative to the basePath.
+ *
+ * @return {String} Load errors or empty if none
*/
loadDir (dirName) {
- const dir = path.resolve(this._base, dirName);
+ const dir = path.resolve(this.basePath, dirName);
let errorText = '';
try {
@@ -68,11 +56,11 @@ class ImportsManager {
return errorText;
}
- if (!this._imports[dirName]) {
- this._imports[dirName] = {};
+ if (!this.imports[dirName]) {
+ this.imports[dirName] = {};
}
- this._imports[dirName][file] = imported;
+ this.imports[dirName][file] = imported;
});
} catch (e) {
let err = `Unable to load modules from ${dirName}\n${e}`;
@@ -88,14 +76,22 @@ class ImportsManager {
* Unlink references to each loaded module, pray to google that gc knows it's job,
* then reinitialize this class to start the reload
*
- * @param {String} dirName The name of the dir to load, relative to the _base path.
+ * @param {Array} dirName The name of the dir to load, relative to the _base path.
+ *
+ * @return {String} Load errors or empty if none
*/
- reloadDirCache (dirName) {
- Object.keys(this._imports[dirName]).forEach((mod) => {
- delete require.cache[require.resolve(mod)];
+ reloadDirCache () {
+ let errorText = '';
+
+ Object.keys(this.imports).forEach(dir => {
+ Object.keys(this.imports[dir]).forEach((mod) => {
+ delete require.cache[require.resolve(mod)];
+ });
+
+ errorText += this.loadDir(dir);
});
- return this.init();
+ return errorText;
}
/**
@@ -103,19 +99,18 @@ class ImportsManager {
* load required directory if not found
*
* @param {String} dirName The name of the dir to load, relative to the _base path.
+ *
+ * @return {Object} Object containing command module paths and structs
*/
getImport (dirName) {
- let imported = this._imports[dirName];
+ let imported = this.imports[dirName];
if (!imported) {
this.loadDir(dirName);
}
- return Object.assign({}, this._imports[dirName]);
+ return Object.assign({}, this.imports[dirName]);
}
}
-// automagically loaded directorys on instantiation
-ImportsManager.load_dirs = ['src/commands'];
-
module.exports = ImportsManager;
diff --git a/server/src/serverLib/MainServer.js b/server/src/serverLib/MainServer.js
index 628d8ab..c7a4bbe 100644
--- a/server/src/serverLib/MainServer.js
+++ b/server/src/serverLib/MainServer.js
@@ -7,15 +7,15 @@
*
*/
-const wsServer = require('ws').Server;
-const socketReady = require('ws').OPEN;
-const crypto = require('crypto');
-const ipSalt = [...Array(Math.floor(Math.random()*128)+128)].map(i=>(~~(Math.random()*36)).toString(36)).join('');
-const internalCmdKey = [...Array(Math.floor(Math.random()*128)+128)].map(i=>(~~(Math.random()*36)).toString(36)).join('');
+const WsServer = require('ws').Server;
+const SocketReady = require('ws').OPEN;
+const Crypto = require('crypto');
const RateLimiter = require('./RateLimiter');
-const pulseSpeed = 16000; // ping all clients every X ms
+const PulseSpeed = 16000; // ping all clients every X ms
+const IpSalt = [...Array(Math.floor(Math.random()*128)+128)].map(i=>(~~(Math.random()*36)).toString(36)).join('');
+const InternalCmdKey = [...Array(Math.floor(Math.random()*128)+128)].map(i=>(~~(Math.random()*36)).toString(36)).join('');
-class MainServer extends wsServer {
+class MainServer extends WsServer {
/**
* Create a HackChat server instance.
*
@@ -24,13 +24,32 @@ class MainServer extends wsServer {
constructor (core) {
super({ port: core.config.websocketPort });
- this._core = core;
- this._hooks = {};
- this._police = new RateLimiter();
- this._cmdBlacklist = {};
- this._cmdKey = internalCmdKey;
+ this.core = core;
+ this.hooks = {};
+ this.police = new RateLimiter();
+ this.cmdBlacklist = {};
- this._heartBeat = setInterval(() => this.beatHeart(), pulseSpeed);
+ this.setupServer();
+ this.loadHooks();
+ }
+
+ /**
+ * Internal command key getter. Used to verify that internal only commands
+ * originate internally and not from a connected client.
+ * TODO: update to a structure that cannot be passed through json
+ *
+ * @type {String} readonly
+ */
+ get cmdKey () {
+ return InternalCmdKey;
+ }
+
+ /**
+ * Create ping interval and setup server event listeners
+ *
+ */
+ setupServer () {
+ this.heartBeat = setInterval(() => this.beatHeart(), PulseSpeed);
this.on('error', (err) => {
this.handleError('server', err);
@@ -39,8 +58,6 @@ class MainServer extends wsServer {
this.on('connection', (socket, request) => {
this.newConnection(socket, request);
});
-
- this.loadHooks();
}
/**
@@ -56,7 +73,7 @@ class MainServer extends wsServer {
for (let i = 0, l = targetSockets.length; i < l; i++) {
try {
- if (targetSockets[i].readyState === socketReady) {
+ if (targetSockets[i].readyState === SocketReady) {
targetSockets[i].ping();
}
} catch (e) { }
@@ -93,18 +110,18 @@ class MainServer extends wsServer {
*/
handleData (socket, data) {
// Don't penalize yet, but check whether IP is rate-limited
- if (this._police.frisk(socket.remoteAddress, 0)) {
- this._core.commands.handleCommand(this, socket, {
+ if (this.police.frisk(socket.remoteAddress, 0)) {
+ this.core.commands.handleCommand(this, socket, {
cmd: 'socketreply',
- cmdKey: this._cmdKey,
- text: 'Your IP is being rate-limited or blocked.'
+ cmdKey: this.cmdKey,
+ text: 'You are being rate-limited or blocked.'
});
return;
}
// Penalize here, but don't do anything about it
- this._police.frisk(socket.remoteAddress, 1);
+ this.police.frisk(socket.remoteAddress, 1);
// Ignore ridiculously large packets
if (data.length > 65536) {
@@ -112,7 +129,7 @@ class MainServer extends wsServer {
}
// Start sent data verification
- var payload = null;
+ let payload = null;
try {
payload = JSON.parse(data);
} catch (e) {
@@ -124,6 +141,11 @@ class MainServer extends wsServer {
return;
}
+ // TODO: make this more flexible
+ /*
+ * Issue #1: hard coded `cmd` check
+ * Issue #2: hard coded `cmd` value checks
+ */
if (typeof payload.cmd === 'undefined') {
return;
}
@@ -136,18 +158,19 @@ class MainServer extends wsServer {
return;
}
- if (typeof this._cmdBlacklist[payload.cmd] === 'function') {
+ if (typeof this.cmdBlacklist[payload.cmd] === 'function') {
return;
}
+ // End TODO //
// Execute `in` (incoming data) hooks and process results
payload = this.executeHooks('in', socket, payload);
if (typeof payload === 'string') {
// A hook malfunctioned, reply with error
- this._core.commands.handleCommand(this, socket, {
+ this.core.commands.handleCommand(this, socket, {
cmd: 'socketreply',
- cmdKey: this._cmdKey,
+ cmdKey: this.cmdKey,
text: payload
});
@@ -158,7 +181,7 @@ class MainServer extends wsServer {
}
// Finished verification & hooks, pass to command modules
- this._core.commands.handleCommand(this, socket, payload);
+ this.core.commands.handleCommand(this, socket, payload);
}
/**
@@ -167,9 +190,9 @@ class MainServer extends wsServer {
* @param {Object} socket Closing socket object
*/
handleClose (socket) {
- this._core.commands.handleCommand(this, socket, {
+ this.core.commands.handleCommand(this, socket, {
cmd: 'disconnect',
- cmdKey: this._cmdKey
+ cmdKey: this.cmdKey
});
}
@@ -198,9 +221,9 @@ class MainServer extends wsServer {
if (typeof payload === 'string') {
// A hook malfunctioned, reply with error
- this._core.commands.handleCommand(this, socket, {
+ this.core.commands.handleCommand(this, socket, {
cmd: 'socketreply',
- cmdKey: this._cmdKey,
+ cmdKey: this.cmdKey,
text: payload
});
@@ -211,7 +234,7 @@ class MainServer extends wsServer {
}
try {
- if (socket.readyState === socketReady) {
+ if (socket.readyState === SocketReady) {
socket.send(JSON.stringify(payload));
}
} catch (e) { }
@@ -232,6 +255,8 @@ class MainServer extends wsServer {
*
* @param {Object} payload Object to convert to json for transmission
* @param {Object} filter see `this.findSockets()`
+ *
+ * @return {Boolean} False if no clients matched the filter, true if data sent
*/
broadcast (payload, filter) {
let targetSockets = this.findSockets(filter);
@@ -255,6 +280,8 @@ class MainServer extends wsServer {
* = {} // matches all
* = { channel: 'programming' } // matches any socket where (`socket.channel` === 'programming')
* = { channel: 'programming', nick: 'Marzavec' } // matches any socket where (`socket.channel` === 'programming' && `socket.nick` === 'Marzavec')
+ *
+ * @return {Array} Clients who matched the filter requirements
*/
findSockets (filter) {
let filterAttribs = Object.keys(filter);
@@ -310,14 +337,16 @@ class MainServer extends wsServer {
* encodes and shortens the output, returns that value
*
* @param {Object||String} target Either the target socket or ip as string
+ *
+ * @return {String} Hashed client connection string
*/
getSocketHash (target) {
- let sha = crypto.createHash('sha256');
+ let sha = Crypto.createHash('sha256');
if (typeof target === 'string') {
- sha.update(target + ipSalt);
+ sha.update(target + IpSalt);
} else {
- sha.update(target.remoteAddress + ipSalt);
+ sha.update(target.remoteAddress + IpSalt);
}
return sha.digest('base64').substr(0, 15);
@@ -332,26 +361,26 @@ class MainServer extends wsServer {
// clear current hooks (if any)
this.clearHooks();
// notify each module to register their hooks (if any)
- this._core.commands.initCommandHooks(this);
+ this.core.commands.initCommandHooks(this);
- if (typeof this._hooks['in'] !== 'undefined') {
+ if (typeof this.hooks['in'] !== 'undefined') {
// start sorting, with incoming first
- let curHooks = [ ...this._hooks['in'].keys() ];
+ let curHooks = [ ...this.hooks['in'].keys() ];
let hookObj = [];
for (let i = 0, j = curHooks.length; i < j; i++) {
- hookObj = this._hooks['in'].get(curHooks[i]);
+ hookObj = this.hooks['in'].get(curHooks[i]);
hookObj.sort( (h1, h2) => h1.priority - h2.priority );
- this._hooks['in'].set(hookObj);
+ this.hooks['in'].set(hookObj);
}
}
- if (typeof this._hooks['out'] !== 'undefined') {
+ if (typeof this.hooks['out'] !== 'undefined') {
// then outgoing
- curHooks = [ ...this._hooks['out'].keys() ];
+ curHooks = [ ...this.hooks['out'].keys() ];
for (let i = 0, j = curHooks.length; i < j; i++) {
- hookObj = this._hooks['out'].get(curHooks[i]);
+ hookObj = this.hooks['out'].get(curHooks[i]);
hookObj.sort( (h1, h2) => h1.priority - h2.priority );
- this._hooks['out'].set(hookObj);
+ this.hooks['out'].set(hookObj);
}
}
}
@@ -371,15 +400,15 @@ class MainServer extends wsServer {
priority = 25;
}
- if (typeof this._hooks[type] === 'undefined') {
- this._hooks[type] = new Map();
+ if (typeof this.hooks[type] === 'undefined') {
+ this.hooks[type] = new Map();
}
- if (!this._hooks[type].has(command)) {
- this._hooks[type].set(command, []);
+ if (!this.hooks[type].has(command)) {
+ this.hooks[type].set(command, []);
}
- this._hooks[type].get(command).push({
+ this.hooks[type].get(command).push({
run: hookFunction,
priority: priority
});
@@ -395,17 +424,19 @@ class MainServer extends wsServer {
* @param {String} type The type of event, typically `in` (incoming) or `out` (outgoing)
* @param {Object} socket Either the target client or the client triggering the hook (depending on `type`)
* @param {Object} payload Either incoming data from client or outgoing data (depending on `type`)
+ *
+ * @return {Object || Boolean}
*/
executeHooks (type, socket, payload) {
let command = payload.cmd;
- if (typeof this._hooks[type] !== 'undefined') {
- if (this._hooks[type].has(command)) {
- let hooks = this._hooks[type].get(command);
+ if (typeof this.hooks[type] !== 'undefined') {
+ if (this.hooks[type].has(command)) {
+ let hooks = this.hooks[type].get(command);
for (let i = 0, j = hooks.length; i < j; i++) {
try {
- payload = hooks[i].run(this._core, this, socket, payload);
+ payload = hooks[i].run(this.core, this, socket, payload);
} catch (err) {
let errText = `Hook failure, '${type}', '${command}': ${err}`;
console.log(errText);
@@ -425,9 +456,10 @@ class MainServer extends wsServer {
/**
* Wipe server hooks to make ready for module reload calls
+ *
*/
clearHooks () {
- this._hooks = {};
+ this.hooks = {};
}
}
diff --git a/server/src/serverLib/RateLimiter.js b/server/src/serverLib/RateLimiter.js
index 87a1f3a..43cf077 100644
--- a/server/src/serverLib/RateLimiter.js
+++ b/server/src/serverLib/RateLimiter.js
@@ -13,25 +13,24 @@ class RateLimiter {
* Create a ratelimiter instance.
*/
constructor () {
- this._records = {};
- this._halflife = 30 * 1000; // milliseconds
- this._threshold = 25;
- this._hashes = [];
+ this.records = {};
+ this.halflife = 30 * 1000; // milliseconds
+ this.threshold = 25;
+ this.hashes = [];
}
/**
* Finds current score by `id`
*
* @param {String} id target id / address
- * @public
*
- * @memberof Police
+ * @return {Object} Object containing the record meta
*/
search (id) {
- let record = this._records[id];
+ let record = this.records[id];
if (!record) {
- record = this._records[id] = {
+ record = this.records[id] = {
time: Date.now(),
score: 0
}
@@ -45,9 +44,8 @@ class RateLimiter {
*
* @param {String} id target id / address
* @param {Number} deltaScore amount to adjust current score by
- * @public
*
- * @memberof Police
+ * @return {Boolean} True if record threshold has been exceeded
*/
frisk (id, deltaScore) {
let record = this.search(id);
@@ -56,11 +54,11 @@ class RateLimiter {
return true;
}
- record.score *= Math.pow(2, -(Date.now() - record.time ) / this._halflife);
+ record.score *= Math.pow(2, -(Date.now() - record.time ) / this.halflife);
record.score += deltaScore;
record.time = Date.now();
- if (record.score >= this._threshold) {
+ if (record.score >= this.threshold) {
return true;
}
@@ -71,28 +69,22 @@ class RateLimiter {
* Statically set server to no longer accept traffic from `id`
*
* @param {String} id target id / address
- * @public
- *
- * @memberof Police
*/
arrest (id, hash) {
let record = this.search(id);
record.arrested = true;
- this._hashes[hash] = id;
+ this.hashes[hash] = id;
}
/**
* Remove statically assigned limit from `id`
*
* @param {String} id target id / address
- * @public
- *
- * @memberof Police
*/
pardon (id) {
- if (typeof this._hashes[id] !== 'undefined') {
- id = this._hashes[id];
+ if (typeof this.hashes[id] !== 'undefined') {
+ id = this.hashes[id];
}
let record = this.search(id);
diff --git a/server/src/serverLib/StatsManager.js b/server/src/serverLib/StatsManager.js
index 4ec7ddf..e472504 100644
--- a/server/src/serverLib/StatsManager.js
+++ b/server/src/serverLib/StatsManager.js
@@ -20,6 +20,8 @@ class StatsManager {
* Retrieve value of arbitrary `key` reference
*
* @param {String} key Reference to the arbitrary store name
+ *
+ * @return {*} Data referenced by `key`
*/
get (key) {
return this.data[key];