/** * Vue Router configuration for Sub2API frontend * Defines all application routes with lazy loading and navigation guards */ import { createRouter, createWebHistory, type RouteRecordRaw } from 'vue-router' import { useAuthStore } from '@/stores/auth' /** * Route definitions with lazy loading */ const routes: RouteRecordRaw[] = [ // ==================== Setup Routes ==================== { path: '/setup', name: 'Setup', component: () => import('@/views/setup/SetupWizardView.vue'), meta: { requiresAuth: false, title: 'Setup' } }, // ==================== Public Routes ==================== { path: '/home', name: 'Home', component: () => import('@/views/HomeView.vue'), meta: { requiresAuth: false, title: 'Home' } }, { path: '/login', name: 'Login', component: () => import('@/views/auth/LoginView.vue'), meta: { requiresAuth: false, title: 'Login' } }, { path: '/register', name: 'Register', component: () => import('@/views/auth/RegisterView.vue'), meta: { requiresAuth: false, title: 'Register' } }, { path: '/email-verify', name: 'EmailVerify', component: () => import('@/views/auth/EmailVerifyView.vue'), meta: { requiresAuth: false, title: 'Verify Email' } }, { path: '/auth/callback', name: 'OAuthCallback', component: () => import('@/views/auth/OAuthCallbackView.vue'), meta: { requiresAuth: false, title: 'OAuth Callback' } }, // ==================== User Routes ==================== { path: '/', redirect: '/home' }, { path: '/dashboard', name: 'Dashboard', component: () => import('@/views/user/DashboardView.vue'), meta: { requiresAuth: true, requiresAdmin: false, title: 'Dashboard', titleKey: 'dashboard.title', descriptionKey: 'dashboard.welcomeMessage' } }, { path: '/keys', name: 'Keys', component: () => import('@/views/user/KeysView.vue'), meta: { requiresAuth: true, requiresAdmin: false, title: 'API Keys', titleKey: 'keys.title', descriptionKey: 'keys.description' } }, { path: '/usage', name: 'Usage', component: () => import('@/views/user/UsageView.vue'), meta: { requiresAuth: true, requiresAdmin: false, title: 'Usage Records', titleKey: 'usage.title', descriptionKey: 'usage.description' } }, { path: '/redeem', name: 'Redeem', component: () => import('@/views/user/RedeemView.vue'), meta: { requiresAuth: true, requiresAdmin: false, title: 'Redeem Code', titleKey: 'redeem.title', descriptionKey: 'redeem.description' } }, { path: '/profile', name: 'Profile', component: () => import('@/views/user/ProfileView.vue'), meta: { requiresAuth: true, requiresAdmin: false, title: 'Profile', titleKey: 'profile.title', descriptionKey: 'profile.description' } }, { path: '/subscriptions', name: 'Subscriptions', component: () => import('@/views/user/SubscriptionsView.vue'), meta: { requiresAuth: true, requiresAdmin: false, title: 'My Subscriptions', titleKey: 'userSubscriptions.title', descriptionKey: 'userSubscriptions.description' } }, // ==================== Admin Routes ==================== { path: '/admin', redirect: '/admin/dashboard' }, { path: '/admin/dashboard', name: 'AdminDashboard', component: () => import('@/views/admin/DashboardView.vue'), meta: { requiresAuth: true, requiresAdmin: true, title: 'Admin Dashboard', titleKey: 'admin.dashboard.title', descriptionKey: 'admin.dashboard.description' } }, { path: '/admin/users', name: 'AdminUsers', component: () => import('@/views/admin/UsersView.vue'), meta: { requiresAuth: true, requiresAdmin: true, title: 'User Management', titleKey: 'admin.users.title', descriptionKey: 'admin.users.description' } }, { path: '/admin/groups', name: 'AdminGroups', component: () => import('@/views/admin/GroupsView.vue'), meta: { requiresAuth: true, requiresAdmin: true, title: 'Group Management', titleKey: 'admin.groups.title', descriptionKey: 'admin.groups.description' } }, { path: '/admin/subscriptions', name: 'AdminSubscriptions', component: () => import('@/views/admin/SubscriptionsView.vue'), meta: { requiresAuth: true, requiresAdmin: true, title: 'Subscription Management', titleKey: 'admin.subscriptions.title', descriptionKey: 'admin.subscriptions.description' } }, { path: '/admin/accounts', name: 'AdminAccounts', component: () => import('@/views/admin/AccountsView.vue'), meta: { requiresAuth: true, requiresAdmin: true, title: 'Account Management', titleKey: 'admin.accounts.title', descriptionKey: 'admin.accounts.description' } }, { path: '/admin/proxies', name: 'AdminProxies', component: () => import('@/views/admin/ProxiesView.vue'), meta: { requiresAuth: true, requiresAdmin: true, title: 'Proxy Management', titleKey: 'admin.proxies.title', descriptionKey: 'admin.proxies.description' } }, { path: '/admin/redeem', name: 'AdminRedeem', component: () => import('@/views/admin/RedeemView.vue'), meta: { requiresAuth: true, requiresAdmin: true, title: 'Redeem Code Management', titleKey: 'admin.redeem.title', descriptionKey: 'admin.redeem.description' } }, { path: '/admin/settings', name: 'AdminSettings', component: () => import('@/views/admin/SettingsView.vue'), meta: { requiresAuth: true, requiresAdmin: true, title: 'System Settings', titleKey: 'admin.settings.title', descriptionKey: 'admin.settings.description' } }, { path: '/admin/usage', name: 'AdminUsage', component: () => import('@/views/admin/UsageView.vue'), meta: { requiresAuth: true, requiresAdmin: true, title: 'Usage Records', titleKey: 'admin.usage.title', descriptionKey: 'admin.usage.description' } }, // ==================== 404 Not Found ==================== { path: '/:pathMatch(.*)*', name: 'NotFound', component: () => import('@/views/NotFoundView.vue'), meta: { title: '404 Not Found' } } ] /** * Create router instance */ const router = createRouter({ history: createWebHistory(import.meta.env.BASE_URL), routes, scrollBehavior(_to, _from, savedPosition) { // Scroll to saved position when using browser back/forward if (savedPosition) { return savedPosition } // Scroll to top for new routes return { top: 0 } } }) /** * Navigation guard: Authentication check */ let authInitialized = false router.beforeEach((to, _from, next) => { const authStore = useAuthStore() // Restore auth state from localStorage on first navigation (page refresh) if (!authInitialized) { authStore.checkAuth() authInitialized = true } // Set page title if (to.meta.title) { document.title = `${to.meta.title} - Sub2API` } else { document.title = 'Sub2API' } // Check if route requires authentication const requiresAuth = to.meta.requiresAuth !== false // Default to true const requiresAdmin = to.meta.requiresAdmin === true // If route doesn't require auth, allow access if (!requiresAuth) { // If already authenticated and trying to access login/register, redirect to appropriate dashboard if (authStore.isAuthenticated && (to.path === '/login' || to.path === '/register')) { // Admin users go to admin dashboard, regular users go to user dashboard next(authStore.isAdmin ? '/admin/dashboard' : '/dashboard') return } next() return } // Route requires authentication if (!authStore.isAuthenticated) { // Not authenticated, redirect to login next({ path: '/login', query: { redirect: to.fullPath } // Save intended destination }) return } // Check admin requirement if (requiresAdmin && !authStore.isAdmin) { // User is authenticated but not admin, redirect to user dashboard next('/dashboard') return } // 简易模式下限制访问某些页面 if (authStore.isSimpleMode) { const restrictedPaths = [ '/admin/groups', '/admin/subscriptions', '/admin/redeem', '/subscriptions', '/redeem' ] if (restrictedPaths.some((path) => to.path.startsWith(path))) { // 简易模式下访问受限页面,重定向到仪表板 next(authStore.isAdmin ? '/admin/dashboard' : '/dashboard') return } } // All checks passed, allow navigation next() }) /** * Navigation guard: Error handling */ router.onError((error) => { console.error('Router error:', error) }) export default router