136 lines
3.4 KiB
TypeScript
136 lines
3.4 KiB
TypeScript
import { useEffect } from "react";
|
|
import { useAuth } from "~/contexts/auth-context";
|
|
import { useNavigate, useLocation } from "react-router";
|
|
import toast from "react-hot-toast";
|
|
|
|
interface UseRouteGuardOptions {
|
|
requireAuth?: boolean;
|
|
redirectTo?: string;
|
|
showToast?: boolean;
|
|
}
|
|
|
|
export function useRouteGuard(options: UseRouteGuardOptions = {}) {
|
|
const {
|
|
requireAuth = true,
|
|
redirectTo = "/login",
|
|
showToast = true,
|
|
} = options;
|
|
|
|
const { isAuthenticated, isLoading, token, user } = useAuth();
|
|
const navigate = useNavigate();
|
|
const location = useLocation();
|
|
|
|
useEffect(() => {
|
|
// Don't do anything while loading
|
|
if (isLoading) return;
|
|
|
|
// If authentication is required but user is not authenticated
|
|
if (requireAuth && !isAuthenticated) {
|
|
if (showToast) {
|
|
toast.error("برای دسترسی به این صفحه باید وارد شوید");
|
|
}
|
|
|
|
// Save the current location so we can redirect back after login
|
|
const currentPath = location.pathname + location.search;
|
|
const loginPath =
|
|
redirectTo === "/login"
|
|
? `${redirectTo}?returnTo=${encodeURIComponent(currentPath)}`
|
|
: redirectTo;
|
|
|
|
navigate(loginPath, { replace: true });
|
|
return;
|
|
}
|
|
|
|
// If authentication is required but token is missing/invalid
|
|
if (requireAuth && isAuthenticated && !token) {
|
|
if (showToast) {
|
|
toast.error("جلسه کاری شما منقضی شده است. لطفاً دوباره وارد شوید");
|
|
}
|
|
|
|
// Clear any stored authentication data
|
|
localStorage.removeItem("auth_user");
|
|
localStorage.removeItem("auth_token");
|
|
|
|
navigate("/login", { replace: true });
|
|
return;
|
|
}
|
|
|
|
// If user is authenticated but trying to access login page
|
|
if (!requireAuth && isAuthenticated && location.pathname === "/login") {
|
|
navigate("/dashboard", { replace: true });
|
|
return;
|
|
}
|
|
}, [
|
|
isLoading,
|
|
isAuthenticated,
|
|
token,
|
|
requireAuth,
|
|
redirectTo,
|
|
showToast,
|
|
navigate,
|
|
location.pathname,
|
|
location.search,
|
|
]);
|
|
|
|
return {
|
|
isAuthenticated,
|
|
isLoading,
|
|
token,
|
|
user,
|
|
canAccess: requireAuth ? isAuthenticated && !!token?.accessToken : true,
|
|
};
|
|
}
|
|
|
|
// Helper hook for protected routes
|
|
export function useProtectedRoute(redirectTo?: string) {
|
|
return useRouteGuard({
|
|
requireAuth: true,
|
|
redirectTo,
|
|
showToast: true,
|
|
});
|
|
}
|
|
|
|
// Helper hook for public routes (like login)
|
|
export function usePublicRoute() {
|
|
return useRouteGuard({
|
|
requireAuth: false,
|
|
showToast: false,
|
|
});
|
|
}
|
|
|
|
// Hook to check if user has specific permissions
|
|
export function usePermissionGuard(
|
|
requiredPermissions: string[] = [],
|
|
userPermissions: string[] = [],
|
|
) {
|
|
const { isAuthenticated, token } = useAuth();
|
|
const navigate = useNavigate();
|
|
|
|
const hasPermission = requiredPermissions.every((permission) =>
|
|
userPermissions.includes(permission),
|
|
);
|
|
|
|
useEffect(() => {
|
|
if (
|
|
isAuthenticated &&
|
|
token?.accessToken &&
|
|
!hasPermission &&
|
|
requiredPermissions.length > 0
|
|
) {
|
|
toast.error("شما دسترسی لازم برای این صفحه را ندارید");
|
|
navigate("/dashboard", { replace: true });
|
|
}
|
|
}, [
|
|
isAuthenticated,
|
|
token,
|
|
hasPermission,
|
|
requiredPermissions.length,
|
|
navigate,
|
|
]);
|
|
|
|
return {
|
|
hasPermission,
|
|
canAccess: isAuthenticated && !!token?.accessToken && hasPermission,
|
|
};
|
|
}
|