@0xchain/request 1.1.0-beta.61 → 1.1.0-beta.63
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.js +80 -72
- package/dist/interceptors/refreshToken.d.ts +4 -0
- package/dist/interceptors/refreshToken.d.ts.map +1 -1
- package/dist/interceptors/response.d.ts +12 -2
- package/dist/interceptors/response.d.ts.map +1 -1
- package/dist/tokenRefresh.d.ts +7 -0
- package/dist/tokenRefresh.d.ts.map +1 -0
- package/package.json +3 -3
package/dist/index.js
CHANGED
|
@@ -1,8 +1,8 @@
|
|
|
1
1
|
import axios, { isAxiosError, AxiosError } from "axios";
|
|
2
2
|
import qs from "qs";
|
|
3
|
-
import
|
|
3
|
+
import jsCookie from "js-cookie";
|
|
4
4
|
import { logger } from "@0xchain/logger";
|
|
5
|
-
import
|
|
5
|
+
import { getToken, setToken, isTokenExpired } from "@0xchain/token";
|
|
6
6
|
const isSimplifiedChinese = () => {
|
|
7
7
|
return /^(zh-hans|zh-cn|zh-sg|zh-my)$/.test(window?.navigator.language.toLowerCase());
|
|
8
8
|
};
|
|
@@ -79,8 +79,8 @@ async function commonHeader(config) {
|
|
|
79
79
|
}
|
|
80
80
|
}
|
|
81
81
|
} else {
|
|
82
|
-
access_token =
|
|
83
|
-
language =
|
|
82
|
+
access_token = jsCookie.get("access_token") || "";
|
|
83
|
+
language = jsCookie.get("NEXT_LOCALE") || matchSystemLanguage() || "en";
|
|
84
84
|
}
|
|
85
85
|
const isRefreshToken = config.headers?.["isRefreshToken"];
|
|
86
86
|
setHeader(config, "Accept-Language", language);
|
|
@@ -193,15 +193,58 @@ function errorInterceptor(error) {
|
|
|
193
193
|
logger.error({ message, details: err.details, errType: "server error" });
|
|
194
194
|
return Promise.reject(err);
|
|
195
195
|
}
|
|
196
|
-
|
|
197
|
-
|
|
198
|
-
|
|
199
|
-
|
|
196
|
+
const refreshBaseURL = typeof window !== "undefined" ? process.env.NEXT_PUBLIC_AUTH_URL || window.location.origin : process.env.AUTH_INNER_API_URL || process.env.NEXT_PUBLIC_AUTH_URL;
|
|
197
|
+
const refreshClient = axios.create({
|
|
198
|
+
baseURL: refreshBaseURL,
|
|
199
|
+
timeout: 1e4,
|
|
200
|
+
withCredentials: true,
|
|
201
|
+
paramsSerializer(params) {
|
|
202
|
+
return qs.stringify(params, { arrayFormat: "brackets" });
|
|
203
|
+
}
|
|
204
|
+
});
|
|
205
|
+
let refreshPromise = null;
|
|
206
|
+
function refreshAccessToken() {
|
|
207
|
+
if (refreshPromise) {
|
|
208
|
+
return refreshPromise;
|
|
200
209
|
}
|
|
201
|
-
|
|
202
|
-
|
|
203
|
-
|
|
210
|
+
const refresh_token = getToken("refresh_token");
|
|
211
|
+
if (!refresh_token) {
|
|
212
|
+
return Promise.resolve(null);
|
|
213
|
+
}
|
|
214
|
+
const pending = refreshClient.get("/api/refresh/token", {
|
|
215
|
+
params: {
|
|
216
|
+
grant_type: "refresh_token",
|
|
217
|
+
refresh_token
|
|
218
|
+
},
|
|
219
|
+
headers: {
|
|
220
|
+
isRefreshToken: true
|
|
221
|
+
}
|
|
222
|
+
}).then((res) => {
|
|
223
|
+
const data = res?.data?.data;
|
|
224
|
+
if (data?.access_token) {
|
|
225
|
+
setToken(data);
|
|
226
|
+
return data.access_token;
|
|
204
227
|
}
|
|
228
|
+
return null;
|
|
229
|
+
}).catch(() => null).finally(() => {
|
|
230
|
+
refreshPromise = null;
|
|
231
|
+
});
|
|
232
|
+
refreshPromise = pending;
|
|
233
|
+
return pending;
|
|
234
|
+
}
|
|
235
|
+
function getCurrentAccessToken() {
|
|
236
|
+
return jsCookie.get("access_token") || getToken("access_token") || "";
|
|
237
|
+
}
|
|
238
|
+
function shouldSkipRefresh(config) {
|
|
239
|
+
const url = typeof config?.url === "string" ? config.url : "";
|
|
240
|
+
if (url.includes("/api/refresh/token")) return true;
|
|
241
|
+
if (url.includes("/v2/auth/")) return true;
|
|
242
|
+
if (config?.headers?.isRefreshToken) return true;
|
|
243
|
+
return false;
|
|
244
|
+
}
|
|
245
|
+
function handleBusinessResponse(response) {
|
|
246
|
+
const config = response.config;
|
|
247
|
+
if (config.returnFullResponse) {
|
|
205
248
|
return response;
|
|
206
249
|
}
|
|
207
250
|
if (response.status === 200) {
|
|
@@ -247,6 +290,23 @@ function ResponseInterceptor(response) {
|
|
|
247
290
|
}
|
|
248
291
|
return response;
|
|
249
292
|
}
|
|
293
|
+
function createResponseInterceptor(instance) {
|
|
294
|
+
return async function ResponseInterceptor(response) {
|
|
295
|
+
if (response.status === 401) {
|
|
296
|
+
const config = response.config;
|
|
297
|
+
const canRetry = !isServerRuntime() && !shouldSkipRefresh(config) && !config.__isRetryAfterRefresh;
|
|
298
|
+
if (canRetry) {
|
|
299
|
+
const newAccessToken = await refreshAccessToken();
|
|
300
|
+
if (newAccessToken) {
|
|
301
|
+
config.__isRetryAfterRefresh = true;
|
|
302
|
+
return instance(config);
|
|
303
|
+
}
|
|
304
|
+
}
|
|
305
|
+
return response;
|
|
306
|
+
}
|
|
307
|
+
return handleBusinessResponse(response);
|
|
308
|
+
};
|
|
309
|
+
}
|
|
250
310
|
function setAuthHeader(config, accessToken) {
|
|
251
311
|
const value = `Bearer ${accessToken}`;
|
|
252
312
|
const h = config.headers;
|
|
@@ -256,16 +316,6 @@ function setAuthHeader(config, accessToken) {
|
|
|
256
316
|
h.Authorization = value;
|
|
257
317
|
}
|
|
258
318
|
}
|
|
259
|
-
let refreshPromise = null;
|
|
260
|
-
const axiosInstance$1 = axios.create({
|
|
261
|
-
baseURL: process.env.AUTH_INNER_API_URL || process.env.NEXT_PUBLIC_AUTH_URL,
|
|
262
|
-
timeout: 1e4,
|
|
263
|
-
withCredentials: true,
|
|
264
|
-
// Always include credentials by default
|
|
265
|
-
paramsSerializer: function(params) {
|
|
266
|
-
return qs.stringify(params, { arrayFormat: "brackets" });
|
|
267
|
-
}
|
|
268
|
-
});
|
|
269
319
|
async function refreshToken(config) {
|
|
270
320
|
if (isServerRuntime()) {
|
|
271
321
|
return config;
|
|
@@ -273,13 +323,10 @@ async function refreshToken(config) {
|
|
|
273
323
|
if (typeof config.url === "string" && config.url.includes("/api/refresh/token")) {
|
|
274
324
|
return config;
|
|
275
325
|
}
|
|
276
|
-
|
|
277
|
-
|
|
278
|
-
|
|
279
|
-
|
|
280
|
-
if (accessAfterWait) {
|
|
281
|
-
setAuthHeader(config, accessAfterWait);
|
|
282
|
-
}
|
|
326
|
+
if (typeof config.url === "string" && config.url.includes("/v2/auth/")) {
|
|
327
|
+
return config;
|
|
328
|
+
}
|
|
329
|
+
if (config.headers?.isRefreshToken) {
|
|
283
330
|
return config;
|
|
284
331
|
}
|
|
285
332
|
let expired = false;
|
|
@@ -289,49 +336,10 @@ async function refreshToken(config) {
|
|
|
289
336
|
expired = false;
|
|
290
337
|
}
|
|
291
338
|
if (expired) {
|
|
292
|
-
const
|
|
293
|
-
|
|
294
|
-
|
|
295
|
-
|
|
296
|
-
}
|
|
297
|
-
if (refreshPromise) {
|
|
298
|
-
await refreshPromise;
|
|
299
|
-
const accessAfterWait = Cookies.get("access_token") || getToken("access_token");
|
|
300
|
-
if (accessAfterWait) {
|
|
301
|
-
setAuthHeader(config, accessAfterWait);
|
|
302
|
-
}
|
|
303
|
-
return config;
|
|
304
|
-
}
|
|
305
|
-
const refreshHeaders = { ...headers };
|
|
306
|
-
delete refreshHeaders.Authorization;
|
|
307
|
-
delete refreshHeaders.authorization;
|
|
308
|
-
refreshHeaders.isRefreshToken = true;
|
|
309
|
-
refreshPromise = axiosInstance$1.get(`/api/refresh/token`, {
|
|
310
|
-
params: {
|
|
311
|
-
grant_type: "refresh_token",
|
|
312
|
-
refresh_token
|
|
313
|
-
},
|
|
314
|
-
headers: {
|
|
315
|
-
...refreshHeaders
|
|
316
|
-
}
|
|
317
|
-
}).then((res) => {
|
|
318
|
-
if (res?.data?.data) {
|
|
319
|
-
setToken(res.data.data);
|
|
320
|
-
return res.data.data;
|
|
321
|
-
} else {
|
|
322
|
-
removeAllToken();
|
|
323
|
-
return null;
|
|
324
|
-
}
|
|
325
|
-
}).catch(() => {
|
|
326
|
-
removeAllToken();
|
|
327
|
-
return null;
|
|
328
|
-
}).finally(() => {
|
|
329
|
-
refreshPromise = null;
|
|
330
|
-
});
|
|
331
|
-
await refreshPromise;
|
|
332
|
-
const accessAfterRefresh = Cookies.get("access_token") || getToken("access_token");
|
|
333
|
-
if (accessAfterRefresh) {
|
|
334
|
-
setAuthHeader(config, accessAfterRefresh);
|
|
339
|
+
const newAccessToken = await refreshAccessToken();
|
|
340
|
+
const accessToken = newAccessToken || getCurrentAccessToken();
|
|
341
|
+
if (accessToken) {
|
|
342
|
+
setAuthHeader(config, accessToken);
|
|
335
343
|
}
|
|
336
344
|
}
|
|
337
345
|
return config;
|
|
@@ -350,7 +358,7 @@ const axiosInstance = axios.create({
|
|
|
350
358
|
});
|
|
351
359
|
axiosInstance.interceptors.request.use(refreshToken);
|
|
352
360
|
axiosInstance.interceptors.request.use(commonHeader, errorInterceptor);
|
|
353
|
-
axiosInstance.interceptors.response.use(
|
|
361
|
+
axiosInstance.interceptors.response.use(createResponseInterceptor(axiosInstance), errorInterceptor);
|
|
354
362
|
export {
|
|
355
363
|
axiosInstance as default,
|
|
356
364
|
setServerHeaderProvider
|
|
@@ -1,3 +1,7 @@
|
|
|
1
1
|
import { InternalAxiosRequestConfig } from 'axios';
|
|
2
|
+
/**
|
|
3
|
+
* 请求前的「预测式刷新」:若本地判断 access_token 即将/已过期,提前刷新。
|
|
4
|
+
* 这是一层优化;真正的兜底由响应拦截器的 401 自动刷新+重放保证。
|
|
5
|
+
*/
|
|
2
6
|
export default function refreshToken(config: InternalAxiosRequestConfig): Promise<InternalAxiosRequestConfig<any>>;
|
|
3
7
|
//# sourceMappingURL=refreshToken.d.ts.map
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"refreshToken.d.ts","sourceRoot":"","sources":["../../src/interceptors/refreshToken.ts"],"names":[],"mappings":"AAAA,
|
|
1
|
+
{"version":3,"file":"refreshToken.d.ts","sourceRoot":"","sources":["../../src/interceptors/refreshToken.ts"],"names":[],"mappings":"AAAA,OAAO,EAAoB,0BAA0B,EAAE,MAAM,OAAO,CAAC;AAerE;;;GAGG;AACH,wBAA8B,YAAY,CACxC,MAAM,EAAE,0BAA0B,4CAkCnC"}
|
|
@@ -1,3 +1,13 @@
|
|
|
1
|
-
import { AxiosResponse } from 'axios';
|
|
2
|
-
|
|
1
|
+
import { AxiosInstance, AxiosResponse } from 'axios';
|
|
2
|
+
/**
|
|
3
|
+
* 响应拦截器工厂。
|
|
4
|
+
*
|
|
5
|
+
* 业界标准 401 处理:业务接口返回 401 时,用 refresh_token 刷新一次并重放原请求;
|
|
6
|
+
* 仅当刷新失败时把 401 透传给上层(由显式 logout 决定是否清登录态)。
|
|
7
|
+
* - 仅在客户端重试(SSR 无 cookie jar)。
|
|
8
|
+
* - 每个请求最多重试一次,避免死循环。
|
|
9
|
+
* - /v2/auth/*、刷新接口本身不触发重试。
|
|
10
|
+
*/
|
|
11
|
+
export declare function createResponseInterceptor(instance: AxiosInstance): (response: AxiosResponse) => Promise<any>;
|
|
12
|
+
export default createResponseInterceptor;
|
|
3
13
|
//# sourceMappingURL=response.d.ts.map
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"response.d.ts","sourceRoot":"","sources":["../../src/interceptors/response.ts"],"names":[],"mappings":"AAAA,OAAO,EAAc,aAAa,EAAE,MAAM,OAAO,CAAC;
|
|
1
|
+
{"version":3,"file":"response.d.ts","sourceRoot":"","sources":["../../src/interceptors/response.ts"],"names":[],"mappings":"AAAA,OAAO,EAAc,aAAa,EAAE,aAAa,EAA8B,MAAM,OAAO,CAAC;AAqE7F;;;;;;;;GAQG;AACH,wBAAgB,yBAAyB,CAAC,QAAQ,EAAE,aAAa,IACrB,UAAU,aAAa,KAAG,OAAO,CAAC,GAAG,CAAC,CAoBjF;AAED,eAAe,yBAAyB,CAAC"}
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"tokenRefresh.d.ts","sourceRoot":"","sources":["../src/tokenRefresh.ts"],"names":[],"mappings":"AA+BA;;;GAGG;AACH,wBAAgB,kBAAkB,IAAI,OAAO,CAAC,MAAM,GAAG,IAAI,CAAC,CAmC3D;AAED,wBAAgB,qBAAqB,IAAI,MAAM,CAE9C"}
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@0xchain/request",
|
|
3
|
-
"version": "1.1.0-beta.
|
|
3
|
+
"version": "1.1.0-beta.63",
|
|
4
4
|
"type": "module",
|
|
5
5
|
"main": "./dist/index.js",
|
|
6
6
|
"module": "./dist/index.js",
|
|
@@ -32,8 +32,8 @@
|
|
|
32
32
|
"qs": "6.14.0"
|
|
33
33
|
},
|
|
34
34
|
"dependencies": {
|
|
35
|
-
"@0xchain/logger": "1.1.0-beta.
|
|
36
|
-
"@0xchain/token": "1.1.0-beta.
|
|
35
|
+
"@0xchain/logger": "1.1.0-beta.63",
|
|
36
|
+
"@0xchain/token": "1.1.0-beta.63"
|
|
37
37
|
},
|
|
38
38
|
"nx": {
|
|
39
39
|
"tags": [
|