forked from nikhilb2/simple_invoicing
-
Notifications
You must be signed in to change notification settings - Fork 0
Expand file tree
/
Copy pathclient.ts
More file actions
102 lines (82 loc) · 2.76 KB
/
client.ts
File metadata and controls
102 lines (82 loc) · 2.76 KB
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
import axios, { InternalAxiosRequestConfig } from 'axios';
const baseURL = import.meta.env.VITE_API_BASE_URL || '/api';
const api = axios.create({
baseURL,
timeout: 10000,
});
let refreshPromise: Promise<string | null> | null = null;
type RetryableRequestConfig = InternalAxiosRequestConfig & {
_retry?: boolean;
};
api.interceptors.request.use((config) => {
const token = localStorage.getItem('token');
if (token) {
config.headers.Authorization = `Bearer ${token}`;
}
return config;
});
api.interceptors.response.use(
(response) => response,
async (error) => {
if (!axios.isAxiosError(error)) {
return Promise.reject(error);
}
const status = error.response?.status;
const originalRequest = error.config as RetryableRequestConfig | undefined;
const requestUrl = originalRequest?.url || '';
const isRefreshCall = requestUrl.includes('/auth/refresh');
if (status !== 401 || !originalRequest || originalRequest._retry || isRefreshCall) {
return Promise.reject(error);
}
const refreshToken = localStorage.getItem('refresh_token');
if (!refreshToken) {
localStorage.removeItem('token');
return Promise.reject(error);
}
originalRequest._retry = true;
if (!refreshPromise) {
refreshPromise = axios
.post(`${baseURL}/auth/refresh`, { refresh_token: refreshToken })
.then((res) => {
const nextAccessToken = res.data?.access_token as string | undefined;
const nextRefreshToken = res.data?.refresh_token as string | undefined;
if (!nextAccessToken || !nextRefreshToken) {
throw new Error('Invalid refresh response');
}
localStorage.setItem('token', nextAccessToken);
localStorage.setItem('refresh_token', nextRefreshToken);
return nextAccessToken;
})
.catch(() => {
localStorage.removeItem('token');
localStorage.removeItem('refresh_token');
return null;
})
.finally(() => {
refreshPromise = null;
});
}
const newToken = await refreshPromise;
if (!newToken) {
return Promise.reject(error);
}
if (!originalRequest.headers) {
originalRequest.headers = {} as InternalAxiosRequestConfig['headers'];
}
(originalRequest.headers as Record<string, string>).Authorization = `Bearer ${newToken}`;
return api.request(originalRequest);
}
);
export function getApiErrorMessage(error: unknown, fallback = 'Something went wrong') {
if (axios.isAxiosError(error)) {
const detail = error.response?.data?.detail;
if (typeof detail === 'string') {
return detail;
}
}
if (error instanceof Error && error.message) {
return error.message;
}
return fallback;
}
export default api;