@brggroup/share-lib 0.0.30 → 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';
@@ -419,7 +419,14 @@ class TokenStorage {
419
419
  static saveToken(tokenRes) {
420
420
  AppStorage.tokenStorage.setItem(this.JWT_TOKEN, tokenRes.Data.Token);
421
421
  AppStorage.tokenStorage.setItem(this.JWT_RFT_TOKEN, tokenRes.Data.RefreshToken);
422
- const userInfo = jwtDecode(tokenRes.Data.Token);
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);
423
430
  AppStorage.tokenStorage.setItem(this.USER_NAME, userInfo[this.USER_NAME]);
424
431
  AppStorage.tokenStorage.setItem(this.FULL_NAME, userInfo[this.FULL_NAME]);
425
432
  AppStorage.tokenStorage.setItem(this.ORG_ID, userInfo[this.ORG_ID]);
@@ -479,6 +486,21 @@ class AuthService extends HTTPService {
479
486
  commonService = inject(CommonService);
480
487
  router = inject(Router);
481
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
+ }
482
504
  async pingAuth() {
483
505
  try {
484
506
  const res = await this.post(AppGlobals.apiEndpoint + URLs.pingAuth);
@@ -621,23 +643,24 @@ i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "19.1.7", ngImpor
621
643
  }] });
622
644
 
623
645
  let initialReturnUrl = null;
646
+ let isRefreshing = false;
647
+ const refreshTokenSubject = new BehaviorSubject(null);
624
648
  function authInterceptor(req, next) {
625
649
  const router = inject(Router);
626
650
  const loadingService = inject(LoadingService);
627
- const modalService = inject(NzModalService);
628
- const notiService = inject(NotiService);
629
- const translate = inject(TranslateService);
651
+ const authService = inject(AuthService);
630
652
  const authToken = TokenStorage.getToken();
631
653
  const noLoadingMark = req.headers.get('No-Loading-Mark');
632
- if (noLoadingMark != '1') {
654
+ if (noLoadingMark !== '1')
633
655
  loadingService.loadingUp();
634
- }
656
+ // Convert dates
635
657
  if (!(req.body instanceof FormData)) {
636
658
  const newBody = convertDatesInBody(req.body);
637
659
  if (req.body !== newBody) {
638
660
  req = req.clone({ body: newBody });
639
661
  }
640
662
  }
663
+ // Add token
641
664
  if (authToken) {
642
665
  req = req.clone({
643
666
  setHeaders: { Authorization: `Bearer ${authToken}` },
@@ -645,8 +668,46 @@ function authInterceptor(req, next) {
645
668
  }
646
669
  return next(req).pipe(catchError((error) => {
647
670
  if (error.status === 401) {
648
- modalService.closeAll();
649
- 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();
650
711
  const currentUrl = router.routerState.snapshot.url;
651
712
  if (!initialReturnUrl && !currentUrl.startsWith('/login')) {
652
713
  initialReturnUrl = currentUrl;
@@ -654,14 +715,15 @@ function authInterceptor(req, next) {
654
715
  router.navigate(['/login'], {
655
716
  queryParams: { returnUrl: initialReturnUrl || '/' },
656
717
  });
657
- return EMPTY;
658
- }
659
- return throwError(() => error);
660
- }), finalize(() => {
661
- if (noLoadingMark != '1') {
662
- loadingService.loadingDown();
663
- }
664
- }));
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
+ }
665
727
  }
666
728
  function convertDatesInBody(obj) {
667
729
  if (obj instanceof Date) {