chore: no magic strings #34
4 changed files with 50 additions and 24 deletions
|
|
@ -1,11 +1,17 @@
|
||||||
{
|
{
|
||||||
"$schema": "https://inlang.com/schema/inlang-message-format",
|
"$schema": "https://inlang.com/schema/inlang-message-format",
|
||||||
"greeting": "Hello {name}!",
|
"nav_greeting": "Hello {name}!",
|
||||||
"email_required": "Email is required",
|
"login_tab_login": "Login",
|
||||||
"password_required": "Password is required",
|
"login_tab_register": "Register",
|
||||||
"name_required": "Name is required",
|
"login_label_email": "Email",
|
||||||
"email_incorrect": "Email is incorrect",
|
"login_label_password": "Password",
|
||||||
"password_incorrect": "Password is incorrect",
|
"login_label_name": "Name",
|
||||||
"email_inuse": "Email is already in use",
|
"login_button_submit": "Submit",
|
||||||
"user_not_found": "The user could not be found"
|
"login_error_email_required": "Email is required",
|
||||||
|
"login_error_password_required": "Password is required",
|
||||||
|
"login_error_name_required": "Name is required",
|
||||||
|
"login_error_email_incorrect": "Email is incorrect",
|
||||||
|
"login_error_password_incorrect": "Password is incorrect",
|
||||||
|
"login_error_email_inuse": "Email is already in use",
|
||||||
|
"login_error_user_not_found": "The user could not be found"
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -3,7 +3,7 @@
|
||||||
|
|
||||||
let { title, username }: { title: string; username: string } = $props();
|
let { title, username }: { title: string; username: string } = $props();
|
||||||
|
|
||||||
let message = $derived(messages.greeting({ name: username }));
|
let message = $derived(messages.nav_greeting({ name: username }));
|
||||||
</script>
|
</script>
|
||||||
|
|
||||||
<header class="navbar justify-between bg-base-200 px-4">
|
<header class="navbar justify-between bg-base-200 px-4">
|
||||||
|
|
|
||||||
|
|
@ -1,5 +1,4 @@
|
||||||
import { messages } from '$lib/i18n';
|
import { messages } from '$lib/i18n';
|
||||||
import { email_inuse } from '$lib/paraglide/messages';
|
|
||||||
import { logger } from '$lib/server/logger';
|
import { logger } from '$lib/server/logger';
|
||||||
import { auth } from '$lib/server/lucia.js';
|
import { auth } from '$lib/server/lucia.js';
|
||||||
import { prisma } from '$lib/server/prisma';
|
import { prisma } from '$lib/server/prisma';
|
||||||
|
|
@ -12,12 +11,12 @@ export const actions = {
|
||||||
const form = await event.request.formData();
|
const form = await event.request.formData();
|
||||||
const email = form.get('email');
|
const email = form.get('email');
|
||||||
if (typeof email !== 'string') {
|
if (typeof email !== 'string') {
|
||||||
return fail(400, { email: { error: messages.email_required() } });
|
return fail(400, { email: { error: messages.login_error_email_required() } });
|
||||||
}
|
}
|
||||||
|
|
||||||
const password = form.get('password') as string;
|
const password = form.get('password') as string;
|
||||||
if (!password) {
|
if (!password) {
|
||||||
return fail(400, { password: { error: messages.password_required() } });
|
return fail(400, { password: { error: messages.login_error_password_required() } });
|
||||||
}
|
}
|
||||||
|
|
||||||
const user = await prisma.user.findUnique({
|
const user = await prisma.user.findUnique({
|
||||||
|
|
@ -27,13 +26,15 @@ export const actions = {
|
||||||
});
|
});
|
||||||
if (!user) {
|
if (!user) {
|
||||||
logger.error(`User not found! ${email}`);
|
logger.error(`User not found! ${email}`);
|
||||||
return fail(404, { email: { value: email, error: messages.user_not_found() } });
|
return fail(404, {
|
||||||
|
email: { value: email, error: messages.login_error_user_not_found() },
|
||||||
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
const validPassword = await new Argon2id().verify(user.password, password);
|
const validPassword = await new Argon2id().verify(user.password, password);
|
||||||
if (!validPassword) {
|
if (!validPassword) {
|
||||||
return fail(400, {
|
return fail(400, {
|
||||||
password: { error: messages.password_incorrect() },
|
password: { error: messages.login_error_password_incorrect() },
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
const session = await auth.createSession(user.id, []);
|
const session = await auth.createSession(user.id, []);
|
||||||
|
|
@ -48,26 +49,30 @@ export const actions = {
|
||||||
register: async (event) => {
|
register: async (event) => {
|
||||||
const form = await event.request.formData();
|
const form = await event.request.formData();
|
||||||
if (!form.has('email')) {
|
if (!form.has('email')) {
|
||||||
return fail(400, { email: { error: messages.email_required() } });
|
return fail(400, { email: { error: messages.login_error_email_required() } });
|
||||||
}
|
}
|
||||||
const { success, data: email, error } = string().email().safeParse(form.get('email'));
|
const { success, data: email, error } = string().email().safeParse(form.get('email'));
|
||||||
if (!success) {
|
if (!success) {
|
||||||
logger.error(error);
|
logger.error(error);
|
||||||
return fail(400, { email: { value: email, error: messages.email_incorrect() } });
|
return fail(400, {
|
||||||
|
email: { value: email, error: messages.login_error_email_incorrect() },
|
||||||
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
const password = form.get('password');
|
const password = form.get('password');
|
||||||
if (typeof password !== 'string') {
|
if (typeof password !== 'string') {
|
||||||
return fail(400, { password: { error: messages.password_required() } });
|
return fail(400, { password: { error: messages.login_error_password_required() } });
|
||||||
}
|
}
|
||||||
const name = form.get('name');
|
const name = form.get('name');
|
||||||
if (typeof name !== 'string') {
|
if (typeof name !== 'string') {
|
||||||
return fail(400, { name: { error: messages.name_required() } });
|
return fail(400, { name: { error: messages.login_error_name_required() } });
|
||||||
}
|
}
|
||||||
|
|
||||||
const usersWithEmail = await prisma.user.count({ where: { email: email } });
|
const usersWithEmail = await prisma.user.count({ where: { email: email } });
|
||||||
if (usersWithEmail !== 0) {
|
if (usersWithEmail !== 0) {
|
||||||
return fail(409, { email: { value: email, error: email_inuse() } });
|
return fail(409, {
|
||||||
|
email: { value: email, error: messages.login_error_email_inuse() },
|
||||||
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
const hashedPassword = await new Argon2id().hash(password);
|
const hashedPassword = await new Argon2id().hash(password);
|
||||||
|
|
|
||||||
|
|
@ -2,6 +2,7 @@
|
||||||
import Button from '$lib/components/common/Button';
|
import Button from '$lib/components/common/Button';
|
||||||
import TextInput from '$lib/components/common/TextInput';
|
import TextInput from '$lib/components/common/TextInput';
|
||||||
import Tabs from '$lib/components/Navigation/Tabs';
|
import Tabs from '$lib/components/Navigation/Tabs';
|
||||||
|
import { messages } from '$lib/i18n/index.js';
|
||||||
import { fade } from 'svelte/transition';
|
import { fade } from 'svelte/transition';
|
||||||
|
|
||||||
let { form } = $props();
|
let { form } = $props();
|
||||||
|
|
@ -31,19 +32,29 @@
|
||||||
{#snippet Form(variant: 'login' | 'register')}
|
{#snippet Form(variant: 'login' | 'register')}
|
||||||
<form method="POST" action={`?/${variant}`}>
|
<form method="POST" action={`?/${variant}`}>
|
||||||
<div class="card-body gap-4">
|
<div class="card-body gap-4">
|
||||||
<TextInput start={userIcon} placeholder="Email" name="email" type="email" />
|
<TextInput
|
||||||
|
start={userIcon}
|
||||||
|
placeholder={messages.login_label_email()}
|
||||||
|
name="email"
|
||||||
|
type="email"
|
||||||
|
/>
|
||||||
<TextInput
|
<TextInput
|
||||||
start={passwordIcon}
|
start={passwordIcon}
|
||||||
placeholder="Password"
|
placeholder={messages.login_label_password()}
|
||||||
name="password"
|
name="password"
|
||||||
type="password"
|
type="password"
|
||||||
/>
|
/>
|
||||||
{#if variant === 'register'}
|
{#if variant === 'register'}
|
||||||
<TextInput start={nameIcon} placeholder="Name" name="name" fade />
|
<TextInput
|
||||||
|
start={nameIcon}
|
||||||
|
placeholder={messages.login_label_name()}
|
||||||
|
name="name"
|
||||||
|
fade
|
||||||
|
/>
|
||||||
{/if}
|
{/if}
|
||||||
</div>
|
</div>
|
||||||
<div class="card-actions px-4">
|
<div class="card-actions px-4">
|
||||||
<Button block type="submit" label="Submit" outline />
|
<Button block type="submit" label={messages.login_button_submit()} outline />
|
||||||
</div>
|
</div>
|
||||||
</form>
|
</form>
|
||||||
{/snippet}
|
{/snippet}
|
||||||
|
|
@ -54,7 +65,11 @@
|
||||||
{#if form}
|
{#if form}
|
||||||
{@render alert(Object.values(form)[0].error)}
|
{@render alert(Object.values(form)[0].error)}
|
||||||
{/if}
|
{/if}
|
||||||
<Tabs variant="bordered" bind:selected={tab} tabs={['Login', 'Register']} />
|
<Tabs
|
||||||
|
variant="bordered"
|
||||||
|
bind:selected={tab}
|
||||||
|
tabs={[messages.login_tab_login(), messages.login_tab_register()]}
|
||||||
|
/>
|
||||||
</div>
|
</div>
|
||||||
{@render Form(tab === 0 ? 'login' : 'register')}
|
{@render Form(tab === 0 ? 'login' : 'register')}
|
||||||
</div>
|
</div>
|
||||||
|
|
|
||||||
Loading…
Add table
Reference in a new issue