96 lines
2.7 KiB
JavaScript
96 lines
2.7 KiB
JavaScript
import axios from 'axios'
|
||
import router from '@/router'
|
||
|
||
const BASE_URL = import.meta.env.VITE_API_BASE_URL
|
||
let refreshPromise = null
|
||
|
||
async function refreshAccessToken() {
|
||
if (refreshPromise) return refreshPromise
|
||
const refreshToken = localStorage.getItem('refresh_token')
|
||
if (!refreshToken) throw new Error('missing_refresh_token')
|
||
|
||
refreshPromise = axios
|
||
.post(`${BASE_URL}/auth/refresh`, { refresh_token: refreshToken })
|
||
.then((res) => {
|
||
const nextAccessToken = res.data?.access_token
|
||
const nextRefreshToken = res.data?.refresh_token || refreshToken
|
||
if (!nextAccessToken) {
|
||
throw new Error('missing_access_token')
|
||
}
|
||
localStorage.setItem('access_token', nextAccessToken)
|
||
localStorage.setItem('refresh_token', nextRefreshToken)
|
||
return nextAccessToken
|
||
})
|
||
.finally(() => {
|
||
refreshPromise = null
|
||
})
|
||
|
||
return refreshPromise
|
||
}
|
||
|
||
function hardLogoutToLogin() {
|
||
localStorage.removeItem('access_token')
|
||
localStorage.removeItem('refresh_token')
|
||
router.push('/login')
|
||
}
|
||
|
||
// 使用者 API:帶 Bearer token
|
||
export const userHttp = axios.create({ baseURL: BASE_URL })
|
||
|
||
userHttp.interceptors.request.use(config => {
|
||
const token = localStorage.getItem('access_token')
|
||
if (token) {
|
||
config.headers['Authorization'] = `Bearer ${token}`
|
||
}
|
||
return config
|
||
})
|
||
|
||
userHttp.interceptors.response.use(
|
||
res => res,
|
||
async err => {
|
||
const original = err.config || {}
|
||
if (err.response?.status === 401 && !original._retriedByRefresh) {
|
||
original._retriedByRefresh = true
|
||
try {
|
||
const nextToken = await refreshAccessToken()
|
||
original.headers = original.headers || {}
|
||
original.headers['Authorization'] = `Bearer ${nextToken}`
|
||
return userHttp.request(original)
|
||
} catch (_refreshErr) {
|
||
hardLogoutToLogin()
|
||
}
|
||
}
|
||
return Promise.reject(err)
|
||
}
|
||
)
|
||
|
||
// 管理員 API:只帶 Bearer token(後端再檢查 admin 群組)
|
||
export const adminHttp = axios.create({ baseURL: BASE_URL })
|
||
|
||
adminHttp.interceptors.request.use(config => {
|
||
const token = localStorage.getItem('access_token')
|
||
if (token) {
|
||
config.headers['Authorization'] = `Bearer ${token}`
|
||
}
|
||
return config
|
||
})
|
||
|
||
adminHttp.interceptors.response.use(
|
||
res => res,
|
||
async err => {
|
||
const original = err.config || {}
|
||
if (err.response?.status === 401 && !original._retriedByRefresh) {
|
||
original._retriedByRefresh = true
|
||
try {
|
||
const nextToken = await refreshAccessToken()
|
||
original.headers = original.headers || {}
|
||
original.headers['Authorization'] = `Bearer ${nextToken}`
|
||
return adminHttp.request(original)
|
||
} catch (_refreshErr) {
|
||
hardLogoutToLogin()
|
||
}
|
||
}
|
||
return Promise.reject(err)
|
||
}
|
||
)
|