Files
mb-grid-solutions.com/app/api/contact/route.ts

173 lines
5.3 KiB
TypeScript

import { NextResponse } from "next/server";
import { getServerAppServices } from "@/lib/services/create-services.server";
import {
render,
ContactFormNotification,
ConfirmationMessage,
} from "@mintel/mail";
import React from "react";
import nodemailer from "nodemailer";
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 });
}
// 2. Email sending via standalone Nodemailer (bypassing Payload adapter)
try {
const { config } = await import("@/lib/config");
const clientName = "MB Grid Solutions";
// Robust recipient resolution
const recipients = Array.isArray(config.mail.recipients)
? config.mail.recipients.filter(Boolean)
: [];
const to =
recipients.length > 0
? recipients.join(",")
: process.env.CONTACT_RECIPIENT || "info@mb-grid-solutions.com";
logger.info("Instantiating standalone nodemailer transport");
const transporter = nodemailer.createTransport({
host: config.mail.host,
port: config.mail.port,
auth: {
user: config.mail.user,
pass: config.mail.pass,
},
});
logger.info("Preparing to send notification email", {
to,
host: config.mail.host,
});
// 2a. Notification to MB Grid
const notificationHtml = await render(
React.createElement(ContactFormNotification, {
name,
email,
message,
company,
}),
);
try {
const info = await transporter.sendMail({
from: config.mail.from,
to,
replyTo: email,
subject: `Kontaktanfrage von ${name}`,
html: notificationHtml,
});
logger.info("Notification email sent successfully", {
messageId: info?.messageId,
});
} catch (notifyError) {
logger.error("Failed to send notification email", {
error: notifyError,
});
throw notifyError; // Re-throw to be caught by the outer SMTP catch
}
// 2b. Confirmation to the User
try {
logger.info("Preparing to send confirmation email", { to: email });
const confirmationHtml = await render(
React.createElement(ConfirmationMessage, {
name,
clientName,
}),
);
const info = await transporter.sendMail({
from: config.mail.from,
to: email,
subject: `Ihre Kontaktanfrage bei ${clientName}`,
html: confirmationHtml,
});
logger.info("Confirmation email sent successfully", {
messageId: info?.messageId,
});
} catch (confirmError) {
logger.warn(
"Failed to send confirmation email, but notification was sent",
{
error:
confirmError instanceof Error
? confirmError.message
: String(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" });
return NextResponse.json(
{ error: "Systemfehler (E-Mail-Versand fehlgeschlagen)" },
{ status: 500 },
);
}
// 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 },
);
}
}