@brggroup/share-lib 0.0.30 → 0.0.32

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.
@@ -1,11 +1,11 @@
1
1
  import * as i0 from '@angular/core';
2
- import { inject, Injectable, Component, HostListener, ViewContainerRef, Input, ViewChild, EventEmitter, Output, Directive, Optional, Self, HostBinding, forwardRef, Pipe } from '@angular/core';
2
+ import { inject, Injectable, NgZone, Component, HostListener, ViewContainerRef, Input, ViewChild, EventEmitter, Output, Directive, Optional, Self, HostBinding, forwardRef, Pipe } from '@angular/core';
3
3
  import * as i1$3 from '@angular/router';
4
4
  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]);
@@ -470,6 +477,7 @@ class TokenStorage {
470
477
 
471
478
  const URLs = {
472
479
  login: '/api/Auth/Login',
480
+ register: '/api/Auth/Register',
473
481
  pingAuth: '/api/Auth/PingAuth',
474
482
  changePassword: '/api/Auth/ChangePassword',
475
483
  setPass: '/api/Auth/SetPass',
@@ -477,11 +485,32 @@ const URLs = {
477
485
  class AuthService extends HTTPService {
478
486
  notiService = inject(NotiService);
479
487
  commonService = inject(CommonService);
488
+ ngZone = inject(NgZone);
480
489
  router = inject(Router);
481
490
  translate = inject(TranslateService);
491
+ async refreshToken() {
492
+ const refreshToken = TokenStorage.getRftToken();
493
+ if (!refreshToken)
494
+ return null;
495
+ const body = { rft: refreshToken };
496
+ try {
497
+ const res = await this.sendRftToken(body);
498
+ if (res?.IsSuccess) {
499
+ TokenStorage.saveTokenRft(res.Data);
500
+ return res;
501
+ }
502
+ else {
503
+ return null;
504
+ }
505
+ }
506
+ catch (err) {
507
+ console.log(err);
508
+ }
509
+ return null;
510
+ }
482
511
  async pingAuth() {
483
512
  try {
484
- const res = await this.post(AppGlobals.apiEndpoint + URLs.pingAuth);
513
+ const res = await this.get(AppGlobals.apiEndpoint + URLs.pingAuth, true);
485
514
  return res?.IsSuccess;
486
515
  }
487
516
  catch {
@@ -502,6 +531,7 @@ class AuthService extends HTTPService {
502
531
  }
503
532
  this.notiService.success('Đăng nhập thành công');
504
533
  TokenStorage.saveToken(res);
534
+ this.startWatching();
505
535
  if (returnUrl) {
506
536
  this.router.navigateByUrl(returnUrl);
507
537
  }
@@ -517,9 +547,18 @@ class AuthService extends HTTPService {
517
547
  headers: new HttpHeaders().set('Content-Type', 'application/json'),
518
548
  }));
519
549
  }
550
+ async sendRftToken(body) {
551
+ return firstValueFrom(this.httpClient.post(AppGlobals.apiEndpoint + '/api/Auth/RefreshToken', body, {
552
+ headers: new HttpHeaders().set('Content-Type', 'application/json'),
553
+ }));
554
+ }
520
555
  signOut() {
521
556
  TokenStorage.clearToken();
522
- this.router.navigate(['/login']);
557
+ this.stopWatching();
558
+ const currentUrl = this.router.routerState.snapshot.url;
559
+ this.router.navigate(['/login'], {
560
+ queryParams: { returnUrl: currentUrl || '/' },
561
+ });
523
562
  }
524
563
  getUserMenu() {
525
564
  let data = {
@@ -546,6 +585,51 @@ class AuthService extends HTTPService {
546
585
  setPass(data) {
547
586
  return this.postData(AppGlobals.apiEndpoint + URLs.setPass, data);
548
587
  }
588
+ async signUp(d) {
589
+ try {
590
+ const res = await this.sendSignUp(d);
591
+ if (!res.IsSuccess) {
592
+ this.notiService.error(res.ErrorMessage);
593
+ return;
594
+ }
595
+ this.notiService.success('Đăng ký thành công');
596
+ this.router.navigateByUrl('/login');
597
+ }
598
+ catch (err) {
599
+ console.log(err);
600
+ this.notiService.handleError(err);
601
+ }
602
+ }
603
+ async sendSignUp(d) {
604
+ return this.postData(AppGlobals.apiEndpoint + URLs.register, d);
605
+ }
606
+ idleTimer;
607
+ idleTimeInMinutes = 15; // phút
608
+ idleTime = this.idleTimeInMinutes * 60 * 1000;
609
+ startWatching() {
610
+ console.log(`-- start watching idle (${this.idleTimeInMinutes} ${this.idleTimeInMinutes == 1 ? 'min' : 'mins'}) --`);
611
+ this.resetTimer();
612
+ window.addEventListener('mousemove', () => this.resetTimer());
613
+ window.addEventListener('keydown', () => this.resetTimer());
614
+ window.addEventListener('click', () => this.resetTimer());
615
+ }
616
+ resetTimer() {
617
+ if (this.idleTimer) {
618
+ clearTimeout(this.idleTimer);
619
+ }
620
+ this.ngZone.runOutsideAngular(() => {
621
+ this.idleTimer = setTimeout(() => {
622
+ this.ngZone.run(() => {
623
+ console.log('-- idle logout --');
624
+ this.signOut(); // auto logout
625
+ });
626
+ }, this.idleTime);
627
+ });
628
+ }
629
+ stopWatching() {
630
+ console.log('-- stop watching idle --');
631
+ clearTimeout(this.idleTimer);
632
+ }
549
633
  static ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "19.1.7", ngImport: i0, type: AuthService, deps: null, target: i0.ɵɵFactoryTarget.Injectable });
550
634
  static ɵprov = i0.ɵɵngDeclareInjectable({ minVersion: "12.0.0", version: "19.1.7", ngImport: i0, type: AuthService, providedIn: 'root' });
551
635
  }
@@ -621,23 +705,24 @@ i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "19.1.7", ngImpor
621
705
  }] });
