11import axios from 'axios' ;
22import { getUserStore , globalErrorStore , loaderStore } from '$lib/helpers/store.js' ;
33import { renewToken } from '$lib/services/auth-service' ;
4+ import { delay } from './utils/common' ;
45
5- // Refresh handling state
6- let isRefreshing = false ;
76
8- /** @type {{config: import('axios').InternalAxiosRequestConfig, resolve: (value:any)=> void, reject: (reason?: any)=> void}[] } */
9- const failedQueue = [ ] ;
7+ /** @type {{config: import('axios').InternalAxiosRequestConfig, resolve: (value: any) => void, reject: (reason?: any) => void}[] } */
8+ const retryQueue = [ ] ;
109
11- /** @type { {config: import('axios').InternalAxiosRequestConfig, resolve: (value:any)=>void, reject: (reason?: any)=>void}[] } */
12- const pendingRequestQueue = [ ] ;
10+ // Refresh handling state
11+ let isRefreshingToken = false ;
1312
1413/**
1514 * Wrap renewToken into a Promise that resolves with the new access token string
@@ -22,76 +21,17 @@ function refreshAccessToken(token) {
2221 } ) ;
2322}
2423
25- /**
26- * Retry queued requests sequentially with the provided token
27- * @param {string } newToken
28- * @returns {Promise<void> }
29- */
30- function processQueueSequentially ( newToken ) {
31- let chain = Promise . resolve ( ) ;
32- while ( failedQueue . length ) {
33- const item = failedQueue . shift ( ) ;
34- if ( ! item ) continue ;
35- const { config, resolve, reject } = item ;
36- // Ensure headers exists; Axios may use AxiosHeaders type
37- // @ts -ignore
38- config . headers = config . headers || { } ;
39- // @ts -ignore
40- config . headers . Authorization = `Bearer ${ newToken } ` ;
41- chain = chain . then ( ( ) => axios ( config ) . then ( resolve ) . catch ( reject ) ) ;
42- }
43- return chain ;
44- }
4524
4625// Add a request interceptor to attach authentication tokens or headers
4726axios . interceptors . request . use (
4827 ( config ) => {
28+ // Add your authentication logic here
4929 const user = getUserStore ( ) ;
5030 if ( ! skipLoader ( config ) ) {
5131 loaderStore . set ( true ) ;
5232 }
53-
54- // Proactive token refresh: if expired or a refresh is in progress,
55- // queue this request until a new token is available
56- if ( isTokenExired ( ) || isRefreshing ) {
57- return new Promise ( ( resolve , reject ) => {
58- pendingRequestQueue . push ( { config, resolve, reject } ) ;
59-
60- if ( ! isRefreshing ) {
61- isRefreshing = true ;
62- refreshAccessToken ( user ?. token || '' )
63- . then ( ( newToken ) => {
64- isRefreshing = false ;
65- // Release queued requests with the new token
66- while ( pendingRequestQueue . length ) {
67- const item = pendingRequestQueue . shift ( ) ;
68- if ( ! item ) continue ;
69- const { config : cfg , resolve : res } = item ;
70- // @ts -ignore
71- cfg . headers = cfg . headers || { } ;
72- // @ts -ignore
73- cfg . headers . Authorization = `Bearer ${ newToken } ` ;
74- res ( cfg ) ;
75- }
76- } )
77- . catch ( ( err ) => {
78- isRefreshing = false ;
79- // Reject queued requests and redirect to login
80- while ( pendingRequestQueue . length ) {
81- const item = pendingRequestQueue . shift ( ) ;
82- if ( item ) item . reject ( err ) ;
83- }
84- redirectToLogin ( ) ;
85- } ) ;
86- }
87- } ) ;
88- }
89-
90- // Attach current token if present
33+ // Attach an authentication token to the request headers
9134 if ( user . token ) {
92- // @ts -ignore
93- config . headers = config . headers || { } ;
94- // @ts -ignore
9535 config . headers . Authorization = `Bearer ${ user . token } ` ;
9636 }
9737 return config ;
@@ -105,46 +45,17 @@ axios.interceptors.request.use(
10545// Add a response interceptor to handle 401 errors globally
10646axios . interceptors . response . use (
10747 ( response ) => {
108- // If the request was successful, return the response
10948 loaderStore . set ( false ) ;
11049 return response ;
11150 } ,
11251 ( error ) => {
11352 loaderStore . set ( false ) ;
11453 const originalRequest = error ?. config ;
11554
116- // If token expired or 401 returned, attempt a single token refresh and
117- // retry failed requests in sequence.
118- if ( ( error ?. response ?. status === 401 || isTokenExired ( ) ) && originalRequest && ! originalRequest . _retry ) {
119- originalRequest . _retry = true ;
120-
55+ // If token expired or 401 returned, attempt a single token refresh and retry requests in queue.
56+ if ( ( error ?. response ?. status === 401 || isTokenExired ( ) ) && originalRequest ) {
12157 return new Promise ( ( resolve , reject ) => {
122- // Push the current request into the queue
123- failedQueue . push ( { config : originalRequest , resolve, reject } ) ;
124-
125- // Start refresh if not already in progress
126- if ( ! isRefreshing ) {
127- isRefreshing = true ;
128- const user = getUserStore ( ) ;
129-
130- refreshAccessToken ( user ?. token || '' )
131- . then ( ( newToken ) => {
132- isRefreshing = false ;
133- return processQueueSequentially ( newToken ) ;
134- } )
135- . catch ( ( err ) => {
136- isRefreshing = false ;
137-
138- // Reject all queued requests
139- while ( failedQueue . length ) {
140- const item = failedQueue . shift ( ) ;
141- if ( item ) item . reject ( err ) ;
142- }
143-
144- redirectToLogin ( ) ;
145- throw err ;
146- } ) ;
147- }
58+ enqueue ( { config : originalRequest , resolve, reject } ) ;
14859 } ) ;
14960 } else if ( ! skipGlobalError ( originalRequest ) ) {
15061 globalErrorStore . set ( true ) ;
@@ -158,6 +69,59 @@ axios.interceptors.response.use(
15869 }
15970) ;
16071
72+ /**
73+ * @param {{config: import('axios').InternalAxiosRequestConfig, resolve: (value: any) => void, reject: (reason?: any) => void} } retryItem
74+ */
75+ function enqueue ( retryItem ) {
76+ // Push the current request into the queue
77+ retryQueue . push ( { ...retryItem } ) ;
78+
79+ // Start refresh token
80+ if ( ! isRefreshingToken ) {
81+ isRefreshingToken = true ;
82+ const user = getUserStore ( ) ;
83+
84+ refreshAccessToken ( user ?. token || '' )
85+ . then ( ( newToken ) => {
86+ const promise = dequeue ( newToken ) ;
87+ isRefreshingToken = false ;
88+ return promise ;
89+ } )
90+ . catch ( ( err ) => {
91+ // Reject all queued requests
92+ while ( retryQueue . length > 0 ) {
93+ const item = retryQueue . shift ( ) ;
94+ if ( item ) {
95+ item . reject ( err ) ;
96+ }
97+ }
98+ isRefreshingToken = false ;
99+ redirectToLogin ( ) ;
100+ } ) ;
101+ }
102+ }
103+
104+ /**
105+ * Retry queued requests sequentially
106+ * @param {string } newToken
107+ * @returns {Promise<void> }
108+ */
109+ function dequeue ( newToken ) {
110+ let chain = Promise . resolve ( ) ;
111+ while ( retryQueue . length > 0 ) {
112+ const item = retryQueue . shift ( ) ;
113+ if ( ! item ) continue ;
114+ const { config, resolve, reject } = item ;
115+ // @ts -ignore
116+ config . headers = config . headers || { } ;
117+ // @ts -ignore
118+ config . headers . Authorization = `Bearer ${ newToken } ` ;
119+ chain = chain . then ( ( ) => delay ( 20 ) )
120+ . then ( ( ) => { axios ( config ) . then ( resolve ) . catch ( reject ) ; } ) ;
121+ }
122+ return chain ;
123+ }
124+
161125function isTokenExired ( ) {
162126 const user = getUserStore ( ) ;
163127 return Date . now ( ) / 1000 > user . expires ;
0 commit comments