finlion-website/src/app/api/contact/route.ts

120 lines
3.4 KiB
TypeScript

import { NextResponse } from 'next/server'
import nodemailer from 'nodemailer'
import { rateLimit } from '@/lib/rate-limit'
interface FormData {
name: string
email: string
service: string
message: string
}
const validateEmail = (email: string): boolean => {
const pattern = /^[a-zA-Z0-9._%+-]+@[a-zA-Z0-9.-]+\.[a-zA-Z]{2,}$/
return pattern.test(email)
}
const validateInput = (data: FormData): { isValid: boolean; error?: string } => {
if (!data.name || data.name.length < 2 || data.name.length > 50) {
return { isValid: false, error: 'Invalid name length' }
}
if (!data.email || !validateEmail(data.email)) {
return { isValid: false, error: 'Invalid email format' }
}
if (!data.service) {
return { isValid: false, error: 'Service selection required' }
}
if (!data.message || data.message.length < 10 || data.message.length > 1000) {
return { isValid: false, error: 'Invalid message length' }
}
return { isValid: true }
}
const sanitizeInput = (input: string): string => {
return input
.trim()
.replace(/[<>]/g, '') // Basic XSS prevention
.slice(0, 1000) // Length limitation
}
export async function POST(req: Request) {
try {
// Rate limiting check
const rateLimitResult = await rateLimit()
if (!rateLimitResult.success) {
return NextResponse.json(
{ success: false, message: 'Too many requests. Please try again later.' },
{ status: 429 }
)
}
const data: FormData = await req.json()
// Input validation
const validation = validateInput(data)
if (!validation.isValid) {
return NextResponse.json(
{ success: false, message: validation.error },
{ status: 400 }
)
}
// Sanitize inputs
const sanitizedData = {
name: sanitizeInput(data.name),
email: sanitizeInput(data.email),
service: sanitizeInput(data.service),
message: sanitizeInput(data.message)
}
const transporter = nodemailer.createTransport({
host: process.env.SMTP_HOST,
port: Number(process.env.SMTP_PORT),
secure: process.env.SMTP_SECURE === 'true',
requireTLS: true,
auth: {
user: process.env.GOOGLE_EMAIL,
pass: process.env.GOOGLE_APP_PASSWORD,
},
})
const mailOptions = {
from: {
name: 'NO REPLY',
address: process.env.GOOGLE_EMAIL!
},
to: process.env.RECIPIENT_EMAIL!,
subject: `New Contact Form Submission - ${sanitizedData.service}`,
html: `
<div style="font-family: Arial, sans-serif; max-width: 600px; margin: 0 auto;">
<h2 style="color: #2563eb;">New Contact Form Submission</h2>
<div style="background-color: #f3f4f6; padding: 20px; border-radius: 8px;">
<p><strong>Name:</strong> ${sanitizedData.name}</p>
<p><strong>Email:</strong> ${sanitizedData.email}</p>
<p><strong>Service:</strong> ${sanitizedData.service}</p>
<p><strong>Message:</strong></p>
<p style="white-space: pre-wrap;">${sanitizedData.message}</p>
</div>
</div>
`,
}
await transporter.sendMail(mailOptions)
return NextResponse.json({
success: true,
message: 'Email sent successfully'
})
} catch (error) {
console.error('Error sending email:', error)
return NextResponse.json(
{ success: false, message: 'Failed to send email' },
{ status: 500 }
)
}
}