website refactor
This commit is contained in:
@@ -1,7 +1,7 @@
|
|||||||
import 'reflect-metadata';
|
import 'reflect-metadata';
|
||||||
|
|
||||||
import { Test } from '@nestjs/testing';
|
import { Test } from '@nestjs/testing';
|
||||||
import { afterEach, beforeEach, describe, expect, it, vi } from 'vitest';
|
import { afterEach, beforeEach, describe, expect, it } from 'vitest';
|
||||||
|
|
||||||
import { LeagueModule } from './LeagueModule';
|
import { LeagueModule } from './LeagueModule';
|
||||||
import {
|
import {
|
||||||
|
|||||||
@@ -79,6 +79,42 @@ describe('RouteConfig', () => {
|
|||||||
expect(routeMatchers.isPublic('/admin')).toBe(false);
|
expect(routeMatchers.isPublic('/admin')).toBe(false);
|
||||||
expect(routeMatchers.isPublic('/sponsor/dashboard')).toBe(false);
|
expect(routeMatchers.isPublic('/sponsor/dashboard')).toBe(false);
|
||||||
});
|
});
|
||||||
|
|
||||||
|
it('should return true for league sub-pages (schedule, standings, roster, rulebook)', () => {
|
||||||
|
// Test with various league IDs
|
||||||
|
const leagueId = '123';
|
||||||
|
const anotherLeagueId = 'abc-def-456';
|
||||||
|
|
||||||
|
// Schedule page
|
||||||
|
expect(routeMatchers.isPublic(`/leagues/${leagueId}/schedule`)).toBe(true);
|
||||||
|
expect(routeMatchers.isPublic(`/leagues/${anotherLeagueId}/schedule`)).toBe(true);
|
||||||
|
|
||||||
|
// Standings page
|
||||||
|
expect(routeMatchers.isPublic(`/leagues/${leagueId}/standings`)).toBe(true);
|
||||||
|
expect(routeMatchers.isPublic(`/leagues/${anotherLeagueId}/standings`)).toBe(true);
|
||||||
|
|
||||||
|
// Roster page
|
||||||
|
expect(routeMatchers.isPublic(`/leagues/${leagueId}/roster`)).toBe(true);
|
||||||
|
expect(routeMatchers.isPublic(`/leagues/${anotherLeagueId}/roster`)).toBe(true);
|
||||||
|
|
||||||
|
// Rulebook page
|
||||||
|
expect(routeMatchers.isPublic(`/leagues/${leagueId}/rulebook`)).toBe(true);
|
||||||
|
expect(routeMatchers.isPublic(`/leagues/${anotherLeagueId}/rulebook`)).toBe(true);
|
||||||
|
});
|
||||||
|
|
||||||
|
it('should return true for league detail page', () => {
|
||||||
|
expect(routeMatchers.isPublic('/leagues/123')).toBe(true);
|
||||||
|
expect(routeMatchers.isPublic('/leagues/abc-def')).toBe(true);
|
||||||
|
});
|
||||||
|
|
||||||
|
it('should return false for league admin pages', () => {
|
||||||
|
expect(routeMatchers.isPublic('/leagues/123/schedule/admin')).toBe(false);
|
||||||
|
expect(routeMatchers.isPublic('/leagues/123/roster/admin')).toBe(false);
|
||||||
|
expect(routeMatchers.isPublic('/leagues/123/settings')).toBe(false);
|
||||||
|
expect(routeMatchers.isPublic('/leagues/123/sponsorships')).toBe(false);
|
||||||
|
expect(routeMatchers.isPublic('/leagues/123/stewarding')).toBe(false);
|
||||||
|
expect(routeMatchers.isPublic('/leagues/123/wallet')).toBe(false);
|
||||||
|
});
|
||||||
});
|
});
|
||||||
|
|
||||||
describe('routeMatchers.requiresAuth()', () => {
|
describe('routeMatchers.requiresAuth()', () => {
|
||||||
|
|||||||
@@ -15,90 +15,91 @@ import { ConsoleLogger } from '@/lib/infrastructure/logging/ConsoleLogger';
|
|||||||
const logger = new ConsoleLogger();
|
const logger = new ConsoleLogger();
|
||||||
|
|
||||||
export interface RouteDefinition {
|
export interface RouteDefinition {
|
||||||
path: string;
|
path: string;
|
||||||
name: string;
|
name: string;
|
||||||
description?: string;
|
description?: string;
|
||||||
}
|
}
|
||||||
|
|
||||||
export interface RouteGroup {
|
export interface RouteGroup {
|
||||||
auth: {
|
auth: {
|
||||||
login: string;
|
login: string;
|
||||||
signup: string;
|
signup: string;
|
||||||
forgotPassword: string;
|
forgotPassword: string;
|
||||||
resetPassword: string;
|
resetPassword: string;
|
||||||
};
|
};
|
||||||
public: {
|
public: {
|
||||||
home: string;
|
home: string;
|
||||||
leagues: string;
|
leagues: string;
|
||||||
drivers: string;
|
drivers: string;
|
||||||
teams: string;
|
teams: string;
|
||||||
leaderboards: string;
|
leaderboards: string;
|
||||||
races: string;
|
races: string;
|
||||||
sponsorSignup: string;
|
sponsorSignup: string;
|
||||||
};
|
};
|
||||||
protected: {
|
protected: {
|
||||||
dashboard: string;
|
dashboard: string;
|
||||||
onboarding: string;
|
onboarding: string;
|
||||||
profile: string;
|
profile: string;
|
||||||
profileSettings: string;
|
profileSettings: string;
|
||||||
profileLeagues: string;
|
profileLeagues: string;
|
||||||
profileLiveries: string;
|
profileLiveries: string;
|
||||||
profileLiveryUpload: string;
|
profileLiveryUpload: string;
|
||||||
profileSponsorshipRequests: string;
|
profileSponsorshipRequests: string;
|
||||||
};
|
};
|
||||||
sponsor: {
|
sponsor: {
|
||||||
root: string;
|
root: string;
|
||||||
dashboard: string;
|
dashboard: string;
|
||||||
billing: string;
|
billing: string;
|
||||||
campaigns: string;
|
campaigns: string;
|
||||||
leagues: string;
|
leagues: string;
|
||||||
leagueDetail: (id: string) => string;
|
leagueDetail: (id: string) => string;
|
||||||
settings: string;
|
settings: string;
|
||||||
};
|
};
|
||||||
admin: {
|
admin: {
|
||||||
root: string;
|
root: string;
|
||||||
users: string;
|
users: string;
|
||||||
};
|
};
|
||||||
league: {
|
league: {
|
||||||
detail: (id: string) => string;
|
detail: (id: string) => string;
|
||||||
rosterAdmin: (id: string) => string;
|
roster: (id: string) => string;
|
||||||
rulebook: (id: string) => string;
|
rosterAdmin: (id: string) => string;
|
||||||
schedule: (id: string) => string;
|
rulebook: (id: string) => string;
|
||||||
scheduleAdmin: (id: string) => string;
|
schedule: (id: string) => string;
|
||||||
settings: (id: string) => string;
|
scheduleAdmin: (id: string) => string;
|
||||||
sponsorships: (id: string) => string;
|
settings: (id: string) => string;
|
||||||
standings: (id: string) => string;
|
sponsorships: (id: string) => string;
|
||||||
stewarding: (id: string) => string;
|
standings: (id: string) => string;
|
||||||
wallet: (id: string) => string;
|
stewarding: (id: string) => string;
|
||||||
create: string;
|
wallet: (id: string) => string;
|
||||||
migration: string;
|
create: string;
|
||||||
};
|
migration: string;
|
||||||
race: {
|
};
|
||||||
root: string;
|
race: {
|
||||||
all: string;
|
root: string;
|
||||||
detail: (id: string) => string;
|
all: string;
|
||||||
results: (id: string) => string;
|
detail: (id: string) => string;
|
||||||
stewarding: (id: string) => string;
|
results: (id: string) => string;
|
||||||
};
|
stewarding: (id: string) => string;
|
||||||
team: {
|
};
|
||||||
root: string;
|
team: {
|
||||||
leaderboard: string;
|
root: string;
|
||||||
detail: (id: string) => string;
|
leaderboard: string;
|
||||||
create: string;
|
detail: (id: string) => string;
|
||||||
};
|
create: string;
|
||||||
driver: {
|
};
|
||||||
root: string;
|
driver: {
|
||||||
detail: (id: string) => string;
|
root: string;
|
||||||
};
|
detail: (id: string) => string;
|
||||||
leaderboards: {
|
};
|
||||||
root: string;
|
leaderboards: {
|
||||||
drivers: string;
|
root: string;
|
||||||
teams: string;
|
drivers: string;
|
||||||
};
|
teams: string;
|
||||||
error: {
|
};
|
||||||
notFound: string;
|
error: {
|
||||||
serverError: string;
|
notFound: string;
|
||||||
};
|
serverError: string;
|
||||||
|
};
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
@@ -121,239 +122,259 @@ export interface RouteGroup {
|
|||||||
* ```
|
* ```
|
||||||
*/
|
*/
|
||||||
export const routes: RouteGroup & { leaderboards: { root: string; drivers: string; teams: string } } = {
|
export const routes: RouteGroup & { leaderboards: { root: string; drivers: string; teams: string } } = {
|
||||||
auth: {
|
auth: {
|
||||||
login: '/auth/login',
|
login: '/auth/login',
|
||||||
signup: '/auth/signup',
|
signup: '/auth/signup',
|
||||||
forgotPassword: '/auth/forgot-password',
|
forgotPassword: '/auth/forgot-password',
|
||||||
resetPassword: '/auth/reset-password',
|
resetPassword: '/auth/reset-password',
|
||||||
},
|
},
|
||||||
public: {
|
public: {
|
||||||
home: '/',
|
home: '/',
|
||||||
leagues: '/leagues',
|
leagues: '/leagues',
|
||||||
drivers: '/drivers',
|
drivers: '/drivers',
|
||||||
teams: '/teams',
|
teams: '/teams',
|
||||||
leaderboards: '/leaderboards',
|
leaderboards: '/leaderboards',
|
||||||
races: '/races',
|
races: '/races',
|
||||||
sponsorSignup: '/sponsor/signup',
|
sponsorSignup: '/sponsor/signup',
|
||||||
},
|
},
|
||||||
protected: {
|
protected: {
|
||||||
dashboard: '/dashboard',
|
dashboard: '/dashboard',
|
||||||
onboarding: '/onboarding',
|
onboarding: '/onboarding',
|
||||||
profile: '/profile',
|
profile: '/profile',
|
||||||
profileSettings: '/profile/settings',
|
profileSettings: '/profile/settings',
|
||||||
profileLeagues: '/profile/leagues',
|
profileLeagues: '/profile/leagues',
|
||||||
profileLiveries: '/profile/liveries',
|
profileLiveries: '/profile/liveries',
|
||||||
profileLiveryUpload: '/profile/liveries/upload',
|
profileLiveryUpload: '/profile/liveries/upload',
|
||||||
profileSponsorshipRequests: '/profile/sponsorship-requests',
|
profileSponsorshipRequests: '/profile/sponsorship-requests',
|
||||||
},
|
},
|
||||||
sponsor: {
|
sponsor: {
|
||||||
root: '/sponsor',
|
root: '/sponsor',
|
||||||
dashboard: '/sponsor/dashboard',
|
dashboard: '/sponsor/dashboard',
|
||||||
billing: '/sponsor/billing',
|
billing: '/sponsor/billing',
|
||||||
campaigns: '/sponsor/campaigns',
|
campaigns: '/sponsor/campaigns',
|
||||||
leagues: '/sponsor/leagues',
|
leagues: '/sponsor/leagues',
|
||||||
leagueDetail: (id: string) => `/sponsor/leagues/${id}`,
|
leagueDetail: (id: string) => `/sponsor/leagues/${id}`,
|
||||||
settings: '/sponsor/settings',
|
settings: '/sponsor/settings',
|
||||||
},
|
},
|
||||||
admin: {
|
admin: {
|
||||||
root: '/admin',
|
root: '/admin',
|
||||||
users: '/admin/users',
|
users: '/admin/users',
|
||||||
},
|
},
|
||||||
league: {
|
league: {
|
||||||
detail: (id: string) => `/leagues/${id}`,
|
detail: (id: string) => `/leagues/${id}`,
|
||||||
rosterAdmin: (id: string) => `/leagues/${id}/roster/admin`,
|
roster: (id: string) => `/leagues/${id}/roster`,
|
||||||
rulebook: (id: string) => `/leagues/${id}/rulebook`,
|
rosterAdmin: (id: string) => `/leagues/${id}/roster/admin`,
|
||||||
schedule: (id: string) => `/leagues/${id}/schedule`,
|
rulebook: (id: string) => `/leagues/${id}/rulebook`,
|
||||||
scheduleAdmin: (id: string) => `/leagues/${id}/schedule/admin`,
|
schedule: (id: string) => `/leagues/${id}/schedule`,
|
||||||
settings: (id: string) => `/leagues/${id}/settings`,
|
scheduleAdmin: (id: string) => `/leagues/${id}/schedule/admin`,
|
||||||
sponsorships: (id: string) => `/leagues/${id}/sponsorships`,
|
settings: (id: string) => `/leagues/${id}/settings`,
|
||||||
standings: (id: string) => `/leagues/${id}/standings`,
|
sponsorships: (id: string) => `/leagues/${id}/sponsorships`,
|
||||||
stewarding: (id: string) => `/leagues/${id}/stewarding`,
|
standings: (id: string) => `/leagues/${id}/standings`,
|
||||||
wallet: (id: string) => `/leagues/${id}/wallet`,
|
stewarding: (id: string) => `/leagues/${id}/stewarding`,
|
||||||
create: '/leagues/create',
|
wallet: (id: string) => `/leagues/${id}/wallet`,
|
||||||
migration: '/leagues/migration',
|
create: '/leagues/create',
|
||||||
},
|
migration: '/leagues/migration',
|
||||||
race: {
|
},
|
||||||
root: '/races',
|
race: {
|
||||||
all: '/races/all',
|
root: '/races',
|
||||||
detail: (id: string) => `/races/${id}`,
|
all: '/races/all',
|
||||||
results: (id: string) => `/races/${id}/results`,
|
detail: (id: string) => `/races/${id}`,
|
||||||
stewarding: (id: string) => `/races/${id}/stewarding`,
|
results: (id: string) => `/races/${id}/results`,
|
||||||
},
|
stewarding: (id: string) => `/races/${id}/stewarding`,
|
||||||
team: {
|
},
|
||||||
root: '/teams',
|
team: {
|
||||||
leaderboard: '/teams/leaderboard',
|
root: '/teams',
|
||||||
detail: (id: string) => `/teams/${id}`,
|
leaderboard: '/teams/leaderboard',
|
||||||
create: '/teams/create',
|
detail: (id: string) => `/teams/${id}`,
|
||||||
},
|
create: '/teams/create',
|
||||||
driver: {
|
},
|
||||||
root: '/drivers',
|
driver: {
|
||||||
detail: (id: string) => `/drivers/${id}`,
|
root: '/drivers',
|
||||||
},
|
detail: (id: string) => `/drivers/${id}`,
|
||||||
leaderboards: {
|
},
|
||||||
root: '/leaderboards',
|
leaderboards: {
|
||||||
drivers: '/leaderboards/drivers',
|
root: '/leaderboards',
|
||||||
teams: '/leaderboards/teams',
|
drivers: '/leaderboards/drivers',
|
||||||
},
|
teams: '/leaderboards/teams',
|
||||||
error: {
|
},
|
||||||
notFound: '/404',
|
error: {
|
||||||
serverError: '/500',
|
notFound: '/404',
|
||||||
},
|
serverError: '/500',
|
||||||
|
},
|
||||||
};
|
};
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Route matcher utilities for pattern matching
|
* Route matcher utilities for pattern matching
|
||||||
*/
|
*/
|
||||||
export const routeMatchers = {
|
export const routeMatchers = {
|
||||||
/**
|
/**
|
||||||
* Check if path matches a pattern
|
* Check if path matches a pattern
|
||||||
*/
|
*/
|
||||||
matches(path: string, pattern: string): boolean {
|
matches(path: string, pattern: string): boolean {
|
||||||
// Exact match
|
// Exact match
|
||||||
if (pattern === path) return true;
|
if (pattern === path) return true;
|
||||||
|
|
||||||
// Wildcard match (starts with)
|
// Wildcard match (ends with /*)
|
||||||
if (pattern.endsWith('/*') && path.startsWith(pattern.slice(0, -2))) {
|
if (pattern.endsWith('/*') && path.startsWith(pattern.slice(0, -2))) {
|
||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
|
|
||||||
// Parameterized match (e.g., /leagues/[id])
|
// Wildcard match (contains /* in the middle)
|
||||||
const paramPattern = pattern.replace(/\[([^\]]+)\]/g, '([^/]+)');
|
if (pattern.includes('/*')) {
|
||||||
const regex = new RegExp(`^${paramPattern}$`);
|
// Convert wildcard pattern to regex
|
||||||
return regex.test(path);
|
// e.g., /leagues/*/schedule -> ^/leagues/[^/]+/schedule$
|
||||||
},
|
const regexPattern = pattern.replace(/\*/g, '[^/]+');
|
||||||
|
const regex = new RegExp(`^${regexPattern}$`);
|
||||||
|
return regex.test(path);
|
||||||
|
}
|
||||||
|
|
||||||
/**
|
// Parameterized match (e.g., /leagues/[id])
|
||||||
* Check if path is in a route group
|
const paramPattern = pattern.replace(/\[([^\]]+)\]/g, '([^/]+)');
|
||||||
*/
|
const regex = new RegExp(`^${paramPattern}$`);
|
||||||
isInGroup(path: string, group: keyof RouteGroup): boolean {
|
return regex.test(path);
|
||||||
const groupRoutes = routes[group];
|
},
|
||||||
|
|
||||||
// Handle nested objects (like sponsor.leagueDetail)
|
/**
|
||||||
const values = Object.values(groupRoutes);
|
* Check if path is in a route group
|
||||||
|
*/
|
||||||
|
isInGroup(path: string, group: keyof RouteGroup): boolean {
|
||||||
|
const groupRoutes = routes[group];
|
||||||
|
|
||||||
return values.some(value => {
|
// Handle nested objects (like sponsor.leagueDetail)
|
||||||
if (typeof value === 'function') {
|
const values = Object.values(groupRoutes);
|
||||||
// For parameterized routes, check pattern
|
|
||||||
const pattern = value('placeholder');
|
|
||||||
return path.startsWith(pattern.replace('/placeholder', ''));
|
|
||||||
}
|
|
||||||
return path.startsWith(value as string);
|
|
||||||
});
|
|
||||||
},
|
|
||||||
|
|
||||||
/**
|
return values.some(value => {
|
||||||
* Get all public route patterns
|
if (typeof value === 'function') {
|
||||||
*/
|
// For parameterized routes, check pattern
|
||||||
getPublicPatterns(): string[] {
|
const pattern = value('placeholder');
|
||||||
return [
|
return path.startsWith(pattern.replace('/placeholder', ''));
|
||||||
routes.public.home,
|
}
|
||||||
routes.public.leagues,
|
return path.startsWith(value as string);
|
||||||
routes.public.drivers,
|
});
|
||||||
routes.public.teams,
|
},
|
||||||
routes.public.leaderboards,
|
|
||||||
routes.leaderboards.drivers,
|
|
||||||
routes.leaderboards.teams,
|
|
||||||
routes.public.races,
|
|
||||||
routes.public.sponsorSignup,
|
|
||||||
routes.auth.login,
|
|
||||||
routes.auth.signup,
|
|
||||||
routes.auth.forgotPassword,
|
|
||||||
routes.auth.resetPassword,
|
|
||||||
routes.error.notFound,
|
|
||||||
routes.error.serverError,
|
|
||||||
];
|
|
||||||
},
|
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Check if path is public
|
* Get all public route patterns
|
||||||
*/
|
*/
|
||||||
isPublic(path: string): boolean {
|
getPublicPatterns(): string[] {
|
||||||
// logger.info('[RouteConfig] isPublic check', { path });
|
return [
|
||||||
|
routes.public.home,
|
||||||
|
routes.public.leagues,
|
||||||
|
routes.public.drivers,
|
||||||
|
routes.public.teams,
|
||||||
|
routes.public.leaderboards,
|
||||||
|
routes.leaderboards.drivers,
|
||||||
|
routes.leaderboards.teams,
|
||||||
|
routes.public.races,
|
||||||
|
routes.public.sponsorSignup,
|
||||||
|
routes.auth.login,
|
||||||
|
routes.auth.signup,
|
||||||
|
routes.auth.forgotPassword,
|
||||||
|
routes.auth.resetPassword,
|
||||||
|
routes.error.notFound,
|
||||||
|
routes.error.serverError,
|
||||||
|
// League sub-pages (public)
|
||||||
|
routes.league.schedule('*'),
|
||||||
|
routes.league.standings('*'),
|
||||||
|
routes.league.roster('*'),
|
||||||
|
routes.league.rulebook('*'),
|
||||||
|
];
|
||||||
|
},
|
||||||
|
|
||||||
const publicPatterns = this.getPublicPatterns();
|
/**
|
||||||
|
* Check if path is public
|
||||||
|
*/
|
||||||
|
isPublic(path: string): boolean {
|
||||||
|
// logger.info('[RouteConfig] isPublic check', { path });
|
||||||
|
|
||||||
// Check exact matches
|
const publicPatterns = this.getPublicPatterns();
|
||||||
if (publicPatterns.includes(path)) {
|
|
||||||
// logger.info('[RouteConfig] Path is public (exact match)', { path });
|
|
||||||
return true;
|
|
||||||
}
|
|
||||||
|
|
||||||
// Treat top-level detail pages as public (e2e relies on this)
|
// Check exact matches
|
||||||
// Examples: /leagues/:id, /races/:id, /drivers/:id, /teams/:id
|
if (publicPatterns.includes(path)) {
|
||||||
const segments = path.split('/').filter(Boolean);
|
// logger.info('[RouteConfig] Path is public (exact match)', { path });
|
||||||
if (segments.length === 2) {
|
return true;
|
||||||
const [group, slug] = segments;
|
}
|
||||||
if (group === 'leagues' && slug !== 'create') {
|
|
||||||
// logger.info('[RouteConfig] Path is public (league detail)', { path });
|
|
||||||
return true;
|
|
||||||
}
|
|
||||||
if (group === 'races') {
|
|
||||||
// logger.info('[RouteConfig] Path is public (race detail)', { path });
|
|
||||||
return true;
|
|
||||||
}
|
|
||||||
if (group === 'drivers') {
|
|
||||||
// logger.info('[RouteConfig] Path is public (driver detail)', { path });
|
|
||||||
return true;
|
|
||||||
}
|
|
||||||
if (group === 'teams' && slug !== 'create') {
|
|
||||||
// logger.info('[RouteConfig] Path is public (team detail)', { path });
|
|
||||||
return true;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
// Check parameterized patterns
|
// Treat top-level detail pages as public (e2e relies on this)
|
||||||
const isPublicParam = publicPatterns.some(pattern => {
|
// Examples: /leagues/:id, /races/:id, /drivers/:id, /teams/:id
|
||||||
if (pattern.includes('[')) {
|
const segments = path.split('/').filter(Boolean);
|
||||||
const paramPattern = pattern.replace(/\[([^\]]+)\]/g, '([^/]+)');
|
if (segments.length === 2) {
|
||||||
const regex = new RegExp(`^${paramPattern}$`);
|
const [group, slug] = segments;
|
||||||
return regex.test(path);
|
if (group === 'leagues' && slug !== 'create') {
|
||||||
}
|
// logger.info('[RouteConfig] Path is public (league detail)', { path });
|
||||||
return false;
|
return true;
|
||||||
});
|
}
|
||||||
|
if (group === 'races') {
|
||||||
|
// logger.info('[RouteConfig] Path is public (race detail)', { path });
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
if (group === 'drivers') {
|
||||||
|
// logger.info('[RouteConfig] Path is public (driver detail)', { path });
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
if (group === 'teams' && slug !== 'create') {
|
||||||
|
// logger.info('[RouteConfig] Path is public (team detail)', { path });
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
// if (isPublicParam) {
|
// Check parameterized patterns and wildcard patterns
|
||||||
// logger.info('[RouteConfig] Path is public (parameterized match)', { path });
|
const isPublicParam = publicPatterns.some(pattern => {
|
||||||
// } else {
|
// Check for parameterized patterns (e.g., /leagues/[id])
|
||||||
// logger.info('[RouteConfig] Path is NOT public', { path });
|
if (pattern.includes('[')) {
|
||||||
// }
|
const paramPattern = pattern.replace(/\[([^\]]+)\]/g, '([^/]+)');
|
||||||
|
const regex = new RegExp(`^${paramPattern}$`);
|
||||||
|
return regex.test(path);
|
||||||
|
}
|
||||||
|
// Check for wildcard patterns (e.g., /leagues/*)
|
||||||
|
if (pattern.includes('/*')) {
|
||||||
|
return this.matches(path, pattern);
|
||||||
|
}
|
||||||
|
return false;
|
||||||
|
});
|
||||||
|
|
||||||
return isPublicParam;
|
// if (isPublicParam) {
|
||||||
},
|
// logger.info('[RouteConfig] Path is public (parameterized match)', { path });
|
||||||
|
// } else {
|
||||||
|
// logger.info('[RouteConfig] Path is NOT public', { path });
|
||||||
|
// }
|
||||||
|
|
||||||
/**
|
return isPublicParam;
|
||||||
* Check if path requires authentication
|
},
|
||||||
*/
|
|
||||||
requiresAuth(path: string): boolean {
|
|
||||||
return !this.isPublic(path);
|
|
||||||
},
|
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Check if path requires specific role
|
* Check if path requires authentication
|
||||||
*/
|
*/
|
||||||
requiresRole(path: string): string[] | null {
|
requiresAuth(path: string): boolean {
|
||||||
// logger.info('[RouteConfig] requiresRole check', { path });
|
return !this.isPublic(path);
|
||||||
|
},
|
||||||
|
|
||||||
// Public routes never require a role
|
/**
|
||||||
if (this.isPublic(path)) {
|
* Check if path requires specific role
|
||||||
// logger.info('[RouteConfig] Path is public, no role required', { path });
|
*/
|
||||||
return null;
|
requiresRole(path: string): string[] | null {
|
||||||
}
|
// logger.info('[RouteConfig] requiresRole check', { path });
|
||||||
|
|
||||||
if (this.isInGroup(path, 'admin')) {
|
// Public routes never require a role
|
||||||
// Website session roles come from the API and are more specific than just "admin".
|
if (this.isPublic(path)) {
|
||||||
// Keep "admin"/"owner" for backwards compatibility.
|
// logger.info('[RouteConfig] Path is public, no role required', { path });
|
||||||
const roles = ['admin', 'owner', 'league-admin', 'league-steward', 'league-owner', 'system-owner', 'super-admin'];
|
return null;
|
||||||
// logger.info('[RouteConfig] Path requires admin roles', { path, roles });
|
}
|
||||||
return roles;
|
|
||||||
}
|
if (this.isInGroup(path, 'admin')) {
|
||||||
if (this.isInGroup(path, 'sponsor')) {
|
// Website session roles come from the API and are more specific than just "admin".
|
||||||
// logger.info('[RouteConfig] Path requires sponsor role', { path });
|
// Keep "admin"/"owner" for backwards compatibility.
|
||||||
return ['sponsor'];
|
const roles = ['admin', 'owner', 'league-admin', 'league-steward', 'league-owner', 'system-owner', 'super-admin'];
|
||||||
}
|
// logger.info('[RouteConfig] Path requires admin roles', { path, roles });
|
||||||
// logger.info('[RouteConfig] Path requires no specific role', { path });
|
return roles;
|
||||||
return null;
|
}
|
||||||
},
|
if (this.isInGroup(path, 'sponsor')) {
|
||||||
|
// logger.info('[RouteConfig] Path requires sponsor role', { path });
|
||||||
|
return ['sponsor'];
|
||||||
|
}
|
||||||
|
// logger.info('[RouteConfig] Path requires no specific role', { path });
|
||||||
|
return null;
|
||||||
|
},
|
||||||
};
|
};
|
||||||
|
|
||||||
/**
|
/**
|
||||||
@@ -369,31 +390,31 @@ export const routeMatchers = {
|
|||||||
* ```
|
* ```
|
||||||
*/
|
*/
|
||||||
export function buildPath(
|
export function buildPath(
|
||||||
routeName: string,
|
routeName: string,
|
||||||
params: Record<string, string> = {},
|
params: Record<string, string> = {},
|
||||||
_: string = ''
|
_: string = ''
|
||||||
): string {
|
): string {
|
||||||
// This is a placeholder for future i18n implementation
|
// This is a placeholder for future i18n implementation
|
||||||
// For now, it just builds the path using the route config
|
// For now, it just builds the path using the route config
|
||||||
|
|
||||||
const parts = routeName.split('.');
|
const parts = routeName.split('.');
|
||||||
let route: any = routes;
|
let route: any = routes;
|
||||||
|
|
||||||
for (const part of parts) {
|
for (const part of parts) {
|
||||||
route = (route as Record<string, any>)[part];
|
route = (route as Record<string, any>)[part];
|
||||||
if (!route) {
|
if (!route) {
|
||||||
throw new Error(`Unknown route: ${routeName}`);
|
throw new Error(`Unknown route: ${routeName}`);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
if (typeof route === 'function') {
|
if (typeof route === 'function') {
|
||||||
const paramKeys = Object.keys(params);
|
const paramKeys = Object.keys(params);
|
||||||
const paramKey = paramKeys[0];
|
const paramKey = paramKeys[0];
|
||||||
if (!paramKey) {
|
if (!paramKey) {
|
||||||
throw new Error(`Route ${routeName} requires parameters`);
|
throw new Error(`Route ${routeName} requires parameters`);
|
||||||
}
|
}
|
||||||
return route(params[paramKey]);
|
return route(params[paramKey]);
|
||||||
}
|
}
|
||||||
|
|
||||||
return route as string;
|
return route as string;
|
||||||
}
|
}
|
||||||
Reference in New Issue
Block a user