"use server"; import { z } from "zod"; import nodemailer from "nodemailer"; import { GoogleGenerativeAI } from "@google/generative-ai"; // Define the schema for contact form validation const ContactSchema = z.object({ name: z.string().min(2, "Name must be at least 2 characters"), email: z.string().email("Invalid email address"), subject: z.string().min(5, "Subject must be at least 5 characters"), message: z.string().min(10, "Message must be at least 10 characters"), }); // Define the state structure for the form action export type ContactFormState = { errors?: { name?: string[]; email?: string[]; subject?: string[]; message?: string[]; _form?: string[]; // General form error }; message?: string | null; // Success or general error message success?: boolean; // Flag for successful submission }; // --- Initialize Gemini --- const genAI = new GoogleGenerativeAI(process.env.GEMINI_API_KEY || ""); const geminiModel = genAI.getGenerativeModel({ model: "gemini-2.0-flash" }); // Or other suitable model // --- Initialize Nodemailer Transporter --- const transporter = nodemailer.createTransport({ host: process.env.EMAIL_SERVER_HOST, port: parseInt(process.env.EMAIL_SERVER_PORT || "587"), // Default to 587 secure: parseInt(process.env.EMAIL_SERVER_PORT || "587") === 465, // true for 465, false for other ports auth: { user: process.env.EMAIL_SERVER_USER, pass: process.env.EMAIL_SERVER_PASSWORD, }, }); // --- Helper function for Spam Check --- async function isSpamOrAdvertisement(content: { subject: string; message: string; email: string; }): Promise { if (!process.env.GEMINI_API_KEY) { console.warn("GEMINI_API_KEY not set. Skipping spam check."); return false; // Skip check if API key is missing } const prompt = `Analyze the following email content and classify it as "spam", "advertisement", or "legitimate inquiry". Consider the subject, message body, and sender email. Provide only the classification word as the response. Subject: ${content.subject} Sender Email: ${content.email} Message: ${content.message}`; try { const result = await geminiModel.generateContent(prompt); const response = await result.response; const text = response.text().trim().toLowerCase(); console.log("Gemini Classification:", text); // Log classification for debugging // Consider "spam" or "advertisement" as unwanted return text === "spam" || text === "advertisement"; } catch (error) { console.error("Error checking content with Gemini:", error); // Fail open (treat as not spam) if Gemini check fails return false; } } // Server action to process the contact form export async function submitContactForm( prevState: ContactFormState, formData: FormData ): Promise { // Validate form data const validatedFields = ContactSchema.safeParse({ name: formData.get("name"), email: formData.get("email"), subject: formData.get("subject"), message: formData.get("message"), }); // If validation fails, return errors if (!validatedFields.success) { console.error( "Contact Form Validation Errors:", validatedFields.error.flatten().fieldErrors ); return { errors: validatedFields.error.flatten().fieldErrors, message: "Please correct the errors above.", success: false, }; } const { name, email, subject, message } = validatedFields.data; // --- Spam/Advertisement Check --- try { const isUnwanted = await isSpamOrAdvertisement({ subject, message, email }); if (isUnwanted) { console.log(`Message from ${email} flagged as spam/advertisement.`); // Return generic error as requested return { message: "Message could not be sent.", success: false }; } } catch (error) { // Log error but proceed if check fails unexpectedly console.error("Error during spam check:", error); } // --- End Spam Check --- // --- Send Email using Nodemailer --- const mailOptions = { from: process.env.EMAIL_FROM, // Sender address (configured in .env) to: process.env.EMAIL_TO, // List of receivers (configured in .env) replyTo: email, // Set reply-to to the user's email subject: `Website Contact: ${subject}`, // Subject line text: `Name: ${name}\nEmail: ${email}\n\nMessage:\n${message}`, // Plain text body html: `

Name: ${name}

Email: ${email}


Message:

${message.replace(/\n/g, "
")}

`, // HTML body }; try { await transporter.sendMail(mailOptions); console.log("Contact email sent successfully from:", email); return { message: "Thank you for your message! We'll get back to you soon.", success: true, }; } catch (error) { console.error("Failed to send contact email:", error); return { message: "Failed to send message due to a server error. Please try again later.", success: false, errors: { _form: ["Email sending failed."] }, }; } // --- End Send Email --- }