fix:added more rolelabel logging to see why missing pages
This commit is contained in:
parent
05508cc76c
commit
826eead605
6 changed files with 209 additions and 64 deletions
|
|
@ -11,7 +11,8 @@ export interface User {
|
|||
fullName: string;
|
||||
language: string;
|
||||
enabled: boolean;
|
||||
privilege: string;
|
||||
privilege?: string; // Deprecated - use roleLabels instead
|
||||
roleLabels?: string[]; // Array of role labels from backend (e.g., ["user"])
|
||||
authenticationAuthority: string;
|
||||
mandateId: string;
|
||||
[key: string]: any; // Allow additional properties
|
||||
|
|
@ -80,10 +81,23 @@ export async function fetchCurrentUser(
|
|||
endpoint = '/api/google/me';
|
||||
}
|
||||
|
||||
return await request({
|
||||
console.log('📡 fetchCurrentUser: Requesting user data from:', endpoint);
|
||||
const response = await request({
|
||||
url: endpoint,
|
||||
method: 'get'
|
||||
});
|
||||
|
||||
console.log('📥 fetchCurrentUser: Received response:', {
|
||||
endpoint,
|
||||
hasData: !!response,
|
||||
username: response?.username,
|
||||
roleLabels: response?.roleLabels,
|
||||
privilege: response?.privilege,
|
||||
allKeys: response ? Object.keys(response) : [],
|
||||
fullResponse: response
|
||||
});
|
||||
|
||||
return response;
|
||||
}
|
||||
|
||||
/**
|
||||
|
|
|
|||
|
|
@ -28,28 +28,70 @@ const PageManager: React.FC<PageManagerProps> = ({
|
|||
|
||||
// Check if user has access to a page using backend RBAC permissions
|
||||
const checkPageAccess = async (pageData: GenericPageData): Promise<boolean> => {
|
||||
console.log('🔍 PageManager: Checking page access:', {
|
||||
path: pageData.path,
|
||||
label: pageData.label,
|
||||
hide: pageData.hide,
|
||||
moduleEnabled: pageData.moduleEnabled
|
||||
});
|
||||
|
||||
try {
|
||||
return await canView('UI', pageData.path);
|
||||
const hasAccess = await canView('UI', pageData.path);
|
||||
console.log('🔍 PageManager: Page access result:', {
|
||||
path: pageData.path,
|
||||
hasAccess
|
||||
});
|
||||
return hasAccess;
|
||||
} catch (error) {
|
||||
console.error(`Error checking RBAC access for ${pageData.path}:`, error);
|
||||
console.error(`❌ PageManager: Error checking RBAC access for ${pageData.path}:`, error);
|
||||
return false;
|
||||
}
|
||||
};
|
||||
|
||||
useEffect(() => {
|
||||
console.log('🔄 PageManager: useEffect triggered for path:', currentPath);
|
||||
const pageData = getPageDataByPath(currentPath);
|
||||
|
||||
console.log('📄 PageManager: Page data found:', {
|
||||
path: currentPath,
|
||||
hasPageData: !!pageData,
|
||||
hide: pageData?.hide,
|
||||
moduleEnabled: pageData?.moduleEnabled,
|
||||
label: pageData?.label
|
||||
});
|
||||
|
||||
if (!pageData || pageData.hide || !pageData.moduleEnabled) {
|
||||
console.log('⛔ PageManager: Page not rendered:', {
|
||||
path: currentPath,
|
||||
reason: !pageData ? 'not found' : pageData.hide ? 'hidden' : 'module disabled'
|
||||
});
|
||||
return;
|
||||
}
|
||||
|
||||
// Check page access
|
||||
console.log('🔍 PageManager: Checking access before rendering:', currentPath);
|
||||
checkPageAccess(pageData).then(hasAccess => {
|
||||
console.log('🔍 PageManager: Access check complete:', {
|
||||
path: currentPath,
|
||||
hasAccess
|
||||
});
|
||||
|
||||
if (!hasAccess) {
|
||||
console.log('⛔ PageManager: Page not rendered due to access check:', currentPath);
|
||||
return;
|
||||
}
|
||||
|
||||
console.log('✅ PageManager: Rendering page:', {
|
||||
path: currentPath,
|
||||
label: pageData.label
|
||||
});
|
||||
|
||||
setPageInstances(prev => {
|
||||
console.log('📦 PageManager: Creating/updating page instance:', {
|
||||
path: currentPath,
|
||||
existingInstances: Array.from(prev.keys()),
|
||||
willCreateNew: !prev.has(currentPath)
|
||||
});
|
||||
const newInstances = new Map(prev);
|
||||
|
||||
// Update active states
|
||||
|
|
@ -59,6 +101,10 @@ const PageManager: React.FC<PageManagerProps> = ({
|
|||
|
||||
// Create instance if it doesn't exist
|
||||
if (!newInstances.has(currentPath)) {
|
||||
console.log('📦 PageManager: Creating new page instance:', {
|
||||
path: currentPath,
|
||||
label: pageData.label
|
||||
});
|
||||
const shouldPreserve = pageData.preserveState || false;
|
||||
|
||||
const pageInstance: PageInstance = {
|
||||
|
|
@ -84,9 +130,13 @@ const PageManager: React.FC<PageManagerProps> = ({
|
|||
};
|
||||
|
||||
newInstances.set(currentPath, pageInstance);
|
||||
|
||||
|
||||
console.log('✅ PageManager: Page instance created:', {
|
||||
path: currentPath,
|
||||
totalInstances: newInstances.size,
|
||||
allPaths: Array.from(newInstances.keys())
|
||||
});
|
||||
} else {
|
||||
console.log('🔄 PageManager: Page instance already exists, updating active state:', currentPath);
|
||||
if (import.meta.env.DEV) {
|
||||
const _instance = newInstances.get(currentPath);
|
||||
void _instance; // Intentionally unused, for debugging purposes
|
||||
|
|
|
|||
|
|
@ -160,15 +160,28 @@ export const SidebarProvider: React.FC<SidebarProviderProps> = ({ children }) =>
|
|||
.sort((a, b) => (a.order || 0) - (b.order || 0));
|
||||
|
||||
// Process each main page
|
||||
console.log('📋 SidebarProvider: Processing pages, total:', mainPages.length);
|
||||
for (const pageData of mainPages) {
|
||||
console.log('🔍 SidebarProvider: Checking access for page:', {
|
||||
path: pageData.path,
|
||||
label: pageData.label,
|
||||
hasSubpages: pageData.hasSubpages
|
||||
});
|
||||
|
||||
// Check RBAC permissions
|
||||
try {
|
||||
const hasRBACAccess = await canView('UI', pageData.path);
|
||||
console.log('🔍 SidebarProvider: RBAC check result:', {
|
||||
path: pageData.path,
|
||||
hasAccess: hasRBACAccess
|
||||
});
|
||||
|
||||
if (!hasRBACAccess) {
|
||||
console.log('⛔ SidebarProvider: Page hidden due to RBAC:', pageData.path);
|
||||
continue;
|
||||
}
|
||||
} catch (error) {
|
||||
console.error(`Error checking RBAC access for ${pageData.path}:`, error);
|
||||
console.error(`❌ SidebarProvider: Error checking RBAC access for ${pageData.path}:`, error);
|
||||
continue;
|
||||
}
|
||||
|
||||
|
|
@ -183,18 +196,49 @@ export const SidebarProvider: React.FC<SidebarProviderProps> = ({ children }) =>
|
|||
|
||||
// Filter subpages by RBAC access
|
||||
const accessibleSubpages = [];
|
||||
console.log('📋 SidebarProvider: Checking subpages for:', {
|
||||
parentPath: pageData.path,
|
||||
totalSubpages: allSubpages.length
|
||||
});
|
||||
|
||||
for (const subpage of allSubpages) {
|
||||
try {
|
||||
console.log('🔍 SidebarProvider: Checking subpage access:', {
|
||||
parentPath: pageData.path,
|
||||
subpagePath: subpage.path,
|
||||
subpageLabel: subpage.label
|
||||
});
|
||||
|
||||
const hasSubpageRBACAccess = await canView('UI', subpage.path);
|
||||
console.log('🔍 SidebarProvider: Subpage RBAC result:', {
|
||||
subpagePath: subpage.path,
|
||||
hasAccess: hasSubpageRBACAccess
|
||||
});
|
||||
|
||||
if (hasSubpageRBACAccess) {
|
||||
accessibleSubpages.push(subpage);
|
||||
console.log('✅ SidebarProvider: Subpage added:', subpage.path);
|
||||
} else {
|
||||
console.log('⛔ SidebarProvider: Subpage hidden due to RBAC:', subpage.path);
|
||||
}
|
||||
} catch (error) {
|
||||
console.error(`Error checking RBAC access for subpage ${subpage.path}:`, error);
|
||||
console.error(`❌ SidebarProvider: Error checking RBAC access for subpage ${subpage.path}:`, error);
|
||||
}
|
||||
}
|
||||
|
||||
console.log('📋 SidebarProvider: Subpage filtering complete:', {
|
||||
parentPath: pageData.path,
|
||||
totalSubpages: allSubpages.length,
|
||||
accessibleSubpages: accessibleSubpages.length,
|
||||
accessiblePaths: accessibleSubpages.map(s => s.path)
|
||||
});
|
||||
|
||||
if (accessibleSubpages.length > 0) {
|
||||
console.log('✅ SidebarProvider: Adding parent page with subpages:', {
|
||||
path: pageData.path,
|
||||
label: pageData.label,
|
||||
subpagesCount: accessibleSubpages.length
|
||||
});
|
||||
// Create expandable item with submenu
|
||||
items.push({
|
||||
id: pageData.id,
|
||||
|
|
@ -212,6 +256,10 @@ export const SidebarProvider: React.FC<SidebarProviderProps> = ({ children }) =>
|
|||
});
|
||||
} else {
|
||||
// No accessible subpages, show as regular item
|
||||
console.log('✅ SidebarProvider: Adding parent page without accessible subpages:', {
|
||||
path: pageData.path,
|
||||
label: pageData.label
|
||||
});
|
||||
items.push({
|
||||
id: pageData.id,
|
||||
name: resolveLanguageText(pageData.name, t),
|
||||
|
|
@ -223,6 +271,10 @@ export const SidebarProvider: React.FC<SidebarProviderProps> = ({ children }) =>
|
|||
}
|
||||
} else {
|
||||
// Regular items without subpages
|
||||
console.log('✅ SidebarProvider: Adding regular page:', {
|
||||
path: pageData.path,
|
||||
label: pageData.label
|
||||
});
|
||||
items.push({
|
||||
id: pageData.id,
|
||||
name: resolveLanguageText(pageData.name, t),
|
||||
|
|
@ -235,19 +287,36 @@ export const SidebarProvider: React.FC<SidebarProviderProps> = ({ children }) =>
|
|||
}
|
||||
|
||||
// Sort all items by order
|
||||
return items.sort((a, b) => (a.order || 0) - (b.order || 0));
|
||||
const sortedItems = items.sort((a, b) => (a.order || 0) - (b.order || 0));
|
||||
console.log('📊 SidebarProvider: Final sidebar items built and sorted:', {
|
||||
totalItems: sortedItems.length,
|
||||
sortedPaths: sortedItems.map(item => item.link),
|
||||
items: sortedItems.map(item => ({
|
||||
id: item.id,
|
||||
link: item.link,
|
||||
name: item.name,
|
||||
hasSubmenu: !!item.submenu,
|
||||
submenuCount: item.submenu?.length || 0
|
||||
}))
|
||||
});
|
||||
return sortedItems;
|
||||
};
|
||||
|
||||
// Refresh sidebar items
|
||||
const refreshSidebar = async () => {
|
||||
console.log('🔄 SidebarProvider: Refreshing sidebar items...');
|
||||
setLoading(true);
|
||||
setError(null);
|
||||
|
||||
try {
|
||||
const items = await getSidebarItems();
|
||||
console.log('✅ SidebarProvider: Setting sidebar items:', {
|
||||
count: items.length,
|
||||
items: items.map(item => ({ id: item.id, link: item.link, name: item.name }))
|
||||
});
|
||||
setSidebarItems(items);
|
||||
} catch (err) {
|
||||
console.error('Error refreshing sidebar:', err);
|
||||
console.error('❌ SidebarProvider: Error refreshing sidebar:', err);
|
||||
setError(err instanceof Error ? err.message : 'Failed to load sidebar items');
|
||||
} finally {
|
||||
setLoading(false);
|
||||
|
|
|
|||
|
|
@ -90,10 +90,31 @@ export const usePermissions = () => {
|
|||
try {
|
||||
// Use retry logic for 429 errors
|
||||
// Note: We wrap the API call in retry logic since useApiRequest doesn't handle 429 retries
|
||||
console.log('🔐 usePermissions: Checking permissions for:', { context, item, cacheKey: key });
|
||||
|
||||
const permissions = await retryWithBackoff(async () => {
|
||||
try {
|
||||
return await fetchPermissionsApi(request, context, item);
|
||||
const result = await fetchPermissionsApi(request, context, item);
|
||||
console.log('✅ usePermissions: Received permissions response:', {
|
||||
context,
|
||||
item,
|
||||
permissions: result,
|
||||
hasView: result?.view,
|
||||
hasCreate: result?.create,
|
||||
hasUpdate: result?.update,
|
||||
hasDelete: result?.delete,
|
||||
fullResponse: result
|
||||
});
|
||||
return result;
|
||||
} catch (error: any) {
|
||||
console.error('❌ usePermissions: Error fetching permissions:', {
|
||||
context,
|
||||
item,
|
||||
error: error.message,
|
||||
status: error.response?.status,
|
||||
statusText: error.response?.statusText,
|
||||
fullError: error
|
||||
});
|
||||
// If useApiRequest throws, we need to check if it's a 429
|
||||
// For now, we'll let the retry logic handle it
|
||||
throw error;
|
||||
|
|
@ -104,6 +125,7 @@ export const usePermissions = () => {
|
|||
setCache(prev => {
|
||||
const newCache = { ...prev, [key]: permissions };
|
||||
cacheRef.current = newCache;
|
||||
console.log('💾 usePermissions: Cached permissions:', { context, item, permissions });
|
||||
return newCache;
|
||||
});
|
||||
|
||||
|
|
@ -170,8 +192,17 @@ export const usePermissions = () => {
|
|||
context: PermissionContext,
|
||||
item: string
|
||||
): Promise<boolean> => {
|
||||
console.log('👁️ canView: Checking view access for:', { context, item });
|
||||
const permissions = await checkPermission(context, item);
|
||||
return permissions.view;
|
||||
const hasAccess = permissions.view === true;
|
||||
console.log('👁️ canView: Result:', {
|
||||
context,
|
||||
item,
|
||||
hasAccess,
|
||||
permissions: permissions,
|
||||
viewPermission: permissions.view
|
||||
});
|
||||
return hasAccess;
|
||||
}, [checkPermission]);
|
||||
|
||||
/**
|
||||
|
|
|
|||
|
|
@ -29,23 +29,15 @@ export function useCurrentUser() {
|
|||
try {
|
||||
// Check if we already have user data in sessionStorage cache
|
||||
const cachedUser = getUserDataCache();
|
||||
if (cachedUser) {
|
||||
// Validate cached user data - if privilege is missing, refetch from API
|
||||
if (cachedUser.privilege === undefined || cachedUser.privilege === null) {
|
||||
console.warn('⚠️ Cached user data missing privilege, refetching from API...', {
|
||||
username: cachedUser.username,
|
||||
privilege: cachedUser.privilege
|
||||
});
|
||||
// Clear incomplete cache and continue to fetch from API
|
||||
clearUserDataCache();
|
||||
} else {
|
||||
setUser(cachedUser);
|
||||
console.log('✅ Using cached user data from sessionStorage (persists during session):', {
|
||||
username: cachedUser.username,
|
||||
privilege: cachedUser.privilege
|
||||
});
|
||||
return;
|
||||
}
|
||||
if (cachedUser && cachedUser.username) {
|
||||
// Use cached user data - permissions are checked via RBAC API, not client-side
|
||||
setUser(cachedUser);
|
||||
console.log('✅ Using cached user data from sessionStorage (persists during session):', {
|
||||
username: cachedUser.username,
|
||||
roleLabels: cachedUser.roleLabels,
|
||||
privilege: cachedUser.privilege
|
||||
});
|
||||
return;
|
||||
}
|
||||
|
||||
// JWT tokens are now stored in httpOnly cookies, so we fetch user data from API
|
||||
|
|
@ -78,33 +70,33 @@ export function useCurrentUser() {
|
|||
// Log full response for debugging
|
||||
console.log('📦 User data received from API:', {
|
||||
username: data?.username,
|
||||
roleLabels: data?.roleLabels,
|
||||
privilege: data?.privilege,
|
||||
hasRoleLabels: !!data?.roleLabels,
|
||||
hasPrivilege: !!data?.privilege,
|
||||
allKeys: data ? Object.keys(data) : [],
|
||||
fullData: data
|
||||
});
|
||||
|
||||
// Validate user data before caching - ensure privilege is present
|
||||
if (!data || !data.privilege) {
|
||||
console.error('❌ User data from API missing privilege field - this may cause permission issues:', {
|
||||
// Always cache user data - permissions are checked via RBAC API, not client-side
|
||||
// roleLabels/privilege are optional metadata for display/logging purposes
|
||||
if (!data || !data.username) {
|
||||
console.error('❌ User data from API is invalid:', {
|
||||
username: data?.username,
|
||||
privilege: data?.privilege,
|
||||
dataKeys: data ? Object.keys(data) : [],
|
||||
fullResponse: data
|
||||
});
|
||||
// Don't cache incomplete data - it will cause permission issues on next load
|
||||
// But still set user so the app can function (permissions are checked via RBAC API)
|
||||
setUser(data);
|
||||
console.warn('⚠️ User data set but not cached due to missing privilege - will refetch on next load');
|
||||
} else {
|
||||
// Only cache if privilege is present
|
||||
setUserDataCache(data);
|
||||
console.log('✅ User data fetched from API and cached in sessionStorage (secure):', {
|
||||
username: data.username,
|
||||
privilege: data.privilege
|
||||
});
|
||||
setUser(data);
|
||||
throw new Error('Invalid user data received from API');
|
||||
}
|
||||
|
||||
// Cache user data (permissions are checked via RBAC API)
|
||||
setUserDataCache(data);
|
||||
console.log('✅ User data fetched from API and cached in sessionStorage (secure):', {
|
||||
username: data.username,
|
||||
roleLabels: data.roleLabels,
|
||||
privilege: data.privilege
|
||||
});
|
||||
setUser(data);
|
||||
} catch (error: any) {
|
||||
console.error('❌ Failed to fetch user data:', error);
|
||||
|
||||
|
|
@ -272,16 +264,10 @@ export function useCurrentUser() {
|
|||
useEffect(() => {
|
||||
// Try to load user from sessionStorage cache first for faster initial load
|
||||
const cachedUser = getUserDataCache();
|
||||
if (cachedUser) {
|
||||
// Validate cached user data - if privilege is missing, don't use cache
|
||||
if (cachedUser.privilege === undefined || cachedUser.privilege === null) {
|
||||
console.warn('⚠️ Cached user data missing privilege on mount, will refetch from API');
|
||||
clearUserDataCache();
|
||||
// Don't set user - let fetchCurrentUser handle it
|
||||
} else {
|
||||
setUser(cachedUser);
|
||||
console.log('✅ Using cached user data from sessionStorage on mount (persists during session)');
|
||||
}
|
||||
if (cachedUser && cachedUser.username) {
|
||||
// Use cached user data - permissions are checked via RBAC API
|
||||
setUser(cachedUser);
|
||||
console.log('✅ Using cached user data from sessionStorage on mount (persists during session)');
|
||||
}
|
||||
|
||||
// For OAuth authentication, wait a bit longer before fetching user data
|
||||
|
|
|
|||
|
|
@ -17,7 +17,8 @@ export interface CachedUserData {
|
|||
username: string;
|
||||
email: string;
|
||||
fullName: string;
|
||||
privilege: string;
|
||||
privilege?: string; // Deprecated - use roleLabels instead
|
||||
roleLabels?: string[]; // Array of role labels from backend (e.g., ["user"])
|
||||
mandateId: string;
|
||||
language: string;
|
||||
enabled: boolean;
|
||||
|
|
@ -30,14 +31,8 @@ export interface CachedUserData {
|
|||
*/
|
||||
export const setUserDataCache = (userData: CachedUserData): void => {
|
||||
if (userData) {
|
||||
// Validate that privilege is present before caching
|
||||
if (!userData.privilege) {
|
||||
console.warn('⚠️ Attempted to cache user data without privilege, skipping cache:', {
|
||||
username: userData.username,
|
||||
hasPrivilege: !!userData.privilege
|
||||
});
|
||||
return;
|
||||
}
|
||||
// Always cache user data - permissions are checked via RBAC API, not client-side
|
||||
// roleLabels/privilege are optional metadata, not required for app functionality
|
||||
try {
|
||||
sessionStorage.setItem(USER_CACHE_KEY, JSON.stringify(userData));
|
||||
} catch (error) {
|
||||
|
|
|
|||
Loading…
Reference in a new issue