Files
afrov2/app/api/auth/register/route.ts
2026-06-08 21:23:39 +02:00

168 lines
7.3 KiB
TypeScript

import { NextRequest, NextResponse } from 'next/server';
import prisma from '@/lib/prisma';
import bcrypt from 'bcryptjs';
import crypto from 'crypto';
import { verifyCsrfToken, verifyOrigin } from '@/lib/csrf';
export async function POST(request: NextRequest) {
// CSRF Protection
const isOriginValid = verifyOrigin(request);
const isCsrfValid = await verifyCsrfToken(request);
if (!isOriginValid || !isCsrfValid) {
return NextResponse.json(
{ error: 'Protection CSRF : Requête invalide' },
{ status: 403 }
);
}
try {
const rawBody = await request.json();
const name = rawBody.name;
const email = rawBody.email?.toLowerCase().trim();
const password = rawBody.password;
// Basic validation
if (!name || !email || !password) {
return NextResponse.json(
{ error: 'Tous les champs sont obligatoires' },
{ status: 400 }
);
}
if (password.length < 6) {
return NextResponse.json(
{ error: 'Le mot de passe doit contenir au moins 6 caractères' },
{ status: 400 }
);
}
// Check if user already exists
const existingUser = await prisma.user.findUnique({
where: { email }
});
if (existingUser) {
return NextResponse.json(
{ error: 'Un utilisateur avec cet email existe déjà' },
{ status: 400 }
);
}
// Hash password with 10 rounds to match seed and system verification standards
const hashedPassword = await bcrypt.hash(password, 10);
// Generate verification token
const verificationToken = crypto.randomBytes(32).toString('hex');
// Create user
const user = await prisma.user.create({
data: {
name,
email,
password: hashedPassword,
role: 'VISITOR', // Default role
verificationToken,
emailVerified: process.env.NODE_ENV !== 'production', // Auto-vérifié en développement
}
});
// 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 || `
<div style="font-family: sans-serif; max-width: 600px; margin: 0 auto; padding: 20px; border: 1px solid #e5e7eb; border-radius: 8px;">
<h2 style="color: #0d9488; text-align: center;">${siteName}</h2>
<p>Bonjour ${name},</p>
<p>Merci de vous être inscrit sur ${siteName}. Veuillez vérifier votre adresse email en cliquant sur le bouton ci-dessous :</p>
<div style="text-align: center; margin: 30px 0;">
<a href="${verifyUrl}" style="background-color: #0d9488; color: white; padding: 12px 24px; text-decoration: none; border-radius: 6px; font-weight: bold;">Vérifier mon compte</a>
</div>
</div>
`;
// Replace placeholders
html = html
.replace(/{name}/g, name)
.replace(/{verifyUrl}/g, verifyUrl)
.replace(/{siteName}/g, siteName);
const { sendEmail } = await import('@/lib/mail');
// Send user verification email
await sendEmail({
to: email,
subject,
html,
});
// Send admin notification email
try {
const adminEmail = settings?.contactEmail || 'support@afrohub.com';
const adminSubject = `[Admin] Nouvelle inscription sur ${siteName} : ${name}`;
const adminHtml = `
<div style="font-family: sans-serif; max-width: 600px; margin: 0 auto; padding: 20px; border: 1px solid #e5e7eb; border-radius: 8px;">
<h2 style="color: #4f46e5; text-align: center;">Notification Admin - Inscription</h2>
<p>Bonjour,</p>
<p>Un nouvel utilisateur vient de s'inscrire sur <strong>${siteName}</strong> :</p>
<table style="width: 100%; border-collapse: collapse; margin-top: 15px;">
<tr>
<td style="padding: 8px; border-bottom: 1px solid #e5e7eb; font-weight: bold; width: 35%;">Nom complet :</td>
<td style="padding: 8px; border-bottom: 1px solid #e5e7eb;">${name}</td>
</tr>
<tr>
<td style="padding: 8px; border-bottom: 1px solid #e5e7eb; font-weight: bold;">Adresse email :</td>
<td style="padding: 8px; border-bottom: 1px solid #e5e7eb;"><a href="mailto:${email}">${email}</a></td>
</tr>
<tr>
<td style="padding: 8px; border-bottom: 1px solid #e5e7eb; font-weight: bold;">Date d'inscription :</td>
<td style="padding: 8px; border-bottom: 1px solid #e5e7eb;">${new Date().toLocaleString('fr-FR')}</td>
</tr>
<tr>
<td style="padding: 8px; border-bottom: 1px solid #e5e7eb; font-weight: bold;">Rôle par défaut :</td>
<td style="padding: 8px; border-bottom: 1px solid #e5e7eb;">VISITOR</td>
</tr>
</table>
<p style="margin-top: 25px; text-align: center;">
<a href="${process.env.NEXT_PUBLIC_APP_URL || 'http://localhost:3000'}/admin" style="background-color: #4f46e5; color: white; padding: 12px 24px; text-decoration: none; border-radius: 6px; font-weight: bold; display: inline-block;">Accéder au Dashboard Admin</a>
</p>
</div>
`;
await sendEmail({
to: adminEmail,
subject: adminSubject,
html: adminHtml,
});
} catch (adminMailError) {
console.error('Failed to send admin signup notification email:', adminMailError);
}
} catch (mailError) {
console.error('Failed to send registration email(s):', mailError);
}
// Omit password from response
const { password: _, ...userWithoutPassword } = user;
return NextResponse.json(
{ message: 'Compte créé avec succès', user: userWithoutPassword },
{ status: 201 }
);
} catch (error) {
console.error('Registration error:', error);
return NextResponse.json(
{ error: 'Une erreur est survenue lors de l\'inscription' },
{ status: 500 }
);
}
}