mirror of
https://github.com/chillpadclub/bedolaga-cabinet.git
synced 2026-07-28 09:33:46 +00:00
PermissionRoute unconditionally sent permission-failing users to /admin. The current router wraps /admin in AdminRoute (no permission check), so this never triggered. But if /admin ever picks up a PermissionRoute guard — even by accident in a future change — the user would loop on itself with replace history (no back-button escape). Detect that case explicitly: if we're already on /admin and still failing the permission check, fall back to / instead. Costs nothing today, prevents a hard-to-diagnose lock-out later.
71 lines
2.4 KiB
TypeScript
71 lines
2.4 KiB
TypeScript
import { Navigate, useLocation } from 'react-router';
|
|
import { useAuthStore } from '../../store/auth';
|
|
import { usePermissionStore } from '../../store/permissions';
|
|
import { saveReturnUrl } from '../../utils/token';
|
|
import PageLoader from '../common/PageLoader';
|
|
import Layout from '../layout/Layout';
|
|
|
|
interface PermissionRouteProps {
|
|
children: React.ReactNode;
|
|
/** Single permission to check */
|
|
permission?: string;
|
|
/** Multiple permissions (OR by default, AND if requireAll is true) */
|
|
permissions?: string[];
|
|
/** When true, ALL listed permissions are required instead of ANY */
|
|
requireAll?: boolean;
|
|
}
|
|
|
|
export function PermissionRoute({
|
|
children,
|
|
permission,
|
|
permissions,
|
|
requireAll = false,
|
|
}: PermissionRouteProps) {
|
|
const isAuthenticated = useAuthStore((state) => state.isAuthenticated);
|
|
const isLoading = useAuthStore((state) => state.isLoading);
|
|
const isAdmin = useAuthStore((state) => state.isAdmin);
|
|
const location = useLocation();
|
|
|
|
const permissionsLoaded = usePermissionStore((state) => state.isLoaded);
|
|
const hasPermission = usePermissionStore((state) => state.hasPermission);
|
|
const hasAnyPermission = usePermissionStore((state) => state.hasAnyPermission);
|
|
const hasAllPermissions = usePermissionStore((state) => state.hasAllPermissions);
|
|
|
|
// Still loading auth state, or admin but permissions not fetched yet
|
|
if (isLoading || (isAdmin && !permissionsLoaded)) {
|
|
return <PageLoader variant="light" />;
|
|
}
|
|
|
|
if (!isAuthenticated) {
|
|
saveReturnUrl();
|
|
return <Navigate to="/login" replace state={{ from: location.pathname }} />;
|
|
}
|
|
|
|
if (!isAdmin) {
|
|
return <Navigate to="/" replace />;
|
|
}
|
|
|
|
// Check permissions if specified
|
|
const needsPermissionCheck = permission || (permissions && permissions.length > 0);
|
|
|
|
if (needsPermissionCheck) {
|
|
let hasAccess = false;
|
|
|
|
if (permission) {
|
|
hasAccess = hasPermission(permission);
|
|
} else if (permissions && permissions.length > 0) {
|
|
hasAccess = requireAll ? hasAllPermissions(...permissions) : hasAnyPermission(...permissions);
|
|
}
|
|
|
|
if (!hasAccess) {
|
|
// Redirect back to the admin landing — unless we're already there
|
|
// (would loop) or the landing itself is what we lack permission for.
|
|
// Fall back to the user dashboard in that case.
|
|
const target = location.pathname === '/admin' ? '/' : '/admin';
|
|
return <Navigate to={target} replace />;
|
|
}
|
|
}
|
|
|
|
return <Layout>{children}</Layout>;
|
|
}
|