274 lines
7.3 KiB
TypeScript
274 lines
7.3 KiB
TypeScript
import { routes, routeMatchers } from '../routing/RouteConfig';
|
|
|
|
/**
|
|
* RouteCatalog exposes route IDs and patterns for matching
|
|
*
|
|
* Route IDs follow the pattern: 'category.routeName'
|
|
* Examples:
|
|
* - 'auth.login' → '/auth/login'
|
|
* - 'protected.dashboard' → '/dashboard'
|
|
* - 'league.detail' → '/leagues/[id]' (pattern)
|
|
*/
|
|
export class RouteCatalog {
|
|
/**
|
|
* List all public route IDs
|
|
* Public routes are accessible without authentication
|
|
*/
|
|
listPublicRoutes(): string[] {
|
|
return [
|
|
'public.home',
|
|
'public.leagues',
|
|
'public.drivers',
|
|
'public.teams',
|
|
'public.leaderboards',
|
|
'public.races',
|
|
'public.sponsorSignup',
|
|
'auth.login',
|
|
'auth.signup',
|
|
'auth.forgotPassword',
|
|
'auth.resetPassword',
|
|
'auth.iRacingStart',
|
|
'auth.iRacingCallback',
|
|
'error.notFound',
|
|
'error.serverError',
|
|
// Parameterized public routes
|
|
'league.detail',
|
|
'league.rulebook',
|
|
'league.schedule',
|
|
'league.standings',
|
|
'driver.detail',
|
|
'team.detail',
|
|
'race.detail',
|
|
'race.results',
|
|
'race.all',
|
|
];
|
|
}
|
|
|
|
/**
|
|
* List all protected route IDs
|
|
* Protected routes require authentication
|
|
*/
|
|
listProtectedRoutes(): string[] {
|
|
return [
|
|
'protected.dashboard',
|
|
'protected.onboarding',
|
|
'protected.profile',
|
|
'protected.profileSettings',
|
|
'protected.profileLeagues',
|
|
'protected.profileLiveries',
|
|
'protected.profileLiveryUpload',
|
|
'protected.profileSponsorshipRequests',
|
|
'sponsor.root',
|
|
'sponsor.dashboard',
|
|
'sponsor.billing',
|
|
'sponsor.campaigns',
|
|
'sponsor.leagues',
|
|
'sponsor.settings',
|
|
'admin.root',
|
|
'admin.users',
|
|
'league.create',
|
|
'race.root',
|
|
'team.root',
|
|
'team.leaderboard',
|
|
];
|
|
}
|
|
|
|
/**
|
|
* List all admin route IDs
|
|
* Admin routes require admin-level permissions
|
|
*/
|
|
listAdminRoutes(): string[] {
|
|
return [
|
|
'admin.root',
|
|
'admin.users',
|
|
'league.rosterAdmin',
|
|
'league.scheduleAdmin',
|
|
'league.stewarding',
|
|
'league.settings',
|
|
'league.sponsorships',
|
|
'league.wallet',
|
|
'race.stewarding',
|
|
];
|
|
}
|
|
|
|
/**
|
|
* List all sponsor route IDs
|
|
* Sponsor routes require sponsor role
|
|
*/
|
|
listSponsorRoutes(): string[] {
|
|
return [
|
|
'sponsor.root',
|
|
'sponsor.dashboard',
|
|
'sponsor.billing',
|
|
'sponsor.campaigns',
|
|
'sponsor.leagues',
|
|
'sponsor.settings',
|
|
];
|
|
}
|
|
|
|
/**
|
|
* Get the path pattern for a route ID
|
|
* @param routeId - Route ID in format 'category.routeName'
|
|
* @returns Path pattern (e.g., '/auth/login' or '/leagues/[id]')
|
|
* @throws Error if route ID is unknown
|
|
*/
|
|
getPattern(routeId: string): string {
|
|
const parts = routeId.split('.');
|
|
let route: any = routes;
|
|
|
|
for (const part of parts) {
|
|
route = route[part];
|
|
if (!route) {
|
|
throw new Error(`Unknown route ID: ${routeId}`);
|
|
}
|
|
}
|
|
|
|
// Handle parameterized routes
|
|
if (typeof route === 'function') {
|
|
// Return pattern with placeholder
|
|
const paramPattern = route('placeholder');
|
|
return paramPattern.replace('/placeholder', '/[id]');
|
|
}
|
|
|
|
return route as string;
|
|
}
|
|
|
|
/**
|
|
* Check if a path is an auth page
|
|
* @param logicalPath - Path to check
|
|
* @returns True if path is an auth page
|
|
*/
|
|
isAuthPage(logicalPath: string): boolean {
|
|
return routeMatchers.isInGroup(logicalPath, 'auth');
|
|
}
|
|
|
|
/**
|
|
* Get all route patterns with their IDs
|
|
* @returns Array of route patterns with IDs
|
|
*/
|
|
getAllPatterns(): Array<{ routeId: string; pattern: string }> {
|
|
const patterns: Array<{ routeId: string; pattern: string }> = [];
|
|
|
|
// Helper to traverse routes and build patterns
|
|
const traverse = (obj: any, prefix: string) => {
|
|
for (const [key, value] of Object.entries(obj)) {
|
|
const routeId = prefix ? `${prefix}.${key}` : key;
|
|
|
|
if (typeof value === 'function') {
|
|
// Parameterized route
|
|
const pattern = value('placeholder').replace('/placeholder', '/[id]');
|
|
patterns.push({ routeId, pattern });
|
|
} else if (typeof value === 'object' && value !== null) {
|
|
// Nested category
|
|
traverse(value, routeId);
|
|
} else if (typeof value === 'string') {
|
|
// Simple route
|
|
patterns.push({ routeId, pattern: value });
|
|
}
|
|
}
|
|
};
|
|
|
|
traverse(routes, '');
|
|
return patterns;
|
|
}
|
|
|
|
/**
|
|
* Get route ID by path
|
|
* @param path - Path to find
|
|
* @returns Route ID or null if not found
|
|
*
|
|
* Note: This method prioritizes exact matches over parameterized matches.
|
|
* For example, '/leagues/create' will match 'league.create' before 'league.detail'.
|
|
*/
|
|
getRouteIdByPath(path: string): string | null {
|
|
const allPatterns = this.getAllPatterns();
|
|
|
|
// First, try exact matches
|
|
for (const { routeId, pattern } of allPatterns) {
|
|
if (pattern === path) {
|
|
return routeId;
|
|
}
|
|
}
|
|
|
|
// Then, try parameterized matches
|
|
for (const { routeId, pattern } of allPatterns) {
|
|
if (pattern.includes('[')) {
|
|
const paramPattern = pattern.replace(/\[([^\]]+)\]/g, '([^/]+)');
|
|
const regex = new RegExp(`^${paramPattern}$`);
|
|
if (regex.test(path)) {
|
|
return routeId;
|
|
}
|
|
}
|
|
}
|
|
|
|
return null;
|
|
}
|
|
|
|
/**
|
|
* Check if a path requires specific role-based access
|
|
* @param logicalPath - Path to check
|
|
* @returns Array of required roles or null
|
|
*/
|
|
getRequiredRoles(logicalPath: string): string[] | null {
|
|
// Check admin routes
|
|
if (routeMatchers.isInGroup(logicalPath, 'admin')) {
|
|
return ['system-owner', 'super-admin', 'league-admin'];
|
|
}
|
|
|
|
// Check sponsor routes
|
|
if (routeMatchers.isInGroup(logicalPath, 'sponsor')) {
|
|
return ['sponsor'];
|
|
}
|
|
|
|
// Check league admin routes (specific patterns)
|
|
if (logicalPath.match(/\/leagues\/[^/]+\/(roster\/admin|schedule\/admin|stewarding|settings|sponsorships|wallet)/)) {
|
|
return ['system-owner', 'super-admin', 'league-admin'];
|
|
}
|
|
|
|
// Check race stewarding routes
|
|
if (logicalPath.match(/\/races\/[^/]+\/stewarding/)) {
|
|
return ['system-owner', 'super-admin', 'league-steward'];
|
|
}
|
|
|
|
// Public or auth-only routes (no specific role)
|
|
return null;
|
|
}
|
|
|
|
/**
|
|
* Get the home path for a specific role
|
|
* @param role - The role name
|
|
* @returns The logical path for that role's home page
|
|
*/
|
|
getRoleHome(role: string): string {
|
|
const roleHomeMap: Record<string, string> = {
|
|
'driver': '/dashboard',
|
|
'sponsor': '/sponsor/dashboard',
|
|
'league-admin': '/admin',
|
|
'league-steward': '/admin',
|
|
'league-owner': '/admin',
|
|
'system-owner': '/admin',
|
|
'super-admin': '/admin',
|
|
};
|
|
|
|
return roleHomeMap[role] || '/dashboard';
|
|
}
|
|
|
|
/**
|
|
* Get the route ID for a specific role's home page
|
|
* @param role - The role name
|
|
* @returns The route ID for that role's home page
|
|
*/
|
|
getRoleHomeRouteId(role: string): string {
|
|
const roleHomeRouteMap: Record<string, string> = {
|
|
'driver': 'protected.dashboard',
|
|
'sponsor': 'sponsor.dashboard',
|
|
'league-admin': 'admin',
|
|
'league-steward': 'admin',
|
|
'league-owner': 'admin',
|
|
'system-owner': 'admin',
|
|
'super-admin': 'admin',
|
|
};
|
|
|
|
return roleHomeRouteMap[role] || 'protected.dashboard';
|
|
}
|
|
} |