Some checks failed
Build & Deploy / 🔍 Prepare (push) Successful in 4s
Build & Deploy / 🧪 QA (push) Successful in 2m55s
Build & Deploy / 🏗️ Build (push) Successful in 11m40s
Build & Deploy / 🚀 Deploy (push) Failing after 8s
Build & Deploy / 🩺 Health Check (push) Has been skipped
Build & Deploy / 🔔 Notify (push) Successful in 1s
169 lines
5.2 KiB
TypeScript
169 lines
5.2 KiB
TypeScript
import { NextResponse } from "next/server";
|
|
import { getPayload } from "payload";
|
|
import configPromise from "@payload-config";
|
|
import { getServerAppServices } from "@/lib/services/create-services.server";
|
|
import {
|
|
render,
|
|
ContactFormNotification,
|
|
ConfirmationMessage,
|
|
} from "@mintel/mail";
|
|
import React from "react";
|
|
|
|
export async function POST(req: Request) {
|
|
const services = getServerAppServices();
|
|
const logger = services.logger.child({ action: "contact_submission" });
|
|
|
|
// Set analytics context from request headers for high-fidelity server-side tracking
|
|
if (services.analytics.setServerContext) {
|
|
services.analytics.setServerContext({
|
|
userAgent: req.headers.get("user-agent") || undefined,
|
|
language: req.headers.get("accept-language")?.split(",")[0] || undefined,
|
|
referrer: req.headers.get("referer") || undefined,
|
|
ip: req.headers.get("x-forwarded-for")?.split(",")[0] || undefined,
|
|
});
|
|
}
|
|
|
|
try {
|
|
const { name, email, company, message, website } = await req.json();
|
|
|
|
// Track attempt
|
|
services.analytics.track("contact-form-attempt");
|
|
|
|
// Honeypot check
|
|
if (website) {
|
|
logger.info("Spam detected (honeypot)");
|
|
return NextResponse.json({ message: "Ok" });
|
|
}
|
|
|
|
// Validation
|
|
if (!name || name.length < 2 || name.length > 100) {
|
|
return NextResponse.json({ error: "Ungültiger Name" }, { status: 400 });
|
|
}
|
|
if (!email || !/^\S+@\S+\.\S+$/.test(email)) {
|
|
return NextResponse.json({ error: "Ungültige E-Mail" }, { status: 400 });
|
|
}
|
|
if (!message || message.length < 20) {
|
|
return NextResponse.json({ error: "message_too_short" }, { status: 400 });
|
|
}
|
|
if (message.length > 4000) {
|
|
return NextResponse.json({ error: "message_too_long" }, { status: 400 });
|
|
}
|
|
|
|
const payload = await getPayload({ config: configPromise });
|
|
|
|
// 1. Payload save
|
|
let payloadSaved = false;
|
|
try {
|
|
await payload.create({
|
|
collection: "form-submissions",
|
|
data: {
|
|
name,
|
|
email,
|
|
company: company || "Nicht angegeben",
|
|
message,
|
|
},
|
|
});
|
|
logger.info("Contact submission saved to PayloadCMS");
|
|
payloadSaved = true;
|
|
} catch (payloadError) {
|
|
const errorMessage =
|
|
payloadError instanceof Error
|
|
? payloadError.message
|
|
: String(payloadError);
|
|
logger.error("Failed to save to Payload", {
|
|
error: errorMessage,
|
|
details: payloadError,
|
|
});
|
|
services.errors.captureException(payloadError, { phase: "payload_save" });
|
|
}
|
|
|
|
// 2. Email sending via Payload (which uses configured nodemailer)
|
|
try {
|
|
const { config } = await import("@/lib/config");
|
|
const clientName = "MB Grid Solutions";
|
|
|
|
// 2a. Notification to MB Grid
|
|
const notificationHtml = await render(
|
|
React.createElement(ContactFormNotification, {
|
|
name,
|
|
email,
|
|
message,
|
|
company,
|
|
}),
|
|
);
|
|
|
|
await payload.sendEmail({
|
|
from: config.mail.from,
|
|
to:
|
|
config.mail.recipients.join(",") ||
|
|
process.env.CONTACT_RECIPIENT ||
|
|
"info@mb-grid-solutions.com",
|
|
replyTo: email,
|
|
subject: `Kontaktanfrage von ${name}`,
|
|
html: notificationHtml,
|
|
});
|
|
|
|
// 2b. Confirmation to the User
|
|
try {
|
|
const confirmationHtml = await render(
|
|
React.createElement(ConfirmationMessage, {
|
|
name,
|
|
clientName,
|
|
}),
|
|
);
|
|
|
|
await payload.sendEmail({
|
|
from: config.mail.from,
|
|
to: email,
|
|
subject: `Ihre Kontaktanfrage bei ${clientName}`,
|
|
html: confirmationHtml,
|
|
});
|
|
} catch (confirmError) {
|
|
logger.warn(
|
|
"Failed to send confirmation email, but notification was sent",
|
|
{ error: confirmError },
|
|
);
|
|
}
|
|
|
|
logger.info("Emails sent successfully");
|
|
|
|
// Notify success for important leads
|
|
await services.notifications.notify({
|
|
title: "📩 Neue Kontaktanfrage",
|
|
message: `Anfrage von ${name} (${email}) erhalten.\nFirma: ${company || "Nicht angegeben"}`,
|
|
priority: 5,
|
|
});
|
|
} catch (smtpError) {
|
|
logger.error("SMTP Error", { error: smtpError });
|
|
services.errors.captureException(smtpError, { phase: "smtp_send" });
|
|
|
|
if (!payloadSaved) {
|
|
return NextResponse.json(
|
|
{ error: "Systemfehler (Speicherung und Versand fehlgeschlagen)" },
|
|
{ status: 500 },
|
|
);
|
|
}
|
|
|
|
await services.notifications.notify({
|
|
title: "🚨 SMTP Fehler (Kontaktformular)",
|
|
message: `Anfrage von ${name} (${email}) in Payload gespeichert, aber E-Mail-Versand fehlgeschlagen: ${smtpError instanceof Error ? smtpError.message : String(smtpError)}`,
|
|
priority: 8,
|
|
});
|
|
}
|
|
|
|
// Track success
|
|
services.analytics.track("contact-form-success", {
|
|
has_company: Boolean(company),
|
|
});
|
|
|
|
return NextResponse.json({ message: "Ok" });
|
|
} catch (error) {
|
|
logger.error("Global API Error", { error });
|
|
services.errors.captureException(error, { phase: "api_global" });
|
|
return NextResponse.json(
|
|
{ error: "Interner Serverfehler" },
|
|
{ status: 500 },
|
|
);
|
|
}
|
|
}
|