Files
Aether/frontend/src/router/index.ts
fawney19 0061fc04b7 feat: 添加访问令牌管理功能并升级至 0.2.4
- 新增 Management Token(访问令牌)功能,支持创建、更新、删除和管理
- 前端添加访问令牌管理页面,支持普通用户和管理员
- 后端实现完整的令牌生命周期管理 API
- 添加数据库迁移脚本创建 management_tokens 表
- Nginx 配置添加 gzip 压缩,优化响应传输
- Dialog 组件添加 persistent 属性,防止意外关闭
- 为管理后台 API 添加详细的中文文档注释
- 简化多处类型注解,统一代码风格
2026-01-07 14:55:07 +08:00

228 lines
7.4 KiB
TypeScript

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: 'management-tokens',
name: 'ManagementTokens',
component: () => importWithRetry(() => import('@/views/user/ManagementTokens.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: 'management-tokens',
name: 'AdminManagementTokens',
component: () => importWithRetry(() => import('@/views/user/ManagementTokens.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: 'email',
name: 'EmailSettings',
component: () => importWithRetry(() => import('@/views/admin/EmailSettings.vue'))
},
{
path: 'ldap',
name: 'LdapSettings',
component: () => importWithRetry(() => import('@/views/admin/LdapSettings.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