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