feature: create post

This commit is contained in:
libertyoms
2025-04-22 08:50:55 +02:00
parent a8c6b5297b
commit 43f867cfe4
8 changed files with 518 additions and 12 deletions

148
actions/contact.ts Normal file
View File

@ -0,0 +1,148 @@
"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 ---
}