From a1d4ce0cdd85d695dede31d3da87671bcc0fb18c Mon Sep 17 00:00:00 2001 From: PotapovaA Date: Wed, 2 Sep 2026 16:33:32 +0300 Subject: [PATCH] fix --- README.md | 2 +- web/package.json | 2 +- web/src/app/context/AuthProvider.jsx | 101 +++++-------------- web/src/components/HeaderMenu/HeaderMenu.jsx | 6 +- web/src/index.jsx | 18 +++- web/src/pages/LoginPage.jsx | 8 +- 6 files changed, 51 insertions(+), 86 deletions(-) diff --git a/README.md b/README.md index bebab73..1d9dbe1 100644 --- a/README.md +++ b/README.md @@ -174,4 +174,4 @@ REACT_APP_NAMESPACE=example-namespace REACT_APP_APP_NAME=example-app ``` -При `REACT_APP_IS_PRODUCTION=false` или если переменная отсутствует, Keycloak не инициализируется. +При `REACT_APP_IS_PRODUCTION=false` или если переменная отсутствует, Keycloak не инициализируется и продолжает работать локальная форма входа. Production-авторизация подключается через `raisa-fastapi-protected-web`. diff --git a/web/package.json b/web/package.json index 5a3bde3..0f66452 100644 --- a/web/package.json +++ b/web/package.json @@ -15,7 +15,7 @@ "@mui/x-tree-view": "^9.1.0", "axios": "^1.9.0", "date-fns": "^4.1.0", - "keycloak-js": "^26.2.4", + "raisa-fastapi-protected-web": "1.1.0", "material-react-table": "^3.2.1", "react": "^18.3.1", "react-daisyui": "^5.0.5", diff --git a/web/src/app/context/AuthProvider.jsx b/web/src/app/context/AuthProvider.jsx index fcc73de..b23b92c 100644 --- a/web/src/app/context/AuthProvider.jsx +++ b/web/src/app/context/AuthProvider.jsx @@ -1,5 +1,5 @@ import { CircularProgress } from '@mui/material'; -import Keycloak from 'keycloak-js'; +import { TokensContext } from 'raisa-fastapi-protected-web'; import { createContext, useCallback, useContext, useEffect, useState } from 'react'; import { AuthApi } from '../../api/auth'; import { UsersApi } from '../../api/users'; @@ -67,7 +67,7 @@ const LocalAuthProvider = ({ children }) => { } }; - const logout = async () => { + const logout = () => { try { AuthApi.logout(); setIsAuthenticated(false); @@ -113,27 +113,12 @@ const LocalAuthProvider = ({ children }) => { ); }; -const KeycloakAuthProvider = ({ children }) => { - const [keycloak] = useState( - () => - new Keycloak({ - url: Config.KEYCLOAK_URL, - realm: Config.REALM, - clientId: Config.CLIENT_ID, - }), - ); +const ProductionAuthProvider = ({ children }) => { + const tokenInfo = useContext(TokensContext); 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(); @@ -144,73 +129,41 @@ const KeycloakAuthProvider = ({ children }) => { } }, []); - 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 accessToken = tokenInfo?.accessToken; - 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 = () => { + if (!accessToken) { + localStorage.removeItem('token'); localStorage.removeItem('access_token'); + localStorage.removeItem('refresh_token'); setIsAuthenticated(false); setUser(null); - }; + setLoading(true); - initialize(); + return () => { + active = false; + }; + } + + // `token` ожидается библиотекой, а `access_token` — существующими API-клиентами приложения. + localStorage.setItem('token', accessToken); + localStorage.setItem('access_token', accessToken); + localStorage.removeItem('refresh_token'); + setIsAuthenticated(true); + setLoading(true); + + loadUser().finally(() => { + if (active) setLoading(false); + }); return () => { active = false; - keycloak.onAuthRefreshSuccess = undefined; - keycloak.onTokenExpired = undefined; - keycloak.onAuthLogout = undefined; }; - }, [keycloak, loadUser, login, saveKeycloakToken]); + }, [loadUser, tokenInfo?.accessToken]); return ( - + {loading ? : children} ); @@ -218,7 +171,7 @@ const KeycloakAuthProvider = ({ children }) => { export const AuthProvider = ({ children }) => { if (Config.IS_PRODUCTION) { - return {children}; + return {children}; } return {children}; diff --git a/web/src/components/HeaderMenu/HeaderMenu.jsx b/web/src/components/HeaderMenu/HeaderMenu.jsx index 45241c9..3075412 100644 --- a/web/src/components/HeaderMenu/HeaderMenu.jsx +++ b/web/src/components/HeaderMenu/HeaderMenu.jsx @@ -2,7 +2,7 @@ import styled from '@emotion/styled'; import { useCallback, useEffect, useRef, useState } from 'react'; import { useNavigate } from 'react-router-dom'; import { useAuth } from '../../app/context/AuthProvider'; -import { isTestMode } from '../../conf/config'; +import { Config } from '../../conf/config'; export function HeaderMenu() { const { user, logout } = useAuth(); @@ -58,8 +58,8 @@ export function HeaderMenu() { {/* на стенде прода не показываем кнопку выхода */} - {isTestMode && } - {isTestMode && isMenuOpen && ( + {!Config.IS_PRODUCTION && } + {!Config.IS_PRODUCTION && isMenuOpen && ( Выйти diff --git a/web/src/index.jsx b/web/src/index.jsx index afa1635..dc98c34 100644 --- a/web/src/index.jsx +++ b/web/src/index.jsx @@ -3,6 +3,7 @@ import { AdapterDateFns } from '@mui/x-date-pickers/AdapterDateFns'; import { LocalizationProvider } from '@mui/x-date-pickers/LocalizationProvider'; import { ruRU } from '@mui/x-date-pickers/locales'; import ruLocale from 'date-fns/locale/ru'; +import { RaisaAuthContext, createAuthContext } from 'raisa-fastapi-protected-web'; import { createRoot } from 'react-dom/client'; import { BrowserRouter } from 'react-router-dom'; import { AuthProvider } from './app/context/AuthProvider'; @@ -11,10 +12,11 @@ import './index.css'; import { ToastContainer } from 'react-toastify'; import App from './App'; import 'react-toastify/dist/ReactToastify.css'; +import { Config } from './conf/config'; const baseName = (process.env.REACT_APP_ROOT_PATH || '/').replace(/\/$/, ''); -createRoot(document.getElementById('root')).render( +const application = ( - , + ); + +const root = Config.IS_PRODUCTION ? ( + + {application} + +) : ( + application +); + +createRoot(document.getElementById('root')).render(root); diff --git a/web/src/pages/LoginPage.jsx b/web/src/pages/LoginPage.jsx index 8496950..ddd316f 100644 --- a/web/src/pages/LoginPage.jsx +++ b/web/src/pages/LoginPage.jsx @@ -1,7 +1,6 @@ import { useEffect, useState } from 'react'; import { useNavigate } from 'react-router-dom'; import { useAuth } from '../app/context/AuthProvider'; -import { isTestMode } from '../conf/config'; export default function LoginPage() { const [username, setUsername] = useState('admin'); @@ -11,18 +10,17 @@ export default function LoginPage() { const navigate = useNavigate(); useEffect(() => { - if (isTestMode && isAuthenticated) { + if (isAuthenticated) { navigate('/', { replace: true }); - return; } - }, [isTestMode, isAuthenticated, navigate]); + }, [isAuthenticated, navigate]); const onSubmit = async (e) => { e.preventDefault(); try { await login(username, password); navigate('/tasks'); - } catch (err) { + } catch (_err) { setError('Неверный логин или пароль'); } };