diff --git a/next.config.mjs b/next.config.mjs index 4678774e6d606704bce1897a5dab960cd798bf66..c9a3c0c4982148fee841236eca5f36fe1a572ab2 100644 --- a/next.config.mjs +++ b/next.config.mjs @@ -1,4 +1,8 @@ /** @type {import('next').NextConfig} */ -const nextConfig = {}; +const nextConfig = { + images: { + domains: ['https://res.cloudinary.com'], + } +}; export default nextConfig; diff --git a/src/app/auth/change-password/page.jsx b/src/app/auth/change-password/page.jsx new file mode 100644 index 0000000000000000000000000000000000000000..a89f4840e8ec4f373062d0090f2ca5b16243e275 --- /dev/null +++ b/src/app/auth/change-password/page.jsx @@ -0,0 +1,120 @@ +"use client" +import fetchRequest from '@/app/lib/fetchRequest' +import Image from 'next/image' +import Link from 'next/link' +import React, { useEffect, useState } from 'react' +import { useSearchParams } from 'next/navigation' +import Loader from '@/components/Loader/Loader' +import { PASSWORD_REGEX } from '@/app/lib/constants' + +const ChangePassword = () => { + const [isSuccess, setIsSuccess] = useState(false) + const [formErrors, setFormErrors] = useState([]) + const [password, setPassword] = useState("") + const [confirmPassword, setConfirmPassword] = useState("") + const [isLoading, setIsLoading] = useState(false) + const params = useSearchParams(); + const handleChangePassword = async (event) => { + event.preventDefault() + setIsLoading(true) + const { isSuccess, data, errors } = await fetchRequest(`/password_reset/confirm/?token=${params.get("token")}`, { + method: "POST", + body: JSON.stringify({ + password, + token: params.get("token") + }) + }) + if (isSuccess) { + setIsSuccess(true) + } + else { + console.log(errors) + setIsLoading(false) + if (errors.type === "Validation Error") { + if (errors.detail.token) { + setFormErrors(["Le lien que vous avez utilisé pour réinitialiser votre mot de passe est invalide"]) + } + else { + setFormErrors(["Le Mot de passe est invalide"]) + } + } else if (errors?.detail?.startsWith("The OTP password entered is not valid")) { + setFormErrors(["Le lien que vous avez utilisé pour réinitialiser votre mot de passe est déja utilisé"]) + } + else { + console.log("Internal Server Error") + } + } + } + const isEmptyFields = !password && !confirmPassword + useEffect(() => { + var currentErrors = [] + if (password !== confirmPassword) { + currentErrors.push("Les mots de passe ne sont pas identiques.") + } + if (password.length < 8) { + currentErrors.push("Le mot de passe doit comporter au moins 8 caractères.") + } + if (!PASSWORD_REGEX.test(password)) { + currentErrors.push("Le mot de passe doit contenir au moins une lettre, un chiffre et un caractère spécial.") + } + setFormErrors(currentErrors) + }, [password, confirmPassword]) + return ( +
+
+
+
+ teamwill +
+ {(!isSuccess) &&
+

Change your password. +

+
+
    +
  • Le mot de passe doit contenir au moins 8 caractères
  • +
  • Le mot de passe doit contenir au moins un chiffre
  • +
  • Le mot de passe doit contenir au moins un caractère spécial
  • +
+
+
+
+ + setPassword(event.target.value)} type="password" name="new_password1" id="new_password1" + className="rounded-md px-3 w-full duration-150 delay-75 focus:ring ring-offset-1 ring-sushi-200 border h-10 border-neutral-300 outline-none" /> +
+
+ + setConfirmPassword(event.target.value)} type="password" name="new_password2" id="new_password2" + className="rounded-md px-3 duration-150 delay-75 w-full focus:ring ring-offset-1 ring-sushi-200 border h-10 border-neutral-300 outline-none" /> +
+
    0 && !isEmptyFields ? "bg-red-100 border border-red-300" : ""} min-h-10 px-3 text-xs py-3 rounded relative mt-9 mb-6 list-inside list-disc`} role="alert"> + {!isEmptyFields && formErrors.map((error, index) => { + return
  • {error}
  • + })} +
+
+ +
+
+
} + {(isSuccess) && ( +
+

The password has been changed!

+ log in again? +
+ )} +
+
+
+ ) +} + +export default ChangePassword \ No newline at end of file diff --git a/src/app/auth/forgot-password/page.jsx b/src/app/auth/forgot-password/page.jsx new file mode 100644 index 0000000000000000000000000000000000000000..3895740406b0799162512b482ddfdd3a23385135 --- /dev/null +++ b/src/app/auth/forgot-password/page.jsx @@ -0,0 +1,98 @@ +'use client' +import fetchRequest from '@/app/lib/fetchRequest' +import Loader from '@/components/Loader/Loader' +import Image from 'next/image' +import Link from 'next/link' +import React, { useState } from 'react' + +const ForgotPassword = () => { + const [email, setEmail] = useState("") + const [isSuccess, setIsSuccess] = useState(false) + const [isLoading, setIsLoading] = useState(false) + const [requestErrors, setRequestErrors] = useState([]) + const handleForgetPassword = async (event) => { + event.preventDefault() + setIsLoading(true) + const { isSuccess, errors, data } = await fetchRequest("/password_reset/", { + body: JSON.stringify({ email }), + method: "POST" + }) + if (isSuccess) { + setIsSuccess(true) + } + else { + setIsLoading(false) + if (errors.type === "Validation Error") { + // setRequestErrors(Object.keys(errors.detail).reduce((prev, current) => { + // return [...prev, ...errors.detail[current]] + // }, [])) + setRequestErrors(["Nous n'avons pas pu trouver de compte associé à cet e-mail. Veuillez essayer une autre adresse e-mail."]) + } else { + console.log("Internal server error") + } + } + } + return ( +
+
+
+ teamwill + {(!isSuccess) &&
+
+

Forgot your password!! No Problem + Reset it here +

+
+
+
+
+
+ setEmail(e.target.value)} autocomplete="off" id="email" name="email" type="text" + className="peer placeholder-transparent h-10 w-full border-b-2 border-gray-300 text-gray-900 focus:outline-none focus:borer-rose-600" + placeholder="Email address" /> + +
+ + + {(requestErrors.length !== 0) && } +
+ Back to log + in +
+ +
+
+
+
} + {(isSuccess) &&
+ + + +
+

Email Sent!

+

Check your email and open the link we sent to continue

+
+
} +
+
+
+ ) +} + +export default ForgotPassword \ No newline at end of file diff --git a/src/app/auth/login/page.jsx b/src/app/auth/login/page.jsx index 33b3f092806c43146c129778fdef8fa71449bd18..a657afd1ac960fbd7982db00585136d279b5201b 100644 --- a/src/app/auth/login/page.jsx +++ b/src/app/auth/login/page.jsx @@ -3,7 +3,7 @@ import { useState } from 'react'; import Link from 'next/link'; import Cookies from 'js-cookie'; -import {createSession} from "@/app/lib/session"; +import { createSession } from "@/app/lib/session"; const LoginPage = () => { const [username, setUsername] = useState(''); @@ -18,14 +18,10 @@ const LoginPage = () => { // Add your form submission logic here }; - const secretKey = process.env.NEXT_PUBLIC_SESSION_SECRET; - console.log(secretKey); - - // send login request to the server const login = async (event) => { event.preventDefault(); try { - const response = await fetch('http://localhost:8000/login/', { + const response = await fetch(`${process.env.NEXT_PUBLIC_API_URL}/login/`, { method: 'POST', headers: { 'Content-Type': 'application/json', @@ -33,47 +29,13 @@ const LoginPage = () => { body: JSON.stringify({ username, password }), }); const data = await response.json(); - if (data.error) { - setMessages(data.error); + if (data.non_field_errors) { + setMessages(data.non_field_errors[0]); } else { setMessages('Login successful'); - // Set the cookie - const expiresAt = new Date(new Date().getTime() + 60 * 60 * 1000); // Set cookie to expire in 1 hour - // const session = json strigify it - Cookies.set('session', data.token, { - expires: expiresAt, - secure: true, - sameSite: 'Lax', - }); - // Redirect to the dashboard - // window.location.href = '/auth/verif'; - } - } catch (error) { - setMessages('An error occurred'); - } - }; - const loginn = async (event) => { - event.preventDefault(); - try { - const response = await fetch('http://localhost:8000/login/', { - method: 'POST', - headers: { - 'Content-Type': 'application/json', - }, - body: JSON.stringify({ username, password }), - }); - const data = await response.json(); - if (data.error) { - setMessages(data.error); - } else { - setMessages('Login successful'); - // Set the cookie - const expiresAt = new Date(new Date().getTime() + 60 * 60 * 1000); // Set cookie to expire in 1 hour - // const session = json strigify it await createSession(data); - // Redirect to the dashboard - // window.location.href = '/auth/verif'; + window.location.href = '/auth/verif'; } } catch (error) { setMessages('An error occurred'); @@ -91,7 +53,7 @@ const LoginPage = () => {
{
{ />
- -

Mot de passe oublié?

+ +

Mot de passe oublié?

{messages && ( @@ -124,7 +86,7 @@ const LoginPage = () => { )} diff --git a/src/app/auth/verif/page.jsx b/src/app/auth/verif/page.jsx index cfea4df1253203ecab55cdbcb6d5644d8980cac5..15d3d4083be7e343e184e71bde68c51c9ab26e99 100644 --- a/src/app/auth/verif/page.jsx +++ b/src/app/auth/verif/page.jsx @@ -5,7 +5,7 @@ const Verif = () => { // fetch data from the server /example const fetchExampleData = async () => { try { - const data = await fetchRequest('http://localhost:8000/example/', + const data = await fetchRequest(`/example/`, {method: 'POST', body: JSON.stringify({ "name": "jhon", @@ -20,18 +20,18 @@ const Verif = () => { } }; const isAuth = async () => { - isAuthenticated(); + await isAuthenticated(); } return ( - <> +
user is redirected to this page after successful login
{/*test fetchExampleData */} - +
); } export default Verif; \ No newline at end of file diff --git a/src/app/globals.css b/src/app/globals.css index 6c139df80533021db2ee61f9f06c23be2eb0a57f..b6ba7d37334c09788aa0026af48f37b048eed1d8 100644 --- a/src/app/globals.css +++ b/src/app/globals.css @@ -2,29 +2,6 @@ @tailwind components; @tailwind utilities; -:root { - --foreground-rgb: 0, 0, 0; - --background-start-rgb: 214, 219, 220; - --background-end-rgb: 255, 255, 255; -} - -@media (prefers-color-scheme: dark) { - :root { - --foreground-rgb: 255, 255, 255; - --background-start-rgb: 0, 0, 0; - --background-end-rgb: 0, 0, 0; - } -} - -body { - color: rgb(var(--foreground-rgb)); - background: linear-gradient( - to bottom, - transparent, - rgb(var(--background-end-rgb)) - ) - rgb(var(--background-start-rgb)); -} @layer utilities { .text-balance { @@ -34,18 +11,23 @@ body { /* Customize the scrollbar */ ::-webkit-scrollbar { - width: 6px; /* Set the width of the scrollbar */ + width: 6px; + /* Set the width of the scrollbar */ } ::-webkit-scrollbar-thumb { - background-color: rgba(0, 0, 0, 0.3); /* Color of the thumb */ - border-radius: 3px; /* Rounded corners of the thumb */ + background-color: rgba(0, 0, 0, 0.3); + /* Color of the thumb */ + border-radius: 3px; + /* Rounded corners of the thumb */ } ::-webkit-scrollbar-thumb:hover { - background-color: rgba(0, 0, 0, 0.5); /* Color of the thumb on hover */ + background-color: rgba(0, 0, 0, 0.5); + /* Color of the thumb on hover */ } ::-webkit-scrollbar-track { - background-color: rgba(0, 0, 0, 0.1); /* Color of the track */ -} + background-color: rgba(0, 0, 0, 0.1); + /* Color of the track */ +} \ No newline at end of file diff --git a/src/app/layout.js b/src/app/layout.js index 9aef1df7d6c3d50515ab62f578c5cc963a1243a7..3cf1c509714b5ff4b72ca6c66ac3cbaaed88e86a 100644 --- a/src/app/layout.js +++ b/src/app/layout.js @@ -1,5 +1,6 @@ import { Inter } from "next/font/google"; import "./globals.css"; +import Header from "@/app/ui/Header"; const inter = Inter({ subsets: ["latin"] }); @@ -11,7 +12,10 @@ export const metadata = { export default function RootLayout({ children }) { return ( - {children} + +
+ {children} + ); } diff --git a/src/app/lib/constants.js b/src/app/lib/constants.js new file mode 100644 index 0000000000000000000000000000000000000000..f4da663555ffe54dfea91d044d2b58ec3c463486 --- /dev/null +++ b/src/app/lib/constants.js @@ -0,0 +1 @@ +export const PASSWORD_REGEX = /^(?=.*[a-zA-Z])(?=.*\d)(?=.*[^a-zA-Z\d\s]).*$/; \ No newline at end of file diff --git a/src/app/lib/dal.js b/src/app/lib/dal.js deleted file mode 100644 index 74c6293b78f5f50dac5fc7d8a9b11ed8f1b15fbe..0000000000000000000000000000000000000000 --- a/src/app/lib/dal.js +++ /dev/null @@ -1,16 +0,0 @@ -import 'server-only' - -import Cookies from 'js-cookie'; -import { decrypt } from '@/app/lib/session' -import {redirect} from "next/navigation"; - -export const verifySession = cache(async () => { - const cookie = Cookies.get('session').value - const session = await decrypt(cookie) - - if (!session.token) { - redirect('/login') - } - - return { isAuth: true, userName: session.username } -}) \ No newline at end of file diff --git a/src/app/lib/fetchRequest.js b/src/app/lib/fetchRequest.js index 3afb1c3072b662f2ba0461e36390f1f9e0700eed..9effc1e3b2c63c8d919ab26c92990e5ca65e98ba 100644 --- a/src/app/lib/fetchRequest.js +++ b/src/app/lib/fetchRequest.js @@ -1,29 +1,35 @@ //custom fetch tha have the token in header import Cookies from 'js-cookie'; -import {decrypt} from "@/app/lib/session"; - +import { decrypt } from "@/app/lib/session"; +const BASE_URL = process.env.NEXT_PUBLIC_API_URL || "http://localhost:8000" const fetchRequest = async (url, options = {}) => { - const token = Cookies.get('session'); - console.log('token', token) - const jwtDecrypted = decrypt(token); + const jwtCookie = Cookies.get('session'); + console.log('jwtCookie', jwtCookie) + const jwtDecrypted = await decrypt(jwtCookie); console.log('jwtDecrypted', jwtDecrypted) const headers = { ...options.headers, - 'Authorization': `Token ${token}`, + // add authorization header with token if there is jwtDecrypted.sessionData.token + Authorization: jwtDecrypted?.sessionData.token ? `Bearer ${jwtDecrypted.sessionData.token}` : undefined, 'Content-Type': 'application/json', }; - const response = await fetch(url, { + const response = await fetch(`${BASE_URL}${url}`, { ...options, headers, }); if (!response.ok) { - throw new Error('Network response was not ok'); + try { + const errorData = await response.json(); + return { isSuccess: false, errors: errorData, data: null } + } catch (error) { + return { isSuccess: false, errors: error, data: null } + } } - - return response.json(); + const data = await response.json() + return { isSuccess: true, errors: null, data: data }; }; export default fetchRequest; diff --git a/src/app/lib/isAuthenticated.js b/src/app/lib/isAuthenticated.js index 0a880acf5d8ce15ae58da3d0295208ff7a4ca3f6..7c77a5e9fe0c6cb8dff56ea1052bd1b27e4b6f6b 100644 --- a/src/app/lib/isAuthenticated.js +++ b/src/app/lib/isAuthenticated.js @@ -1,16 +1,20 @@ //verify if the user is authenticated import Cookies from "js-cookie"; +import {decrypt} from "@/app/lib/session"; -export default function isAuthenticated() { +export default async function isAuthenticated() { if (!Cookies.get('session')) { console.log('user is not authenticated'); // redirect to auth/login page - window.location.href = '/auth/login'; - + // window.location.href = '/auth/login'; return false; } - else console.log('user is authenticated'); - return true; + + console.log('user is authenticated'); + const session = Cookies.get('session') + const cookieDecoded = await decrypt(session) + return { isAuth: true, sessionData: cookieDecoded.sessionData } + } diff --git a/src/app/lib/isAuthenticatedSSR.js b/src/app/lib/isAuthenticatedSSR.js new file mode 100644 index 0000000000000000000000000000000000000000..09a8fd77ead1a9e744b9e263ec5ddf81362ab5f1 --- /dev/null +++ b/src/app/lib/isAuthenticatedSSR.js @@ -0,0 +1,21 @@ +//verify if the user is authenticated + +import { cookies } from 'next/headers' + +import {decrypt} from "@/app/lib/session"; + +export default async function isAuthenticatedSSR() { + if (!cookies().get('session')) { + console.log('user is not authenticated'); + // redirect to auth/login page + // window.location.href = '/auth/login'; + return false; + } + + console.log('user is authenticated'); + const cookie = cookies().get('session')?.value + const session = await decrypt(cookie) + return { isAuth: true, sessionData: session.sessionData } + +} + diff --git a/src/app/lib/session.js b/src/app/lib/session.js index 850ed748db48de63bae014a7fe245ff3a84f3278..1eb509a2e30326ef4e345033f9811c8dc0688513 100644 --- a/src/app/lib/session.js +++ b/src/app/lib/session.js @@ -2,7 +2,8 @@ import Cookies from 'js-cookie'; import { SignJWT, jwtVerify } from 'jose' -const secretKey = "password1234" +const secretKey = process.env.NEXT_PUBLIC_SESSION_SECRET; +console.log(secretKey); const encodedKey = new TextEncoder().encode(secretKey) export async function encrypt(payload) { diff --git a/src/app/page.js b/src/app/page.js index ecfca6f8e5034eb7f60a623d3db202433f6b96b9..87ce7aa74a17bf1ba51f0e9979b2d955634bf8ca 100644 --- a/src/app/page.js +++ b/src/app/page.js @@ -1,149 +1,8 @@ -import Image from "next/image"; export default function Home() { return (
-
-

- Get started by editing  - src/app/page.js -

-
- - By{" "} - Vercel Logo - -
-
- -
- Next.js Logo -
- -
- -

- Docs{" "} - - -> - -

-

- Find in-depth information about Next.js features and API. -

-
- - -

- Learn{" "} - - -> - -

-

- Learn about Next.js in an interactive course with quizzes! -

-
- - -

- Templates{" "} - - -> - -

-

- Explore starter templates for Next.js. -

-
- - -

- Deploy{" "} - - -> - -

-

- Instantly deploy your Next.js site to a shareable URL with Vercel. -

-

- Instantly deploy your Next.js site to a shareable URL with Vercel. -

-

- Instantly deploy your Next.js site to a shareable URL with Vercel. -

-

- Instantly deploy your Next.js site to a shareable URL with Vercel. -

-

- Instantly deploy your Next.js site to a shareable URL with Vercel. -

-

- Instantly deploy your Next.js site to a shareable URL with Vercel. -

-

- Instantly deploy your Next.js site to a shareable URL with Vercel. -

-

- Instantly deploy your Next.js site to a shareable URL with Vercel. -

-

- Instantly deploy your Next.js site to a shareable URL with Vercel. -

-

- Instantly deploy your Next.js site to a shareable URL with Vercel. -

-

- Instantly deploy your Next.js site to a shareable URL with Vercel. -

-

- Instantly deploy your Next.js site to a shareable URL with Vercel. -

-

- Instantly deploy your Next.js site to a shareable URL with Vercel. -

-
-
+ Home Page
); } diff --git a/src/app/ui/Header.jsx b/src/app/ui/Header.jsx new file mode 100644 index 0000000000000000000000000000000000000000..b9d5df239d8cd894961ef8108103ed744d0617b5 --- /dev/null +++ b/src/app/ui/Header.jsx @@ -0,0 +1,54 @@ +// 'use client'; + +// import { useState, useEffect } from 'react'; +import Link from 'next/link'; +// import isAuthenticated from "@/app/lib/isAuthenticated"; +import isAuthenticatedSSR from "@/app/lib/isAuthenticatedSSR"; +import LogoutButton from "@/app/ui/LogoutButton"; +// import fetchRequest from "@/app/lib/fetchRequest"; +// import Cookies from "js-cookie"; + +const Header = async () => { + + const {isAuth, sessionData} = await isAuthenticatedSSR() + console.log('isAuth', isAuth) + console.log('sessionData', sessionData) + return ( +
+
+ +

TeamBook

+ +
+ +
+ ); +}; + +export default Header; \ No newline at end of file diff --git a/src/app/ui/LogoutButton.js b/src/app/ui/LogoutButton.js new file mode 100644 index 0000000000000000000000000000000000000000..b219fc4381e545d6cd4e3facfe37c20696fbe65b --- /dev/null +++ b/src/app/ui/LogoutButton.js @@ -0,0 +1,26 @@ +'use client'; + +import Cookies from 'js-cookie'; +import fetchRequest from "@/app/lib/fetchRequest"; + +const LogoutButton = () => { + const logout = async () => { + const response = await fetchRequest(`/logout`, { + method: 'GET'}); + console.log(response); + if (response.isSuccess) { + console.log('logout successful'); + Cookies.remove('session'); + window.location.href = '/'; + } + + }; + + return ( + + ); +}; + +export default LogoutButton; diff --git a/src/components/Loader/Loader.css b/src/components/Loader/Loader.css new file mode 100644 index 0000000000000000000000000000000000000000..1e1ca65608312aa3c0c9ecfbed194e5cee0cbf75 --- /dev/null +++ b/src/components/Loader/Loader.css @@ -0,0 +1,46 @@ +@keyframes ldio-gcsicpsikdq { + 0% { + transform: rotate(0deg); + } + + 100% { + transform: rotate(360deg); + } +} + +.ldio-gcsicpsikdq div { + width: 50px; + height: 50px; + border-top-color: transparent; + border: 5px solid transparent; + border-radius: 50%; +} + +.ldio-gcsicpsikdq div { + animation: ldio-gcsicpsikdq 1s linear infinite; +} + +.loadingio-spinner-rolling-daoiuzlm498 { + width: min-content; + height: 70px; + display: flex; + flex-direction: column; + justify-content: center; + align-items: center; + background: transparent; +} + +.ldio-gcsicpsikdq { + display: flex; + flex-direction: column; + align-items: center; + justify-content: center; + transform: translateZ(0) scale(1); + backface-visibility: hidden; + transform-origin: 0 0; + /* see note above */ +} + +.ldio-gcsicpsikdq div { + box-sizing: content-box; +} \ No newline at end of file diff --git a/src/components/Loader/Loader.jsx b/src/components/Loader/Loader.jsx new file mode 100644 index 0000000000000000000000000000000000000000..2430f19a7bd30aade3569d38228bdc38b43b5288 --- /dev/null +++ b/src/components/Loader/Loader.jsx @@ -0,0 +1,13 @@ +import React from 'react' +import "./Loader.css" +const Loader = ({ size, border, className, color }) => { + return ( +
+
+
+
+
+ ) +} + +export default Loader \ No newline at end of file diff --git a/src/middleware.js b/src/middleware.js index 1308f827736c79a423734acb70d0051515d7d5b1..62024a0b3f987cb5b71312e1770bb7245c57fed8 100644 --- a/src/middleware.js +++ b/src/middleware.js @@ -1,12 +1,10 @@ -"use client"; - import { NextResponse } from 'next/server' import { decrypt } from '@/app/lib/session' -import Cookies from 'js-cookie'; +import { cookies } from 'next/headers' // 1. Specify protected and public routes -const protectedRoutes = ['/dashboard'] -const publicRoutes = ['/login', '/signup', '/'] +const protectedRoutes = ['/dashboard', '/auth/verif'] +const publicRoutes = ['/auth/login', '/auth/signup'] export default async function middleware(req) { // 2. Check if the current route is protected or public @@ -15,22 +13,21 @@ export default async function middleware(req) { const isPublicRoute = publicRoutes.includes(path) // 3. Decrypt the session from the cookie - const cookie = Cookies.get('session'); - console.log('cookie', cookie) + const cookie = cookies().get('session')?.value const session = await decrypt(cookie) - + console.log('session dfdf', session) // 5. Redirect to /login if the user is not authenticated - if (isProtectedRoute && !session?.token) { - return NextResponse.redirect(new URL('/login', req.nextUrl)) + if (isProtectedRoute && !session?.sessionData.token) { + return NextResponse.redirect(new URL('/auth/login', req.nextUrl)) } // 6. Redirect to /dashboard if the user is authenticated if ( isPublicRoute && - session?.token && - !req.nextUrl.pathname.startsWith('/auth') + session?.sessionData.token && + !req.nextUrl.pathname.startsWith('/auth/verif') ) { - return NextResponse.redirect(new URL('/auth', req.nextUrl)) + return NextResponse.redirect(new URL('/auth/verif', req.nextUrl)) } return NextResponse.next()