40 lines
1.3 KiB
TypeScript
40 lines
1.3 KiB
TypeScript
"use server";
|
|
|
|
import { headers } from "next/headers";
|
|
import { prisma } from "@/lib/db";
|
|
import { recordAnalyticsEvent } from "@/lib/analytics";
|
|
import { sendContactMessageEmail } from "@/lib/email";
|
|
import { consumeContactRateLimit, getClientIp } from "@/lib/rate-limit";
|
|
import { contactSchema } from "@/lib/validations/contact";
|
|
|
|
export type ContactActionResult = {
|
|
success?: true;
|
|
warning?: "email-not-sent";
|
|
error?: "invalid" | "rate-limit";
|
|
};
|
|
|
|
export async function submitContactMessage(input: unknown): Promise<ContactActionResult> {
|
|
const parsed = contactSchema.safeParse(input);
|
|
if (!parsed.success) return { error: "invalid" };
|
|
if (parsed.data.website) return { success: true };
|
|
|
|
const ip = getClientIp(headers());
|
|
if (!consumeContactRateLimit(`contact:${ip}`)) return { error: "rate-limit" };
|
|
|
|
const { name, email, message } = parsed.data;
|
|
await prisma.contactMessage.create({ data: { name, email, message } });
|
|
try {
|
|
await recordAnalyticsEvent({ type: "CONTACT_SUBMITTED", path: "/contact" });
|
|
} catch (error) {
|
|
console.error("Contact analytics failed", error);
|
|
}
|
|
|
|
try {
|
|
await sendContactMessageEmail({ name, email, message });
|
|
return { success: true };
|
|
} catch (error) {
|
|
console.error("Contact email delivery failed", error);
|
|
return { success: true, warning: "email-not-sent" };
|
|
}
|
|
}
|