import nodemailer from "nodemailer"; import { NextRequest, NextResponse } from "next/server"; import { verifyCaptchaToken } from "../../../lib/contact-captcha"; const WINDOW_MS = 30 * 60 * 1000; const MAX_REQUESTS = 5; const MIN_FILL_MS = 3000; type RateLimitEntry = { count: number; resetAt: number; }; const rateLimitStore = globalThis as typeof globalThis & { __contactRateLimit?: Map; }; const requests = rateLimitStore.__contactRateLimit ?? new Map(); rateLimitStore.__contactRateLimit = requests; function getClientIp(request: NextRequest) { const forwardedFor = request.headers.get("x-forwarded-for"); if (forwardedFor) { return forwardedFor.split(",")[0]?.trim() ?? "unknown"; } return request.headers.get("x-real-ip") ?? "unknown"; } function isRateLimited(ip: string) { const now = Date.now(); const existing = requests.get(ip); if (!existing || existing.resetAt <= now) { requests.set(ip, { count: 1, resetAt: now + WINDOW_MS }); return false; } if (existing.count >= MAX_REQUESTS) { return true; } existing.count += 1; requests.set(ip, existing); return false; } export async function POST(request: NextRequest) { const ip = getClientIp(request); if (isRateLimited(ip)) { return NextResponse.json( { message: "Terlalu banyak percobaan. Silakan coba lagi beberapa saat." }, { status: 429 } ); } const body = (await request.json()) as { fullName?: string; email?: string; subject?: string; message?: string; website?: string; startedAt?: string; captchaAnswer?: string; captchaToken?: string; }; if (body.website) { return NextResponse.json({ message: "Permintaan ditolak." }, { status: 400 }); } const startedAt = Number(body.startedAt ?? 0); if (!startedAt || Date.now() - startedAt < MIN_FILL_MS) { return NextResponse.json( { message: "Form dikirim terlalu cepat. Silakan isi kembali dengan benar." }, { status: 400 } ); } const fullName = body.fullName?.trim(); const email = body.email?.trim(); const subject = body.subject?.trim(); const message = body.message?.trim(); const captchaAnswer = body.captchaAnswer?.trim(); const captchaToken = body.captchaToken?.trim(); if (!fullName || !email || !subject || !message) { return NextResponse.json( { message: "Semua field wajib diisi sebelum mengirim." }, { status: 400 } ); } if (!captchaAnswer || !captchaToken || !verifyCaptchaToken(captchaToken, captchaAnswer)) { return NextResponse.json( { message: "Captcha tidak valid atau sudah kedaluwarsa." }, { status: 400 } ); } const transporter = nodemailer.createTransport({ host: process.env.SMTP_HOST, port: Number(process.env.SMTP_PORT ?? 465), secure: process.env.SMTP_SECURE !== "false", auth: { user: process.env.SMTP_USER, pass: process.env.SMTP_PASS } }); try { await transporter.sendMail({ from: `"Kelola Bumi Contact Form" <${process.env.SMTP_USER}>`, to: process.env.CONTACT_TO_EMAIL, replyTo: email, subject: `[Kelola Bumi] ${subject}`, text: [ `Nama: ${fullName}`, `Email: ${email}`, `Subjek: ${subject}`, "", "Pesan:", message ].join("\n"), html: `

Pesan Baru dari Form Kontak Kelola Bumi

Nama: ${fullName}

Email: ${email}

Subjek: ${subject}

Pesan:

${message.replace(/\n/g, "
")}

` }); return NextResponse.json({ message: "Pesan berhasil dikirim. Tim kami akan menghubungi Anda." }); } catch { return NextResponse.json( { message: "Email gagal dikirim. Periksa konfigurasi mailer." }, { status: 500 } ); } }