@etsoo/appscript 1.5.19 → 1.5.21

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.
@@ -88,6 +88,13 @@ class CoreAppTest extends CoreApp<
88
88
  */
89
89
  endpoint: 'http://{hostname}/com.etsoo.SmartERPApi/api/',
90
90
 
91
+ endpoints: {
92
+ core: {
93
+ endpoint: 'http://{hostname}:9001/api/',
94
+ webUrl: ''
95
+ }
96
+ },
97
+
91
98
  /**
92
99
  * App root url
93
100
  */
@@ -155,6 +162,16 @@ const appClass = EnhanceApp(CoreAppTest);
155
162
  const app = new appClass();
156
163
  app.changeCulture(app.settings.cultures[0]);
157
164
 
165
+ test('Test for domain replacement', () => {
166
+ expect(app.settings.endpoint).toBe(
167
+ 'http://localhost/com.etsoo.SmartERPApi/api/'
168
+ );
169
+
170
+ expect(app.settings.endpoints?.core.endpoint).toBe(
171
+ 'http://localhost:9001/api/'
172
+ );
173
+ });
174
+
158
175
  test('Test for properties', () => {
159
176
  expect(app.settings.currentRegion.label).toBe('中国大陆');
160
177
  });
@@ -9,6 +9,10 @@ import { IUser } from '../state/User';
9
9
  import { IAppSettings } from './AppSettings';
10
10
  import { FormatResultCustomCallback, IApp, IAppFields, IDetectIPCallback, NavigateOptions, RefreshTokenProps, RefreshTokenResult } from './IApp';
11
11
  import { UserRole } from './UserRole';
12
+ import { ExternalEndpoint } from './ExternalSettings';
13
+ import { ApiRefreshTokenDto } from '../erp/dto/ApiRefreshTokenDto';
14
+ type ApiRefreshTokenFunction = (api: IApi, token: string) => Promise<[string, number] | undefined>;
15
+ type ApiTaskData = [IApi, number, number, ApiRefreshTokenFunction, string?];
12
16
  /**
13
17
  * Core application interface
14
18
  */
@@ -124,10 +128,6 @@ export declare abstract class CoreApp<U extends IUser, S extends IAppSettings, N
124
128
  * Last called with token refresh
125
129
  */
126
130
  protected lastCalled: boolean;
127
- /**
128
- * Token refresh count down seed
129
- */
130
- protected refreshCountdownSeed: number;
131
131
  /**
132
132
  * Init call Api URL
133
133
  */
@@ -137,6 +137,8 @@ export declare abstract class CoreApp<U extends IUser, S extends IAppSettings, N
137
137
  */
138
138
  protected passphrase: string;
139
139
  private cachedRefreshToken?;
140
+ private apis;
141
+ private tasks;
140
142
  /**
141
143
  * Get persisted fields
142
144
  */
@@ -150,7 +152,7 @@ export declare abstract class CoreApp<U extends IUser, S extends IAppSettings, N
150
152
  * @param name Application name
151
153
  * @param debug Debug mode
152
154
  */
153
- protected constructor(settings: S, api: IApi, notifier: INotifier<N, C>, storage: IStorage, name: string, debug?: boolean);
155
+ protected constructor(settings: S, api: IApi | undefined | null, notifier: INotifier<N, C>, storage: IStorage, name: string, debug?: boolean);
154
156
  private getDeviceId;
155
157
  private resetKeys;
156
158
  /**
@@ -178,11 +180,32 @@ export declare abstract class CoreApp<U extends IUser, S extends IAppSettings, N
178
180
  * Persist settings to source when application exit
179
181
  */
180
182
  persist(): void;
