From 5130201ca2332b5cd8144b9c1ca08f3ea41db86e Mon Sep 17 00:00:00 2001 From: Jakub Wilk Date: Wed, 8 Jan 2025 17:10:44 +0100 Subject: [PATCH] feat: enhance route matching logic with support for exact, parameter, and wildcard routes --- apps/web/app/config/navigationConfig.ts | 51 +++++++++++++++++++++++-- 1 file changed, 48 insertions(+), 3 deletions(-) diff --git a/apps/web/app/config/navigationConfig.ts b/apps/web/app/config/navigationConfig.ts index 6c585ce8e..5197eb76e 100644 --- a/apps/web/app/config/navigationConfig.ts +++ b/apps/web/app/config/navigationConfig.ts @@ -72,18 +72,63 @@ export const adminNavigationConfig: NavigationItem[] = [ }, ]; +/** + * Finds matching route access roles for a given path by checking different types of routes in order: + * 1. Exact matches (e.g., "courses/new" matches "courses/new") + * 2. Parameter routes (e.g., "teachers/123" matches "teachers/:id") + * 3. Wildcard routes (e.g., "teachers/123/settings" matches "teachers/*") + * + * @param path - The actual URL path to match (e.g., "teachers/123") + * @returns UserRole[] | undefined - Array of user roles that can access this path, or undefined if no match + * + * @example + * // Exact match + * findMatchingRoute("courses/new") // matches "courses/new" in config + * + * // Parameter match + * findMatchingRoute("teachers/123") // matches "teachers/:id" in config + * findMatchingRoute("course/456/lesson/789") // matches "course/:courseId/lesson/:lessonId" + * + * // Wildcard match + * findMatchingRoute("teachers/123/settings") // matches "teachers/*" + * + * How matching works: + * 1. First, tries to find an exact match in routeAccessConfig + * 2. If no exact match, looks for parameter routes (:id) + * - Splits both paths into segments + * - Segments with ":" are treated as valid matches for any value + * - All other segments must match exactly + * 3. If still no match, checks wildcard routes (*) + * - Matches if path starts with the part before "*" + */ export const findMatchingRoute = (path: string) => { if (routeAccessConfig[path]) { - console.log("mariusz ->", routeAccessConfig[path]); return routeAccessConfig[path]; } + const paramRoutes = Object.entries(routeAccessConfig).filter( + ([route]) => route.includes(":") && !route.includes("*"), + ); + + for (const [route, roles] of paramRoutes) { + const routeParts = route.split("/"); + const pathParts = path.split("/"); + + if (routeParts.length !== pathParts.length) continue; + + const matches = routeParts.every((part, index) => { + if (part.startsWith(":")) return true; + return part === pathParts[index]; + }); + + if (matches) return roles; + } + const wildcardRoutes = Object.entries(routeAccessConfig).filter(([route]) => route.includes("*")); + for (const [route, roles] of wildcardRoutes) { const routeWithoutWildcard = route.replace("/*", ""); - console.log({ routeWithoutWildcard }); if (path.startsWith(routeWithoutWildcard)) { - console.log({ roles }); return roles; } }