Skip to content

Commit

Permalink
feat: add signup page (#1)
Browse files Browse the repository at this point in the history
* feat: add signup page

* refactor: rename to schemas
  • Loading branch information
braaar authored Dec 2, 2023
1 parent 22cc0df commit 0b2d8c0
Show file tree
Hide file tree
Showing 8 changed files with 185 additions and 2 deletions.
5 changes: 4 additions & 1 deletion package.json
Original file line number Diff line number Diff line change
Expand Up @@ -29,5 +29,8 @@
"vite": "^4.4.2",
"vitest": "^0.34.0"
},
"type": "module"
"type": "module",
"dependencies": {
"zod": "^3.22.4"
}
}
9 changes: 9 additions & 0 deletions pnpm-lock.yaml

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

1 change: 0 additions & 1 deletion src/lib/index.ts

This file was deleted.

10 changes: 10 additions & 0 deletions src/lib/schemas.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,10 @@
// place files you want to import through the `$lib` alias in this folder.

import { z } from 'zod';

export const userSchema = z.object({
id: z.number(),
username: z.string(),
email: z.string().email(),
matrixId: z.string().nullable()
});
30 changes: 30 additions & 0 deletions src/routes/+page.server.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,30 @@
import type { PageServerLoad } from './$types';
import { STRAPI_URL } from '$env/static/private';
import { userSchema } from '$lib/schemas';

export const load: PageServerLoad = async ({ cookies }) => {
const jwt = cookies.get('jwt');

if (jwt) {
const url = new URL(STRAPI_URL);
const endpoint = 'api/users/me';
const route = new URL(endpoint, url);

const response = await fetch(route.toString(), {
headers: { Authorization: `Bearer ${jwt}` },
method: 'GET'
});

const body = await response.json();

const user = userSchema.parse(body);
return {
loggedIn: true,
username: user.username
};
}

return {
loggedIn: false
};
};
16 changes: 16 additions & 0 deletions src/routes/+page.svelte
Original file line number Diff line number Diff line change
@@ -1,2 +1,18 @@
<script lang="ts">
import type { PageData } from './$types';
export let data: PageData;
</script>

<h1>Welcome to SvelteKit</h1>
<p>Visit <a href="https://kit.svelte.dev">kit.svelte.dev</a> to read the documentation</p>

{#if data.loggedIn}
<p>You are logged in as {data?.username}</p>
<a href="/logout">Log Out</a>
{:else}
<p>You are not logged in</p>
<a href="/login">Log In</a>
<a href="/signup">Sign Up</a>
{/if}
```
91 changes: 91 additions & 0 deletions src/routes/signup/+page.server.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,91 @@
import type { PageServerLoad } from './$types';
import { HACKLAB_NAME, STRAPI_URL } from '$env/static/private';
import { redirect, type Actions, fail } from '@sveltejs/kit';
import { z } from 'zod';
import { userSchema } from '$lib/schemas';

export const load: PageServerLoad = () => {
return {
hacklabName: HACKLAB_NAME
};
};

const signupFormSchema = z
.object({
username: z.string().min(3),
email: z.string().email(),
password: z.string().min(8),
passwordRepeat: z.string().min(8)
})
.refine((data) => data.password === data.passwordRepeat, {
message: "Passwords don't match",
path: ['passwordRepeat']
});

const signupResponseSchema = z.object({
jwt: z.string(),
user: userSchema
});

export const actions: Actions = {
default: async ({ request, cookies }) => {
const formData = await request.formData();

const username = formData.get('username');
const email = formData.get('email');
const password = formData.get('password');
const passwordRepeat = formData.get('passwordRepeat');

const parsedData = signupFormSchema.safeParse({ username, email, password, passwordRepeat });

if (!parsedData.success) {
const errors = {
username: '',
email: '',
password: ''
};

parsedData.error.errors.forEach((error) => {
if (error.path[0] === 'username') {
errors.username = error.message;
}
if (error.path[0] === 'email') {
errors.email = error.message;
}
if (error.path[0] === 'password') {
errors.password = error.message;
}
});
return fail(400, { error: true, errors });
}

const url = new URL(STRAPI_URL);

const endpoint = 'api/auth/local/register';
const route = new URL(endpoint, url);

const response = await fetch(route.toString(), {
method: 'POST',
headers: {
'Content-Type': 'application/json'
},
body: JSON.stringify({
username: parsedData.data.username,
email: parsedData.data.email,
password: parsedData.data.password
})
});

if (!response.ok) {
const body = await response.json();
return fail(400, { error: true, errors: body.data });
}

const body = await response.json();

const signupData = signupResponseSchema.parse(body);

cookies.set('jwt', signupData.jwt);
throw redirect(302, '/');
}
};
25 changes: 25 additions & 0 deletions src/routes/signup/+page.svelte
Original file line number Diff line number Diff line change
@@ -0,0 +1,25 @@
<script lang="ts">
import { page } from '$app/stores';
import type { ActionData, PageData } from './$types';
export let data: PageData;
export let form: ActionData;
</script>

<h1>Become a member of {data.hacklabName}!</h1>
<form method="post">
{#if $page.status === 400 && form?.errors?.username}
<p>{form.errors.username}</p>
{/if}
<input type="text" placeholder="Username" name="username" />
{#if $page.status === 400 && form?.errors?.email}
<p>{form.errors.email}</p>
{/if}
<input type="email" placeholder="Email" name="email" />
{#if $page.status === 400 && form?.errors?.password}
<p>{form.errors.password}</p>
{/if}
<input type="password" placeholder="Password" name="password" />
<input type="password" placeholder="Confirm Password" name="passwordRepeat" />
<button type="submit">Sign Up</button>
</form>

0 comments on commit 0b2d8c0

Please sign in to comment.