@goat-bravos/shared-lib-client 1.0.2 → 1.0.3

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.
package/README.md CHANGED
@@ -1,26 +1,26 @@
1
1
  # @goat-bravos/shared-lib-client
2
2
 
3
- A specialized TypeScript library for InternHub micro-frontend applications. This library provides shared utilities, global state management (Signals), authentication interceptors, and standardized API interfaces.
3
+ Thư viện TypeScript dùng chung cho hệ sinh thái micro-frontend của InternHub. Thư viện cung cấp utility tái sử dụng, global state bằng Signals, auth interceptor các interface API chuẩn hóa.
4
4
 
5
- ## 🚀 Features
5
+ ## 🚀 Tính năng
6
6
 
7
- - 🚦 **Global Store**: Shared application state (User, Theme, Loading, Language) using Angular Signals.
8
- - 🔐 **Auth Interceptor**: Automatic JWT token injection and smart token refresh mechanism.
9
- - 🌍 **I18n Support**: Standardized language management with persistent storage.
10
- - 📦 **Type-Safe API**: Consistent `ResponseApi<T>` interfaces and HTTP enums.
11
- - 💾 **Storage Utils**: Type-safe wrapper for `localStorage`.
7
+ - 🚦 **Global Store**: Quản state dùng chung (User, Theme, Loading, Language) bằng Angular Signals.
8
+ - 🔐 **Auth Interceptor**: Tự gắn JWT vào header đồng bộ luồng refresh token qua event.
9
+ - 🌍 **I18n Support**: Quản ngôn ngữ thống nhất và lưu bền vững trong localStorage.
10
+ - 📦 **Type-Safe API**: Chuẩn hóa `ResponseApi<T>` các enum HTTP.
11
+ - 💾 **Storage Utils**: Wrapper an toàn kiểu dữ liệu cho `localStorage`.
12
12
 
13
13
  ---
14
14
 
15
- ## 📦 Installation
15
+ ## 📦 Cài đặt
16
16
 
17
17
  ```bash
18
18
  npm install @goat-bravos/shared-lib-client
19
19
  ```
20
20
 
21
- ### Peer Dependencies
21
+ ### Dependency đồng cấp
22
22
 
23
- Ensure you have the following dependencies installed in your project:
23
+ Đảm bảo project đang các dependency sau:
24
24
 
25
25
  - `@angular/core` (>= 18.0.0)
26
26
  - `@angular/common` (>= 18.0.0)
@@ -28,11 +28,11 @@ Ensure you have the following dependencies installed in your project:
28
28
 
29
29
  ---
30
30
 
31
- ## 🛠 Usage Guide
31
+ ## 🛠 Hướng dẫn sử dụng
32
32
 
33
- ### 1. Global Store (Manager State)
33
+ ### 1. Global Store (Quản lý state)
34
34
 
35
- The `GlobalStoreService` uses **Angular Signals** to provide a reactive state that can be shared across multiple micro-frontends.
35
+ `GlobalStoreService` dùng **Angular Signals** để cung cấp state phản ứng thể chia sẻ giữa nhiều micro-frontend.
36
36
 
37
37
  ```typescript
38
38
  import { inject } from "@angular/core";
@@ -41,11 +41,11 @@ import { GlobalStoreService, Language } from "@goat-bravos/shared-lib-client";
41
41
  export class AppSidebarComponent {
42
42
  private globalStore = inject(GlobalStoreService);
43
43
 
44
- // Read state
44
+ // Đọc state
45
45
  user = this.globalStore.user;
46
46
  language = this.globalStore.language;
47
47
 
48
- // Update state
48
+ // Cập nhật state
49
49
  changeLanguage(lang: Language) {
50
50
  this.globalStore.setLanguage(lang);
51
51
  }
@@ -56,30 +56,30 @@ export class AppSidebarComponent {
56
56
  }
57
57
  ```
58
58
 
59
- ### 2. Internationalization (i18n)
59
+ ### 2. Quốc tế hóa (i18n)
60
60
 
61
- Management of language state is built into the `GlobalStoreService`.
61
+ Việc quản ngôn ngữ được tích hợp sẵn trong `GlobalStoreService`.
62
62
 
63
- - **Enums**: Use the `Language` enum (`VI`, `EN`).
64
- - **Initial State**: Automatically loads from `localStorage` on initialization.
65
- - **Syncing**: Calling `setLanguage()` updates both the signal and `localStorage`.
63
+ - **Enum**: Dùng `Language` (`VI`, `EN`).
64
+ - **Khởi tạo**: Tự đọc từ `localStorage` khi service khởi tạo.
65
+ - **Đồng bộ**: Gọi `setLanguage()` sẽ cập nhật cả signal lẫn `localStorage`.
66
66
 
