@etsoo/appscript 1.5.20 → 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.
@@ -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
@@ -597,3 +643,4 @@ export declare abstract class CoreApp<U extends IUser, S extends IAppSettings, N
597
643
  */
598
644
  warning(message: NotificationContent<N>, align?: NotificationAlign): void;
599
645
  }
646
+ export {};
@@ -1,6 +1,6 @@
1
1
  import { NotificationAlign, NotificationMessageType } from '@etsoo/notificationbase';
2
- import { ApiDataError } from '@etsoo/restclient';
3
- import { DataTypes, DateUtils, DomUtils, NumberUtils, Utils } from '@etsoo/shared';
2
+ import { ApiDataError, createClient } from '@etsoo/restclient';
3
+ import { DataTypes, DateUtils, DomUtils, ExtendUtils, NumberUtils, Utils } from '@etsoo/shared';
4
4
  import { AddressRegion } from '../address/AddressRegion';
5
5
  import { BridgeUtils } from '../bridges/BridgeUtils';
6
6
  import { DataPrivacy } from '../business/DataPrivacy';
@@ -112,10 +112,6 @@ export class CoreApp {
112
112
  * Last called with token refresh
113
113
  */
114
114
  this.lastCalled = false;
115
- /**
116
- * Token refresh count down seed
117
- */
118
- this.refreshCountdownSeed = 0;
119
115
  /**
120
116
  * Init call Api URL
121
117
  */
@@ -124,6 +120,8 @@ export class CoreApp {
124
120
  * Passphrase for encryption
125
121
  */
126
122
  this.passphrase = '';
123
+ this.apis = {};
124
+ this.tasks = [];
127
125
  if (settings?.regions?.length === 0) {
128
126
  throw new Error('No regions defined');
129
127
  }
@@ -133,7 +131,30 @@ export class CoreApp {
133
131
  throw new Error('No default region defined');
134
132
  }
135
133
  this.defaultRegion = region;
136
- this.api = api;
134
+ const refresh = async (api, token) => {
135
+ if (this.lastCalled) {
136
+ // Call refreshToken to update access token
137
+ await this.refreshToken();
138
+ }
139
+ else {
140
+ // Popup countdown for user action
141
+ this.freshCountdownUI();
142
+ }
143
+ return undefined;
144
+ };
145
+ if (api) {
146
+ // Base URL of the API
147
+ api.baseUrl = this.settings.endpoint;
148
+ api.name = 'system';
149
+ this.setApi(api, refresh);
150
+ this.api = api;
151
+ }
152
+ else {
153
+ this.api = this.createApi('system', {
154
+ endpoint: settings.endpoint,
155
+ webUrl: settings.webUrl
156
+ }, refresh);
157
+ }
137
158
  this.notifier = notifier;
138
159
  this.storage = storage;
139
160
  this.name = name;
@@ -142,7 +163,6 @@ export class CoreApp {
142
163
  this.fields = appFields.reduce((a, v) => ({ ...a, [v]: 'smarterp-' + v + '-' + name }), {});
143
164
  // Device id
144
165
  this._deviceId = storage.getData(this.fields.deviceId, '');
145
- this.setApi(api);
146
166
  const { currentCulture, currentRegion } = settings;
147
167
  // Load resources
148
168
  Promise.all([loadCrypto(), this.changeCulture(currentCulture)]).then(([cj, _resources]) => {
@@ -260,17 +280,56 @@ export class CoreApp {
260
280
  return;
261
281
  this.storage.copyTo(this.persistedFields);
262
282
  }
283
+ /**
284
+ * Add scheduled task
285
+ * @param task Task, return false to stop
286
+ * @param seconds Interval in seconds
287
+ */
288
+ addTask(task, seconds) {
289
+ this.tasks.push([task, seconds, seconds]);
290
+ }
291
+ /**
292
+ * Create API client, override to implement custom client creation by name
293
+ * @param name Client name
294
+ * @param item External endpoint item
295
+ * @returns Result
296
+ */
297
+ createApi(name, item, refresh) {
298
+ if (this.apis[name] != null) {
299
+ throw new Error(`API ${name} already exists`);
300
+ }
301
+ const api = createClient();
302
+ api.name = name;
303
+ api.baseUrl = item.endpoint;
304
+ this.setApi(api, refresh);
305
+ return api;
306
+ }
307
+ updateApi(nameOrData, token, seconds) {
308
+ const api = typeof nameOrData === 'string' ? this.apis[nameOrData] : nameOrData;
309
+ if (api == null)
310
+ return;
311
+ // Consider the API call delay
312
+ if (seconds > 0) {
313
+ seconds -= 30;
314
+ if (seconds < 10)
315
+ seconds = 10;
316
+ }
317
+ api[1] = seconds;
318
+ api[2] = seconds;
319
+ api[4] = token;
320
+ }
263
321
  /**
264
322
  * Setup Api
265
323
  * @param api Api
266
324
  */
267
- setApi(api) {
268
- // Base URL of the API
269
- api.baseUrl = this.settings.endpoint;
325
+ setApi(api, refresh) {
270
326
  // onRequest, show loading or not, rewrite the property to override default action
271
327
  this.setApiLoading(api);
272
328
  // Global API error handler
273
329
  this.setApiErrorHandler(api);
330
+ // Setup API countdown
331
+ refresh ?? (refresh = this.apiRefreshToken.bind(this));
332
+ this.apis[api.name] = [api, -1, -1, refresh];
274
333
  }
275
334
  /**
276
335
  * Setup Api error handler
@@ -281,7 +340,7 @@ export class CoreApp {
281
340
  api.onError = (error) => {
282
341
  // Debug
283
342
  if (this.debug) {
284
- console.debug('CoreApp.setApiErrorHandler', api, error, handlerFor401);
343
+ console.debug(`CoreApp.${this.name}.setApiErrorHandler`, api, error, handlerFor401);
285
344
  }
286
345
  // Error code
287
346
  const status = error.response
@@ -303,12 +362,12 @@ export class CoreApp {
303
362
  (error.message === 'Network Error' ||
304
363
  error.message === 'Failed to fetch')) {
305
364
  // Network error
306
- this.notifier.alert(this.get('networkError'));
365
+ this.notifier.alert(this.get('networkError') + ` [${this.name}]`);
307
366
  return;
308
367
  }
309
368
  else {
310
369
  // Log
311
- console.error('API error', error);
370
+ console.error(`${this.name} API error`, error);
312
371
  }
313
372
  // Report the error
314
373
  this.notifier.alert(this.formatError(error));
@@ -323,7 +382,7 @@ export class CoreApp {
323
382
  api.onRequest = (data) => {
324
383
  // Debug
325
384
  if (this.debug) {
326
- console.debug('CoreApp.setApiLoading.onRequest', api, data, this.notifier.loadingCount);
385
+ console.debug(`CoreApp.${this.name}.setApiLoading.onRequest`, api, data, this.notifier.loadingCount);
327
386
  }
328
387
  if (data.showLoading == null || data.showLoading) {
329
388
  this.notifier.showLoading();
@@ -333,13 +392,13 @@ export class CoreApp {
333
392
  api.onComplete = (data) => {
334
393
  // Debug
335
394
  if (this.debug) {
336
- console.debug('CoreApp.setApiLoading.onComplete', api, data, this.notifier.loadingCount, this.lastCalled);
395
+ console.debug(`CoreApp.${this.name}.setApiLoading.onComplete`, api, data, this.notifier.loadingCount, this.lastCalled);
337
396
  }
338
397
  if (data.showLoading == null || data.showLoading) {
339
398
  this.notifier.hideLoading();
340
399
  // Debug
341
400
  if (this.debug) {
342
- console.debug('CoreApp.setApiLoading.onComplete.showLoading', api, this.notifier.loadingCount);
401
+ console.debug(`CoreApp.${this.name}.setApiLoading.onComplete.showLoading`, api, this.notifier.loadingCount);
343
402
  }
344
403
  }
345
404
  this.lastCalled = true;
@@ -550,11 +609,15 @@ export class CoreApp {
550
609
  // Reset tryLogin state
551
610
  this._isTryingLogin = false;
552
611
  // Token countdown
553
- if (this.authorized)
554
- this.refreshCountdown(this.userData.seconds);
612
+ if (this.authorized) {
613
+ this.lastCalled = false;
614
+ if (refreshToken) {
615
+ this.updateApi(this.api.name, refreshToken, this.userData.seconds);
616
+ }
617
+ }
555
618
  else {
556
619
  this.cachedRefreshToken = undefined;
557
- this.refreshCountdownClear();
620
+ this.updateApi(this.api.name, undefined, -1);
558
621
  }
559
622
  // Host notice
560
623
  BridgeUtils.host?.userAuthorization(this.authorized);
@@ -1286,40 +1349,6 @@ export class CoreApp {
1286
1349
  this.lastWarning?.dismiss();
1287
1350
  this.notifier.hideLoading(true);
1288
1351
  }
1289
- /**
1290
- * Refresh countdown
1291
- * @param seconds Seconds
1292
- */
1293
- refreshCountdown(seconds) {
1294
- // Make sure is big than 60 seconds
1295
- // Take action 60 seconds before expiry
1296
- seconds -= 60;
1297
- if (seconds <= 0)
1298
- return;
1299
- // Clear the current timeout seed
1300
- this.refreshCountdownClear();
1301
- // Reset last call flag
1302
- // Any success call will update it to true
1303
- // So first time after login will be always silent
1304
- this.lastCalled = false;
1305
- this.refreshCountdownSeed = window.setTimeout(() => {
1306
- if (this.lastCalled) {
1307
- // Call refreshToken to update access token
1308
- this.refreshToken();
1309
- }
1310
- else {
1311
- // Popup countdown for user action
1312
- this.freshCountdownUI();
1313
- }
1314
- }, 1000 * seconds);
1315
- }
1316
- refreshCountdownClear() {
1317
- // Clear the current timeout seed
1318
- if (this.refreshCountdownSeed > 0) {
1319
- window.clearTimeout(this.refreshCountdownSeed);
1320
- this.refreshCountdownSeed = 0;
1321
- }
1322
- }
1323
1352
  /**
1324
1353
  * Refresh token
1325
1354
  * @param props Props
@@ -1333,12 +1362,142 @@ export class CoreApp {
1333
1362
  * Setup callback
1334
1363
  */
1335
1364
  setup() {
1365
+ // Done already
1366
+ if (this.isReady)
1367
+ return;
1336
1368
  // Ready
1337
1369
  this.isReady = true;
1338
1370
  // Restore
1339
1371
  this.restore();
1340
1372
  // Pending actions
1341
1373
  this.pendings.forEach((p) => p());
1374
+ // Setup scheduled tasks
1375
+ this.setupTasks();
1376
+ }
1377
+ /**
1378
+ * Exchange token data
1379
+ * @param api API
1380
+ * @param token Core system's refresh token to exchange
1381
+ * @returns Result
1382
+ */
1383
+ async exchangeToken(api, token) {
1384
+ // Call the API quietly, no loading bar and no error popup
1385
+ const data = await api.put('Auth/ExchangeToken', {
1386
+ token
1387
+ }, {
1388
+ showLoading: false,
1389
+ onError: (error) => {
1390
+ console.error(`CoreApp.${api.name}.ExchangeToken error`, error);
1391
+ // Prevent further processing
1392
+ return false;
1393
+ }
1394
+ });
1395
+ if (data) {
1396
+ // Update the access token
1397
+ api.authorize(data.tokenType, data.accessToken);
1398
+ // Update the API
1399
+ this.updateApi(api.name, data.refreshToken, data.expiresIn);
1400
+ // Update notice
1401
+ this.exchangeTokenUpdate(api, data);
1402
+ }
1403
+ }
1404
+ /**
1405
+ * Exchange token update, override to get the new token
1406
+ * @param api API
1407
+ * @param data API refresh token data
1408
+ */
1409
+ exchangeTokenUpdate(api, data) { }
1410
+ /**
1411
+ * Exchange intergration tokens for all APIs
1412
+ * @param token Core system's refresh token to exchange
1413
+ */
1414
+ exchangeTokenAll(token) {
1415
+ for (const name in this.apis) {
1416
+ const api = this.apis[name];
1417
+ this.exchangeToken(api[0], token);
1418
+ }
1419
+ }
1420
+ /**
1421
+ * API refresh token
1422
+ * @param api Current API
1423
+ * @param token Refresh token
1424
+ * @returns Result
1425
+ */
1426
+ async apiRefreshToken(api, token) {
1427
+ // Call the API quietly, no loading bar and no error popup
1428
+ const data = await api.put('Auth/ApiRefreshToken', {
1429
+ token
1430
+ }, {
1431
+ showLoading: false,
1432
+ onError: (error) => {
1433
+ console.error(`CoreApp.${api.name}.apiRefreshToken error`, error);
1434
+ // Prevent further processing
1435
+ return false;
1436
+ }
1437
+ });
1438
+ if (data == null)
1439
+ return undefined;
1440
+ // Update the access token
1441
+ api.authorize(data.tokenType, data.accessToken);
1442
+ // Return the new refresh token and access token expiration seconds
1443
+ return [data.refreshToken, data.expiresIn];
1444
+ }
1445
+ /**
1446
+ * Setup tasks
1447
+ */
1448
+ setupTasks() {
1449
+ ExtendUtils.intervalFor(() => {
1450
+ // Exit when not authorized
1451
+ if (!this.authorized)
1452
+ return;
1453
+ // APIs
1454
+ for (const name in this.apis) {
1455
+ // Get the API
1456
+ const api = this.apis[name];
1457
+ // Skip the negative value or when refresh token is not set
1458
+ if (!api[4] || api[2] < 0)
1459
+ continue;
1460
+ // Minus one second
1461
+ api[2] -= 1;
1462
+ // Ready to trigger
1463
+ if (api[2] === 0) {
1464
+ // Refresh token
1465
+ api[3](api[0], api[4]).then((data) => {
1466
+ if (data == null) {
1467
+ // Failed, try it again in 2 seconds
1468
+ api[2] = 2;
1469
+ }
1470
+ else {
1471
+ // Reset the API
1472
+ const [token, seconds] = data;
1473
+ this.updateApi(api, token, seconds);
1474
+ }
1475
+ });
1476
+ }
1477
+ }
1478
+ for (let t = this.tasks.length - 1; t >= 0; t--) {
1479
+ // Get the task
1480
+ const task = this.tasks[t];
1481
+ // Minus one second
1482
+ task[2] -= 1;
1483
+ // Remove the tasks with negative value with splice
1484
+ if (task[2] < 0) {
1485
+ this.tasks.splice(t, 1);
1486
+ }
1487
+ else if (task[2] === 0) {
1488
+ // Ready to trigger
1489
+ // Reset the task
1490
+ task[2] = task[1];
1491
+ // Trigger the task
1492
+ task[0]().then((result) => {
1493
+ if (result === false) {
1494
+ // Asynchronous task, unsafe to splice the index, flag as pending
1495
+ task[2] = -1;
1496
+ }
1497
+ });
1498
+ }
1499
+ }
1500
+ }, 1000);
1342
1501
  }
1343
1502
  /**
1344
1503
  * Signout, with userLogout and toLoginPage
@@ -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
@@ -32,6 +32,9 @@ export var ExternalSettings;
32
32
  if (typeof value === 'string') {
33
33
  settings[key] = value.replace('{hostname}', hostname);
34
34
  }
35
+ else if (typeof value === 'object') {
36
+ format(value, hostname);
37
+ }
35
38
  }
36
39
  return settings;
37
40
  }
@@ -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
@@ -508,6 +534,13 @@ export interface IApp {
508
534
  * @param showLoading Show loading bar or not
509
535
  */
510
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
542
+ */
543
+ updateApi(name: string, token: string | undefined, seconds: number): void;
511
544
  /**
512
545
  * User login
513
546
  * @param user User data
@@ -51,7 +51,7 @@ export class AuthApi extends BaseApi {
51
51
  const url = await this.getLogInUrl();
52
52
  if (url == null)
53
53
  return;
54
- window.location.replace(url);
54
+ globalThis.location.replace(url);
55
55
  }
56
56
  /**
57
57
  * Reset password
@@ -0,0 +1,21 @@
1
+ /**
2
+ * API refresh token data
3
+ */
4
+ export type ApiRefreshTokenDto = {
5
+ /**
6
+ * Refresh token
7
+ */
8
+ readonly refreshToken: string;
9
+ /**
10
+ * Access token
11
+ */
12
+ readonly accessToken: string;
13
+ /**
14
+ * Token type
15
+ */
16
+ readonly tokenType: string;
17
+ /**
18
+ * Expires in
19
+ */
20
+ readonly expiresIn: number;
21
+ };
@@ -0,0 +1 @@
1
+ export {};