Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
1 change: 1 addition & 0 deletions src/classes/extension.ts
Original file line number Diff line number Diff line change
Expand Up @@ -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"
Expand Down
2 changes: 2 additions & 0 deletions src/extensions/admin/index.html
Original file line number Diff line number Diff line change
Expand Up @@ -6,6 +6,8 @@


{% block body %}
<a href="/invite" class="button">Invites</a>
<hr>
<table id="user_table">
<thead>
<tr>
Expand Down
4 changes: 4 additions & 0 deletions src/extensions/admin/static/index.css
Original file line number Diff line number Diff line change
@@ -1,3 +1,7 @@
#content {
padding-top: 10px;
}

#user_table{
border-collapse: collapse;
margin: 10px, 0;
Expand Down
3 changes: 3 additions & 0 deletions src/extensions/invite/index.ts
Original file line number Diff line number Diff line change
Expand Up @@ -7,6 +7,7 @@ export default class extends ExtensionBase {
override name = 'invite'
override title = 'Invite'
override tables = true
override hidden = true

salt: string

Expand Down Expand Up @@ -109,6 +110,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, {
Expand Down
4 changes: 4 additions & 0 deletions src/extensions/minecraft/index.html
Original file line number Diff line number Diff line change
Expand Up @@ -10,4 +10,8 @@
</table>
<input type="submit" value="Change" name="change">
<span id="error" style="color: red;">{{auth_err}}</span>
</form>

<sub>Updating the whitelist can take up to a minute, please be patient.</sub>

{% endblock %}
52 changes: 50 additions & 2 deletions src/extensions/minecraft/index.ts
Original file line number Diff line number Diff line change
@@ -1,14 +1,21 @@
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'
override title = 'Minecraft'
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) => {
Expand All @@ -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')
Expand All @@ -51,10 +58,51 @@ 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}[]>, unpack<undefined>)
if (err)
return
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 = 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))

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)
}
}
}
60 changes: 60 additions & 0 deletions src/extensions/minecraft/lib.ts
Original file line number Diff line number Diff line change
@@ -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).catch(() => '')
}
export const sendRaw = async (buffer: Buffer) => {
await connected()
return rcon.sendRaw(buffer).catch(() => '')
}

export const connected = async (): Promise<void> => 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")
})
19 changes: 14 additions & 5 deletions src/extensions/minecraft/package-lock.json

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

2 changes: 1 addition & 1 deletion src/extensions/minecraft/package.json
Original file line number Diff line number Diff line change
Expand Up @@ -3,6 +3,6 @@
"version": "0.0.1",
"type": "module",
"dependencies": {
"rcon-srcds": "2.1.0"
"rcon-client": "^4.2.5"
}
}
22 changes: 11 additions & 11 deletions src/extensions/root/index.ts
Original file line number Diff line number Diff line change
Expand Up @@ -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<User>('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<User>('u.*')
// .join('_profile_device', 'u.id', '=', 'p.user_id')
// .where('p.ip', ip)
// .first()

// return user
// }
Comment on lines +165 to +175
Copy link
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Should remove this at some point.

}

private decrypt_auth(auth: BasicAuth): [name: string, password: string] | Error {
Expand Down
4 changes: 3 additions & 1 deletion src/templates/includes/extensions.html
Original file line number Diff line number Diff line change
@@ -1,6 +1,8 @@
<section class="extensions">
{% for _,ext in extensions %}
<a href="/{{ext.name}}" class="{{'selected' if ext.name == location}} item">{{ext.title}}</a>
{% if not ext.hidden %}
<a href="/{{ext.name}}" class="{{'selected' if ext.name == location}} item">{{ext.title}}</a>
{% endif %}
{% endfor %}

</section>
1 change: 1 addition & 0 deletions types/classes/extension.d.ts
Original file line number Diff line number Diff line change
Expand Up @@ -2,6 +2,7 @@ declare interface Extension {
admin_only: boolean
tables: boolean
disabled: boolean
hidden: boolean

name: string
title: string
Expand Down
Loading