import type { APIRoute } from 'astro'; import nodemailer from 'nodemailer'; const POCKETBASE_URL = import.meta.env.POCKETBASE_URL || 'http://localhost:8090'; const SMTP_HOST = import.meta.env.SMTP_HOST || 'localhost'; const SMTP_PORT = import.meta.env.SMTP_PORT || '1025'; const NOTIFY_EMAIL = import.meta.env.NOTIFY_EMAIL || 'info@avtourist.ru'; const transporter = nodemailer.createTransport({ host: SMTP_HOST, port: parseInt(SMTP_PORT), secure: false, ignoreTLS: true, }); const RATE_LIMIT_WINDOW = 60 * 1000; const MAX_REQUESTS = 3; const requestCounts = new Map(); function checkRateLimit(ip: string): boolean { const now = Date.now(); const record = requestCounts.get(ip); if (!record || now - record.timestamp > RATE_LIMIT_WINDOW) { requestCounts.set(ip, { count: 1, timestamp: now }); return true; } if (record.count >= MAX_REQUESTS) { return false; } record.count++; return true; } function validatePhone(phone: string): boolean { const cleaned = phone.replace(/\D/g, ''); return cleaned.length >= 10 && cleaned.length <= 15; } export const POST: APIRoute = async ({ request }) => { const clientIP = request.headers.get('x-forwarded-for')?.split(',')[0] || request.headers.get('x-real-ip') || 'unknown'; if (!checkRateLimit(clientIP)) { return new Response(JSON.stringify({ success: false, error: 'Слишком много запросов. Попробуйте позже.' }), { status: 429, headers: { 'Content-Type': 'application/json' } }); } try { const data = await request.json(); const { name, phone, website } = data; if (website) { return new Response(JSON.stringify({ success: false, error: 'Спам обнаружен' }), { status: 400, headers: { 'Content-Type': 'application/json' } }); } if (!name || !phone) { return new Response(JSON.stringify({ success: false, error: 'Имя и телефон обязательны' }), { status: 400, headers: { 'Content-Type': 'application/json' } }); } if (name.length < 2 || name.length > 100) { return new Response(JSON.stringify({ success: false, error: 'Некорректное имя' }), { status: 400, headers: { 'Content-Type': 'application/json' } }); } if (!validatePhone(phone)) { return new Response(JSON.stringify({ success: false, error: 'Некорректный номер телефона' }), { status: 400, headers: { 'Content-Type': 'application/json' } }); } const cleanPhone = phone.replace(/\D/g, ''); const response = await fetch( `${POCKETBASE_URL}/api/collections/consultations/records`, { method: 'POST', headers: { 'Content-Type': 'application/json', }, body: JSON.stringify({ name: name.trim(), phone: cleanPhone, status: 'new', }), } ); const result = await response.json(); if (!response.ok) { return new Response(JSON.stringify({ success: false, error: result.message || 'Ошибка при отправке заявки' }), { status: response.status, headers: { 'Content-Type': 'application/json' } }); } const emailHtml = `

Новая заявка на консультацию!

Имя: ${name}
Телефон: ${phone}
Дата: ${new Date().toLocaleString('ru-RU')}
`.trim(); try { await transporter.sendMail({ from: 'avtourist@surgut.ru', to: NOTIFY_EMAIL, subject: `Новая заявка от ${name}`, html: emailHtml, }); console.log('Email notification sent'); } catch (emailError) { console.error('Email send error:', emailError); } return new Response(JSON.stringify({ success: true, message: 'Заявка отправлена! Мы свяжемся с вами в течение 15 минут.', id: result.id }), { status: 201, headers: { 'Content-Type': 'application/json' } }); } catch (error: any) { console.error('Consultation error:', error); return new Response(JSON.stringify({ success: false, error: error.message || 'Ошибка при отправке заявки' }), { status: 400, headers: { 'Content-Type': 'application/json' } }); } };