mirror of
https://github.com/mofeng-git/One-KVM.git
synced 2026-02-02 02:51:53 +08:00
fix: 将会话失效处理集中到路由并避免在登录页刷新循环
This commit is contained in:
@@ -6,7 +6,6 @@ const API_BASE = '/api'
|
|||||||
// Toast debounce mechanism - prevent toast spam (5 seconds)
|
// Toast debounce mechanism - prevent toast spam (5 seconds)
|
||||||
const toastDebounceMap = new Map<string, number>()
|
const toastDebounceMap = new Map<string, number>()
|
||||||
const TOAST_DEBOUNCE_TIME = 5000
|
const TOAST_DEBOUNCE_TIME = 5000
|
||||||
let sessionExpiredNotified = false
|
|
||||||
|
|
||||||
function shouldShowToast(key: string): boolean {
|
function shouldShowToast(key: string): boolean {
|
||||||
const now = Date.now()
|
const now = Date.now()
|
||||||
@@ -84,24 +83,10 @@ export async function request<T>(
|
|||||||
const message = getErrorMessage(data, `HTTP ${response.status}`)
|
const message = getErrorMessage(data, `HTTP ${response.status}`)
|
||||||
const normalized = message.toLowerCase()
|
const normalized = message.toLowerCase()
|
||||||
const isNotAuthenticated = normalized.includes('not authenticated')
|
const isNotAuthenticated = normalized.includes('not authenticated')
|
||||||
if (response.status === 401 && !sessionExpiredNotified) {
|
const isSessionExpired = normalized.includes('session expired')
|
||||||
const isLoggedInElsewhere = normalized.includes('logged in elsewhere')
|
const isLoggedInElsewhere = normalized.includes('logged in elsewhere')
|
||||||
const isSessionExpired = normalized.includes('session expired')
|
const isAuthIssue = response.status === 401 && (isNotAuthenticated || isSessionExpired || isLoggedInElsewhere)
|
||||||
if (isLoggedInElsewhere || isSessionExpired) {
|
if (toastOnError && shouldShowToast(toastKey) && !isAuthIssue) {
|
||||||
sessionExpiredNotified = true
|
|
||||||
const titleKey = isLoggedInElsewhere ? 'auth.loggedInElsewhere' : 'auth.sessionExpired'
|
|
||||||
if (toastOnError && shouldShowToast('error_session_expired')) {
|
|
||||||
toast.error(t(titleKey), {
|
|
||||||
description: message,
|
|
||||||
duration: 3000,
|
|
||||||
})
|
|
||||||
}
|
|
||||||
setTimeout(() => {
|
|
||||||
window.location.reload()
|
|
||||||
}, 1200)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
if (toastOnError && shouldShowToast(toastKey) && !(response.status === 401 && isNotAuthenticated)) {
|
|
||||||
toast.error(t('api.operationFailed'), {
|
toast.error(t('api.operationFailed'), {
|
||||||
description: message,
|
description: message,
|
||||||
duration: 4000,
|
duration: 4000,
|
||||||
|
|||||||
@@ -1,4 +1,7 @@
|
|||||||
import { createRouter, createWebHistory, type RouteRecordRaw } from 'vue-router'
|
import { createRouter, createWebHistory, type RouteRecordRaw } from 'vue-router'
|
||||||
|
import { toast } from 'vue-sonner'
|
||||||
|
import i18n from '@/i18n'
|
||||||
|
import { ApiError } from '@/api/request'
|
||||||
import { useAuthStore } from '@/stores/auth'
|
import { useAuthStore } from '@/stores/auth'
|
||||||
|
|
||||||
const routes: RouteRecordRaw[] = [
|
const routes: RouteRecordRaw[] = [
|
||||||
@@ -33,6 +36,12 @@ const router = createRouter({
|
|||||||
routes,
|
routes,
|
||||||
})
|
})
|
||||||
|
|
||||||
|
let sessionExpiredNotified = false
|
||||||
|
|
||||||
|
function t(key: string, params?: Record<string, unknown>): string {
|
||||||
|
return String(i18n.global.t(key, params as any))
|
||||||
|
}
|
||||||
|
|
||||||
// Navigation guard
|
// Navigation guard
|
||||||
router.beforeEach(async (to, _from, next) => {
|
router.beforeEach(async (to, _from, next) => {
|
||||||
const authStore = useAuthStore()
|
const authStore = useAuthStore()
|
||||||
@@ -54,8 +63,21 @@ router.beforeEach(async (to, _from, next) => {
|
|||||||
if (!authStore.isAuthenticated) {
|
if (!authStore.isAuthenticated) {
|
||||||
try {
|
try {
|
||||||
await authStore.checkAuth()
|
await authStore.checkAuth()
|
||||||
} catch {
|
} catch (e) {
|
||||||
// Not authenticated
|
// Not authenticated
|
||||||
|
if (e instanceof ApiError && e.status === 401 && !sessionExpiredNotified) {
|
||||||
|
const normalized = e.message.toLowerCase()
|
||||||
|
const isLoggedInElsewhere = normalized.includes('logged in elsewhere')
|
||||||
|
const isSessionExpired = normalized.includes('session expired')
|
||||||
|
if (isLoggedInElsewhere || isSessionExpired) {
|
||||||
|
sessionExpiredNotified = true
|
||||||
|
const titleKey = isLoggedInElsewhere ? 'auth.loggedInElsewhere' : 'auth.sessionExpired'
|
||||||
|
toast.error(t(titleKey), {
|
||||||
|
description: e.message,
|
||||||
|
duration: 3000,
|
||||||
|
})
|
||||||
|
}
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
if (!authStore.isAuthenticated) {
|
if (!authStore.isAuthenticated) {
|
||||||
|
|||||||
@@ -32,10 +32,14 @@ export const useAuthStore = defineStore('auth', () => {
|
|||||||
user.value = result.user || null
|
user.value = result.user || null
|
||||||
isAdmin.value = result.is_admin ?? false
|
isAdmin.value = result.is_admin ?? false
|
||||||
return result
|
return result
|
||||||
} catch {
|
} catch (e) {
|
||||||
isAuthenticated.value = false
|
isAuthenticated.value = false
|
||||||
user.value = null
|
user.value = null
|
||||||
isAdmin.value = false
|
isAdmin.value = false
|
||||||
|
error.value = e instanceof Error ? e.message : 'Not authenticated'
|
||||||
|
if (e instanceof Error) {
|
||||||
|
throw e
|
||||||
|
}
|
||||||
throw new Error('Not authenticated')
|
throw new Error('Not authenticated')
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
Reference in New Issue
Block a user