fix: harden bot accounts and download handling

This commit is contained in:
ShukeBta
2026-06-07 18:30:27 +08:00
parent 7e37126f7c
commit df02fd1166
26 changed files with 1373 additions and 136 deletions
+32 -8
View File
@@ -12,19 +12,31 @@ export const api = axios.create({
// Flag to prevent multiple simultaneous refresh attempts
let isRefreshing = false
let refreshSubscribers: Array<(token: string) => void> = []
let refreshSubscribers: Array<{
resolve: (token: string) => void
reject: (error: unknown) => void
}> = []
// Subscribe to token refresh
function subscribeTokenRefresh(callback: (token: string) => void) {
refreshSubscribers.push(callback)
function subscribeTokenRefresh(resolve: (token: string) => void, reject: (error: unknown) => void) {
refreshSubscribers.push({ resolve, reject })
}
// Notify all subscribers about new token
function onTokenRefreshed(newToken: string) {
refreshSubscribers.forEach(callback => callback(newToken))
refreshSubscribers.forEach((subscriber) => subscriber.resolve(newToken))
refreshSubscribers = []
}
function onTokenRefreshFailed(error: unknown) {
refreshSubscribers.forEach((subscriber) => subscriber.reject(error))
refreshSubscribers = []
}
function isRefreshRequest(config?: InternalAxiosRequestConfig | null): boolean {
return Boolean(config?.url?.includes('/auth/refresh'))
}
// Add auth token to requests
api.interceptors.request.use((config) => {
const token = useAuthStore.getState().token
@@ -51,16 +63,21 @@ api.interceptors.response.use(
const originalRequest = err.config as InternalAxiosRequestConfig & { _retry?: boolean }
// If 401 and not already retried
if (err.response?.status === 401 && originalRequest && !originalRequest._retry) {
if (
err.response?.status === 401 &&
originalRequest &&
!originalRequest._retry &&
!isRefreshRequest(originalRequest)
) {
if (isRefreshing) {
// Wait for token refresh to complete
return new Promise((resolve) => {
return new Promise((resolve, reject) => {
subscribeTokenRefresh((token: string) => {
if (originalRequest.headers) {
originalRequest.headers.Authorization = `Bearer ${token}`
}
resolve(api(originalRequest))
})
}, reject)
})
}
@@ -80,10 +97,17 @@ api.interceptors.response.use(
}
} catch (refreshError) {
isRefreshing = false
refreshSubscribers = []
onTokenRefreshFailed(refreshError)
useAuthStore.getState().logout()
if (typeof window !== 'undefined' && window.location.pathname !== '/login') {
window.location.href = '/login'
}
return Promise.reject(refreshError)
}
// Refresh failed, logout
isRefreshing = false
onTokenRefreshFailed(err)
useAuthStore.getState().logout()
if (typeof window !== 'undefined' && window.location.pathname !== '/login') {
window.location.href = '/login'