@sdcorejs/angular 20.0.6 → 20.0.7

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.
Files changed (82) hide show
  1. package/components/index.d.ts +1 -0
  2. package/components/upload-file/index.d.ts +7 -4
  3. package/fesm2022/sdcorejs-angular-components-anchor.mjs +9 -9
  4. package/fesm2022/sdcorejs-angular-components-autoid-inspector.mjs +15 -15
  5. package/fesm2022/sdcorejs-angular-components-avatar.mjs +3 -3
  6. package/fesm2022/sdcorejs-angular-components-badge.mjs +3 -3
  7. package/fesm2022/sdcorejs-angular-components-base.mjs +3 -3
  8. package/fesm2022/sdcorejs-angular-components-button.mjs +3 -3
  9. package/fesm2022/sdcorejs-angular-components-chart.mjs +12 -12
  10. package/fesm2022/sdcorejs-angular-components-ckeditor-styles.mjs +3 -3
  11. package/fesm2022/sdcorejs-angular-components-code-editor.mjs +3 -3
  12. package/fesm2022/sdcorejs-angular-components-document-builder.mjs +6 -6
  13. package/fesm2022/sdcorejs-angular-components-editor.mjs +3 -3
  14. package/fesm2022/sdcorejs-angular-components-form-generic.mjs +201 -201
  15. package/fesm2022/sdcorejs-angular-components-history.mjs +7 -7
  16. package/fesm2022/sdcorejs-angular-components-import-excel.mjs +10 -10
  17. package/fesm2022/sdcorejs-angular-components-inform.mjs +6 -6
  18. package/fesm2022/sdcorejs-angular-components-mini-editor.mjs +3 -3
  19. package/fesm2022/sdcorejs-angular-components-modal-resizable.mjs +3 -3
  20. package/fesm2022/sdcorejs-angular-components-modal.mjs +3 -3
  21. package/fesm2022/sdcorejs-angular-components-operator.mjs +3 -3
  22. package/fesm2022/sdcorejs-angular-components-org-chart.mjs +6 -6
  23. package/fesm2022/sdcorejs-angular-components-preview.mjs +6 -6
  24. package/fesm2022/sdcorejs-angular-components-query-bar.mjs +27 -27
  25. package/fesm2022/sdcorejs-angular-components-query-builder.mjs +3 -3
  26. package/fesm2022/sdcorejs-angular-components-quick-action.mjs +3 -3
  27. package/fesm2022/sdcorejs-angular-components-section.mjs +6 -6
  28. package/fesm2022/sdcorejs-angular-components-side-drawer.mjs +3 -3
  29. package/fesm2022/sdcorejs-angular-components-splitter.mjs +28 -14
  30. package/fesm2022/sdcorejs-angular-components-splitter.mjs.map +1 -1
  31. package/fesm2022/sdcorejs-angular-components-stepper.mjs +6 -6
  32. package/fesm2022/sdcorejs-angular-components-tab-router.mjs +18 -18
  33. package/fesm2022/sdcorejs-angular-components-tab.mjs +6 -6
  34. package/fesm2022/sdcorejs-angular-components-table.mjs +105 -105
  35. package/fesm2022/sdcorejs-angular-components-tree.mjs +6 -6
  36. package/fesm2022/sdcorejs-angular-components-upload-file.mjs +30 -24
  37. package/fesm2022/sdcorejs-angular-components-upload-file.mjs.map +1 -1
  38. package/fesm2022/sdcorejs-angular-components-view.mjs +3 -3
  39. package/fesm2022/sdcorejs-angular-components.mjs +1 -0
  40. package/fesm2022/sdcorejs-angular-components.mjs.map +1 -1
  41. package/fesm2022/sdcorejs-angular-directives.mjs +21 -21
  42. package/fesm2022/sdcorejs-angular-forms-autocomplete.mjs +3 -3
  43. package/fesm2022/sdcorejs-angular-forms-checkbox.mjs +3 -3
  44. package/fesm2022/sdcorejs-angular-forms-chip-calendar.mjs +6 -6
  45. package/fesm2022/sdcorejs-angular-forms-chip.mjs +6 -6
  46. package/fesm2022/sdcorejs-angular-forms-date-range.mjs +3 -3
  47. package/fesm2022/sdcorejs-angular-forms-date.mjs +3 -3
  48. package/fesm2022/sdcorejs-angular-forms-datetime.mjs +9 -9
  49. package/fesm2022/sdcorejs-angular-forms-directives.mjs +12 -12
  50. package/fesm2022/sdcorejs-angular-forms-inline-text.mjs +3 -3
  51. package/fesm2022/sdcorejs-angular-forms-input-color.mjs +3 -3
  52. package/fesm2022/sdcorejs-angular-forms-input-number.mjs +3 -3
  53. package/fesm2022/sdcorejs-angular-forms-input.mjs +3 -3
  54. package/fesm2022/sdcorejs-angular-forms-label.mjs +3 -3
  55. package/fesm2022/sdcorejs-angular-forms-radio.mjs +3 -3
  56. package/fesm2022/sdcorejs-angular-forms-select.mjs +6 -6
  57. package/fesm2022/sdcorejs-angular-forms-switch.mjs +3 -3
  58. package/fesm2022/sdcorejs-angular-forms-textarea.mjs +3 -3
  59. package/fesm2022/sdcorejs-angular-forms.mjs +4 -4
  60. package/fesm2022/sdcorejs-angular-handlers.mjs +3 -3
  61. package/fesm2022/sdcorejs-angular-i18n.mjs +6 -6
  62. package/fesm2022/sdcorejs-angular-interceptors.mjs +6 -6
  63. package/fesm2022/sdcorejs-angular-modules-auth.mjs +9 -9
  64. package/fesm2022/sdcorejs-angular-modules-authom.mjs +7 -7
  65. package/fesm2022/sdcorejs-angular-modules-keycloak.mjs +37 -14
  66. package/fesm2022/sdcorejs-angular-modules-keycloak.mjs.map +1 -1
  67. package/fesm2022/sdcorejs-angular-modules-layout.mjs +73 -73
  68. package/fesm2022/sdcorejs-angular-modules-permission.mjs +9 -9
  69. package/fesm2022/sdcorejs-angular-pipes.mjs +14 -14
  70. package/fesm2022/sdcorejs-angular-services-api.mjs +10 -10
  71. package/fesm2022/sdcorejs-angular-services-cache.mjs +3 -3
  72. package/fesm2022/sdcorejs-angular-services-confirm.mjs +6 -6
  73. package/fesm2022/sdcorejs-angular-services-docx.mjs +3 -3
  74. package/fesm2022/sdcorejs-angular-services-excel.mjs +3 -3
  75. package/fesm2022/sdcorejs-angular-services-license.mjs +3 -3
  76. package/fesm2022/sdcorejs-angular-services-loading.mjs +3 -3
  77. package/fesm2022/sdcorejs-angular-services-notify.mjs +9 -9
  78. package/fesm2022/sdcorejs-angular-services-storage.mjs +3 -3
  79. package/modules/keycloak/htmls/auth-keycloak-error.html +54 -0
  80. package/modules/keycloak/htmls/silent-renew.html +7 -0
  81. package/modules/keycloak/index.d.ts +30 -0
  82. package/package.json +1 -1
