fix ssh key parsing
This commit is contained in:
@@ -0,0 +1,93 @@
|
||||
'use client'
|
||||
|
||||
import { usePathname, useRouter } from 'next/navigation'
|
||||
import { useEffect, useState } from 'react'
|
||||
import { apiFetch } from '@/lib/api'
|
||||
import { toDashboardAppPath, toPublicDashboardPath } from '@/lib/dashboardPaths'
|
||||
|
||||
type GeneratedMenuItem = {
|
||||
id: string
|
||||
itemType: 'INTERNAL_PAGE' | 'EXTERNAL_LINK' | 'PARENT_MENU' | 'SECTION_LABEL' | 'DIVIDER'
|
||||
routeOrUrl: string | null
|
||||
children: GeneratedMenuItem[]
|
||||
}
|
||||
|
||||
type EmployeeMenuResponse = {
|
||||
items: GeneratedMenuItem[]
|
||||
}
|
||||
|
||||
function flattenInternalRoutes(items: GeneratedMenuItem[]): string[] {
|
||||
const routes: string[] = []
|
||||
|
||||
const walk = (entry: GeneratedMenuItem) => {
|
||||
if (entry.itemType === 'INTERNAL_PAGE' && entry.routeOrUrl) {
|
||||
routes.push(entry.routeOrUrl)
|
||||
}
|
||||
entry.children.forEach(walk)
|
||||
}
|
||||
|
||||
items.forEach(walk)
|
||||
return routes
|
||||
}
|
||||
|
||||
function isAllowedRoute(currentPath: string, allowedRoutes: string[]) {
|
||||
return allowedRoutes.some((route) => {
|
||||
if (route === '/') return currentPath === '/'
|
||||
return currentPath === route || currentPath.startsWith(`${route}/`)
|
||||
})
|
||||
}
|
||||
|
||||
export default function DashboardAccessGuard({ children }: { children: React.ReactNode }) {
|
||||
const pathname = usePathname()
|
||||
const router = useRouter()
|
||||
const [ready, setReady] = useState(false)
|
||||
|
||||
useEffect(() => {
|
||||
let cancelled = false
|
||||
|
||||
async function enforceAccess() {
|
||||
try {
|
||||
setReady(false)
|
||||
const currentPath = toDashboardAppPath(pathname)
|
||||
const menu = await apiFetch<EmployeeMenuResponse>('/auth/employee/menu')
|
||||
if (cancelled) return
|
||||
|
||||
const allowedRoutes = flattenInternalRoutes(menu.items)
|
||||
const fallbackRoute = allowedRoutes[0] ?? '/'
|
||||
|
||||
if (!isAllowedRoute(currentPath, allowedRoutes)) {
|
||||
router.replace(toPublicDashboardPath(fallbackRoute))
|
||||
return
|
||||
}
|
||||
|
||||
setReady(true)
|
||||
} catch (error: any) {
|
||||
if (cancelled) return
|
||||
|
||||
if (error?.statusCode === 401 || error?.statusCode === 403 || error?.statusCode === 402) {
|
||||
router.replace(toPublicDashboardPath('/'))
|
||||
return
|
||||
}
|
||||
|
||||
// Do not deadlock the dashboard on transient network failures.
|
||||
setReady(true)
|
||||
}
|
||||
}
|
||||
|
||||
enforceAccess()
|
||||
|
||||
return () => {
|
||||
cancelled = true
|
||||
}
|
||||
}, [pathname, router])
|
||||
|
||||
if (!ready) {
|
||||
return (
|
||||
<div className="flex min-h-[40vh] items-center justify-center">
|
||||
<div className="h-6 w-6 animate-spin rounded-full border-2 border-orange-500 border-t-transparent" aria-label="Checking access" />
|
||||
</div>
|
||||
)
|
||||
}
|
||||
|
||||
return <>{children}</>
|
||||
}
|
||||
Reference in New Issue
Block a user