feat: Initial auth components
This commit is contained in:
@@ -0,0 +1,31 @@
|
|||||||
|
<script setup lang="ts">
|
||||||
|
const { login } = useAuthConnexion();
|
||||||
|
|
||||||
|
const fields = [
|
||||||
|
{
|
||||||
|
name: "username",
|
||||||
|
type: "text" as const,
|
||||||
|
label: "Courriel",
|
||||||
|
placeholder: "Entrez votre courriel",
|
||||||
|
required: true,
|
||||||
|
},
|
||||||
|
{
|
||||||
|
name: "password",
|
||||||
|
label: "Mot de passe",
|
||||||
|
type: "password" as const,
|
||||||
|
placeholder: "Entrez votre mot de passe",
|
||||||
|
required: true,
|
||||||
|
},
|
||||||
|
];
|
||||||
|
</script>
|
||||||
|
|
||||||
|
<template>
|
||||||
|
<UAuthForm
|
||||||
|
:schema="authLoginFormSchema"
|
||||||
|
:fields="fields"
|
||||||
|
title="Connexion"
|
||||||
|
description="Veuillez vous identifier."
|
||||||
|
loading-auto
|
||||||
|
@submit="login"
|
||||||
|
/>
|
||||||
|
</template>
|
||||||
@@ -0,0 +1,20 @@
|
|||||||
|
<script setup lang="ts">
|
||||||
|
const { logout } = useAuthConnexion();
|
||||||
|
</script>
|
||||||
|
|
||||||
|
<template>
|
||||||
|
<div class="w-full space-y-6">
|
||||||
|
<div class="flex flex-col text-center">
|
||||||
|
<div class="text-xl font-semibold text-pretty text-highlighted">Déconnexion</div>
|
||||||
|
<div class="mt-1 text-base text-pretty text-muted">Veuillez confirmer la déconnexion.</div>
|
||||||
|
</div>
|
||||||
|
<UButton
|
||||||
|
icon="i-lucide-log-out"
|
||||||
|
block
|
||||||
|
loading-auto
|
||||||
|
to="#"
|
||||||
|
label="Déconnexion"
|
||||||
|
@click="logout()"
|
||||||
|
/>
|
||||||
|
</div>
|
||||||
|
</template>
|
||||||
@@ -0,0 +1,8 @@
|
|||||||
|
<template>
|
||||||
|
<div class="w-full space-y-6">
|
||||||
|
<div class="flex flex-col text-center">
|
||||||
|
<div class="text-xl font-semibold text-pretty text-highlighted">Redirection en cours</div>
|
||||||
|
<div class="mt-1 text-base text-pretty text-muted">Veuillez patienter...</div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</template>
|
||||||
7
wp-content/themes/headless/app/composables/useAuth.ts
Normal file
7
wp-content/themes/headless/app/composables/useAuth.ts
Normal file
@@ -0,0 +1,7 @@
|
|||||||
|
export function useAuth() {
|
||||||
|
const { loggedIn: isLoggedIn, session } = useUserSession();
|
||||||
|
const hasRole = (role: string) => session.value?.user?.roles?.includes(role) || false;
|
||||||
|
const isAdmin = computed(() => hasRole("administrator"));
|
||||||
|
|
||||||
|
return { isLoggedIn, hasRole, isAdmin };
|
||||||
|
}
|
||||||
@@ -0,0 +1,80 @@
|
|||||||
|
import { delay } from "es-toolkit/promise";
|
||||||
|
|
||||||
|
import type { FormSubmitEvent } from "@nuxt/ui";
|
||||||
|
|
||||||
|
export function useAuthConnexion() {
|
||||||
|
const toast = useToast();
|
||||||
|
|
||||||
|
const defaultRedirect = (useRoute().query.redirect as string) || undefined;
|
||||||
|
const isRedirecting = useState("auth.isRedirecting", () => false);
|
||||||
|
const { fetch: refreshUserSession } = useUserSession();
|
||||||
|
|
||||||
|
// Helper: Redirect after login / logout
|
||||||
|
async function redirectTo(to: string | undefined) {
|
||||||
|
isRedirecting.value = true;
|
||||||
|
await delay(1000);
|
||||||
|
await refreshUserSession();
|
||||||
|
await navigateTo(to || defaultRedirect || "/");
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Attempt login with the provided credentials.
|
||||||
|
*
|
||||||
|
* @param event The form submit event containing the login credentials.
|
||||||
|
* @param redirect Optional URL to redirect to after successful login.
|
||||||
|
* @returns A promise that resolves when the login process is complete.
|
||||||
|
*/
|
||||||
|
async function login({ data: body }: FormSubmitEvent<AuthLoginForm>, redirect?: string) {
|
||||||
|
try {
|
||||||
|
const { success, message } = await $fetch("/api/login", { method: "POST", body });
|
||||||
|
if (!success) throw new Error(message);
|
||||||
|
|
||||||
|
toast.add({
|
||||||
|
color: "success",
|
||||||
|
title: "Connexion réussie",
|
||||||
|
description: message,
|
||||||
|
});
|
||||||
|
|
||||||
|
await redirectTo(redirect);
|
||||||
|
} catch (error) {
|
||||||
|
toast.add({
|
||||||
|
color: "error",
|
||||||
|
title: "Erreur de connexion",
|
||||||
|
description:
|
||||||
|
error instanceof Error ? error.message : "Une erreur est survenue lors de la connexion.",
|
||||||
|
});
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Logout the current user.
|
||||||
|
*
|
||||||
|
* @param redirect Optional URL to redirect to after successful logout.
|
||||||
|
* @returns A promise that resolves when the logout process is complete.
|
||||||
|
*/
|
||||||
|
async function logout(redirect?: string) {
|
||||||
|
try {
|
||||||
|
const { success, message } = await $fetch("/api/logout", { method: "POST" });
|
||||||
|
if (!success) throw new Error(message);
|
||||||
|
|
||||||
|
toast.add({
|
||||||
|
color: "success",
|
||||||
|
title: "Déconnexion réussie",
|
||||||
|
description: message,
|
||||||
|
});
|
||||||
|
|
||||||
|
await redirectTo(redirect);
|
||||||
|
} catch (error) {
|
||||||
|
toast.add({
|
||||||
|
color: "error",
|
||||||
|
title: "Erreur de déconnexion",
|
||||||
|
description:
|
||||||
|
error instanceof Error
|
||||||
|
? error.message
|
||||||
|
: "Une erreur est survenue lors de la déconnexion.",
|
||||||
|
});
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return { isRedirecting, login, logout };
|
||||||
|
}
|
||||||
17
wp-content/themes/headless/app/pages/connexion.vue
Normal file
17
wp-content/themes/headless/app/pages/connexion.vue
Normal file
@@ -0,0 +1,17 @@
|
|||||||
|
<script setup lang="ts">
|
||||||
|
const { isLoggedIn } = useAuth();
|
||||||
|
const { isRedirecting } = useAuthConnexion();
|
||||||
|
onBeforeMount(() => (isRedirecting.value = false));
|
||||||
|
</script>
|
||||||
|
|
||||||
|
<template>
|
||||||
|
<div id="page-connexion" class="container-sm py-12">
|
||||||
|
<AuthState>
|
||||||
|
<AuthRedirecting v-if="isRedirecting" />
|
||||||
|
<template v-else>
|
||||||
|
<AuthLoginForm v-if="!isLoggedIn" />
|
||||||
|
<AuthLogoutForm v-else />
|
||||||
|
</template>
|
||||||
|
</AuthState>
|
||||||
|
</div>
|
||||||
|
</template>
|
||||||
@@ -48,7 +48,7 @@ export default defineNuxtConfig({
|
|||||||
|
|
||||||
vite: {
|
vite: {
|
||||||
optimizeDeps: {
|
optimizeDeps: {
|
||||||
include: ["@vue/devtools-core", "@vue/devtools-kit", "zod"],
|
include: ["@vue/devtools-core", "@vue/devtools-kit", "zod", "es-toolkit/promise"],
|
||||||
},
|
},
|
||||||
},
|
},
|
||||||
});
|
});
|
||||||
|
|||||||
@@ -22,6 +22,7 @@
|
|||||||
"@lewebsimple/nuxt-graphql": "^0.7.5",
|
"@lewebsimple/nuxt-graphql": "^0.7.5",
|
||||||
"@nuxt/ui": "^4.6.0",
|
"@nuxt/ui": "^4.6.0",
|
||||||
"@nuxtjs/seo": "^4.0.2",
|
"@nuxtjs/seo": "^4.0.2",
|
||||||
|
"es-toolkit": "^1.45.1",
|
||||||
"nuxt": "^4.4.2",
|
"nuxt": "^4.4.2",
|
||||||
"nuxt-auth-utils": "^0.5.29",
|
"nuxt-auth-utils": "^0.5.29",
|
||||||
"nuxt-svgo": "^4.2.6",
|
"nuxt-svgo": "^4.2.6",
|
||||||
|
|||||||
3
wp-content/themes/headless/pnpm-lock.yaml
generated
3
wp-content/themes/headless/pnpm-lock.yaml
generated
@@ -23,6 +23,9 @@ importers:
|
|||||||
'@nuxtjs/seo':
|
'@nuxtjs/seo':
|
||||||
specifier: ^4.0.2
|
specifier: ^4.0.2
|
||||||
version: 4.0.2(489f84e1ce5b91b262b98d380824761d)
|
version: 4.0.2(489f84e1ce5b91b262b98d380824761d)
|
||||||
|
es-toolkit:
|
||||||
|
specifier: ^1.45.1
|
||||||
|
version: 1.45.1
|
||||||
nuxt:
|
nuxt:
|
||||||
specifier: ^4.4.2
|
specifier: ^4.4.2
|
||||||
version: 4.4.2(@babel/core@7.29.0)(@babel/plugin-syntax-jsx@7.28.6(@babel/core@7.29.0))(@parcel/watcher@2.5.6)(@types/node@25.5.0)(@vue/compiler-sfc@3.5.31)(cac@6.7.14)(db0@0.3.4)(ioredis@5.10.1)(lightningcss@1.32.0)(magicast@0.5.2)(oxlint@1.57.0)(rollup-plugin-visualizer@7.0.1(rollup@4.60.0))(rollup@4.60.0)(terser@5.46.1)(typescript@5.9.3)(vite@7.3.1(@types/node@25.5.0)(jiti@2.6.1)(lightningcss@1.32.0)(terser@5.46.1)(yaml@2.8.3))(vue-tsc@3.2.6(typescript@5.9.3))(yaml@2.8.3)
|
version: 4.4.2(@babel/core@7.29.0)(@babel/plugin-syntax-jsx@7.28.6(@babel/core@7.29.0))(@parcel/watcher@2.5.6)(@types/node@25.5.0)(@vue/compiler-sfc@3.5.31)(cac@6.7.14)(db0@0.3.4)(ioredis@5.10.1)(lightningcss@1.32.0)(magicast@0.5.2)(oxlint@1.57.0)(rollup-plugin-visualizer@7.0.1(rollup@4.60.0))(rollup@4.60.0)(terser@5.46.1)(typescript@5.9.3)(vite@7.3.1(@types/node@25.5.0)(jiti@2.6.1)(lightningcss@1.32.0)(terser@5.46.1)(yaml@2.8.3))(vue-tsc@3.2.6(typescript@5.9.3))(yaml@2.8.3)
|
||||||
|
|||||||
@@ -4,13 +4,12 @@ export default defineEventHandler(async (event) => {
|
|||||||
const variables = authLoginFormSchema.parse(await readBody(event));
|
const variables = authLoginFormSchema.parse(await readBody(event));
|
||||||
|
|
||||||
// Execute the GraphQL operation to authenticate the user
|
// Execute the GraphQL operation to authenticate the user
|
||||||
const { data, error } = await executeSchemaOperation(event, {
|
const { data } = await executeSchemaOperation(event, {
|
||||||
operationName: "AuthLogin",
|
operationName: "AuthLogin",
|
||||||
variables,
|
variables,
|
||||||
});
|
});
|
||||||
|
|
||||||
// Handle errors and validate the response data
|
// Validate the response data
|
||||||
if (error) throw error;
|
|
||||||
if (!data?.login) throw new Error("Identifiants invalides. Veuillez réessayer.");
|
if (!data?.login) throw new Error("Identifiants invalides. Veuillez réessayer.");
|
||||||
|
|
||||||
// Handle the login process by setting the session data
|
// Handle the login process by setting the session data
|
||||||
@@ -18,7 +17,7 @@ export default defineEventHandler(async (event) => {
|
|||||||
throw new Error("Une erreur est survenue lors de la connexion.");
|
throw new Error("Une erreur est survenue lors de la connexion.");
|
||||||
}
|
}
|
||||||
|
|
||||||
return { success: true, message: "Connexion réussie" };
|
return { success: true, message: "Vous avez été connecté avec succès." };
|
||||||
} catch (error) {
|
} catch (error) {
|
||||||
const message =
|
const message =
|
||||||
error instanceof Error ? error.message : "Une erreur est survenue lors de la connexion.";
|
error instanceof Error ? error.message : "Une erreur est survenue lors de la connexion.";
|
||||||
|
|||||||
@@ -4,7 +4,7 @@ export default defineEventHandler(async (event) => {
|
|||||||
try {
|
try {
|
||||||
await handleLogout(event);
|
await handleLogout(event);
|
||||||
|
|
||||||
return { success: true, message: "Déconnexion réussie" };
|
return { success: true, message: "Vous avez été déconnecté avec succès." };
|
||||||
} catch (error) {
|
} catch (error) {
|
||||||
const message =
|
const message =
|
||||||
error instanceof Error ? error.message : "Une erreur est survenue lors de la déconnexion.";
|
error instanceof Error ? error.message : "Une erreur est survenue lors de la déconnexion.";
|
||||||
|
|||||||
Reference in New Issue
Block a user