Fixing the login page

This commit is contained in:
Bradley Shellnut 2025-01-01 21:51:11 -08:00
parent 2c6a22d686
commit 42d7627783
9 changed files with 63 additions and 50 deletions

View file

@ -22,7 +22,7 @@ const apiClient: Handle = async ({ event, resolve }) => {
/* ----------------------------- Auth functions ----------------------------- */
async function getAuthedUser() {
const { data } = await api.users.me.$get().then(parseApiResponse);
return { user: data?.user, session: data?.session };
return data?.user;
}
async function getAuthedUserOrThrow() {

View file

@ -0,0 +1,12 @@
import {z} from "zod";
export const signinDto = z.object({
username: z
.string()
.trim()
.min(3, { message: 'Must be at least 3 characters' })
.max(50, { message: 'Must be less than 50 characters' }),
password: z.string({ required_error: 'Password is required' }).trim(),
});
export type signinDto = z.infer<typeof signinDto>;

View file

@ -0,0 +1,12 @@
import {z} from "zod";
export const signinDto = z.object({
username: z
.string()
.trim()
.min(3, { message: 'Must be at least 3 characters' })
.max(50, { message: 'Must be less than 50 characters' }),
password: z.string({ required_error: 'Password is required' }).trim(),
});
export type signinDto = z.infer<typeof signinDto>;

View file

@ -25,6 +25,7 @@ export class UsersController extends Controller {
.get('/me', async (c) => {
const session = c.var.session;
const user = session ? await this.usersRepository.findOneByIdOrThrow(session.userId) : null;
c.var.logger.info(`Get user: ${JSON.stringify(user)}`);
return c.json(user);
})
.patch('/me', authState('session'), zValidator('form', updateUserDto), async (c) => {

View file

@ -11,6 +11,7 @@
import HouseIcon from 'lucide-svelte/icons/house';
import { page } from '$app/state';
import { enhance } from '$app/forms';
import ThemeDropdown from '@/components/theme-dropdown.svelte';
const { children, data } = $props();
@ -86,10 +87,11 @@
/>
</div>
</form>
<ThemeDropdown />
{#if data.authedUser}
{@render userDropdown()}
{:else}
<Button href="/register">Login</Button>
<Button href="/login">Login</Button>
{/if}
</div>
</header>

View file

@ -2,14 +2,12 @@ import { StatusCodes } from "$lib/constants/status-codes";
import { redirect } from "@sveltejs/kit";
export const load = async ({ locals }) => {
const user = await locals.getAuthedUser();
return { user: user };
};
export const actions = {
logout: async ({ locals }) => {
await locals.api.iam.logout.$post()
redirect(StatusCodes.SEE_OTHER, '/register')
redirect(StatusCodes.SEE_OTHER, '/')
}
}

View file

@ -2,5 +2,5 @@
let { data } = $props();
</script>
<h1>Welcome {data?.user?.email}</h1>
<h1>Welcome {data?.authedUser?.username}</h1>
<p>Visit <a href="https://kit.svelte.dev">kit.svelte.dev</a> to read the documentation</p>

View file

@ -1,23 +1,22 @@
import { signinUsernameDto } from '$lib/dtos/signin-username.dto';
import { StatusCodes } from '$lib/utils/status-codes';
import { type Actions, fail } from '@sveltejs/kit';
import { redirect } from 'sveltekit-flash-message/server';
import { zod } from 'sveltekit-superforms/adapters';
import { setError, superValidate } from 'sveltekit-superforms/server';
import type { PageServerLoad } from './$types';
import { signinDto } from '@/server/api/dtos/signin.dto';
export const load: PageServerLoad = async (event) => {
const { locals } = event;
const { locals, parent } = event;
const { authedUser } = await parent();
const { user } = await locals.getAuthedUser();
if (user) {
if (authedUser) {
console.log('user already signed in');
const message = { type: 'success', message: 'You are already signed in' } as const;
throw redirect('/', message, event);
// redirect(302, '/', message, event)
}
const form = await superValidate(event, zod(signinUsernameDto));
const form = await superValidate(event, zod(signinDto));
return {
form,
@ -35,7 +34,7 @@ export const actions: Actions = {
throw redirect('/', message, event);
}
const form = await superValidate(event, zod(signinUsernameDto));
const form = await superValidate(event, zod(signinDto));
const { error } = await locals.api.login.$post({ json: form.data }).then(locals.parseApiResponse);
console.log('Login error', error);

View file

@ -1,40 +1,25 @@
<script lang="ts">
import { Button } from "$components/ui/button";
import * as Card from "$components/ui/card";
import * as Form from "$components/ui/form";
import { Input } from "$components/ui/input";
import { boredState } from "$lib/stores/boredState.js";
import { Button } from '$lib/components/ui/button';
import * as Card from '$lib/components/ui/card';
import { Input } from '$lib/components/ui/input';
import * as Form from '$lib/components/ui/form';
import { receive, send } from "$lib/utils/pageCrossfade";
import * as flashModule from "sveltekit-flash-message/client";
import { zodClient } from "sveltekit-superforms/adapters";
import { superForm } from "sveltekit-superforms/client";
import { signinDto } from '@/dtos/signin.dto.js';
let { data } = $props();
const superLoginForm = superForm(data.form, {
onSubmit: () => boredState.update((n) => ({ ...n, loading: true })),
onResult: () => boredState.update((n) => ({ ...n, loading: false })),
flashMessage: {
module: flashModule,
onError: ({ result, flashMessage }) => {
// Error handling for the flash message:
// - result is the ActionResult
// - message is the flash store (not the status message store)
const errorMessage = result.error.message;
flashMessage.set({ type: "error", message: errorMessage });
},
},
syncFlashMessage: false,
taintedMessage: null,
// validators: zodClient(signInSchema),
// validationMethod: 'oninput',
delayMs: 0,
const sf_login_password = superForm(data.form, {
validators: zodClient(signinDto),
resetForm: false
});
const { form: loginForm, enhance } = superLoginForm;
const { form: loginForm, enhance: loginEnhance, errors: loginErrors } = sf_login_password;
</script>
<svelte:head>
<title>Bored Game | Login</title>
<title>Acme | Login</title>
</svelte:head>
<div in:receive={{ key: "auth-card" }} out:send={{ key: "auth-card" }}>
@ -44,8 +29,8 @@
</Card.Header>
<Card.Content class="grid gap-4">
{@render usernamePasswordForm()}
<span class="text-center text-sm text-muted-foreground">or sign in with</span>
{@render oAuthButtons()}
<!-- <span class="text-center text-sm text-muted-foreground">or sign in with</span> -->
<!-- {@render oAuthButtons()} -->
<p class="px-8 py-4 text-center text-sm text-muted-foreground">
By clicking continue, you agree to our
<a href="/terms" class="underline underline-offset-4 hover:text-primary"> Terms of Use </a>
@ -57,18 +42,22 @@
</div>
{#snippet usernamePasswordForm()}
<form method="POST" use:enhance>
<Form.Field form={superLoginForm} name="username">
<Form.Control let:attrs>
<Form.Label for="username">Username</Form.Label>
<Input {...attrs} autocomplete="username" bind:value={$loginForm.username} />
<form method="POST" use:loginEnhance>
<Form.Field form={sf_login_password} name="username">
<Form.Control>
{#snippet children({ props })}
<Form.Label for="username">Username / Email</Form.Label>
<Input {...props} autocomplete="username" placeholder="john.doe@example.com" bind:value={$loginForm.username} />
{/snippet}
</Form.Control>
<Form.FieldErrors />
</Form.Field>
<Form.Field form={superLoginForm} name="password">
<Form.Control let:attrs>
<Form.Label for="password">Password</Form.Label>
<Input {...attrs} autocomplete="current-password" type="password" bind:value={$loginForm.password} />
<Form.Field form={sf_login_password} name="password">
<Form.Control>
{#snippet children({ props })}
<Form.Label for="password">Password</Form.Label>
<Input {...props} autocomplete="current-password" placeholder={"••••••••"} type="password" bind:value={$loginForm.password} />
{/snippet}
</Form.Control>
<Form.FieldErrors />
</Form.Field>