"use client"; import { createContext, useContext, useState, useEffect, ReactNode, useRef } from "react"; import { useRouter, usePathname } from "next/navigation"; import { authApi } from "./api"; // Интерфейс пользователя export interface User { id: number; email: string; first_name: string; last_name: string; is_admin: boolean; } // Интерфейс ответа API с данными пользователя interface UserApiResponse { user: { id: number; email: string; first_name: string; last_name: string; is_admin: boolean; [key: string]: any; // Для других полей, которые могут быть в ответе }; [key: string]: any; // Для других полей, которые могут быть в ответе } // Интерфейс контекста аутентификации interface AuthContextType { user: User | null; loading: boolean; isAdmin: boolean; authChecked: boolean; login: (email: string, password: string) => Promise; logout: () => void; } // Создание контекста аутентификации const AuthContext = createContext(undefined); // Глобальная переменная для отслеживания состояния аутентификации между рендерами let globalAuthState = { isAuthenticated: false, isAdmin: false, redirectAttempted: false }; // Провайдер аутентификации export function AuthProvider({ children }: { children: ReactNode }) { const [user, setUser] = useState(null); const [loading, setLoading] = useState(true); const [authChecked, setAuthChecked] = useState(false); const router = useRouter(); // Проверка аутентификации при загрузке useEffect(() => { const checkAuth = async () => { try { const token = localStorage.getItem("token"); if (token) { const response = await authApi.getProfile(); console.log("Проверка аутентификации:", response); if (response.data && typeof response.data === 'object' && response.data !== null && 'user' in response.data) { // Приводим ответ к нужному типу const apiResponse = response.data as UserApiResponse; // Создаем объект пользователя const userData: User = { id: apiResponse.user.id, email: apiResponse.user.email, first_name: apiResponse.user.first_name, last_name: apiResponse.user.last_name, is_admin: apiResponse.user.is_admin }; setUser(userData); // Обновляем глобальное состояние аутентификации globalAuthState.isAuthenticated = true; globalAuthState.isAdmin = userData.is_admin; globalAuthState.redirectAttempted = false; console.log("Пользователь авторизован при загрузке:", userData); console.log("Глобальное состояние обновлено:", globalAuthState); } else { console.log("Токен недействителен или неверный формат данных, удаляем"); localStorage.removeItem("token"); setUser(null); // Сбрасываем глобальное состояние globalAuthState.isAuthenticated = false; globalAuthState.isAdmin = false; } } else { console.log("Токен отсутствует"); setUser(null); // Сбрасываем глобальное состояние globalAuthState.isAuthenticated = false; globalAuthState.isAdmin = false; } } catch (error) { console.error("Ошибка при проверке аутентификации:", error); localStorage.removeItem("token"); setUser(null); // Сбрасываем глобальное состояние globalAuthState.isAuthenticated = false; globalAuthState.isAdmin = false; } finally { setLoading(false); setAuthChecked(true); } }; checkAuth(); }, []); // Функция входа const login = async (email: string, password: string): Promise => { try { console.log("Попытка входа:", email); const response = await authApi.login(email, password); console.log("Ответ API при входе:", response); if (response.data?.access_token) { localStorage.setItem("token", response.data.access_token); // Получение данных пользователя const userResponse = await authApi.getProfile(); console.log("Данные пользователя:", userResponse); if (userResponse.data && typeof userResponse.data === 'object' && userResponse.data !== null && 'user' in userResponse.data) { // Приводим ответ к нужному типу const apiResponse = userResponse.data as UserApiResponse; // Создаем объект пользователя const userData: User = { id: apiResponse.user.id, email: apiResponse.user.email, first_name: apiResponse.user.first_name, last_name: apiResponse.user.last_name, is_admin: apiResponse.user.is_admin }; setUser(userData); // Обновляем глобальное состояние globalAuthState.isAuthenticated = true; globalAuthState.isAdmin = userData.is_admin; globalAuthState.redirectAttempted = false; console.log("Пользователь авторизован:", userData); console.log("Глобальное состояние обновлено:", globalAuthState); return true; } } console.log("Ошибка входа: неверные учетные данные"); // Сбрасываем глобальное состояние globalAuthState.isAuthenticated = false; globalAuthState.isAdmin = false; return false; } catch (error) { console.error("Ошибка при входе:", error); // Сбрасываем глобальное состояние globalAuthState.isAuthenticated = false; globalAuthState.isAdmin = false; return false; } }; // Функция выхода const logout = () => { localStorage.removeItem("token"); setUser(null); // Сбрасываем глобальное состояние globalAuthState.isAuthenticated = false; globalAuthState.isAdmin = false; globalAuthState.redirectAttempted = false; console.log("Пользователь вышел из системы, глобальное состояние сброшено"); // Добавляем небольшую задержку перед перенаправлением setTimeout(() => { router.push("/admin/login"); }, 100); }; // Проверка на администратора const isAdmin = user?.is_admin || false; const value = { user, loading, isAdmin, login, logout, authChecked }; return ( {children} ); } // Хук для использования контекста аутентификации export function useAuth() { const context = useContext(AuthContext); if (context === undefined) { throw new Error("useAuth должен использоваться внутри AuthProvider"); } return context; } // Компонент для защиты маршрутов администратора export function AdminProtected({ children }: { children: ReactNode }) { const { user, loading, isAdmin, authChecked } = useAuth(); const router = useRouter(); const pathname = usePathname(); const [redirected, setRedirected] = useState(false); const [redirectionTimer, setRedirectionTimer] = useState(null); const redirectAttemptedRef = useRef(false); // Обновляем глобальное состояние при изменении пользователя useEffect(() => { if (user) { globalAuthState.isAuthenticated = true; globalAuthState.isAdmin = isAdmin; } }, [user, isAdmin]); useEffect(() => { // Пропускаем проверку во время загрузки или если проверка аутентификации еще не завершена if (loading || !authChecked) return; console.log("AdminProtected проверка:", { user: user ? `${user.email} (admin: ${user.is_admin})` : "null", isAdmin, pathname, authChecked, redirectAttempted: redirectAttemptedRef.current, globalState: globalAuthState }); // Очищаем предыдущий таймер, если он существует if (redirectionTimer) { clearTimeout(redirectionTimer); setRedirectionTimer(null); } // Если пользователь уже аутентифицирован в глобальном состоянии, пропускаем перенаправление if (globalAuthState.isAuthenticated && globalAuthState.isAdmin) { console.log("Доступ разрешен: пользователь авторизован в глобальном состоянии"); return; } // Предотвращаем бесконечные перенаправления if (redirected || redirectAttemptedRef.current || globalAuthState.redirectAttempted) return; // Если пользователь не авторизован, перенаправляем на страницу входа if (!user) { console.log("Перенаправление: неавторизованный пользователь -> /admin/login"); setRedirected(true); redirectAttemptedRef.current = true; globalAuthState.redirectAttempted = true; // Используем таймер для перенаправления const timer = setTimeout(() => { router.push("/admin/login"); }, 100); setRedirectionTimer(timer); return; } // Если пользователь не админ, перенаправляем на главную страницу if (!isAdmin) { console.log("Перенаправление: авторизованный не-админ -> /"); setRedirected(true); redirectAttemptedRef.current = true; globalAuthState.redirectAttempted = true; // Используем таймер для перенаправления const timer = setTimeout(() => { router.push("/"); }, 100); setRedirectionTimer(timer); return; } // Если пользователь авторизован и является админом, обновляем глобальное состояние globalAuthState.isAuthenticated = true; globalAuthState.isAdmin = true; console.log("Доступ разрешен: авторизованный админ"); }, [user, loading, isAdmin, router, pathname, redirected, authChecked]); // Сбрасываем флаг перенаправления при размонтировании компонента useEffect(() => { return () => { if (redirectionTimer) { clearTimeout(redirectionTimer); } // Сбрасываем флаг при размонтировании redirectAttemptedRef.current = false; }; }, [redirectionTimer]); // Показываем индикатор загрузки if (loading) { return (
Загрузка...
); } // Если пользователь авторизован в глобальном состоянии, рендерим содержимое if (globalAuthState.isAuthenticated && globalAuthState.isAdmin) { return <>{children}; } // Если пользователь не авторизован или не админ, не рендерим содержимое if (!user || !isAdmin) { return (
Перенаправление...
); } // Если все проверки пройдены, рендерим содержимое return <>{children}; }