diff --git a/frontend/src/router/README.md b/frontend/src/router/README.md
index 9f600f81..4ffacb8f 100644
--- a/frontend/src/router/README.md
+++ b/frontend/src/router/README.md
@@ -13,38 +13,38 @@ This directory contains the Vue Router configuration for the Sub2API frontend ap
### Public Routes (No Authentication Required)
-| Path | Component | Description |
-|------|-----------|-------------|
-| `/login` | LoginView | User login page |
+| Path | Component | Description |
+| ----------- | ------------ | ---------------------- |
+| `/login` | LoginView | User login page |
| `/register` | RegisterView | User registration page |
### User Routes (Authentication Required)
-| Path | Component | Description |
-|------|-----------|-------------|
-| `/` | - | Redirects to `/dashboard` |
-| `/dashboard` | DashboardView | User dashboard with stats |
-| `/keys` | KeysView | API key management |
-| `/usage` | UsageView | Usage records and statistics |
-| `/redeem` | RedeemView | Redeem code interface |
-| `/profile` | ProfileView | User profile settings |
+| Path | Component | Description |
+| ------------ | ------------- | ---------------------------- |
+| `/` | - | Redirects to `/dashboard` |
+| `/dashboard` | DashboardView | User dashboard with stats |
+| `/keys` | KeysView | API key management |
+| `/usage` | UsageView | Usage records and statistics |
+| `/redeem` | RedeemView | Redeem code interface |
+| `/profile` | ProfileView | User profile settings |
### Admin Routes (Admin Role Required)
-| Path | Component | Description |
-|------|-----------|-------------|
-| `/admin` | - | Redirects to `/admin/dashboard` |
-| `/admin/dashboard` | AdminDashboardView | Admin dashboard |
-| `/admin/users` | AdminUsersView | User management |
-| `/admin/groups` | AdminGroupsView | Group management |
-| `/admin/accounts` | AdminAccountsView | Account management |
-| `/admin/proxies` | AdminProxiesView | Proxy management |
-| `/admin/redeem` | AdminRedeemView | Redeem code management |
+| Path | Component | Description |
+| ------------------ | ------------------ | ------------------------------- |
+| `/admin` | - | Redirects to `/admin/dashboard` |
+| `/admin/dashboard` | AdminDashboardView | Admin dashboard |
+| `/admin/users` | AdminUsersView | User management |
+| `/admin/groups` | AdminGroupsView | Group management |
+| `/admin/accounts` | AdminAccountsView | Account management |
+| `/admin/proxies` | AdminProxiesView | Proxy management |
+| `/admin/redeem` | AdminRedeemView | Redeem code management |
### Special Routes
-| Path | Component | Description |
-|------|-----------|-------------|
+| Path | Component | Description |
+| ----------------- | ------------ | -------------- |
| `/:pathMatch(.*)` | NotFoundView | 404 error page |
## Navigation Guards
@@ -92,15 +92,16 @@ Each route can define the following meta fields:
```typescript
interface RouteMeta {
- requiresAuth?: boolean; // Default: true (requires authentication)
- requiresAdmin?: boolean; // Default: false (admin access only)
- title?: string; // Page title
- breadcrumbs?: Array<{ // Breadcrumb navigation
- label: string;
- to?: string;
- }>;
- icon?: string; // Icon for navigation menu
- hideInMenu?: boolean; // Hide from navigation menu
+ requiresAuth?: boolean // Default: true (requires authentication)
+ requiresAdmin?: boolean // Default: false (admin access only)
+ title?: string // Page title
+ breadcrumbs?: Array<{
+ // Breadcrumb navigation
+ label: string
+ to?: string
+ }>
+ icon?: string // Icon for navigation menu
+ hideInMenu?: boolean // Hide from navigation menu
}
```
@@ -113,6 +114,7 @@ component: () => import('@/views/user/DashboardView.vue')
```
Benefits:
+
- Reduced initial bundle size
- Faster initial page load
- Components loaded on-demand
@@ -123,7 +125,7 @@ Benefits:
The router integrates with the Pinia auth store (`@/stores/auth`):
```typescript
-const authStore = useAuthStore();
+const authStore = useAuthStore()
// Check authentication status
authStore.isAuthenticated
@@ -137,21 +139,21 @@ authStore.isAdmin
### Programmatic Navigation
```typescript
-import { useRouter } from 'vue-router';
+import { useRouter } from 'vue-router'
-const router = useRouter();
+const router = useRouter()
// Navigate to a route
-router.push('/dashboard');
+router.push('/dashboard')
// Navigate with query parameters
router.push({
path: '/usage',
query: { filter: 'today' }
-});
+})
// Navigate to admin route (will be blocked if not admin)
-router.push('/admin/users');
+router.push('/admin/users')
```
### Route Links
@@ -165,24 +167,22 @@ router.push('/admin/users');
API Keys
-
- Usage
-
+ Usage
```
### Checking Current Route
```typescript
-import { useRoute } from 'vue-router';
+import { useRoute } from 'vue-router'
-const route = useRoute();
+const route = useRoute()
// Check if on admin page
-const isAdminPage = route.path.startsWith('/admin');
+const isAdminPage = route.path.startsWith('/admin')
// Get route meta
-const requiresAdmin = route.meta.requiresAdmin;
+const requiresAdmin = route.meta.requiresAdmin
```
## Scroll Behavior
@@ -199,8 +199,8 @@ The router includes error handling for navigation failures:
```typescript
router.onError((error) => {
- console.error('Router error:', error);
-});
+ console.error('Router error:', error)
+})
```
## Testing Routes
@@ -229,7 +229,7 @@ Enable Vue Router debug mode:
```typescript
// In browser console
-window.__VUE_ROUTER__ = router;
+window.__VUE_ROUTER__ = router
// Check current route
router.currentRoute.value
@@ -238,14 +238,17 @@ router.currentRoute.value
### Common Issues
**Issue**: 404 on page refresh
+
- **Cause**: Server not configured for SPA
- **Solution**: Configure server to serve `index.html` for all routes
**Issue**: Navigation guard runs twice
+
- **Cause**: Multiple `next()` calls
- **Solution**: Ensure only one `next()` call per code path
**Issue**: User data not loaded
+
- **Cause**: Auth store not initialized
- **Solution**: Call `authStore.checkAuth()` in App.vue or main.ts
diff --git a/frontend/src/router/index.ts b/frontend/src/router/index.ts
index d99d7072..ecc18de1 100644
--- a/frontend/src/router/index.ts
+++ b/frontend/src/router/index.ts
@@ -3,8 +3,8 @@
* Defines all application routes with lazy loading and navigation guards
*/
-import { createRouter, createWebHistory, type RouteRecordRaw } from 'vue-router';
-import { useAuthStore } from '@/stores/auth';
+import { createRouter, createWebHistory, type RouteRecordRaw } from 'vue-router'
+import { useAuthStore } from '@/stores/auth'
/**
* Route definitions with lazy loading
@@ -17,8 +17,8 @@ const routes: RouteRecordRaw[] = [
component: () => import('@/views/setup/SetupWizardView.vue'),
meta: {
requiresAuth: false,
- title: 'Setup',
- },
+ title: 'Setup'
+ }
},
// ==================== Public Routes ====================
@@ -28,8 +28,8 @@ const routes: RouteRecordRaw[] = [
component: () => import('@/views/HomeView.vue'),
meta: {
requiresAuth: false,
- title: 'Home',
- },
+ title: 'Home'
+ }
},
{
path: '/login',
@@ -37,8 +37,8 @@ const routes: RouteRecordRaw[] = [
component: () => import('@/views/auth/LoginView.vue'),
meta: {
requiresAuth: false,
- title: 'Login',
- },
+ title: 'Login'
+ }
},
{
path: '/register',
@@ -46,8 +46,8 @@ const routes: RouteRecordRaw[] = [
component: () => import('@/views/auth/RegisterView.vue'),
meta: {
requiresAuth: false,
- title: 'Register',
- },
+ title: 'Register'
+ }
},
{
path: '/email-verify',
@@ -55,14 +55,23 @@ const routes: RouteRecordRaw[] = [
component: () => import('@/views/auth/EmailVerifyView.vue'),
meta: {
requiresAuth: false,
- title: 'Verify Email',
- },
+ 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',
+ redirect: '/home'
},
{
path: '/dashboard',
@@ -73,8 +82,8 @@ const routes: RouteRecordRaw[] = [
requiresAdmin: false,
title: 'Dashboard',
titleKey: 'dashboard.title',
- descriptionKey: 'dashboard.welcomeMessage',
- },
+ descriptionKey: 'dashboard.welcomeMessage'
+ }
},
{
path: '/keys',
@@ -85,8 +94,8 @@ const routes: RouteRecordRaw[] = [
requiresAdmin: false,
title: 'API Keys',
titleKey: 'keys.title',
- descriptionKey: 'keys.description',
- },
+ descriptionKey: 'keys.description'
+ }
},
{
path: '/usage',
@@ -97,8 +106,8 @@ const routes: RouteRecordRaw[] = [
requiresAdmin: false,
title: 'Usage Records',
titleKey: 'usage.title',
- descriptionKey: 'usage.description',
- },
+ descriptionKey: 'usage.description'
+ }
},
{
path: '/redeem',
@@ -109,8 +118,8 @@ const routes: RouteRecordRaw[] = [
requiresAdmin: false,
title: 'Redeem Code',
titleKey: 'redeem.title',
- descriptionKey: 'redeem.description',
- },
+ descriptionKey: 'redeem.description'
+ }
},
{
path: '/profile',
@@ -121,8 +130,8 @@ const routes: RouteRecordRaw[] = [
requiresAdmin: false,
title: 'Profile',
titleKey: 'profile.title',
- descriptionKey: 'profile.description',
- },
+ descriptionKey: 'profile.description'
+ }
},
{
path: '/subscriptions',
@@ -133,14 +142,14 @@ const routes: RouteRecordRaw[] = [
requiresAdmin: false,
title: 'My Subscriptions',
titleKey: 'userSubscriptions.title',
- descriptionKey: 'userSubscriptions.description',
- },
+ descriptionKey: 'userSubscriptions.description'
+ }
},
// ==================== Admin Routes ====================
{
path: '/admin',
- redirect: '/admin/dashboard',
+ redirect: '/admin/dashboard'
},
{
path: '/admin/dashboard',
@@ -151,8 +160,8 @@ const routes: RouteRecordRaw[] = [
requiresAdmin: true,
title: 'Admin Dashboard',
titleKey: 'admin.dashboard.title',
- descriptionKey: 'admin.dashboard.description',
- },
+ descriptionKey: 'admin.dashboard.description'
+ }
},
{
path: '/admin/users',
@@ -163,8 +172,8 @@ const routes: RouteRecordRaw[] = [
requiresAdmin: true,
title: 'User Management',
titleKey: 'admin.users.title',
- descriptionKey: 'admin.users.description',
- },
+ descriptionKey: 'admin.users.description'
+ }
},
{
path: '/admin/groups',
@@ -175,8 +184,8 @@ const routes: RouteRecordRaw[] = [
requiresAdmin: true,
title: 'Group Management',
titleKey: 'admin.groups.title',
- descriptionKey: 'admin.groups.description',
- },
+ descriptionKey: 'admin.groups.description'
+ }
},
{
path: '/admin/subscriptions',
@@ -187,8 +196,8 @@ const routes: RouteRecordRaw[] = [
requiresAdmin: true,
title: 'Subscription Management',
titleKey: 'admin.subscriptions.title',
- descriptionKey: 'admin.subscriptions.description',
- },
+ descriptionKey: 'admin.subscriptions.description'
+ }
},
{
path: '/admin/accounts',
@@ -199,8 +208,8 @@ const routes: RouteRecordRaw[] = [
requiresAdmin: true,
title: 'Account Management',
titleKey: 'admin.accounts.title',
- descriptionKey: 'admin.accounts.description',
- },
+ descriptionKey: 'admin.accounts.description'
+ }
},
{
path: '/admin/proxies',
@@ -211,8 +220,8 @@ const routes: RouteRecordRaw[] = [
requiresAdmin: true,
title: 'Proxy Management',
titleKey: 'admin.proxies.title',
- descriptionKey: 'admin.proxies.description',
- },
+ descriptionKey: 'admin.proxies.description'
+ }
},
{
path: '/admin/redeem',
@@ -223,8 +232,8 @@ const routes: RouteRecordRaw[] = [
requiresAdmin: true,
title: 'Redeem Code Management',
titleKey: 'admin.redeem.title',
- descriptionKey: 'admin.redeem.description',
- },
+ descriptionKey: 'admin.redeem.description'
+ }
},
{
path: '/admin/settings',
@@ -235,8 +244,8 @@ const routes: RouteRecordRaw[] = [
requiresAdmin: true,
title: 'System Settings',
titleKey: 'admin.settings.title',
- descriptionKey: 'admin.settings.description',
- },
+ descriptionKey: 'admin.settings.description'
+ }
},
{
path: '/admin/usage',
@@ -247,8 +256,8 @@ const routes: RouteRecordRaw[] = [
requiresAdmin: true,
title: 'Usage Records',
titleKey: 'admin.usage.title',
- descriptionKey: 'admin.usage.description',
- },
+ descriptionKey: 'admin.usage.description'
+ }
},
// ==================== 404 Not Found ====================
@@ -257,10 +266,10 @@ const routes: RouteRecordRaw[] = [
name: 'NotFound',
component: () => import('@/views/NotFoundView.vue'),
meta: {
- title: '404 Not Found',
- },
- },
-];
+ title: '404 Not Found'
+ }
+ }
+]
/**
* Create router instance
@@ -271,48 +280,48 @@ const router = createRouter({
scrollBehavior(_to, _from, savedPosition) {
// Scroll to saved position when using browser back/forward
if (savedPosition) {
- return savedPosition;
+ return savedPosition
}
// Scroll to top for new routes
- return { top: 0 };
- },
-});
+ return { top: 0 }
+ }
+})
/**
* Navigation guard: Authentication check
*/
-let authInitialized = false;
+let authInitialized = false
router.beforeEach((to, _from, next) => {
- const authStore = useAuthStore();
+ const authStore = useAuthStore()
// Restore auth state from localStorage on first navigation (page refresh)
if (!authInitialized) {
- authStore.checkAuth();
- authInitialized = true;
+ authStore.checkAuth()
+ authInitialized = true
}
// Set page title
if (to.meta.title) {
- document.title = `${to.meta.title} - Sub2API`;
+ document.title = `${to.meta.title} - Sub2API`
} else {
- document.title = 'Sub2API';
+ document.title = 'Sub2API'
}
// Check if route requires authentication
- const requiresAuth = to.meta.requiresAuth !== false; // Default to true
- const requiresAdmin = to.meta.requiresAdmin === true;
+ 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(authStore.isAdmin ? '/admin/dashboard' : '/dashboard')
+ return
}
- next();
- return;
+ next()
+ return
}
// Route requires authentication
@@ -320,27 +329,27 @@ router.beforeEach((to, _from, next) => {
// Not authenticated, redirect to login
next({
path: '/login',
- query: { redirect: to.fullPath }, // Save intended destination
- });
- return;
+ 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;
+ next('/dashboard')
+ return
}
// All checks passed, allow navigation
- next();
-});
+ next()
+})
/**
* Navigation guard: Error handling
*/
router.onError((error) => {
- console.error('Router error:', error);
-});
+ console.error('Router error:', error)
+})
-export default router;
+export default router
diff --git a/frontend/src/router/meta.d.ts b/frontend/src/router/meta.d.ts
index 0e115b06..e7546537 100644
--- a/frontend/src/router/meta.d.ts
+++ b/frontend/src/router/meta.d.ts
@@ -3,7 +3,7 @@
* Extends the RouteMeta interface with custom properties
*/
-import 'vue-router';
+import 'vue-router'
declare module 'vue-router' {
interface RouteMeta {
@@ -11,36 +11,36 @@ declare module 'vue-router' {
* Whether this route requires authentication
* @default true
*/
- requiresAuth?: boolean;
+ requiresAuth?: boolean
/**
* Whether this route requires admin role
* @default false
*/
- requiresAdmin?: boolean;
+ requiresAdmin?: boolean
/**
* Page title for this route
*/
- title?: string;
+ title?: string
/**
* Optional breadcrumb items for navigation
*/
breadcrumbs?: Array<{
- label: string;
- to?: string;
- }>;
+ label: string
+ to?: string
+ }>
/**
* Icon name for this route (for sidebar navigation)
*/
- icon?: string;
+ icon?: string
/**
* Whether to hide this route from navigation menu
* @default false
*/
- hideInMenu?: boolean;
+ hideInMenu?: boolean
}
}