@@ -52,10 +52,10 @@ class SdGlobalErrorHandler {
52
52
  }
53
53
  return '';
54
54
  };
55
- static ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "20.3.24", ngImport: i0, type: SdGlobalErrorHandler, deps: [], target: i0.ɵɵFactoryTarget.Injectable });
56
- static ɵprov = i0.ɵɵngDeclareInjectable({ minVersion: "12.0.0", version: "20.3.24", ngImport: i0, type: SdGlobalErrorHandler });
55
+ static ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "20.3.25", ngImport: i0, type: SdGlobalErrorHandler, deps: [], target: i0.ɵɵFactoryTarget.Injectable });
56
+ static ɵprov = i0.ɵɵngDeclareInjectable({ minVersion: "12.0.0", version: "20.3.25", ngImport: i0, type: SdGlobalErrorHandler });
57
57
  }
58
- i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "20.3.24", ngImport: i0, type: SdGlobalErrorHandler, decorators: [{
58
+ i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "20.3.25", ngImport: i0, type: SdGlobalErrorHandler, decorators: [{
59
59
  type: Injectable
60
60
  }], ctorParameters: () => [] });
61
61
 
@@ -2583,10 +2583,10 @@ class I18nService {
2583
2583
  console.warn('[I18n] Custom language fn threw:', err);
2584
2584
  }
2585
2585
  }
2586
- static ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "20.3.24", ngImport: i0, type: I18nService, deps: [], target: i0.ɵɵFactoryTarget.Injectable });
2587
- static ɵprov = i0.ɵɵngDeclareInjectable({ minVersion: "12.0.0", version: "20.3.24", ngImport: i0, type: I18nService, providedIn: 'root' });
2586
+ static ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "20.3.25", ngImport: i0, type: I18nService, deps: [], target: i0.ɵɵFactoryTarget.Injectable });
2587
+ static ɵprov = i0.ɵɵngDeclareInjectable({ minVersion: "12.0.0", version: "20.3.25", ngImport: i0, type: I18nService, providedIn: 'root' });
2588
2588
  }
2589
- i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "20.3.24", ngImport: i0, type: I18nService, decorators: [{
2589
+ i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "20.3.25", ngImport: i0, type: I18nService, decorators: [{
2590
2590
  type: Injectable,
2591
2591
  args: [{ providedIn: 'root' }]
2592
2592
  }] });
@@ -2596,10 +2596,10 @@ class TranslatePipe {
2596
2596
  transform(key, params) {
2597
2597
  return this.#i18n.t(key, params);
2598
2598
  }
2599
- static ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "20.3.24", ngImport: i0, type: TranslatePipe, deps: [], target: i0.ɵɵFactoryTarget.Pipe });
2600
- static ɵpipe = i0.ɵɵngDeclarePipe({ minVersion: "14.0.0", version: "20.3.24", ngImport: i0, type: TranslatePipe, isStandalone: true, name: "translate" });
2599
+ static ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "20.3.25", ngImport: i0, type: TranslatePipe, deps: [], target: i0.ɵɵFactoryTarget.Pipe });
2600
+ static ɵpipe = i0.ɵɵngDeclarePipe({ minVersion: "14.0.0", version: "20.3.25", ngImport: i0, type: TranslatePipe, isStandalone: true, name: "translate" });
2601
2601
  }
2602
- i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "20.3.24", ngImport: i0, type: TranslatePipe, decorators: [{
2602
+ i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "20.3.25", ngImport: i0, type: TranslatePipe, decorators: [{
2603
2603
  type: Pipe,
2604
2604
  args: [{ name: 'translate', standalone: true }]
2605
2605
  }] });
@@ -129,10 +129,10 @@ class SdNoInternetInterceptor {
129
129
  this.#pollInterval = null;
130
130
  }
131
131
  };
