Skip to content

Commit

Permalink
register
Browse files Browse the repository at this point in the history
  • Loading branch information
AstroCorp committed Sep 19, 2024
1 parent d1b09f3 commit 38f7ad3
Show file tree
Hide file tree
Showing 12 changed files with 168 additions and 32 deletions.
9 changes: 5 additions & 4 deletions src/i18n/en/register.json
Original file line number Diff line number Diff line change
Expand Up @@ -8,10 +8,11 @@
"password": "Password",
"password_error": "This password is not valid.",
"password_validation_1": "Minimum 8 characters",
"password_validation_2": "One uppercase",
"password_validation_3": "One lowercase",
"password_validation_4": "One number",
"password_validation_5": "One special character",
"password_validation_2": "Maximum 32 characters",
"password_validation_3": "One uppercase",
"password_validation_4": "One lowercase",
"password_validation_5": "One number",
"password_validation_6": "One special character",
"repeat_password": "Repeat password",
"repeat_password_error": "Passwords do not match.",
"question": "Already have an account?",
Expand Down
9 changes: 5 additions & 4 deletions src/i18n/es/register.json
Original file line number Diff line number Diff line change
Expand Up @@ -8,10 +8,11 @@
"password": "Contraseña",
"password_error": "Esta contraseña no es válida.",
"password_validation_1": "Mínimo 8 caracteres",
"password_validation_2": "Una mayúscula",
"password_validation_3": "Una minúscula",
"password_validation_4": "Un número",
"password_validation_5": "Un carácter especial",
"password_validation_2": "Máximo 32 caracteres",
"password_validation_3": "Una mayúscula",
"password_validation_4": "Una minúscula",
"password_validation_5": "Un número",
"password_validation_6": "Un carácter especial",
"repeat_password": "Repetir contraseña",
"repeat_password_error": "Las contraseñas no coinciden.",
"question": "¿Ya tienes una cuenta?",
Expand Down
Original file line number Diff line number Diff line change
@@ -1,6 +1,4 @@
export default defineNuxtRouteMiddleware(async (middleware) => {
const nuxtApp = useNuxtApp();
const localeRoute = useLocaleRoute();
const { loggedIn, fetch, session, clear } = useUserSession();
const { extractTokenData } = useJwt();

Expand All @@ -25,12 +23,4 @@ export default defineNuxtRouteMiddleware(async (middleware) => {
await fetch();
}
}

// Si el usuario no está logueado, lo redirige al login
if (!loggedIn.value) {
const loginRoute = localeRoute('login', nuxtApp.$i18n.locale);
const loginPath = loginRoute != null ? loginRoute.path : '/';

return navigateTo(loginPath);
}
});
13 changes: 13 additions & 0 deletions src/middleware/disabledWithSession.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,13 @@
export default defineNuxtRouteMiddleware(async (middleware) => {
const nuxtApp = useNuxtApp();
const localeRoute = useLocaleRoute();
const { loggedIn } = useUserSession();

// Si el usuario está logueado, lo redirige a su biblioteca
if (loggedIn.value) {
const libraryRoute = localeRoute('library', nuxtApp.$i18n.locale.value);
const libraryPath = libraryRoute != null ? libraryRoute.path : '/';

return navigateTo(libraryPath);
}
});
13 changes: 13 additions & 0 deletions src/middleware/enabledWithSession.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,13 @@
export default defineNuxtRouteMiddleware(async (middleware) => {
const nuxtApp = useNuxtApp();
const localeRoute = useLocaleRoute();
const { loggedIn } = useUserSession();

// Si el usuario no está logueado, lo redirige al login
if (!loggedIn.value) {
const loginRoute = localeRoute('login', nuxtApp.$i18n.locale.value);
const loginPath = loginRoute != null ? loginRoute.path : '/';

return navigateTo(loginPath);
}
});
2 changes: 1 addition & 1 deletion src/pages/(user)/library.vue
Original file line number Diff line number Diff line change
Expand Up @@ -10,7 +10,7 @@ defineI18nRoute({
});
definePageMeta({
middleware: ["auth"],
middleware: ["enabled-with-session"],
});
useHead({
Expand Down
4 changes: 4 additions & 0 deletions src/pages/auth/login.vue
Original file line number Diff line number Diff line change
Expand Up @@ -11,6 +11,10 @@ defineI18nRoute({
},
});
definePageMeta({
middleware: ["disabled-with-session"],
});
useHead({
title: t('home.title') + ' - ' + t('login.title'),
meta: [
Expand Down
82 changes: 72 additions & 10 deletions src/pages/auth/register.vue
Original file line number Diff line number Diff line change
@@ -1,5 +1,10 @@
<script setup lang="ts">
const { t } = useI18n();
import type { SessionSuccessResponse, RegisterErrorResponse } from '~/types/auth';
const { t, locale } = useI18n();
const localeRoute = useLocaleRoute();
const config = useRuntimeConfig();
const { fetch } = useUserSession();
defineI18nRoute({
paths: {
Expand All @@ -8,6 +13,10 @@ defineI18nRoute({
},
});
definePageMeta({
middleware: ["disabled-with-session"],
});
useHead({
title: t('home.title') + ' - ' + t('register.title'),
meta: [
Expand All @@ -17,6 +26,51 @@ useHead({
},
],
});
const registerForm = ref({
email: '',
password: '',
repeatPassword: '',
});
const emailErrors = ref<string[]>([]);
const passwordErrors = ref<string[]>([]);
const passwordMinLength = computed(() => registerForm.value.password.length >= 8);
const passwordMaxLength = computed(() => registerForm.value.password.length <= 32);
const passwordHasUppercase = computed(() => /[A-Z]/.test(registerForm.value.password));
const passwordHasLowercase = computed(() => /[a-z]/.test(registerForm.value.password));
const passwordHasNumber = computed(() => /\d/.test(registerForm.value.password));
const passwordHasSpecialCharacter = computed(() => /[!@#$%^&*()_+\-=\[\]{};':"\\|,.<>\/?]/.test(registerForm.value.password));
const samePassword = computed(() => registerForm.value.password === registerForm.value.repeatPassword);
const submitForm = async (event: Event) => {
event.preventDefault();
emailErrors.value = [];
passwordErrors.value = [];
const response = await $fetch<SessionSuccessResponse | RegisterErrorResponse>(config.public.frontendUrl + '/api/auth/register', {
method: 'POST',
body: registerForm.value,
ignoreResponseError: true,
});
if ('data' in response && response.data.statusCode === 400) {
emailErrors.value = response.data.message.filter((message) => message.includes('email'));
passwordErrors.value = response.data.message.filter((message) => !message.includes('email'));
return;
}
await fetch();
const libraryRoute = localeRoute('library', locale.value);
const libraryPath = libraryRoute != null ? libraryRoute.path : '/';
await navigateTo(libraryPath);
};
</script>

<template>
Expand All @@ -27,44 +81,52 @@ useHead({
</div>

<div class="flex flex-row items-center lg:px-8 w-full min-h-svh sm:w-1/2 md:w-2/5 xl:w-2/6 bg-white">
<form class="w-full px-6 py-4">
<form class="w-full px-6 py-4" @submit="submitForm">
<NuxtLinkLocale to="/">
<nuxt-icon name="logo" class="flex w-1/3 mx-auto mb-4 xl:mb-5" />
</NuxtLinkLocale>

<label class="block text-sm font-medium text-gray-700">{{ t('register.email') }}</label>
<div class="mt-1 mb-4">
<input
v-model="registerForm.email"
type="email"
class="shadow-sm block w-full sm:text-sm border-gray-300 rounded-md"
required
/>

<div class="text-sm text-red-500 pt-1">{{ t('register.email_error') }}</div>
<div v-if="emailErrors.length > 0" class="text-sm text-red-500 pt-1">{{ t('register.email_error') }}</div>
</div>

<label class="block text-sm font-medium text-gray-700">{{ t('register.password') }}</label>
<div class="mt-1 mb-4">
<input
v-model="registerForm.password"
type="password"
class="shadow-sm block w-full sm:text-sm border-gray-300 rounded-md"
required
/>

<FormValidationRule :isValid="true">{{ t('register.password_error') }}</FormValidationRule>
<FormValidationRule :isValid="true">{{ t('register.password_validation_1') }}</FormValidationRule>
<FormValidationRule :isValid="true">{{ t('register.password_validation_2') }}</FormValidationRule>
<FormValidationRule :isValid="false">{{ t('register.password_validation_3') }}</FormValidationRule>
<FormValidationRule :isValid="false">{{ t('register.password_validation_4') }}</FormValidationRule>
<FormValidationRule :isValid="false">{{ t('register.password_validation_5') }}</FormValidationRule>
<div v-if="passwordErrors.length > 0" class="text-sm text-red-500 py-1">{{ t('register.password_error') }}</div>

<FormValidationRule v-if="registerForm.password.length > 0" :isValid="passwordMinLength">{{ t('register.password_validation_1') }}</FormValidationRule>
<FormValidationRule v-if="registerForm.password.length > 0" :isValid="passwordMaxLength">{{ t('register.password_validation_2') }}</FormValidationRule>
<FormValidationRule v-if="registerForm.password.length > 0" :isValid="passwordHasUppercase">{{ t('register.password_validation_3') }}</FormValidationRule>
<FormValidationRule v-if="registerForm.password.length > 0" :isValid="passwordHasLowercase">{{ t('register.password_validation_4') }}</FormValidationRule>
<FormValidationRule v-if="registerForm.password.length > 0" :isValid="passwordHasNumber">{{ t('register.password_validation_5') }}</FormValidationRule>
<FormValidationRule v-if="registerForm.password.length > 0" :isValid="passwordHasSpecialCharacter">{{ t('register.password_validation_6') }}</FormValidationRule>
</div>

<label class="block text-sm font-medium text-gray-700">{{ t('register.repeat_password') }}</label>
<div class="mt-1 mb-4">
<input
v-model="registerForm.repeatPassword"
type="password"
class="shadow-sm block w-full sm:text-sm border-gray-300 rounded-md"
required
/>

<div class="text-sm text-red-500 pt-1">{{ t('register.repeat_password_error') }}</div>
<div v-if="!samePassword" class="text-sm text-red-500 pt-1">{{ t('register.repeat_password_error') }}</div>
</div>

<i18n-t keypath="register.warning" tag="p" class="text-xs mb-3">
Expand Down
4 changes: 4 additions & 0 deletions src/pages/auth/reset.vue
Original file line number Diff line number Diff line change
Expand Up @@ -8,6 +8,10 @@ defineI18nRoute({
},
});
definePageMeta({
middleware: ["disabled-with-session"],
});
useHead({
title: t('home.title') + ' - ' + t('reset.title'),
meta: [
Expand Down
4 changes: 2 additions & 2 deletions src/server/api/auth/login.post.ts
Original file line number Diff line number Diff line change
@@ -1,10 +1,10 @@
import { LoginAndRefreshResponse } from "~/types/auth";
import { SessionResponse } from "~/types/auth";

export default defineEventHandler(async (event) => {
const config = useRuntimeConfig();
const body = await readBody(event);

const response = await $fetch<LoginAndRefreshResponse>(config.public.backendUrl + '/auth/login', {
const response = await $fetch<SessionResponse>(config.public.backendUrl + '/auth/login', {
method: 'POST',
body: JSON.stringify({
email: body.email,
Expand Down
31 changes: 31 additions & 0 deletions src/server/api/auth/register.post.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,31 @@
import { SessionResponse } from "~/types/auth";

export default defineEventHandler(async (event) => {
const config = useRuntimeConfig();
const body = await readBody(event);

const response = await $fetch<SessionResponse>(config.public.backendUrl + '/auth/register', {
method: 'POST',
body: JSON.stringify({
email: body.email,
password: body.password,
}),
});

await replaceUserSession(event, {
user: {
email: response.user.email,
avatar: response.user.avatar,
isAdmin: response.user.isAdmin,
isVerified: response.user.isVerified,
},
access_token: response.access_token,
refresh_token: response.refresh_token,
}, {
maxAge: Number(config.nuxtSessionTime),
});

return {
success: true,
};
});
19 changes: 18 additions & 1 deletion src/types/auth.d.ts
Original file line number Diff line number Diff line change
Expand Up @@ -17,4 +17,21 @@ declare module '#auth-utils' {
interface UserSession extends LoginData {}
}

export interface LoginAndRefreshResponse extends LoginData {}
export interface SessionResponse extends LoginData {}

export interface RegisterErrorResponse {
data: {
message: string[];
error: string;
statusCode: number;
};
message: string;
stack: string;
statusCode: number;
statusMessage: string;
url: string;
}

export interface SessionSuccessResponse {
success: boolean;
}

0 comments on commit 38f7ad3

Please sign in to comment.