From 4018bdbd3392003ddc9398a359910018ac06ba65 Mon Sep 17 00:00:00 2001 From: Elliot Axel Noriega Date: Fri, 30 Aug 2024 08:25:56 -0600 Subject: [PATCH 1/2] Configuracion correcta de interceptores de api, adaptacion de opciones de navegacion --- .../common/layouts/LayoutBaseNavigation.jsx | 31 ++++++- .../common/utility/Autenticador.jsx | 93 +++++++++++-------- .../common/utility/LoginRequiredRoute.jsx | 35 ++----- .../src/components/users/Login/LoginForm.jsx | 8 +- 4 files changed, 95 insertions(+), 72 deletions(-) diff --git a/cosiap_frontend/src/components/common/layouts/LayoutBaseNavigation.jsx b/cosiap_frontend/src/components/common/layouts/LayoutBaseNavigation.jsx index da255dd..3a1d65b 100644 --- a/cosiap_frontend/src/components/common/layouts/LayoutBaseNavigation.jsx +++ b/cosiap_frontend/src/components/common/layouts/LayoutBaseNavigation.jsx @@ -4,6 +4,28 @@ import Navbar from "@/components/common/layouts/LayoutsNavigation/Navbar/Navbar" import Sidebar from "@/components/common/layouts/LayoutsNavigation/Sidebar/Sidebar"; import SettingsToggle from "@/components/common/layouts/LayoutsNavigation/SettingsToggle"; import Settings from "@/components/common/layouts/LayoutsNavigation/Settings"; +import { useAutenticacion } from "@/components/common/utility/Autenticador"; + +const linksItemsAdmin=[ + { + nameIcon: 'library_books', + nameItem: 'Modalidades', + navigate: '/modalidades', + isSelected: false + }, + { + nameIcon: 'group', + nameItem: 'Usuarios', + navigate: '/usuarios', + isSelected: false + }, + { + nameIcon: 'list_alt', + nameItem: 'Solicitudes', + navigate: '/solicitudes', + isSelected: false + }, +]; const linksItemsSolicitante = [ { @@ -48,14 +70,19 @@ export default function LayoutBaseNavigation() { const [viewMenu, setViewMenu] = useState(false); const [selectedNav, setSelectedNav] = useState('vertical'); const [viewSettings, setViewSettings] = useState(false); - const [links, setLinks] = useState([...linksItemsSolicitante]); - + const {isAdmin} = useAutenticacion(); + console.log(isAdmin) + const [links, setLinks] = useState(isAdmin != undefined && isAdmin ? linksItemsAdmin : linksItemsSolicitante); const settingsRef = useRef(); const settingsToggleRef = useRef(); const location = useLocation(); const [sectionURL, setSectionURL] = useState(""); + useEffect(() => { + setLinks(isAdmin ? linksItemsAdmin : linksItemsSolicitante); + }, [isAdmin]); + useEffect(() => { setSectionURL(location.pathname.split('/').filter(Boolean).pop()); }, [location]); diff --git a/cosiap_frontend/src/components/common/utility/Autenticador.jsx b/cosiap_frontend/src/components/common/utility/Autenticador.jsx index 0a9d8e1..b1b030a 100644 --- a/cosiap_frontend/src/components/common/utility/Autenticador.jsx +++ b/cosiap_frontend/src/components/common/utility/Autenticador.jsx @@ -4,6 +4,7 @@ import { useContext, createContext, useState, + useRef, } from "react"; import api from '@/api' import PropTypes from 'prop-types'; @@ -23,7 +24,8 @@ export const useAutenticacion = () => { export const Autenticador = ({ setViewPageLoader, children }) => { const [token, setToken] = useState(); - const [isAdmin, setIsAdmin] = useState(false); + const [isAdmin, setIsAdmin] = useState(); + const interceptadoresRef = useRef({}); useEffect(() => { const buscarUsuario = async () => { @@ -34,7 +36,6 @@ export const Autenticador = ({ setViewPageLoader, children }) => { configurarInterceptors(response.data.access); const responseAd = await api.usuarios.admin.is_admin(); setIsAdmin(responseAd.data.user_is_admin); - console.log(isAdmin) } catch { setToken(null); setIsAdmin(false); @@ -45,46 +46,53 @@ export const Autenticador = ({ setViewPageLoader, children }) => { }, []); const configurarInterceptors = (token) => { - const interceptadorAccess = api.axios.interceptors.request.use((config) => { - config.headers.Authorization = !config.__retry && token ? `Bearer ${token}` : config.headers.Authorization; - return config; - }); - - const interceptadorRefresh = api.axios.interceptors.response.use( - (response) => response, async (error) => { - const requestOriginal = error.config; - - if (error.response.status === 403 && error.response.data.message === 'Unauthorized') { - try { - const response = await api.usuarios.token.refresh(); - setToken(response.data.access); - requestOriginal.headers.Authorization = `Bearer ${response.data.access}`; - requestOriginal.__retry = true; - const responseAd = await api.usuarios.admin.is_admin(); - setIsAdmin(responseAd.data.user_is_admin); - - return api(requestOriginal); - } catch { - setToken(null); - setIsAdmin(false); + // Si ya existen interceptores, primero eliminarlos + if (interceptadoresRef.current.requestInterceptor) { + api.axios.interceptors.request.eject(interceptadoresRef.current.requestInterceptor); + } + if (interceptadoresRef.current.responseInterceptor) { + api.axios.interceptors.response.eject(interceptadoresRef.current.responseInterceptor); + } + + if (token) { + const requestInterceptor = api.axios.interceptors.request.use((config) => { + config.headers.Authorization = !config.__retry && token ? `Bearer ${token}` : config.headers.Authorization; + return config; + }); + + const responseInterceptor = api.axios.interceptors.response.use( + (response) => response, async (error) => { + const requestOriginal = error.config; + + if (error.response.status === 403 && error.response.data.message === 'Unauthorized') { + try { + const response = await api.usuarios.token.refresh(); + setToken(response.data.access); + requestOriginal.headers.Authorization = `Bearer ${response.data.access}`; + requestOriginal.__retry = true; + const responseAd = await api.usuarios.admin.is_admin(); + setIsAdmin(responseAd.data.user_is_admin); + + return api(requestOriginal); + } catch { + setToken(null); + setIsAdmin(false); + } } - } - return Promise.reject(error); - }, - ); + return Promise.reject(error); + }, + ); - // Eject interceptors on component unmount - return () => { - api.axios.interceptors.request.eject(interceptadorAccess); - api.axios.interceptors.response.eject(interceptadorRefresh); - }; + // Guardar los identificadores de interceptores + interceptadoresRef.current.requestInterceptor = requestInterceptor; + interceptadoresRef.current.responseInterceptor = responseInterceptor; + } }; useLayoutEffect(() => { if (token) { - const ejectInterceptors = configurarInterceptors(token); - return () => ejectInterceptors(); + configurarInterceptors(token); } }, [token]); @@ -92,21 +100,28 @@ export const Autenticador = ({ setViewPageLoader, children }) => { setViewPageLoader(true); try { const response = await api.usuarios.token.logout(); - console.log('Logout succesful ', response); + console.log('Logout successful', response); + + // Eliminar los interceptores configurados para el token actual + configurarInterceptors(token); + + // Limpiar el estado de autenticación setToken(null); + setIsAdmin(undefined); } catch (error) { - console.error('Logout unsuccesful ', error); + console.error('Logout unsuccessful', error); } setViewPageLoader(false); }; return ( - + {children} ); }; Autenticador.propTypes = { - children: PropTypes.node, + setViewPageLoader: PropTypes.func.isRequired, + children: PropTypes.node.isRequired, }; diff --git a/cosiap_frontend/src/components/common/utility/LoginRequiredRoute.jsx b/cosiap_frontend/src/components/common/utility/LoginRequiredRoute.jsx index 33607d6..3ff2a8d 100644 --- a/cosiap_frontend/src/components/common/utility/LoginRequiredRoute.jsx +++ b/cosiap_frontend/src/components/common/utility/LoginRequiredRoute.jsx @@ -4,8 +4,7 @@ import { Navigate, Outlet } from "react-router-dom"; import api from "@/api"; export const LoginRequiredRoute = () => { - const { token, isAdmin } = useAutenticacion(); - console.log(isAdmin) + const { token } = useAutenticacion(); if (token === null) { return ; } else if (token === undefined) { @@ -16,33 +15,15 @@ export const LoginRequiredRoute = () => { }; export const IsAdminRequiredRoute = ({ setViewPageLoader }) => { - const [isAdmin, setIsAdmin] = useState(false); - const [hasCheckedAdmin, setHasCheckedAdmin] = useState(false); // Nuevo estado para verificar si ya se hizo la comprobación - - useEffect(() => { - const verificarAdmin = async () => { - setViewPageLoader(true); // Muestra el loader - try { - const response = await api.usuarios.admin.is_admin(); - setIsAdmin(response.data.user_is_admin); - } catch (error) { - console.error('Determinación de permisos admin ha fallado', error); - setIsAdmin(false); - } finally { - setHasCheckedAdmin(true); // Indica que la verificación ha terminado - setViewPageLoader(false); // Oculta el loader después de recibir la respuesta - } - }; - - verificarAdmin(); - }, [setViewPageLoader]); - - if (!hasCheckedAdmin) { - // Mientras no se haya verificado si es admin o no, retorna null + const {isAdmin} = useAutenticacion(); + + if (isAdmin === false) { + return ; + } else if (isAdmin === undefined) { return null; + }else { + return ; } - - return isAdmin ? : ; }; export const IsLogged = () => { diff --git a/cosiap_frontend/src/components/users/Login/LoginForm.jsx b/cosiap_frontend/src/components/users/Login/LoginForm.jsx index 57d0a42..61fbcef 100644 --- a/cosiap_frontend/src/components/users/Login/LoginForm.jsx +++ b/cosiap_frontend/src/components/users/Login/LoginForm.jsx @@ -16,7 +16,7 @@ import {useAutenticacion} from "@/components/common/utility/Autenticador" export function LoginForm( {setViewPageLoader} ) { const [loginError, setLoginError] = useState('') const navigate = useNavigate(); - const { setToken, setIsAdmin, isAdmin } = useAutenticacion(); + const { setToken, setIsAdmin, configurarInterceptors } = useAutenticacion(); const { register, handleSubmit, @@ -30,11 +30,11 @@ export function LoginForm( {setViewPageLoader} ) { try { const response = await api.usuarios.token.login(data); console.log("Login successful:", response.data); - setToken(response.data.access) + setToken(response.data.access); + configurarInterceptors(response.data.access); //Establecemos si es admin o no const responseAd = await api.usuarios.admin.is_admin(); setIsAdmin(responseAd.data.user_is_admin); - navigate("/inicio"); } catch (error) { console.error("Login failed:", error); setLoginError(error.response.data.detail) @@ -66,7 +66,7 @@ export function LoginForm( {setViewPageLoader} ) { register={register} errors={errors.password ? errors.password.message : undefined} /> - +