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 @@
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) => {