Files
sub2api/frontend/src/router/index.ts
2025-12-18 13:50:39 +08:00

346 lines
8.3 KiB
TypeScript

/**
* 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',
},
},
// ==================== 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 dashboard
if (authStore.isAuthenticated && (to.path === '/login' || to.path === '/register')) {
next('/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;
}
// All checks passed, allow navigation
next();
});
/**
* Navigation guard: Error handling
*/
router.onError((error) => {
console.error('Router error:', error);
});
export default router;