Compare commits
No commits in common. "beeec4740e8db8bc0054debb7cd28200f0c6f66d" and "815986969aca71021f7d02cbf2c29a58c8c119f1" have entirely different histories.
beeec4740e
...
815986969a
4 changed files with 10 additions and 130 deletions
|
|
@ -42,7 +42,6 @@ const {
|
|||
role,
|
||||
tabindex,
|
||||
title,
|
||||
'data-modal-target': modalTarget,
|
||||
...dataAttrs // Все data-* атрибуты
|
||||
}: Props = Astro.props;
|
||||
|
||||
|
|
@ -92,26 +91,10 @@ const commonAttrs = Object.fromEntries(
|
|||
class={classes}
|
||||
disabled={disabled}
|
||||
{...commonAttrs}
|
||||
{...(modalTarget ? { 'data-modal-target': modalTarget } : {})}
|
||||
>
|
||||
<slot />
|
||||
</button>
|
||||
)}
|
||||
|
||||
{modalTarget && (
|
||||
<script define:vars={{ modalTarget }}>
|
||||
document.querySelectorAll(`[data-modal-target="${modalTarget}"]`).forEach(btn => {
|
||||
btn.addEventListener('click', () => {
|
||||
const modal = document.getElementById(modalTarget);
|
||||
if (modal) {
|
||||
modal.classList.add('active');
|
||||
modal.setAttribute('aria-hidden', 'false');
|
||||
document.body.style.overflow = 'hidden';
|
||||
}
|
||||
});
|
||||
});
|
||||
</script>
|
||||
)}
|
||||
)}
|
||||
|
||||
<style>
|
||||
.btn {
|
||||
|
|
|
|||
|
|
@ -17,9 +17,6 @@ const title = 'Бесплатная консультация';
|
|||
Оставьте свои контактные данные, и мы свяжемся с вами в течение 15 минут
|
||||
</p>
|
||||
|
||||
<!-- Honeypot поле для защиты от спама -->
|
||||
<input type="text" name="website" id="website-field" class="hidden-field" tabindex="-1" autocomplete="off" />
|
||||
|
||||
<form class="modal-form" action="#" method="POST" id="consultation-form">
|
||||
<div class="form-group">
|
||||
<label for="name" class="form-label">Ваше имя</label>
|
||||
|
|
@ -104,45 +101,11 @@ const title = 'Бесплатная консультация';
|
|||
});
|
||||
|
||||
// Обработка отправки формы
|
||||
form?.addEventListener('submit', async (e) => {
|
||||
form?.addEventListener('submit', (e) => {
|
||||
e.preventDefault();
|
||||
|
||||
const formData = new FormData(form);
|
||||
const website = formData.get('website');
|
||||
|
||||
if (website) {
|
||||
console.log('Spam detected');
|
||||
return;
|
||||
}
|
||||
|
||||
const name = formData.get('name');
|
||||
const phone = formData.get('phone');
|
||||
|
||||
if (!name || !phone) {
|
||||
alert('Пожалуйста, заполните имя и телефон');
|
||||
return;
|
||||
}
|
||||
|
||||
try {
|
||||
const response = await fetch('/api/consultation', {
|
||||
method: 'POST',
|
||||
headers: { 'Content-Type': 'application/json' },
|
||||
body: JSON.stringify({ name, phone })
|
||||
});
|
||||
|
||||
const result = await response.json();
|
||||
|
||||
if (result.success) {
|
||||
alert('Спасибо! Мы свяжемся с вами в течение 15 минут.');
|
||||
form.reset();
|
||||
// Здесь добавь логику отправки формы
|
||||
console.log('Форма отправлена');
|
||||
closeModal();
|
||||
} else {
|
||||
alert('Ошибка: ' + result.error);
|
||||
}
|
||||
} catch (error) {
|
||||
console.error('Error:', error);
|
||||
alert('Ошибка отправки. Попробуйте позже.');
|
||||
}
|
||||
});
|
||||
|
||||
// Для Astro View Transitions
|
||||
|
|
@ -310,13 +273,6 @@ const title = 'Бесплатная консультация';
|
|||
text-decoration: none;
|
||||
}
|
||||
|
||||
.hidden-field {
|
||||
position: absolute;
|
||||
left: -9999px;
|
||||
opacity: 0;
|
||||
pointer-events: none;
|
||||
}
|
||||
|
||||
@media (max-width: 480px) {
|
||||
.modal-content {
|
||||
padding: 32px 24px 24px;
|
||||
|
|
|
|||
|
|
@ -39,7 +39,7 @@ const {
|
|||
modalTarget,
|
||||
bgImage = "",
|
||||
minHeight = "100vh",
|
||||
headerOffset = "10px",
|
||||
headerOffset = "80px",
|
||||
layout = "default",
|
||||
sideImage = "",
|
||||
sideImageAlt = "",
|
||||
|
|
|
|||
|
|
@ -1,56 +1,11 @@
|
|||
import type { APIRoute } from 'astro';
|
||||
import { pb } from '../../lib/pb';
|
||||
|
||||
const RATE_LIMIT_WINDOW = 60 * 1000;
|
||||
const MAX_REQUESTS = 3;
|
||||
|
||||
const requestCounts = new Map<string, { count: number; timestamp: number }>();
|
||||
|
||||
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 });
|
||||
}
|
||||
|
||||
try {
|
||||
const data = await request.json();
|
||||
|
||||
const { name, phone, service, website } = data;
|
||||
|
||||
if (website) {
|
||||
return new Response(JSON.stringify({
|
||||
success: false,
|
||||
error: 'Спам обнаружен'
|
||||
}), { status: 400 });
|
||||
}
|
||||
const { name, phone, service } = data;
|
||||
|
||||
if (!name || !phone) {
|
||||
return new Response(JSON.stringify({
|
||||
|
|
@ -59,23 +14,9 @@ export const POST: APIRoute = async ({ request }) => {
|
|||
}), { status: 400 });
|
||||
}
|
||||
|
||||
if (name.length < 2 || name.length > 100) {
|
||||
return new Response(JSON.stringify({
|
||||
success: false,
|
||||
error: 'Некорректное имя'
|
||||
}), { status: 400 });
|
||||
}
|
||||
|
||||
if (!validatePhone(phone)) {
|
||||
return new Response(JSON.stringify({
|
||||
success: false,
|
||||
error: 'Некорректный номер телефона'
|
||||
}), { status: 400 });
|
||||
}
|
||||
|
||||
const record = await pb.collection('consultations').create({
|
||||
name: name.trim(),
|
||||
phone: phone.replace(/\D/g, ''),
|
||||
name,
|
||||
phone,
|
||||
service: service || '',
|
||||
status: 'new',
|
||||
created_at: new Date().toISOString(),
|
||||
|
|
|
|||
Loading…
Add table
Add a link
Reference in a new issue