WIP: Working homepage with Login and logout

This commit is contained in:
Pablu23
2025-10-08 12:31:16 +02:00
parent d9a946d9ca
commit 1f3813ea78
6 changed files with 191 additions and 134 deletions

4
src/app.d.ts vendored
View File

@@ -46,7 +46,3 @@ export type WebSocketMessage = {
type: string; type: string;
[key: string]: any; [key: string]: any;
} }
export {
};

View File

@@ -1,40 +1,40 @@
import { db } from '$lib/server/db'; import { db } from '$lib/server/db';
import { sessionsTable, usersTable } from '$lib/server/db/schema'; import { sessionsTable } from '$lib/server/db/schema';
import { eq } from 'drizzle-orm'; import { eq } from 'drizzle-orm';
import { redirect, type Handle } from '@sveltejs/kit'; import { redirect, type Handle } from '@sveltejs/kit';
export const handle: Handle = async ({ event, resolve }) => { export const handle: Handle = async ({ event, resolve }) => {
const sessionId = event.cookies.get('session_id'); const sessionId = event.cookies.get('session_id');
let user = { let user = {
isLoggedIn: false, isLoggedIn: false,
email: "", email: "",
username: "" username: ""
}; };
if (sessionId) { if (sessionId) {
const session = await db.query.sessionsTable.findFirst({ const session = await db.query.sessionsTable.findFirst({
with: { with: {
user: true user: true
}, },
where: eq(sessionsTable.id, sessionId) where: eq(sessionsTable.id, sessionId)
}); });
if (session && session.user && session.user.email) { if (session && session.user && session.user.email) {
user = { user = {
isLoggedIn: true, isLoggedIn: true,
email: session.user.email, email: session.user.email,
username: session.user.username || "Unknown username" username: session.user.username || "Unknown username"
}; };
}
}
if (event.url.pathname.startsWith("/private") && !user.isLoggedIn) {
redirect(307, "/error");
} else if (event.url.pathname.startsWith("/api") && !user.isLoggedIn) {
return new Response(null, { status: 401 });
} }
}
event.locals.user = user; if (event.url.pathname.startsWith("/private") && !user.isLoggedIn) {
const response = await resolve(event); redirect(307, "/error");
return response; } else if (event.url.pathname.startsWith("/api") && !user.isLoggedIn) {
} return new Response(null, { status: 401 });
}
event.locals.user = user;
const response = await resolve(event);
return response;
}

View File

@@ -1,6 +1,6 @@
<script lang="ts"> <script lang="ts">
import favicon from '$lib/assets/favicon.svg'; import favicon from '$lib/assets/favicon.svg';
import "../app.css"; import '../app.css';
let { children } = $props(); let { children } = $props();
</script> </script>
@@ -9,3 +9,12 @@
</svelte:head> </svelte:head>
{@render children?.()} {@render children?.()}
<!-- Footer -->
<footer class="bg-white py-6">
<div class="max-w-5xl mx-auto px-4 sm:px-6 lg:px-8">
<p class="text-center text-gray-500 text-sm">
© 2025 Pablu. All rights reserved.
</p>
</div>
</footer>

View File

@@ -1,46 +1,8 @@
import { db } from "$lib/server/db";
import { sessionsTable, usersTable } from "$lib/server/db/schema";
import { eq } from "drizzle-orm";
import { redirect, type Actions } from "@sveltejs/kit";
import type { PageServerLoad } from "./$types"; import type { PageServerLoad } from "./$types";
export const load: PageServerLoad = async ({ locals }) => { export const load: PageServerLoad = async ({ locals }) => {
let allUsers = null; return {
if (locals.user.isLoggedIn) { user: locals.user,
allUsers = await db.select().from(usersTable); }
}
return {
user: locals.user,
users: allUsers ?? []
}
}; };
export const actions = {
logout: async ({ locals, cookies }) => {
const sessionId = cookies.get('session_id');
if (!sessionId) {
redirect(307, "/error")
}
await db.delete(sessionsTable).where(eq(sessionsTable.id, sessionId))
cookies.delete('session_id', { path: "/" });
locals.user.isLoggedIn = false;
locals.user.email = null;
locals.user.username = null;
return { success: true };
},
deleteUsers: async ({ locals, fetch }) => {
await fetch("/api/deleteUsers");
locals.user.isLoggedIn = false;
locals.user.email = null;
locals.user.username = null;
return { success: true }
}
} satisfies Actions;

View File

