'Method not allowed']); exit; } // ─── Rate limiting (5 requests per minute per IP) ──────────────────────────── $ip = $_SERVER['HTTP_X_FORWARDED_FOR'] ? explode(',', $_SERVER['HTTP_X_FORWARDED_FOR'])[0] : ($_SERVER['REMOTE_ADDR'] ?? 'unknown'); $ip = trim($ip); $rate_file = sys_get_temp_dir() . '/rl_' . md5($ip) . '.json'; $now = time(); $window = 60; $max_req = 5; $rate_data = file_exists($rate_file) ? json_decode(file_get_contents($rate_file), true) : []; $rate_data = array_filter($rate_data ?? [], fn($t) => $t > $now - $window); if (count($rate_data) >= $max_req) { http_response_code(429); echo json_encode(['error' => 'Too many requests. Please try again later.']); exit; } $rate_data[] = $now; file_put_contents($rate_file, json_encode(array_values($rate_data))); $data = json_decode(file_get_contents('php://input'), true); if (!$data) { http_response_code(400); echo json_encode(['error' => 'Invalid JSON']); exit; } $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); echo json_encode(['error' => 'Missing required fields']); exit; } // ─── Validation ─────────────────────────────────────────────────────────────── if ($email && !filter_var($email, FILTER_VALIDATE_EMAIL)) { http_response_code(400); echo json_encode(['error' => 'Invalid email format']); 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 = "🔔 Заявка с sag24.ru\n\n"; $text .= "👤 Имя: {$name}\n"; if ($company) $text .= "🏢 Компания: {$company}\n"; if ($email) $text .= "📧 Email: {$email}\n"; if ($phone) $text .= "📱 Телефон: {$phone}\n"; $text .= "\n💬 Сообщение:\n{$message}\n"; $text .= "\n📊 Метаданные:\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, [ CURLOPT_POST => true, CURLOPT_POSTFIELDS => json_encode(['chat_id' => $CHAT_ID, 'text' => $text, 'parse_mode' => 'HTML']), CURLOPT_HTTPHEADER => ['Content-Type: application/json'], CURLOPT_RETURNTRANSFER => true, CURLOPT_TIMEOUT => 10, ]); curl_exec($ch); curl_close($ch); // ─── Email via SMTP (noreply@sag24.ru → info@sag24.ru) ─────────────────────── $smtp_host = 'mx.hhivp.com'; $smtp_port = 587; $smtp_user = 'noreply@sag24.ru'; $smtp_pass = '9hsnDyBAk5&S4#lE'; $mail_to = 'info@sag24.ru'; $subject = "=?UTF-8?B?" . base64_encode("Заявка с sag24.ru от {$name}") . "?="; $body = "Имя: {$name}\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 { $smtp = fsockopen($smtp_host, $smtp_port, $errno, $errstr, 10); if ($smtp) { fgets($smtp, 512); fwrite($smtp, "EHLO sag24.ru\r\n"); $caps = ''; while ($line = fgets($smtp, 512)) { $caps .= $line; if ($line[3] == ' ') break; } fwrite($smtp, "STARTTLS\r\n"); fgets($smtp, 512); stream_socket_enable_crypto($smtp, true, STREAM_CRYPTO_METHOD_TLS_CLIENT); fwrite($smtp, "EHLO sag24.ru\r\n"); while ($line = fgets($smtp, 512)) { if ($line[3] == ' ') break; } fwrite($smtp, "AUTH LOGIN\r\n"); fgets($smtp, 512); fwrite($smtp, base64_encode($smtp_user) . "\r\n"); fgets($smtp, 512); fwrite($smtp, base64_encode($smtp_pass) . "\r\n"); fgets($smtp, 512); fwrite($smtp, "MAIL FROM:<{$smtp_user}>\r\n"); fgets($smtp, 512); fwrite($smtp, "RCPT TO:<{$mail_to}>\r\n"); fgets($smtp, 512); fwrite($smtp, "DATA\r\n"); fgets($smtp, 512); fwrite($smtp, "From: noreply@sag24.ru\r\nTo: {$mail_to}\r\nSubject: {$subject}\r\n"); fwrite($smtp, "MIME-Version: 1.0\r\nContent-Type: text/plain; charset=UTF-8\r\n\r\n"); fwrite($smtp, $body . "\r\n.\r\n"); fgets($smtp, 512); fwrite($smtp, "QUIT\r\n"); fclose($smtp); } } catch (Throwable $e) { // email failure is non-fatal } echo json_encode(['success' => true]);