This commit is contained in:
2026-08-07 14:47:00 +08:00
parent 54fe05d864
commit 0bd872fdbf
12 changed files with 461 additions and 36 deletions
+22 -11
View File
@@ -3,6 +3,7 @@ import { parseApiJsonResponse } from '@chongchong/shared'
import { clearSessionTokens, getAccessToken, getRefreshToken, persistTokenPair, type TokenPair } from '@/api/session'
import { apiErrorMessage, asFieldErrors } from '@/api/errorDetails'
import { ApiError, isApiEnvelope, type ApiEnvelope } from '@/api/types'
import { coordinateCrossTabRefresh } from '@/api/crossTabRefresh'
const transport = axios.create({
baseURL: import.meta.env.VITE_API_BASE_URL || '/api',
@@ -42,7 +43,7 @@ function mapError(error: unknown) {
return new ApiError(error.message || '请求失败', 'HTTP_ERROR', error.response?.status)
}
let refreshPromise: Promise<TokenPair> | null = null
let refreshPromise: Promise<string> | null = null
let redirectingToLogin = false
function redirectToLoginAfterSessionExpiry() {
@@ -57,16 +58,26 @@ function redirectToLoginAfterSessionExpiry() {
}
async function refreshSession() {
const refreshToken = getRefreshToken()
if (!refreshToken) throw new ApiError('登录已过期,请重新登录', 'A0401', 401)
const observedRefreshToken = getRefreshToken()
if (!observedRefreshToken) throw new ApiError('登录已过期,请重新登录', 'A0401', 401)
if (!refreshPromise) {
refreshPromise = transport
.post<ApiEnvelope<TokenPair>>('/auth/refresh', { refreshToken })
.then((response) => unwrap(response.data))
.then((tokens) => {
refreshPromise = coordinateCrossTabRefresh({
lockName: 'cc-client-auth-refresh',
observedRefreshToken,
currentRefreshToken: getRefreshToken,
reuseCurrentSession: async () => {
const currentAccessToken = getAccessToken()
if (!currentAccessToken) throw new ApiError('登录已过期,请重新登录', 'A0401', 401)
return currentAccessToken
},
refresh: async (currentRefreshToken) => {
const response = await transport.post<ApiEnvelope<TokenPair>>('/auth/refresh', { refreshToken: currentRefreshToken })
const tokens = unwrap(response.data)
if (!getRefreshToken()) throw new ApiError('登录已在其他标签页退出', 'A0401', 401)
persistTokenPair(tokens)
return tokens
})
return tokens.accessToken
},
})
.finally(() => {
refreshPromise = null
})
@@ -92,8 +103,8 @@ transport.interceptors.response.use(
request._retriedAfterRefresh = true
try {
const tokens = await refreshSession()
request.headers.Authorization = `Bearer ${tokens.accessToken}`
const accessToken = await refreshSession()
request.headers.Authorization = `Bearer ${accessToken}`
return await transport.request(request)
} catch (refreshError) {
const mappedError = mapError(refreshError)