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:
@@ -3,3 +3,6 @@ export const WHATSAPP_PHONE = '74953637476' // без +
|
||||
export const TELEGRAM_USERNAME = 'sag24ru' // @username без @
|
||||
export const FORM_ENDPOINT = '/api/contact.php'
|
||||
export const EMAIL = 'info@sag24.ru'
|
||||
|
||||
// Cloudflare Turnstile — создать виджет: dash.cloudflare.com → Turnstile → Add site (sag24.ru)
|
||||
export const TURNSTILE_SITE_KEY = ''
|
||||
|
||||
@@ -2,7 +2,7 @@ import React, { useState } from 'react'
|
||||
import { ChevronRight, Server, Shield, Headphones, Phone, Mail, MapPin, CheckCircle2, ChevronDown } from 'lucide-react'
|
||||
import { useLanguage } from '../contexts/LanguageContext.jsx'
|
||||
import { useReveal } from '../components/useReveal.js'
|
||||
import { FORM_ENDPOINT, EMAIL } from '../config.js'
|
||||
import { FORM_ENDPOINT, EMAIL, TURNSTILE_SITE_KEY } from '../config.js'
|
||||
|
||||
const serviceIcons = [Server, Shield, Headphones]
|
||||
|
||||
@@ -49,7 +49,7 @@ function FaqItem({ q, a }) {
|
||||
|
||||
export default function Home() {
|
||||
const { t } = useLanguage()
|
||||
const [formState, setFormState] = useState({ name: '', company: '', phone: '', message: '' })
|
||||
const [formState, setFormState] = useState({ name: '', company: '', email: '', phone: '', message: '' })
|
||||
const [submitted, setSubmitted] = useState(false)
|
||||
const [sending, setSending] = useState(false)
|
||||
const [error, setError] = useState('')
|
||||
@@ -70,10 +70,11 @@ export default function Home() {
|
||||
|
||||
if (FORM_ENDPOINT) {
|
||||
try {
|
||||
const turnstileToken = document.querySelector('[name="cf-turnstile-response"]')?.value || ''
|
||||
const res = await fetch(FORM_ENDPOINT, {
|
||||
method: 'POST',
|
||||
headers: { 'Content-Type': 'application/json', Accept: 'application/json' },
|
||||
body: JSON.stringify(formState),
|
||||
body: JSON.stringify({ ...formState, turnstileToken }),
|
||||
})
|
||||
if (res.ok) {
|
||||
setSubmitted(true)
|
||||
@@ -316,6 +317,13 @@ export default function Home() {
|
||||
onChange={e => setFormState({ ...formState, company: e.target.value })}
|
||||
className="w-full bg-white/10 border border-white/10 rounded-lg px-4 py-3 text-white placeholder-slate-400 focus:outline-none focus:border-blue-500 transition-colors text-sm"
|
||||
/>
|
||||
<input
|
||||
type="email"
|
||||
placeholder={t('contact.formEmail')}
|
||||
value={formState.email}
|
||||
onChange={e => setFormState({ ...formState, email: e.target.value })}
|
||||
className="w-full bg-white/10 border border-white/10 rounded-lg px-4 py-3 text-white placeholder-slate-400 focus:outline-none focus:border-blue-500 transition-colors text-sm"
|
||||
/>
|
||||
<input
|
||||
type="tel"
|
||||
placeholder={t('contact.formPhone')}
|
||||
@@ -331,6 +339,13 @@ export default function Home() {
|
||||
onChange={e => setFormState({ ...formState, message: e.target.value })}
|
||||
className="w-full bg-white/10 border border-white/10 rounded-lg px-4 py-3 text-white placeholder-slate-400 focus:outline-none focus:border-blue-500 transition-colors text-sm resize-none"
|
||||
/>
|
||||
{TURNSTILE_SITE_KEY && (
|
||||
<div
|
||||
className="cf-turnstile"
|
||||
data-sitekey={TURNSTILE_SITE_KEY}
|
||||
data-theme="dark"
|
||||
/>
|
||||
)}
|
||||
{error && <p className="text-red-400 text-sm">{error}</p>}
|
||||
<button
|
||||
type="submit"
|
||||
|
||||
@@ -50,6 +50,7 @@ export const en = {
|
||||
addressValue: 'Moscow Region, Pushkino, 38/14',
|
||||
formName: 'Your name',
|
||||
formCompany: 'Company',
|
||||
formEmail: 'Your email',
|
||||
formMessage: 'Describe your task',
|
||||
formPhone: 'Your phone',
|
||||
formSubmit: 'Send Request',
|
||||
|
||||
@@ -50,6 +50,7 @@ export const ru = {
|
||||
addressValue: 'Московская область, Пушкино, д. 38/14',
|
||||
formName: 'Ваше имя',
|
||||
formCompany: 'Компания',
|
||||
formEmail: 'Ваш email',
|
||||
formMessage: 'Опишите задачу',
|
||||
formPhone: 'Ваш телефон',
|
||||
formSubmit: 'Отправить заявку',
|
||||
|
||||
Reference in New Issue
Block a user