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"; 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 }); } 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 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" }); 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 }, ); } }