132
- static ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "20.3.24", ngImport: i0, type: SdNoInternetInterceptor, deps: [{ token: i1.MatSnackBar }, { token: i0.Injector }], target: i0.ɵɵFactoryTarget.Injectable });
133
- static ɵprov = i0.ɵɵngDeclareInjectable({ minVersion: "12.0.0", version: "20.3.24", ngImport: i0, type: SdNoInternetInterceptor });
132
+ static ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "20.3.25", ngImport: i0, type: SdNoInternetInterceptor, deps: [{ token: i1.MatSnackBar }, { token: i0.Injector }], target: i0.ɵɵFactoryTarget.Injectable });
133
+ static ɵprov = i0.ɵɵngDeclareInjectable({ minVersion: "12.0.0", version: "20.3.25", ngImport: i0, type: SdNoInternetInterceptor });
134
134
  }
135
- i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "20.3.24", ngImport: i0, type: SdNoInternetInterceptor, decorators: [{
135
+ i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "20.3.25", ngImport: i0, type: SdNoInternetInterceptor, decorators: [{
136
136
  type: Injectable
137
137
  }], ctorParameters: () => [{ type: i1.MatSnackBar }, { type: i0.Injector }] });
138
138
 
@@ -151,10 +151,10 @@ class SdUnauthorizedInterceptor {
151
151
  return throwError(() => error);
152
152
  }));
153
153
  }
154
- static ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "20.3.24", ngImport: i0, type: SdUnauthorizedInterceptor, deps: [{ token: i1$1.SdAuthService }], target: i0.ɵɵFactoryTarget.Injectable });
155
- static ɵprov = i0.ɵɵngDeclareInjectable({ minVersion: "12.0.0", version: "20.3.24", ngImport: i0, type: SdUnauthorizedInterceptor });
154
+ static ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "20.3.25", ngImport: i0, type: SdUnauthorizedInterceptor, deps: [{ token: i1$1.SdAuthService }], target: i0.ɵɵFactoryTarget.Injectable });
155
+ static ɵprov = i0.ɵɵngDeclareInjectable({ minVersion: "12.0.0", version: "20.3.25", ngImport: i0, type: SdUnauthorizedInterceptor });
156
156
  }
157
- i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "20.3.24", ngImport: i0, type: SdUnauthorizedInterceptor, decorators: [{
157
+ i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "20.3.25", ngImport: i0, type: SdUnauthorizedInterceptor, decorators: [{
158
158
  type: Injectable
159
159
  }], ctorParameters: () => [{ type: i1$1.SdAuthService }] });
160
160
 
@@ -62,10 +62,10 @@ class SdAuthService {
62
62
  });
63
63
  }
64
64
  };
65
- static ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "20.3.24", ngImport: i0, type: SdAuthService, deps: [{ token: SD_AUTH_CONFIGURATION, optional: true }], target: i0.ɵɵFactoryTarget.Injectable });
66
- static ɵprov = i0.ɵɵngDeclareInjectable({ minVersion: "12.0.0", version: "20.3.24", ngImport: i0, type: SdAuthService, providedIn: 'root' });
65
+ static ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "20.3.25", ngImport: i0, type: SdAuthService, deps: [{ token: SD_AUTH_CONFIGURATION, optional: true }], target: i0.ɵɵFactoryTarget.Injectable });
66
+ static ɵprov = i0.ɵɵngDeclareInjectable({ minVersion: "12.0.0", version: "20.3.25", ngImport: i0, type: SdAuthService, providedIn: 'root' });
67
67
  }