67
- **Usage with Ng-Zorro or other libraries:**
67
+ ** dụ với Ng-Zorro hoặc thư viện khác:**
68
68
 
69
69
  ```typescript
70
70
  import { GlobalStoreService, Language } from "@goat-bravos/shared-lib-client";
71
71
  import { en_US, vi_VN, NzI18nService } from "ng-zorro-antd/i18n";
72
72
 
73
- // In your AppComponent or localized component
74
- effect(() => {
73
+ // Trong AppComponent hoặc component có xử lý i18n
74
+ effect(() => {
75
75
  const currentLang = this.globalStore.language();
76
76
  this.i18n.setLocale(currentLang === Language.VI ? vi_VN : en_US);
77
77
  });
78
78
  ```
79
79
 
80
- ### 3. Authentication Interceptor
81
-
82
- Standardize your API calls by providing the `authInterceptor` in your `app.config.ts`.
80
+ ### 3. Bộ chặn xác thực (Authentication Interceptor)
81
+
82
+ Chuẩn hóa các API call bằng cách đăng `authInterceptor` trong `app.config.ts`.
83
83
 
84
84
  ```typescript
85
85
  import { provideHttpClient, withInterceptors } from "@angular/common/http";
@@ -90,25 +90,36 @@ export const appConfig: ApplicationConfig = {
90
90
  };
91
91
  ```
92
92
 
93
- _The interceptor automatically handles 401 Unauthorized errors by dispatching a `AUTH_TOKEN_EXPIRED` event and waiting for a refresh._
94
-
95
- ### 4. Storage Utilities
96
-
97
- Access local storage safely using `StorageUtil`.
93
+ Interceptor này không tự gọi API refresh token. Khi gặp `401 Unauthorized`, sẽ phát event `AUTH_TOKEN_EXPIRED` để Shell/Auth MFE xử refresh token, sau đó chờ access token mới được đẩy lại qua `notifyTokenRefreshed(...)`.
94
+
95
+ dụ Shell/Auth MFE:
96
+
97
+ ```typescript
98
+ import { notifyTokenRefreshed } from "@goat-bravos/shared-lib-client";
99
+
100
+ window.addEventListener("AUTH_TOKEN_EXPIRED", async () => {
101
+ const newAccessToken = await refreshTokenFromApi();
102
+ notifyTokenRefreshed(newAccessToken);
103
+ });
104
+ ```
105
+
106
+ ### 4. Tiện ích Storage
107
+
108
+ Truy cập `localStorage` an toàn thông qua `StorageUtil`.
98
109
 
99
110
  ```typescript
100
111
  import { StorageUtil, Language } from "@goat-bravos/shared-lib-client";
101
112
 
102
- // Get tokens
103
- const token = StorageUtil.getAccessToken();
104
-
105
- // Set language
106
- StorageUtil.setLanguage(Language.VI);
113
+ // Lấy token
114
+ const token = StorageUtil.getAccessToken();
115
+
116
+ // Cập nhật ngôn ngữ
117
+ StorageUtil.setLanguage(Language.VI);
107
118
  ```
108
119
 
109
- ### 5. API Response Formatting
110
-
111
- Ensure consistency across all backend responses.
120
+ ### 5. Chuẩn hóa phản hồi API
121
+
122
+ Giữ format phản hồi thống nhất giữa các backend service.
112
123
 
113
124
  ```typescript
114
125
  import { ResponseApi, SuccessResponse } from '@goat-bravos/shared-lib-client';
@@ -118,28 +129,28 @@ interface UserData {
118
129
  name: string;
119
130
  }
120
131
 
121
- // In your service
122
- getProfile(): Observable<ResponseApi<UserData>> {
132
+ // Trong service
133
+ getProfile(): Observable<ResponseApi<UserData>> {
123
134
  return this.http.get<ResponseApi<UserData>>('/api/profile');
124
135
  }
125
136
  ```
126
137
 
127
138
  ---
128
139
 
129
- ## 📂 Project Structure
140
+ ## 📂 Cấu trúc thư mục
130
141
 
131
142
  ```text
132
143
  src/
133
- ├── enums/ # Http status codes, error codes, storage keys, language
134
- ├── interceptors/ # Shared Auth & Error interceptors
135
- ├── interfaces/ # Standardized API response & pagination models
136
- ├── store/ # Global Signals Store (Singleton)
137
- ├── utils/ # Storage & helper utilities
138
- └── index.ts # Public API exports
144
+ ├── enums/ # Các enum mã HTTP, lỗi, khóa localStorage, ngôn ngữ
145
+ ├── interceptors/ # Các interceptor dùng chung cho auth và lỗi
146
+ ├── interfaces/ # Các interface chuẩn hóa cho response API phân trang
147
+ ├── store/ # Global store dùng Angular Signals (singleton)
148
+ ├── utils/ # Các utility cho storage và helper
149
+ └── index.ts # Điểm export public của thư viện
139
150
  ```
