mirror of
https://github.com/BradNut/AdelieStack
synced 2025-09-08 17:40:20 +00:00
Fixing the login page
This commit is contained in:
parent
2c6a22d686
commit
42d7627783
9 changed files with 63 additions and 50 deletions
|
|
@ -22,7 +22,7 @@ const apiClient: Handle = async ({ event, resolve }) => {
|
||||||
/* ----------------------------- Auth functions ----------------------------- */
|
/* ----------------------------- Auth functions ----------------------------- */
|
||||||
async function getAuthedUser() {
|
async function getAuthedUser() {
|
||||||
const { data } = await api.users.me.$get().then(parseApiResponse);
|
const { data } = await api.users.me.$get().then(parseApiResponse);
|
||||||
return { user: data?.user, session: data?.session };
|
return data?.user;
|
||||||
}
|
}
|
||||||
|
|
||||||
async function getAuthedUserOrThrow() {
|
async function getAuthedUserOrThrow() {
|
||||||
|
|
|
||||||
12
src/lib/dtos/signin.dto.ts
Normal file
12
src/lib/dtos/signin.dto.ts
Normal 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>;
|
||||||
12
src/lib/server/api/dtos/signin.dto.ts
Normal file
12
src/lib/server/api/dtos/signin.dto.ts
Normal 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>;
|
||||||
|
|
@ -25,6 +25,7 @@ export class UsersController extends Controller {
|
||||||
.get('/me', async (c) => {
|
.get('/me', async (c) => {
|
||||||
const session = c.var.session;
|
const session = c.var.session;
|
||||||
const user = session ? await this.usersRepository.findOneByIdOrThrow(session.userId) : null;
|
const user = session ? await this.usersRepository.findOneByIdOrThrow(session.userId) : null;
|
||||||
|
c.var.logger.info(`Get user: ${JSON.stringify(user)}`);
|
||||||
return c.json(user);
|
return c.json(user);
|
||||||
})
|
})
|
||||||
.patch('/me', authState('session'), zValidator('form', updateUserDto), async (c) => {
|
.patch('/me', authState('session'), zValidator('form', updateUserDto), async (c) => {
|
||||||
|
|
|
||||||
|
|
@ -11,6 +11,7 @@
|
||||||
import HouseIcon from 'lucide-svelte/icons/house';
|
import HouseIcon from 'lucide-svelte/icons/house';
|
||||||
import { page } from '$app/state';
|
import { page } from '$app/state';
|
||||||
import { enhance } from '$app/forms';
|
import { enhance } from '$app/forms';
|
||||||
|
import ThemeDropdown from '@/components/theme-dropdown.svelte';
|
||||||
|
|
||||||
const { children, data } = $props();
|
const { children, data } = $props();
|
||||||
|
|
||||||
|
|
@ -86,10 +87,11 @@
|
||||||
/>
|
/>
|
||||||
</div>
|
</div>
|
||||||
</form>
|
</form>
|
||||||
|
<ThemeDropdown />
|
||||||
{#if data.authedUser}
|
{#if data.authedUser}
|
||||||
{@render userDropdown()}
|
{@render userDropdown()}
|
||||||
{:else}
|
{:else}
|
||||||
<Button href="/register">Login</Button>
|
<Button href="/login">Login</Button>
|
||||||
{/if}
|
{/if}
|
||||||
</div>
|
</div>
|
||||||
</header>
|
</header>
|
||||||
|
|
|
||||||
|
|
@ -2,14 +2,12 @@ import { StatusCodes } from "$lib/constants/status-codes";
|
||||||
import { redirect } from "@sveltejs/kit";
|
import { redirect } from "@sveltejs/kit";
|
||||||
|
|
||||||
export const load = async ({ locals }) => {
|
export const load = async ({ locals }) => {
|
||||||
const user = await locals.getAuthedUser();
|
|
||||||
return { user: user };
|
|
||||||
};
|
};
|
||||||
|
|
||||||
export const actions = {
|
export const actions = {
|
||||||
logout: async ({ locals }) => {
|
logout: async ({ locals }) => {
|
||||||
await locals.api.iam.logout.$post()
|
await locals.api.iam.logout.$post()
|
||||||
redirect(StatusCodes.SEE_OTHER, '/register')
|
redirect(StatusCodes.SEE_OTHER, '/')
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -2,5 +2,5 @@
|
||||||
let { data } = $props();
|
let { data } = $props();
|
||||||
</script>
|
</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>
|
<p>Visit <a href="https://kit.svelte.dev">kit.svelte.dev</a> to read the documentation</p>
|
||||||
|
|
|
||||||
|
|
@ -1,23 +1,22 @@
|
||||||
import { signinUsernameDto } from '$lib/dtos/signin-username.dto';
|
|
||||||
import { StatusCodes } from '$lib/utils/status-codes';
|
import { StatusCodes } from '$lib/utils/status-codes';
|
||||||
import { type Actions, fail } from '@sveltejs/kit';
|
import { type Actions, fail } from '@sveltejs/kit';
|
||||||
import { redirect } from 'sveltekit-flash-message/server';
|
import { redirect } from 'sveltekit-flash-message/server';
|
||||||
import { zod } from 'sveltekit-superforms/adapters';
|
import { zod } from 'sveltekit-superforms/adapters';
|
||||||
import { setError, superValidate } from 'sveltekit-superforms/server';
|
import { setError, superValidate } from 'sveltekit-superforms/server';
|
||||||
import type { PageServerLoad } from './$types';
|
import type { PageServerLoad } from './$types';
|
||||||
|
import { signinDto } from '@/server/api/dtos/signin.dto';
|
||||||
|
|
||||||
export const load: PageServerLoad = async (event) => {
|
export const load: PageServerLoad = async (event) => {
|
||||||
const { locals } = event;
|
const { locals, parent } = event;
|
||||||
|
const { authedUser } = await parent();
|
||||||
|
|
||||||
const { user } = await locals.getAuthedUser();
|
if (authedUser) {
|
||||||
|
|
||||||
if (user) {
|
|
||||||
console.log('user already signed in');
|
console.log('user already signed in');
|
||||||
const message = { type: 'success', message: 'You are already signed in' } as const;
|
const message = { type: 'success', message: 'You are already signed in' } as const;
|
||||||
throw redirect('/', message, event);
|
throw redirect('/', message, event);
|
||||||
// redirect(302, '/', message, event)
|
// redirect(302, '/', message, event)
|
||||||
}
|
}
|
||||||
const form = await superValidate(event, zod(signinUsernameDto));
|
const form = await superValidate(event, zod(signinDto));
|
||||||
|
|
||||||
return {
|
return {
|
||||||
form,
|
form,
|
||||||
|
|
@ -35,7 +34,7 @@ export const actions: Actions = {
|
||||||
throw redirect('/', message, event);
|
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);
|
const { error } = await locals.api.login.$post({ json: form.data }).then(locals.parseApiResponse);
|
||||||
console.log('Login error', error);
|
console.log('Login error', error);
|
||||||
|
|
|
||||||
|
|
@ -1,40 +1,25 @@
|
||||||
<script lang="ts">
|
<script lang="ts">
|
||||||
import { Button } from "$components/ui/button";
|
import { Button } from '$lib/components/ui/button';
|
||||||
import * as Card from "$components/ui/card";
|
import * as Card from '$lib/components/ui/card';
|
||||||
import * as Form from "$components/ui/form";
|
import { Input } from '$lib/components/ui/input';
|
||||||
import { Input } from "$components/ui/input";
|
import * as Form from '$lib/components/ui/form';
|
||||||
import { boredState } from "$lib/stores/boredState.js";
|
|
||||||
import { receive, send } from "$lib/utils/pageCrossfade";
|
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 { superForm } from "sveltekit-superforms/client";
|
||||||
|
import { signinDto } from '@/dtos/signin.dto.js';
|
||||||
|
|
||||||
let { data } = $props();
|
let { data } = $props();
|
||||||
|
|
||||||
const superLoginForm = superForm(data.form, {
|
const sf_login_password = superForm(data.form, {
|
||||||
onSubmit: () => boredState.update((n) => ({ ...n, loading: true })),
|
validators: zodClient(signinDto),
|
||||||
onResult: () => boredState.update((n) => ({ ...n, loading: false })),
|
resetForm: 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 { form: loginForm, enhance } = superLoginForm;
|
const { form: loginForm, enhance: loginEnhance, errors: loginErrors } = sf_login_password;
|
||||||
</script>
|
</script>
|
||||||
|
|
||||||
<svelte:head>
|
<svelte:head>
|
||||||
<title>Bored Game | Login</title>
|
<title>Acme | Login</title>
|
||||||
</svelte:head>
|
</svelte:head>
|
||||||
|
|
||||||
<div in:receive={{ key: "auth-card" }} out:send={{ key: "auth-card" }}>
|
<div in:receive={{ key: "auth-card" }} out:send={{ key: "auth-card" }}>
|
||||||
|
|
@ -44,8 +29,8 @@
|
||||||
</Card.Header>
|
</Card.Header>
|
||||||
<Card.Content class="grid gap-4">
|
<Card.Content class="grid gap-4">
|
||||||
{@render usernamePasswordForm()}
|
{@render usernamePasswordForm()}
|
||||||
<span class="text-center text-sm text-muted-foreground">or sign in with</span>
|
<!-- <span class="text-center text-sm text-muted-foreground">or sign in with</span> -->
|
||||||
{@render oAuthButtons()}
|
<!-- {@render oAuthButtons()} -->
|
||||||
<p class="px-8 py-4 text-center text-sm text-muted-foreground">
|
<p class="px-8 py-4 text-center text-sm text-muted-foreground">
|
||||||
By clicking continue, you agree to our
|
By clicking continue, you agree to our
|
||||||
<a href="/terms" class="underline underline-offset-4 hover:text-primary"> Terms of Use </a>
|
<a href="/terms" class="underline underline-offset-4 hover:text-primary"> Terms of Use </a>
|
||||||
|
|
@ -57,18 +42,22 @@
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
{#snippet usernamePasswordForm()}
|
{#snippet usernamePasswordForm()}
|
||||||
<form method="POST" use:enhance>
|
<form method="POST" use:loginEnhance>
|
||||||
<Form.Field form={superLoginForm} name="username">
|
<Form.Field form={sf_login_password} name="username">
|
||||||
<Form.Control let:attrs>
|
<Form.Control>
|
||||||
<Form.Label for="username">Username</Form.Label>
|
{#snippet children({ props })}
|
||||||
<Input {...attrs} autocomplete="username" bind:value={$loginForm.username} />
|
<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.Control>
|
||||||
<Form.FieldErrors />
|
<Form.FieldErrors />
|
||||||
</Form.Field>
|
</Form.Field>
|
||||||
<Form.Field form={superLoginForm} name="password">
|
<Form.Field form={sf_login_password} name="password">
|
||||||
<Form.Control let:attrs>
|
<Form.Control>
|
||||||
|
{#snippet children({ props })}
|
||||||
<Form.Label for="password">Password</Form.Label>
|
<Form.Label for="password">Password</Form.Label>
|
||||||
<Input {...attrs} autocomplete="current-password" type="password" bind:value={$loginForm.password} />
|
<Input {...props} autocomplete="current-password" placeholder={"••••••••"} type="password" bind:value={$loginForm.password} />
|
||||||
|
{/snippet}
|
||||||
</Form.Control>
|
</Form.Control>
|
||||||
<Form.FieldErrors />
|
<Form.FieldErrors />
|
||||||
</Form.Field>
|
</Form.Field>
|
||||||
|
|
|
||||||
Loading…
Reference in a new issue