68
- i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "20.3.24", ngImport: i0, type: SdAuthService, decorators: [{
68
+ i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "20.3.25", ngImport: i0, type: SdAuthService, decorators: [{
69
69
  type: Injectable,
70
70
  args: [{ providedIn: 'root' }]
71
71
  }], ctorParameters: () => [{ type: undefined, decorators: [{
@@ -89,10 +89,10 @@ class SdAuthGuard {
89
89
  // Nếu chưa tích hợp Auth Guard thì pass canActive
90
90
  return true;
91
91
  };
92
- static ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "20.3.24", ngImport: i0, type: SdAuthGuard, deps: [{ token: SD_AUTH_CONFIGURATION, optional: true }, { token: i1.Router }], target: i0.ɵɵFactoryTarget.Injectable });
93
- static ɵprov = i0.ɵɵngDeclareInjectable({ minVersion: "12.0.0", version: "20.3.24", ngImport: i0, type: SdAuthGuard, providedIn: 'root' });
92
+ static ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "20.3.25", ngImport: i0, type: SdAuthGuard, deps: [{ token: SD_AUTH_CONFIGURATION, optional: true }, { token: i1.Router }], target: i0.ɵɵFactoryTarget.Injectable });
93
+ static ɵprov = i0.ɵɵngDeclareInjectable({ minVersion: "12.0.0", version: "20.3.25", ngImport: i0, type: SdAuthGuard, providedIn: 'root' });
94
94
  }
95
- i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "20.3.24", ngImport: i0, type: SdAuthGuard, decorators: [{
95
+ i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "20.3.25", ngImport: i0, type: SdAuthGuard, decorators: [{
96
96
  type: Injectable,
97
97
  args: [{ providedIn: 'root' }]
98
98
  }], ctorParameters: () => [{ type: undefined, decorators: [{
@@ -116,10 +116,10 @@ class SdPortalGuard {
116
116
  // Nếu chưa tích hợp Portal Guard thì pass canActive
117
117
  return true;
118
118
  };
119
- static ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "20.3.24", ngImport: i0, type: SdPortalGuard, deps: [{ token: SD_AUTH_CONFIGURATION, optional: true }, { token: i1.Router }], target: i0.ɵɵFactoryTarget.Injectable });
120
- static ɵprov = i0.ɵɵngDeclareInjectable({ minVersion: "12.0.0", version: "20.3.24", ngImport: i0, type: SdPortalGuard, providedIn: 'root' });
119
+ static ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "20.3.25", ngImport: i0, type: SdPortalGuard, deps: [{ token: SD_AUTH_CONFIGURATION, optional: true }, { token: i1.Router }], target: i0.ɵɵFactoryTarget.Injectable });
120
+ static ɵprov = i0.ɵɵngDeclareInjectable({ minVersion: "12.0.0", version: "20.3.25", ngImport: i0, type: SdPortalGuard, providedIn: 'root' });
121
121
  }
122
- i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "20.3.24", ngImport: i0, type: SdPortalGuard, decorators: [{
122
+ i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "20.3.25", ngImport: i0, type: SdPortalGuard, decorators: [{
123
123
  type: Injectable,
124
124
  args: [{ providedIn: 'root' }]
125
125
  }], ctorParameters: () => [{ type: undefined, decorators: [{
@@ -247,10 +247,10 @@ class SdAuthOmService {
247
247
  }
248
248
  return this.silentRefresh();
249
249
  }
250
- static ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "20.3.24", ngImport: i0, type: SdAuthOmService, deps: [], target: i0.ɵɵFactoryTarget.Injectable });
251
- static ɵprov = i0.ɵɵngDeclareInjectable({ minVersion: "12.0.0", version: "20.3.24", ngImport: i0, type: SdAuthOmService, providedIn: 'root' });
250
+ static ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "20.3.25", ngImport: i0, type: SdAuthOmService, deps: [], target: i0.ɵɵFactoryTarget.Injectable });
251
+ static ɵprov = i0.ɵɵngDeclareInjectable({ minVersion: "12.0.0", version: "20.3.25", ngImport: i0, type: SdAuthOmService, providedIn: 'root' });
252
252
  }
253
- i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "20.3.24", ngImport: i0, type: SdAuthOmService, decorators: [{
253
+ i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "20.3.25", ngImport: i0, type: SdAuthOmService, decorators: [{
254
254
  type: Injectable,
255
255
  args: [{ providedIn: 'root' }]
256
256
  }] });
@@ -311,11 +311,11 @@ class SdAuthOmModule {
311
311
  ],
312
312
  };
313
313
  }
314
- static ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "20.3.24", ngImport: i0, type: SdAuthOmModule, deps: [], target: i0.ɵɵFactoryTarget.NgModule });
315
- static ɵmod = i0.ɵɵngDeclareNgModule({ minVersion: "14.0.0", version: "20.3.24", ngImport: i0, type: SdAuthOmModule });
316
- static ɵinj = i0.ɵɵngDeclareInjector({ minVersion: "12.0.0", version: "20.3.24", ngImport: i0, type: SdAuthOmModule });
314
+ static ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "20.3.25", ngImport: i0, type: SdAuthOmModule, deps: [], target: i0.ɵɵFactoryTarget.NgModule });
315
+ static ɵmod = i0.ɵɵngDeclareNgModule({ minVersion: "14.0.0", version: "20.3.25", ngImport: i0, type: SdAuthOmModule });
316
+ static ɵinj = i0.ɵɵngDeclareInjector({ minVersion: "12.0.0", version: "20.3.25", ngImport: i0, type: SdAuthOmModule });
317
317
  }
318
- i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "20.3.24", ngImport: i0, type: SdAuthOmModule, decorators: [{
318
+ i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "20.3.25", ngImport: i0, type: SdAuthOmModule, decorators: [{
319
319
  type: NgModule,
320
320
  args: [{}]
321
321
  }] });
