import { useState, useEffect, useMemo, useCallback } from 'react' import { useNavigate, useLocation, useSearchParams } from 'react-router-dom' import { useTranslation } from 'react-i18next' import { useQuery } from '@tanstack/react-query' import { useAuthStore } from '../store/auth' import { brandingApi, getCachedBranding, setCachedBranding, preloadLogo, isLogoPreloaded, type BrandingInfo, type EmailAuthEnabled } from '../api/branding' import { getAndClearReturnUrl } from '../utils/token' import LanguageSwitcher from '../components/LanguageSwitcher' import TelegramLoginButton from '../components/TelegramLoginButton' export default function Login() { const { t } = useTranslation() const navigate = useNavigate() const location = useLocation() const [searchParams] = useSearchParams() const { isAuthenticated, loginWithTelegram, loginWithEmail, registerWithEmail } = useAuthStore() // Extract referral code from URL const referralCode = searchParams.get('ref') || '' const [activeTab, setActiveTab] = useState<'telegram' | 'email'>(() => referralCode ? 'email' : 'telegram' ) const [authMode, setAuthMode] = useState<'login' | 'register'>(() => referralCode ? 'register' : 'login' ) const [email, setEmail] = useState('') const [password, setPassword] = useState('') const [confirmPassword, setConfirmPassword] = useState('') const [firstName, setFirstName] = useState('') const [error, setError] = useState('') const [isLoading, setIsLoading] = useState(false) const [isTelegramWebApp, setIsTelegramWebApp] = useState(false) const [logoLoaded, setLogoLoaded] = useState(() => isLogoPreloaded()) const [registeredEmail, setRegisteredEmail] = useState(null) // Получаем URL для возврата после авторизации const getReturnUrl = useCallback(() => { // Сначала проверяем state от React Router const stateFrom = (location.state as { from?: string })?.from if (stateFrom && stateFrom !== '/login') { return stateFrom } // Затем проверяем сохранённый URL в sessionStorage (от safeRedirectToLogin) const savedUrl = getAndClearReturnUrl() if (savedUrl && savedUrl !== '/login') { return savedUrl } // По умолчанию на главную return '/' }, [location.state]) // Fetch branding with unified cache const cachedBranding = useMemo(() => getCachedBranding(), []) const { data: branding } = useQuery({ queryKey: ['branding'], queryFn: async () => { const data = await brandingApi.getBranding() setCachedBranding(data) preloadLogo(data) return data }, staleTime: 60000, initialData: cachedBranding ?? undefined, }) // Check if email auth is enabled const { data: emailAuthConfig } = useQuery({ queryKey: ['email-auth-enabled'], queryFn: brandingApi.getEmailAuthEnabled, staleTime: 60000, }) const isEmailAuthEnabled = emailAuthConfig?.enabled ?? true const botUsername = import.meta.env.VITE_TELEGRAM_BOT_USERNAME || '' // If email auth is disabled but user came with ref param, redirect to bot useEffect(() => { if (referralCode && emailAuthConfig?.enabled === false && botUsername) { window.location.href = `https://t.me/${botUsername}?start=ref_${referralCode}` } }, [referralCode, emailAuthConfig, botUsername]) // If email auth is disabled but we initially set to email tab, switch back to telegram useEffect(() => { if (!isEmailAuthEnabled && activeTab === 'email') { setActiveTab('telegram') } }, [isEmailAuthEnabled, activeTab]) const appName = branding ? branding.name : (import.meta.env.VITE_APP_NAME || 'VPN') const appLogo = branding?.logo_letter || import.meta.env.VITE_APP_LOGO || 'V' const logoUrl = branding ? brandingApi.getLogoUrl(branding) : null // Set document title useEffect(() => { document.title = appName || 'VPN' }, [appName]) useEffect(() => { if (isAuthenticated) { navigate(getReturnUrl(), { replace: true }) } }, [isAuthenticated, navigate, getReturnUrl]) // Try Telegram WebApp authentication on mount useEffect(() => { const tryTelegramAuth = async () => { const tg = window.Telegram?.WebApp if (tg?.initData) { setIsTelegramWebApp(true) tg.ready() tg.expand() setIsLoading(true) try { await loginWithTelegram(tg.initData) navigate(getReturnUrl(), { replace: true }) } catch (err) { // Log only status code to avoid leaking sensitive data const status = (err as { response?: { status?: number } })?.response?.status console.warn('Telegram auth failed with status:', status) setError(t('auth.telegramRequired')) } finally { setIsLoading(false) } } } tryTelegramAuth() }, [loginWithTelegram, navigate, t, getReturnUrl]) const handleEmailSubmit = async (e: React.FormEvent) => { e.preventDefault() setError('') // Валидация email const emailRegex = /^[^\s@]+@[^\s@]+\.[^\s@]+$/ if (!email.trim() || !emailRegex.test(email.trim())) { setError(t('auth.invalidEmail', 'Please enter a valid email address')) return } if (authMode === 'register') { // Валидация для регистрации if (password !== confirmPassword) { setError(t('auth.passwordMismatch', 'Passwords do not match')) return } if (password.length < 8) { setError(t('auth.passwordTooShort', 'Password must be at least 8 characters')) return } } setIsLoading(true) try { if (authMode === 'login') { await loginWithEmail(email, password) navigate(getReturnUrl(), { replace: true }) } else { const result = await registerWithEmail(email, password, firstName || undefined, referralCode || undefined) // Show "check your email" screen setRegisteredEmail(result.email) } } catch (err: unknown) { const error = err as { response?: { status?: number; data?: { detail?: string } } } const status = error.response?.status const detail = error.response?.data?.detail if (status === 400 && detail?.includes('already registered')) { setError(t('auth.emailAlreadyRegistered', 'This email is already registered')) } else if (status === 401 || status === 403) { if (detail?.includes('verify your email')) { setError(t('auth.emailNotVerified', 'Please verify your email first')) } else { setError(t('auth.invalidCredentials', 'Invalid email or password')) } } else if (status === 429) { setError(t('auth.tooManyAttempts', 'Too many attempts. Please try again later')) } else { setError(detail || t('common.error')) } } finally { setIsLoading(false) } } return (
{/* Background gradient */}
{/* Language switcher */}
{/* Logo */}
{/* Always show letter as fallback */} {appLogo} {/* Logo image with smooth fade-in */} {branding?.has_custom_logo && logoUrl && ( {appName setLogoLoaded(true)} /> )}
{appName && (

{appName}

)}

{t('auth.loginSubtitle')}

{/* Referral Banner - only show when email auth is enabled */} {referralCode && isEmailAuthEnabled && (
{t('auth.referralInvite')}
)}
{/* Check Email Screen */} {registeredEmail ? (

{t('auth.checkEmail', 'Check your email')}

{t('auth.verificationSent', 'We sent a verification link to:')}

{registeredEmail}

{t('auth.clickLinkToVerify', 'Click the link in the email to verify your account and log in.')}

) : ( /* Card */
{/* Tabs */} {isEmailAuthEnabled ? (
) : (

Telegram

)} {error && (
{error}
)} {activeTab === 'telegram' || !isEmailAuthEnabled ? (

{t('auth.registerHint')}

{isLoading && isTelegramWebApp ? (

{t('auth.authenticating')}

) : ( )}
) : (
{/* Login / Register toggle */}
{/* First name field - only for registration */} {authMode === 'register' && (
setFirstName(e.target.value)} />
)}
setEmail(e.target.value)} />
setPassword(e.target.value)} />
{/* Confirm password - only for registration */} {authMode === 'register' && (
setConfirmPassword(e.target.value)} />
)}
{/* Verification notice for registration */} {authMode === 'register' && (

{t('auth.verificationEmailNotice', 'After registration, a verification email will be sent to your address')}

)} {/* Forgot password link - only for login */} {authMode === 'login' && (

{t('auth.registerHint')}

)}
)}
)}
) }