middleware fix wip

This commit is contained in:
2026-01-04 12:49:30 +01:00
parent 729d95cd73
commit 691e6e2c7e
10 changed files with 741 additions and 152 deletions

View File

@@ -1,7 +1,7 @@
/**
* @file RouteConfig.ts
* Centralized routing configuration for clean, maintainable paths
*
*
* Design Principles:
* - Single source of truth for all routes
* - i18n-ready: paths can be localized
@@ -10,6 +10,10 @@
* - Environment-specific: can vary by mode
*/
import { ConsoleLogger } from '@/lib/infrastructure/logging/ConsoleLogger';
const logger = new ConsoleLogger();
export interface RouteDefinition {
path: string;
name: string;
@@ -249,24 +253,41 @@ export const routeMatchers = {
* Check if path is public
*/
isPublic(path: string): boolean {
logger.info('[RouteConfig] isPublic check', { path });
const publicPatterns = this.getPublicPatterns();
// Check exact matches
if (publicPatterns.includes(path)) return true;
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)
// Examples: /leagues/:id, /races/:id, /drivers/:id, /teams/:id
const segments = path.split('/').filter(Boolean);
if (segments.length === 2) {
const [group, slug] = segments;
if (group === 'leagues' && slug !== 'create') return true;
if (group === 'races') return true;
if (group === 'drivers') return true;
if (group === 'teams') return true;
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') {
logger.info('[RouteConfig] Path is public (team detail)', { path });
return true;
}
}
// Check parameterized patterns
return publicPatterns.some(pattern => {
const isPublicParam = publicPatterns.some(pattern => {
if (pattern.includes('[')) {
const paramPattern = pattern.replace(/\[([^\]]+)\]/g, '([^/]+)');
const regex = new RegExp(`^${paramPattern}$`);
@@ -274,6 +295,14 @@ export const routeMatchers = {
}
return false;
});
if (isPublicParam) {
logger.info('[RouteConfig] Path is public (parameterized match)', { path });
} else {
logger.info('[RouteConfig] Path is NOT public', { path });
}
return isPublicParam;
},
/**
@@ -287,14 +316,20 @@ export const routeMatchers = {
* Check if path requires specific role
*/
requiresRole(path: string): string[] | null {
logger.info('[RouteConfig] requiresRole check', { path });
if (this.isInGroup(path, 'admin')) {
// Website session roles come from the API and are more specific than just "admin".
// Keep "admin"/"owner" for backwards compatibility.
return ['admin', 'owner', 'league-admin', 'league-steward', 'league-owner', 'system-owner', 'super-admin'];
const roles = ['admin', 'owner', 'league-admin', 'league-steward', 'league-owner', 'system-owner', 'super-admin'];
logger.info('[RouteConfig] Path requires admin roles', { path, roles });
return roles;
}
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;
},
};