Merge pull request #2568 from wucm667/fix/setup-page-guard-after-init

fix(setup): 初始化完成后阻止访问 setup 页面
This commit is contained in:
Wesley Liddick 2026-05-19 16:02:58 +08:00 committed by GitHub
commit 4fa4e372ca
No known key found for this signature in database
GPG Key ID: B5690EEEBB952194
3 changed files with 54 additions and 1 deletions

View File

@ -1,5 +1,6 @@
import { describe, it, expect, vi, beforeEach } from 'vitest'
import { setActivePinia, createPinia } from 'pinia'
import { resolveCompletedSetupRedirectPath } from '@/router/setupRedirect'
// Mock 导航加载状态
vi.mock('@/composables/useNavigationLoading', () => {
@ -53,6 +54,7 @@ interface MockAuthState {
isSimpleMode: boolean
backendModeEnabled: boolean
hasPendingAuthSession: boolean
setupNeedsSetup?: boolean
}
/**
@ -66,6 +68,10 @@ function simulateGuard(
const requiresAuth = toMeta.requiresAuth !== false
const requiresAdmin = toMeta.requiresAdmin === true
if (toPath === '/setup' && authState.setupNeedsSetup === false) {
return resolveCompletedSetupRedirectPath(authState.isAuthenticated, authState.isAdmin)
}
// 不需要认证的路由
if (!requiresAuth) {
if (
@ -378,6 +384,32 @@ describe('路由守卫逻辑', () => {
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', () => {
const authState: MockAuthState = {
isAuthenticated: true,

View File

@ -9,6 +9,8 @@ import { useAppStore } from '@/stores/app'
import { useAdminSettingsStore } from '@/stores/adminSettings'
import { useNavigationLoadingState } from '@/composables/useNavigationLoading'
import { useRoutePrefetch } from '@/composables/useRoutePrefetch'
import { getSetupStatus } from '@/api/setup'
import { resolveCompletedSetupRedirectPath } from './setupRedirect'
import { resolveDocumentTitle } from './title'
/**
@ -715,7 +717,7 @@ function isBackendModePublicRouteAllowed(path: string, hasPendingAuthSession: bo
return false
}
router.beforeEach((to, _from, next) => {
router.beforeEach(async (to, _from, next) => {
// 开始导航加载状态
navigationLoading.startNavigation()
@ -750,6 +752,18 @@ router.beforeEach((to, _from, next) => {
const requiresAuth = to.meta.requiresAuth !== false // Default to 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 (!requiresAuth) {
// If already authenticated and trying to access login/register, redirect to appropriate dashboard

View File

@ -0,0 +1,7 @@
export function resolveCompletedSetupRedirectPath(isAuthenticated: boolean, isAdmin: boolean): string {
if (!isAuthenticated) {
return '/login'
}
return isAdmin ? '/admin/dashboard' : '/dashboard'
}