@jetlinks-web/core 2.2.0 → 2.2.2
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/package.json +3 -3
- package/src/axios.ts +69 -9
- package/src/websocket.ts +5 -4
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@jetlinks-web/core",
|
|
3
|
-
"version": "2.2.
|
|
3
|
+
"version": "2.2.2",
|
|
4
4
|
"main": "index.ts",
|
|
5
5
|
"module": "index.ts",
|
|
6
6
|
"keywords": [
|
|
@@ -22,8 +22,8 @@
|
|
|
22
22
|
"axios": "^1.7.4",
|
|
23
23
|
"rxjs": "^7.8.1",
|
|
24
24
|
"@jetlinks-web/constants": "^1.0.9",
|
|
25
|
-
"@jetlinks-web/
|
|
26
|
-
"@jetlinks-web/
|
|
25
|
+
"@jetlinks-web/types": "^1.0.2",
|
|
26
|
+
"@jetlinks-web/utils": "^1.2.8"
|
|
27
27
|
},
|
|
28
28
|
"publishConfig": {
|
|
29
29
|
"registry": "https://registry.npmjs.org/",
|
package/src/axios.ts
CHANGED
|
@@ -1,5 +1,5 @@
|
|
|
1
1
|
import { TOKEN_KEY, BASE_API } from '@jetlinks-web/constants'
|
|
2
|
-
import {
|
|
2
|
+
import {getToken, randomString} from '@jetlinks-web/utils'
|
|
3
3
|
import axios from 'axios'
|
|
4
4
|
import type {
|
|
5
5
|
AxiosInstance,
|
|
@@ -13,6 +13,7 @@ import {isFunction, isObject} from 'lodash-es'
|
|
|
13
13
|
interface Options {
|
|
14
14
|
|
|
15
15
|
tokenExpiration: (err: AxiosError<any>, response: AxiosResponse) => void
|
|
16
|
+
handleReconnect: (err: AxiosError<any>, response: AxiosResponse) => Promise<any>
|
|
16
17
|
filter_url?: Array<string>
|
|
17
18
|
code?: number
|
|
18
19
|
codeKey?: string
|
|
@@ -35,7 +36,7 @@ interface Options {
|
|
|
35
36
|
*/
|
|
36
37
|
handleError?: (msg: string, status: string | number, error: AxiosError<any>) => void
|
|
37
38
|
requestOptions?: (config: InternalAxiosRequestConfig) => InternalAxiosRequestConfig | Record<string, any>
|
|
38
|
-
|
|
39
|
+
isCreateTokenRefresh?: boolean
|
|
39
40
|
}
|
|
40
41
|
|
|
41
42
|
interface RequestOptions {
|
|
@@ -58,13 +59,33 @@ let _options: Options = {
|
|
|
58
59
|
langKey: 'lang',
|
|
59
60
|
requestOptions: (config) => ({}),
|
|
60
61
|
tokenExpiration: () => {},
|
|
62
|
+
handleReconnect: () => new Promise(),
|
|
63
|
+
isCreateTokenRefresh: false
|
|
61
64
|
}
|
|
62
65
|
|
|
66
|
+
let failedQueue = [];
|
|
67
|
+
let isRefreshing = false;
|
|
68
|
+
|
|
63
69
|
const isApp = (window as any).__MICRO_APP_ENVIRONMENT__
|
|
64
|
-
const controller = new AbortController();
|
|
65
70
|
|
|
71
|
+
const pendingRequests = new Map<string, AbortController>();
|
|
72
|
+
const requestRecords = (config: InternalAxiosRequestConfig) => {
|
|
73
|
+
const key = randomString(32)
|
|
74
|
+
|
|
75
|
+
// 取消重复请求
|
|
76
|
+
if (pendingRequests.has(key)) {
|
|
77
|
+
pendingRequests.get(key)?.abort()
|
|
78
|
+
}
|
|
79
|
+
|
|
80
|
+
const controller = new AbortController()
|
|
81
|
+
config.signal = controller.signal;
|
|
82
|
+
config.__requestKey = key;
|
|
83
|
+
|
|
84
|
+
pendingRequests.set(key, controller)
|
|
85
|
+
}
|
|
66
86
|
const handleRequest = (config: InternalAxiosRequestConfig) => {
|
|
67
|
-
|
|
87
|
+
requestRecords(config)
|
|
88
|
+
const token = getToken();
|
|
68
89
|
const lang = localStorage.getItem(_options.langKey)
|
|
69
90
|
const localBaseApi = localStorage.getItem('')
|
|
70
91
|
|
|
@@ -97,16 +118,19 @@ const handleRequest = (config: InternalAxiosRequestConfig) => {
|
|
|
97
118
|
}
|
|
98
119
|
}
|
|
99
120
|
|
|
100
|
-
|
|
101
121
|
return config
|
|
102
122
|
}
|
|
103
123
|
|
|
104
124
|
const handleResponse = (response: AxiosResponse) => {
|
|
105
|
-
|
|
106
125
|
if (_options.handleResponse && isFunction(_options.handleResponse)) {
|
|
107
126
|
return _options.handleResponse(response)
|
|
108
127
|
}
|
|
109
128
|
|
|
129
|
+
const __key = response.config?.__requestKey
|
|
130
|
+
if(__key){
|
|
131
|
+
pendingRequests.delete(__key)
|
|
132
|
+
}
|
|
133
|
+
|
|
110
134
|
if (response.data instanceof ArrayBuffer) {
|
|
111
135
|
return response
|
|
112
136
|
}
|
|
@@ -124,7 +148,35 @@ const handleResponse = (response: AxiosResponse) => {
|
|
|
124
148
|
return response.data
|
|
125
149
|
}
|
|
126
150
|
|
|
127
|
-
const
|
|
151
|
+
const createTokenRefreshHandler = async (err) => {
|
|
152
|
+
const originalRequest = err.config;
|
|
153
|
+
if (isRefreshing) { // 记录之后失败的请求
|
|
154
|
+
return new Promise((resolve, reject) => {
|
|
155
|
+
failedQueue.push({ resolve, reject });
|
|
156
|
+
}).then((_token) => {
|
|
157
|
+
originalRequest.headers[TOKEN_KEY] = _token;
|
|
158
|
+
instance(originalRequest)
|
|
159
|
+
}).catch(err => Promise.reject(err))
|
|
160
|
+
}
|
|
161
|
+
originalRequest._retry = true;
|
|
162
|
+
isRefreshing = true;
|
|
163
|
+
try {
|
|
164
|
+
const loginResult = await _options.handleReconnect?.()
|
|
165
|
+
if(loginResult){
|
|
166
|
+
const token = getToken() // 更新请求头, 修改全部的token
|
|
167
|
+
originalRequest.headers[TOKEN_KEY] = token;
|
|
168
|
+
failedQueue.forEach(a => a.resolve(token));
|
|
169
|
+
return instance(originalRequest);
|
|
170
|
+
}
|
|
171
|
+
} catch (err) {
|
|
172
|
+
failedQueue.forEach(cb => cb.reject(err));
|
|
173
|
+
throw err;
|
|
174
|
+
} finally {
|
|
175
|
+
failedQueue = [];
|
|
176
|
+
isRefreshing = false;
|
|
177
|
+
}
|
|
178
|
+
}
|
|
179
|
+
const errorHandler = async (err: AxiosError<any>) => {
|
|
128
180
|
let description = err.response?.message || 'Error'
|
|
129
181
|
let _status: string | number = 0
|
|
130
182
|
if (err.response) {
|
|
@@ -137,8 +189,11 @@ const errorHandler = (err: AxiosError<any>) => {
|
|
|
137
189
|
description = (`${data?.message}`).substring(0, 90)
|
|
138
190
|
break;
|
|
139
191
|
case 401:
|
|
140
|
-
description = err.response.data.result.text || '用户未登录'
|
|
141
|
-
_options.tokenExpiration?.(
|
|
192
|
+
description = err.response.data.result.text || '用户未登录';
|
|
193
|
+
_options.tokenExpiration?.()
|
|
194
|
+
if(_options.isCreateTokenRefresh){
|
|
195
|
+
return createTokenRefreshHandler(err)
|
|
196
|
+
}
|
|
142
197
|
break;
|
|
143
198
|
case 404:
|
|
144
199
|
description = err?.response?.data?.message || `${data?.error} ${data?.path}`
|
|
@@ -158,6 +213,11 @@ const errorHandler = (err: AxiosError<any>) => {
|
|
|
158
213
|
return Promise.reject(err)
|
|
159
214
|
}
|
|
160
215
|
|
|
216
|
+
export const abortAllRequests = () => {
|
|
217
|
+
pendingRequests.forEach(controller => controller.abort())
|
|
218
|
+
pendingRequests.clear()
|
|
219
|
+
}
|
|
220
|
+
|
|
161
221
|
export const crateAxios = (options: Options) => {
|
|
162
222
|
if (options) {
|
|
163
223
|
_options = Object.assign(_options, options)
|
package/src/websocket.ts
CHANGED
|
@@ -1,10 +1,7 @@
|
|
|
1
|
-
import { wsClient } from '@/utils/websocket';
|
|
2
1
|
import { webSocket, WebSocketSubject } from 'rxjs/webSocket';
|
|
3
2
|
import { Observable, Subject, timer, Subscription, EMPTY } from 'rxjs';
|
|
4
3
|
import { retry, catchError } from 'rxjs/operators';
|
|
5
4
|
import { notification } from 'ant-design-vue';
|
|
6
|
-
import app from '@micro-zoe/micro-app'
|
|
7
|
-
import { log } from 'console';
|
|
8
5
|
|
|
9
6
|
interface WebSocketMessage {
|
|
10
7
|
type: string;
|
|
@@ -36,7 +33,7 @@ export class WebSocketClient {
|
|
|
36
33
|
private wsClient: WebSocketClient | undefined
|
|
37
34
|
|
|
38
35
|
constructor(options?: WS_Options) {
|
|
39
|
-
this.options
|
|
36
|
+
this.setOptions(options)
|
|
40
37
|
this.setupConnectionMonitor();
|
|
41
38
|
if (isApp) {
|
|
42
39
|
(window as any).microApp.addGlobalDataListener((data) => {
|
|
@@ -45,6 +42,10 @@ export class WebSocketClient {
|
|
|
45
42
|
}
|
|
46
43
|
}
|
|
47
44
|
|
|
45
|
+
public setOptions(options: WS_Options) {
|
|
46
|
+
this.options = options || {}
|
|
47
|
+
}
|
|
48
|
+
|
|
48
49
|
public initWebSocket(url: string) {
|
|
49
50
|
this.url = url;
|
|
50
51
|
}
|