From e81d5ac424c1aed379568a9dfe5a39e892f239e7 Mon Sep 17 00:00:00 2001 From: seaznCode Date: Sun, 18 Jan 2026 21:21:24 +0100 Subject: [PATCH 1/2] feat: add AdminLayout component for user authentication and authorization --- src/app/admin/layout.tsx | 79 ++++++++++++++++++++++++++++++++++++++++ 1 file changed, 79 insertions(+) create mode 100644 src/app/admin/layout.tsx diff --git a/src/app/admin/layout.tsx b/src/app/admin/layout.tsx new file mode 100644 index 0000000..e66f59a --- /dev/null +++ b/src/app/admin/layout.tsx @@ -0,0 +1,79 @@ +'use client' + +import { useEffect, useMemo, useState } from 'react' +import { useRouter } from 'next/navigation' +import useAuthStore from '../store/authStore' + +function isUserAdmin(user: any): boolean { + if (!user) return false + const role = user.role ?? user.userType ?? user.user_type + if (role === 'admin' || role === 'super_admin') return true + if (user.isAdmin === true || user.isSuperAdmin === true) return true + if (Array.isArray(user.roles) && (user.roles.includes('admin') || user.roles.includes('super_admin'))) return true + return false +} + +export default function AdminLayout({ children }: { children: React.ReactNode }) { + const router = useRouter() + const user = useAuthStore(s => s.user) + const isAuthReady = useAuthStore(s => s.isAuthReady) + const refreshAuthToken = useAuthStore(s => s.refreshAuthToken) + const [mounted, setMounted] = useState(false) + + const isAdmin = useMemo(() => isUserAdmin(user), [user]) + + useEffect(() => { + setMounted(true) + }, []) + + useEffect(() => { + let cancelled = false + + const guard = async () => { + if (!mounted || !isAuthReady) return + + if (!user) { + try { + await refreshAuthToken?.() + } catch {} + } + + const currentUser = useAuthStore.getState().user + const ok = isUserAdmin(currentUser) + + if (!currentUser) { + router.replace('/login') + return + } + + if (!ok) { + router.replace('/dashboard') + return + } + + if (!cancelled) { + // allowed + } + } + + guard() + return () => { cancelled = true } + }, [mounted, isAuthReady, user, refreshAuthToken, router]) + + if (!mounted || !isAuthReady) { + return ( +
+
+
+

Loading...

+
+
+ ) + } + + if (!isAdmin) { + return null + } + + return <>{children} +} From 54f3d48c1d9570941fb71b48abc08f75d0850718 Mon Sep 17 00:00:00 2001 From: seaznCode Date: Sun, 18 Jan 2026 21:27:58 +0100 Subject: [PATCH 2/2] fix: enhance admin user validation and logging in AdminLayout --- src/app/admin/layout.tsx | 17 +++++++++++++++++ 1 file changed, 17 insertions(+) diff --git a/src/app/admin/layout.tsx b/src/app/admin/layout.tsx index e66f59a..20eea55 100644 --- a/src/app/admin/layout.tsx +++ b/src/app/admin/layout.tsx @@ -6,6 +6,9 @@ import useAuthStore from '../store/authStore' function isUserAdmin(user: any): boolean { if (!user) return false + if (user.user && typeof user.user === 'object') { + return isUserAdmin(user.user) + } const role = user.role ?? user.userType ?? user.user_type if (role === 'admin' || role === 'super_admin') return true if (user.isAdmin === true || user.isSuperAdmin === true) return true @@ -32,8 +35,16 @@ export default function AdminLayout({ children }: { children: React.ReactNode }) const guard = async () => { if (!mounted || !isAuthReady) return + console.log('🔐 AdminLayout guard:start', { + mounted, + isAuthReady, + hasUser: !!user, + userRole: (user && (user.role ?? user.userType ?? user.user_type)) || null + }) + if (!user) { try { + console.log('🔐 AdminLayout: no user, attempting refresh') await refreshAuthToken?.() } catch {} } @@ -41,6 +52,12 @@ export default function AdminLayout({ children }: { children: React.ReactNode }) const currentUser = useAuthStore.getState().user const ok = isUserAdmin(currentUser) + console.log('🔐 AdminLayout guard:resolved', { + hasUser: !!currentUser, + userRole: currentUser && (currentUser.role ?? currentUser.userType ?? currentUser.user_type), + isAdmin: ok + }) + if (!currentUser) { router.replace('/login') return