diff --git a/composables/useAuthorization.ts b/composables/useAuthorization.ts index c7ffcd7..24b4d27 100644 --- a/composables/useAuthorization.ts +++ b/composables/useAuthorization.ts @@ -93,30 +93,34 @@ export const useAuthorization = () => { // Initialize immediately (both client and server) initializeAuth(); - // Watch for changes in payload data and reinitialize + // Watch for changes in payload data (optimized to prevent loops) if (process.client) { + let watcherTimeout: NodeJS.Timeout | null = null; + watch( () => nuxtApp.payload?.data?.authState, - (newAuthState) => { - if (newAuthState && typeof newAuthState === 'object') { - console.log('[useAuthorization] Auth state updated, reinitializing...'); - hasInitialized.value = false; // Force re-initialization - initializeAuth(); + (newAuthState, oldAuthState) => { + // Only update if the auth state actually changed + if (newAuthState && typeof newAuthState === 'object' && newAuthState !== oldAuthState) { + // Debounce to prevent rapid re-initialization + if (watcherTimeout) clearTimeout(watcherTimeout); + + watcherTimeout = setTimeout(() => { + console.log('[useAuthorization] Auth state updated, syncing...'); + + // Direct sync instead of full re-initialization + authState.user = newAuthState.user || null; + authState.authenticated = newAuthState.authenticated || false; + authState.groups = Array.isArray(newAuthState.groups) ? newAuthState.groups : []; + + if (!hasInitialized.value) { + hasInitialized.value = true; + isLoading.value = false; + } + }, 100); // 100ms debounce } }, - { immediate: true, deep: true } - ); - - // Also watch for session cache updates - watch( - () => nuxtApp.payload?.data?.['auth:session:cache'], - (newSessionCache) => { - if (newSessionCache && typeof newSessionCache === 'object' && !hasInitialized.value) { - console.log('[useAuthorization] Session cache updated, initializing...'); - initializeAuth(); - } - }, - { immediate: true, deep: true } + { immediate: false, deep: false } ); } diff --git a/composables/useCustomAuth.ts b/composables/useCustomAuth.ts index b9ce206..4abdacf 100644 --- a/composables/useCustomAuth.ts +++ b/composables/useCustomAuth.ts @@ -16,42 +16,62 @@ export const useCustomAuth = () => { const authenticated = ref(false) const loading = ref(true) const refreshing = ref(false) - const retryCount = ref(0) - const maxRetries = 3 - // Check authentication status with retry logic - const checkAuth = async (skipRetry = false) => { + // Get auth state from middleware cache (no API calls!) + const syncFromCache = () => { try { - loading.value = true - const data = await $fetch('/api/auth/session', { - retry: skipRetry ? 0 : 2, - retryDelay: 1000 - }) - user.value = data.user - authenticated.value = data.authenticated - retryCount.value = 0 // Reset retry count on success + const nuxtApp = useNuxtApp() - console.log('[CUSTOM_AUTH] Session check result:', { - authenticated: data.authenticated, - userId: data.user?.id - }) - } catch (error) { - console.error('[CUSTOM_AUTH] Session check failed:', error) - - // If it's a network error and we haven't exceeded retry limit, try refresh - if (!skipRetry && retryCount.value < maxRetries && (error as any)?.status >= 500) { - retryCount.value++ - console.log(`[CUSTOM_AUTH] Retrying session check (${retryCount.value}/${maxRetries})...`) + // Try to get from auth state cache first + const authState = nuxtApp.payload?.data?.authState + if (authState && typeof authState === 'object') { + user.value = authState.user || null + authenticated.value = authState.authenticated || false + loading.value = false - // Wait a bit before retrying - await new Promise(resolve => setTimeout(resolve, 1000 * retryCount.value)) - return checkAuth(false) + console.log('[CUSTOM_AUTH] Session synced from cache:', { + authenticated: authenticated.value, + userId: user.value?.id + }) + return true } + // Fallback to session cache + const sessionCache = nuxtApp.payload?.data?.['auth:session:cache'] + if (sessionCache && typeof sessionCache === 'object') { + user.value = sessionCache.user || null + authenticated.value = sessionCache.authenticated || false + loading.value = false + + console.log('[CUSTOM_AUTH] Session synced from session cache:', { + authenticated: authenticated.value, + userId: user.value?.id + }) + return true + } + + // No cache available + console.log('[CUSTOM_AUTH] No cache available, setting defaults') user.value = null authenticated.value = false - } finally { loading.value = false + return false + + } catch (error) { + console.error('[CUSTOM_AUTH] Error syncing from cache:', error) + user.value = null + authenticated.value = false + loading.value = false + return false + } + } + + // Simple check auth that only uses cache + const checkAuth = async (skipRetry = false) => { + loading.value = true + const synced = syncFromCache() + if (!synced) { + console.warn('[CUSTOM_AUTH] No auth cache available, user may need to refresh') } } @@ -133,6 +153,23 @@ export const useCustomAuth = () => { checkAuth() }) + // Watch for auth state changes in the cache (client-side only) + if (process.client) { + watch( + () => { + const nuxtApp = useNuxtApp() + return nuxtApp.payload?.data?.authState + }, + (newAuthState) => { + if (newAuthState && typeof newAuthState === 'object') { + console.log('[CUSTOM_AUTH] Auth state changed, syncing from cache') + syncFromCache() + } + }, + { deep: false } + ) + } + return { user: readonly(user), authenticated: readonly(authenticated), diff --git a/pages/dashboard.vue b/pages/dashboard.vue index c83c86a..8c03d03 100644 --- a/pages/dashboard.vue +++ b/pages/dashboard.vue @@ -187,9 +187,24 @@ const defaultMenu = computed(() => { return baseMenu; }); -const menu = computed(() => - toValue(tags).interest ? interestMenu : defaultMenu -); +const menu = computed(() => { + try { + const tagsValue = toValue(tags); + const menuToUse = tagsValue.interest ? interestMenu.value : defaultMenu.value; + + console.log('[Dashboard] Computing menu:', { + hasInterestTag: tagsValue.interest, + menuType: tagsValue.interest ? 'interestMenu' : 'defaultMenu', + menuIsArray: Array.isArray(menuToUse), + menuLength: menuToUse?.length + }); + + return menuToUse; + } catch (error) { + console.error('[Dashboard] Error computing menu:', error); + return []; + } +}); // Safe menu wrapper to prevent crashes when menu is undefined const safeMenu = computed(() => { diff --git a/pages/dashboard/admin/audit-logs.vue b/pages/dashboard/admin/audit-logs.vue new file mode 100644 index 0000000..4d3b2fc --- /dev/null +++ b/pages/dashboard/admin/audit-logs.vue @@ -0,0 +1,392 @@ + + + diff --git a/pages/dashboard/admin/system-logs.vue b/pages/dashboard/admin/system-logs.vue new file mode 100644 index 0000000..85cc550 --- /dev/null +++ b/pages/dashboard/admin/system-logs.vue @@ -0,0 +1,448 @@ + + + + + diff --git a/plugins/01.auth-refresh.client.ts b/plugins/01.auth-refresh.client.ts index 9e116cd..99b59e3 100644 --- a/plugins/01.auth-refresh.client.ts +++ b/plugins/01.auth-refresh.client.ts @@ -78,9 +78,14 @@ export default defineNuxtPlugin(() => { const checkAndScheduleRefresh = async () => { try { - const sessionData = await $fetch<{ user: any; authenticated: boolean }>('/api/auth/session') + // Use middleware cache instead of API call + const nuxtApp = useNuxtApp() + const authState = nuxtApp.payload?.data?.authState + const sessionCache = nuxtApp.payload?.data?.['auth:session:cache'] - if (sessionData.authenticated) { + const sessionData = authState || sessionCache + + if (sessionData?.authenticated) { // Get the session cookie to extract expiry time const sessionCookie = useCookie('nuxt-oidc-auth') @@ -98,6 +103,8 @@ export default defineNuxtPlugin(() => { console.error('[AUTH_REFRESH] Failed to parse session cookie:', parseError) } } + } else { + console.log('[AUTH_REFRESH] No authenticated session found in cache') } } catch (error) { console.error('[AUTH_REFRESH] Failed to check session:', error)