import { NextRequest, NextResponse } from 'next/server'; import { supabaseRateLimiter } from '@/utils/rateLimitSupabase'; interface ContactFormData { name: string; email: string; message: string; } /** * Extract client IP address from Next.js request headers * Handles Vercel's forwarding headers and fallback scenarios */ function getClientIp(request: NextRequest): string { // Check Vercel's forwarded IP header first const forwardedFor = request.headers.get('x-forwarded-for'); if (forwardedFor) { // x-forwarded-for may contain multiple IPs, take the first one return forwardedFor.split(',')[0].trim(); } // Check for real IP header const realIp = request.headers.get('x-real-ip'); if (realIp) { return realIp; } // Fallback to a default identifier for development return 'unknown-ip'; } /** * Validate contact form data */ function validateFormData(data: unknown): data is ContactFormData { if (!data || typeof data !== 'object') { return false; } const formData = data as Record; return ( typeof formData.name === 'string' && formData.name.trim().length > 0 && typeof formData.email === 'string' && /^[^\s@]+@[^\s@]+\.[^\s@]+$/.test(formData.email) && typeof formData.message === 'string' && formData.message.trim().length > 0 ); } /** * POST /api/contact * Handle contact form submissions with rate limiting */ export async function POST(request: NextRequest) { try { // Extract client IP for rate limiting const clientIp = getClientIp(request); // Check rate limit const isRateLimited = await supabaseRateLimiter.isRateLimited(clientIp); if (isRateLimited) { const timeToReset = await supabaseRateLimiter.getTimeToReset(clientIp); const retryAfterSeconds = Math.ceil(timeToReset / 1000); return NextResponse.json( { error: 'Too many requests. Please try again later.', retryAfter: retryAfterSeconds, }, { status: 429, headers: { 'X-RateLimit-Remaining': '0', 'Retry-After': retryAfterSeconds.toString(), }, } ); } // Parse and validate request body const body = await request.json(); if (!validateFormData(body)) { return NextResponse.json( { error: 'Invalid form data. Please check all fields.' }, { status: 400 } ); } const formData = body as ContactFormData; // Get remaining attempts for response headers const remainingAttempts = await supabaseRateLimiter.getRemainingAttempts(clientIp); // TODO: In a real implementation, you would: // 1. Send email via a service like SendGrid, Resend, or AWS SES // 2. Store the message in a database // 3. Send a confirmation email to the user // For now, we'll just simulate success // Simulate processing delay await new Promise(resolve => setTimeout(resolve, 500)); return NextResponse.json( { success: true, message: 'Your message has been received. We will get back to you soon!', }, { status: 200, headers: { 'X-RateLimit-Remaining': remainingAttempts.toString(), }, } ); } catch (error) { console.error('Error processing contact form:', error); return NextResponse.json( { error: 'An unexpected error occurred. Please try again later.' }, { status: 500 } ); } }