import { useState, useRef, useEffect, useCallback } from 'react'; import { Link, useNavigate, useLocation } from 'react-router-dom'; import axios from 'axios'; import { Turnstile } from '@marsidev/react-turnstile'; import type { TurnstileInstance } from '@marsidev/react-turnstile'; import useAuth from '../context/useAuth'; import { API_URL } from '../config/api'; import { useToast } from '../hooks/useToast'; import { useTranslation } from '../i18n'; import { useLocalePath } from '../middleware'; import { validateEmail } from '../utils/emailValidation'; import { validateUsername } from '../utils/usernameBlacklist'; import { quickEmailCheck, verifyEmailWithAPI } from '../utils/emailVerification'; const RegisterPage = () => { const { addToast } = useToast(); const [username, setUsername] = useState(''); const [usernameError, setUsernameError] = useState(null); const [email, setEmail] = useState(''); const [emailError, setEmailError] = useState(null); const [emailSuggestion, setEmailSuggestion] = useState(null); const [isVerifyingEmail, setIsVerifyingEmail] = useState(false); const [password, setPassword] = useState(''); const [error, setError] = useState(''); const [isLoading, setIsLoading] = useState(false); const [turnstileToken, setTurnstileToken] = useState(null); const turnstileRef = useRef(null); const navigate = useNavigate(); const location = useLocation(); const { login } = useAuth(); const { t, locale } = useTranslation(); const localePath = useLocalePath(); const siteKey = import.meta.env.VITE_TURNSTILE_SITE_KEY; // Username validation on blur const handleUsernameBlur = useCallback(() => { if (!username.trim()) { setUsernameError(null); return; } const result = validateUsername(username, locale); setUsernameError(result.isValid ? null : result.error ?? null); }, [username, locale]); // Email validation on blur const handleEmailBlur = useCallback(async () => { if (!email.trim()) { setEmailError(null); setEmailSuggestion(null); return; } // Quick client-side check const quickCheck = quickEmailCheck(email); if (!quickCheck.valid) { setEmailError(quickCheck.reason || (locale === 'en' ? 'Invalid email' : 'Недействительный email')); setEmailSuggestion(null); return; } const result = validateEmail(email, locale); setEmailError(result.isValid ? null : result.error ?? null); setEmailSuggestion(result.suggestion ?? null); // Optional: deep verification with API (only if basic checks pass) if (result.isValid) { setIsVerifyingEmail(true); try { const apiResult = await verifyEmailWithAPI(email, { checkMX: true }); if (!apiResult.valid) { setEmailError(apiResult.error || (locale === 'en' ? 'This email address appears to be invalid' : 'Этот email-адрес недействителен')); } else if (apiResult.disposable) { setEmailError(locale === 'en' ? 'Temporary email addresses are not allowed' : 'Временные email-адреса не допускаются'); } } catch (err) { console.warn('Email verification failed:', err); } finally { setIsVerifyingEmail(false); } } }, [email, locale]); // Apply email suggestion const applySuggestion = useCallback(() => { if (emailSuggestion) { const match = emailSuggestion.match(/([a-zA-Z0-9._%+-]+@[a-zA-Z0-9.-]+\.[a-zA-Z]{2,})/); if (match) { setEmail(match[1]); setEmailSuggestion(null); } } }, [emailSuggestion]); // Handle OAuth token from URL useEffect(() => { const handleOAuthLogin = async () => { const params = new URLSearchParams(location.search); const token = params.get('token'); const authError = params.get('error'); if (token) { await login(token); navigate(localePath('/dashboard'), { replace: true }); } if (authError) { setError(locale === 'en' ? 'Social login error. Please try again.' : 'Ошибка авторизации через социальную сеть. Попробуйте снова.'); } }; handleOAuthLogin(); }, [location, login, navigate, localePath, locale]); const handleRegister = async (e: React.FormEvent) => { e.preventDefault(); setError(''); // Validate username const usernameValidation = validateUsername(username, locale); if (!usernameValidation.isValid) { setUsernameError(usernameValidation.error ?? t('auth.register.invalidEmail')); return; } // Validate email before submit const emailValidation = validateEmail(email, locale); if (!emailValidation.isValid) { setEmailError(emailValidation.error ?? t('auth.register.invalidEmail')); return; } if (!turnstileToken) { setError(t('auth.register.captchaRequired')); return; } setIsLoading(true); try { await axios.post(`${API_URL}/api/auth/register`, { username: username, email: email, password: password, turnstileToken: turnstileToken, }); addToast(t('auth.register.success'), 'success'); navigate(localePath('/login')); } catch (err) { // Reset captcha on error if (turnstileRef.current) { turnstileRef.current.reset(); } setTurnstileToken(null); if (axios.isAxiosError(err) && err.response) { const errorMsg = err.response.data.message || t('auth.register.unknownError'); setError(errorMsg); } else { setError(t('auth.register.networkError')); } } finally { setIsLoading(false); } }; const handleOAuthLogin = (provider: string) => { window.location.href = `${API_URL}/api/auth/${provider}`; }; return (

{t('auth.register.title')}

{ setUsername(e.target.value); setUsernameError(null); }} onBlur={handleUsernameBlur} placeholder={t('auth.register.usernamePlaceholder')} className={`w-full px-5 py-3 border rounded-full focus:outline-none focus:ring-2 ${ usernameError ? 'border-red-500 focus:ring-red-500' : 'border-gray-300 focus:ring-ospab-primary' }`} required disabled={isLoading} /> {usernameError && (

{usernameError}

)}
{ setEmail(e.target.value); setEmailError(null); setEmailSuggestion(null); }} onBlur={handleEmailBlur} placeholder={t('auth.register.emailPlaceholder')} className={`w-full px-5 py-3 border rounded-full focus:outline-none focus:ring-2 ${ emailError ? 'border-red-500 focus:ring-red-500' : isVerifyingEmail ? 'border-yellow-400 focus:ring-yellow-400' : 'border-gray-300 focus:ring-ospab-primary' }`} required disabled={isLoading} /> {isVerifyingEmail && (

{locale === 'en' ? 'Verifying email...' : 'Проверка email...'}

)} {emailError && (

{emailError}

)} {emailSuggestion && !emailError && ( )}
setPassword(e.target.value)} placeholder={t('auth.register.passwordPlaceholder')} className="w-full px-5 py-3 mb-4 border border-gray-300 rounded-full focus:outline-none focus:ring-2 focus:ring-ospab-primary" required disabled={isLoading} /> {/* Terms and Privacy Checkbox */}
{/* Cloudflare Turnstile Captcha */}
setTurnstileToken(token)} onError={() => { setTurnstileToken(null); setError(t('auth.register.captchaError')); }} onExpire={() => setTurnstileToken(null)} />
{error && (

{error}

)} {/* Social networks */}
{t('auth.register.orRegisterWith')}

{t('auth.register.haveAccount')}{' '} {t('auth.register.loginLink')}

); }; export default RegisterPage;