@@ -2,64 +2,158 @@
import { goto } from '$app/navigation'; import { goto } from '$app/navigation';
import type { PageProps } from './$types'; import type { PageProps } from './$types';
let { data, form }: PageProps = $props(); let lobbyCode = $state('');
let isLoggingIn = $state(false);
let loginError = $state('');
let { data }: PageProps = $props();
let user = $state(data.user);
// Example login function (would connect to a real auth service)
function handleLogin() {
isLoggingIn = true;
loginError = '';
goto('/login');
}
function handleLogout() {
user.isLoggedIn = false;
user.username = null;
user.email = null;
goto("/logout");
}
function createLobby() {
goto('/lobby/create');
}
function joinLobby() {
if (lobbyCode.trim()) {
goto(`/lobby/${lobbyCode}`);
}
}
</script> </script>
<h1>Welcome to SvelteKit</h1> <div class="min-h-screen bg-white flex flex-col">
<p>Visit <a href="https://svelte.dev/docs/kit">svelte.dev/docs/kit</a> to read the documentation</p> <!-- Header -->
<header class="bg-white shadow-sm py-4">
<div class="max-w-5xl mx-auto px-4 sm:px-6 lg:px-8 flex justify-between items-center">
<h1 class="text-2xl font-bold text-indigo-600">Hitstar</h1>
<!-- <div> {#if user.isLoggedIn}
<label>Email: <input type="text" bind:value={email} /></label> <div class="flex items-center">
<label>Username: <input type="text" bind:value={username} /></label> <span class="mr-4 text-gray-700">Welcome, {user.username}</span>
</div> --> <button
class="px-4 py-2 rounded-md text-gray-600 hover:text-gray-900 hover:bg-gray-100 transition-colors"
onclick={handleLogout}
>
Log Out
</button>
</div>
{/if}
</div>
</header>
{#if form?.success} <!-- Main Content -->
<p>Successfully logged out</p> <main class="flex-1 flex flex-col items-center justify-center px-4">
{/if} <div class="w-full max-w-md">
<!-- Hero -->
<div class="text-center mb-12">
<h2 class="text-4xl font-bold text-gray-800 mb-4">Test Your Music Knowledge</h2>
<p class="text-xl text-gray-600">Place songs on a timeline in the correct order.</p>
</div>
{#if !data.user.isLoggedIn} {#if !user.isLoggedIn}
<button onclick={async () => await goto('/login')}> Login </button> <!-- Login Section -->
{:else} <div class="bg-white rounded-lg shadow-md p-8 mb-6">
<h2>Hello {data.user.username}</h2> <h3 class="text-xl font-semibold text-gray-800 mb-6">Sign In to Play</h3>
<form method="POST" action="?/logout">
<button type="submit">Logout</button>
</form>
<form method="POST" action="?/deleteUsers"> {#if loginError}
<button type="submit">Delete all Users</button> <div class="bg-red-50 text-red-700 p-3 rounded-md mb-4 text-sm">
</form> {loginError}
</div>
{/if}
<button type="submit" onclick={async () => await goto("/lobby/create")}>Create Lobby</button> <button
{/if} class="w-full py-3 px-4 bg-indigo-600 hover:bg-indigo-700 text-white font-medium rounded-md transition-colors flex justify-center items-center"
<!-- onclick={handleLogin}
<button disabled={isLoggingIn}
onclick={async () => { >
const response = await fetch('/api/createUser', { {#if isLoggingIn}
method: 'POST', <svg
body: JSON.stringify({ class="animate-spin -ml-1 mr-2 h-4 w-4 text-white"
email: email, xmlns="http://www.w3.org/2000/svg"
username: username fill="none"
}), viewBox="0 0 24 24"
headers: { >
'Content-Type': 'application/json' <circle
} class="opacity-25"
}); cx="12"
cy="12"
r="10"
stroke="currentColor"
stroke-width="4"
></circle>
<path
class="opacity-75"
fill="currentColor"
d="M4 12a8 8 0 018-8V0C5.373 0 0 5.373 0 12h4zm2 5.291A7.962 7.962 0 014 12H0c0 3.042 1.135 5.824 3 7.938l3-2.647z"
></path>
</svg>
Logging in...
{:else}
Login to Play
{/if}
</button>
</div>
{:else}
<!-- Game Options -->
<div class="bg-white rounded-lg shadow-md p-8 space-y-6">
<h3 class="text-xl font-semibold text-gray-800 mb-6">Ready to Play?</h3>
console.log(response.status); <button
class="w-full py-3 px-4 bg-indigo-600 hover:bg-indigo-700 text-white font-medium rounded-md transition-colors flex items-center justify-center"
onclick={createLobby}
>
<svg
xmlns="http://www.w3.org/2000/svg"
class="h-5 w-5 mr-2"
fill="none"
viewBox="0 0 24 24"
stroke="currentColor"
>
<path
stroke-linecap="round"
stroke-linejoin="round"
stroke-width="2"
d="M12 6v6m0 0v6m0-6h6m-6 0H6"
/>
</svg>
Create New Lobby
</button>
if (response.ok) { <div class="relative mt-4">
const newUser: Array<any> = await response.json(); <div class="flex rounded-md shadow-sm">
users.push(...newUser); <input
} else { type="text"
console.log(`Encountered Error ${response.status}`); bind:value={lobbyCode}
} placeholder="Enter lobby code"
}}>Create User</button class="flex-1 min-w-0 block w-full px-3 py-3 rounded-none rounded-l-md border border-gray-300 focus:outline-none focus:ring-indigo-500 focus:border-indigo-500"
> --> />
<button
<ul> class="inline-flex items-center px-4 py-2 border border-l-0 border-gray-300 rounded-r-md bg-indigo-600 hover:bg-indigo-700 text-white font-medium"
{#each data.users as user (user.email)} onclick={joinLobby}
<li> disabled={!lobbyCode.trim()}
{user.username} = {user.email} >
</li> Join Lobby
{/each} </button>
</ul> </div>
</div>
</div>
{/if}
</div>
</main>
</div>

View File

@@ -1,9 +1,5 @@
import { generateRandomString } from "$lib/server/auth/spotify";
import type { states } from "$lib/server/db/schema";
import { redirect } from "@sveltejs/kit"; import { redirect } from "@sveltejs/kit";
import type { PageServerLoad } from "./$types"; import type { PageServerLoad } from "./$types";
import { setContext } from "svelte";
export const load: PageServerLoad = async ({locals, fetch}) => { export const load: PageServerLoad = async ({locals, fetch}) => {
const response = await fetch("/api/createLobby", { const response = await fetch("/api/createLobby", {
@@ -19,4 +15,4 @@ export const load: PageServerLoad = async ({locals, fetch}) => {
const lobby = await response.json(); const lobby = await response.json();
redirect(307, `/lobby/${lobby.id}`); redirect(307, `/lobby/${lobby.id}`);
}; };