@@ -10,6 +10,13 @@ class SdKeycloakService {
10
10
  config;
11
11
  async init(config) {
12
12
  this.config = config;
13
+ // Đường dẫn 2 file tĩnh trong public/ (consumer cấu hình qua silentRenewUrl / authErrorUrl).
14
+ const silentRenewHref = this.toPublicHtmlUrl(config.silentRenewUrl || 'silent-renew');
15
+ const authErrorHref = this.toPublicHtmlUrl(config.authErrorUrl || 'auth-keycloak-error');
16
+ // Nếu đang đứng ở chính trang lỗi → KHÔNG init lại (chống vòng lặp redirect khi init liên tục lỗi).
17
+ if (window.location.pathname === new URL(authErrorHref).pathname) {
18
+ return false;
19
+ }
13
20
  // 1. Khởi tạo instance
14
21
  this.keycloak = new Keycloak({
15
22
  url: config.url,
@@ -20,26 +27,42 @@ class SdKeycloakService {
20
27
  this.keycloak.onTokenExpired = () => {
21
28
  this.keycloak.updateToken(30).catch(() => {
22
29
  // @i18n-ignore — dev console warning
23
- console.warn('Không thể làm mới token. Yêu cầu đăng nhập lại.');
30
+ console.warn('Token refresh failed. Re-authentication required.');
24
31
  this.keycloak.login();
25
32
  });
26
33
  };
27
- // 3. Thực thi quá trình boot Keycloak
28
- return this.keycloak.init({
29
- onLoad: 'check-sso',
30
- silentCheckSsoRedirectUri: window.location.origin + '/silent-renew.html',
31
- checkLoginIframe: false, // Tắt check Iframe để chống lỗi vòng lặp
32
- });
34
+ // 3. Thực thi quá trình boot Keycloak; nếu lỗi → điều hướng tới trang lỗi tĩnh trong public/.
35
+ try {
36
+ return await this.keycloak.init({
37
+ onLoad: 'check-sso',
38
+ silentCheckSsoRedirectUri: silentRenewHref,
39
+ checkLoginIframe: false, // Tắt check Iframe để chống lỗi vòng lặp
40
+ });
41
+ }
42
+ catch (error) {
43
+ // @i18n-ignore — dev console error
44
+ console.error('Keycloak init failed:', error);
45
+ window.location.href = authErrorHref;
46
+ return false;
47
+ }
48
+ }
49
+ /**
50
+ * Chuẩn hoá basename file tĩnh trong public/ thành URL tuyệt đối.
51
+ * `'silent-renew'` | `'/silent-renew'` | `'silent-renew.html'` → `${origin}/silent-renew.html`.
52
+ */
53
+ toPublicHtmlUrl(name) {
54
+ const base = name.trim().replace(/^\/+/, '').replace(/\.html$/i, '');
55
+ return `${window.location.origin}/${base}.html`;
33
56
  }
34
57
  // Tiện ích nhanh cho Dev sử dụng
35
58
  login() { return this.keycloak.login(); }
36
59
  logout() { return this.keycloak.logout({ redirectUri: window.location.origin }); }
37
60
  getToken() { return this.keycloak.token; }
38
61
  getIsAuthenticated() { return this.keycloak.authenticated; }
39
- static ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "20.3.24", ngImport: i0, type: SdKeycloakService, deps: [], target: i0.ɵɵFactoryTarget.Injectable });
40
- static ɵprov = i0.ɵɵngDeclareInjectable({ minVersion: "12.0.0", version: "20.3.24", ngImport: i0, type: SdKeycloakService, providedIn: 'root' });
62
+ static ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "20.3.25", ngImport: i0, type: SdKeycloakService, deps: [], target: i0.ɵɵFactoryTarget.Injectable });
63
+ static ɵprov = i0.ɵɵngDeclareInjectable({ minVersion: "12.0.0", version: "20.3.25", ngImport: i0, type: SdKeycloakService, providedIn: 'root' });
41
64
  }
42
- i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "20.3.24", ngImport: i0, type: SdKeycloakService, decorators: [{
65
+ i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "20.3.25", ngImport: i0, type: SdKeycloakService, decorators: [{
43
66
  type: Injectable,
44
67
  args: [{ providedIn: 'root' }]
45
68
  }] });
@@ -110,11 +133,11 @@ class SdKeycloakModule {
110
133
  ]
111
134
  };
112
135
  }
113
- static ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "20.3.24", ngImport: i0, type: SdKeycloakModule, deps: [], target: i0.ɵɵFactoryTarget.NgModule });
114
- static ɵmod = i0.ɵɵngDeclareNgModule({ minVersion: "14.0.0", version: "20.3.24", ngImport: i0, type: SdKeycloakModule });
115
- static ɵinj = i0.ɵɵngDeclareInjector({ minVersion: "12.0.0", version: "20.3.24", ngImport: i0, type: SdKeycloakModule });
136
+ static ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "20.3.25", ngImport: i0, type: SdKeycloakModule, deps: [], target: i0.ɵɵFactoryTarget.NgModule });
137
+ static ɵmod = i0.ɵɵngDeclareNgModule({ minVersion: "14.0.0", version: "20.3.25", ngImport: i0, type: SdKeycloakModule });
138
+ static ɵinj = i0.ɵɵngDeclareInjector({ minVersion: "12.0.0", version: "20.3.25", ngImport: i0, type: SdKeycloakModule });
116
139
  }
117
- i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "20.3.24", ngImport: i0, type: SdKeycloakModule, decorators: [{
140
+ i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "20.3.25", ngImport: i0, type: SdKeycloakModule, decorators: [{
118
141
  type: NgModule,
119
142
  args: [{}]
120
143
  }] });
