mirror of
https://github.com/OwethuManagedServices/oms-website-nextjs.git
synced 2025-12-17 17:18:09 +00:00
149 lines
5.1 KiB
TypeScript
149 lines
5.1 KiB
TypeScript
"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<boolean> {
|
|
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<ContactFormState> {
|
|
// 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: `<p><strong>Name:</strong> ${name}</p>
|
|
<p><strong>Email:</strong> <a href="mailto:${email}">${email}</a></p>
|
|
<hr>
|
|
<p><strong>Message:</strong></p>
|
|
<p>${message.replace(/\n/g, "<br>")}</p>`, // 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 ---
|
|
}
|