Merge pull request #2568 from wucm667/fix/setup-page-guard-after-init
fix(setup): 初始化完成后阻止访问 setup 页面
This commit is contained in:
commit
4fa4e372ca
@ -1,5 +1,6 @@
|
|||||||
import { describe, it, expect, vi, beforeEach } from 'vitest'
|
import { describe, it, expect, vi, beforeEach } from 'vitest'
|
||||||
import { setActivePinia, createPinia } from 'pinia'
|
import { setActivePinia, createPinia } from 'pinia'
|
||||||
|
import { resolveCompletedSetupRedirectPath } from '@/router/setupRedirect'
|
||||||
|
|
||||||
// Mock 导航加载状态
|
// Mock 导航加载状态
|
||||||
vi.mock('@/composables/useNavigationLoading', () => {
|
vi.mock('@/composables/useNavigationLoading', () => {
|
||||||
@ -53,6 +54,7 @@ interface MockAuthState {
|
|||||||
isSimpleMode: boolean
|
isSimpleMode: boolean
|
||||||
backendModeEnabled: boolean
|
backendModeEnabled: boolean
|
||||||
hasPendingAuthSession: boolean
|
hasPendingAuthSession: boolean
|
||||||
|
setupNeedsSetup?: boolean
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
@ -66,6 +68,10 @@ function simulateGuard(
|
|||||||
const requiresAuth = toMeta.requiresAuth !== false
|
const requiresAuth = toMeta.requiresAuth !== false
|
||||||
const requiresAdmin = toMeta.requiresAdmin === true
|
const requiresAdmin = toMeta.requiresAdmin === true
|
||||||
|
|
||||||
|
if (toPath === '/setup' && authState.setupNeedsSetup === false) {
|
||||||
|
return resolveCompletedSetupRedirectPath(authState.isAuthenticated, authState.isAdmin)
|
||||||
|
}
|
||||||
|
|
||||||
// 不需要认证的路由
|
// 不需要认证的路由
|
||||||
if (!requiresAuth) {
|
if (!requiresAuth) {
|
||||||
if (
|
if (
|
||||||
@ -378,6 +384,32 @@ describe('路由守卫逻辑', () => {
|
|||||||
expect(redirect).toBeNull()
|
expect(redirect).toBeNull()
|
||||||
})
|
})
|
||||||
|
|
||||||
|
it('unauthenticated: initialized /setup redirects to /login', () => {
|
||||||
|
const authState: MockAuthState = {
|
||||||
|
isAuthenticated: false,
|
||||||
|
isAdmin: false,
|
||||||
|
isSimpleMode: false,
|
||||||
|
backendModeEnabled: true,
|
||||||
|
hasPendingAuthSession: false,
|
||||||
|
setupNeedsSetup: false,
|
||||||
|
}
|
||||||
|
const redirect = simulateGuard('/setup', { requiresAuth: false }, authState)
|
||||||
|
expect(redirect).toBe('/login')
|
||||||
|
})
|
||||||
|
|
||||||
|
it('admin: initialized /setup redirects to /admin/dashboard', () => {
|
||||||
|
const authState: MockAuthState = {
|
||||||
|
isAuthenticated: true,
|
||||||
|
isAdmin: true,
|
||||||
|
isSimpleMode: false,
|
||||||
|
backendModeEnabled: true,
|
||||||
|
hasPendingAuthSession: false,
|
||||||
|
setupNeedsSetup: false,
|
||||||
|
}
|
||||||
|
const redirect = simulateGuard('/setup', { requiresAuth: false }, authState)
|
||||||
|
expect(redirect).toBe('/admin/dashboard')
|
||||||
|
})
|
||||||
|
|
||||||
it('admin: /admin/dashboard is allowed', () => {
|
it('admin: /admin/dashboard is allowed', () => {
|
||||||
const authState: MockAuthState = {
|
const authState: MockAuthState = {
|
||||||
isAuthenticated: true,
|
isAuthenticated: true,
|
||||||
|
|||||||
@ -9,6 +9,8 @@ import { useAppStore } from '@/stores/app'
|
|||||||
import { useAdminSettingsStore } from '@/stores/adminSettings'
|
import { useAdminSettingsStore } from '@/stores/adminSettings'
|
||||||
import { useNavigationLoadingState } from '@/composables/useNavigationLoading'
|
import { useNavigationLoadingState } from '@/composables/useNavigationLoading'
|
||||||
import { useRoutePrefetch } from '@/composables/useRoutePrefetch'
|
import { useRoutePrefetch } from '@/composables/useRoutePrefetch'
|
||||||
|
import { getSetupStatus } from '@/api/setup'
|
||||||
|
import { resolveCompletedSetupRedirectPath } from './setupRedirect'
|
||||||
import { resolveDocumentTitle } from './title'
|
import { resolveDocumentTitle } from './title'
|
||||||
|
|
||||||
/**
|
/**
|
||||||
@ -715,7 +717,7 @@ function isBackendModePublicRouteAllowed(path: string, hasPendingAuthSession: bo
|
|||||||
return false
|
return false
|
||||||
}
|
}
|
||||||
|
|
||||||
router.beforeEach((to, _from, next) => {
|
router.beforeEach(async (to, _from, next) => {
|
||||||
// 开始导航加载状态
|
// 开始导航加载状态
|
||||||
navigationLoading.startNavigation()
|
navigationLoading.startNavigation()
|
||||||
|
|
||||||
@ -750,6 +752,18 @@ router.beforeEach((to, _from, next) => {
|
|||||||
const requiresAuth = to.meta.requiresAuth !== false // Default to true
|
const requiresAuth = to.meta.requiresAuth !== false // Default to true
|
||||||
const requiresAdmin = to.meta.requiresAdmin === true
|
const requiresAdmin = to.meta.requiresAdmin === true
|
||||||
|
|
||||||
|
if (to.path === '/setup') {
|
||||||
|
try {
|
||||||
|
const status = await getSetupStatus()
|
||||||
|
if (!status.needs_setup) {
|
||||||
|
next(resolveCompletedSetupRedirectPath(authStore.isAuthenticated, authStore.isAdmin))
|
||||||
|
return
|
||||||
|
}
|
||||||
|
} catch {
|
||||||
|
// If setup status cannot be determined, keep the setup page reachable.
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
// If route doesn't require auth, allow access
|
// If route doesn't require auth, allow access
|
||||||
if (!requiresAuth) {
|
if (!requiresAuth) {
|
||||||
// If already authenticated and trying to access login/register, redirect to appropriate dashboard
|
// If already authenticated and trying to access login/register, redirect to appropriate dashboard
|
||||||
|
|||||||
7
frontend/src/router/setupRedirect.ts
Normal file
7
frontend/src/router/setupRedirect.ts
Normal file
@ -0,0 +1,7 @@
|
|||||||
|
export function resolveCompletedSetupRedirectPath(isAuthenticated: boolean, isAdmin: boolean): string {
|
||||||
|
if (!isAuthenticated) {
|
||||||
|
return '/login'
|
||||||
|
}
|
||||||
|
|
||||||
|
return isAdmin ? '/admin/dashboard' : '/dashboard'
|
||||||
|
}
|
||||||
Loading…
x
Reference in New Issue
Block a user