140
151
 
141
152
  ---
142
153
 
143
- ## 🛡️ License
154
+ ## 🛡️ Giấy phép
144
155
 
145
156
  MIT
@@ -1,12 +1,14 @@
1
1
  import { HttpInterceptorFn } from "@angular/common/http";
2
2
  /**
3
- * Shared Auth Interceptor
4
- * - Injects Authorization header
5
- * - Handles 401 errors with synchronized token refresh
3
+ * Auth interceptor dùng chung cho các micro-frontend.
4
+ * - Tự gắn header Authorization nếu có access token
5
+ * - Khi gặp lỗi 401 thì phát event để Shell/Auth xử lý refresh token
6
+ * - Các request đang chờ sẽ được đồng bộ qua `notifyTokenRefreshed`
6
7
  */
7
8
  export declare const authInterceptor: HttpInterceptorFn;
8
9
  /**
9
- * Utility to be called by the Shell/Auth MFE when a new token is received
10
+ * Được Shell/Auth MFE gọi sau khi refresh token thành công
11
+ * để đánh thức các request đang chờ.
10
12
  */
11
13
  export declare function notifyTokenRefreshed(newToken: string): void;
12
14
  //# sourceMappingURL=auth.interceptor.d.ts.map
@@ -1 +1 @@
1
- {"version":3,"file":"auth.interceptor.d.ts","sourceRoot":"","sources":["../../src/interceptors/auth.interceptor.ts"],"names":[],"mappings":"AAAA,OAAO,EAKL,iBAAiB,EAClB,MAAM,sBAAsB,CAAC;AAY9B;;;;GAIG;AACH,eAAO,MAAM,eAAe,EAAE,iBAoC7B,CAAC;AAqDF;;GAEG;AACH,wBAAgB,oBAAoB,CAAC,QAAQ,EAAE,MAAM,GAAG,IAAI,CAG3D"}
1
+ {"version":3,"file":"auth.interceptor.d.ts","sourceRoot":"","sources":["../../src/interceptors/auth.interceptor.ts"],"names":[],"mappings":"AAAA,OAAO,EAKL,iBAAiB,EAClB,MAAM,sBAAsB,CAAC;AAY9B;;;;;GAKG;AACH,eAAO,MAAM,eAAe,EAAE,iBAqC7B,CAAC;AAiDF;;;GAGG;AACH,wBAAgB,oBAAoB,CAAC,QAAQ,EAAE,MAAM,GAAG,IAAI,CAG3D"}
@@ -2,56 +2,56 @@ import { throwError, BehaviorSubject } from "rxjs";
2
2
  import { catchError, filter, take, switchMap, finalize } from "rxjs/operators";
3
3
  import { StorageUtil } from "../utils/storage.util";
4
4
  import { ErrorCode } from "../enums/error-code.enum";
5
- // Flag and Subject to manage token refreshing state globally
5
+ // Cờ subject dùng để đồng bộ các request khi access token hết hạn.
6
6
  let isRefreshing = false;
7
7
  const refreshTokenSubject = new BehaviorSubject(null);
8
8
  /**
9
- * Shared Auth Interceptor
10
- * - Injects Authorization header
11
- * - Handles 401 errors with synchronized token refresh
9
+ * Auth interceptor dùng chung cho các micro-frontend.
10
+ * - Tự gắn header Authorization nếu có access token
11
+ * - Khi gặp lỗi 401 thì phát event để Shell/Auth xử lý refresh token
12
+ * - Các request đang chờ sẽ được đồng bộ qua `notifyTokenRefreshed`
12
13
  */
