Files
Aether/frontend/src/router/index.ts

208 lines
6.7 KiB
TypeScript
Raw Normal View History

2025-12-10 20:52:44 +08:00
import { createRouter, createWebHistory } from 'vue-router'
import type { RouteRecordRaw } from 'vue-router'
import { useAuthStore } from '@/stores/auth'
import { importWithRetry } from '@/utils/importRetry'
import { log } from '@/utils/logger'
const routes: RouteRecordRaw[] = [
{
path: '/',
name: 'Home',
component: () => importWithRetry(() => import('@/views/public/Home.vue')),
meta: { requiresAuth: false }
},
{
path: '/logo-demo',
name: 'LogoColorDemo',
component: () => importWithRetry(() => import('@/views/public/LogoColorDemo.vue')),
meta: { requiresAuth: false }
},
{
path: '/dashboard',
component: () => importWithRetry(() => import('@/layouts/MainLayout.vue')),
meta: { requiresAuth: true },
children: [
{
path: '',
name: 'Dashboard',
component: () => importWithRetry(() => import('@/views/shared/Dashboard.vue'))
},
{
path: 'api-keys',
name: 'MyApiKeys',
component: () => importWithRetry(() => import('@/views/user/MyApiKeys.vue'))
},
{
path: 'announcements',
name: 'Announcements',
component: () => importWithRetry(() => import('@/views/user/Announcements.vue'))
},
{
path: 'usage',
name: 'MyUsage',
component: () => importWithRetry(() => import('@/views/shared/Usage.vue'))
},
{
path: 'endpoint-status',
name: 'EndpointStatus',
component: () => importWithRetry(() => import('@/views/shared/HealthMonitor.vue'))
},
{
path: 'settings',
name: 'Settings',
component: () => importWithRetry(() => import('@/views/user/Settings.vue'))
},
{
path: 'models',
name: 'ModelCatalog',
component: () => importWithRetry(() => import('@/views/user/ModelCatalog.vue'))
}
]
},
{
path: '/admin',
component: () => importWithRetry(() => import('@/layouts/MainLayout.vue')),
meta: { requiresAuth: true, requiresAdmin: true },
children: [
{
path: 'dashboard',
name: 'AdminDashboard',
component: () => importWithRetry(() => import('@/views/shared/Dashboard.vue'))
},
{
path: 'users',
name: 'Users',
component: () => importWithRetry(() => import('@/views/admin/Users.vue'))
},
{
path: 'keys',
name: 'ApiKeys',
component: () => importWithRetry(() => import('@/views/admin/ApiKeys.vue'))
},
{
path: 'providers',
name: 'ProviderManagement',
component: () => importWithRetry(() => import('@/views/admin/ProviderManagement.vue'))
},
{
path: 'models',
name: 'ModelManagement',
component: () => importWithRetry(() => import('@/views/admin/ModelManagement.vue'))
},
{
path: 'health-monitor',
name: 'HealthMonitor',
component: () => importWithRetry(() => import('@/views/shared/HealthMonitor.vue'))
},
{
path: 'usage',
name: 'Usage',
component: () => importWithRetry(() => import('@/views/shared/Usage.vue'))
},
{
path: 'system',
name: 'SystemSettings',
component: () => importWithRetry(() => import('@/views/admin/SystemSettings.vue'))
},
{
path: 'audit-logs',
name: 'AuditLogs',
component: () => importWithRetry(() => import('@/views/admin/AuditLogs.vue'))
},
{
path: 'cache-monitoring',
name: 'CacheMonitoring',
component: () => importWithRetry(() => import('@/views/admin/CacheMonitoring.vue'))
},
{
path: 'ip-security',
name: 'IPSecurity',
component: () => importWithRetry(() => import('@/views/admin/IPSecurity.vue'))
},
{
path: 'announcements',
name: 'AnnouncementManagement',
component: () => importWithRetry(() => import('@/views/user/Announcements.vue'))
}
]
}
]
const router = createRouter({
history: createWebHistory(import.meta.env.BASE_URL),
routes
})
/**
*
*/
function isNetworkError(error: any): boolean {
return !error.response || error.message?.includes('Network') || error.message?.includes('timeout')
}
router.beforeEach(async (to, from, next) => {
const authStore = useAuthStore()
try {
// 如果有token但没有用户信息,尝试获取用户信息
if (authStore.token && !authStore.user) {
try {
await authStore.fetchCurrentUser()
} catch (error: any) {
// 区分网络错误和认证错误
if (isNetworkError(error)) {
log.warn('Network error while fetching user info, keeping session', { error: error?.message })
} else if (error.response?.status === 401) {
log.info('Authentication failed, clearing session')
authStore.logout()
} else {
log.warn('Failed to fetch user info, but keeping session', { error: error?.message })
}
}
}
// 检查整个路由匹配记录链中的 meta
const requiresAuth = to.matched.some(record => record.meta.requiresAuth !== false)
const requiresAdmin = to.matched.some(record => record.meta.requiresAdmin)
// 如果需要认证但没有token,跳转到首页
if (requiresAuth && !authStore.token) {
sessionStorage.setItem('redirectPath', to.fullPath)
log.debug('No valid token found, redirecting to home')
next('/')
} else if (to.path === '/' && authStore.isAuthenticated && (to.query.returnTo || from.path.startsWith('/dashboard') || from.path.startsWith('/admin') || from.path === '/')) {
// 已登录用户如果是从dashboard返回首页、刷新首页、或者有returnTo参数,允许访问首页
next()
} else if (authStore.isAuthenticated && to.path === '/' && !to.query.returnTo) {
// 已登录用户首次访问首页(非返回/刷新场景),根据角色跳转到对应仪表盘
const isAdmin = authStore.user?.role === 'admin'
const redirectPath = sessionStorage.getItem('redirectPath')
if (redirectPath && redirectPath !== '/') {
sessionStorage.removeItem('redirectPath')
next(redirectPath)
} else {
next(isAdmin ? '/admin/dashboard' : '/dashboard')
}
} else if (requiresAdmin) {
// 需要管理员权限的页面
const isAdmin = authStore.user?.role === 'admin'
if (!isAdmin) {
log.warn('Non-admin user attempted to access admin page, redirecting to user dashboard')
next('/dashboard')
} else {
next()
}
} else {
next()
}
} catch (error) {
log.error('Router guard error', error)
// 发生错误时,直接放行,不要乱跳转
next()
}
})
export default router