mbt-api-client 1.0.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/dist/index.cjs +561 -0
- package/dist/index.cjs.map +1 -0
- package/dist/index.d.cts +879 -0
- package/dist/index.d.ts +879 -0
- package/dist/index.js +519 -0
- package/dist/index.js.map +1 -0
- package/package.json +59 -0
package/dist/index.cjs
ADDED
|
@@ -0,0 +1,561 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
var __create = Object.create;
|
|
3
|
+
var __defProp = Object.defineProperty;
|
|
4
|
+
var __getOwnPropDesc = Object.getOwnPropertyDescriptor;
|
|
5
|
+
var __getOwnPropNames = Object.getOwnPropertyNames;
|
|
6
|
+
var __getProtoOf = Object.getPrototypeOf;
|
|
7
|
+
var __hasOwnProp = Object.prototype.hasOwnProperty;
|
|
8
|
+
var __export = (target, all) => {
|
|
9
|
+
for (var name in all)
|
|
10
|
+
__defProp(target, name, { get: all[name], enumerable: true });
|
|
11
|
+
};
|
|
12
|
+
var __copyProps = (to, from, except, desc) => {
|
|
13
|
+
if (from && typeof from === "object" || typeof from === "function") {
|
|
14
|
+
for (let key of __getOwnPropNames(from))
|
|
15
|
+
if (!__hasOwnProp.call(to, key) && key !== except)
|
|
16
|
+
__defProp(to, key, { get: () => from[key], enumerable: !(desc = __getOwnPropDesc(from, key)) || desc.enumerable });
|
|
17
|
+
}
|
|
18
|
+
return to;
|
|
19
|
+
};
|
|
20
|
+
var __toESM = (mod, isNodeMode, target) => (target = mod != null ? __create(__getProtoOf(mod)) : {}, __copyProps(
|
|
21
|
+
// If the importer is in node compatibility mode or this is not an ESM
|
|
22
|
+
// file that has been converted to a CommonJS file using a Babel-
|
|
23
|
+
// compatible transform (i.e. "__esModule" has not been set), then set
|
|
24
|
+
// "default" to the CommonJS "module.exports" for node compatibility.
|
|
25
|
+
isNodeMode || !mod || !mod.__esModule ? __defProp(target, "default", { value: mod, enumerable: true }) : target,
|
|
26
|
+
mod
|
|
27
|
+
));
|
|
28
|
+
var __toCommonJS = (mod) => __copyProps(__defProp({}, "__esModule", { value: true }), mod);
|
|
29
|
+
|
|
30
|
+
// src/index.ts
|
|
31
|
+
var index_exports = {};
|
|
32
|
+
__export(index_exports, {
|
|
33
|
+
ApiClientContext: () => ApiClientContext,
|
|
34
|
+
ApiClientProvider: () => ApiClientProvider,
|
|
35
|
+
TokenRefreshManager: () => TokenRefreshManager,
|
|
36
|
+
createApiClient: () => createApiClient,
|
|
37
|
+
createTokenRefreshManager: () => createTokenRefreshManager,
|
|
38
|
+
useApiClient: () => useApiClient
|
|
39
|
+
});
|
|
40
|
+
module.exports = __toCommonJS(index_exports);
|
|
41
|
+
|
|
42
|
+
// src/api-client.ts
|
|
43
|
+
var import_axios = __toESM(require("axios"), 1);
|
|
44
|
+
var import_react = require("react");
|
|
45
|
+
|
|
46
|
+
// src/token-refresh.ts
|
|
47
|
+
var defaultGetAccessToken = () => localStorage.getItem("accessToken");
|
|
48
|
+
var defaultGetRefreshToken = () => localStorage.getItem("refreshToken");
|
|
49
|
+
var defaultSetTokens = (accessToken, refreshToken) => {
|
|
50
|
+
localStorage.setItem("accessToken", accessToken);
|
|
51
|
+
if (refreshToken) {
|
|
52
|
+
localStorage.setItem("refreshToken", refreshToken);
|
|
53
|
+
}
|
|
54
|
+
};
|
|
55
|
+
var defaultClearTokens = () => {
|
|
56
|
+
localStorage.removeItem("accessToken");
|
|
57
|
+
localStorage.removeItem("refreshToken");
|
|
58
|
+
};
|
|
59
|
+
var defaultNavigateToLogin = () => {
|
|
60
|
+
defaultClearTokens();
|
|
61
|
+
window.location.href = "#/login";
|
|
62
|
+
};
|
|
63
|
+
var TokenRefreshManager = class {
|
|
64
|
+
/**
|
|
65
|
+
* Создаёт новый экземпляр TokenRefreshManager
|
|
66
|
+
*
|
|
67
|
+
* @param config - Конфигурация менеджера
|
|
68
|
+
*
|
|
69
|
+
* @example
|
|
70
|
+
* ```typescript
|
|
71
|
+
* const manager = new TokenRefreshManager({
|
|
72
|
+
* refreshTokenFn: myRefreshFunction,
|
|
73
|
+
* onNavigateToLogin: () => router.push('/login'),
|
|
74
|
+
* });
|
|
75
|
+
* ```
|
|
76
|
+
*/
|
|
77
|
+
constructor(config) {
|
|
78
|
+
this.isRefreshing = false;
|
|
79
|
+
this.refreshPromise = null;
|
|
80
|
+
this.queuedRequests = [];
|
|
81
|
+
this.config = {
|
|
82
|
+
refreshTokenFn: config.refreshTokenFn,
|
|
83
|
+
onNavigateToLogin: config.onNavigateToLogin ?? defaultNavigateToLogin,
|
|
84
|
+
getAccessToken: config.getAccessToken ?? defaultGetAccessToken,
|
|
85
|
+
getRefreshToken: config.getRefreshToken ?? defaultGetRefreshToken,
|
|
86
|
+
setTokens: config.setTokens ?? defaultSetTokens,
|
|
87
|
+
clearTokens: config.clearTokens ?? defaultClearTokens
|
|
88
|
+
};
|
|
89
|
+
}
|
|
90
|
+
/**
|
|
91
|
+
* Получает текущий access токен
|
|
92
|
+
*
|
|
93
|
+
* @returns Access токен или null, если токен отсутствует
|
|
94
|
+
*
|
|
95
|
+
* @example
|
|
96
|
+
* ```typescript
|
|
97
|
+
* const token = manager.getAccessToken();
|
|
98
|
+
* if (token) {
|
|
99
|
+
* headers.Authorization = `Bearer ${token}`;
|
|
100
|
+
* }
|
|
101
|
+
* ```
|
|
102
|
+
*/
|
|
103
|
+
getAccessToken() {
|
|
104
|
+
return this.config.getAccessToken();
|
|
105
|
+
}
|
|
106
|
+
/**
|
|
107
|
+
* Получает текущий refresh токен
|
|
108
|
+
*
|
|
109
|
+
* @returns Refresh токен или null, если токен отсутствует
|
|
110
|
+
*/
|
|
111
|
+
getRefreshToken() {
|
|
112
|
+
return this.config.getRefreshToken();
|
|
113
|
+
}
|
|
114
|
+
/**
|
|
115
|
+
* Проверяет, идёт ли в данный момент процесс обновления токена
|
|
116
|
+
*
|
|
117
|
+
* Используйте для определения, нужно ли ставить запрос в очередь
|
|
118
|
+
* или инициировать новое обновление.
|
|
119
|
+
*
|
|
120
|
+
* @returns `true` если обновление в процессе, `false` если нет
|
|
121
|
+
*
|
|
122
|
+
* @example
|
|
123
|
+
* ```typescript
|
|
124
|
+
* if (manager.isRefreshInProgress()) {
|
|
125
|
+
* // Ждём завершения текущего обновления
|
|
126
|
+
* const newToken = await manager.waitForTokenRefresh();
|
|
127
|
+
* } else {
|
|
128
|
+
* // Инициируем новое обновление
|
|
129
|
+
* await manager.refreshToken();
|
|
130
|
+
* }
|
|
131
|
+
* ```
|
|
132
|
+
*/
|
|
133
|
+
isRefreshInProgress() {
|
|
134
|
+
return this.isRefreshing;
|
|
135
|
+
}
|
|
136
|
+
/**
|
|
137
|
+
* Добавляет запрос в очередь ожидания обновления токена
|
|
138
|
+
*
|
|
139
|
+
* Возвращает промис, который разрешится с новым токеном после
|
|
140
|
+
* успешного обновления или будет отклонён при ошибке.
|
|
141
|
+
*
|
|
142
|
+
* @returns Промис с новым access токеном
|
|
143
|
+
*
|
|
144
|
+
* @example
|
|
145
|
+
* ```typescript
|
|
146
|
+
* // В response interceptor при множественных 401
|
|
147
|
+
* if (manager.isRefreshInProgress()) {
|
|
148
|
+
* try {
|
|
149
|
+
* const newToken = await manager.waitForTokenRefresh();
|
|
150
|
+
* // Повторить запрос с новым токеном
|
|
151
|
+
* } catch {
|
|
152
|
+
* // Обновление не удалось
|
|
153
|
+
* }
|
|
154
|
+
* }
|
|
155
|
+
* ```
|
|
156
|
+
*/
|
|
157
|
+
waitForTokenRefresh() {
|
|
158
|
+
return new Promise((resolve, reject) => {
|
|
159
|
+
this.queuedRequests.push({ resolve, reject });
|
|
160
|
+
});
|
|
161
|
+
}
|
|
162
|
+
/**
|
|
163
|
+
* Выполняет обновление токена
|
|
164
|
+
*
|
|
165
|
+
* Если обновление уже в процессе, вернёт существующий промис.
|
|
166
|
+
* После обновления уведомляет все запросы в очереди.
|
|
167
|
+
*
|
|
168
|
+
* @returns Промис с результатом обновления
|
|
169
|
+
*
|
|
170
|
+
* @example
|
|
171
|
+
* ```typescript
|
|
172
|
+
* const result = await manager.refreshToken();
|
|
173
|
+
* if (result.ok) {
|
|
174
|
+
* console.log('Новый токен:', result.accessToken);
|
|
175
|
+
* } else {
|
|
176
|
+
* console.log('Ошибка обновления токена');
|
|
177
|
+
* }
|
|
178
|
+
* ```
|
|
179
|
+
*/
|
|
180
|
+
async refreshToken() {
|
|
181
|
+
if (this.refreshPromise) {
|
|
182
|
+
return this.refreshPromise;
|
|
183
|
+
}
|
|
184
|
+
this.isRefreshing = true;
|
|
185
|
+
this.refreshPromise = this.performRefresh().finally(() => {
|
|
186
|
+
setTimeout(() => {
|
|
187
|
+
this.refreshPromise = null;
|
|
188
|
+
}, 100);
|
|
189
|
+
});
|
|
190
|
+
return this.refreshPromise;
|
|
191
|
+
}
|
|
192
|
+
/**
|
|
193
|
+
* Обрабатывает ошибку 401 — либо обновляет токен, либо перенаправляет на логин
|
|
194
|
+
*
|
|
195
|
+
* Основной метод для использования в interceptors. Автоматически:
|
|
196
|
+
* - Перенаправляет на логин при невалидном токене
|
|
197
|
+
* - Ставит запрос в очередь, если обновление уже идёт
|
|
198
|
+
* - Инициирует обновление, если ещё не запущено
|
|
199
|
+
*
|
|
200
|
+
* @param isInvalidToken - Если true, токен считается невалидным и будет выполнен переход на логин
|
|
201
|
+
* @returns Промис с новым access токеном или null при ошибке
|
|
202
|
+
*
|
|
203
|
+
* @example
|
|
204
|
+
* ```typescript
|
|
205
|
+
* // В axios response interceptor
|
|
206
|
+
* if (error.response?.status === 401) {
|
|
207
|
+
* const isInvalid = error.response.data?.message === 'Invalid token';
|
|
208
|
+
* const newToken = await manager.handle401(isInvalid);
|
|
209
|
+
*
|
|
210
|
+
* if (newToken) {
|
|
211
|
+
* // Повторить запрос с новым токеном
|
|
212
|
+
* error.config.headers.Authorization = `Bearer ${newToken}`;
|
|
213
|
+
* return axios(error.config);
|
|
214
|
+
* }
|
|
215
|
+
* // newToken === null означает переход на логин
|
|
216
|
+
* }
|
|
217
|
+
* ```
|
|
218
|
+
*/
|
|
219
|
+
async handle401(isInvalidToken = false) {
|
|
220
|
+
if (isInvalidToken) {
|
|
221
|
+
this.config.onNavigateToLogin();
|
|
222
|
+
return null;
|
|
223
|
+
}
|
|
224
|
+
if (this.isRefreshing) {
|
|
225
|
+
return this.waitForTokenRefresh();
|
|
226
|
+
}
|
|
227
|
+
const result = await this.refreshToken();
|
|
228
|
+
if (!result.ok) {
|
|
229
|
+
this.config.onNavigateToLogin();
|
|
230
|
+
return null;
|
|
231
|
+
}
|
|
232
|
+
return result.accessToken || this.config.getAccessToken();
|
|
233
|
+
}
|
|
234
|
+
/**
|
|
235
|
+
* Принудительно перенаправляет на страницу логина
|
|
236
|
+
*
|
|
237
|
+
* Вызывает колбэк onNavigateToLogin из конфигурации.
|
|
238
|
+
*
|
|
239
|
+
* @example
|
|
240
|
+
* ```typescript
|
|
241
|
+
* // При необходимости принудительного выхода
|
|
242
|
+
* manager.navigateToLogin();
|
|
243
|
+
* ```
|
|
244
|
+
*/
|
|
245
|
+
navigateToLogin() {
|
|
246
|
+
this.config.onNavigateToLogin();
|
|
247
|
+
}
|
|
248
|
+
/**
|
|
249
|
+
* Сбрасывает внутреннее состояние менеджера
|
|
250
|
+
*
|
|
251
|
+
* Полезно для тестирования или при необходимости
|
|
252
|
+
* принудительно сбросить очередь запросов.
|
|
253
|
+
*
|
|
254
|
+
* @example
|
|
255
|
+
* ```typescript
|
|
256
|
+
* // В тестах
|
|
257
|
+
* beforeEach(() => {
|
|
258
|
+
* manager.reset();
|
|
259
|
+
* });
|
|
260
|
+
* ```
|
|
261
|
+
*/
|
|
262
|
+
reset() {
|
|
263
|
+
this.isRefreshing = false;
|
|
264
|
+
this.refreshPromise = null;
|
|
265
|
+
this.queuedRequests = [];
|
|
266
|
+
}
|
|
267
|
+
async performRefresh() {
|
|
268
|
+
try {
|
|
269
|
+
const result = await this.config.refreshTokenFn();
|
|
270
|
+
if (result.ok && result.accessToken) {
|
|
271
|
+
this.config.setTokens(result.accessToken, result.refreshToken);
|
|
272
|
+
this.notifyQueueSuccess(result.accessToken);
|
|
273
|
+
} else {
|
|
274
|
+
this.notifyQueueFailure(new Error("Token refresh failed"));
|
|
275
|
+
}
|
|
276
|
+
this.isRefreshing = false;
|
|
277
|
+
return result;
|
|
278
|
+
} catch (error) {
|
|
279
|
+
this.isRefreshing = false;
|
|
280
|
+
this.notifyQueueFailure(error);
|
|
281
|
+
return { ok: false };
|
|
282
|
+
}
|
|
283
|
+
}
|
|
284
|
+
notifyQueueSuccess(newToken) {
|
|
285
|
+
this.queuedRequests.forEach(({ resolve }) => resolve(newToken));
|
|
286
|
+
this.queuedRequests = [];
|
|
287
|
+
}
|
|
288
|
+
notifyQueueFailure(error) {
|
|
289
|
+
this.queuedRequests.forEach(({ reject }) => reject(error));
|
|
290
|
+
this.queuedRequests = [];
|
|
291
|
+
}
|
|
292
|
+
};
|
|
293
|
+
var createTokenRefreshManager = (config) => {
|
|
294
|
+
return new TokenRefreshManager(config);
|
|
295
|
+
};
|
|
296
|
+
|
|
297
|
+
// src/api-client.ts
|
|
298
|
+
var RETRY_KEY = /* @__PURE__ */ Symbol("retry");
|
|
299
|
+
var defaultNavigateToLogin2 = () => {
|
|
300
|
+
localStorage.removeItem("accessToken");
|
|
301
|
+
localStorage.removeItem("refreshToken");
|
|
302
|
+
window.location.href = "#/login";
|
|
303
|
+
};
|
|
304
|
+
var defaultDecodeJwt = (token) => {
|
|
305
|
+
try {
|
|
306
|
+
const base64Url = token.split(".")[1];
|
|
307
|
+
const base64 = base64Url.replace(/-/g, "+").replace(/_/g, "/");
|
|
308
|
+
const jsonPayload = decodeURIComponent(
|
|
309
|
+
atob(base64).split("").map((c) => "%" + ("00" + c.charCodeAt(0).toString(16)).slice(-2)).join("")
|
|
310
|
+
);
|
|
311
|
+
return JSON.parse(jsonPayload);
|
|
312
|
+
} catch {
|
|
313
|
+
return null;
|
|
314
|
+
}
|
|
315
|
+
};
|
|
316
|
+
var createAxiosInstance = (baseUrl, tokenRefreshManager, onNavigateToLogin, timeout, decodeJwt) => {
|
|
317
|
+
const instance = import_axios.default.create({
|
|
318
|
+
baseURL: baseUrl,
|
|
319
|
+
timeout,
|
|
320
|
+
headers: {
|
|
321
|
+
"Content-Type": "application/json"
|
|
322
|
+
}
|
|
323
|
+
});
|
|
324
|
+
instance.interceptors.request.use(
|
|
325
|
+
(requestConfig) => {
|
|
326
|
+
const isRefreshRequest = requestConfig.url?.includes("auth/refresh-token");
|
|
327
|
+
const accessToken = tokenRefreshManager.getAccessToken();
|
|
328
|
+
const refreshToken = tokenRefreshManager.getRefreshToken();
|
|
329
|
+
const token = isRefreshRequest ? refreshToken : accessToken;
|
|
330
|
+
if (token) {
|
|
331
|
+
Object.assign(requestConfig.headers, {
|
|
332
|
+
Authorization: `Bearer ${token}`
|
|
333
|
+
});
|
|
334
|
+
}
|
|
335
|
+
const decodedToken = decodeJwt(accessToken || "");
|
|
336
|
+
if (decodedToken?.userId) {
|
|
337
|
+
Object.assign(requestConfig.headers, {
|
|
338
|
+
"X-User-Id": decodedToken.userId
|
|
339
|
+
});
|
|
340
|
+
}
|
|
341
|
+
return requestConfig;
|
|
342
|
+
},
|
|
343
|
+
(error) => Promise.reject(error)
|
|
344
|
+
);
|
|
345
|
+
instance.interceptors.response.use(
|
|
346
|
+
(response) => response,
|
|
347
|
+
async (error) => {
|
|
348
|
+
const originalRequest = error.config;
|
|
349
|
+
if (!originalRequest) {
|
|
350
|
+
return Promise.reject(error);
|
|
351
|
+
}
|
|
352
|
+
const isRefreshRequest = originalRequest.url?.includes("auth/refresh-token");
|
|
353
|
+
if (error.response?.status === 401) {
|
|
354
|
+
const responseData = error.response?.data;
|
|
355
|
+
const isInvalidToken = responseData?.message === "Invalid token";
|
|
356
|
+
if (isInvalidToken) {
|
|
357
|
+
onNavigateToLogin();
|
|
358
|
+
return Promise.reject(error);
|
|
359
|
+
}
|
|
360
|
+
if (isRefreshRequest) {
|
|
361
|
+
onNavigateToLogin();
|
|
362
|
+
return Promise.reject(error);
|
|
363
|
+
}
|
|
364
|
+
if (originalRequest[RETRY_KEY]) {
|
|
365
|
+
onNavigateToLogin();
|
|
366
|
+
return Promise.reject(error);
|
|
367
|
+
}
|
|
368
|
+
if (tokenRefreshManager.isRefreshInProgress()) {
|
|
369
|
+
try {
|
|
370
|
+
const newToken = await tokenRefreshManager.waitForTokenRefresh();
|
|
371
|
+
const retryConfig2 = { ...originalRequest };
|
|
372
|
+
Object.assign(retryConfig2.headers, {
|
|
373
|
+
Authorization: `Bearer ${newToken}`
|
|
374
|
+
});
|
|
375
|
+
return instance(retryConfig2);
|
|
376
|
+
} catch {
|
|
377
|
+
return Promise.reject(error);
|
|
378
|
+
}
|
|
379
|
+
}
|
|
380
|
+
const retryConfig = {
|
|
381
|
+
...originalRequest,
|
|
382
|
+
[RETRY_KEY]: true
|
|
383
|
+
};
|
|
384
|
+
try {
|
|
385
|
+
const newToken = await tokenRefreshManager.handle401(false);
|
|
386
|
+
if (!newToken) {
|
|
387
|
+
return Promise.reject(new Error("Failed to refresh token"));
|
|
388
|
+
}
|
|
389
|
+
Object.assign(retryConfig.headers, {
|
|
390
|
+
Authorization: `Bearer ${newToken}`
|
|
391
|
+
});
|
|
392
|
+
return instance(retryConfig);
|
|
393
|
+
} catch (refreshError) {
|
|
394
|
+
onNavigateToLogin();
|
|
395
|
+
return Promise.reject(refreshError);
|
|
396
|
+
}
|
|
397
|
+
}
|
|
398
|
+
if (error.response?.status === 403) {
|
|
399
|
+
onNavigateToLogin();
|
|
400
|
+
return Promise.reject(error);
|
|
401
|
+
}
|
|
402
|
+
if (error.response?.status === 400 && isRefreshRequest) {
|
|
403
|
+
await new Promise((resolve) => {
|
|
404
|
+
setTimeout(resolve, 1e3);
|
|
405
|
+
});
|
|
406
|
+
const newToken = tokenRefreshManager.getAccessToken();
|
|
407
|
+
if (newToken) {
|
|
408
|
+
const retryConfig = { ...originalRequest };
|
|
409
|
+
Object.assign(retryConfig.headers, {
|
|
410
|
+
Authorization: `Bearer ${newToken}`
|
|
411
|
+
});
|
|
412
|
+
return instance(retryConfig);
|
|
413
|
+
}
|
|
414
|
+
onNavigateToLogin();
|
|
415
|
+
return Promise.reject(new Error("Failed to refresh token"));
|
|
416
|
+
}
|
|
417
|
+
return Promise.reject(error);
|
|
418
|
+
}
|
|
419
|
+
);
|
|
420
|
+
return instance;
|
|
421
|
+
};
|
|
422
|
+
var createApiService = (axiosInstance) => {
|
|
423
|
+
return {
|
|
424
|
+
get: (url, config) => {
|
|
425
|
+
const cacheBuster = `${url.includes("?") ? "&" : "?"}_t=${Date.now()}`;
|
|
426
|
+
return axiosInstance.get(url + cacheBuster, config).then((response) => response.data);
|
|
427
|
+
},
|
|
428
|
+
post: (url, data, config) => axiosInstance.post(url, data, config).then((response) => response.data),
|
|
429
|
+
put: (url, data, config) => axiosInstance.put(url, data, config).then((response) => response.data),
|
|
430
|
+
delete: (url, config) => axiosInstance.delete(url, config).then((response) => response.data),
|
|
431
|
+
axiosInstance
|
|
432
|
+
};
|
|
433
|
+
};
|
|
434
|
+
var createApiClient = (config) => {
|
|
435
|
+
const {
|
|
436
|
+
urls,
|
|
437
|
+
onNavigateToLogin = defaultNavigateToLogin2,
|
|
438
|
+
timeout = 1e4,
|
|
439
|
+
decodeJwt = defaultDecodeJwt
|
|
440
|
+
} = config;
|
|
441
|
+
const authAxiosInstance = import_axios.default.create({
|
|
442
|
+
baseURL: urls.authService,
|
|
443
|
+
timeout,
|
|
444
|
+
headers: {
|
|
445
|
+
"Content-Type": "application/json"
|
|
446
|
+
}
|
|
447
|
+
});
|
|
448
|
+
const refreshTokenFn = async () => {
|
|
449
|
+
try {
|
|
450
|
+
const refreshToken = localStorage.getItem("refreshToken");
|
|
451
|
+
if (!refreshToken) {
|
|
452
|
+
return { ok: false };
|
|
453
|
+
}
|
|
454
|
+
const response = await authAxiosInstance.post(
|
|
455
|
+
"/auth/refresh-token",
|
|
456
|
+
{},
|
|
457
|
+
{
|
|
458
|
+
headers: {
|
|
459
|
+
Authorization: `Bearer ${refreshToken}`
|
|
460
|
+
}
|
|
461
|
+
}
|
|
462
|
+
);
|
|
463
|
+
const { accessToken, refreshToken: newRefreshToken } = response.data;
|
|
464
|
+
return {
|
|
465
|
+
ok: true,
|
|
466
|
+
accessToken,
|
|
467
|
+
refreshToken: newRefreshToken
|
|
468
|
+
};
|
|
469
|
+
} catch {
|
|
470
|
+
return { ok: false };
|
|
471
|
+
}
|
|
472
|
+
};
|
|
473
|
+
const tokenRefreshManager = createTokenRefreshManager({
|
|
474
|
+
refreshTokenFn,
|
|
475
|
+
onNavigateToLogin
|
|
476
|
+
});
|
|
477
|
+
authAxiosInstance.interceptors.request.use(
|
|
478
|
+
(requestConfig) => {
|
|
479
|
+
const isRefreshRequest = requestConfig.url?.includes("auth/refresh-token");
|
|
480
|
+
const accessToken = tokenRefreshManager.getAccessToken();
|
|
481
|
+
const refreshToken = tokenRefreshManager.getRefreshToken();
|
|
482
|
+
const token = isRefreshRequest ? refreshToken : accessToken;
|
|
483
|
+
if (token && !requestConfig.headers.Authorization) {
|
|
484
|
+
Object.assign(requestConfig.headers, {
|
|
485
|
+
Authorization: `Bearer ${token}`
|
|
486
|
+
});
|
|
487
|
+
}
|
|
488
|
+
const decodedToken = decodeJwt(accessToken || "");
|
|
489
|
+
if (decodedToken?.userId) {
|
|
490
|
+
Object.assign(requestConfig.headers, {
|
|
491
|
+
"X-User-Id": decodedToken.userId
|
|
492
|
+
});
|
|
493
|
+
}
|
|
494
|
+
return requestConfig;
|
|
495
|
+
},
|
|
496
|
+
(error) => Promise.reject(error)
|
|
497
|
+
);
|
|
498
|
+
const userProgressAxiosInstance = createAxiosInstance(
|
|
499
|
+
urls.userProgressService,
|
|
500
|
+
tokenRefreshManager,
|
|
501
|
+
onNavigateToLogin,
|
|
502
|
+
timeout,
|
|
503
|
+
decodeJwt
|
|
504
|
+
);
|
|
505
|
+
const recommendationAxiosInstance = createAxiosInstance(
|
|
506
|
+
urls.recommendationService,
|
|
507
|
+
tokenRefreshManager,
|
|
508
|
+
onNavigateToLogin,
|
|
509
|
+
timeout,
|
|
510
|
+
decodeJwt
|
|
511
|
+
);
|
|
512
|
+
const partnerManagerAxiosInstance = createAxiosInstance(
|
|
513
|
+
urls.partnerManager,
|
|
514
|
+
tokenRefreshManager,
|
|
515
|
+
onNavigateToLogin,
|
|
516
|
+
timeout,
|
|
517
|
+
decodeJwt
|
|
518
|
+
);
|
|
519
|
+
const puzzleControllerAxiosInstance = createAxiosInstance(
|
|
520
|
+
urls.puzzleController,
|
|
521
|
+
tokenRefreshManager,
|
|
522
|
+
onNavigateToLogin,
|
|
523
|
+
timeout,
|
|
524
|
+
decodeJwt
|
|
525
|
+
);
|
|
526
|
+
const monolithAxiosInstance = createAxiosInstance(
|
|
527
|
+
urls.monolith,
|
|
528
|
+
tokenRefreshManager,
|
|
529
|
+
onNavigateToLogin,
|
|
530
|
+
timeout,
|
|
531
|
+
decodeJwt
|
|
532
|
+
);
|
|
533
|
+
return {
|
|
534
|
+
authService: createApiService(authAxiosInstance),
|
|
535
|
+
userProgressService: createApiService(userProgressAxiosInstance),
|
|
536
|
+
recommendationService: createApiService(recommendationAxiosInstance),
|
|
537
|
+
partnerManager: createApiService(partnerManagerAxiosInstance),
|
|
538
|
+
puzzleController: createApiService(puzzleControllerAxiosInstance),
|
|
539
|
+
monolith: createApiService(monolithAxiosInstance),
|
|
540
|
+
tokenRefreshManager
|
|
541
|
+
};
|
|
542
|
+
};
|
|
543
|
+
var ApiClientContext = (0, import_react.createContext)(null);
|
|
544
|
+
var useApiClient = () => {
|
|
545
|
+
const context = (0, import_react.useContext)(ApiClientContext);
|
|
546
|
+
if (!context) {
|
|
547
|
+
throw new Error("useApiClient must be used within ApiClientProvider");
|
|
548
|
+
}
|
|
549
|
+
return context;
|
|
550
|
+
};
|
|
551
|
+
var ApiClientProvider = ApiClientContext.Provider;
|
|
552
|
+
// Annotate the CommonJS export names for ESM import in node:
|
|
553
|
+
0 && (module.exports = {
|
|
554
|
+
ApiClientContext,
|
|
555
|
+
ApiClientProvider,
|
|
556
|
+
TokenRefreshManager,
|
|
557
|
+
createApiClient,
|
|
558
|
+
createTokenRefreshManager,
|
|
559
|
+
useApiClient
|
|
560
|
+
});
|
|
561
|
+
//# sourceMappingURL=index.cjs.map
|