@@ -1 +1 @@
1
- {"version":3,"file":"sdcorejs-angular-modules-keycloak.mjs","sources":["../../../projects/sdcorejs-angular/modules/keycloak/keycloak.configuration.ts","../../../projects/sdcorejs-angular/modules/keycloak/keycloak.service.ts","../../../projects/sdcorejs-angular/modules/keycloak/keycloak.interceptor.ts","../../../projects/sdcorejs-angular/modules/keycloak/keycloak.module.ts","../../../projects/sdcorejs-angular/modules/keycloak/sdcorejs-angular-modules-keycloak.ts"],"sourcesContent":["import { InjectionToken } from '@angular/core';\n\nexport interface SdKeycloakTenantConfig {\n url: string;\n realm: string;\n clientId: string;\n secureRoutes?: string[]; // Các API cần đính token (vd: ['/api/v1'])\n}\n\nexport interface ISdKeycloakConfiguration {\n loadTenantConfig: () => Promise<SdKeycloakTenantConfig>;\n}\n\nexport const SD_KEYCLOAK_CONFIGURATION = new InjectionToken<ISdKeycloakConfiguration>('sd-keycloak.configuration');","import { Injectable } from '@angular/core';\nimport Keycloak from 'keycloak-js'; // Import trực tiếp SDK gốc của Keycloak\nimport { SdKeycloakTenantConfig } from './keycloak.configuration';\n\n@Injectable({ providedIn: 'root' })\nexport class SdKeycloakService {\n public keycloak!: Keycloak;\n public config!: SdKeycloakTenantConfig;\n\n async init(config: SdKeycloakTenantConfig): Promise<boolean> {\n this.config = config;\n \n // 1. Khởi tạo instance\n this.keycloak = new Keycloak({\n url: config.url,\n realm: config.realm,\n clientId: config.clientId,\n });\n\n // 2. Lắng nghe sự kiện hết hạn token để tự động làm mới ngầm\n this.keycloak.onTokenExpired = () => {\n this.keycloak.updateToken(30).catch(() => {\n // @i18n-ignore — dev console warning\n console.warn('Không thể làm mới token. Yêu cầu đăng nhập lại.');\n this.keycloak.login();\n });\n };\n\n // 3. Thực thi quá trình boot Keycloak\n return this.keycloak.init({\n onLoad: 'check-sso',\n silentCheckSsoRedirectUri: window.location.origin + '/silent-renew.html',\n checkLoginIframe: false, // Tắt check Iframe để chống lỗi vòng lặp\n });\n }\n\n // Tiện ích nhanh cho Dev sử dụng\n login() { return this.keycloak.login(); }\n logout() { return this.keycloak.logout({ redirectUri: window.location.origin }); }\n getToken() { return this.keycloak.token; }\n getIsAuthenticated() { return this.keycloak.authenticated; }\n}","import { HttpInterceptorFn } from '@angular/common/http';\nimport { inject } from '@angular/core';\nimport { from, switchMap } from 'rxjs';\nimport { SdKeycloakService } from './keycloak.service';\n\nexport const SdKeycloakInterceptor: HttpInterceptorFn = (req, next) => {\n const keycloakService = inject(SdKeycloakService);\n const { keycloak, config } = keycloakService;\n\n // Nếu chưa init xong hoặc chưa đăng nhập -> cho request đi qua bình thường\n if (!keycloak || !keycloak.authenticated || !config) {\n return next(req);\n }\n\n // Kiểm tra xem URL của request có nằm trong mảng secureRoutes không\n const isSecure = config.secureRoutes?.some(route => req.url.includes(route));\n if (!isSecure) {\n return next(req);\n }\n\n // Đảm bảo token luôn hợp lệ (cập nhật nếu token sẽ hết hạn trong 30s tới)\n return from(keycloak.updateToken(30)).pipe(\n switchMap(() => {\n const authReq = req.clone({\n headers: req.headers.set('Authorization', `Bearer ${keycloak.token}`)\n });\n return next(authReq);\n })\n );\n};","import { \n ModuleWithProviders, \n NgModule, \n Provider, \n Type, \n EnvironmentProviders, \n makeEnvironmentProviders, \n provideAppInitializer, \n inject,\n APP_INITIALIZER\n} from '@angular/core';\nimport { ISdKeycloakConfiguration, SD_KEYCLOAK_CONFIGURATION } from './keycloak.configuration';\nimport { SdKeycloakService } from './keycloak.service';\n\n// =======================================================================\n// CÁCH 1: Dùng cho Angular 19 Standalone (Gọi trong app.config.ts)\n// =======================================================================\nexport function provideSdKeycloak(options: {\n useClass?: Type<ISdKeycloakConfiguration>;\n // eslint-disable-next-line @typescript-eslint/no-explicit-any\n useFactory?: (...args: any[]) => ISdKeycloakConfiguration;\n // eslint-disable-next-line @typescript-eslint/no-explicit-any\n deps?: any[];\n}): EnvironmentProviders {\n \n // FIX 1: Khai báo mảng nhận cả Provider (cho Service/Token) lẫn EnvironmentProviders (cho AppInitializer)\n const providers: Array<Provider | EnvironmentProviders> = [SdKeycloakService];\n\n if (options.useFactory) {\n providers.push({ provide: SD_KEYCLOAK_CONFIGURATION, useFactory: options.useFactory, deps: options.deps || [] });\n } else if (options.useClass) {\n providers.push({ provide: SD_KEYCLOAK_CONFIGURATION, useClass: options.useClass });\n }\n\n // Standalone dùng được provideAppInitializer ngon lành\n providers.push(\n provideAppInitializer(() => {\n const configLoader = inject(SD_KEYCLOAK_CONFIGURATION);\n const keycloakService = inject(SdKeycloakService);\n return configLoader.loadTenantConfig().then((config) => keycloakService.init(config));\n })\n );\n\n return makeEnvironmentProviders(providers);\n}\n\n// =======================================================================\n// CÁCH 2: Dùng cho kiến trúc NgModule cũ (Backward Compatibility)\n// =======================================================================\n@NgModule({})\nexport class SdKeycloakModule {\n static forRoot(options: {\n useClass?: Type<ISdKeycloakConfiguration>;\n // eslint-disable-next-line @typescript-eslint/no-explicit-any\n useFactory?: (...args: any[]) => ISdKeycloakConfiguration;\n // eslint-disable-next-line @typescript-eslint/no-explicit-any\n deps?: any[];\n }): ModuleWithProviders<SdKeycloakModule> {\n \n return {\n ngModule: SdKeycloakModule,\n providers: [\n SdKeycloakService,\n ...(options.useFactory \n ? [{ provide: SD_KEYCLOAK_CONFIGURATION, useFactory: options.useFactory, deps: options.deps || [] }]\n : [{ provide: SD_KEYCLOAK_CONFIGURATION, useClass: options.useClass! }]\n ),\n \n // FIX 2: NgModule bắt buộc phải dùng APP_INITIALIZER (nhưng viết kiểu xịn của Angular 14+, dùng inject)\n {\n provide: APP_INITIALIZER,\n multi: true,\n useFactory: () => {\n const configLoader = inject(SD_KEYCLOAK_CONFIGURATION);\n const keycloakService = inject(SdKeycloakService);\n return () => configLoader.loadTenantConfig().then((config) => keycloakService.init(config));\n }\n }\n ]\n };\n }\n}","/**\n * Generated bundle index. Do not edit.\n */\n\nexport * from './index';\n"],"names":[],"mappings":"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"}
1
+ {"version":3,"file":"sdcorejs-angular-modules-keycloak.mjs","sources":["../../../projects/sdcorejs-angular/modules/keycloak/keycloak.configuration.ts","../../../projects/sdcorejs-angular/modules/keycloak/keycloak.service.ts","../../../projects/sdcorejs-angular/modules/keycloak/keycloak.interceptor.ts","../../../projects/sdcorejs-angular/modules/keycloak/keycloak.module.ts","../../../projects/sdcorejs-angular/modules/keycloak/sdcorejs-angular-modules-keycloak.ts"],"sourcesContent":["import { InjectionToken } from '@angular/core';\n\nexport interface SdKeycloakTenantConfig {\n url: string;\n realm: string;\n clientId: string;\n secureRoutes?: string[]; // Các API cần đính token (vd: ['/api/v1'])\n\n /**\n * Basename (KHÔNG kèm đuôi `.html`) của file Keycloak **silent SSO redirect** mà consumer\n * BẮT BUỘC đặt trong thư mục `public/`. Mặc định `'silent-renew'`.\n *\n * Service sẽ truyền `${origin}/<silentRenewUrl>.html` vào `silentCheckSsoRedirectUri`. Với giá trị\n * mặc định, Keycloak gọi `${origin}/silent-renew.html` (giống hành vi cũ — không phá vỡ project hiện có).\n *\n * Consumer cần tạo file tĩnh này (nội dung mẫu ở `modules/keycloak/htmls/silent-renew.html`); nếu THIẾU,\n * silent `check-sso` thất bại và `init()` trả về `false` không mong muốn (user bị coi là chưa đăng nhập).\n * Cho phép truyền kèm `/` đầu hoặc đuôi `.html` (tự chuẩn hoá), hoặc đặt sâu hơn:\n * `'assets/silent-renew'` → `${origin}/assets/silent-renew.html`.\n */\n silentRenewUrl?: string;\n\n /**\n * Basename (KHÔNG kèm đuôi `.html`) của **trang lỗi tĩnh** trong `public/`, dùng làm đích redirect khi\n * `keycloak.init()` NÉM LỖI (Keycloak sập / mạng lỗi / cấu hình realm sai). Mặc định `'auth-keycloak-error'`.\n *\n * Khi init thất bại, app điều hướng full-page tới `${origin}/<authErrorUrl>.html`. Vì là file tĩnh nên\n * trang lỗi vẫn hiển thị kể cả khi bundle Angular chưa khởi tạo được. Service cũng tự bỏ qua `init()` nếu\n * đang đứng sẵn ở trang lỗi này (chống vòng lặp redirect).\n *\n * Consumer nên tạo file (nội dung mẫu ở `modules/keycloak/htmls/auth-keycloak-error.html`) và viết lại\n * branding/nội dung. Nếu THIẾU, redirect rơi vào trang 404 của host. Chuẩn hoá giá trị giống `silentRenewUrl`.\n */\n authErrorUrl?: string;\n}\n\nexport interface ISdKeycloakConfiguration {\n loadTenantConfig: () => Promise<SdKeycloakTenantConfig>;\n}\n\nexport const SD_KEYCLOAK_CONFIGURATION = new InjectionToken<ISdKeycloakConfiguration>('sd-keycloak.configuration');\n","import { Injectable } from '@angular/core';\nimport Keycloak from 'keycloak-js'; // Import trực tiếp SDK gốc của Keycloak\nimport { SdKeycloakTenantConfig } from './keycloak.configuration';\n\n@Injectable({ providedIn: 'root' })\nexport class SdKeycloakService {\n public keycloak!: Keycloak;\n public config!: SdKeycloakTenantConfig;\n\n async init(config: SdKeycloakTenantConfig): Promise<boolean> {\n this.config = config;\n\n // Đường dẫn 2 file tĩnh trong public/ (consumer cấu hình qua silentRenewUrl / authErrorUrl).\n const silentRenewHref = this.toPublicHtmlUrl(config.silentRenewUrl || 'silent-renew');\n const authErrorHref = this.toPublicHtmlUrl(config.authErrorUrl || 'auth-keycloak-error');\n\n // Nếu đang đứng ở chính trang lỗi → KHÔNG init lại (chống vòng lặp redirect khi init liên tục lỗi).\n if (window.location.pathname === new URL(authErrorHref).pathname) {\n return false;\n }\n\n // 1. Khởi tạo instance\n this.keycloak = new Keycloak({\n url: config.url,\n realm: config.realm,\n clientId: config.clientId,\n });\n\n // 2. Lắng nghe sự kiện hết hạn token để tự động làm mới ngầm\n this.keycloak.onTokenExpired = () => {\n this.keycloak.updateToken(30).catch(() => {\n // @i18n-ignore — dev console warning\n console.warn('Token refresh failed. Re-authentication required.');\n this.keycloak.login();\n });\n };\n\n // 3. Thực thi quá trình boot Keycloak; nếu lỗi → điều hướng tới trang lỗi tĩnh trong public/.\n try {\n return await this.keycloak.init({\n onLoad: 'check-sso',\n silentCheckSsoRedirectUri: silentRenewHref,\n checkLoginIframe: false, // Tắt check Iframe để chống lỗi vòng lặp\n });\n } catch (error) {\n // @i18n-ignore — dev console error\n console.error('Keycloak init failed:', error);\n window.location.href = authErrorHref;\n return false;\n }\n }\n\n /**\n * Chuẩn hoá basename file tĩnh trong public/ thành URL tuyệt đối.\n * `'silent-renew'` | `'/silent-renew'` | `'silent-renew.html'` → `${origin}/silent-renew.html`.\n */\n private toPublicHtmlUrl(name: string): string {\n const base = name.trim().replace(/^\\/+/, '').replace(/\\.html$/i, '');\n return `${window.location.origin}/${base}.html`;\n }\n\n // Tiện ích nhanh cho Dev sử dụng\n login() { return this.keycloak.login(); }\n logout() { return this.keycloak.logout({ redirectUri: window.location.origin }); }\n getToken() { return this.keycloak.token; }\n getIsAuthenticated() { return this.keycloak.authenticated; }\n}\n","import { HttpInterceptorFn } from '@angular/common/http';\nimport { inject } from '@angular/core';\nimport { from, switchMap } from 'rxjs';\nimport { SdKeycloakService } from './keycloak.service';\n\nexport const SdKeycloakInterceptor: HttpInterceptorFn = (req, next) => {\n const keycloakService = inject(SdKeycloakService);\n const { keycloak, config } = keycloakService;\n\n // Nếu chưa init xong hoặc chưa đăng nhập -> cho request đi qua bình thường\n if (!keycloak || !keycloak.authenticated || !config) {\n return next(req);\n }\n\n // Kiểm tra xem URL của request có nằm trong mảng secureRoutes không\n const isSecure = config.secureRoutes?.some(route => req.url.includes(route));\n if (!isSecure) {\n return next(req);\n }\n\n // Đảm bảo token luôn hợp lệ (cập nhật nếu token sẽ hết hạn trong 30s tới)\n return from(keycloak.updateToken(30)).pipe(\n switchMap(() => {\n const authReq = req.clone({\n headers: req.headers.set('Authorization', `Bearer ${keycloak.token}`)\n });\n return next(authReq);\n })\n );\n};","import { \n ModuleWithProviders, \n NgModule, \n Provider, \n Type, \n EnvironmentProviders, \n makeEnvironmentProviders, \n provideAppInitializer, \n inject,\n APP_INITIALIZER\n} from '@angular/core';\nimport { ISdKeycloakConfiguration, SD_KEYCLOAK_CONFIGURATION } from './keycloak.configuration';\nimport { SdKeycloakService } from './keycloak.service';\n\n// =======================================================================\n// CÁCH 1: Dùng cho Angular 19 Standalone (Gọi trong app.config.ts)\n// =======================================================================\nexport function provideSdKeycloak(options: {\n useClass?: Type<ISdKeycloakConfiguration>;\n // eslint-disable-next-line @typescript-eslint/no-explicit-any\n useFactory?: (...args: any[]) => ISdKeycloakConfiguration;\n // eslint-disable-next-line @typescript-eslint/no-explicit-any\n deps?: any[];\n}): EnvironmentProviders {\n \n // FIX 1: Khai báo mảng nhận cả Provider (cho Service/Token) lẫn EnvironmentProviders (cho AppInitializer)\n const providers: Array<Provider | EnvironmentProviders> = [SdKeycloakService];\n\n if (options.useFactory) {\n providers.push({ provide: SD_KEYCLOAK_CONFIGURATION, useFactory: options.useFactory, deps: options.deps || [] });\n } else if (options.useClass) {\n providers.push({ provide: SD_KEYCLOAK_CONFIGURATION, useClass: options.useClass });\n }\n\n // Standalone dùng được provideAppInitializer ngon lành\n providers.push(\n provideAppInitializer(() => {\n const configLoader = inject(SD_KEYCLOAK_CONFIGURATION);\n const keycloakService = inject(SdKeycloakService);\n return configLoader.loadTenantConfig().then((config) => keycloakService.init(config));\n })\n );\n\n return makeEnvironmentProviders(providers);\n}\n\n// =======================================================================\n// CÁCH 2: Dùng cho kiến trúc NgModule cũ (Backward Compatibility)\n// =======================================================================\n@NgModule({})\nexport class SdKeycloakModule {\n static forRoot(options: {\n useClass?: Type<ISdKeycloakConfiguration>;\n // eslint-disable-next-line @typescript-eslint/no-explicit-any\n useFactory?: (...args: any[]) => ISdKeycloakConfiguration;\n // eslint-disable-next-line @typescript-eslint/no-explicit-any\n deps?: any[];\n }): ModuleWithProviders<SdKeycloakModule> {\n \n return {\n ngModule: SdKeycloakModule,\n providers: [\n SdKeycloakService,\n ...(options.useFactory \n ? [{ provide: SD_KEYCLOAK_CONFIGURATION, useFactory: options.useFactory, deps: options.deps || [] }]\n : [{ provide: SD_KEYCLOAK_CONFIGURATION, useClass: options.useClass! }]\n ),\n \n // FIX 2: NgModule bắt buộc phải dùng APP_INITIALIZER (nhưng viết kiểu xịn của Angular 14+, dùng inject)\n {\n provide: APP_INITIALIZER,\n multi: true,\n useFactory: () => {\n const configLoader = inject(SD_KEYCLOAK_CONFIGURATION);\n const keycloakService = inject(SdKeycloakService);\n return () => configLoader.loadTenantConfig().then((config) => keycloakService.init(config));\n }\n }\n ]\n };\n }\n}","/**\n * Generated bundle index. Do not edit.\n */\n\nexport * from './index';\n"],"names":[],"mappings":"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"}