import React, { useState, useEffect } from 'react'; import { QRCodeSVG } from 'qrcode.react'; import { useNavigate } from 'react-router-dom'; import useAuth from '../context/useAuth'; import apiClient from '../utils/apiClient'; interface QRLoginProps { onSuccess?: () => void; } const QRLogin: React.FC = ({ onSuccess }) => { const navigate = useNavigate(); const { login } = useAuth(); const [qrCode, setQrCode] = useState(''); const [status, setStatus] = useState<'generating' | 'waiting' | 'scanning' | 'expired' | 'error'>('generating'); const [pollingInterval, setPollingInterval] = useState(null); const [refreshInterval, setRefreshInterval] = useState(null); const qrLinkBase = typeof window !== 'undefined' ? window.location.origin : ''; useEffect(() => { generateQR(); return () => { if (pollingInterval) clearInterval(pollingInterval); if (refreshInterval) clearInterval(refreshInterval); }; // eslint-disable-next-line react-hooks/exhaustive-deps }, []); const generateQR = async () => { try { setStatus('generating'); const response = await apiClient.post('/api/qr-auth/generate'); setQrCode(response.data.code); setStatus('waiting'); startPolling(response.data.code); // Автоматическое обновление QR-кода каждые 60 секунд if (refreshInterval) clearInterval(refreshInterval); const interval = setInterval(() => { generateQR(); }, 60000); setRefreshInterval(interval); } catch (error) { console.error('Ошибка генерации QR:', error); setStatus('error'); } }; const startPolling = (code: string) => { const interval = setInterval(async () => { try { const response = await apiClient.get(`/api/qr-auth/status/${code}`); // Если статус изменился на "scanning" (пользователь открыл страницу подтверждения) if (response.data.status === 'scanning') { setStatus('scanning'); } if (response.data.status === 'confirmed' && response.data.token) { clearInterval(interval); setPollingInterval(null); // Вызываем login из контекста для обновления состояния login(response.data.token); if (onSuccess) { onSuccess(); } else { navigate('/dashboard'); } } else if (response.data.status === 'rejected') { clearInterval(interval); setPollingInterval(null); setStatus('error'); } } catch (error) { const axiosError = error as { response?: { status?: number } }; if (axiosError.response?.status === 404 || axiosError.response?.status === 410) { clearInterval(interval); setPollingInterval(null); setStatus('expired'); } } }, 2000); // Проверка каждые 2 секунды setPollingInterval(interval); }; const getStatusMessage = () => { switch (status) { case 'generating': return 'Генерация...'; case 'waiting': return 'Отсканируйте QR-код телефоном, на котором вы уже авторизованы'; case 'scanning': return 'Ожидание подтверждения на телефоне...'; case 'expired': return 'QR-код истёк'; case 'error': return 'Ошибка'; default: return ''; } }; return (

Вход по QR-коду

{getStatusMessage()}

{status === 'generating' && (
)} {(status === 'waiting' || status === 'scanning') && qrCode && (
)} {status === 'expired' && (
)} {status === 'error' && (
)}
{/* Alternative Login */}
); }; export default QRLogin;