Skip to content
GitLab
Menu
Projects
Groups
Snippets
Loading...
Help
Help
Support
Community forum
Keyboard shortcuts
?
Submit feedback
Sign in / Register
Toggle navigation
Menu
Open sidebar
陈曦
sub2api
Commits
34183b52
Commit
34183b52
authored
Dec 25, 2025
by
ianshaw
Browse files
feat(frontend): 添加 OAuth 回调路由
- 新增 /auth/callback 路由用于接收 OAuth 授权回调 - 优化路由配置和文档 - 统一代码格式
parent
bceed08f
Changes
3
Hide whitespace changes
Inline
Side-by-side
frontend/src/router/README.md
View file @
34183b52
...
...
@@ -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');
<router-link
:to=
"
{ name: 'Keys' }">API Keys
</router-link>
<!-- With query parameters -->
<router-link
:to=
"
{ path: '/usage', query: { page: 1 } }">
Usage
</router-link>
<router-link
:to=
"
{ path: '/usage', query: { page: 1 } }"> Usage
</router-link>
</
template
>
```
### 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
...
...
frontend/src/router/index.ts
View file @
34183b52
...
...
@@ -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
frontend/src/router/meta.d.ts
View file @
34183b52
...
...
@@ -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
}
}
Write
Preview
Markdown
is supported
0%
Try again
or
attach a new file
.
Attach a file
Cancel
You are about to add
0
people
to the discussion. Proceed with caution.
Finish editing this message first!
Cancel
Please
register
or
sign in
to comment