From dc8de6662548c305101771d47fab25a569b8ebba Mon Sep 17 00:00:00 2001 From: Fizitzfux <66255957+fizitzfux@users.noreply.github.com> Date: Sun, 16 Feb 2025 14:36:36 +0000 Subject: [PATCH 1/7] Implement minecraft whitelist updating --- src/extensions/minecraft/index.html | 4 ++ src/extensions/minecraft/index.ts | 45 +++++++++++++++- src/extensions/minecraft/lib.ts | 60 ++++++++++++++++++++++ src/extensions/minecraft/package-lock.json | 19 +++++-- src/extensions/minecraft/package.json | 2 +- 5 files changed, 122 insertions(+), 8 deletions(-) create mode 100644 src/extensions/minecraft/lib.ts diff --git a/src/extensions/minecraft/index.html b/src/extensions/minecraft/index.html index dd55a7d..0757e23 100644 --- a/src/extensions/minecraft/index.html +++ b/src/extensions/minecraft/index.html @@ -10,4 +10,8 @@ {{auth_err}} + + +Updating the whitelist can take up to a minute, please be patient. + {% endblock %} diff --git a/src/extensions/minecraft/index.ts b/src/extensions/minecraft/index.ts index 036ea9b..bb6af32 100644 --- a/src/extensions/minecraft/index.ts +++ b/src/extensions/minecraft/index.ts @@ -1,6 +1,7 @@ import { ExtensionBase } from "../../modules.js" import Knex from "../../modules/knex.ts" import { unpack } from "../../util.ts" +import * as rcon from "./lib.ts" export default class extends ExtensionBase { override name = 'minecraft' @@ -8,7 +9,13 @@ export default class extends ExtensionBase { override tables = true override init: Extension['init'] = async (context) => { - return ExtensionBase.init(this, context) + // Init super here as rest of init happens async + const result = ExtensionBase.init(this, context) + + // On a separate "thread" in case we can't connect to the RCON server immediately + setTimeout(this.update_whitelist, 0) + + return result } override handle: Extension['handle'] = async (ctx) => { @@ -34,7 +41,7 @@ export default class extends ExtensionBase { case 'change':{ if (ctx.data) { - let new_MCName = ctx.data.form.minecraft_name + let new_MCName = ctx.data.form.minecraft_name ?? '' let [name, err] = await knex .query('_minecraft') .select('minecraft_name') @@ -51,10 +58,44 @@ export default class extends ExtensionBase { // @ts-expect-error .insert({minecraft_name: new_MCName, user_id: ctx.context.user?.id}) } + + // Intentionally not awaited as it can take a while + this.update_whitelist() } return this.return(ctx, undefined, location='/minecraft') } } } + + update_whitelist = async () => { + const [knex]: [Knex] = this.get_dependencies('Knex') + const [raw_names, err] = await knex + .query('_minecraft') + .select('minecraft_name') + .then(unpack<{minecraft_name: string}[]>) + const names: string[] = (raw_names ?? []) + // Unpack objects + .flatMap((name) => name.minecraft_name) + // Remove empty rows + .filter((name) => name != '') + + const currently_whitelisted_raw = await rcon.send("whitelist list") + const currently_whitelisted: string[] = currently_whitelisted_raw + .split(' ') + // Remove trash + .slice(5) + + const to_remove = currently_whitelisted.filter((name) => !names.includes(name)) + const to_add = names.filter((name) => !currently_whitelisted.includes(name)) + + for (const name of to_remove) { + const response = await rcon.send(`whitelist remove ${name}`) + console.log(response) + } + for (const name of to_add) { + const response = await rcon.send(`whitelist add ${name}`) + console.log(response) + } + } } diff --git a/src/extensions/minecraft/lib.ts b/src/extensions/minecraft/lib.ts new file mode 100644 index 0000000..93ba166 --- /dev/null +++ b/src/extensions/minecraft/lib.ts @@ -0,0 +1,60 @@ +import { Rcon } from "rcon-client" + +function log(...args: any[]) { + console.log("[MINECRAFT]:",...args) +} + +let is_connected = false + +const rcon = new Rcon({ + host: '127.0.0.1', + port: 25575, + password: '1234', +}) +export const raw = rcon + +export const send = async (command: string) => { + await connected() + return rcon.send(command) +} +export const sendRaw = async (buffer: Buffer) => { + await connected() + return rcon.sendRaw(buffer) +} + +export const connected = async (): Promise => new Promise((resolve) => { + const loop = setInterval(async () => { + if (!is_connected) { + await rcon.connect().then( + () => { + is_connected = true + clearInterval(loop) + resolve() + }, + (err: Error) => { + is_connected = false + log("Failed connecting:", err.message) + } + ) + } + else { + clearInterval(loop) + resolve() + } + }, 5000) +}) + +rcon.on('connect', () => { + is_connected = true + log("connected") +}) +rcon.on('end', () => { + is_connected = false + log("disconnected") +}) +rcon.on('error', (err) => { + log("err: ", err) +}) +rcon.on('authenticated', () => { + log("authenticated") +}) diff --git a/src/extensions/minecraft/package-lock.json b/src/extensions/minecraft/package-lock.json index 343e8e2..49e7b6c 100644 --- a/src/extensions/minecraft/package-lock.json +++ b/src/extensions/minecraft/package-lock.json @@ -8,13 +8,22 @@ "name": "minecraft", "version": "0.0.1", "dependencies": { - "rcon-srcds": "2.1.0" + "rcon-client": "^4.2.5" } }, - "node_modules/rcon-srcds": { - "version": "2.1.0", - "resolved": "https://registry.npmjs.org/rcon-srcds/-/rcon-srcds-2.1.0.tgz", - "integrity": "sha512-kkF32wt+xsX19b6wrerU8qTOHa9AX81eJTffgZD0FfaVV2U+sL1gFr5W2VX4m+ILh/l4ZEAYBHx653l2Ci+h+Q==", + "node_modules/rcon-client": { + "version": "4.2.5", + "resolved": "https://registry.npmjs.org/rcon-client/-/rcon-client-4.2.5.tgz", + "integrity": "sha512-AnX1GU/ZTlwtYup3H6h0J1hwfP3OYltXVe+8ReBzmNEepX3xGH8nDg7gYqT5Y9rpAS/LmQ48h0BKINt1YGd8bA==", + "license": "MIT", + "dependencies": { + "typed-emitter": "^0.1.0" + } + }, + "node_modules/typed-emitter": { + "version": "0.1.0", + "resolved": "https://registry.npmjs.org/typed-emitter/-/typed-emitter-0.1.0.tgz", + "integrity": "sha512-Tfay0l6gJMP5rkil8CzGbLthukn+9BN/VXWcABVFPjOoelJ+koW8BuPZYk+h/L+lEeIp1fSzVRiWRPIjKVjPdg==", "license": "MIT" } } diff --git a/src/extensions/minecraft/package.json b/src/extensions/minecraft/package.json index 3e69605..8c967cc 100644 --- a/src/extensions/minecraft/package.json +++ b/src/extensions/minecraft/package.json @@ -3,6 +3,6 @@ "version": "0.0.1", "type": "module", "dependencies": { - "rcon-srcds": "2.1.0" + "rcon-client": "^4.2.5" } } From 48218d334e7690c3508566ffc026e408b1f6cc54 Mon Sep 17 00:00:00 2001 From: Fizitzfux <66255957+fizitzfux@users.noreply.github.com> Date: Sun, 16 Feb 2025 15:09:17 +0000 Subject: [PATCH 2/7] Fix legacy code that should not have still been active --- src/extensions/root/index.ts | 22 +++++++++++----------- 1 file changed, 11 insertions(+), 11 deletions(-) diff --git a/src/extensions/root/index.ts b/src/extensions/root/index.ts index 5b6354a..9c76711 100644 --- a/src/extensions/root/index.ts +++ b/src/extensions/root/index.ts @@ -162,17 +162,17 @@ export default class extends ExtensionBase implements RootExtension { else return new Error('Wrong name or password') } - else if (ip.startsWith(subnet)) { - // Try using IP-address if no name and password - const user = await knex - .query({u: 'user', p: '_profile_device'}) - .select('u.*') - .join('_profile_device', 'u.id', '=', 'p.user_id') - .where('p.ip', ip) - .first() - - return user - } + // else if (ip.startsWith(subnet)) { + // // Try using IP-address if no name and password + // const user = await knex + // .query({u: 'user', p: '_profile_device'}) + // .select('u.*') + // .join('_profile_device', 'u.id', '=', 'p.user_id') + // .where('p.ip', ip) + // .first() + + // return user + // } } private decrypt_auth(auth: BasicAuth): [name: string, password: string] | Error { From 8017bc69ae7518fc2f90c5600738df8c8d65cd98 Mon Sep 17 00:00:00 2001 From: Fizitzfux <66255957+fizitzfux@users.noreply.github.com> Date: Sun, 16 Feb 2025 15:18:54 +0000 Subject: [PATCH 3/7] Register triggers whitelist update --- src/extensions/invite/index.ts | 2 ++ src/extensions/minecraft/index.ts | 4 +++- 2 files changed, 5 insertions(+), 1 deletion(-) diff --git a/src/extensions/invite/index.ts b/src/extensions/invite/index.ts index ab4b9ca..02d6464 100644 --- a/src/extensions/invite/index.ts +++ b/src/extensions/invite/index.ts @@ -109,6 +109,8 @@ export default class extends ExtensionBase { .query('_minecraft_minecraft') // @ts-expect-error .insert({minecraft_name: form.minecraft_name, user_id: id}) + + ;(ctx.context.extensions.get('minecraft') as any)?.update_whitelist() } return this.return_html(ctx, 'login', undefined, 500, 303, { diff --git a/src/extensions/minecraft/index.ts b/src/extensions/minecraft/index.ts index bb6af32..f97761e 100644 --- a/src/extensions/minecraft/index.ts +++ b/src/extensions/minecraft/index.ts @@ -73,7 +73,9 @@ export default class extends ExtensionBase { const [raw_names, err] = await knex .query('_minecraft') .select('minecraft_name') - .then(unpack<{minecraft_name: string}[]>) + .then(unpack<{minecraft_name: string}[]>, unpack) + if (err) + return const names: string[] = (raw_names ?? []) // Unpack objects .flatMap((name) => name.minecraft_name) From e146449447ee4278a96b2db7a8644bf6345e3bf9 Mon Sep 17 00:00:00 2001 From: Fizitzfux <66255957+fizitzfux@users.noreply.github.com> Date: Sun, 16 Feb 2025 15:26:21 +0000 Subject: [PATCH 4/7] Add extension hiding --- src/classes/extension.ts | 1 + src/templates/includes/extensions.html | 4 +++- types/classes/extension.d.ts | 1 + 3 files changed, 5 insertions(+), 1 deletion(-) diff --git a/src/classes/extension.ts b/src/classes/extension.ts index 01ca103..824a484 100644 --- a/src/classes/extension.ts +++ b/src/classes/extension.ts @@ -6,6 +6,7 @@ export abstract class ExtensionBase implements Extension { admin_only = false tables = false disabled = false + hidden = false initialized_deps: DependencyMap = new DependencyMapImpl() name: Extension['name'] = "default_name" title: Extension['title'] = "Default Title" diff --git a/src/templates/includes/extensions.html b/src/templates/includes/extensions.html index 736f0aa..5cb4369 100644 --- a/src/templates/includes/extensions.html +++ b/src/templates/includes/extensions.html @@ -1,6 +1,8 @@
{% for _,ext in extensions %} - {{ext.title}} + {% if not ext.hidden %} + {{ext.title}} + {% endif %} {% endfor %}
diff --git a/types/classes/extension.d.ts b/types/classes/extension.d.ts index cf7f1c5..1a4fbb6 100644 --- a/types/classes/extension.d.ts +++ b/types/classes/extension.d.ts @@ -2,6 +2,7 @@ declare interface Extension { admin_only: boolean tables: boolean disabled: boolean + hidden: boolean name: string title: string From 134d904012a3a6bebe98cac53653fedc66f9595b Mon Sep 17 00:00:00 2001 From: Fizitzfux <66255957+fizitzfux@users.noreply.github.com> Date: Sun, 16 Feb 2025 15:27:07 +0000 Subject: [PATCH 5/7] Hide Invites by default --- src/extensions/invite/index.ts | 1 + 1 file changed, 1 insertion(+) diff --git a/src/extensions/invite/index.ts b/src/extensions/invite/index.ts index 02d6464..d909461 100644 --- a/src/extensions/invite/index.ts +++ b/src/extensions/invite/index.ts @@ -7,6 +7,7 @@ export default class extends ExtensionBase { override name = 'invite' override title = 'Invite' override tables = true + override hidden = true salt: string From 15129751a632a9cd55684806f46954c81a6db57c Mon Sep 17 00:00:00 2001 From: Fizitzfux <66255957+fizitzfux@users.noreply.github.com> Date: Sun, 16 Feb 2025 15:32:06 +0000 Subject: [PATCH 6/7] Add admin button for Invites --- src/extensions/admin/index.html | 2 ++ src/extensions/admin/static/index.css | 4 ++++ 2 files changed, 6 insertions(+) diff --git a/src/extensions/admin/index.html b/src/extensions/admin/index.html index 2535a42..c59efb4 100644 --- a/src/extensions/admin/index.html +++ b/src/extensions/admin/index.html @@ -6,6 +6,8 @@ {% block body %} +Invites +
diff --git a/src/extensions/admin/static/index.css b/src/extensions/admin/static/index.css index 7221834..025a5cb 100644 --- a/src/extensions/admin/static/index.css +++ b/src/extensions/admin/static/index.css @@ -1,3 +1,7 @@ +#content { + padding-top: 10px; +} + #user_table{ border-collapse: collapse; margin: 10px, 0; From 2dc6a5e9e8f1dcaa190e46b31cee5bc08032272c Mon Sep 17 00:00:00 2001 From: Fizitzfux <66255957+fizitzfux@users.noreply.github.com> Date: Sun, 16 Feb 2025 15:46:49 +0000 Subject: [PATCH 7/7] Fix minecraft whitelist usernames --- src/extensions/minecraft/index.ts | 7 ++++++- src/extensions/minecraft/lib.ts | 4 ++-- 2 files changed, 8 insertions(+), 3 deletions(-) diff --git a/src/extensions/minecraft/index.ts b/src/extensions/minecraft/index.ts index f97761e..3bb36c2 100644 --- a/src/extensions/minecraft/index.ts +++ b/src/extensions/minecraft/index.ts @@ -83,10 +83,15 @@ export default class extends ExtensionBase { .filter((name) => name != '') const currently_whitelisted_raw = await rcon.send("whitelist list") - const currently_whitelisted: string[] = currently_whitelisted_raw + const currently_whitelisted = currently_whitelisted_raw .split(' ') // Remove trash .slice(5) + .flatMap((name) => { + if (name.endsWith(',')) + return name.substring(0, name.length -1) + return name + }) const to_remove = currently_whitelisted.filter((name) => !names.includes(name)) const to_add = names.filter((name) => !currently_whitelisted.includes(name)) diff --git a/src/extensions/minecraft/lib.ts b/src/extensions/minecraft/lib.ts index 93ba166..291e67a 100644 --- a/src/extensions/minecraft/lib.ts +++ b/src/extensions/minecraft/lib.ts @@ -15,11 +15,11 @@ export const raw = rcon export const send = async (command: string) => { await connected() - return rcon.send(command) + return rcon.send(command).catch(() => '') } export const sendRaw = async (buffer: Buffer) => { await connected() - return rcon.sendRaw(buffer) + return rcon.sendRaw(buffer).catch(() => '') } export const connected = async (): Promise => new Promise((resolve) => {