622
706
 
623
707
  let initialReturnUrl = null;
708
+ let isRefreshing = false;
709
+ const refreshTokenSubject = new BehaviorSubject(null);
624
710
  function authInterceptor(req, next) {
625
711
  const router = inject(Router);
626
712
  const loadingService = inject(LoadingService);
627
- const modalService = inject(NzModalService);
628
- const notiService = inject(NotiService);
629
- const translate = inject(TranslateService);
713
+ const authService = inject(AuthService);
630
714
  const authToken = TokenStorage.getToken();
631
715
  const noLoadingMark = req.headers.get('No-Loading-Mark');
632
- if (noLoadingMark != '1') {
716
+ if (noLoadingMark !== '1')
633
717
  loadingService.loadingUp();
634
- }
718
+ // Convert dates
635
719
  if (!(req.body instanceof FormData)) {
636
720
  const newBody = convertDatesInBody(req.body);
637
721
  if (req.body !== newBody) {
638
722
  req = req.clone({ body: newBody });
639
723
  }
640
724
  }
725
+ // Add token
641
726
  if (authToken) {
642
727
  req = req.clone({
643
728
  setHeaders: { Authorization: `Bearer ${authToken}` },
@@ -645,8 +730,44 @@ function authInterceptor(req, next) {
645
730
  }
646
731
  return next(req).pipe(catchError((error) => {
647
732
  if (error.status === 401) {
648
- modalService.closeAll();
649
- notiService.error(translate.instant('EXPIRED_TOKEN'));
733
+ return handle401(req, next, router, authService);
734
+ }
735
+ return throwError(() => error);
736
+ }), finalize(() => {
737
+ if (noLoadingMark !== '1')
738
+ loadingService.loadingDown();
739
+ }));
740
+ }
741
+ //
742
+ // ⭐ REFRESH TOKEN HANDLER
743
+ //
744
+ function handle401(req, next, router, authService) {
745
+ if (!isRefreshing) {
746
+ isRefreshing = true;
747
+ refreshTokenSubject.next(null);
748
+ return from(authService.refreshToken()).pipe(switchMap$1((resAny) => {
749
+ const res = resAny;
750
+ isRefreshing = false;
751
+ if (!res?.IsSuccess) {
752
+ const currentUrl = router.routerState.snapshot.url;
753
+ if (!initialReturnUrl && !currentUrl.startsWith('/login')) {
754
+ initialReturnUrl = currentUrl;
755
+ }
756
+ router.navigate(['/login'], {
757
+ queryParams: { returnUrl: initialReturnUrl || '/' },
758
+ });
759
+ refreshTokenSubject.next(null);
760
+ return EMPTY;
761
+ }
762
+ // Phát token mới cho các request đang chờ
763
+ refreshTokenSubject.next(TokenStorage.getToken());
764
+ // Retry request
765
+ return next(req.clone({
766
+ setHeaders: { Authorization: `Bearer ${TokenStorage.getToken()}` },
767
+ }));
768
+ }), catchError((err) => {
769
+ isRefreshing = false;
770
+ TokenStorage.clearToken();
650
771
  const currentUrl = router.routerState.snapshot.url;
651
772
  if (!initialReturnUrl && !currentUrl.startsWith('/login')) {
652
773
  initialReturnUrl = currentUrl;
@@ -654,14 +775,15 @@ function authInterceptor(req, next) {
654
775
  router.navigate(['/login'], {
655
776
  queryParams: { returnUrl: initialReturnUrl || '/' },
656
777
  });
657
- return EMPTY;
658
- }
659
- return throwError(() => error);
660
- }), finalize(() => {
661
- if (noLoadingMark != '1') {
662
- loadingService.loadingDown();
663
- }
664
- }));
778
+ return throwError(() => err);
779
+ }));
780
+ }
781
+ else {
782
+ // Nếu đang refresh → chờ token mới rồi retry
783
+ return refreshTokenSubject.pipe(filter((token) => token != null), take(1), switchMap$1((token) => next(req.clone({
784
+ setHeaders: { Authorization: `Bearer ${token}` },
785
+ }))));
786
+ }
665
787
  }
666
788
  function convertDatesInBody(obj) {
667
789
  if (obj instanceof Date) {