From 6e723aadf6de45a79b4ef64d288ea275628232c5 Mon Sep 17 00:00:00 2001
From: mat <github@matdoes.dev>
Date: Thu, 15 Dec 2022 14:59:56 -0600
Subject: start updating to sveltekit v1

---
 src/routes/verify/+page.svelte | 66 ++++++++++++++++++++++++++++++++++++++++++
 1 file changed, 66 insertions(+)
 create mode 100644 src/routes/verify/+page.svelte

(limited to 'src/routes/verify/+page.svelte')

diff --git a/src/routes/verify/+page.svelte b/src/routes/verify/+page.svelte
new file mode 100644
index 0000000..20de403
--- /dev/null
+++ b/src/routes/verify/+page.svelte
@@ -0,0 +1,66 @@
+<script lang="ts" context="module">
+	import type { Load } from '@sveltejs/kit'
+	export const load: Load = async ({ session, url }) => {
+		if (session.sid === undefined) {
+			return { redirect: '/login', status: 303 }
+		}
+		return {
+			props: {
+				errorCode: url.searchParams.get('error'),
+				current: url.searchParams.get('current'),
+				correct: url.searchParams.get('correct'),
+			},
+		}
+	}
+</script>
+
+<script lang="ts">
+	import Emoji from '$lib/Emoji.svelte'
+	import Head from '$lib/Head.svelte'
+	import Header from '$lib/Header.svelte'
+
+	export let errorCode: string | null
+	export let current: string | null
+	export let correct: string | null
+
+	const errorCodes = {
+		NO_IGN: 'Please enter a valid Minecraft username.',
+		NOT_LINKED:
+			'Please link your Discord in Hypixel by doing /profile -> Social media -> Discord. If you just changed it, wait a few minutes and try again.',
+		WRONG_NAME: `You're linked to ${current} on Hypixel. Please change this to ${correct} by doing /profile -> Social media -> Discord. If you just changed it, wait a few minutes and try again.`,
+		NO_KEY:
+			"This instance of skyblock-stats doesn't have a skyblock-api key set. Please contact the owner of the website if you believe this to be a mistake.",
+	}
+</script>
+
+<Head title="Verify Account" />
+<Header />
+
+<main>
+	<h1>Verify Minecraft account</h1>
+	<p>Please enter your Minecraft username to verify that this is your account.</p>
+	<p>
+		This will check with the Hypixel API that your Discord username matches the Discord name set in
+		your Hypixel settings.
+	</p>
+	{#if errorCode && errorCode in errorCodes}
+		<div class="error">
+			<Emoji value="🚫" />
+			{errorCodes[errorCode]}
+		</div>
+	{/if}
+	<form method="post" action="/verify">
+		<input placeholder="Username or UUID" name="ign" required />
+		<input type="submit" value="Enter" />
+	</form>
+</main>
+
+<style>
+	p {
+		margin: 0;
+	}
+	.error {
+		font-weight: bold;
+		margin: 1em 0;
+	}
+</style>
-- 
cgit 


From 925ad2bfff17f8d9381731fb0050936dcd7e0a72 Mon Sep 17 00:00:00 2001
From: mat <github@matdoes.dev>
Date: Thu, 15 Dec 2022 18:14:46 -0600
Subject: finish migrating to sveltekit v1

hopefully
---
 src/app.d.ts                                  |   8 +-
 src/hooks.server.ts                           |   7 +
 src/hooks.ts                                  |  25 ---
 src/lib/LoginButton.svelte                    |   2 +-
 src/lib/MayorSkin.svelte                      |  21 ++-
 src/routes/+page.server.ts                    |   7 +
 src/routes/+page.ts                           |   7 -
 src/routes/[player=username]/+page.ts         |  11 +-
 src/routes/auctionprices/+page.ts             |   4 +-
 src/routes/election/+page.svelte              |  17 --
 src/routes/election/+page.ts                  |   8 +
 src/routes/items/+page.svelte                 |  15 --
 src/routes/items/+page.ts                     |   6 +
 src/routes/leaderboard/+page.ts               |   8 +-
 src/routes/leaderboard/[name]/+page.ts        |   8 +-
 src/routes/leaderboards/+page.ts              |   4 +-
 src/routes/leaderboards/[name]/+page.ts       |  13 +-
 src/routes/loggedin/+page.ts                  |  37 -----
 src/routes/loggedin/+server.ts                |  30 ++++
 src/routes/login/+page.ts                     |  36 -----
 src/routes/login/+server.ts                   |  18 +++
 src/routes/logout/+page.ts                    |  28 ----
 src/routes/logout/+server.ts                  |  23 +++
 src/routes/player/+page.server.ts             |   8 +-
 src/routes/player/[player]/+page.ts           |  11 +-
 src/routes/player/[player]/[profile]/+page.ts |  16 +-
 src/routes/profile/+page.server.ts            |   7 +
 src/routes/profile/+page.svelte               | 180 +++++++++++++++++++++
 src/routes/profile/+page.ts                   |  44 ++++++
 src/routes/profile/index.svelte               | 219 --------------------------
 src/routes/profile/update.ts                  | 126 ---------------
 src/routes/profile/update/+server.ts          |  95 +++++++++++
 src/routes/verify/+page.server.ts             | 116 +++++++-------
 src/routes/verify/+page.svelte                |  25 +--
 src/routes/verify/+page.ts                    |  13 ++
 35 files changed, 549 insertions(+), 654 deletions(-)
 create mode 100644 src/hooks.server.ts
 delete mode 100644 src/hooks.ts
 create mode 100644 src/routes/+page.server.ts
 delete mode 100644 src/routes/+page.ts
 create mode 100644 src/routes/election/+page.ts
 create mode 100644 src/routes/items/+page.ts
 delete mode 100644 src/routes/loggedin/+page.ts
 create mode 100644 src/routes/loggedin/+server.ts
 delete mode 100644 src/routes/login/+page.ts
 create mode 100644 src/routes/login/+server.ts
 delete mode 100644 src/routes/logout/+page.ts
 create mode 100644 src/routes/logout/+server.ts
 create mode 100644 src/routes/profile/+page.server.ts
 create mode 100644 src/routes/profile/+page.svelte
 create mode 100644 src/routes/profile/+page.ts
 delete mode 100644 src/routes/profile/index.svelte
 delete mode 100644 src/routes/profile/update.ts
 create mode 100644 src/routes/profile/update/+server.ts
 create mode 100644 src/routes/verify/+page.ts

(limited to 'src/routes/verify/+page.svelte')

diff --git a/src/app.d.ts b/src/app.d.ts
index b41fa6f..9f4467d 100644
--- a/src/app.d.ts
+++ b/src/app.d.ts
@@ -3,10 +3,13 @@
 // // See https://kit.svelte.dev/docs/typescript
 // // for information about these interfaces
 declare namespace App {
+	// interface Error { }
+
 	interface Locals {
 		sid: string | undefined
 	}
 
+	// interface PageData { }
 
 	interface Platform {
 		env: {
@@ -18,9 +21,4 @@ declare namespace App {
 		}
 	}
 
-	interface Session {
-		sid: string | undefined
-	}
-
-	// interface Stuff {}
 }
diff --git a/src/hooks.server.ts b/src/hooks.server.ts
new file mode 100644
index 0000000..0d2087b
--- /dev/null
+++ b/src/hooks.server.ts
@@ -0,0 +1,7 @@
+import type { Handle } from '@sveltejs/kit'
+
+export const handle = (async ({ event, resolve }) => {
+	event.locals.sid = event.cookies.get('sid')
+	const response = await resolve(event)
+	return response
+}) satisfies Handle
\ No newline at end of file
diff --git a/src/hooks.ts b/src/hooks.ts
deleted file mode 100644
index 457e8ae..0000000
--- a/src/hooks.ts
+++ /dev/null
@@ -1,25 +0,0 @@
-import cookie from 'cookie'
-import type { Handle } from '@sveltejs/kit'
-
-
-export const handle: Handle = async ({ event, resolve }) => {
-	const cookies = cookie.parse(event.request.headers.get('cookie') || '')
-
-	event.locals.sid = cookies.sid
-
-	const response = await resolve(event)
-
-	return response
-}
-
-export const getSession: GetSession = async ({ locals }) => {
-	return {
-		sid: locals.sid
-	}
-}
-
-// export const externalFetch: ExternalFetch = async (request) => {
-// 	const response = await fetch(request)
-
-// 	return response
-// }
\ No newline at end of file
diff --git a/src/lib/LoginButton.svelte b/src/lib/LoginButton.svelte
index bba078e..7ebf5d7 100644
--- a/src/lib/LoginButton.svelte
+++ b/src/lib/LoginButton.svelte
@@ -6,7 +6,7 @@
 	{#if loggedIn}
 		<a href="/profile"><button class="login-button">Edit profile</button></a>
 	{:else}
-		<a href="/login">
+		<a href="/login" rel="external">
 			<button class="login-button"
 				><img src="/discord-mark-light.svg" alt="Discord logo" />Log in with Discord</button
 			>
diff --git a/src/lib/MayorSkin.svelte b/src/lib/MayorSkin.svelte
index ad0a9a8..2360fab 100644
--- a/src/lib/MayorSkin.svelte
+++ b/src/lib/MayorSkin.svelte
@@ -23,14 +23,21 @@
 		faith: '64b39d0756b92b8b7599d1f971580088954e21c5f60c673d0d4f63693fb002b5',
 	}
 
-	let url: string
+	let url: string | undefined
 	$: {
 		if (name.toLowerCase() === 'derpy') url = '/villager.png'
-		else url = `https://mc-heads.net/body/${skinIds[name.toLowerCase()]}`
+		else {
+			const skinId = skinIds[name.toLowerCase()]
+			url = skinId ? `https://mc-heads.net/body/${skinId}` : undefined
+		}
 	}
 </script>
 
-<img src={url} alt="Mayor {toTitleCase(name)}" />
+{#if url}
+	<img src={url} alt="Mayor {toTitleCase(name)}" />
+{:else}
+	<div class="no-skin-found">Unknown mayor</div>
+{/if}
 
 <style>
 	img {
@@ -39,4 +46,12 @@
 		height: 12em;
 		margin: 0 auto;
 	}
+
+	.no-skin-found {
+		display: flex;
+		align-items: center;
+		justify-content: center;
+		height: 12em;
+		margin: 0 auto;
+	}
 </style>
diff --git a/src/routes/+page.server.ts b/src/routes/+page.server.ts
new file mode 100644
index 0000000..4596c1b
--- /dev/null
+++ b/src/routes/+page.server.ts
@@ -0,0 +1,7 @@
+import type { ServerLoad } from '@sveltejs/kit'
+
+export const load = (({ locals }) => {
+	return {
+		loggedIn: locals.sid !== undefined,
+	}
+}) satisfies ServerLoad
diff --git a/src/routes/+page.ts b/src/routes/+page.ts
deleted file mode 100644
index cd45f69..0000000
--- a/src/routes/+page.ts
+++ /dev/null
@@ -1,7 +0,0 @@
-import type { PageData } from './$types'
-
-export const load = (({ session }) => {
-	return {
-		loggedIn: session.sid !== undefined,
-	}
-}) satisfies PageData
diff --git a/src/routes/[player=username]/+page.ts b/src/routes/[player=username]/+page.ts
index c846929..289cd48 100644
--- a/src/routes/[player=username]/+page.ts
+++ b/src/routes/[player=username]/+page.ts
@@ -1,11 +1,6 @@
+import { redirect } from '@sveltejs/kit'
 import type { PageLoad } from './$types'
 
-
-export const get = (({ params }) => {
-	return {
-		status: 303,
-		headers: {
-			location: `/player/${params.player}`
-		}
-	}
+export const load = (({ params }) => {
+	throw redirect(303, `/player/${params.player}`)
 }) satisfies PageLoad
\ No newline at end of file
diff --git a/src/routes/auctionprices/+page.ts b/src/routes/auctionprices/+page.ts
index 8166a3c..31af8e3 100644
--- a/src/routes/auctionprices/+page.ts
+++ b/src/routes/auctionprices/+page.ts
@@ -1,4 +1,4 @@
-import type { PageData } from './$types'
+import type { PageLoad } from './$types'
 import { fetchApi } from '$lib/api'
 
 export const load = (async ({ fetch }) => {
@@ -10,4 +10,4 @@ export const load = (async ({ fetch }) => {
 		prices,
 		items,
 	}
-}) satisfies PageData
+}) satisfies PageLoad
diff --git a/src/routes/election/+page.svelte b/src/routes/election/+page.svelte
index 1754655..dc4719e 100644
--- a/src/routes/election/+page.svelte
+++ b/src/routes/election/+page.svelte
@@ -1,20 +1,3 @@
-<script lang="ts" context="module">
-	import type { Load } from '@sveltejs/kit'
-	import { fetchApi } from '$lib/api'
-
-	export const load: Load = async ({ params, fetch }) => {
-		const data = await fetchApi(`election?t=${Math.floor(Date.now() / 1000)}`, fetch).then(r =>
-			r.json()
-		)
-
-		return {
-			props: {
-				data,
-			},
-		}
-	}
-</script>
-
 <script lang="ts">
 	import Header from '$lib/Header.svelte'
 	import Head from '$lib/Head.svelte'
diff --git a/src/routes/election/+page.ts b/src/routes/election/+page.ts
new file mode 100644
index 0000000..e41087d
--- /dev/null
+++ b/src/routes/election/+page.ts
@@ -0,0 +1,8 @@
+import { fetchApi } from '$lib/api'
+import type { PageLoad } from './$types'
+
+export const load = (async ({ fetch }) => {
+	return await fetchApi(`election?t=${Math.floor(Date.now() / 1000)}`, fetch).then(r =>
+		r.json()
+	)
+}) satisfies PageLoad
diff --git a/src/routes/items/+page.svelte b/src/routes/items/+page.svelte
index 82b3ee3..e378bdf 100644
--- a/src/routes/items/+page.svelte
+++ b/src/routes/items/+page.svelte
@@ -1,18 +1,3 @@
-<script lang="ts" context="module">
-	import type { Load } from '@sveltejs/kit'
-	import { fetchApi } from '$lib/api'
-
-	export const load: Load = async ({ params, fetch }) => {
-		const data = await fetchApi(`items`, fetch).then(r => r.json())
-
-		return {
-			props: {
-				data,
-			},
-		}
-	}
-</script>
-
 <script lang="ts">
 	import Header from '$lib/Header.svelte'
 	import Head from '$lib/Head.svelte'
diff --git a/src/routes/items/+page.ts b/src/routes/items/+page.ts
new file mode 100644
index 0000000..e5bb249
--- /dev/null
+++ b/src/routes/items/+page.ts
@@ -0,0 +1,6 @@
+import { fetchApi } from '$lib/api'
+import type { PageLoad } from './$types'
+
+export const load = (async ({ fetch }) => {
+	return await fetchApi(`items`, fetch).then(r => r.json())
+}) satisfies PageLoad
diff --git a/src/routes/leaderboard/+page.ts b/src/routes/leaderboard/+page.ts
index 871f4bc..68f69ac 100644
--- a/src/routes/leaderboard/+page.ts
+++ b/src/routes/leaderboard/+page.ts
@@ -1,10 +1,6 @@
+import { redirect } from '@sveltejs/kit'
 import type { PageLoad } from './$types'
 
 export const load = (() => {
-    return {
-        status: 303,
-        headers: {
-            location: '/leaderboards'
-        }
-    }
+    throw redirect(303, '/leaderboards')
 }) satisfies PageLoad
diff --git a/src/routes/leaderboard/[name]/+page.ts b/src/routes/leaderboard/[name]/+page.ts
index c4b668e..8de4334 100644
--- a/src/routes/leaderboard/[name]/+page.ts
+++ b/src/routes/leaderboard/[name]/+page.ts
@@ -1,12 +1,8 @@
+import { redirect } from '@sveltejs/kit'
 import type { PageLoad } from './$types'
 
 // The route /leaderboard/<name> was moved to /leaderboards/<name> for
 // consistency.
 export const get = (({ params }) => {
-    return {
-        status: 303,
-        headers: {
-            location: `/leaderboards/${params.name}`
-        }
-    }
+    throw redirect(303, `/leaderboards/${params.name}`)
 }) satisfies PageLoad
diff --git a/src/routes/leaderboards/+page.ts b/src/routes/leaderboards/+page.ts
index d5a8480..f28ac79 100644
--- a/src/routes/leaderboards/+page.ts
+++ b/src/routes/leaderboards/+page.ts
@@ -1,6 +1,6 @@
-import type { PageData } from './$types'
+import type { PageLoad } from './$types'
 import { fetchApi } from '$lib/api'
 
 export const load = (async ({ fetch }) => {
 	return await fetchApi(`leaderboards`, fetch).then(r => r.json())
-}) satisfies PageData
+}) satisfies PageLoad
diff --git a/src/routes/leaderboards/[name]/+page.ts b/src/routes/leaderboards/[name]/+page.ts
index 78f3f6b..735be61 100644
--- a/src/routes/leaderboards/[name]/+page.ts
+++ b/src/routes/leaderboards/[name]/+page.ts
@@ -1,16 +1,13 @@
-import type { PageData } from './$types'
+import type { PageLoad } from './$types'
 import { fetchApi } from '$lib/api'
+import { error } from '@sveltejs/kit'
 
 export const load = (async ({ params, fetch }) => {
 	const dataText = await fetchApi(`leaderboards/${params.name}`, fetch).then(r => r.text())
 
 	const data = JSON.parse(dataText)
 
-	if (data.list.length === 0) return { status: 404, error: 'Unknown leaderboard' }
+	if (data.list.length === 0) throw error(404, 'Unknown leaderboard')
 
-	return {
-		props: {
-			data,
-		},
-	} as any
-}) satisfies PageData
+	return data
+}) satisfies PageLoad
diff --git a/src/routes/loggedin/+page.ts b/src/routes/loggedin/+page.ts
deleted file mode 100644
index 9ae28ef..0000000
--- a/src/routes/loggedin/+page.ts
+++ /dev/null
@@ -1,37 +0,0 @@
-import { fetchApi } from '$lib/api'
-import type { RequestHandler } from '@sveltejs/kit'
-
-export const get: RequestHandler = async ({ url }) => {
-	const code = url.searchParams.get('code')
-	const redirectUri = `${url.protocol}//${url.host}/loggedin`
-	const response = await fetchApi(`accounts/createsession`, fetch, {
-		method: 'POST',
-		headers: {
-			'content-type': 'application/json',
-		},
-		body: JSON.stringify({
-			code,
-			redirectUri: redirectUri
-		}),
-	}).then(res => {
-		if (res.status !== 200)
-			throw new Error(res.statusText)
-		return res.json()
-	})
-
-	if (response.ok) {
-		return {
-			status: 303,
-			headers: {
-				location: '/verify',
-				'Set-Cookie': `sid=${response.session_id}; Max-Age=31536000000; Path=/; HttpOnly`
-			}
-		}
-	}
-	return {
-		status: 303,
-		headers: {
-			location: '/login',
-		}
-	}
-}
diff --git a/src/routes/loggedin/+server.ts b/src/routes/loggedin/+server.ts
new file mode 100644
index 0000000..4772cf6
--- /dev/null
+++ b/src/routes/loggedin/+server.ts
@@ -0,0 +1,30 @@
+import { fetchApi } from '$lib/api'
+import { redirect, type RequestHandler, } from '@sveltejs/kit'
+
+export const GET = (async ({ url, cookies }) => {
+	const code = url.searchParams.get('code')
+	const redirectUri = `${url.protocol}//${url.host}/loggedin`
+	const response = await fetchApi(`accounts/createsession`, fetch, {
+		method: 'POST',
+		headers: {
+			'content-type': 'application/json',
+		},
+		body: JSON.stringify({
+			code,
+			redirectUri: redirectUri
+		}),
+	}).then(res => {
+		if (res.status !== 200)
+			throw new Error(res.statusText)
+		return res.json()
+	})
+
+	if (response.ok) {
+		cookies.set('sid', response.session_id, {
+			maxAge: 31536000000,
+			httpOnly: true,
+		})
+		throw redirect(303, '/verify')
+	}
+	throw redirect(303, '/login')
+}) satisfies RequestHandler
diff --git a/src/routes/login/+page.ts b/src/routes/login/+page.ts
deleted file mode 100644
index 1cd9b23..0000000
--- a/src/routes/login/+page.ts
+++ /dev/null
@@ -1,36 +0,0 @@
-import type { RequestHandler } from '@sveltejs/kit'
-import env from '$lib/env'
-
-
-export const get: RequestHandler = async ({ request, platform }) => {
-	const host = request.headers.get('host')
-
-	const clientId = env(platform).DISCORD_CLIENT_ID
-
-	if (!clientId)
-		return {
-			status: 500,
-			headers: {
-				'content-type': 'text/plain',
-			},
-			body: 'DISCORD_CLIENT_ID is not set as an environment variable. Please contact the owner of the website if this was expected to work.',
-		}
-
-	if (!host)
-		return {
-			status: 400,
-			headers: {
-				'content-type': 'text/plain',
-			},
-			body: 'Host header is required.',
-		}
-
-	const protocol = request.url.startsWith('https') ? 'https' : 'http'
-
-	return {
-		status: 303,
-		headers: {
-			location: `https://discord.com/oauth2/authorize?client_id=${clientId}&redirect_uri=${protocol}://${host}%2Floggedin&response_type=code&scope=identify`
-		}
-	}
-}
diff --git a/src/routes/login/+server.ts b/src/routes/login/+server.ts
new file mode 100644
index 0000000..684f11e
--- /dev/null
+++ b/src/routes/login/+server.ts
@@ -0,0 +1,18 @@
+import env from '$lib/env'
+import { error, redirect, type RequestHandler } from '@sveltejs/kit'
+
+export const GET = (async ({ request, platform }) => {
+	const host = request.headers.get('host')
+
+	const clientId = env(platform).DISCORD_CLIENT_ID
+
+	if (!clientId)
+		throw error(500, 'DISCORD_CLIENT_ID is not set as an environment variable. Please contact the owner of the website if this was expected to work.')
+
+	if (!host)
+		throw error(400, 'Host header is required.')
+
+	const protocol = request.url.startsWith('https') ? 'https' : 'http'
+
+	throw redirect(303, `https://discord.com/oauth2/authorize?client_id=${clientId}&redirect_uri=${protocol}://${host}%2Floggedin&response_type=code&scope=identify`)
+}) satisfies RequestHandler
diff --git a/src/routes/logout/+page.ts b/src/routes/logout/+page.ts
deleted file mode 100644
index 25be86c..0000000
--- a/src/routes/logout/+page.ts
+++ /dev/null
@@ -1,28 +0,0 @@
-import { fetchApi } from '$lib/api'
-import type { RequestHandler } from '@sveltejs/kit'
-
-export const get: RequestHandler = async ({ locals, url }) => {
-	// if the sid is wrong, nothing to do
-	if (url.searchParams.has('sid') && url.searchParams.get('sid') === locals.sid) {
-		await fetchApi(`accounts/session`, fetch, {
-			method: 'DELETE',
-			headers: {
-				'Content-Type': 'application/json',
-			},
-			body: JSON.stringify({
-				uuid: locals.sid
-			}),
-		}).then(res => {
-			if (res.status !== 200)
-				throw new Error(res.statusText)
-		})
-	}
-	return {
-		status: 303,
-		headers: {
-			location: '/',
-			'Set-Cookie': 'sid=; expires=Thu, 01 Jan 1970 00:00:00 GMT; path=/;'
-		}
-	}
-
-}
diff --git a/src/routes/logout/+server.ts b/src/routes/logout/+server.ts
new file mode 100644
index 0000000..2e0f40d
--- /dev/null
+++ b/src/routes/logout/+server.ts
@@ -0,0 +1,23 @@
+import { fetchApi } from '$lib/api'
+import { redirect, type RequestHandler } from '@sveltejs/kit'
+
+export const GET = (async ({ url, cookies, locals }) => {
+	// if the sid is wrong, nothing to do
+	if (url.searchParams.has('sid') && url.searchParams.get('sid') === locals.sid) {
+		await fetchApi(`accounts/session`, fetch, {
+			method: 'DELETE',
+			headers: {
+				'Content-Type': 'application/json',
+			},
+			body: JSON.stringify({
+				uuid: locals.sid
+			}),
+		}).then(res => {
+			if (res.status !== 200)
+				throw new Error(res.statusText)
+		})
+	}
+
+	cookies.delete('sid')
+	throw redirect(303, '/')
+}) as RequestHandler
diff --git a/src/routes/player/+page.server.ts b/src/routes/player/+page.server.ts
index 3a1b069..eafae71 100644
--- a/src/routes/player/+page.server.ts
+++ b/src/routes/player/+page.server.ts
@@ -1,3 +1,4 @@
+import { redirect } from '@sveltejs/kit'
 import type { Actions } from './$types'
 
 export const actions: Actions = {
@@ -6,11 +7,6 @@ export const actions: Actions = {
 
         const player = form.get('user-search')
 
-        return {
-            status: 303,
-            headers: {
-                location: `/player/${player}`
-            }
-        }
+        throw redirect(303, `/player/${player}`)
     }
 }
