import { CircularProgress } from '@mui/material'; import Keycloak from 'keycloak-js'; import { createContext, useCallback, useContext, useEffect, useState } from 'react'; import { AuthApi } from '../../api/auth'; import { UsersApi } from '../../api/users'; import { getAuthToken, isTokenExpired, saveAuthTokens } from '../../api/utils/token'; import { Config } from '../../conf/config'; const AuthContext = createContext(null); const LoadingScreen = () => (
); const LocalAuthProvider = ({ children }) => { const [loading, setLoading] = useState(true); const [user, setUser] = useState(null); const [isAuthenticated, setIsAuthenticated] = useState(() => { const token = getAuthToken(); return !!token && !isTokenExpired(); }); const loadUser = useCallback(async () => { if (!isAuthenticated) { setUser(null); return; } try { const userData = await UsersApi.me(); setUser(userData); } catch (error) { console.error('Ошибка загрузки пользователя:', error); if (error.response?.status === 401) { setIsAuthenticated(false); setUser(null); } else { setUser(null); } } }, [isAuthenticated]); const login = async (username, password) => { setLoading(true); try { const data = await AuthApi.login(username, password); saveAuthTokens(data); setIsAuthenticated(true); try { const userData = await UsersApi.me(); setUser(userData); } catch (userError) { console.error('Ошибка загрузки пользователя:', userError); setUser(null); } } catch (error) { setIsAuthenticated(false); setUser(null); console.error('Ошибка авторизации:', error); throw new Error(error?.response?.data?.detail || 'Неверный логин или пароль', { cause: error }); } finally { setLoading(false); } }; const logout = async () => { try { AuthApi.logout(); setIsAuthenticated(false); setUser(null); } catch (error) { console.error('Ошибка выхода:', error); } }; useEffect(() => { setLoading(false); }, []); useEffect(() => { loadUser(); }, [loadUser]); useEffect(() => { const handleAuthChange = (_event) => { const token = getAuthToken(); const newIsAuthenticated = !!token && !isTokenExpired(); setIsAuthenticated(newIsAuthenticated); }; const handleAuthExpired = () => { setIsAuthenticated(false); setUser(null); }; window.addEventListener('auth:changed', handleAuthChange); window.addEventListener('auth:expired', handleAuthExpired); return () => { window.removeEventListener('auth:changed', handleAuthChange); window.removeEventListener('auth:expired', handleAuthExpired); }; }, []); return ( {loading ? : children} ); }; const KeycloakAuthProvider = ({ children }) => { const [keycloak] = useState( () => new Keycloak({ url: Config.KEYCLOAK_URL, realm: Config.REALM, clientId: Config.CLIENT_ID, }), ); const [loading, setLoading] = useState(true); const [isAuthenticated, setIsAuthenticated] = useState(false); const [user, setUser] = useState(null); const saveKeycloakToken = useCallback(() => { if (!keycloak.token) return; localStorage.setItem('access_token', keycloak.token); localStorage.removeItem('refresh_token'); window.dispatchEvent(new CustomEvent('auth:changed', { detail: { loggedIn: true } })); }, [keycloak]); const loadUser = useCallback(async () => { try { const userData = await UsersApi.me(); setUser(userData); } catch (error) { console.error('Ошибка загрузки пользователя:', error); setUser(null); } }, []); const login = useCallback(() => keycloak.login(), [keycloak]); const logout = useCallback(async () => { localStorage.removeItem('access_token'); localStorage.removeItem('refresh_token'); setIsAuthenticated(false); setUser(null); await keycloak.logout({ redirectUri: window.location.origin }); }, [keycloak]); useEffect(() => { let active = true; const initialize = async () => { try { const authenticated = await keycloak.init({ onLoad: 'login-required', checkLoginIframe: false, pkceMethod: 'S256', }); if (!active) return; setIsAuthenticated(authenticated); if (authenticated) { saveKeycloakToken(); await loadUser(); } } catch (error) { console.error('Ошибка инициализации Keycloak:', error); if (active) { setIsAuthenticated(false); setUser(null); } } finally { if (active) setLoading(false); } }; keycloak.onAuthRefreshSuccess = saveKeycloakToken; keycloak.onTokenExpired = async () => { try { await keycloak.updateToken(30); saveKeycloakToken(); } catch (error) { console.error('Не удалось обновить токен Keycloak:', error); await login(); } }; keycloak.onAuthLogout = () => { localStorage.removeItem('access_token'); setIsAuthenticated(false); setUser(null); }; initialize(); return () => { active = false; keycloak.onAuthRefreshSuccess = undefined; keycloak.onTokenExpired = undefined; keycloak.onAuthLogout = undefined; }; }, [keycloak, loadUser, login, saveKeycloakToken]); return ( {loading ? : children} ); }; export const AuthProvider = ({ children }) => { if (Config.IS_PRODUCTION) { return {children}; } return {children}; }; export const useAuth = () => { const context = useContext(AuthContext); if (!context) { throw new Error('useAuth must be used within an AuthProvider'); } return context; };