183
+ /**
184
+ * Add scheduled task
185
+ * @param task Task, return false to stop
186
+ * @param seconds Interval in seconds
187
+ */
188
+ addTask(task: () => PromiseLike<void | false>, seconds: number): void;
189
+ /**
190
+ * Create API client, override to implement custom client creation by name
191
+ * @param name Client name
192
+ * @param item External endpoint item
193
+ * @returns Result
194
+ */
195
+ createApi(name: string, item: ExternalEndpoint, refresh?: (api: IApi, token: string) => Promise<[string, number] | undefined>): IApi<any>;
196
+ /**
197
+ * Update API token and expires
198
+ * @param name Api name
199
+ * @param token Refresh token
200
+ * @param seconds Access token expires in seconds
201
+ */
202
+ updateApi(name: string, token: string | undefined, seconds: number): void;
203
+ updateApi(data: ApiTaskData, token: string | undefined, seconds: number): void;
181
204
  /**
182
205
  * Setup Api
183
206
  * @param api Api
184
207
  */
185
- protected setApi(api: IApi): void;
208
+ protected setApi(api: IApi, refresh?: ApiRefreshTokenFunction): void;
186
209
  /**
187
210
  * Setup Api error handler
188
211
  * @param api Api
@@ -535,12 +558,6 @@ export declare abstract class CoreApp<U extends IUser, S extends IAppSettings, N
535
558
  * Callback where exit a page
536
559
  */
537
560
  pageExit(): void;
538
- /**
539
- * Refresh countdown
540
- * @param seconds Seconds
541
- */
542
- protected refreshCountdown(seconds: number): void;
543
- protected refreshCountdownClear(): void;
544
561
  /**
545
562
  * Fresh countdown UI
546
563
  * @param callback Callback
@@ -555,6 +572,35 @@ export declare abstract class CoreApp<U extends IUser, S extends IAppSettings, N
555
572
  * Setup callback
556
573
  */
557
574
  setup(): void;
575
+ /**
576
+ * Exchange token data
577
+ * @param api API
578
+ * @param token Core system's refresh token to exchange
579
+ * @returns Result
580
+ */
581
+ exchangeToken(api: IApi, token: string): Promise<void>;
582
+ /**
583
+ * Exchange token update, override to get the new token
584
+ * @param api API
585
+ * @param data API refresh token data
586
+ */
587
+ protected exchangeTokenUpdate(api: IApi, data: ApiRefreshTokenDto): void;
588
+ /**
589
+ * Exchange intergration tokens for all APIs
590
+ * @param token Core system's refresh token to exchange
591
+ */
592
+ exchangeTokenAll(token: string): void;
593
+ /**
594
+ * API refresh token
595
+ * @param api Current API
596
+ * @param token Refresh token
597
+ * @returns Result
598
+ */
599
+ protected apiRefreshToken(api: IApi, token: string): Promise<[string, number] | undefined>;
600
+ /**
601
+ * Setup tasks
602
+ */
603
+ protected setupTasks(): void;
558
604
  /**
559
605
  * Signout, with userLogout and toLoginPage
560
606
  * @param apiUrl Signout API URL
@@ -569,10 +615,9 @@ export declare abstract class CoreApp<U extends IUser, S extends IAppSettings, N
569
615
  /**
570
616
  * Try login, returning false means is loading
571
617
  * UI get involved while refreshToken not intended
572
- * @param data Additional request data
573
618
  * @param showLoading Show loading bar or not during call
574
619
  */
575
- tryLogin<D extends object = {}>(_data?: D, _showLoading?: boolean): Promise<boolean>;
620
+ tryLogin(_showLoading?: boolean): Promise<boolean>;
576
621
  /**
577
622
  * User login
578
623
  * @param user User data
@@ -598,3 +643,4 @@ export declare abstract class CoreApp<U extends IUser, S extends IAppSettings, N
598
643
  */
599
644
  warning(message: NotificationContent<N>, align?: NotificationAlign): void;
600
645
  }
