feat: add Turnstile anti-bot, email field, and metadata to contact form

- Add Cloudflare Turnstile widget support (site key configurable in config.js,
  secret key in contact.php — both empty until widget created at dash.cloudflare.com)
- Add email input field to contact form (parity with hhivp)
- Add company length validation (200 chars) to contact.php
- Add IP, country (CF-IPCountry header), and referer metadata to Telegram notifications
- Add company and email fields to SMTP email body
- Turnstile script loaded in index.html, widget rendered conditionally when TURNSTILE_SITE_KEY is set

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
This commit is contained in:
2026-03-15 14:20:23 +03:00
parent a42007e0a5
commit c155ff366c
6 changed files with 68 additions and 6 deletions

View File

@@ -37,9 +37,11 @@ if (!$data) {
}
$name = htmlspecialchars(trim($data['name'] ?? ''), ENT_QUOTES);
$company = htmlspecialchars(trim($data['company'] ?? ''), ENT_QUOTES);
$phone = htmlspecialchars(trim($data['phone'] ?? ''), ENT_QUOTES);
$email = htmlspecialchars(trim($data['email'] ?? ''), ENT_QUOTES);
$message = htmlspecialchars(trim($data['message'] ?? ''), ENT_QUOTES);
$turnstileToken = trim($data['turnstileToken'] ?? '');
if (!$name || !$message) {
http_response_code(400);
@@ -54,20 +56,54 @@ if ($email && !filter_var($email, FILTER_VALIDATE_EMAIL)) {
exit;
}
if (mb_strlen($name) > 100) { http_response_code(400); echo json_encode(['error' => 'Name too long']); exit; }
if (mb_strlen($company) > 200) { http_response_code(400); echo json_encode(['error' => 'Company too long']); exit; }
if (mb_strlen($email) > 254) { http_response_code(400); echo json_encode(['error' => 'Email too long']); exit; }
if (mb_strlen($phone) > 30) { http_response_code(400); echo json_encode(['error' => 'Phone too long']); exit; }
if (mb_strlen($message) > 5000) { http_response_code(400); echo json_encode(['error' => 'Message too long']); exit; }
// ─── Cloudflare Turnstile verification ───────────────────────────────────────
$TURNSTILE_SECRET = ''; // TODO: Create widget at dash.cloudflare.com → Turnstile → Add site (sag24.ru)
if ($TURNSTILE_SECRET) {
if (!$turnstileToken) {
http_response_code(400);
echo json_encode(['error' => 'Bot verification required.']);
exit;
}
$ch = curl_init('https://challenges.cloudflare.com/turnstile/v0/siteverify');
curl_setopt_array($ch, [
CURLOPT_POST => true,
CURLOPT_POSTFIELDS => http_build_query(['secret' => $TURNSTILE_SECRET, 'response' => $turnstileToken, 'remoteip' => $ip]),
CURLOPT_RETURNTRANSFER => true,
CURLOPT_TIMEOUT => 10,
]);
$tsResult = json_decode(curl_exec($ch), true);
curl_close($ch);
if (($tsResult['success'] ?? false) !== true) {
http_response_code(400);
echo json_encode(['error' => 'Bot verification failed. Please try again.']);
exit;
}
}
// ─── Telegram (best-effort, not fatal) ───────────────────────────────────────
$BOT_TOKEN = '8138813013:AAElH2L5NspRLSdiFjDz6Qf32n4G24P_cj8';
$CHAT_ID = '-5230603582';
$userAgent = $_SERVER['HTTP_USER_AGENT'] ?? 'unknown';
$referer = $_SERVER['HTTP_REFERER'] ?? '';
$country = $_SERVER['HTTP_CF_IPCOUNTRY'] ?? '';
$text = "🔔 <b>Заявка с sag24.ru</b>\n\n";
$text .= "👤 <b>Имя:</b> {$name}\n";
if ($phone) $text .= "📱 <b>Телефон:</b> {$phone}\n";
if ($company) $text .= "🏢 <b>Компания:</b> {$company}\n";
if ($email) $text .= "📧 <b>Email:</b> {$email}\n";
if ($phone) $text .= "📱 <b>Телефон:</b> {$phone}\n";
$text .= "\n💬 <b>Сообщение:</b>\n{$message}\n";
$text .= "\n" . date('d.m.Y H:i', time() + 3 * 3600) . " MSK";
$text .= "\n📊 <b>Метаданные:</b>\n";
$text .= "🌍 IP: {$ip}\n";
if ($country) $text .= "🌐 Страна: {$country}\n";
if ($referer) $text .= "🔗 Источник: {$referer}\n";
$text .= "" . date('d.m.Y H:i', time() + 3 * 3600) . " MSK";
$ch = curl_init("https://tg-relay.it-resheniya-2018.workers.dev/bot{$BOT_TOKEN}/sendMessage");
curl_setopt_array($ch, [
@@ -89,9 +125,12 @@ $mail_to = 'info@sag24.ru';
$subject = "=?UTF-8?B?" . base64_encode("Заявка с sag24.ru от {$name}") . "?=";
$body = "Имя: {$name}\r\n";
if ($phone) $body .= "Телефон: {$phone}\r\n";
if ($company) $body .= "Компания: {$company}\r\n";
if ($email) $body .= "Email: {$email}\r\n";
if ($phone) $body .= "Телефон: {$phone}\r\n";
$body .= "\r\nСообщение:\r\n{$message}\r\n";
$body .= "\r\nIP: {$ip}";
if ($country) $body .= " | Страна: {$country}";
$body .= "\r\n" . date('d.m.Y H:i', time() + 3 * 3600) . " MSK";
try {