import { NextResponse } from "next/server"; import * as nodemailer from "nodemailer"; import directus, { ensureAuthenticated } from "@/lib/directus"; import { createItem } from "@directus/sdk"; import { getServerAppServices } from "@/lib/services/create-services.server"; 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 // This fulfills the "server-side via nextjs proxy" requirement 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 }); } // 1. Directus save let directusSaved = false; try { await ensureAuthenticated(); await directus.request( createItem("contact_submissions", { name, email, company: company || "Nicht angegeben", message, }), ); logger.info("Contact submission saved to Directus"); directusSaved = true; } catch (directusError) { const errorMessage = directusError instanceof Error ? directusError.message : String(directusError); logger.error("Failed to save to Directus", { error: errorMessage, details: directusError, }); services.errors.captureException(directusError, { phase: "directus_save", }); // We still try to send the email even if Directus fails } // 2. Email sending try { const { config } = await import("@/lib/config"); const transporter = nodemailer.createTransport({ host: config.mail.host, port: config.mail.port, secure: config.mail.port === 465, auth: { user: config.mail.user, pass: config.mail.pass, }, }); await transporter.sendMail({ from: config.mail.from, to: config.mail.recipients.join(",") || "info@mb-grid-solutions.com", replyTo: email, subject: `Kontaktanfrage von ${name}`, text: ` Name: ${name} Firma: ${company || "Nicht angegeben"} E-Mail: ${email} Zeitpunkt: ${new Date().toISOString()} Nachricht: ${message} `, }); logger.info("Email 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 Directus failed AND SMTP failed, then we really have a problem if (!directusSaved) { return NextResponse.json( { error: "Systemfehler (Speicherung und Versand fehlgeschlagen)" }, { status: 500 }, ); } // If Directus was successful, we tell the user "Ok" but we know internally it was a partial failure await services.notifications.notify({ title: "🚨 SMTP Fehler (Kontaktformular)", message: `Anfrage von ${name} (${email}) in Directus 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 }, ); } }