\ No newline at end of file
diff --git a/src/routes/player/[player]/+page.ts b/src/routes/player/[player]/+page.ts
index bd7c002..023c3ff 100644
--- a/src/routes/player/[player]/+page.ts
+++ b/src/routes/player/[player]/+page.ts
@@ -1,5 +1,6 @@
 import type { PageLoad } from './$types'
 import { fetchApi } from '$lib/api'
+import { error, redirect } from '@sveltejs/kit'
 
 export const load = (async ({ params, fetch }) => {
 	const player: string = params.player!
@@ -7,17 +8,11 @@ export const load = (async ({ params, fetch }) => {
 	const data = await fetchApi(`player/${player}?customization=true`, fetch).then(r => r.json())
 
 	if (!data.player) {
-		return {
-			status: 404,
-			error: 'Unknown player',
-		}
+		throw error(404, 'Unknown player')
 	}
 
 	if (data.player.username !== player) {
-		return {
-			redirect: `/player/${data.player.username}`,
-			status: 302,
-		} as any
+		throw redirect(302, `/player/${data.player.username}`)
 	}
 
 	return data
diff --git a/src/routes/player/[player]/[profile]/+page.ts b/src/routes/player/[player]/[profile]/+page.ts
index e97cbfc..34a4e2a 100644
--- a/src/routes/player/[player]/[profile]/+page.ts
+++ b/src/routes/player/[player]/[profile]/+page.ts
@@ -2,6 +2,7 @@ import type { CleanMemberProfile } from '$lib/APITypes'
 import type { PageLoad } from './$types'
 import { loadPack } from '$lib/packs'
 import { fetchApi } from '$lib/api'
+import { error, redirect } from '@sveltejs/kit'
 
 export const load = (async ({ params, fetch, url }) => {
 	const player: string = params.player
@@ -19,23 +20,14 @@ export const load = (async ({ params, fetch, url }) => {
 	})
 
 	if (!data.member) {
-		return {
-			status: 404,
-			error: 'Unknown profile',
-		}
+		throw error(404, 'Unknown profile')
 	}
 
 	if (data.member.username !== player) {
-		return {
-			redirect: `/player/${data.member.username}/${data.profile.name}`,
-			status: 302,
-		} as any
+		throw redirect(302, `/player/${data.member.username}/${data.profile.name}`)
 	}
 	if (!data.member.left && data.profile.name !== profile) {
-		return {
-			redirect: `/player/${data.member.username}/${data.profile.name}`,
-			status: 302,
-		} as any
+		throw redirect(302, `/player/${data.member.username}/${data.profile.name}`)
 	}
 
 	const packName = url.searchParams.get('pack') ?? data?.customization?.pack
diff --git a/src/routes/profile/+page.server.ts b/src/routes/profile/+page.server.ts
new file mode 100644
index 0000000..60c24c1
--- /dev/null
+++ b/src/routes/profile/+page.server.ts
@@ -0,0 +1,7 @@
+import type { ServerLoad } from '@sveltejs/kit'
+
+export const load = (({ locals }) => {
+	return {
+		sid: locals.sid
+	}
+}) satisfies ServerLoad
diff --git a/src/routes/profile/+page.svelte b/src/routes/profile/+page.svelte
new file mode 100644
index 0000000..244d1a2
--- /dev/null
+++ b/src/routes/profile/+page.svelte
@@ -0,0 +1,180 @@
+<script lang="ts">
+	import Emoji from '$lib/Emoji.svelte'
+	import { browser } from '$app/environment'
+	import Tooltip from '$lib/Tooltip.svelte'
+	import { onDestroy, onMount } from 'svelte'
+	import backgroundNames from '../../_backgrounds.json'
+	import type { AccountCustomization, AccountSchema, CleanUser, SessionSchema } from '$lib/APITypes'
+	import type { PageData } from './$types'
+	import Head from '$lib/Head.svelte'
+	import Header from '$lib/Header.svelte'
+
+	export let data: PageData
+
+	export let session: SessionSchema = data.session
+	export let account: AccountSchema = data.account
+	export let player: CleanUser = data.player
+
+	export let isDonator: boolean = data.isDonator
+
+	let pack: AccountCustomization['pack'] = account?.customization?.pack ?? 'furfsky_reborn'
+	let blurBackground: AccountCustomization['blurBackground'] =
+		account?.customization?.blurBackground ?? false
+	let backgroundName: string | undefined = account?.customization?.backgroundUrl?.replace(
+		/^\/backgrounds\//,
+		''
+	)
+	let emoji: AccountCustomization['emoji'] = account?.customization?.emoji
+
+	let mounted = false
+	onMount(() => {
+		mounted = true
+	})
+	onDestroy(() => {
+		mounted = false
+	})
+
+	let error: string | undefined
+	let loading: boolean = false
+
+	async function updateProfile() {
+		if (!browser || !mounted) return
+		error = undefined
+		loading = true
+		const response = await fetch('/profile/update', {
+			method: 'PATCH',
+			headers: {
+				'Content-Type': 'application/json',
+			},
+			body: JSON.stringify({
+				pack,
+				blurBackground,
+				backgroundName,
+				emoji,
+			}),
+		})
+			.then(r => {
+				loading = false
+				return r.json()
+			})
+			.catch(e => {
+				loading = false
+				error = e.message
+			})
+		if (!response.ok) {
+			error = response.error
+		}
+	}
+
+	// call updateProfile whenever anything is changed
+	$: [pack, blurBackground, backgroundName, emoji, updateProfile()]
+</script>
+
+<Head title="Customize Profile" />
+<Header />
+
+<main>
+	{#if session && session._id}
+		<a href="/logout?sid={session._id}" class="logout"><button>Log out</button></a>
+	{/if}
+	<h1>Customize Profile</h1>
+	<noscript>
+		<p><Emoji value="⚠" /> Please enable JavaScript to use this page.</p>
+	</noscript>
+
+	<p class="status">
+		{#if error}
+			<Emoji value="🚫" /> {error}
+		{:else if loading}
+			<Emoji value="🔄" /> Loading...
+		{/if}
+	</p>
+
+	<a href="/player/{player.player?.username}">View profile</a>
+	<p>
+		<label for="pack-selector">Pack: </label>
+		<select bind:value={pack} id="pack-selector">
+			<option value="ectoplasm">Ectoplasm</option>
+			<option value="furfsky">Furfsky</option>
+			<option value="furfsky_reborn">Furfsky Reborn</option>
+			<option value="hypixel+">Hypixel+</option>
+			<option value="packshq">PacksHQ</option>
+			<option value="rnbw">RNBW</option>
+			<option value="vanilla">Vanilla</option>
+			<option value="worlds_and_beyond">Worlds and Beyond</option>
+		</select>
+	</p>
+	<p>
+		<label for="blur-background-toggle">Blur and darken background behind content: </label>
+		<input type="checkbox" id="blur-background-toggle" bind:checked={blurBackground} />
+	</p>
+	{#if isDonator}
+		<label for="profile-emoji-selector">Emoji next to username: </label>
+		<input type="text" name="emoji" id="profile-emoji-selector" bind:value={emoji} />
+		<Tooltip width="15em">
+			<span slot="tooltip">
+				<p>Windows: <code>win+.</code></p>
+				<p>Mobile: Emoji keyboard</p>
+			</span>
+			<span>ⓘ</span>
+		</Tooltip>
+	{/if}
+	<h2>Background</h2>
+	<div id="background-selector-list">
+		{#each backgroundNames as thisBackgroundName}
+			<span
+				class="selectable-background-option"
+				class:selected={thisBackgroundName === backgroundName}
+				style="background-image: url(/backgrounds-small/{thisBackgroundName})"
+				title={thisBackgroundName}
+				on:click={() => (backgroundName = thisBackgroundName)}
+				on:keypress={e => {
+					if (e.key === 'Enter') {
+						backgroundName = thisBackgroundName
+					}
+				}}
+			/>
+		{/each}
+	</div>
+</main>
+
+<style>
+	.logout {
+		position: absolute;
+		top: 0.5em;
+		right: 0.5em;
+	}
+	.status {
+		position: relative;
+		top: -1.6em;
+		height: 0;
+		margin: 0;
+	}
+	#profile-emoji-selector {
+		width: 2em;
+	}
+	p {
+		margin: 0.2em 0;
+	}
+	#background-selector-list {
+		display: flex;
+		flex-wrap: wrap;
+	}
+	.selectable-background-option {
+		display: inline-block;
+		height: 10em;
+		width: 18em;
+		background-position: center;
+		background-size: 110%;
+		margin: 0.5em;
+		border-radius: 1em;
+		transition: background-size 500ms;
+		cursor: pointer;
+	}
+	.selectable-background-option:hover {
+		background-size: 125%;
+	}
+	.selectable-background-option.selected {
+		box-shadow: 0 0 0 2pt #06ec;
+	}
+</style>
diff --git a/src/routes/profile/+page.ts b/src/routes/profile/+page.ts
new file mode 100644
index 0000000..cdd2a93
--- /dev/null
+++ b/src/routes/profile/+page.ts
@@ -0,0 +1,44 @@
+import { fetchApi } from '$lib/api'
+import type { AccountSchema, SessionSchema } from '$lib/APITypes'
+import donators from '../../_donators.json'
+import admins from '../../_admins.json'
+import type { PageLoad } from './$types'
+import { redirect } from '@sveltejs/kit'
+
+export const load = (async ({ fetch, data }) => {
+	const sessionResponse: { session: SessionSchema | null; account: AccountSchema | null } | null =
+		await fetchApi(`accounts/session`, fetch, {
+			method: 'POST',
+			headers: {
+				'Content-Type': 'application/json',
+			},
+			body: JSON.stringify({
+				uuid: data.sid,
+			}),
+		}).then(r => r.json())
+
+	const playerResponse = sessionResponse?.account
+		? await fetchApi(`player/${sessionResponse.account.minecraftUuid}`, fetch).then(r => r.json())
+		: null
+
+	// redirect to /login if the user is not logged in
+	if (
+		!sessionResponse ||
+		!sessionResponse.account ||
+		!sessionResponse.session ||
+		!playerResponse.player
+	) {
+		throw redirect(303, '/login')
+	}
+
+	const isDonator =
+		donators.find(d => d?.uuid === sessionResponse.account?.minecraftUuid) !== undefined
+	const isAdmin = admins.find(a => a === sessionResponse.account?.minecraftUuid) !== undefined
+
+	return {
+		session: sessionResponse.session,
+		account: sessionResponse.account,
+		player: playerResponse,
+		isDonator: isDonator || isAdmin,
+	}
+}) as PageLoad
diff --git a/src/routes/profile/index.svelte b/src/routes/profile/index.svelte
deleted file mode 100644
index 0d5ad04..0000000
--- a/src/routes/profile/index.svelte
+++ /dev/null
@@ -1,219 +0,0 @@
-<script lang="ts" context="module">
-	import type { Load } from '@sveltejs/kit'
-	import { fetchApi } from '$lib/api'
-	import type { AccountCustomization, AccountSchema, CleanUser, SessionSchema } from '$lib/APITypes'
-	import Head from '$lib/Head.svelte'
-	import Header from '$lib/Header.svelte'
-	import donators from '../../_donators.json'
-	import admins from '../../_admins.json'
-
-	export const load: Load = async ({ fetch, session }) => {
-		const sessionResponse: { session: SessionSchema | null; account: AccountSchema | null } | null =
-			await fetchApi(`accounts/session`, fetch, {
-				method: 'POST',
-				headers: {
-					'Content-Type': 'application/json',
-				},
-				body: JSON.stringify({
-					uuid: session.sid,
-				}),
-			}).then(r => r.json())
-
-		const playerResponse = sessionResponse?.account
-			? await fetchApi(`player/${sessionResponse.account.minecraftUuid}`, fetch).then(r => r.json())
-			: null
-
-		// redirect to /login if the user is not logged in
-		if (
-			!sessionResponse ||
-			!sessionResponse.account ||
-			!sessionResponse.session ||
-			!playerResponse.player
-		) {
-			return { redirect: '/login', status: 303 }
-		}
-
-		const isDonator =
-			donators.find(d => d?.uuid === sessionResponse.account?.minecraftUuid) !== undefined
-		const isAdmin = admins.find(a => a === sessionResponse.account?.minecraftUuid) !== undefined
-
-		return {
-			props: {
-				session: sessionResponse.session,
-				account: sessionResponse.account,
-				player: playerResponse,
-				isDonator: isDonator || isAdmin,
-			},
-		}
-	}
-</script>
-
-<script lang="ts">
-	import Emoji from '$lib/Emoji.svelte'
-	import { browser } from '$app/environment'
-	import Tooltip from '$lib/Tooltip.svelte'
-	import { onDestroy, onMount } from 'svelte'
-	import backgroundNames from '../../_backgrounds.json'
-
-	export let session: SessionSchema
-	export let account: AccountSchema
-	export let player: CleanUser
-
-	export let isDonator: boolean
-
-	let pack: AccountCustomization['pack'] = account?.customization?.pack ?? 'furfsky_reborn'
-	let blurBackground: AccountCustomization['blurBackground'] =
-		account?.customization?.blurBackground ?? false
-	let backgroundName: string | undefined = account?.customization?.backgroundUrl?.replace(
-		/^\/backgrounds\//,
-		''
-	)
-	let emoji: AccountCustomization['emoji'] = account?.customization?.emoji
-
-	let mounted = false
-	onMount(() => {
-		mounted = true
-	})
-	onDestroy(() => {
-		mounted = false
-	})
-
-	let error: string | undefined
-	let loading: boolean = false
-
-	async function updateProfile() {
-		if (!browser || !mounted) return
-		error = undefined
-		loading = true
-		const response = await fetch('/profile/update', {
-			method: 'PATCH',
-			headers: {
-				'Content-Type': 'application/json',
-			},
-			body: JSON.stringify({
-				pack,
-				blurBackground,
-				backgroundName,
-				emoji,
-			}),
-		})
-			.then(r => {
-				loading = false
-				return r.json()
-			})
-			.catch(e => {
-				loading = false
-				error = e.message
-			})
-		if (!response.ok) {
-			error = response.error
-		}
-	}
-
-	// call updateProfile whenever anything is changed
-	$: [pack, blurBackground, backgroundName, emoji, updateProfile()]
-</script>
-
-<Head title="Customize Profile" />
-<Header />
-
-<main>
-	{#if session && session._id}
-		<a href="/logout?sid={session._id}" class="logout"><button>Log out</button></a>
-	{/if}
-	<h1>Customize Profile</h1>
-	<noscript>
-		<p><Emoji value="⚠" /> Please enable JavaScript to use this page.</p>
-	</noscript>
-
-	<p class="status">
-		{#if error}
-			<Emoji value="🚫" /> {error}
-		{:else if loading}
-			<Emoji value="🔄" /> Loading...
-		{/if}
-	</p>
-
-	<a href="/player/{player.player?.username}">View profile</a>
-	<p>
-		<label for="pack-selector">Pack: </label>
-		<select bind:value={pack} id="pack-selector">
-			<option value="ectoplasm">Ectoplasm</option>
-			<option value="furfsky">Furfsky</option>
-			<option value="furfsky_reborn">Furfsky Reborn</option>
-			<option value="hypixel+">Hypixel+</option>
-			<option value="packshq">PacksHQ</option>
-			<option value="rnbw">RNBW</option>
-			<option value="vanilla">Vanilla</option>
-			<option value="worlds_and_beyond">Worlds and Beyond</option>
-		</select>
-	</p>
-	<p>
-		<label for="blur-background-toggle">Blur and darken background behind content: </label>
-		<input type="checkbox" id="blur-background-toggle" bind:checked={blurBackground} />
-	</p>
-	{#if isDonator}
-		<label for="profile-emoji-selector">Emoji next to username: </label>
-		<input type="text" name="emoji" id="profile-emoji-selector" bind:value={emoji} />
-		<Tooltip width="15em">
-			<span slot="tooltip">
-				<p>Windows: <code>win+.</code></p>
-				<p>Mobile: Emoji keyboard</p>
-			</span>
-			<span>ⓘ</span>
-		</Tooltip>
-	{/if}
-	<h2>Background</h2>
-	<div id="background-selector-list">
-		{#each backgroundNames as thisBackgroundName}
-			<span
-				class="selectable-background-option"
-				class:selected={thisBackgroundName === backgroundName}
-				style="background-image: url(/backgrounds-small/{thisBackgroundName})"
-				title={thisBackgroundName}
-				on:click={() => (backgroundName = thisBackgroundName)}
-			/>
-		{/each}
-	</div>
-</main>
-
-<style>
-	.logout {
-		position: absolute;
-		top: 0.5em;
-		right: 0.5em;
-	}
-	.status {
-		position: relative;
-		top: -1.6em;
-		height: 0;
-		margin: 0;
-	}
-	#profile-emoji-selector {
-		width: 2em;
-	}
-	p {
-		margin: 0.2em 0;
-	}
-	#background-selector-list {
-		display: flex;
-		flex-wrap: wrap;
-	}
-	.selectable-background-option {
-		display: inline-block;
-		height: 10em;
-		width: 18em;
-		background-position: center;
-		background-size: 110%;
-		margin: 0.5em;
-		border-radius: 1em;
-		transition: background-size 500ms;
-		cursor: pointer;
-	}
-	.selectable-background-option:hover {
-		background-size: 125%;
-	}
-	.selectable-background-option.selected {
-		box-shadow: 0 0 0 2pt #06ec;
-	}
-</style>
diff --git a/src/routes/profile/update.ts b/src/routes/profile/update.ts
deleted file mode 100644
index 168cba8..0000000
--- a/src/routes/profile/update.ts
+++ /dev/null
@@ -1,126 +0,0 @@
-import { fetchApi } from '$lib/api'
-import type { AccountSchema, SessionSchema } from '$lib/APITypes'
-import type { RequestHandler } from '@sveltejs/kit'
-import backgroundFileNames from '../../_backgrounds.json'
-import donators from '../../_donators.json'
-import admins from '../../_admins.json'
-import type { JSONValue } from '@sveltejs/kit/types/internal'
-import env from '$lib/env'
-
-const emojiRegex = /^(\u00a9|\u00ae|[\u2000-\u3300]|\ud83c[\ud000-\udfff]|\ud83d[\ud000-\udfff]|\ud83e[\ud000-\udfff])$/
-
-function isValidEmoji(emoji: string) {
-	const match = emojiRegex.exec(emoji)
-	return match && match[0] === emoji && match.index === 0
-}
-
-
-export const patch: RequestHandler = async ({ request, locals, platform }) => {
-	if (locals.sid === undefined) {
-		return {
-			body: { ok: false, error: 'You are not logged in.' },
-			status: 401,
-		}
-	}
-	const key = env(platform).SKYBLOCK_STATS_API_KEY
-	if (!key) {
-		return {
-			body: { ok: false, error: 'The SKYBLOCK_STATS_API_KEY environment variable is not set.' },
-			status: 500,
-		}
-	}
-	const data = await request.json()
-
-	const sessionResponse: { session: SessionSchema | null, account: AccountSchema | null } = await fetchApi(`accounts/session`, fetch, {
-		method: 'POST',
-		headers: {
-			'Content-Type': 'application/json',
-		},
-		body: JSON.stringify({
-			uuid: locals.sid,
-		}),
-	}).then(r => r.json())
-	if (!sessionResponse.session || !sessionResponse.account?.minecraftUuid)
-		return {
-			body: { ok: false, error: 'Invalid session.' },
-			status: 401,
-		}
-
-	const backgroundName = data.backgroundName
-	const pack = data.pack
-	const blurBackground = data.blurBackground
-	const emoji = data.emoji
-
-	const isDonator = donators.find(d => d.uuid === sessionResponse.account?.minecraftUuid) !== undefined
-	const isAdmin = admins.includes(sessionResponse.account?.minecraftUuid)
-
-	if (typeof backgroundName !== 'undefined' && typeof backgroundName !== 'string') {
-		return {
-			body: { ok: false, error: 'Invalid background.' },
-			status: 400,
-		}
-	}
-	if (typeof pack !== 'string') {
-		return {
-			body: { ok: false, error: 'Invalid pack.' },
-			status: 400,
-		}
-	}
-	if (typeof blurBackground !== 'boolean') {
-		return {
-			body: { ok: false, error: 'Invalid blurBackground.' },
-			status: 400,
-		}
-	}
-	if (typeof emoji !== 'undefined' && typeof emoji !== 'string') {
-		return {
-			body: { ok: false, error: 'Invalid emoji.' },
-			status: 400,
-		}
-	}
-
-	// prevent people from putting non-existent backgrounds
-	if (backgroundName && !backgroundFileNames.includes(backgroundName))
-		return {
-			body: { ok: false, error: 'Invalid background.' },
-			status: 400,
-		}
-	const backgroundUrl = backgroundName ? `/backgrounds/${backgroundName}` : undefined
-
-	if (emoji) {
-		if (!isDonator && !isAdmin)
-			return {
-				body: { ok: false, error: 'You are not allowed to use emojis.' },
-				status: 401,
-			}
-		if (!isValidEmoji(emoji))
-			return {
-				body: { ok: false, error: 'Invalid emoji.' },
-				status: 400,
-			}
-	}
-
-	const updatedAccount: AccountSchema = {
-		discordId: sessionResponse.account.discordId,
-		customization: {
-			backgroundUrl,
-			pack,
-			blurBackground,
-			emoji,
-		},
-	}
-
-	const response = await fetchApi(`accounts/update`, fetch, {
-		method: 'POST',
-		headers: {
-			'Content-Type': 'application/json',
-			key: key
-		},
-		body: JSON.stringify(updatedAccount),
-	}).then(r => r.json())
-
-
-	return {
-		body: { ok: true } as JSONValue,
-	}
-}
\ No newline at end of file
diff --git a/src/routes/profile/update/+server.ts b/src/routes/profile/update/+server.ts
new file mode 100644
index 0000000..7190fb8
--- /dev/null
+++ b/src/routes/profile/update/+server.ts
@@ -0,0 +1,95 @@
+import { fetchApi } from '$lib/api'
+import type { AccountSchema, SessionSchema } from '$lib/APITypes'
+import backgroundFileNames from '../../../_backgrounds.json'
+import donators from '../../../_donators.json'
+import admins from '../../../_admins.json'
+import env from '$lib/env'
+import type { PageServerLoad } from '../$types'
+import { error, json } from '@sveltejs/kit'
+
+const emojiRegex = /^(\u00a9|\u00ae|[\u2000-\u3300]|\ud83c[\ud000-\udfff]|\ud83d[\ud000-\udfff]|\ud83e[\ud000-\udfff])$/
+
+function isValidEmoji(emoji: string) {
+	const match = emojiRegex.exec(emoji)
+	return match && match[0] === emoji && match.index === 0
+}
+
+
+export const PATCH = (async ({ request, locals, platform }) => {
+	if (locals.sid === undefined) {
+		throw error(401, 'You are not logged in.')
+	}
+	const key = env(platform).SKYBLOCK_STATS_API_KEY
+	if (!key) {
+		throw error(500, 'The SKYBLOCK_STATS_API_KEY environment variable is not set.')
+	}
+	const data = await request.json()
+
+	const sessionResponse: { session: SessionSchema | null, account: AccountSchema | null } = await fetchApi(`accounts/session`, fetch, {
+		method: 'POST',
+		headers: {
+			'Content-Type': 'application/json',
+		},
+		body: JSON.stringify({
+			uuid: locals.sid,
+		}),
+	}).then(r => r.json())
+	if (!sessionResponse.session || !sessionResponse.account?.minecraftUuid)
+		throw error(401, 'Invalid session.')
+
+	const backgroundName = data.backgroundName
+	const pack = data.pack
+	const blurBackground = data.blurBackground
+	const emoji = data.emoji
+
+	const isDonator = donators.find(d => d.uuid === sessionResponse.account?.minecraftUuid) !== undefined
+	const isAdmin = admins.includes(sessionResponse.account?.minecraftUuid)
+
+	if (typeof backgroundName !== 'undefined' && typeof backgroundName !== 'string') {
+		throw error(400, 'Invalid background.')
+	}
+	if (typeof pack !== 'string') {
+		throw error(400, 'Invalid pack.')
+	}
+	if (typeof blurBackground !== 'boolean') {
+		throw error(400, 'Invalid blurBackground.')
+	}
+	if (typeof emoji !== 'undefined' && typeof emoji !== 'string') {
+		throw error(400, 'Invalid emoji.')
+	}
+
+	// prevent people from putting non-existent backgrounds
+	if (backgroundName && !backgroundFileNames.includes(backgroundName))
+		throw error(400, 'Invalid background.')
+	const backgroundUrl = backgroundName ? `/backgrounds/${backgroundName}` : undefined
+
+	if (emoji) {
+		if (!isDonator && !isAdmin)
+			throw error(401, 'You are not allowed to use emojis.')
+		if (!isValidEmoji(emoji))
+			throw error(400, 'Invalid emoji.')
+	}
+
+	const updatedAccount: AccountSchema = {
+		discordId: sessionResponse.account.discordId,
+		customization: {
+			backgroundUrl,
+			pack,
+			blurBackground,
+			emoji,
+		},
+	}
+
+	const response = await fetchApi(`accounts/update`, fetch, {
+		method: 'POST',
+		headers: {
+			'Content-Type': 'application/json',
+			key: key
+		},
+		body: JSON.stringify(updatedAccount),
+	}).then(r => r.json())
+	console.log(response)
+
+
+	return json({ ok: true })
+}) satisfies PageServerLoad
\ No newline at end of file
diff --git a/src/routes/verify/+page.server.ts b/src/routes/verify/+page.server.ts
index 3321164..bd3ea7f 100644
--- a/src/routes/verify/+page.server.ts
+++ b/src/routes/verify/+page.server.ts
@@ -1,75 +1,75 @@
 import { fetchApi } from '$lib/api'
 import type { AccountSchema, CleanUser, SessionSchema } from '$lib/APITypes'
-import type { RequestHandler } from '@sveltejs/kit'
+import { redirect, type RequestHandler, type ServerLoad } from '@sveltejs/kit'
 import env from '$lib/env'
+import type { Actions } from './$types'
 
 
-function redirect(status: number, location: string) {
-	return {
-		status,
-		headers: {
-			location,
-		},
-	}
-}
+export const actions: Actions = {
+	default: async ({ platform, locals, request }) => {
+		const key = env(platform).SKYBLOCK_STATS_API_KEY
+		if (!key) {
+			throw redirect(303, `/verify?error=NO_KEY`)
+		}
+		if (locals.sid === undefined) {
+			throw redirect(303, '/login')
+		}
 
-export const post: RequestHandler = async ({ request, locals, platform }) => {
-	const key = env(platform).SKYBLOCK_STATS_API_KEY
-	if (!key) {
-		return redirect(303, `/verify?error=NO_KEY`)
-	}
-	if (locals.sid === undefined) {
-		return redirect(303, '/login')
-	}
+		const form = await request.formData()
 
-	const form = await request.formData()
+		// username or uuid
+		const playerIdentifier = form.get('ign')
+		if (!playerIdentifier) {
+			throw redirect(303, `/verify?error=NO_IGN`)
+		}
 
-	// username or uuid
-	const playerIdentifier = form.get('ign')
-	if (!playerIdentifier) {
-		return redirect(303, `/verify?error=NO_IGN`)
-	}
+		const playerResponse: CleanUser = await fetchApi(`player/${playerIdentifier}`, fetch).then(res => res.json())
+		const sessionResponse: { session: SessionSchema | null, account: AccountSchema | null } = await fetchApi(`accounts/session`, fetch, {
+			method: 'POST',
+			headers: {
+				'Content-Type': 'application/json',
+			},
+			body: JSON.stringify({
+				uuid: locals.sid,
+			}),
+		}).then(r => r.json())
 
-	const playerResponse: CleanUser = await fetchApi(`player/${playerIdentifier}`, fetch).then(res => res.json())
-	const sessionResponse: { session: SessionSchema | null, account: AccountSchema | null } = await fetchApi(`accounts/session`, fetch, {
-		method: 'POST',
-		headers: {
-			'Content-Type': 'application/json',
-		},
-		body: JSON.stringify({
-			uuid: locals.sid,
-		}),
-	}).then(r => r.json())
+		if (!sessionResponse.session)
+			throw redirect(303, '/login')
 
-	if (!sessionResponse.session)
-		return redirect(303, '/login')
+		const hypixelDiscordName = playerResponse.player?.socials.discord
 
-	const hypixelDiscordName = playerResponse.player?.socials.discord
+		if (!hypixelDiscordName)
+			throw redirect(303, `/verify?error=NOT_LINKED`)
 
-	if (!hypixelDiscordName)
-		return redirect(303, `/verify?error=NOT_LINKED`)
+		const discordUser = sessionResponse.session.discord_user
+		const actualDiscordName = discordUser.name
+		// some people link themselves as <id>#<discrim> instead of <name>#<discrim>
+		const actualDiscordIdDiscrim = `${discordUser.id}#${discordUser.name.split('#')[1]}`
 
-	const discordUser = sessionResponse.session.discord_user
-	const actualDiscordName = discordUser.name
-	// some people link themselves as <id>#<discrim> instead of <name>#<discrim>
-	const actualDiscordIdDiscrim = `${discordUser.id}#${discordUser.name.split('#')[1]}`
+		if (!(hypixelDiscordName === actualDiscordName || hypixelDiscordName === actualDiscordIdDiscrim))
+			throw redirect(303, `/verify?error=WRONG_NAME&current=${encodeURIComponent(hypixelDiscordName)}&correct=${encodeURIComponent(actualDiscordName)}`)
 
-	if (!(hypixelDiscordName === actualDiscordName || hypixelDiscordName === actualDiscordIdDiscrim))
-		return redirect(303, `/verify?error=WRONG_NAME&current=${encodeURIComponent(hypixelDiscordName)}&correct=${encodeURIComponent(actualDiscordName)}`)
+		const updatedAccount: AccountSchema = {
+			discordId: sessionResponse.session.discord_user.id,
+			minecraftUuid: playerResponse.player?.uuid
+		}
 
-	const updatedAccount: AccountSchema = {
-		discordId: sessionResponse.session.discord_user.id,
-		minecraftUuid: playerResponse.player?.uuid
-	}
+		await fetchApi(`accounts/update`, fetch, {
+			method: 'POST',
+			headers: {
+				'Content-Type': 'application/json',
+				key: key
+			},
+			body: JSON.stringify(updatedAccount),
+		}).then(r => r.json())
 
-	await fetchApi(`accounts/update`, fetch, {
-		method: 'POST',
-		headers: {
-			'Content-Type': 'application/json',
-			key: key
-		},
-		body: JSON.stringify(updatedAccount),
-	}).then(r => r.json())
+		throw redirect(303, '/profile')
+	}
+}
 
-	return redirect(303, '/profile')
-}
\ No newline at end of file
+export const load = (({ locals }) => {
+	return {
+		sid: locals.sid
+	}
+}) satisfies ServerLoad
diff --git a/src/routes/verify/+page.svelte b/src/routes/verify/+page.svelte
index 20de403..4edc50d 100644
--- a/src/routes/verify/+page.svelte
+++ b/src/routes/verify/+page.svelte
@@ -1,27 +1,14 @@
-<script lang="ts" context="module">
-	import type { Load } from '@sveltejs/kit'
-	export const load: Load = async ({ session, url }) => {
-		if (session.sid === undefined) {
-			return { redirect: '/login', status: 303 }
-		}
-		return {
-			props: {
-				errorCode: url.searchParams.get('error'),
-				current: url.searchParams.get('current'),
-				correct: url.searchParams.get('correct'),
-			},
-		}
-	}
-</script>
-
 <script lang="ts">
 	import Emoji from '$lib/Emoji.svelte'
 	import Head from '$lib/Head.svelte'
 	import Header from '$lib/Header.svelte'
+	import type { PageData } from './$types'
+
+	export let data: PageData
 
-	export let errorCode: string | null
-	export let current: string | null
-	export let correct: string | null
+	export let errorCode: string | null = data.errorCode
+	export let current: string | null = data.current
+	export let correct: string | null = data.correct
 
 	const errorCodes = {
 		NO_IGN: 'Please enter a valid Minecraft username.',
diff --git a/src/routes/verify/+page.ts b/src/routes/verify/+page.ts
new file mode 100644
index 0000000..8e5f698
--- /dev/null
+++ b/src/routes/verify/+page.ts
@@ -0,0 +1,13 @@
+import { redirect } from '@sveltejs/kit'
+import type { PageLoad } from './$types'
+
+export const load = (async ({ data, url }) => {
+	if (data.sid === undefined) {
+		throw redirect(303, '/login')
+	}
+	return {
+		errorCode: url.searchParams.get('error'),
+		current: url.searchParams.get('current'),
+		correct: url.searchParams.get('correct'),
+	}
+}) satisfies PageLoad
-- 
cgit 

