fix: handle duplicate email sign-up and null user in protected layout
- Register action now returns success instead of redirecting to /account/setup so users without a session (duplicate email with requireEmailVerification) see a 'check your email' message instead of crashing - Protected layout guards against null/undefined locals.user before querying Prisma (was passing id: undefined) - Updated register page to show success banner with verification instructions - Updated test to match new action response
This commit is contained in:
@@ -3,8 +3,12 @@ import { error, redirect } from '@sveltejs/kit';
|
|||||||
import { prisma } from '$lib/db';
|
import { prisma } from '$lib/db';
|
||||||
|
|
||||||
export const load: LayoutServerLoad = async ({ locals }) => {
|
export const load: LayoutServerLoad = async ({ locals }) => {
|
||||||
|
if (!locals.user?.id) {
|
||||||
|
throw error(403, 'not authorized');
|
||||||
|
}
|
||||||
|
|
||||||
const user = await prisma.user.findUnique({
|
const user = await prisma.user.findUnique({
|
||||||
where: { id: locals.user?.id },
|
where: { id: locals.user.id },
|
||||||
select: { id: true, roleId: true, profile: { select: { id: true } } }
|
select: { id: true, roleId: true, profile: { select: { id: true } } }
|
||||||
});
|
});
|
||||||
if (!user) {
|
if (!user) {
|
||||||
|
|||||||
@@ -1,6 +1,6 @@
|
|||||||
import type { Actions } from './$types';
|
import type { Actions } from './$types';
|
||||||
import { auth } from '$lib/auth';
|
import { auth } from '$lib/auth';
|
||||||
import { fail, isRedirect, redirect } from '@sveltejs/kit';
|
import { fail } from '@sveltejs/kit';
|
||||||
|
|
||||||
export const actions: Actions = {
|
export const actions: Actions = {
|
||||||
default: async ({ request }) => {
|
default: async ({ request }) => {
|
||||||
@@ -17,7 +17,7 @@ export const actions: Actions = {
|
|||||||
}
|
}
|
||||||
|
|
||||||
try {
|
try {
|
||||||
const response = await auth.api.signUpEmail({
|
await auth.api.signUpEmail({
|
||||||
body: {
|
body: {
|
||||||
name: `${firstName} ${lastName}`,
|
name: `${firstName} ${lastName}`,
|
||||||
email: email,
|
email: email,
|
||||||
@@ -25,11 +25,8 @@ export const actions: Actions = {
|
|||||||
}
|
}
|
||||||
});
|
});
|
||||||
|
|
||||||
if (response.user) {
|
return { success: true };
|
||||||
throw redirect(303, '/account/setup');
|
|
||||||
}
|
|
||||||
} catch (err) {
|
} catch (err) {
|
||||||
if (isRedirect(err)) throw err;
|
|
||||||
const message = err instanceof Error ? err.message : 'An unexpected error occurred';
|
const message = err instanceof Error ? err.message : 'An unexpected error occurred';
|
||||||
return fail(400, { error: message });
|
return fail(400, { error: message });
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -8,7 +8,17 @@
|
|||||||
|
|
||||||
<Window>
|
<Window>
|
||||||
<div class="flex flex-col items-start justify-center">
|
<div class="flex flex-col items-start justify-center">
|
||||||
{#if form?.error}
|
{#if form?.success}
|
||||||
|
<div
|
||||||
|
class="mb-4 w-full rounded-lg border border-green-200 bg-green-50 p-4 text-green-700"
|
||||||
|
>
|
||||||
|
<p class="font-medium">Account created!</p>
|
||||||
|
<p class="mt-1 text-sm">
|
||||||
|
We've sent a verification email. Check your inbox and click the link to verify your
|
||||||
|
email address, then log in.
|
||||||
|
</p>
|
||||||
|
</div>
|
||||||
|
{:else if form?.error}
|
||||||
<div class="mb-4 w-full rounded-lg border border-red-200 bg-red-50 p-3 text-red-700">
|
<div class="mb-4 w-full rounded-lg border border-red-200 bg-red-50 p-3 text-red-700">
|
||||||
{form.error}
|
{form.error}
|
||||||
</div>
|
</div>
|
||||||
|
|||||||
@@ -41,7 +41,7 @@ describe('register page server action', () => {
|
|||||||
expect(signUpEmail).not.toHaveBeenCalled();
|
expect(signUpEmail).not.toHaveBeenCalled();
|
||||||
});
|
});
|
||||||
|
|
||||||
it('redirects to /account/setup on successful signup', async () => {
|
it('returns success on successful signup', async () => {
|
||||||
signUpEmail.mockResolvedValue({ user: { id: 'user-1' } });
|
signUpEmail.mockResolvedValue({ user: { id: 'user-1' } });
|
||||||
|
|
||||||
const request = createFormRequest({
|
const request = createFormRequest({
|
||||||
@@ -52,9 +52,8 @@ describe('register page server action', () => {
|
|||||||
password2: 'secret123'
|
password2: 'secret123'
|
||||||
});
|
});
|
||||||
|
|
||||||
await expect(actions.default({ request } as any)).rejects.toMatchObject({
|
const result = await invokeDefault(request);
|
||||||
status: 303
|
expect(result).toEqual({ success: true });
|
||||||
});
|
|
||||||
});
|
});
|
||||||
|
|
||||||
it('returns fail(400) when signUpEmail throws', async () => {
|
it('returns fail(400) when signUpEmail throws', async () => {
|
||||||
|
|||||||
Reference in New Issue
Block a user