646
+ export {};
@@ -115,10 +115,6 @@ class CoreApp {
115
115
  * Last called with token refresh
116
116
  */
117
117
  this.lastCalled = false;
118
- /**
119
- * Token refresh count down seed
120
- */
121
- this.refreshCountdownSeed = 0;
122
118
  /**
123
119
  * Init call Api URL
124
120
  */
@@ -127,6 +123,8 @@ class CoreApp {
127
123
  * Passphrase for encryption
128
124
  */
129
125
  this.passphrase = '';
126
+ this.apis = {};
127
+ this.tasks = [];
130
128
  if (settings?.regions?.length === 0) {
131
129
  throw new Error('No regions defined');
132
130
  }
@@ -136,7 +134,30 @@ class CoreApp {
136
134
  throw new Error('No default region defined');
137
135
  }
138
136
  this.defaultRegion = region;
139
- this.api = api;
137
+ const refresh = async (api, token) => {
138
+ if (this.lastCalled) {
139
+ // Call refreshToken to update access token
140
+ await this.refreshToken();
141
+ }
142
+ else {
143
+ // Popup countdown for user action
144
+ this.freshCountdownUI();
145
+ }
146
+ return undefined;
147
+ };
148
+ if (api) {
149
+ // Base URL of the API
150
+ api.baseUrl = this.settings.endpoint;
151
+ api.name = 'system';
152
+ this.setApi(api, refresh);
153
+ this.api = api;
154
+ }
155
+ else {
156
+ this.api = this.createApi('system', {
157
+ endpoint: settings.endpoint,
158
+ webUrl: settings.webUrl
159
+ }, refresh);
160
+ }
140
161
  this.notifier = notifier;
141
162
  this.storage = storage;
142
163
  this.name = name;
@@ -145,7 +166,6 @@ class CoreApp {
145
166
  this.fields = IApp_1.appFields.reduce((a, v) => ({ ...a, [v]: 'smarterp-' + v + '-' + name }), {});
146
167
  // Device id
147
168
  this._deviceId = storage.getData(this.fields.deviceId, '');
148
- this.setApi(api);
149
169
  const { currentCulture, currentRegion } = settings;
150
170
  // Load resources
151
171
  Promise.all([loadCrypto(), this.changeCulture(currentCulture)]).then(([cj, _resources]) => {
@@ -263,17 +283,56 @@ class CoreApp {
263
283
  return;
264
284
  this.storage.copyTo(this.persistedFields);
265
285
  }
286
+ /**
287
+ * Add scheduled task
288
+ * @param task Task, return false to stop
289
+ * @param seconds Interval in seconds
290
+ */
291
+ addTask(task, seconds) {
292
+ this.tasks.push([task, seconds, seconds]);
293
+ }
294
+ /**
295
+ * Create API client, override to implement custom client creation by name
296
+ * @param name Client name
297
+ * @param item External endpoint item
298
+ * @returns Result
299
+ */
300
+ createApi(name, item, refresh) {
301
+ if (this.apis[name] != null) {
302
+ throw new Error(`API ${name} already exists`);
303
+ }
304
+ const api = (0, restclient_1.createClient)();
305
+ api.name = name;
306
+ api.baseUrl = item.endpoint;
307
+ this.setApi(api, refresh);
308
+ return api;
309
+ }
310
+ updateApi(nameOrData, token, seconds) {
311
+ const api = typeof nameOrData === 'string' ? this.apis[nameOrData] : nameOrData;
312
+ if (api == null)
313
+ return;
314
+ // Consider the API call delay
315
+ if (seconds > 0) {
316
+ seconds -= 30;
317
+ if (seconds < 10)
318
+ seconds = 10;
319
+ }
320
+ api[1] = seconds;
321
+ api[2] = seconds;
322
+ api[4] = token;
323
+ }
266
324
  /**
267
325
  * Setup Api
268
326
  * @param api Api
269
327
  */
270
- setApi(api) {
271
- // Base URL of the API
272
- api.baseUrl = this.settings.endpoint;
328
+ setApi(api, refresh) {
273
329
  // onRequest, show loading or not, rewrite the property to override default action
274
330
  this.setApiLoading(api);
275
331
  // Global API error handler
276
332
  this.setApiErrorHandler(api);
333
+ // Setup API countdown
334
+ refresh ?? (refresh = this.apiRefreshToken.bind(this));
335
+ this.apis[api.name] = [api, -1, -1, refresh];
277
336
  }
278
337
  /**
279
338
  * Setup Api error handler
@@ -284,7 +343,7 @@ class CoreApp {
284
343
  api.onError = (error) => {
285
344
  // Debug
286
345
  if (this.debug) {
287
- console.debug('CoreApp.setApiErrorHandler', api, error, handlerFor401);
346
+ console.debug(`CoreApp.${this.name}.setApiErrorHandler`, api, error, handlerFor401);
288
347
  }
289
348
  // Error code
290
349
  const status = error.response
@@ -306,12 +365,12 @@ class CoreApp {
306
365
  (error.message === 'Network Error' ||
307
366
  error.message === 'Failed to fetch')) {
308
367
  // Network error
309
- this.notifier.alert(this.get('networkError'));
368
+ this.notifier.alert(this.get('networkError') + ` [${this.name}]`);
310
369
  return;
311
370
  }
312
371
  else {
313
372
  // Log
314
- console.error('API error', error);
373
+ console.error(`${this.name} API error`, error);
315
374
  }
316
375
  // Report the error
317
376
  this.notifier.alert(this.formatError(error));
@@ -326,7 +385,7 @@ class CoreApp {
326
385
  api.onRequest = (data) => {
327
386
  // Debug
328
387
  if (this.debug) {
329
- console.debug('CoreApp.setApiLoading.onRequest', api, data, this.notifier.loadingCount);
388
+ console.debug(`CoreApp.${this.name}.setApiLoading.onRequest`, api, data, this.notifier.loadingCount);
330
389
  }
331
390
  if (data.showLoading == null || data.showLoading) {
332
391
  this.notifier.showLoading();
@@ -336,13 +395,13 @@ class CoreApp {
336
395
  api.onComplete = (data) => {
337
396
  // Debug
338
397
  if (this.debug) {
339
- console.debug('CoreApp.setApiLoading.onComplete', api, data, this.notifier.loadingCount, this.lastCalled);
398
+ console.debug(`CoreApp.${this.name}.setApiLoading.onComplete`, api, data, this.notifier.loadingCount, this.lastCalled);
340
399
  }
341
400
  if (data.showLoading == null || data.showLoading) {
342
401
  this.notifier.hideLoading();
343
402
  // Debug
344
403
  if (this.debug) {
345
- console.debug('CoreApp.setApiLoading.onComplete.showLoading', api, this.notifier.loadingCount);
404
+ console.debug(`CoreApp.${this.name}.setApiLoading.onComplete.showLoading`, api, this.notifier.loadingCount);
346
405
  }
347
406
  }
348
407
  this.lastCalled = true;
@@ -553,11 +612,15 @@ class CoreApp {
553
612
  // Reset tryLogin state
554
613
  this._isTryingLogin = false;
555
614
  // Token countdown
556
- if (this.authorized)
557
- this.refreshCountdown(this.userData.seconds);
615
+ if (this.authorized) {
616
+ this.lastCalled = false;
617
+ if (refreshToken) {
618
+ this.updateApi(this.api.name, refreshToken, this.userData.seconds);
619
+ }
620
+ }
558
621
  else {
559
622
  this.cachedRefreshToken = undefined;
560
- this.refreshCountdownClear();
623
+ this.updateApi(this.api.name, undefined, -1);
561
624
  }
562
625
  // Host notice
563
626
  BridgeUtils_1.BridgeUtils.host?.userAuthorization(this.authorized);
@@ -1289,40 +1352,6 @@ class CoreApp {
1289
1352
  this.lastWarning?.dismiss();
1290
1353
  this.notifier.hideLoading(true);
1291
1354
  }
1292
- /**
1293
- * Refresh countdown
1294
- * @param seconds Seconds
1295
- */
1296
- refreshCountdown(seconds) {
1297
- // Make sure is big than 60 seconds
1298
- // Take action 60 seconds before expiry
1299
- seconds -= 60;
1300
- if (seconds <= 0)
1301
- return;
1302
- // Clear the current timeout seed
1303
- this.refreshCountdownClear();
1304
- // Reset last call flag
1305
- // Any success call will update it to true
1306
- // So first time after login will be always silent
1307
- this.lastCalled = false;
1308
- this.refreshCountdownSeed = window.setTimeout(() => {
1309
- if (this.lastCalled) {
1310
- // Call refreshToken to update access token
1311
- this.refreshToken();
1312
- }
1313
- else {
1314
- // Popup countdown for user action
1315
- this.freshCountdownUI();
1316
- }
1317
- }, 1000 * seconds);
1318
- }
1319
- refreshCountdownClear() {
1320
- // Clear the current timeout seed
1321
- if (this.refreshCountdownSeed > 0) {
1322
- window.clearTimeout(this.refreshCountdownSeed);
1323
- this.refreshCountdownSeed = 0;
1324
- }
1325
- }
1326
1355
  /**
1327
1356
  * Refresh token
1328
1357
  * @param props Props
@@ -1336,12 +1365,142 @@ class CoreApp {
1336
1365
  * Setup callback
1337
1366
  */
1338
1367
  setup() {
1368
+ // Done already
1369
+ if (this.isReady)
1370
+ return;
1339
1371
  // Ready
1340
1372
  this.isReady = true;
1341
1373
  // Restore
1342
1374
  this.restore();
1343
1375
  // Pending actions
1344
1376
  this.pendings.forEach((p) => p());
1377
+ // Setup scheduled tasks
1378
+ this.setupTasks();
1379
+ }
1380
+ /**
1381
+ * Exchange token data
1382
+ * @param api API
1383
+ * @param token Core system's refresh token to exchange
1384
+ * @returns Result
1385
+ */
1386
+ async exchangeToken(api, token) {
1387
+ // Call the API quietly, no loading bar and no error popup
1388
+ const data = await api.put('Auth/ExchangeToken', {
1389
+ token
1390
+ }, {
1391
+ showLoading: false,
1392
+ onError: (error) => {
1393
+ console.error(`CoreApp.${api.name}.ExchangeToken error`, error);
1394
+ // Prevent further processing
1395
+ return false;
1396
+ }
1397
+ });
1398
+ if (data) {
1399
+ // Update the access token
1400
+ api.authorize(data.tokenType, data.accessToken);
1401
+ // Update the API
1402
+ this.updateApi(api.name, data.refreshToken, data.expiresIn);
1403
+ // Update notice
1404
+ this.exchangeTokenUpdate(api, data);
1405
+ }
1406
+ }
1407
+ /**
1408
+ * Exchange token update, override to get the new token
1409
+ * @param api API
1410
+ * @param data API refresh token data
1411
+ */
1412
+ exchangeTokenUpdate(api, data) { }
1413
+ /**
1414
+ * Exchange intergration tokens for all APIs
1415
+ * @param token Core system's refresh token to exchange
1416
+ */
1417
+ exchangeTokenAll(token) {
1418
+ for (const name in this.apis) {
1419
+ const api = this.apis[name];
1420
+ this.exchangeToken(api[0], token);
1421
+ }
1422
+ }
1423
+ /**
1424
+ * API refresh token
1425
+ * @param api Current API
1426
+ * @param token Refresh token
1427
+ * @returns Result
1428
+ */
1429
+ async apiRefreshToken(api, token) {
1430
+ // Call the API quietly, no loading bar and no error popup
1431
+ const data = await api.put('Auth/ApiRefreshToken', {
1432
+ token
1433
+ }, {
1434
+ showLoading: false,
1435
+ onError: (error) => {
1436
+ console.error(`CoreApp.${api.name}.apiRefreshToken error`, error);
1437
+ // Prevent further processing
1438
+ return false;
1439
+ }
1440
+ });
1441
+ if (data == null)
1442
+ return undefined;
1443
+ // Update the access token
1444
+ api.authorize(data.tokenType, data.accessToken);
1445
+ // Return the new refresh token and access token expiration seconds
1446
+ return [data.refreshToken, data.expiresIn];
1447
+ }
1448
+ /**
1449
+ * Setup tasks
1450
+ */
1451
+ setupTasks() {
1452
+ shared_1.ExtendUtils.intervalFor(() => {
1453
+ // Exit when not authorized
1454
+ if (!this.authorized)
1455
+ return;
1456
+ // APIs
1457
+ for (const name in this.apis) {
1458
+ // Get the API
1459
+ const api = this.apis[name];
1460
+ // Skip the negative value or when refresh token is not set
1461
+ if (!api[4] || api[2] < 0)
1462
+ continue;
1463
+ // Minus one second
1464
+ api[2] -= 1;
1465
+ // Ready to trigger
1466
+ if (api[2] === 0) {
1467
+ // Refresh token
1468
+ api[3](api[0], api[4]).then((data) => {
1469
+ if (data == null) {
1470
+ // Failed, try it again in 2 seconds
1471
+ api[2] = 2;
1472
+ }
1473
+ else {
1474
+ // Reset the API
1475
+ const [token, seconds] = data;
1476
+ this.updateApi(api, token, seconds);
1477
+ }
1478
+ });
1479
+ }
1480
+ }
1481
+ for (let t = this.tasks.length - 1; t >= 0; t--) {
1482
+ // Get the task
1483
+ const task = this.tasks[t];
1484
+ // Minus one second
1485
+ task[2] -= 1;
1486
+ // Remove the tasks with negative value with splice
1487
+ if (task[2] < 0) {
1488
+ this.tasks.splice(t, 1);
1489
+ }
1490
+ else if (task[2] === 0) {
1491
+ // Ready to trigger
1492
+ // Reset the task
1493
+ task[2] = task[1];
1494
+ // Trigger the task
1495
+ task[0]().then((result) => {
1496
+ if (result === false) {
1497
+ // Asynchronous task, unsafe to splice the index, flag as pending
1498
+ task[2] = -1;
1499
+ }
1500
+ });
1501
+ }
1502
+ }
1503
+ }, 1000);
1345
1504
  }
1346
1505
  /**
1347
1506
  * Signout, with userLogout and toLoginPage
@@ -1373,10 +1532,9 @@ class CoreApp {
1373
1532
  /**
1374
1533
  * Try login, returning false means is loading
1375
1534
  * UI get involved while refreshToken not intended
1376
- * @param data Additional request data
1377
1535
  * @param showLoading Show loading bar or not during call
1378
1536
  */
1379
- async tryLogin(_data, _showLoading) {
1537
+ async tryLogin(_showLoading) {
1380
1538
  if (this._isTryingLogin)
1381
1539
  return false;
1382
1540
  this._isTryingLogin = true;
@@ -1,31 +1,32 @@
1
1
  /**
2
- * External settings items
2
+ * External endpoint
3
3
  */
4
- export interface IExternalSettings {
4
+ export type ExternalEndpoint = {
5
5
  /**
6
- * Core system API endpoint
6
+ * API endpoint
7
7
  */
8
8
  readonly endpoint: string;
9
+ /**
10
+ * Web url
11
+ */
12
+ readonly webUrl: string;
13
+ };
14
+ /**
15
+ * External settings items
16
+ */
17
+ export interface IExternalSettings extends ExternalEndpoint {
9
18
  /**
10
19
  * Message hub endpoint
11
20
  */
12
21
  readonly messageHub?: string;
13
22
  /**
14
- * Core system app root url
23
+ * App root url
15
24
  */
16
25
  readonly homepage: string;
17
26
  /**
18
- * Core system web url
19
- */
20
- readonly webUrl: string;
21
- /**
22
- * Service API endpoint
23
- */
24
- readonly serviceEndpoint?: string;
25
- /**
26
- * Service web Url
27
+ * Endpoints to other services
27
28
  */
28
- readonly serviceUrl?: string;
29
+ readonly endpoints?: Record<'core' | 'accounting' | 'crm' | 'calandar' | 'task' | string, ExternalEndpoint>;
29
30
  }
30
31
  /**
31
32
  * External settings namespace
@@ -35,6 +35,9 @@ var ExternalSettings;
35
35
  if (typeof value === 'string') {
36
36
  settings[key] = value.replace('{hostname}', hostname);
37
37
  }
38
+ else if (typeof value === 'object') {
39
+ format(value, hostname);
40
+ }
38
41
  }
39
42
  return settings;
40
43
  }
@@ -7,6 +7,7 @@ import { IAppSettings } from './AppSettings';
7
7
  import { UserRole } from './UserRole';
8
8
  import { EntityStatus } from '../business/EntityStatus';
9
9
  import { Currency } from '../business/Currency';
10
+ import { ExternalEndpoint } from './ExternalSettings';
10
11
  /**
11
12
  * Detect IP callback interface
12
13
  */
@@ -155,6 +156,12 @@ export interface IApp {
155
156
  * @returns Result
156
157
  */
157
158
  addRootUrl(url: string): string;
159
+ /**
160
+ * Add scheduled task
161
+ * @param task Task, return false to stop
162
+ * @param interval Interval in milliseconds
163
+ */
164
+ addTask(task: () => PromiseLike<void | false>, interval: number): void;
158
165
  /**
159
166
  * Alert result
160
167
  * @param result Result message
@@ -209,6 +216,13 @@ export interface IApp {
209
216
  * Clear device id
210
217
  */
211
218
  clearDeviceId(): void;
219
+ /**
220
+ * Create API client, override to implement custom client creation by name
221
+ * @param name Client name
222
+ * @param item External endpoint item
223
+ * @returns Result
224
+ */
225
+ createApi(name: string, item: ExternalEndpoint, refresh?: (api: IApi, token: string) => Promise<[string, number] | undefined>): IApi;
212
226
  /**
213
227
  * Decrypt message
214
228
  * @param messageEncrypted Encrypted message
@@ -252,6 +266,18 @@ export interface IApp {
252
266
  * @returns Result
253
267
  */
254
268
  encryptEnhanced(message: string, passphrase?: string, iterations?: number): string;
269
+ /**
270
+ * Exchange token data
271
+ * @param api API
272
+ * @param token Core system's refresh token to exchange
273
+ * @returns Result
274
+ */
275
+ exchangeToken(api: IApi, token: string): Promise<void>;
276
+ /**
277
+ * Exchange intergration tokens for all APIs
278
+ * @param token Core system's refresh token to exchange
279
+ */
280
+ exchangeTokenAll(token: string): void;
255
281
  /**
256
282
  * Format date to string
257
283
  * @param input Input date
@@ -505,9 +531,16 @@ export interface IApp {
505
531
  /**
506
532
  * Try login, returning false means is loading
507
533
  * UI get involved while refreshToken not intended
508
- * @param data Additional request data
534
+ * @param showLoading Show loading bar or not
535
+ */
536
+ tryLogin(showLoading?: boolean): Promise<boolean>;
537
+ /**
538
+ * Update API token and expires
539
+ * @param name Api name
540
+ * @param token Refresh token
541
+ * @param seconds Access token expires in seconds
509
542
  */
510
- tryLogin<D extends object = {}>(data?: D): Promise<boolean>;
543
+ updateApi(name: string, token: string | undefined, seconds: number): void;
511
544
  /**
512
545
  * User login
513
546
  * @param user User data
@@ -54,7 +54,7 @@ class AuthApi extends BaseApi_1.BaseApi {
54
54
  const url = await this.getLogInUrl();
55
55
  if (url == null)
56
56
  return;
57
- window.location.replace(url);
57
+ globalThis.location.replace(url);
58
58
  }
59
59
  /**
60
60
  * Reset password