@etsoo/react 1.3.41 → 1.3.45
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/lib/app/RefreshTokenRQ.d.ts +4 -0
- package/lib/app/ServiceApp.d.ts +7 -6
- package/lib/app/ServiceApp.js +112 -42
- package/package.json +4 -4
- package/src/app/RefreshTokenRQ.ts +5 -0
- package/src/app/ServiceApp.ts +150 -50
package/lib/app/ServiceApp.d.ts
CHANGED
|
@@ -1,10 +1,10 @@
|
|
|
1
|
-
import {
|
|
2
|
-
import { ApiDataError } from '@etsoo/restclient';
|
|
1
|
+
import { IApi, RefreshTokenProps } from '@etsoo/appscript';
|
|
3
2
|
import { IServiceAppSettings } from './IServiceAppSettings';
|
|
4
3
|
import { IServicePageData } from './IServicePage';
|
|
5
4
|
import { IServiceUser } from './IServiceUser';
|
|
6
5
|
import { ISmartERPUser } from './ISmartERPUser';
|
|
7
6
|
import { ReactApp } from './ReactApp';
|
|
7
|
+
import { RefreshTokenRQ } from './RefreshTokenRQ';
|
|
8
8
|
/**
|
|
9
9
|
* Core Service App
|
|
10
10
|
* Service login to core system, get the refesh token and access token
|
|
@@ -31,14 +31,15 @@ export declare class ServiceApp<P extends IServicePageData = IServicePageData, U
|
|
|
31
31
|
*/
|
|
32
32
|
toLoginPage(): void;
|
|
33
33
|
/**
|
|
34
|
-
*
|
|
35
|
-
* @param
|
|
34
|
+
* Refresh token
|
|
35
|
+
* @param props Props
|
|
36
36
|
*/
|
|
37
|
-
|
|
37
|
+
refreshToken<D = RefreshTokenRQ>(props?: RefreshTokenProps<D>): Promise<boolean>;
|
|
38
|
+
private loginFailed;
|
|
38
39
|
/**
|
|
39
40
|
* Try login
|
|
40
41
|
*/
|
|
41
|
-
tryLogin(): Promise<boolean>;
|
|
42
|
+
tryLogin<D extends {} = {}>(data?: D): Promise<boolean>;
|
|
42
43
|
/**
|
|
43
44
|
* User login extended
|
|
44
45
|
* @param user New user
|
package/lib/app/ServiceApp.js
CHANGED
|
@@ -41,72 +41,142 @@ export class ServiceApp extends ReactApp {
|
|
|
41
41
|
this.culture;
|
|
42
42
|
}
|
|
43
43
|
/**
|
|
44
|
-
*
|
|
45
|
-
* @param
|
|
44
|
+
* Refresh token
|
|
45
|
+
* @param props Props
|
|
46
46
|
*/
|
|
47
|
-
|
|
48
|
-
|
|
49
|
-
|
|
50
|
-
*/
|
|
51
|
-
async tryLogin() {
|
|
47
|
+
async refreshToken(props) {
|
|
48
|
+
// Destruct
|
|
49
|
+
const { callback, data, relogin = false, showLoading = false } = props !== null && props !== void 0 ? props : {};
|
|
52
50
|
// Token
|
|
53
51
|
const token = this.getCacheToken();
|
|
54
52
|
if (token == null || token === '') {
|
|
55
|
-
|
|
53
|
+
if (callback)
|
|
54
|
+
callback(false);
|
|
56
55
|
return false;
|
|
57
56
|
}
|
|
58
57
|
// Reqest data
|
|
59
|
-
|
|
60
|
-
|
|
58
|
+
// Merge additional data passed
|
|
59
|
+
const rq = {
|
|
60
|
+
timezone: this.getTimeZone(),
|
|
61
|
+
...data
|
|
61
62
|
};
|
|
62
63
|
// Payload
|
|
63
64
|
const payload = {
|
|
65
|
+
showLoading,
|
|
64
66
|
config: { headers: { [CoreConstants.TokenHeaderRefresh]: token } },
|
|
65
67
|
onError: (error) => {
|
|
66
|
-
|
|
68
|
+
if (callback)
|
|
69
|
+
callback(error);
|
|
67
70
|
// Prevent further processing
|
|
68
71
|
return false;
|
|
69
72
|
}
|
|
70
73
|
};
|
|
74
|
+
// Success callback
|
|
75
|
+
const success = async (result, failCallback) => {
|
|
76
|
+
// Token
|
|
77
|
+
const refreshToken = this.getResponseToken(payload.response);
|
|
78
|
+
if (refreshToken == null || result.data == null) {
|
|
79
|
+
if (failCallback)
|
|
80
|
+
failCallback(this.get('noData'));
|
|
81
|
+
return false;
|
|
82
|
+
}
|
|
83
|
+
// User data
|
|
84
|
+
const userData = result.data;
|
|
85
|
+
// Use core system access token to service api to exchange service access token
|
|
86
|
+
const serviceResult = await this.api.put('Auth/ExchangeToken', {
|
|
87
|
+
token: userData.token
|
|
88
|
+
}, {
|
|
89
|
+
showLoading,
|
|
90
|
+
onError: (error) => {
|
|
91
|
+
if (failCallback)
|
|
92
|
+
failCallback(error);
|
|
93
|
+
// Prevent further processing
|
|
94
|
+
return false;
|
|
95
|
+
}
|
|
96
|
+
});
|
|
97
|
+
if (serviceResult == null)
|
|
98
|
+
return false;
|
|
99
|
+
if (!serviceResult.ok) {
|
|
100
|
+
if (failCallback)
|
|
101
|
+
failCallback(serviceResult);
|
|
102
|
+
return false;
|
|
103
|
+
}
|
|
104
|
+
if (serviceResult.data == null) {
|
|
105
|
+
if (failCallback)
|
|
106
|
+
failCallback(this.get('noData'));
|
|
107
|
+
return false;
|
|
108
|
+
}
|
|
109
|
+
this.userLoginEx(serviceResult.data, refreshToken, userData);
|
|
110
|
+
return true;
|
|
111
|
+
};
|
|
71
112
|
// Call API
|
|
72
|
-
const result = await this.coreApi.put('Auth/RefreshToken',
|
|
113
|
+
const result = await this.coreApi.put('Auth/RefreshToken', rq, payload);
|
|
73
114
|
if (result == null)
|
|
74
115
|
return false;
|
|
75
116
|
if (!result.ok) {
|
|
76
|
-
|
|
77
|
-
|
|
78
|
-
|
|
79
|
-
|
|
80
|
-
|
|
81
|
-
|
|
82
|
-
|
|
83
|
-
|
|
84
|
-
|
|
85
|
-
|
|
86
|
-
|
|
87
|
-
|
|
88
|
-
|
|
89
|
-
|
|
90
|
-
|
|
91
|
-
|
|
92
|
-
|
|
93
|
-
|
|
94
|
-
|
|
95
|
-
|
|
117
|
+
if (result.type === 'TokenExpired' && relogin) {
|
|
118
|
+
// Try login
|
|
119
|
+
// Dialog to receive password
|
|
120
|
+
var labels = this.getLabels('reloginTip', 'login');
|
|
121
|
+
this.notifier.prompt(labels.reloginTip, async (pwd) => {
|
|
122
|
+
if (pwd == null) {
|
|
123
|
+
this.toLoginPage();
|
|
124
|
+
return;
|
|
125
|
+
}
|
|
126
|
+
// Set password for the action
|
|
127
|
+
rq.pwd = this.encrypt(pwd, this.settings.serviceId.toString());
|
|
128
|
+
// Submit again
|
|
129
|
+
const result = await this.api.put('Auth/RefreshToken', data, payload);
|
|
130
|
+
if (result == null)
|
|
131
|
+
return;
|
|
132
|
+
if (result.ok) {
|
|
133
|
+
await success(result, (loginResult) => {
|
|
134
|
+
const message = this.formatRefreshTokenResult(loginResult);
|
|
135
|
+
if (message)
|
|
136
|
+
this.notifier.alert(message);
|
|
137
|
+
});
|
|
138
|
+
return;
|
|
139
|
+
}
|
|
140
|
+
// Popup message
|
|
141
|
+
this.alertResult(result);
|
|
142
|
+
return false;
|
|
143
|
+
}, labels.login, { type: 'password' });
|
|
144
|
+
// Fake truth to avoid reloading
|
|
145
|
+
return true;
|
|
96
146
|
}
|
|
97
|
-
|
|
98
|
-
|
|
99
|
-
return false;
|
|
100
|
-
if (!serviceResult.ok) {
|
|
101
|
-
this.tryLoginResult(result);
|
|
147
|
+
if (callback)
|
|
148
|
+
callback(result);
|
|
102
149
|
return false;
|
|
103
150
|
}
|
|
104
|
-
|
|
105
|
-
|
|
151
|
+
return await success(result, callback);
|
|
152
|
+
}
|
|
153
|
+
loginFailed() {
|
|
154
|
+
this.userUnauthorized();
|
|
155
|
+
this.toLoginPage();
|
|
156
|
+
}
|
|
157
|
+
/**
|
|
158
|
+
* Try login
|
|
159
|
+
*/
|
|
160
|
+
async tryLogin(data) {
|
|
161
|
+
// Reset user state
|
|
162
|
+
const result = await super.tryLogin(data);
|
|
163
|
+
if (!result)
|
|
106
164
|
return false;
|
|
107
|
-
|
|
108
|
-
this.
|
|
109
|
-
|
|
165
|
+
// Refresh token
|
|
166
|
+
return await this.refreshToken({
|
|
167
|
+
callback: (result) => {
|
|
168
|
+
const message = this.formatRefreshTokenResult(result);
|
|
169
|
+
if (message == null) {
|
|
170
|
+
this.loginFailed();
|
|
171
|
+
return;
|
|
172
|
+
}
|
|
173
|
+
this.notifier.alert(message, () => {
|
|
174
|
+
this.loginFailed();
|
|
175
|
+
});
|
|
176
|
+
},
|
|
177
|
+
data,
|
|
178
|
+
relogin: true
|
|
179
|
+
});
|
|
110
180
|
}
|
|
111
181
|
/**
|
|
112
182
|
* User login extended
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@etsoo/react",
|
|
3
|
-
"version": "1.3.
|
|
3
|
+
"version": "1.3.45",
|
|
4
4
|
"description": "TypeScript ReactJs framework",
|
|
5
5
|
"main": "lib/index.js",
|
|
6
6
|
"types": "lib/index.d.ts",
|
|
@@ -50,7 +50,7 @@
|
|
|
50
50
|
"@emotion/react": "^11.7.0",
|
|
51
51
|
"@emotion/style": "^0.8.0",
|
|
52
52
|
"@emotion/styled": "^11.6.0",
|
|
53
|
-
"@etsoo/appscript": "^1.1.
|
|
53
|
+
"@etsoo/appscript": "^1.1.63",
|
|
54
54
|
"@etsoo/notificationbase": "^1.0.94",
|
|
55
55
|
"@etsoo/shared": "^1.0.75",
|
|
56
56
|
"@mui/icons-material": "^5.2.0",
|
|
@@ -83,13 +83,13 @@
|
|
|
83
83
|
"@types/react-test-renderer": "^17.0.1",
|
|
84
84
|
"@typescript-eslint/eslint-plugin": "^5.5.0",
|
|
85
85
|
"@typescript-eslint/parser": "^5.5.0",
|
|
86
|
-
"eslint": "^8.
|
|
86
|
+
"eslint": "^8.4.0",
|
|
87
87
|
"eslint-config-airbnb-base": "^15.0.0",
|
|
88
88
|
"eslint-plugin-import": "^2.25.3",
|
|
89
89
|
"eslint-plugin-react": "^7.27.1",
|
|
90
90
|
"jest": "^27.4.3",
|
|
91
91
|
"react-test-renderer": "^17.0.2",
|
|
92
|
-
"ts-jest": "^27.0
|
|
92
|
+
"ts-jest": "^27.1.0",
|
|
93
93
|
"typescript": "^4.5.2"
|
|
94
94
|
}
|
|
95
95
|
}
|
package/src/app/ServiceApp.ts
CHANGED
|
@@ -1,10 +1,10 @@
|
|
|
1
1
|
import {
|
|
2
2
|
createClient,
|
|
3
|
-
IActionResult,
|
|
4
3
|
IApi,
|
|
5
|
-
IApiPayload
|
|
4
|
+
IApiPayload,
|
|
5
|
+
RefreshTokenProps,
|
|
6
|
+
RefreshTokenResult
|
|
6
7
|
} from '@etsoo/appscript';
|
|
7
|
-
import { ApiDataError } from '@etsoo/restclient';
|
|
8
8
|
import { DomUtils } from '@etsoo/shared';
|
|
9
9
|
import { CoreConstants } from './CoreConstants';
|
|
10
10
|
import { IServiceAppSettings } from './IServiceAppSettings';
|
|
@@ -72,93 +72,193 @@ export class ServiceApp<
|
|
|
72
72
|
}
|
|
73
73
|
|
|
74
74
|
/**
|
|
75
|
-
*
|
|
76
|
-
* @param
|
|
75
|
+
* Refresh token
|
|
76
|
+
* @param props Props
|
|
77
77
|
*/
|
|
78
|
-
|
|
78
|
+
override async refreshToken<D = RefreshTokenRQ>(
|
|
79
|
+
props?: RefreshTokenProps<D>
|
|
80
|
+
) {
|
|
81
|
+
// Destruct
|
|
82
|
+
const {
|
|
83
|
+
callback,
|
|
84
|
+
data,
|
|
85
|
+
relogin = false,
|
|
86
|
+
showLoading = false
|
|
87
|
+
} = props ?? {};
|
|
79
88
|
|
|
80
|
-
/**
|
|
81
|
-
* Try login
|
|
82
|
-
*/
|
|
83
|
-
override async tryLogin() {
|
|
84
89
|
// Token
|
|
85
90
|
const token = this.getCacheToken();
|
|
86
91
|
if (token == null || token === '') {
|
|
87
|
-
|
|
92
|
+
if (callback) callback(false);
|
|
88
93
|
return false;
|
|
89
94
|
}
|
|
90
95
|
|
|
91
96
|
// Reqest data
|
|
92
|
-
|
|
93
|
-
|
|
97
|
+
// Merge additional data passed
|
|
98
|
+
const rq: RefreshTokenRQ = {
|
|
99
|
+
timezone: this.getTimeZone(),
|
|
100
|
+
...data
|
|
94
101
|
};
|
|
95
102
|
|
|
96
103
|
// Payload
|
|
97
104
|
const payload: IApiPayload<SmartERPLoginResult, any> = {
|
|
105
|
+
showLoading,
|
|
98
106
|
config: { headers: { [CoreConstants.TokenHeaderRefresh]: token } },
|
|
99
107
|
onError: (error) => {
|
|
100
|
-
|
|
108
|
+
if (callback) callback(error);
|
|
101
109
|
|
|
102
110
|
// Prevent further processing
|
|
103
111
|
return false;
|
|
104
112
|
}
|
|
105
113
|
};
|
|
106
114
|
|
|
115
|
+
// Success callback
|
|
116
|
+
const success = async (
|
|
117
|
+
result: SmartERPLoginResult,
|
|
118
|
+
failCallback?: (result: RefreshTokenResult) => void
|
|
119
|
+
) => {
|
|
120
|
+
// Token
|
|
121
|
+
const refreshToken = this.getResponseToken(payload.response);
|
|
122
|
+
if (refreshToken == null || result.data == null) {
|
|
123
|
+
if (failCallback) failCallback(this.get('noData')!);
|
|
124
|
+
return false;
|
|
125
|
+
}
|
|
126
|
+
|
|
127
|
+
// User data
|
|
128
|
+
const userData = result.data;
|
|
129
|
+
|
|
130
|
+
// Use core system access token to service api to exchange service access token
|
|
131
|
+
const serviceResult = await this.api.put<ServiceLoginResult>(
|
|
132
|
+
'Auth/ExchangeToken',
|
|
133
|
+
{
|
|
134
|
+
token: userData.token
|
|
135
|
+
},
|
|
136
|
+
{
|
|
137
|
+
showLoading,
|
|
138
|
+
onError: (error) => {
|
|
139
|
+
if (failCallback) failCallback(error);
|
|
140
|
+
|
|
141
|
+
// Prevent further processing
|
|
142
|
+
return false;
|
|
143
|
+
}
|
|
144
|
+
}
|
|
145
|
+
);
|
|
146
|
+
|
|
147
|
+
if (serviceResult == null) return false;
|
|
148
|
+
|
|
149
|
+
if (!serviceResult.ok) {
|
|
150
|
+
if (failCallback) failCallback(serviceResult);
|
|
151
|
+
return false;
|
|
152
|
+
}
|
|
153
|
+
|
|
154
|
+
if (serviceResult.data == null) {
|
|
155
|
+
if (failCallback) failCallback(this.get('noData')!);
|
|
156
|
+
return false;
|
|
157
|
+
}
|
|
158
|
+
|
|
159
|
+
this.userLoginEx(serviceResult.data, refreshToken, userData);
|
|
160
|
+
|
|
161
|
+
return true;
|
|
162
|
+
};
|
|
163
|
+
|
|
107
164
|
// Call API
|
|
108
165
|
const result = await this.coreApi.put<SmartERPLoginResult>(
|
|
109
166
|
'Auth/RefreshToken',
|
|
110
|
-
|
|
167
|
+
rq,
|
|
111
168
|
payload
|
|
112
169
|
);
|
|
113
170
|
if (result == null) return false;
|
|
114
171
|
|
|
115
172
|
if (!result.ok) {
|
|
116
|
-
|
|
117
|
-
|
|
118
|
-
|
|
173
|
+
if (result.type === 'TokenExpired' && relogin) {
|
|
174
|
+
// Try login
|
|
175
|
+
// Dialog to receive password
|
|
176
|
+
var labels = this.getLabels('reloginTip', 'login');
|
|
177
|
+
this.notifier.prompt(
|
|
178
|
+
labels.reloginTip,
|
|
179
|
+
async (pwd) => {
|
|
180
|
+
if (pwd == null) {
|
|
181
|
+
this.toLoginPage();
|
|
182
|
+
return;
|
|
183
|
+
}
|
|
119
184
|
|
|
120
|
-
|
|
121
|
-
|
|
122
|
-
|
|
123
|
-
|
|
124
|
-
|
|
125
|
-
}
|
|
185
|
+
// Set password for the action
|
|
186
|
+
rq.pwd = this.encrypt(
|
|
187
|
+
pwd,
|
|
188
|
+
this.settings.serviceId.toString()
|
|
189
|
+
);
|
|
126
190
|
|
|
127
|
-
|
|
128
|
-
|
|
191
|
+
// Submit again
|
|
192
|
+
const result = await this.api.put<SmartERPLoginResult>(
|
|
193
|
+
'Auth/RefreshToken',
|
|
194
|
+
data,
|
|
195
|
+
payload
|
|
196
|
+
);
|
|
129
197
|
|
|
130
|
-
|
|
131
|
-
const serviceResult = await this.api.put<ServiceLoginResult>(
|
|
132
|
-
'Auth/ExchangeToken',
|
|
133
|
-
{
|
|
134
|
-
token: userData.token
|
|
135
|
-
},
|
|
136
|
-
{
|
|
137
|
-
onError: (error) => {
|
|
138
|
-
// Set message
|
|
139
|
-
this.tryLoginResult(error);
|
|
198
|
+
if (result == null) return;
|
|
140
199
|
|
|
141
|
-
|
|
142
|
-
|
|
143
|
-
|
|
144
|
-
|
|
145
|
-
|
|
200
|
+
if (result.ok) {
|
|
201
|
+
await success(
|
|
202
|
+
result,
|
|
203
|
+
(loginResult: RefreshTokenResult) => {
|
|
204
|
+
const message =
|
|
205
|
+
this.formatRefreshTokenResult(
|
|
206
|
+
loginResult
|
|
207
|
+
);
|
|
208
|
+
if (message) this.notifier.alert(message);
|
|
209
|
+
}
|
|
210
|
+
);
|
|
211
|
+
return;
|
|
212
|
+
}
|
|
146
213
|
|
|
147
|
-
|
|
214
|
+
// Popup message
|
|
215
|
+
this.alertResult(result);
|
|
216
|
+
return false;
|
|
217
|
+
},
|
|
218
|
+
labels.login,
|
|
219
|
+
{ type: 'password' }
|
|
220
|
+
);
|
|
148
221
|
|
|
149
|
-
|
|
150
|
-
|
|
151
|
-
|
|
152
|
-
}
|
|
222
|
+
// Fake truth to avoid reloading
|
|
223
|
+
return true;
|
|
224
|
+
}
|
|
153
225
|
|
|
154
|
-
|
|
155
|
-
this.tryLoginResult(this.get('noData')!);
|
|
226
|
+
if (callback) callback(result);
|
|
156
227
|
return false;
|
|
157
228
|
}
|
|
158
229
|
|
|
159
|
-
|
|
230
|
+
return await success(result, callback);
|
|
231
|
+
}
|
|
232
|
+
|
|
233
|
+
private loginFailed() {
|
|
234
|
+
this.userUnauthorized();
|
|
235
|
+
this.toLoginPage();
|
|
236
|
+
}
|
|
160
237
|
|
|
161
|
-
|
|
238
|
+
/**
|
|
239
|
+
* Try login
|
|
240
|
+
*/
|
|
241
|
+
override async tryLogin<D extends {} = {}>(data?: D) {
|
|
242
|
+
// Reset user state
|
|
243
|
+
const result = await super.tryLogin(data);
|
|
244
|
+
if (!result) return false;
|
|
245
|
+
|
|
246
|
+
// Refresh token
|
|
247
|
+
return await this.refreshToken({
|
|
248
|
+
callback: (result) => {
|
|
249
|
+
const message = this.formatRefreshTokenResult(result);
|
|
250
|
+
if (message == null) {
|
|
251
|
+
this.loginFailed();
|
|
252
|
+
return;
|
|
253
|
+
}
|
|
254
|
+
|
|
255
|
+
this.notifier.alert(message, () => {
|
|
256
|
+
this.loginFailed();
|
|
257
|
+
});
|
|
258
|
+
},
|
|
259
|
+
data,
|
|
260
|
+
relogin: true
|
|
261
|
+
});
|
|
162
262
|
}
|
|
163
263
|
|
|
164
264
|
/**
|