mirror of
https://github.com/Dvorinka/MyClubServer.git
synced 2026-06-04 02:32:57 +00:00
58 lines
1.7 KiB
TypeScript
58 lines
1.7 KiB
TypeScript
import { Navigate, useLocation } from 'react-router-dom';
|
|
import { useAuth } from '../contexts/AuthContext';
|
|
import { useEffect, useState } from 'react';
|
|
import { getSetupStatus } from '../services/setup';
|
|
|
|
interface ProtectedRouteProps {
|
|
children: JSX.Element;
|
|
requiredRole?: string;
|
|
}
|
|
|
|
const ProtectedRoute: React.FC<ProtectedRouteProps> = ({ children, requiredRole }) => {
|
|
const { isAuthenticated, isLoading, user } = useAuth();
|
|
const location = useLocation();
|
|
const [checkingSetup, setCheckingSetup] = useState(true);
|
|
const [requiresSetup, setRequiresSetup] = useState<boolean>(false);
|
|
|
|
// Check if setup is required
|
|
useEffect(() => {
|
|
let mounted = true;
|
|
(async () => {
|
|
try {
|
|
const s = await getSetupStatus();
|
|
if (mounted) setRequiresSetup(!!s.requires_setup);
|
|
} catch (_) {
|
|
if (mounted) setRequiresSetup(false);
|
|
} finally {
|
|
if (mounted) setCheckingSetup(false);
|
|
}
|
|
})();
|
|
return () => { mounted = false; };
|
|
}, []);
|
|
|
|
if (isLoading || checkingSetup) {
|
|
// Show loading spinner or skeleton
|
|
return <div>Načítání…</div>;
|
|
}
|
|
|
|
// If setup is required, redirect to setup page
|
|
if (requiresSetup) {
|
|
return <Navigate to="/setup" replace />;
|
|
}
|
|
|
|
if (!isAuthenticated) {
|
|
// Redirect to login page, but save the current location
|
|
return <Navigate to="/login" state={{ from: location }} replace />;
|
|
}
|
|
|
|
// Role-based access control
|
|
if (requiredRole && user && user.role && user.role !== requiredRole && user.role !== 'admin') {
|
|
// Redirect to 403 Forbidden page
|
|
return <Navigate to="/403" state={{ from: location.pathname }} replace />;
|
|
}
|
|
|
|
return children;
|
|
};
|
|
|
|
export default ProtectedRoute;
|