im getting lazy but now i can disable the register page

This commit is contained in:
2026-07-05 12:54:09 +01:00
parent ec56e3794e
commit f93f407d66
9 changed files with 178 additions and 38 deletions

11
TODO.md
View File

@@ -2,15 +2,6 @@
player view - all registered events and scores for those events
team view - all registered players and all points gained from them - Do i need this?
# Login stuff - HIGH PRIORITY
disableable register button on the login page for end users
make a way to toggle on and off the register page
protect the register endpoint?? (low prio)
# Ledger - HIGH PRIORITY
ledger view
inserting manually into ledger
# Database - HIGH PRIORITY
load results from the excel sheet i made
export whole contest results to csv
@@ -29,7 +20,5 @@ result amendments
## Easy shit
animations for all leaderboards
Ongoing since... or ongoing for... on the main page
displaying averages of loaded scores when there's more than one (easy, low prio)
fix the layout on event view to match the homepage view
fix the marquee animation thing and make it global

View File

@@ -3,6 +3,21 @@ import { sql, eq, and, desc, inArray } from 'drizzle-orm';
import * as schema from '$lib/server/db/schema';
import { globalEmitter } from './globalEmitter';
export async function getSetting(key: string): Promise<string | null> {
const [row] = await db
.select({ value: schema.settings.value })
.from(schema.settings)
.where(eq(schema.settings.key, key));
return row?.value ?? null;
}
export async function setSetting(key: string, value: string): Promise<void> {
await db
.insert(schema.settings)
.values({ key, value })
.onConflictDoUpdate({ target: schema.settings.key, set: { value } });
}
// Initial data for page load
export async function getAllInitialInfo() {
return {

View File

@@ -156,6 +156,11 @@ export const registeredEventsView = sqliteView('registeredEventsView').as((qb) =
.innerJoin(divisions, eq(registeredEvents.division, divisions.id));
});
export const settings = sqliteTable('settings', {
key: text('key').primaryKey(),
value: text('value').notNull()
});
export const registeredEventPlayersView = sqliteView('registeredEventPlayersView').as((qb) => {
return qb
.select({

View File

@@ -22,6 +22,11 @@
border-solid border-red-500 px-2"
href="/ledger">ledger</a
>
<a
class="align-text-middle justify-right mx-3 my-1 h-auto content-center rounded-sm border-2
border-solid border-red-500 px-2"
href="/settings">settings</a
>
{/if}
{#if data.user}
<a

View File

@@ -1,7 +1,13 @@
import { getAllInitialInfo } from '$lib/server/databaseManager';
import { redirect } from '@sveltejs/kit';
import { getAllInitialInfo, getSetting } from '$lib/server/databaseManager';
// Provide initial data for the home page
export const load = async () => {
export const load = async ({ locals }) => {
const requireLogin = (await getSetting('requireLoginForLeaderboard')) === 'true';
if (requireLogin && !locals.user) {
throw redirect(303, '/login');
}
return await getAllInitialInfo();
};

View File

@@ -4,10 +4,21 @@ import { eq } from 'drizzle-orm';
import { db } from '$lib/server/db';
import { scorers } from '$lib/server/db/schema';
import { generateSessionToken, createSession, setSessionTokenCookie } from '$lib/server/auth';
import type { Actions } from '../signup/$types';
import { getSetting } from '$lib/server/databaseManager';
import type { PageServerLoad, Actions } from '../signup/$types';
export const load: PageServerLoad = async () => {
const disabled = (await getSetting('disableRegistration')) === 'true';
return { disabled };
};
export const actions: Actions = {
default: async (event) => {
const disabled = (await getSetting('disableRegistration')) === 'true';
if (disabled) {
return fail(403, { message: 'Registration is currently disabled by an administrator.' });
}
const data = await event.request.formData();
const username = data.get('username') as string;
const password = data.get('password') as string;

View File

@@ -1,38 +1,43 @@
<script lang="ts">
import type { ActionData } from './$types';
import type { PageData, ActionData } from './$types';
export let data: PageData;
export let form: ActionData;
</script>
<div class="auth-card">
<h1>Sign up</h1>
<form method="POST">
<label>
Username
<input name="username" class="text-black" type="text" autocomplete="username" required />
</label>
{#if data.disabled}
<p class="error">Registration is currently disabled by an administrator.</p>
{:else}
<form method="POST">
<label>
Username
<input name="username" class="text-black" type="text" autocomplete="username" required />
</label>
<label>
Password
<input
class="text-black"
name="password"
type="password"
autocomplete="new-password"
minlength="8"
required
/>
<small>At least 8 characters</small>
</label>
<label>
Password
<input
class="text-black"
name="password"
type="password"
autocomplete="new-password"
minlength="8"
required
/>
<small>At least 8 characters</small>
</label>
{#if form?.message}
<p class="error">{form.message}</p>
{/if}
{#if form?.message}
<p class="error">{form.message}</p>
{/if}
<button type="submit">Create account</button>
</form>
<button type="submit">Create account</button>
</form>
<p class="switch">Already have an account? <a href="/login">Log in</a></p>
<p class="switch">Already have an account? <a href="/login">Log in</a></p>
{/if}
</div>
<style>

View File

@@ -0,0 +1,58 @@
import { error, redirect, fail } from '@sveltejs/kit';
import { eq } from 'drizzle-orm';
import { db } from '$lib/server/db';
import { scorers } from '$lib/server/db/schema';
import { getSetting, setSetting } from '$lib/server/databaseManager';
import type { PageServerLoad, Actions } from './$types';
export const load: PageServerLoad = async ({ locals }) => {
if (!locals.user) {
throw redirect(303, '/login');
}
const [row] = await db
.select({ role: scorers.role })
.from(scorers)
.where(eq(scorers.id, locals.user.id));
if (row?.role !== 'admin') {
throw error(403, 'Forbidden');
}
const disableRegistration = (await getSetting('disableRegistration')) === 'true';
const requireLoginForLeaderboard = (await getSetting('requireLoginForLeaderboard')) === 'true';
return { disableRegistration, requireLoginForLeaderboard };
};
export const actions: Actions = {
toggleRegistration: async ({ locals }) => {
if (!locals.user) throw error(401, 'Unauthorized');
const [row] = await db
.select({ role: scorers.role })
.from(scorers)
.where(eq(scorers.id, locals.user.id));
if (row?.role !== 'admin') throw error(403, 'Forbidden');
const current = (await getSetting('disableRegistration')) === 'true';
await setSetting('disableRegistration', current ? 'false' : 'true');
return { success: true };
},
toggleLeaderboardLock: async ({ locals }) => {
if (!locals.user) throw error(401, 'Unauthorized');
const [row] = await db
.select({ role: scorers.role })
.from(scorers)
.where(eq(scorers.id, locals.user.id));
if (row?.role !== 'admin') throw error(403, 'Forbidden');
const current = (await getSetting('requireLoginForLeaderboard')) === 'true';
await setSetting('requireLoginForLeaderboard', current ? 'false' : 'true');
return { success: true };
}
};

View File

@@ -0,0 +1,46 @@
<script lang="ts">
import { enhance } from '$app/forms';
import type { PageData } from './$types';
let { data }: { data: PageData } = $props();
</script>
<div class="p-4">
<h2 class="mb-4 text-lg font-bold">Settings</h2>
<div class="flex flex-col gap-4">
<form method="POST" action="?/toggleRegistration" use:enhance>
<div class="flex items-center gap-4 border rounded p-4">
<div class="flex-1">
<p class="font-semibold">Disable Registration</p>
<p class="text-sm opacity-70">Prevents new users from signing up via /register</p>
</div>
<button
type="submit"
class="border rounded px-4 py-1 {data.disableRegistration
? 'bg-red-700 text-white'
: 'bg-green-700 text-white'}"
>
{data.disableRegistration ? 'Disabled' : 'Enabled'}
</button>
</div>
</form>
<form method="POST" action="?/toggleLeaderboardLock" use:enhance>
<div class="flex items-center gap-4 border rounded p-4">
<div class="flex-1">
<p class="font-semibold">Require Login for Leaderboard</p>
<p class="text-sm opacity-70">Non-logged-in users will be redirected to /login</p>
</div>
<button
type="submit"
class="border rounded px-4 py-1 {data.requireLoginForLeaderboard
? 'bg-red-700 text-white'
: 'bg-green-700 text-white'}"
>
{data.requireLoginForLeaderboard ? 'Locked' : 'Public'}
</button>
</div>
</form>
</div>
</div>