13
14
  export const authInterceptor = (req, next) => {
14
- // 1. Get token from StorageUtil
15
+ // 1. Lấy access token từ localStorage
15
16
  const token = StorageUtil.getAccessToken();
16
17
  let authReq = req;
17
- // 2. Add Authorization header if token exists
18
- if (token) {
18
+ const isExcludedRequest = req.url.includes("/login") ||
19
+ req.url.includes("/hrm/users/register") ||
20
+ req.url.includes("/password-reset") ||
21
+ req.url.includes("/refresh");
22
+ // 2. Gắn Authorization header nếu request không nằm trong nhóm loại trừ
23
+ if (token && !isExcludedRequest) {
19
24
  authReq = req.clone({
20
25
  setHeaders: {
21
26
  Authorization: `Bearer ${token}`,
22
27
  },
23
28
  });
24
29
  }
25
- // 3. Process the request
30
+ // 3. Thực thi request và xử lý lỗi xác thực nếu có
26
31
  return next(authReq).pipe(catchError((error) => {
27
32
  const responseBody = error.error;
28
33
  const errorCode = responseBody?.status?.code;
29
- // Check if it's an Authentication error
30
- // 401 is standard, but the backend also uses REFRESH_TOKEN_INVALID in some contexts
34
+ // 401 trường hợp chuẩn; ngoài ra backend có thể trả mã REFRESH_TOKEN_INVALID.
31
35
  const isAuthError = error.status === 401 || errorCode === ErrorCode.REFRESH_TOKEN_INVALID;
32
- // Handle Auth error if not on login or refresh endpoints
33
- if (isAuthError &&
34
- !authReq.url.includes("/auth/login") &&
35
- !authReq.url.includes("/auth/refresh")) {
36
+ // Chỉ xử refresh cho các request nghiệp vụ, không áp dụng cho login/refresh.
37
+ if (isAuthError && !isExcludedRequest) {
36
38
  return handle401Error(authReq, next);
37
39
  }
38
40
  return throwError(() => error);
39
41
  }));
40
42
  };
41
43
  /**
42
- * Handle 401 errors by attempting to refresh the token
44
+ * Khi access token hết hạn, thư viện không tự gọi API refresh.
45
+ * Thay vào đó nó phát event `AUTH_TOKEN_EXPIRED` để Shell/Auth MFE chủ động refresh,
46
+ * sau đó đợi token mới được đẩy ngược lại qua `notifyTokenRefreshed`.
43
47
  */
44
48
  function handle401Error(request, next) {
45
49
  if (!isRefreshing) {
46
50
  isRefreshing = true;
47
51
  refreshTokenSubject.next(null);
48
- const refreshToken = StorageUtil.getRefreshToken();
49
- // Here we assume the Shell/App environment has a way to call refresh token
50
- // For a library, we might need to pass a callback or use a known endpoint
51
- // Let's assume a standard endpoint or dispatch a CustomEvent for the Shell to handle
52
- // Dispatch event so the Shell/Auth MFE can perform the actual API call
52
+ // Phát tín hiệu để Shell/Auth MFE thực hiện API refresh token thật sự.
53
53
  window.dispatchEvent(new CustomEvent("AUTH_TOKEN_EXPIRED"));
54
- // We wait for the new token to be emitted through the subject
54
+ // Đợi access token mới, sau đó phát lại request cũ.
55
55
  return refreshTokenSubject.pipe(filter((token) => token !== null), take(1), switchMap((token) => {
56
56
  return next(request.clone({
57
57
  setHeaders: { Authorization: `Bearer ${token}` },
@@ -61,7 +61,7 @@ function handle401Error(request, next) {
61
61
  }));
62
62
  }
63
63
  else {
64
- // Other requests wait in queue
64
+ // Các request đến sau sẽ chờ cùng một đợt refresh đang diễn ra.
65
65
  return refreshTokenSubject.pipe(filter((token) => token !== null), take(1), switchMap((token) => {
66
66
  return next(request.clone({
67
67
  setHeaders: { Authorization: `Bearer ${token}` },
@@ -70,7 +70,8 @@ function handle401Error(request, next) {
70
70
  }
71
71
  }
72
72
  /**
73
- * Utility to be called by the Shell/Auth MFE when a new token is received
73
+ * Được Shell/Auth MFE gọi sau khi refresh token thành công
74
+ * để đánh thức các request đang chờ.
74
75
  */
75
76
  export function notifyTokenRefreshed(newToken) {
76
77
  refreshTokenSubject.next(newToken);
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@goat-bravos/shared-lib-client",
3
- "version": "1.0.2",
3
+ "version": "1.0.3",
4
4
  "type": "module",
5
5
  "peerDependencies": {
6
6
  "@angular/common": "21.0.1",
@@ -24,11 +24,11 @@
24
24
  "dist",
25
25
  "README.md"
26
26
  ],
27
- "scripts": {
28
- "build": "tsc",
29
- "prepublishOnly": "npm run build",
30
- "publish:lib": "npm --cache .npm-cache publish"
31
- },
27
+ "scripts": {
28
+ "build": "tsc",
29
+ "prepublishOnly": "npm run build",
30
+ "publish:lib": "npm --cache .npm-cache publish"
31
+ },
32
32
  "keywords": [
33
33
  "typescript",
34
34
  "api",