@brggroup/share-lib 0.0.29 → 0.0.31

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.
@@ -5,7 +5,7 @@ import { Router, ActivatedRoute, NavigationEnd } from '@angular/router';
5
5
  import { HttpClient, HttpHeaders, HttpParams } from '@angular/common/http';
6
6
  import * as i3 from '@ngx-translate/core';
7
7
  import { TranslateService, TranslateModule } from '@ngx-translate/core';
8
- import { firstValueFrom, BehaviorSubject, of, catchError, EMPTY, throwError, finalize, filter, fromEvent } from 'rxjs';
8
+ import { firstValueFrom, BehaviorSubject, of, catchError, throwError, finalize, from, switchMap as switchMap$1, EMPTY, filter, take, fromEvent } from 'rxjs';
9
9
  import { NzNotificationService } from 'ng-zorro-antd/notification';
10
10
  import * as i1 from 'ng-zorro-antd/modal';
11
11
  import { NzModalService } from 'ng-zorro-antd/modal';
@@ -409,6 +409,7 @@ i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "19.1.7", ngImpor
409
409
 
410
410
  class TokenStorage {
411
411
  static JWT_TOKEN = 'JWT_TOKEN';
412
+ static JWT_RFT_TOKEN = 'JWT_RFT_TOKEN';
412
413
  static USER_NAME = 'username';
413
414
  static FULL_NAME = 'fullname';
414
415
  static USER_MENU = 'usermenu';
@@ -417,7 +418,15 @@ class TokenStorage {
417
418
  static ORG_NAME = 'orgname';
418
419
  static saveToken(tokenRes) {
419
420
  AppStorage.tokenStorage.setItem(this.JWT_TOKEN, tokenRes.Data.Token);
420
- const userInfo = jwtDecode(tokenRes.Data.Token);
421
+ AppStorage.tokenStorage.setItem(this.JWT_RFT_TOKEN, tokenRes.Data.RefreshToken);
422
+ TokenStorage.saveTokenInfo(tokenRes.Data.Token);
423
+ }
424
+ static saveTokenRft(token) {
425
+ AppStorage.tokenStorage.setItem(this.JWT_TOKEN, token);
426
+ TokenStorage.saveTokenInfo(token);
427
+ }
428
+ static saveTokenInfo(token) {
429
+ const userInfo = jwtDecode(token);
421
430
  AppStorage.tokenStorage.setItem(this.USER_NAME, userInfo[this.USER_NAME]);
422
431
  AppStorage.tokenStorage.setItem(this.FULL_NAME, userInfo[this.FULL_NAME]);
423
432
  AppStorage.tokenStorage.setItem(this.ORG_ID, userInfo[this.ORG_ID]);
@@ -448,6 +457,9 @@ class TokenStorage {
448
457
  static getToken() {
449
458
  return AppStorage.tokenStorage.getItem(this.JWT_TOKEN);
450
459
  }
460
+ static getRftToken() {
461
+ return AppStorage.tokenStorage.getItem(this.JWT_RFT_TOKEN);
462
+ }
451
463
  static isLoggedIn() {
452
464
  return AppStorage.tokenStorage.getItem(this.JWT_TOKEN) != null;
453
465
  }
@@ -474,6 +486,21 @@ class AuthService extends HTTPService {
474
486
  commonService = inject(CommonService);
475
487
  router = inject(Router);
476
488
  translate = inject(TranslateService);
489
+ async refreshToken() {
490
+ const refreshToken = TokenStorage.getRftToken();
491
+ if (!refreshToken)
492
+ return null;
493
+ const body = { rft: refreshToken };
494
+ try {
495
+ const result = (await firstValueFrom(this.httpClient.post(AppGlobals.apiEndpoint + '/api/Auth/RefreshToken', body)));
496
+ if (result?.IsSuccess) {
497
+ TokenStorage.saveTokenRft(result.Data);
498
+ return result;
499
+ }
500
+ }
501
+ catch { }
502
+ return null;
503
+ }
477
504
  async pingAuth() {
478
505
  try {
479
506
  const res = await this.post(AppGlobals.apiEndpoint + URLs.pingAuth);
@@ -616,23 +643,24 @@ i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "19.1.7", ngImpor
616
643
  }] });
617
644
 
618
645
  let initialReturnUrl = null;
646
+ let isRefreshing = false;
647
+ const refreshTokenSubject = new BehaviorSubject(null);
619
648
  function authInterceptor(req, next) {
620
649
  const router = inject(Router);
621
650
  const loadingService = inject(LoadingService);
622
- const modalService = inject(NzModalService);
623
- const notiService = inject(NotiService);
624
- const translate = inject(TranslateService);
651
+ const authService = inject(AuthService);
625
652
  const authToken = TokenStorage.getToken();
626
653
  const noLoadingMark = req.headers.get('No-Loading-Mark');
627
- if (noLoadingMark != '1') {
654
+ if (noLoadingMark !== '1')
628
655
  loadingService.loadingUp();
629
- }
656
+ // Convert dates
630
657
  if (!(req.body instanceof FormData)) {
631
658
  const newBody = convertDatesInBody(req.body);
632
659
  if (req.body !== newBody) {
633
660
  req = req.clone({ body: newBody });
634
661
  }
635
662
  }
663
+ // Add token
636
664
  if (authToken) {
637
665
  req = req.clone({
638
666
  setHeaders: { Authorization: `Bearer ${authToken}` },
@@ -640,8 +668,46 @@ function authInterceptor(req, next) {
640
668
  }
641
669
  return next(req).pipe(catchError((error) => {
642
670
  if (error.status === 401) {
643
- modalService.closeAll();
644
- notiService.error(translate.instant('EXPIRED_TOKEN'));
671
+ return handle401(req, next, router, authService);
672
+ }
673
+ return throwError(() => error);
674
+ }), finalize(() => {
675
+ if (noLoadingMark !== '1')
676
+ loadingService.loadingDown();
677
+ }));
678
+ }
679
+ //
680
+ // ⭐ REFRESH TOKEN HANDLER
681
+ //
682
+ function handle401(req, next, router, authService) {
683
+ if (!isRefreshing) {
684
+ isRefreshing = true;
685
+ refreshTokenSubject.next(null);
686
+ return from(authService.refreshToken()).pipe(switchMap$1((resAny) => {
687
+ const res = resAny;
688
+ isRefreshing = false;
689
+ if (!res?.IsSuccess) {
690
+ TokenStorage.clearToken();
691
+ const currentUrl = router.routerState.snapshot.url;
692
+ if (!initialReturnUrl && !currentUrl.startsWith('/login')) {
693
+ initialReturnUrl = currentUrl;
694
+ }
695
+ router.navigate(['/login'], {
696
+ queryParams: { returnUrl: initialReturnUrl || '/' },
697
+ });
698
+ return EMPTY;
699
+ }
700
+ // Lưu token mới
701
+ TokenStorage.saveToken(res);
702
+ // Phát token mới cho các request đang chờ
703
+ refreshTokenSubject.next(TokenStorage.getToken());
704
+ // Retry request
705
+ return next(req.clone({
706
+ setHeaders: { Authorization: `Bearer ${TokenStorage.getToken()}` },
707
+ }));
708
+ }), catchError((err) => {
709
+ isRefreshing = false;
710
+ TokenStorage.clearToken();
645
711
  const currentUrl = router.routerState.snapshot.url;
646
712
  if (!initialReturnUrl && !currentUrl.startsWith('/login')) {
647
713
  initialReturnUrl = currentUrl;
@@ -649,14 +715,15 @@ function authInterceptor(req, next) {
649
715
  router.navigate(['/login'], {
650
716
  queryParams: { returnUrl: initialReturnUrl || '/' },
651
717
  });
652
- return EMPTY;
653
- }
654
- return throwError(() => error);
655
- }), finalize(() => {
656
- if (noLoadingMark != '1') {
657
- loadingService.loadingDown();
658
- }
659
- }));
718
+ return throwError(() => err);
719
+ }));
720
+ }
721
+ else {
722
+ // Nếu đang refresh → chờ token mới rồi retry
723
+ return refreshTokenSubject.pipe(filter((token) => token != null), take(1), switchMap$1((token) => next(req.clone({
724
+ setHeaders: { Authorization: `Bearer ${token}` },
725
+ }))));
726
+ }
660
727
  }
661
728
  function convertDatesInBody(obj) {
662
729
  if (obj instanceof Date) {