优化
This commit is contained in:
+149
@@ -0,0 +1,149 @@
|
||||
import { refresh as refreshRequest } from '@/api/auth.js'
|
||||
import { isAccessTokenExpiring, session, signOut, updateTokenPair } from '@/stores/session.js'
|
||||
import { requireOnline } from '@/utils/network.js'
|
||||
|
||||
let refreshPromise = null
|
||||
let redirectingToLogin = false
|
||||
|
||||
function responseCode(response) {
|
||||
return String(response?.data?.code ?? response?.statusCode ?? '')
|
||||
}
|
||||
|
||||
function isUnauthorized(responseOrError) {
|
||||
const statusCode = Number(responseOrError?.statusCode ?? responseOrError?.response?.statusCode ?? 0)
|
||||
const code = String(responseOrError?.code ?? responseCode(responseOrError))
|
||||
return statusCode === 401 || code === '401' || code === 'A0401'
|
||||
}
|
||||
|
||||
function sessionExpiredError(source) {
|
||||
const error = new Error('登录已过期,请重新登录')
|
||||
error.code = source?.code || 'A0401'
|
||||
error.statusCode = source?.statusCode || 401
|
||||
return error
|
||||
}
|
||||
|
||||
function redirectAfterSessionExpiry() {
|
||||
if (redirectingToLogin) return
|
||||
redirectingToLogin = true
|
||||
if (typeof uni !== 'undefined' && typeof uni.showToast === 'function') {
|
||||
uni.showToast({ title: '登录已过期,请重新登录', icon: 'none', duration: 2200 })
|
||||
}
|
||||
if (typeof uni !== 'undefined' && typeof uni.reLaunch === 'function') {
|
||||
uni.reLaunch({
|
||||
url: '/pages/login/login',
|
||||
complete() { setTimeout(() => { redirectingToLogin = false }, 1000) }
|
||||
})
|
||||
}
|
||||
}
|
||||
|
||||
function expireSession(source, observedRefreshToken = session.refreshToken) {
|
||||
if (observedRefreshToken && session.refreshToken && observedRefreshToken !== session.refreshToken) return session.token
|
||||
const hadSession = Boolean(session.currentUser || session.token || session.refreshToken)
|
||||
signOut()
|
||||
if (hadSession) redirectAfterSessionExpiry()
|
||||
throw sessionExpiredError(source)
|
||||
}
|
||||
|
||||
function networkError(error, fallback = '网络连接失败') {
|
||||
const next = new Error(error?.errMsg || fallback)
|
||||
next.network = true
|
||||
return next
|
||||
}
|
||||
|
||||
function rawRequest(options, accessToken) {
|
||||
const baseUrl = session.apiBase || '/api/platform/v1'
|
||||
const headers = { ...(options.header || {}) }
|
||||
if (accessToken) headers.Authorization = `Bearer ${accessToken}`
|
||||
if (session.requestId) headers['X-Request-Id'] = session.requestId
|
||||
return new Promise((resolve, reject) => {
|
||||
uni.request({
|
||||
...options,
|
||||
url: `${baseUrl}${options.url}`,
|
||||
header: headers,
|
||||
success: resolve,
|
||||
fail: (error) => reject(networkError(error))
|
||||
})
|
||||
})
|
||||
}
|
||||
|
||||
async function refreshAccessToken() {
|
||||
const observedRefreshToken = session.refreshToken
|
||||
if (!observedRefreshToken) return expireSession(null, observedRefreshToken)
|
||||
if (!refreshPromise) {
|
||||
refreshPromise = (async () => {
|
||||
if (session.refreshToken !== observedRefreshToken && session.token) return session.token
|
||||
try {
|
||||
const tokens = await refreshRequest(observedRefreshToken)
|
||||
if (session.refreshToken !== observedRefreshToken) {
|
||||
if (session.token) return session.token
|
||||
throw sessionExpiredError()
|
||||
}
|
||||
updateTokenPair(tokens)
|
||||
redirectingToLogin = false
|
||||
return session.token
|
||||
} catch (error) {
|
||||
if (isUnauthorized(error)) return expireSession(error, observedRefreshToken)
|
||||
throw error
|
||||
}
|
||||
})().finally(() => { refreshPromise = null })
|
||||
}
|
||||
return refreshPromise
|
||||
}
|
||||
|
||||
async function validAccessToken() {
|
||||
if (!session.token || isAccessTokenExpiring(session.token)) return refreshAccessToken()
|
||||
return session.token
|
||||
}
|
||||
|
||||
export function ensureAccessToken() {
|
||||
return validAccessToken()
|
||||
}
|
||||
|
||||
async function runAuthenticated(execute) {
|
||||
requireOnline()
|
||||
let accessToken = await validAccessToken()
|
||||
let response = await execute(accessToken)
|
||||
if (!isUnauthorized(response)) return response
|
||||
accessToken = session.token && session.token !== accessToken ? session.token : await refreshAccessToken()
|
||||
const activeRefreshToken = session.refreshToken
|
||||
response = await execute(accessToken)
|
||||
if (isUnauthorized(response)) expireSession(response, activeRefreshToken)
|
||||
return response
|
||||
}
|
||||
|
||||
export function apiRequest(options) {
|
||||
return runAuthenticated((accessToken) => rawRequest(options, accessToken)).then((response) => {
|
||||
const body = response.data || {}
|
||||
const code = responseCode(response)
|
||||
if (code !== '200') {
|
||||
const error = new Error(body.message || '服务端请求失败')
|
||||
error.code = body.code || response.statusCode
|
||||
error.statusCode = response.statusCode
|
||||
error.requestId = body.requestId || response.header?.['X-Request-Id'] || response.header?.['x-request-id']
|
||||
error.traceId = body.traceId || response.header?.['X-Trace-Id'] || response.header?.['x-trace-id']
|
||||
throw error
|
||||
}
|
||||
return body.data
|
||||
})
|
||||
}
|
||||
|
||||
export function apiDownload(options) {
|
||||
return runAuthenticated((accessToken) => new Promise((resolve, reject) => {
|
||||
const headers = { ...(options.header || {}) }
|
||||
if (accessToken) headers.Authorization = `Bearer ${accessToken}`
|
||||
if (session.requestId) headers['X-Request-Id'] = session.requestId
|
||||
uni.downloadFile({ ...options, header: headers, success: resolve, fail: (error) => reject(networkError(error, '文件下载失败')) })
|
||||
}))
|
||||
}
|
||||
|
||||
export function apiUpload(options, onProgress) {
|
||||
return runAuthenticated((accessToken) => new Promise((resolve, reject) => {
|
||||
const headers = { ...(options.header || {}) }
|
||||
if (accessToken) headers.Authorization = `Bearer ${accessToken}`
|
||||
if (session.requestId) headers['X-Request-Id'] = session.requestId
|
||||
const task = uni.uploadFile({ ...options, header: headers, success: resolve, fail: (error) => reject(networkError(error)) })
|
||||
if (task && typeof task.onProgressUpdate === 'function' && typeof onProgress === 'function') task.onProgressUpdate(({ progress }) => onProgress(progress))
|
||||
}))
|
||||
}
|
||||
|
||||
export { isUnauthorized }
|
||||
Reference in New Issue
Block a user