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
2 changes: 1 addition & 1 deletion config/texts.example.ts
Original file line number Diff line number Diff line change
Expand Up @@ -9,6 +9,6 @@ export default {
Please do be aware of the fact that this is still very much in development and a lot of stuff will be improved with time.
</p>

<a href="/login" class="button">Register / Login</a>
<a href="/login" class="button">Login</a>
`,
}
7 changes: 5 additions & 2 deletions src/classes/extension.ts
Original file line number Diff line number Diff line change
Expand Up @@ -154,13 +154,16 @@ export abstract class ExtensionBase implements Extension {
key,
value, {
secure: true,
httpOnly: true
httpOnly: true,
path: '/'
}
)
else
return cookie.serialize(
key,
value
value, {
path: '/'
}
)
}

Expand Down
4 changes: 2 additions & 2 deletions src/extensions/invite/index.html
Original file line number Diff line number Diff line change
Expand Up @@ -10,7 +10,7 @@
<thead>
<tr>
<th>ID</th>
<th>Link</th>
<th>Code</th>
<th>Created at</th>
<th>Used</th>
</tr>
Expand All @@ -19,7 +19,7 @@
{% for link in invite_links %}
<tr class={{"used" if link.used else " "}}>
<td>{{link.id}}</td>
<td>{{link.link}}</td>
<td>{{link.code}}</td>
<td>{{link.created_at}}</td>
<td>{{"Yes" if link.used else "No"}}</td>
</tr>
Expand Down
139 changes: 125 additions & 14 deletions src/extensions/invite/index.ts
Original file line number Diff line number Diff line change
@@ -1,52 +1,163 @@
import crypto from 'crypto'
import { ExtensionBase, Knex } from '../../modules.ts'
import { unpack } from '../../util.ts'
import minecraft from '../minecraft/index.ts'

export default class extends ExtensionBase {
override name = 'invite'
override title = 'Invite'
override tables = true
override admin_only = true

invite_URL = 'http://127.0.0.1:8000/invite/register/'
salt: string

override init = (context: InitContext) => {
this.salt = context.modules.config.salt
return ExtensionBase.init(this, context)
}

override requires_admin: Extension['requires_admin'] = (path) => {
if (['register', 'create_acc'].includes(path.at(0)??'')) {
return false
}
return true
}
override requires_login: Extension['requires_login'] = (path) => {
if (['register', 'create_acc'].includes(path.at(0)??'')) {
return false
}
return true
}

override handle: Extension['handle'] = async (ctx) => {
let [knex]: [Knex] = this.get_dependencies('Knex')
var location = ctx.path.shift()
let location = ctx.path.shift()

switch (location) {
case '':
case undefined:{

var [invite_links, err] = await knex
let [invite_links, err] = await knex
.query('_invite')
.select<string[]>('*')
.then(unpack<string[]>)

ctx.context.invite_links = invite_links
return this.return_html(ctx, 'index')
}
case 'create':
case 'create':{

const chars = 'abcdefghijklmnopqrstuvwxyz0123456789'
let random_chars = 'r'
for (let i = 0; i < 8; i++) {
let random_chars = ''
for (let i = 0; i < 10; i++) {
random_chars += chars.charAt(Math.floor(Math.random() * chars.length));
}

let invite_link = this.invite_URL + random_chars + Date.now();
const time = new Date().toLocaleTimeString('en-US', {hour12: false});

let code = random_chars
await knex.query('_invite')
// @ts-expect-error
.insert({link: invite_link, created_at: new Date().toLocaleTimeString('en-US', {hour12: false})})

ctx.context.invite_links = invite_links
.insert({code: code})

return this.return(ctx, undefined, location='/invite')
}
case 'register': {
const invite_code = ctx.args.get('code')
if (!invite_code)
return

let [invite, err] = await knex
.query('_invite')
.select<any>('used')
.where('code', invite_code)
.first()
.then(unpack<any>)

if (!err && !invite?.used){
ctx.context.invite_code = invite_code
return this.return_html(ctx, 'register', undefined, 500, 200, {
"Set-Cookie": this.del_cookie('auth')
})
}
return this.return(ctx, undefined, "/")
}
case 'create_acc':{
if (ctx.data)
{
let form: {invite_code?: string, username?: string, password?: string, minecraft_name?: string} = ctx.data.form
if (form.username && form.password && form.invite_code) {
form.username = form.username.substring(0, 32)

this.addUser(form.username, form.password, async (id?: number, err?: Error) => {
// if invalid credentials
if (err) {
ctx.context.auth_err = err
return this.return_html(ctx, 'login')
}
// success
else {
let auth = Buffer.from(form.username+":"+form.password).toString('base64')
await knex
.query('_invite')
// @ts-expect-error
.update({
used: true,
user_id: id
})
.where('code', form.invite_code)
.then()
if (form.minecraft_name)//If a minecraft name was entered
{
await knex
.query('_minecraft_minecraft')
// @ts-expect-error
.insert({minecraft_name: form.minecraft_name, user_id: id})
}

return this.return_html(ctx, 'login', undefined, 500, 303, {
"Location": "/",
"Set-Cookie": this.set_cookie('auth', 'Basic '+auth, true)
})
}
})
}
}
return
}
default: {
return this.return_file(ctx, location)
}
}

}

private hash_pw(password: string): string {
return crypto.pbkdf2Sync(password, this.salt, 10000, 128, 'sha512').toString('base64')
}

addUser(name: User['name'], password: User['password'], callback: (id?: number, err?: Error) => void) {
let [knex]: [Knex] = this.get_dependencies('Knex')

password = this.hash_pw(password)
// Check if username is already taken
this.exists(name, (exists, err) => {
if (err) return callback(undefined, err)
if (exists) return callback(undefined, new Error("Username already taken"))
// add user to db
knex.query('user')
// @ts-expect-error
.insert({name, password, pfp_code: `seed=${name}`})
.then((id) => callback(id[0] as unknown as number), (err) => callback(undefined, err))
})
}

private exists(name: User['name'], callback: (exists: boolean, err?: Error) => void): void {
let [knex]: [Knex] = this.get_dependencies('Knex')
// check if name already exists
knex.query('user')
.select('id')
.where('name', name)
.then((value) => {
callback(!!value.length)
}, (err) => {
callback(false, err)
})
}
}
Empty file.
34 changes: 34 additions & 0 deletions src/extensions/invite/register.html
Original file line number Diff line number Diff line change
@@ -0,0 +1,34 @@
{% extends "layout.html" %}

{% block head %}
<link href="/invite/index.css" rel="stylesheet">
{% endblock %}


{% block body %}
<form action="/invite/create_acc" method="post" enctype="application/x-www-form-urlencoded">
<input type="text" name="invite_code" hidden="true" value="{{invite_code}}">
<table>
<tr>
<td><label for="username">Name:</label></td>
<td><input type="text" name="username" id="username" maxlength="32" autocomplete="username"></td>
</tr>
<tr>
<td><label for="password">Password:</label></td>
<td><input type="password" name="password" id="password" autocomplete="current-password"></td>
</tr>
<tr>
<td><label for="username">Minecraft Username:</label></td>
<td><input type="text" name="minecraft_name" id="minecraft_name" maxlength="32" value="{{minecraft_username}}"></td>
<td><label>(Only if you want to play on the minecraft server)</label></td>
</tr>
<tr>
<td></td>
<td style="display: flex; flex-direction: row-reverse;">
<input type="submit" value="register" name="register">
</td>
</tr>
</table>
<span id="error" style="color: red;">{{auth_err}}</span>
</form>
{% endblock %}
9 changes: 7 additions & 2 deletions src/extensions/invite/tables.ts
Original file line number Diff line number Diff line change
Expand Up @@ -14,10 +14,15 @@ export default class extends Tables {
await knex.schema()
.createTable('_invite', (table) => {
table.increments('id').primary()
table.string('link').notNullable()
table.timestamp('created_at').notNullable().defaultTo(knex.raw('CURRENT_TIMESTAMP'))
table.string('code').notNullable()
table.datetime('created_at').notNullable().defaultTo(knex.raw('CURRENT_TIMESTAMP'))
table.boolean('used').notNullable().defaultTo(false)
table.integer('user_id')
table.foreign('user_id', 'fk_user_id').references('_root_user.id')
})
await knex.query('_invite')
// @ts-expect-error
.insert({code: 'admin'})
},
})

Expand Down
9 changes: 9 additions & 0 deletions src/extensions/minecraft/index.html
Original file line number Diff line number Diff line change
@@ -1,4 +1,13 @@
{% extends "extension.html" %}

{% block body %}
<form action="/minecraft/change" method="post" enctype="application/x-www-form-urlencoded">
<table>
<tr>
<td><label for="username">Minecraft Username:</label></td>
<td><input type="text" name="minecraft_name" id="minecraft_name" maxlength="32" value="{{minecraft_username}}"></td>
</tr>
</table>
<input type="submit" value="Change" name="change">
<span id="error" style="color: red;">{{auth_err}}</span>
{% endblock %}
41 changes: 40 additions & 1 deletion src/extensions/minecraft/index.ts
Original file line number Diff line number Diff line change
@@ -1,21 +1,60 @@
import { ExtensionBase } from "../../modules.js"
import Knex from "../../modules/knex.ts"
import { unpack } from "../../util.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)
}

override handle: Extension['handle'] = (ctx) => {
override handle: Extension['handle'] = async (ctx) => {
var location = ctx.path.shift()
let [knex]: [Knex] = this.get_dependencies('Knex')

switch (location) {
case '':
case undefined: {
let [name, err] = await knex
.query('_minecraft')
.select('minecraft_name')
.where('user_id', ctx.context.user?.id)
.first()
.then(unpack<any>)

if (name)
ctx.context.minecraft_username = name.minecraft_name
else
ctx.context.minecraft_username = ""
return this.return_html(ctx, 'index')
}
case 'change':{
if (ctx.data)
{
let new_MCName = ctx.data.form.minecraft_name
let [name, err] = await knex
.query('_minecraft')
.select('minecraft_name')
.where('user_id', ctx.context.user?.id)

if (name){
await knex
.query('_minecraft')
.update('minecraft_name', new_MCName)
.where('user_id', ctx.context.user?.id)
}else{
await knex
.query('_minecraft')
// @ts-expect-error
.insert({minecraft_name: new_MCName, user_id: ctx.context.user?.id})
}
}

return this.return(ctx, undefined, location='/minecraft')
}
}
}
}
26 changes: 26 additions & 0 deletions src/extensions/minecraft/tables.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,26 @@
import { MigrationMap, Tables, VersionMap } from '../../classes/tables.ts'
import { Knex } from '../../modules.ts'

export default class extends Tables {
override versions(versions: VersionMap) {
versions.set('minecraft', 0)

return versions
}

override migrations(knex: Knex, migrations: MigrationMap) {
migrations.set('minecraft', {
0: async ()=>{
await knex.schema()
.createTable('_minecraft', (table) => {
table.increments('id').primary()
table.string('minecraft_name').notNullable()
table.integer('user_id')
table.foreign('user_id', 'fk_user_id').references('_root_user.id')
})
},
})

return migrations
}
}
Loading
Loading