From ef7ac122f8a90f1d222a6aa51e5ed3956fe13f0f Mon Sep 17 00:00:00 2001 From: streap2 Date: Mon, 27 Apr 2026 08:01:51 +0200 Subject: [PATCH] feat: add email verification resend functionality to login page and backend route --- app/api/auth/resend-verification/route.ts | 98 +++++++++++++++++++++++ app/login/page.tsx | 60 +++++++++++++- 2 files changed, 155 insertions(+), 3 deletions(-) create mode 100644 app/api/auth/resend-verification/route.ts diff --git a/app/api/auth/resend-verification/route.ts b/app/api/auth/resend-verification/route.ts new file mode 100644 index 0000000..94521ee --- /dev/null +++ b/app/api/auth/resend-verification/route.ts @@ -0,0 +1,98 @@ +import { NextRequest, NextResponse } from 'next/server'; +import prisma from '@/lib/prisma'; +import crypto from 'crypto'; + +export async function POST(request: NextRequest) { + try { + const { email } = await request.json(); + + if (!email) { + return NextResponse.json( + { error: 'Email requis' }, + { status: 400 } + ); + } + + // Find user + const user = await prisma.user.findUnique({ + where: { email } + }); + + if (!user) { + // For security, same message even if user doesn't exist + return NextResponse.json( + { message: 'Si un compte existe pour cet email et n\'est pas encore vérifié, un nouvel email de vérification a été envoyé.' }, + { status: 200 } + ); + } + + if (user.emailVerified) { + return NextResponse.json( + { error: 'Ce compte est déjà vérifié.' }, + { status: 400 } + ); + } + + // Generate new verification token + const verificationToken = crypto.randomBytes(32).toString('hex'); + + // Update user with new token + await prisma.user.update({ + where: { id: user.id }, + data: { verificationToken } + }); + + // Send verification email + try { + const settings = await prisma.siteSetting.findUnique({ + where: { id: 'singleton' } + }); + + const siteName = settings?.siteName || 'Afrohub'; + const subject = settings?.verifyEmailSubject || 'Vérification de votre compte - Afrohub'; + const verifyUrl = `${process.env.NEXT_PUBLIC_APP_URL || 'http://localhost:3000'}/api/auth/verify?token=${verificationToken}`; + + let html = settings?.verifyEmailTemplate || ` +
+

{siteName}

+

Bonjour {name},

+

Vous avez demandé le renvoi de l'email de vérification pour votre compte sur {siteName}. Veuillez vérifier votre adresse email en cliquant sur le bouton ci-dessous :

+
+ Vérifier mon compte +
+
+ `; + + // Replace placeholders + html = html + .replace(/{name}/g, user.name) + .replace(/{verifyUrl}/g, verifyUrl) + .replace(/{siteName}/g, siteName); + + const { sendEmail } = await import('@/lib/mail'); + await sendEmail({ + to: email, + subject, + html, + }); + } catch (mailError) { + console.error('Failed to resend verification email:', mailError); + return NextResponse.json( + { error: 'Erreur lors de l\'envoi de l\'email' }, + { status: 500 } + ); + } + + return NextResponse.json( + { message: 'Un nouvel email de vérification a été envoyé.' }, + { status: 200 } + ); + + } catch (error) { + console.error('Resend verification error:', error); + return NextResponse.json( + { error: 'Une erreur est survenue' }, + { status: 500 } + ); + } +} diff --git a/app/login/page.tsx b/app/login/page.tsx index 4055b89..d5f04f2 100644 --- a/app/login/page.tsx +++ b/app/login/page.tsx @@ -13,7 +13,10 @@ const LoginPage = () => { const [email, setEmail] = useState(''); const [password, setPassword] = useState(''); const [error, setError] = useState(''); + const [isUnverified, setIsUnverified] = useState(false); const [loading, setLoading] = useState(false); + const [resendLoading, setResendLoading] = useState(false); + const [resendMessage, setResendMessage] = useState(''); const router = useRouter(); const searchParams = useSearchParams(); @@ -24,6 +27,8 @@ const LoginPage = () => { e.preventDefault(); setLoading(true); setError(''); + setIsUnverified(false); + setResendMessage(''); try { const res = await fetch('/api/auth/login', { @@ -35,6 +40,9 @@ const LoginPage = () => { const data = await res.json(); if (!res.ok) { + if (res.status === 403 && data.error?.includes('vérifier')) { + setIsUnverified(true); + } throw new Error(data.error || 'Erreur lors de la connexion'); } @@ -48,6 +56,33 @@ const LoginPage = () => { } }; + const handleResendVerification = async () => { + setResendLoading(true); + setError(''); + setResendMessage(''); + + try { + const res = await fetch('/api/auth/resend-verification', { + method: 'POST', + headers: { 'Content-Type': 'application/json' }, + body: JSON.stringify({ email }), + }); + + const data = await res.json(); + + if (!res.ok) { + throw new Error(data.error || "Erreur lors de l'envoi"); + } + + setResendMessage(data.message); + setIsUnverified(false); // Hide the resend link after successful send + } catch (err: any) { + setError(err.message); + } finally { + setResendLoading(false); + } + }; + return (
@@ -69,10 +104,29 @@ const LoginPage = () => {
)} + {resendMessage && ( +
+ +

{resendMessage}

+
+ )} + {(error || queryError) && ( -
- -

{error || queryError}

+
+
+ +

{error || queryError}

+
+ {isUnverified && ( + + )}
)}