import { useState, useEffect, useMemo, useCallback } from 'react' import { useNavigate, useLocation } 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 } 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 { isAuthenticated, loginWithTelegram, loginWithEmail } = useAuthStore() const [activeTab, setActiveTab] = useState<'telegram' | 'email'>('telegram') const [email, setEmail] = useState('') const [password, setPassword] = useState('') const [error, setError] = useState('') const [isLoading, setIsLoading] = useState(false) const [isTelegramWebApp, setIsTelegramWebApp] = useState(false) const [logoLoaded, setLogoLoaded] = useState(() => isLogoPreloaded()) // Получаем 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, }) const botUsername = import.meta.env.VITE_TELEGRAM_BOT_USERNAME || '' 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 handleEmailLogin = async (e: React.FormEvent) => { e.preventDefault() setError('') setIsLoading(true) try { await loginWithEmail(email, password) navigate(getReturnUrl(), { replace: true }) } catch (err: unknown) { const error = err as { response?: { status?: number; data?: { detail?: string } } } // Show user-friendly error messages without exposing sensitive server details const status = error.response?.status if (status === 401 || status === 403) { setError(t('auth.invalidCredentials', 'Неверный email или пароль')) } else if (status === 429) { setError(t('auth.tooManyAttempts', 'Слишком много попыток. Попробуйте позже')) } else { setError(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')}

{/* Card */}
{/* Tabs */}
{error && (
{error}
)} {activeTab === 'telegram' ? (

{t('auth.registerHint')}

{isLoading && isTelegramWebApp ? (

{t('auth.authenticating')}

) : ( )}
) : (
setEmail(e.target.value)} />
setPassword(e.target.value)} />

{t('auth.registerHint')}

)}
) }