import React, { createContext, useContext, useState, useEffect } from "react"; import toast from "react-hot-toast"; import apiService from "~/lib/api"; interface User { id: number; name: string; family: string; email: string; username: string; mobile?: string; nationalCode?: string; status: boolean; customTheme?: string; } interface Token { id: number; accessToken: string; expAccessTokenStamp: string; expAccessToken: string; refreshToken: string; expRefreshToken: string; expRefreshTokenStamp: string; } interface AuthContextType { user: User | null; token: Token | null; isAuthenticated: boolean; isLoading: boolean; login: (username: string, password: string) => Promise; logout: () => void; validateToken: () => Promise; } const AuthContext = createContext(undefined); interface AuthProviderProps { children: React.ReactNode; } export function AuthProvider({ children }: AuthProviderProps) { const [user, setUser] = useState(null); const [token, setToken] = useState(null); const [isLoading, setIsLoading] = useState(true); // Token validation function const validateToken = async (tokenToValidate?: Token): Promise => { const currentToken = tokenToValidate || token; if (!currentToken || !currentToken.accessToken) { return false; } try { // Check if token is expired using the expAccessTokenStamp const expirationDate = new Date(currentToken.expAccessTokenStamp); const currentDate = new Date(); if (expirationDate <= currentDate) { // Token is expired clearAuthData(); return false; } return true; } catch (error) { console.error("Token validation error:", error); return false; } }; const clearAuthData = () => { setUser(null); setToken(null); localStorage.removeItem("auth_user"); localStorage.removeItem("auth_token"); }; useEffect(() => { const initAuth = async () => { try { // Check for existing user and token in localStorage on mount const savedUser = localStorage.getItem("auth_user"); const savedToken = localStorage.getItem("auth_token"); if (savedUser && savedToken) { try { const userData = JSON.parse(savedUser); const tokenData = JSON.parse(savedToken); // Validate the saved token const isValidToken = await validateToken(tokenData); if (isValidToken) { setUser(userData); setToken(tokenData); } else { // Token is invalid, clear auth data clearAuthData(); } } catch (error) { console.error("Error parsing saved user data:", error); clearAuthData(); } } } catch (error) { console.error("Auth initialization error:", error); clearAuthData(); } finally { setIsLoading(false); } }; initAuth(); }, []); // Auto-validate token every 5 minutes useEffect(() => { if (!token || !user) return; const interval = setInterval( async () => { const isValid = await validateToken(); if (!isValid) { clearAuthData(); } }, 5 * 60 * 1000, ); // 5 minutes return () => clearInterval(interval); }, [token, user]); const login = async ( username: string, password: string, ): Promise => { if (!username || !password) { toast.error("لطفاً تمام فیلدها را پر کنید"); return false; } setIsLoading(true); try { const result = await apiService.login(username, password); if (result.success && result.data) { const tokenData: Token = { id: result.data.Token.ID, accessToken: result.data.Token.AccessToken, expAccessTokenStamp: result.data.Token.ExpAccessTokenStamp, expAccessToken: result.data.Token.ExpAccessToken, refreshToken: result.data.Token.RefreshToken, expRefreshToken: result.data.Token.ExpRefreshToken, expRefreshTokenStamp: result.data.Token.ExpRefreshTokenStamp, }; const userData: User = { id: result.data.Person.ID, name: result.data.Person.Name, family: result.data.Person.Family, email: result.data.Person.Email, username: result.data.Person.Username, mobile: result.data.Person.Mobile, nationalCode: result.data.Person.NationalCode, status: result.data.Person.Status, customTheme: result.data.Person.CustomeTheme, }; // Validate the received token const isValidToken = await validateToken(tokenData); if (!isValidToken) { toast.error("توکن دریافتی نامعتبر است"); return false; } setUser(userData); setToken(tokenData); // Save to localStorage localStorage.setItem("auth_user", JSON.stringify(userData)); localStorage.setItem("auth_token", JSON.stringify(tokenData)); toast.success(`خوش آمدید ${userData.name} ${userData.family}!`); return true; } else { toast.error(result.message || "نام کاربری یا رمز عبور اشتباه است"); return false; } } catch (error) { console.error("Login error:", error); const errorMessage = error instanceof Error ? error.message : "خطای غیرمنتظره رخ داد"; toast.error(errorMessage); return false; } finally { setIsLoading(false); } }; const logout = async () => { try { await apiService.logout(); } catch (error) { console.error("Logout error:", error); } finally { // mark logout event to suppress next auth-required toast from guard try { sessionStorage.setItem("justLoggedOut", "1"); } catch {} clearAuthData(); toast.success("با موفقیت خارج شدید", { id: "logout-success" }); } }; const value: AuthContextType = { user, isAuthenticated: !!user && !!token, isLoading, login, logout, token, validateToken, }; return {children}; } export function useAuth() { const context = useContext(AuthContext); if (context === undefined) { throw new Error("useAuth must be used within an AuthProvider"); } return context; }