im getting lazy but now i can disable the register page
This commit is contained in:
11
TODO.md
11
TODO.md
@@ -2,15 +2,6 @@
|
|||||||
player view - all registered events and scores for those events
|
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?
|
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
|
# Database - HIGH PRIORITY
|
||||||
load results from the excel sheet i made
|
load results from the excel sheet i made
|
||||||
export whole contest results to csv
|
export whole contest results to csv
|
||||||
@@ -29,7 +20,5 @@ result amendments
|
|||||||
|
|
||||||
## Easy shit
|
## Easy shit
|
||||||
animations for all leaderboards
|
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 layout on event view to match the homepage view
|
||||||
fix the marquee animation thing and make it global
|
fix the marquee animation thing and make it global
|
||||||
|
|||||||
@@ -3,6 +3,21 @@ import { sql, eq, and, desc, inArray } from 'drizzle-orm';
|
|||||||
import * as schema from '$lib/server/db/schema';
|
import * as schema from '$lib/server/db/schema';
|
||||||
import { globalEmitter } from './globalEmitter';
|
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
|
// Initial data for page load
|
||||||
export async function getAllInitialInfo() {
|
export async function getAllInitialInfo() {
|
||||||
return {
|
return {
|
||||||
|
|||||||
@@ -156,6 +156,11 @@ export const registeredEventsView = sqliteView('registeredEventsView').as((qb) =
|
|||||||
.innerJoin(divisions, eq(registeredEvents.division, divisions.id));
|
.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) => {
|
export const registeredEventPlayersView = sqliteView('registeredEventPlayersView').as((qb) => {
|
||||||
return qb
|
return qb
|
||||||
.select({
|
.select({
|
||||||
|
|||||||
@@ -22,6 +22,11 @@
|
|||||||
border-solid border-red-500 px-2"
|
border-solid border-red-500 px-2"
|
||||||
href="/ledger">ledger</a
|
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}
|
||||||
{#if data.user}
|
{#if data.user}
|
||||||
<a
|
<a
|
||||||
|
|||||||
@@ -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
|
// 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();
|
return await getAllInitialInfo();
|
||||||
};
|
};
|
||||||
|
|
||||||
|
|||||||
@@ -4,10 +4,21 @@ import { eq } from 'drizzle-orm';
|
|||||||
import { db } from '$lib/server/db';
|
import { db } from '$lib/server/db';
|
||||||
import { scorers } from '$lib/server/db/schema';
|
import { scorers } from '$lib/server/db/schema';
|
||||||
import { generateSessionToken, createSession, setSessionTokenCookie } from '$lib/server/auth';
|
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 = {
|
export const actions: Actions = {
|
||||||
default: async (event) => {
|
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 data = await event.request.formData();
|
||||||
const username = data.get('username') as string;
|
const username = data.get('username') as string;
|
||||||
const password = data.get('password') as string;
|
const password = data.get('password') as string;
|
||||||
|
|||||||
@@ -1,11 +1,15 @@
|
|||||||
<script lang="ts">
|
<script lang="ts">
|
||||||
import type { ActionData } from './$types';
|
import type { PageData, ActionData } from './$types';
|
||||||
|
export let data: PageData;
|
||||||
export let form: ActionData;
|
export let form: ActionData;
|
||||||
</script>
|
</script>
|
||||||
|
|
||||||
<div class="auth-card">
|
<div class="auth-card">
|
||||||
<h1>Sign up</h1>
|
<h1>Sign up</h1>
|
||||||
|
|
||||||
|
{#if data.disabled}
|
||||||
|
<p class="error">Registration is currently disabled by an administrator.</p>
|
||||||
|
{:else}
|
||||||
<form method="POST">
|
<form method="POST">
|
||||||
<label>
|
<label>
|
||||||
Username
|
Username
|
||||||
@@ -33,6 +37,7 @@
|
|||||||
</form>
|
</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>
|
</div>
|
||||||
|
|
||||||
<style>
|
<style>
|
||||||
|
|||||||
58
src/routes/settings/+page.server.ts
Normal file
58
src/routes/settings/+page.server.ts
Normal 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 };
|
||||||
|
}
|
||||||
|
};
|
||||||
46
src/routes/settings/+page.svelte
Normal file
46
src/routes/settings/+page.svelte
Normal 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>
|
||||||
Reference in New Issue
Block a user