David Headrick a5fd034a31 Add mailing functionality and improve authentication
- Implemented Logout and RefreshToken methods in AuthenticationController.
- Added IMailingService and IMailingRepository to Program.cs.
- Updated project structure in Surge365.MassEmailReact.API.csproj.
- Modified API host address and endpoints in Server.http.
- Introduced AuthAppCode in appsettings.json for context distinction.
- Changed GenerateTokens method to async in IAuthService.
- Initialized string properties in User.cs to avoid null values.
- Added new Mailing mapping in DapperConfiguration.cs.
- Created MailingsController for handling mailing operations.
- Developed Mailing, MailingUpdateDto, IMailingService, and IMailingRepository classes.
- Updated frontend with MailingEdit and NewMailings components.
- Enhanced authentication handling in AuthCheck.tsx and AuthContext.tsx.
- Introduced ProtectedPageWrapper for route protection based on roles.
- Added EmailList component for email input validation.
- Updated utils.ts for token and cookie management functions.
- Modified vite.config.ts for new HTTPS certificate name.
- Updated CHANGELOG.md to reflect recent changes.
2025-03-21 07:38:46 -05:00

71 lines
2.6 KiB
TypeScript

// src/components/auth/ProtectedPageWrapper.tsx
import React, { useEffect } from "react";
import { useNavigate } from "react-router-dom";
import { useTitle } from "@/context/TitleContext";
import utils from '@/ts/utils';
// Define role requirements for routes
export const routeRoleRequirements: Record<string, string[]> = {
'/home': [], // No role required
'/servers': ['ServerTab'], // Only Admins
'/targets': ['TargetTab'], // Users or Admins
'/testEmailLists': ['TestListTab'],
'/blockedEmails': ['BlockedEmailTab'],
'/emailDomains': ['DomainTab'],
'/unsubscribeUrls': ['UnsubscribeUrlTab'],
'/templates': ['TemplateTab'],
'/newMailings': ['NewMailingTab'],
'/scheduledMailings': ['ScheduledMailingTab'],
'/activeMailings': ['ActiveMailingTab'],
'/completedMailings': ['CompletedMailingTab'],
};
const ProtectedPageWrapper: React.FC<{ title: string; children: React.ReactNode }> = ({ title, children }) => {
const navigate = useNavigate();
const { setTitle } = useTitle();
const accessToken = localStorage.getItem('accessToken');
const currentPath = window.location.pathname; // Or use useLocation().pathname
useEffect(() => {
setTitle(title);
}, [title, setTitle]);
useEffect(() => {
const checkAuthAndRoles = async () => {
if (!accessToken || utils.isTokenExpired(accessToken)) {
try {
const response = await fetch('/api/authentication/refreshtoken', {
method: 'POST',
credentials: 'include',
});
if (response.ok) {
const data = await response.json();
localStorage.setItem('accessToken', data.accessToken);
} else {
navigate('/login');
}
} catch {
navigate('/login');
}
} else {
// Check roles
const userRoles = utils.getUserRoles(accessToken);
const requiredRoles = routeRoleRequirements[currentPath] || [];
const hasRequiredRole = requiredRoles.length === 0 || requiredRoles.some(role => userRoles.includes(role));
if (!hasRequiredRole) {
navigate('/home'); // Redirect to home if unauthorized
}
}
};
checkAuthAndRoles();
}, [navigate, accessToken, currentPath]);
if (!accessToken || utils.isTokenExpired(accessToken)) {
return null; // Or a loading spinner
}
return <>{children}</>;
};
export default ProtectedPageWrapper;