import { NextRequest, NextResponse } from "next/server"; import { db } from "@/lib/db"; import nodemailer from "nodemailer"; // Create reusable transporter (lazy initialization) let transporter: nodemailer.Transporter | null = null; function getTransporter(): nodemailer.Transporter | null { if (transporter) return transporter; const smtpHost = process.env.SMTP_HOST; const smtpPort = process.env.SMTP_PORT; const smtpUser = process.env.SMTP_USER; const smtpPass = process.env.SMTP_PASS; const smtpFrom = process.env.SMTP_FROM || "ml-academy@noreply.com"; if (smtpHost && smtpUser && smtpPass) { transporter = nodemailer.createTransport({ host: smtpHost, port: parseInt(smtpPort || "587"), secure: parseInt(smtpPort || "587") === 465, auth: { user: smtpUser, pass: smtpPass }, }); transporter.verify().catch((err) => { console.error("SMTP verification failed:", err); transporter = null; }); } return transporter; } // GET /api/feedback — list all feedback (admin only, requires token) export async function GET(request: NextRequest) { try { const authHeader = request.headers.get("authorization"); const adminToken = process.env.ADMIN_PASSWORD || "ml-admin-2024"; if (!authHeader || authHeader !== `Bearer ${adminToken}`) { return NextResponse.json({ error: "Non autorisé" }, { status: 401 }); } const feedbacks = await db.feedback.findMany({ orderBy: { createdAt: "desc" }, }); return NextResponse.json(feedbacks); } catch (error) { console.error("Failed to fetch feedbacks:", error); return NextResponse.json( { error: "Failed to fetch feedbacks" }, { status: 500 } ); } } // POST /api/feedback — create feedback + send email notification export async function POST(request: NextRequest) { try { const body = await request.json(); const { name, email, subject, message } = body; // Validate required fields if (!name || !email || !subject || !message) { return NextResponse.json( { error: "Tous les champs sont obligatoires" }, { status: 400 } ); } // Validate name length if (name.trim().length < 2 || name.trim().length > 100) { return NextResponse.json( { error: "Le nom doit contenir entre 2 et 100 caractères" }, { status: 400 } ); } // Email format validation const emailRegex = /^[^\s@]+@[^\s@]+\.[^\s@]+$/; if (!emailRegex.test(email)) { return NextResponse.json( { error: "Format d'email invalide" }, { status: 400 } ); } // Validate subject length if (subject.trim().length < 3 || subject.trim().length > 200) { return NextResponse.json( { error: "Le sujet doit contenir entre 3 et 200 caractères" }, { status: 400 } ); } // Validate message length if (message.trim().length < 10 || message.trim().length > 5000) { return NextResponse.json( { error: "Le message doit contenir entre 10 et 5000 caractères" }, { status: 400 } ); } // Sanitize inputs const sanitizedName = name.trim(); const sanitizedEmail = email.trim().toLowerCase(); const sanitizedSubject = subject.trim(); const sanitizedMessage = message.trim(); // Save to database const feedback = await db.feedback.create({ data: { name: sanitizedName, email: sanitizedEmail, subject: sanitizedSubject, message: sanitizedMessage, }, }); // Attempt to send email notification const mailTransporter = getTransporter(); const recipientEmail = process.env.CONTACT_EMAIL || "imadmaalouf02@gmail.com"; if (mailTransporter) { try { await mailTransporter.sendMail({ from: process.env.SMTP_FROM || "IA Academy ", to: recipientEmail, replyTo: sanitizedEmail, subject: `[IA Academy] ${sanitizedSubject}`, html: `

IA Academy

Nouveau message de contact

Nom ${sanitizedName}
Email ${sanitizedEmail}
Sujet ${sanitizedSubject}
Message

${sanitizedMessage.replace(//g, '>')}

Message envoyé le ${new Date().toLocaleString('fr-FR', { dateStyle: 'full', timeStyle: 'short' })}

IA Academy — Plateforme de Machine Learning

`, }); // Send auto-reply to the user await mailTransporter.sendMail({ from: process.env.SMTP_FROM || "IA Academy ", to: sanitizedEmail, subject: `[IA Academy] Accusé de réception — ${sanitizedSubject}`, html: `

Message reçu !

IA Academy

Bonjour ${sanitizedName},

Nous avons bien reçu votre message concernant "${sanitizedSubject}". Merci de nous avoir contacté !

Nous vous répondrons dans les plus brefs délais. En attendant, n'hésitez pas à consulter nos cours et travaux pratiques.

Votre message :

"${sanitizedMessage.substring(0, 200)}${sanitizedMessage.length > 200 ? '...' : ''}"

Cordialement,
L'équipe IA Academy

${new Date().toLocaleString('fr-FR', { dateStyle: 'full' })}

`, }); return NextResponse.json({ ...feedback, emailSent: true, message: "Message envoyé avec succès. Un accusé de réception a été envoyé à votre adresse email.", }, { status: 201 }); } catch (mailError) { console.error("Failed to send email:", mailError); // Save succeeded but email failed — still return success return NextResponse.json({ ...feedback, emailSent: false, message: "Message enregistré. L'envoi de l'email a échoué, mais votre message a été sauvegardé.", }, { status: 201 }); } } // No SMTP configured — save to DB only return NextResponse.json({ ...feedback, emailSent: false, message: "Message enregistré avec succès dans notre base de données.", }, { status: 201 }); } catch (error) { console.error("Failed to create feedback:", error); return NextResponse.json( { error: "Échec de l'envoi du message" }, { status: 500 } ); } } // PATCH /api/feedback — mark feedback as read/unread (admin only) export async function PATCH(request: NextRequest) { try { const authHeader = request.headers.get("authorization"); const adminToken = process.env.ADMIN_PASSWORD || "ml-admin-2024"; if (!authHeader || authHeader !== `Bearer ${adminToken}`) { return NextResponse.json({ error: "Non autorisé" }, { status: 401 }); } const body = await request.json(); const { id, read } = body; if (!id) { return NextResponse.json({ error: "ID requis" }, { status: 400 }); } const feedback = await db.feedback.update({ where: { id }, data: { read: read !== undefined ? read : true }, }); return NextResponse.json(feedback); } catch (error) { console.error("Failed to update feedback:", error); return NextResponse.json( { error: "Échec de la mise à jour" }, { status: 500 } ); } } // DELETE /api/feedback — delete a feedback entry (admin only) export async function DELETE(request: NextRequest) { try { const authHeader = request.headers.get("authorization"); const adminToken = process.env.ADMIN_PASSWORD || "ml-admin-2024"; if (!authHeader || authHeader !== `Bearer ${adminToken}`) { return NextResponse.json({ error: "Non autorisé" }, { status: 401 }); } const { searchParams } = new URL(request.url); const id = searchParams.get("id"); if (!id) { return NextResponse.json({ error: "ID requis" }, { status: 400 }); } await db.feedback.delete({ where: { id } }); return NextResponse.json({ success: true }); } catch (error) { console.error("Failed to delete feedback:", error); return NextResponse.json( { error: "Échec de la suppression" }, { status: 500 } ); } }