import { type NextRequest, NextResponse } from "next/server"; import nodemailer from "nodemailer"; import SMTPTransport from "nodemailer/lib/smtp-transport"; import Mail from "nodemailer/lib/mailer"; import { checkRateLimit, getRateLimitHeaders, getClientIp, requireSessionAuth } from "@/lib/auth"; const B = { siteUrl: "https://dk0.dev", email: "contact@dk0.dev", mint: "#A7F3D0", sky: "#BAE6FD", purple: "#E9D5FF", red: "#EF4444", }; function escapeHtml(input: string): string { return input .replace(/&/g, "&") .replace(//g, ">") .replace(/"/g, """) .replace(/'/g, "'"); } function nl2br(input: string): string { return escapeHtml(input).replace(/\r\n|\r|\n/g, "
"); } function baseEmail(opts: { title: string; preheader: string; bodyHtml: string }): string { const sentAt = new Date().toLocaleString("de-DE", { year: "numeric", month: "long", day: "numeric", hour: "2-digit", minute: "2-digit", }); return ` ${escapeHtml(opts.title)}
${escapeHtml(opts.preheader)} · ${sentAt}
${escapeHtml(opts.title)}
dk0.dev
${opts.bodyHtml}
${B.siteUrl}
${B.email}
`; } function messageCard(label: string, html: string, accentColor: string = B.mint): string { return `
${label}
${html}
`; } function ctaButton(text: string, href: string): string { return `
${text}
`; } const emailTemplates = { welcome: { subject: "Vielen Dank für deine Nachricht! 👋", template: (name: string, originalMessage: string) => { const safeName = escapeHtml(name); return baseEmail({ title: `Danke, ${safeName}!`, preheader: "Nachricht erhalten", bodyHtml: `

Hey ${safeName},

danke für deine Nachricht — ich habe sie erhalten und melde mich so schnell wie möglich bei dir zurück. 🙌

${messageCard("Deine Nachricht", nl2br(originalMessage))} ${ctaButton("Portfolio ansehen →", B.siteUrl)}`, }); }, }, project: { subject: "Projekt-Anfrage erhalten! 🚀", template: (name: string, originalMessage: string) => { const safeName = escapeHtml(name); return baseEmail({ title: `Projekt-Anfrage: danke, ${safeName}!`, preheader: "Ich melde mich zeitnah", bodyHtml: `

Hey ${safeName},

mega — danke für die Projekt-Anfrage! Ich schaue mir alles an und melde mich bald mit Ideen und Rückfragen. 🚀

${messageCard("Deine Projekt-Anfrage", nl2br(originalMessage), B.sky)} ${ctaButton("Mein Portfolio ansehen →", B.siteUrl)}`, }); }, }, quick: { subject: "Danke für deine Nachricht! ⚡", template: (name: string, originalMessage: string) => { const safeName = escapeHtml(name); return baseEmail({ title: `Danke, ${safeName}!`, preheader: "Kurze Bestätigung", bodyHtml: `

Hey ${safeName},

kurze Bestätigung: deine Nachricht ist angekommen. Ich melde mich bald zurück. ⚡

${messageCard("Deine Nachricht", nl2br(originalMessage))}`, }); }, }, reply: { subject: "Antwort auf deine Nachricht 📧", template: (name: string, originalMessage: string, responseMessage: string) => { const safeName = escapeHtml(name); return baseEmail({ title: `Hey ${safeName}!`, preheader: "Antwort von Dennis", bodyHtml: `

Hey ${safeName},

ich habe mir deine Nachricht angeschaut — hier ist meine Antwort:

${messageCard("Antwort von Dennis", nl2br(responseMessage), B.mint)}
${messageCard("Deine ursprüngliche Nachricht", nl2br(originalMessage), "#2a2a2a")}
${ctaButton("Portfolio ansehen →", B.siteUrl)}`, }); }, }, }; export async function POST(request: NextRequest) { try { const isAdminRequest = request.headers.get("x-admin-request") === "true"; if (!isAdminRequest) return NextResponse.json({ error: "Admin access required" }, { status: 403 }); const authError = requireSessionAuth(request); if (authError) return authError; const ip = getClientIp(request); if (!checkRateLimit(ip, 10, 60000)) { return NextResponse.json( { error: "Rate limit exceeded" }, { status: 429, headers: { ...getRateLimitHeaders(ip, 10, 60000) } }, ); } const body = (await request.json()) as { to: string; name: string; template: 'welcome' | 'project' | 'quick' | 'reply'; originalMessage: string; response?: string; }; const { to, name, template, originalMessage, response } = body; if (!to || !name || !template || !originalMessage) { return NextResponse.json({ error: "Alle Felder sind erforderlich" }, { status: 400 }); } if (template === "reply" && (!response || !response.trim())) { return NextResponse.json({ error: "Antworttext ist erforderlich" }, { status: 400 }); } const emailRegex = /^[^\s@]+@[^\s@]+\.[^\s@]+$/; if (!emailRegex.test(to)) { return NextResponse.json({ error: "Ungültige E-Mail-Adresse" }, { status: 400 }); } if (!emailTemplates[template]) { return NextResponse.json({ error: "Ungültiges Template" }, { status: 400 }); } const user = process.env.MY_EMAIL ?? ""; const pass = process.env.MY_PASSWORD ?? ""; if (!user || !pass) { console.error("❌ Missing email/password environment variables"); return NextResponse.json({ error: "E-Mail-Server nicht konfiguriert" }, { status: 500 }); } const transportOptions: SMTPTransport.Options = { host: "mail.dk0.dev", port: 587, secure: false, requireTLS: true, auth: { type: "login", user, pass }, connectionTimeout: 30000, greetingTimeout: 30000, socketTimeout: 60000, tls: { rejectUnauthorized: false, ciphers: 'SSLv3' }, }; const transport = nodemailer.createTransport(transportOptions); try { await transport.verify(); } catch { return NextResponse.json({ error: "E-Mail-Server-Verbindung fehlgeschlagen" }, { status: 500 }); } const selectedTemplate = emailTemplates[template]; const html = template === "reply" ? emailTemplates.reply.template(name, originalMessage, response || "") : emailTemplates[template as Exclude].template(name, originalMessage); const mailOptions: Mail.Options = { from: `"Dennis Konkol" <${user}>`, to, replyTo: B.email, subject: selectedTemplate.subject, html, text: template === "reply" ? `Hey ${name}!\n\nAntwort:\n${response}\n\nDeine ursprüngliche Nachricht:\n${originalMessage}\n\n-- Dennis Konkol\n${B.siteUrl}` : `Hey ${name}!\n\nDanke für deine Nachricht:\n${originalMessage}\n\nIch melde mich bald!\n\n-- Dennis Konkol\n${B.siteUrl}`, }; const result = await new Promise((resolve, reject) => { transport.sendMail(mailOptions, (err, info) => { if (!err) resolve(info.response); else reject(err.message); }); }); return NextResponse.json({ message: "E-Mail erfolgreich gesendet", template, messageId: result }); } catch (err) { return NextResponse.json({ error: "Fehler beim Senden der E-Mail", details: err instanceof Error ? err.message : 'Unbekannter Fehler', }, { status: 500 }); } }