Improve the inventory page
This commit is contained in:
@@ -23,6 +23,13 @@ class ApiClient {
|
||||
private baseURL: string;
|
||||
private authToken: string | null = null;
|
||||
private tenantId: string | null = null;
|
||||
private refreshToken: string | null = null;
|
||||
private isRefreshing: boolean = false;
|
||||
private failedQueue: Array<{
|
||||
resolve: (value?: any) => void;
|
||||
reject: (error?: any) => void;
|
||||
config: AxiosRequestConfig;
|
||||
}> = [];
|
||||
|
||||
constructor(baseURL: string = import.meta.env.VITE_API_BASE_URL || 'http://localhost:8000/api/v1') {
|
||||
this.baseURL = baseURL;
|
||||
@@ -57,10 +64,58 @@ class ApiClient {
|
||||
}
|
||||
);
|
||||
|
||||
// Response interceptor for error handling
|
||||
// Response interceptor for error handling and automatic token refresh
|
||||
this.client.interceptors.response.use(
|
||||
(response) => response,
|
||||
(error) => {
|
||||
async (error) => {
|
||||
const originalRequest = error.config;
|
||||
|
||||
// Check if error is 401 and we have a refresh token
|
||||
if (error.response?.status === 401 && this.refreshToken && !originalRequest._retry) {
|
||||
if (this.isRefreshing) {
|
||||
// If already refreshing, queue this request
|
||||
return new Promise((resolve, reject) => {
|
||||
this.failedQueue.push({ resolve, reject, config: originalRequest });
|
||||
});
|
||||
}
|
||||
|
||||
originalRequest._retry = true;
|
||||
this.isRefreshing = true;
|
||||
|
||||
try {
|
||||
// Attempt to refresh the token
|
||||
const response = await this.client.post('/auth/refresh', {
|
||||
refresh_token: this.refreshToken
|
||||
});
|
||||
|
||||
const { access_token, refresh_token } = response.data;
|
||||
|
||||
// Update tokens
|
||||
this.setAuthToken(access_token);
|
||||
if (refresh_token) {
|
||||
this.setRefreshToken(refresh_token);
|
||||
}
|
||||
|
||||
// Update auth store if available
|
||||
await this.updateAuthStore(access_token, refresh_token);
|
||||
|
||||
// Process failed queue
|
||||
this.processQueue(null, access_token);
|
||||
|
||||
// Retry original request with new token
|
||||
originalRequest.headers.Authorization = `Bearer ${access_token}`;
|
||||
return this.client(originalRequest);
|
||||
|
||||
} catch (refreshError) {
|
||||
// Refresh failed, clear tokens and redirect to login
|
||||
this.processQueue(refreshError, null);
|
||||
await this.handleAuthFailure();
|
||||
return Promise.reject(this.handleError(refreshError as AxiosError));
|
||||
} finally {
|
||||
this.isRefreshing = false;
|
||||
}
|
||||
}
|
||||
|
||||
return Promise.reject(this.handleError(error));
|
||||
}
|
||||
);
|
||||
@@ -90,11 +145,69 @@ class ApiClient {
|
||||
}
|
||||
}
|
||||
|
||||
private processQueue(error: any, token: string | null = null) {
|
||||
this.failedQueue.forEach(({ resolve, reject, config }) => {
|
||||
if (error) {
|
||||
reject(error);
|
||||
} else {
|
||||
if (token) {
|
||||
config.headers = config.headers || {};
|
||||
config.headers.Authorization = `Bearer ${token}`;
|
||||
}
|
||||
resolve(this.client(config));
|
||||
}
|
||||
});
|
||||
|
||||
this.failedQueue = [];
|
||||
}
|
||||
|
||||
private async updateAuthStore(accessToken: string, refreshToken?: string) {
|
||||
try {
|
||||
// Dynamically import to avoid circular dependency
|
||||
const { useAuthStore } = await import('../../stores/auth.store');
|
||||
const store = useAuthStore.getState();
|
||||
|
||||
// Update the store with new tokens
|
||||
store.token = accessToken;
|
||||
if (refreshToken) {
|
||||
store.refreshToken = refreshToken;
|
||||
}
|
||||
} catch (error) {
|
||||
console.warn('Failed to update auth store:', error);
|
||||
}
|
||||
}
|
||||
|
||||
private async handleAuthFailure() {
|
||||
try {
|
||||
// Clear tokens
|
||||
this.setAuthToken(null);
|
||||
this.setRefreshToken(null);
|
||||
|
||||
// Dynamically import to avoid circular dependency
|
||||
const { useAuthStore } = await import('../../stores/auth.store');
|
||||
const store = useAuthStore.getState();
|
||||
|
||||
// Logout user
|
||||
store.logout();
|
||||
|
||||
// Redirect to login if not already there
|
||||
if (typeof window !== 'undefined' && !window.location.pathname.includes('/login')) {
|
||||
window.location.href = '/login';
|
||||
}
|
||||
} catch (error) {
|
||||
console.warn('Failed to handle auth failure:', error);
|
||||
}
|
||||
}
|
||||
|
||||
// Configuration methods
|
||||
setAuthToken(token: string | null) {
|
||||
this.authToken = token;
|
||||
}
|
||||
|
||||
setRefreshToken(token: string | null) {
|
||||
this.refreshToken = token;
|
||||
}
|
||||
|
||||
setTenantId(tenantId: string | null) {
|
||||
this.tenantId = tenantId;
|
||||
}
|
||||
@@ -103,6 +216,10 @@ class ApiClient {
|
||||
return this.authToken;
|
||||
}
|
||||
|
||||
getRefreshToken(): string | null {
|
||||
return this.refreshToken;
|
||||
}
|
||||
|
||||
getTenantId(): string | null {
|
||||
return this.tenantId;
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user