@gennext/lb-infra 0.3.0 → 0.3.4

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 (119) hide show
  1. package/README.md +414 -342
  2. package/dist/base/applications/default.application.d.ts.map +1 -1
  3. package/dist/base/applications/default.application.js +9 -0
  4. package/dist/base/applications/default.application.js.map +1 -1
  5. package/dist/base/base.sequence.js +3 -4
  6. package/dist/base/base.sequence.js.map +1 -1
  7. package/dist/base/controllers/common.d.ts +9 -0
  8. package/dist/base/controllers/common.d.ts.map +1 -1
  9. package/dist/base/controllers/common.js +20 -1
  10. package/dist/base/controllers/common.js.map +1 -1
  11. package/dist/base/controllers/crud.controller.d.ts +33 -22
  12. package/dist/base/controllers/crud.controller.d.ts.map +1 -1
  13. package/dist/base/controllers/crud.controller.js +20 -332
  14. package/dist/base/controllers/crud.controller.js.map +1 -1
  15. package/dist/base/controllers/crud.executor.d.ts +69 -0
  16. package/dist/base/controllers/crud.executor.d.ts.map +1 -0
  17. package/dist/base/controllers/crud.executor.js +140 -0
  18. package/dist/base/controllers/crud.executor.js.map +1 -0
  19. package/dist/base/controllers/crud.mixin.d.ts +77 -0
  20. package/dist/base/controllers/crud.mixin.d.ts.map +1 -0
  21. package/dist/base/controllers/crud.mixin.js +269 -0
  22. package/dist/base/controllers/crud.mixin.js.map +1 -0
  23. package/dist/base/controllers/index.d.ts +2 -0
  24. package/dist/base/controllers/index.d.ts.map +1 -1
  25. package/dist/base/controllers/index.js +2 -0
  26. package/dist/base/controllers/index.js.map +1 -1
  27. package/dist/base/controllers/kv.controller.js +2 -2
  28. package/dist/base/controllers/kv.controller.js.map +1 -1
  29. package/dist/base/controllers/relational.controller.d.ts.map +1 -1
  30. package/dist/base/controllers/relational.controller.js +67 -118
  31. package/dist/base/controllers/relational.controller.js.map +1 -1
  32. package/dist/base/controllers/service-crud.controller.d.ts +11 -10
  33. package/dist/base/controllers/service-crud.controller.d.ts.map +1 -1
  34. package/dist/base/controllers/service-crud.controller.js +24 -373
  35. package/dist/base/controllers/service-crud.controller.js.map +1 -1
  36. package/dist/base/repositories/base.repository.d.ts.map +1 -1
  37. package/dist/base/repositories/base.repository.js +2 -2
  38. package/dist/base/repositories/base.repository.js.map +1 -1
  39. package/dist/base/repositories/relations/has-many-polymorphic/factory.d.ts.map +1 -1
  40. package/dist/base/repositories/relations/has-many-polymorphic/factory.js +1 -5
  41. package/dist/base/repositories/relations/has-many-polymorphic/factory.js.map +1 -1
  42. package/dist/base/repositories/relations/has-many-through-polymorphic/factory.d.ts.map +1 -1
  43. package/dist/base/repositories/relations/has-many-through-polymorphic/factory.js +9 -10
  44. package/dist/base/repositories/relations/has-many-through-polymorphic/factory.js.map +1 -1
  45. package/dist/base/repositories/searchable-tz-crud.repository.d.ts.map +1 -1
  46. package/dist/base/repositories/searchable-tz-crud.repository.js +8 -9
  47. package/dist/base/repositories/searchable-tz-crud.repository.js.map +1 -1
  48. package/dist/base/repositories/tz-crud.repository.d.ts.map +1 -1
  49. package/dist/base/repositories/tz-crud.repository.js +3 -6
  50. package/dist/base/repositories/tz-crud.repository.js.map +1 -1
  51. package/dist/common/constants.d.ts +1 -0
  52. package/dist/common/constants.d.ts.map +1 -1
  53. package/dist/common/constants.js +1 -0
  54. package/dist/common/constants.js.map +1 -1
  55. package/dist/common/environments.d.ts +19 -0
  56. package/dist/common/environments.d.ts.map +1 -1
  57. package/dist/common/environments.js +104 -1
  58. package/dist/common/environments.js.map +1 -1
  59. package/dist/common/keys.d.ts +16 -0
  60. package/dist/common/keys.d.ts.map +1 -1
  61. package/dist/common/keys.js +23 -0
  62. package/dist/common/keys.js.map +1 -1
  63. package/dist/components/authenticate/component.js +1 -1
  64. package/dist/components/authenticate/component.js.map +1 -1
  65. package/dist/components/authenticate/controllers/auth.controller.d.ts +8 -0
  66. package/dist/components/authenticate/controllers/auth.controller.d.ts.map +1 -1
  67. package/dist/components/authenticate/controllers/auth.controller.js +9 -8
  68. package/dist/components/authenticate/controllers/auth.controller.js.map +1 -1
  69. package/dist/components/authenticate/controllers/oauth2.controller.d.ts +48 -16
  70. package/dist/components/authenticate/controllers/oauth2.controller.d.ts.map +1 -1
  71. package/dist/components/authenticate/controllers/oauth2.controller.js +4 -4
  72. package/dist/components/authenticate/controllers/oauth2.controller.js.map +1 -1
  73. package/dist/components/authenticate/oauth2-handlers/base.d.ts.map +1 -1
  74. package/dist/components/authenticate/oauth2-handlers/base.js +8 -12
  75. package/dist/components/authenticate/oauth2-handlers/base.js.map +1 -1
  76. package/dist/components/authenticate/oauth2-handlers/data/user-data-fetcher.d.ts.map +1 -1
  77. package/dist/components/authenticate/oauth2-handlers/data/user-data-fetcher.js +2 -1
  78. package/dist/components/authenticate/oauth2-handlers/data/user-data-fetcher.js.map +1 -1
  79. package/dist/components/authenticate/services/basic.strategy.d.ts.map +1 -1
  80. package/dist/components/authenticate/services/basic.strategy.js +4 -3
  81. package/dist/components/authenticate/services/basic.strategy.js.map +1 -1
  82. package/dist/components/authenticate/services/jwt.strategy.d.ts.map +1 -1
  83. package/dist/components/authenticate/services/jwt.strategy.js +4 -3
  84. package/dist/components/authenticate/services/jwt.strategy.js.map +1 -1
  85. package/dist/components/authenticate/services/oauth2-scope.service.d.ts.map +1 -1
  86. package/dist/components/authenticate/services/oauth2-scope.service.js +2 -1
  87. package/dist/components/authenticate/services/oauth2-scope.service.js.map +1 -1
  88. package/dist/components/authenticate/services/oauth2.service.js +1 -1
  89. package/dist/components/authenticate/services/oauth2.service.js.map +1 -1
  90. package/dist/components/crash-report/component.js +1 -1
  91. package/dist/components/crash-report/providers/provider.d.ts.map +1 -1
  92. package/dist/components/crash-report/providers/provider.js +5 -4
  93. package/dist/components/crash-report/providers/provider.js.map +1 -1
  94. package/dist/components/crash-report/services/{mt-crash-report.service.d.ts → gn-crash-report.service.d.ts} +2 -2
  95. package/dist/components/crash-report/services/{mt-crash-report.service.d.ts.map → gn-crash-report.service.d.ts.map} +1 -1
  96. package/dist/components/crash-report/services/{mt-crash-report.service.js → gn-crash-report.service.js} +6 -7
  97. package/dist/components/crash-report/services/gn-crash-report.service.js.map +1 -0
  98. package/dist/components/crash-report/services/index.d.ts +1 -1
  99. package/dist/components/crash-report/services/index.js +1 -1
  100. package/dist/components/grpc/helpers/grpc-server.d.ts.map +1 -1
  101. package/dist/components/grpc/helpers/grpc-server.js +4 -6
  102. package/dist/components/grpc/helpers/grpc-server.js.map +1 -1
  103. package/dist/components/migration/component.d.ts.map +1 -1
  104. package/dist/components/migration/component.js +0 -4
  105. package/dist/components/migration/component.js.map +1 -1
  106. package/dist/datasources/postgres/datasource.js +1 -2
  107. package/dist/datasources/postgres/datasource.js.map +1 -1
  108. package/dist/helpers/testing/test-case.js +1 -2
  109. package/dist/helpers/testing/test-case.js.map +1 -1
  110. package/dist/interceptors/content-range.interceptor.d.ts.map +1 -1
  111. package/dist/interceptors/content-range.interceptor.js +6 -5
  112. package/dist/interceptors/content-range.interceptor.js.map +1 -1
  113. package/dist/migrations/handler.js +1 -1
  114. package/dist/migrations/handler.js.map +1 -1
  115. package/dist/utilities/parse.utility.d.ts.map +1 -1
  116. package/dist/utilities/parse.utility.js +1 -2
  117. package/dist/utilities/parse.utility.js.map +1 -1
  118. package/package.json +1 -1
  119. package/dist/components/crash-report/services/mt-crash-report.service.js.map +0 -1
package/README.md CHANGED
@@ -2,24 +2,23 @@
2
2
 
3
3
  Gennex Technology - LoopBack 4 infrastructure framework.
4
4
 
5
- `@gennext/lb-infra` provides shared base classes, components, helpers, datasources, model mixins, repositories and controller generators for building backend services on top of LoopBack 4.
5
+ `@gennext/lb-infra` cung cấp các lớp cơ sở (base classes), components, helpers, datasources, model mixins, repositories các controller generator dùng chung để xây dựng các dịch vụ backend chất lượng cao trên nền tảng LoopBack 4.
6
6
 
7
- ## Requirements
7
+ ---
8
8
 
9
- - Node.js `>= 18`
10
- - Bun `>= 1.3.2`
11
- - TypeScript
9
+ ## 1. Requirements
12
10
 
13
- Install dependencies from the repository root:
11
+ - **Node.js**: `>= 18`
12
+ - **Bun**: `>= 1.3.2`
13
+ - **TypeScript**
14
+
15
+ Cài đặt dependencies từ thư mục gốc của repository:
14
16
 
15
17
  ```bash
16
18
  bun install
17
19
  ```
18
20
 
19
- ## Package Scripts
20
-
21
- Run commands from the repository root:
22
-
21
+ ### Các lệnh chạy trong package:
23
22
  ```bash
24
23
  bun run --filter "@gennext/lb-infra" build
25
24
  bun run --filter "@gennext/lb-infra" clean
@@ -28,33 +27,28 @@ bun run --filter "@gennext/lb-infra" lint
28
27
  bun run --filter "@gennext/lb-infra" test
29
28
  ```
30
29
 
31
- Root shortcut:
32
-
30
+ Lệnh tắt từ root:
33
31
  ```bash
34
32
  bun run rebuild:lb-infra
35
33
  ```
36
34
 
37
- The build script compiles TypeScript, resolves aliases with `tsc-alias`, then copies runtime assets into `dist`:
38
-
39
- - `src/components/authenticate/views`
40
- - `static`
41
- - `tsconfig.json` as `dist/tsconfig.base.json`
35
+ ---
42
36
 
43
- ## CLI
37
+ ## 2. CLI
44
38
 
45
- After the package is installed, the `lb-infra` binary can scaffold the default consumer-app folder structure:
39
+ Sau khi cài đặt package, binary `lb-infra` thể được sử dụng để khởi tạo cấu trúc thư mục mặc định cho dự án consumer-app:
46
40
 
47
41
  ```bash
48
42
  lb-infra init
49
43
  ```
50
44
 
51
- Or target another directory:
45
+ Hoặc trỏ tới một thư mục cụ thể:
52
46
 
53
47
  ```bash
54
48
  lb-infra init ./apps/api
55
49
  ```
56
50
 
57
- Generated structure:
51
+ Cấu trúc thư mục được sinh ra:
58
52
 
59
53
  ```text
60
54
  src
@@ -72,13 +66,17 @@ src
72
66
  public
73
67
  ```
74
68
 
75
- The command creates `index.ts` placeholders for TypeScript folders and `.gitkeep` files for asset/proto/migration folders. Existing placeholder files are not overwritten unless `--force` is passed:
69
+ Các file placeholder `index.ts` `.gitkeep` sẽ được tự động tạo. Sử dụng cờ `--force` để ghi đè các file đã có:
76
70
 
77
71
  ```bash
78
72
  lb-infra init ./apps/api --force
79
73
  ```
80
74
 
81
- ## Main Imports
75
+ ---
76
+
77
+ ## 3. Main Imports & Re-exports
78
+
79
+ Sử dụng trực tiếp các thành phần chính được cung cấp bởi framework:
82
80
 
83
81
  ```ts
84
82
  import {
@@ -91,7 +89,7 @@ import {
91
89
  } from '@gennext/lb-infra';
92
90
  ```
93
91
 
94
- LoopBack re-export aliases:
92
+ Sử dụng các alias re-export của LoopBack để đảm bảo đồng bộ phiên bản:
95
93
 
96
94
  ```ts
97
95
  import {inject, injectable, BindingScope} from '@gennext/lb-infra/lb-core';
@@ -100,229 +98,452 @@ import {model, property, repository} from '@gennext/lb-infra/lb-repo';
100
98
  import {authenticate, TokenServiceBindings} from '@gennext/lb-infra/lb-auth';
101
99
  ```
102
100
 
103
- Optional feature exports:
101
+ Các tính năng tùy chọn:
104
102
 
105
103
  ```ts
106
104
  import {SocketIOComponent} from '@gennext/lb-infra/socket-io';
107
105
  import {GrpcServerComponent} from '@gennext/lb-infra/grpc';
108
106
  ```
109
107
 
110
- ## Project Layout
108
+ ---
109
+
110
+ ## 4. Cấu trúc thư mục (Project Layout)
111
111
 
112
112
  ```text
113
- src
114
- ├── base # base application, sequence, models, repositories, controllers
115
- ├── common # constants, environment keys, shared types
116
- ├── components # auth, authorize, migration, mail, socket.io, grpc, health check
117
- ├── datasources # postgres, redis, memory datasources
118
- ├── helpers # logger, queue, redis, network, crypto, storage, testing
119
- ├── interceptors # global interceptors
120
- ├── middlewares # request middleware
121
- ├── migrations # migration helpers
122
- ├── mixins # model mixins
123
- └── utilities # shared utility functions
113
+ src/
114
+ ├── base/ # Các base class cốt lõi (Application, Sequence, Models, Repositories, Controllers, Services)
115
+ ├── common/ # Định nghĩa constants, kiểu dữ liệu dùng chung (Types), keys và environments
116
+ ├── components/ # Các LoopBack 4 Components cắm ngoài (Auth, Authorize, Mail, Health, v.v.)
117
+ ├── datasources/ # Nguồn cấp dữ liệu (Postgres, Redis, In-memory KV)
118
+ ├── helpers/ # Các wrapper helper: logger, hàng đợi queue, cryptography, testing, cronjob
119
+ ├── interceptors/ # Global Interceptors (ví dụ: Content-Range header)
120
+ ├── middlewares/ # Express Middlewares (Request body parser, Request spy)
121
+ ├── migrations/ # Script chạy migration cơ sở dữ liệu
122
+ ├── mixins/ # TypeScript mixins dùng cho LoopBack Models
123
+ └── utilities/ # Hàm tiện ích (parse data, query helper, format, error handler)
124
124
  ```
125
125
 
126
- ## Base Application
127
-
128
- Use `BaseApplication` when you want full control over configuration:
129
-
130
- ```ts
131
- import {BaseApplication} from '@gennext/lb-infra';
132
- import {ApplicationConfig} from '@gennext/lb-infra/lb-core';
133
-
134
- export class MyApplication extends BaseApplication {
135
- constructor(options: ApplicationConfig = {}) {
136
- super({
137
- scope: 'MyApplication',
138
- serverOptions: options,
139
- });
140
- }
141
-
142
- staticConfigure(): void {
143
- // Bind components, datasources, repositories, services and controllers.
144
- }
126
+ ---
145
127
 
146
- getProjectRoot(): string {
147
- return __dirname;
148
- }
128
+ ## 5. Kiến trúc ứng dụng & Vòng đời (Architecture & Lifecycle)
149
129
 
150
- validateEnv() {
151
- return {result: true};
152
- }
130
+ Ứng dụng kế thừa từ `BaseApplication` hoặc `DefaultRestApplication` tuân theo thứ tự khởi động và nạp cấu hình nghiêm ngặt:
153
131
 
154
- declareModels(): Set<string> {
155
- return new Set([]);
156
- }
157
-
158
- preConfigure(): void {}
159
-
160
- postConfigure(): void {}
161
- }
132
+ ```text
133
+ New Application Instance
134
+
135
+
136
+ staticConfigure()
137
+
138
+
139
+ initialize()
140
+
141
+
142
+ validateEnv()
143
+
144
+
145
+ declareModels()
146
+
147
+
148
+ preConfigure()
149
+
150
+
151
+ Loopback Bootstrapping
152
+
153
+
154
+ postConfigure()
155
+
156
+
157
+ Application Ready
162
158
  ```
163
159
 
164
- Use `DefaultRestApplication` when you want the standard setup with environment validation, PostgreSQL, in-memory KV datasource, migration component, content-range interceptor and boot options.
160
+ ### Chi tiết các lifecycle hooks:
165
161
 
166
- ## Models And Mixins
162
+ 1. **`staticConfigure()`**: Được gọi đầu tiên trong constructor của `BaseApplication`.
163
+ - *Khi nào dùng*: Thực hiện bind trực tiếp các giá trị tĩnh, cấu hình component, datasources tĩnh trước khi nạp sequence hay cấu hình động.
164
+ 2. **`validateEnv()`**: Được gọi khi initialize.
165
+ - *Khi nào dùng*: Thực hiện kiểm tra các biến môi trường thông qua `validateEnvironment()` theo schema định sẵn. Nếu trả về `result: false`, ứng dụng sẽ dừng boot ngay lập tức kèm thông tin lỗi chi tiết.
166
+ 3. **`declareModels()`**: Được gọi ngay sau kiểm tra môi trường.
167
+ - *Khi nào dùng*: Đăng ký danh sách các model của consumer app với metadata schema.
168
+ 4. **`preConfigure()`**: Chạy trước khi LoopBack thực hiện boot process quét thư mục.
169
+ - *Khi nào dùng*: Bind cấu hình dynamic, đăng ký interceptors, khai báo global middleware, thiết lập kết nối datasource.
170
+ 5. **`postConfigure()`**: Chạy sau khi ứng dụng hoàn tất quá trình boot.
171
+ - *Khi nào dùng*: Thực hiện các tác vụ khởi tạo muộn, kiểm tra tính sẵn sàng của database hoặc thiết lập lắng nghe hàng đợi (worker).
167
172
 
168
- Common base models:
173
+ ---
169
174
 
170
- - `BaseEntity`
171
- - `BaseKVEntity`
172
- - `BaseTzEntity`
173
- - `BaseUserAuditTzEntity`
174
- - `BaseTextSearchTzEntity`
175
- - `BaseObjectSearchTzEntity`
176
- - `BaseSearchableTzEntity`
177
- - `BaseSoftDeleteTzEntity`
175
+ ## 6. Quản lý phụ thuộc (Dependency Graph & DI Binding Keys)
178
176
 
179
- Common mixins:
177
+ Framework chuẩn hóa cách đặt tên binding keys để tránh sử dụng các chuỗi string thô không an toàn thông qua lớp static `BindingKeys`:
180
178
 
181
- - `IdMixin`
182
- - `TzMixin`
183
- - `UserAuditMixin`
184
- - `SoftDeleteModelMixin`
185
- - `SoftPersistentMixin`
186
- - `TextSearchMixin`
187
- - `ObjectSearchMixin`
188
- - `VectorMixin`
189
- - `DuplicatableMixin`
190
- - `DataTypeMixin`
191
- - `PrincipalMixin`
179
+ - **Datasource bindings**: `datasources.postgres`, `datasources.redis`
180
+ - **Repository bindings**: `repositories.XxxRepository` (được tạo tự động thông qua `BindingKeys.repositoryFor('XxxRepository')`)
181
+ - **Service bindings**: `services.XxxService` (được tạo tự động thông qua `BindingKeys.serviceFor('XxxService')`)
192
182
 
193
- Example:
183
+ ### Ví dụ Wire Controller - Service - Repository thông qua `@inject`:
194
184
 
195
185
  ```ts
196
- import {BaseUserAuditTzEntity} from '@gennext/lb-infra';
197
- import {model, property} from '@gennext/lb-infra/lb-repo';
186
+ import {BindingKeys} from '@gennext/lb-infra';
187
+ import {inject, injectable, BindingScope} from '@gennext/lb-infra/lb-core';
198
188
 
199
- @model({
200
- settings: {
201
- postgresql: {
202
- schema: 'public',
203
- table: 'user',
204
- },
205
- strict: true,
206
- },
207
- })
208
- export class User extends BaseUserAuditTzEntity {
209
- @property({type: 'string', required: true})
210
- email: string;
189
+ @injectable({scope: BindingScope.SINGLETON})
190
+ export class ProductRepository extends TzCrudRepository<Product> {
191
+ constructor(
192
+ @inject('datasources.postgres') dataSource: PostgresDataSource
193
+ ) {
194
+ super(Product, dataSource);
195
+ }
196
+ }
197
+
198
+ @injectable({scope: BindingScope.TRANSIENT})
199
+ export class ProductService extends BaseCrudService<Product> {
200
+ constructor(
201
+ @inject(BindingKeys.repositoryFor('ProductRepository'))
202
+ protected productRepository: ProductRepository
203
+ ) {
204
+ super({ scope: ProductService.name, repository: productRepository });
205
+ }
211
206
  }
212
207
  ```
213
208
 
214
- ## Repositories
209
+ ---
210
+
211
+ ## 7. Hệ thống Models & Mixins
215
212
 
216
- Use the base repositories in `src/base/repositories`:
213
+ Framework cung cấp sẵn các base class tích hợp mixin nhằm tái sử dụng thuộc tính dữ liệu:
217
214
 
218
- - `TzCrudRepository`
219
- - `SearchableTzCrudRepository`
220
- - `KVRepository`
215
+ | Base Model Class | Các Mixin tích hợp sẵn | Mục đích / Khi nào nên dùng |
216
+ | :--- | :--- | :--- |
217
+ | `BaseEntity` | Không | Model cơ bản nhất không có ID và audit log |
218
+ | `BaseKVEntity` | Không | Dành cho các cặp dữ liệu dạng Key-Value (chứa trường `payload`) |
219
+ | `BaseTzEntity` | `TzMixin` | Tự động tạo và cập nhật `createdAt`, `modifiedAt` |
220
+ | `BaseUserAuditTzEntity` | `TzMixin`, `UserAuditMixin` | Tự động cập nhật timestamp cùng thông tin user tạo/sửa (`createdBy`, `modifiedBy`) |
221
+ | `BaseTextSearchTzEntity` | `TzMixin`, `TextSearchMixin` | Tự động build trường text search `textSearch` |
222
+ | `BaseObjectSearchTzEntity` | `TzMixin`, `ObjectSearchMixin` | Tự động build trường search JSONB `objectSearch` |
223
+ | `BaseSearchableTzEntity` | `TzMixin`, `TextSearchMixin`, `ObjectSearchMixin` | Model tìm kiếm tổng hợp (cả text và object search) |
224
+ | `BaseSoftDeleteTzEntity` | `TzMixin`, `SoftDeleteModelMixin` | Hỗ trợ xóa mềm (đánh dấu cờ `isDeleted` và `deletedAt`) |
225
+ | `BaseVectorEntity` | `TzMixin`, `VectorMixin` | Hỗ trợ lưu trữ vector embedding (cho tìm kiếm AI / pgvector) |
221
226
 
222
- Example:
227
+ ---
228
+
229
+ ## 8. Repository nâng cao
230
+
231
+ Framework cung cấp lớp `TzCrudRepository` và `SearchableTzCrudRepository` kế thừa từ LoopBack repository gốc nhưng được bổ sung cơ chế xử lý timezone và audit user tự động:
223
232
 
224
233
  ```ts
225
234
  import {PostgresDataSource, TzCrudRepository} from '@gennext/lb-infra';
226
- import {inject, injectable, BindingScope} from '@gennext/lb-infra/lb-core';
235
+ import {inject} from '@gennext/lb-infra/lb-core';
227
236
 
228
- @injectable({scope: BindingScope.SINGLETON})
229
- export class UserRepository extends TzCrudRepository<User> {
237
+ export class ProductRepository extends TzCrudRepository<Product> {
230
238
  constructor(@inject('datasources.postgres') dataSource: PostgresDataSource) {
231
- super(User, dataSource);
239
+ super(Product, dataSource);
240
+ }
241
+
242
+ // Ví dụ custom query phức tạp (sử dụng Knex query builder của PostgresDataSource)
243
+ async findActiveProductsWithMinPrice(minPrice: number): Promise<Product[]> {
244
+ const table = this.modelClass.definition.settings.postgresql.table;
245
+ const query = this.dataSource
246
+ .connector!.execute(`SELECT * FROM ${table} WHERE price >= $1 AND is_deleted = false`, [minPrice]);
247
+ return query;
232
248
  }
233
249
  }
234
250
  ```
235
251
 
236
- ## Controller Generators
237
-
238
- ### CRUD
252
+ ---
239
253
 
240
- `defineCrudController` creates standard CRUD routes:
254
+ ## 9. Bộ tạo Controller tự động (Controller Generator)
241
255
 
242
- - `GET /`
243
- - `POST /`
244
- - `PATCH /`
245
- - `GET /count`
246
- - `GET /find-one`
247
- - `GET /{id}`
248
- - `PATCH /{id}`
249
- - `PUT /{id}`
250
- - `DELETE /{id}`
256
+ Framework cho phép sinh nhanh các route REST chuẩn RESTful chỉ qua cấu hình tùy chọn:
251
257
 
252
- Example:
258
+ ### 1. defineCrudController
259
+ Sinh ra các endpoints: `GET /`, `POST /`, `PATCH /`, `GET /count`, `GET /find-one`, `GET /{id}`, `PATCH /{id}`, `PUT /{id}`, `DELETE /{id}`.
253
260
 
254
261
  ```ts
255
262
  import {defineCrudController} from '@gennext/lb-infra';
256
263
  import {api} from '@gennext/lb-infra/lb-rest';
257
264
 
258
- const BaseUserController = defineCrudController<User>({
259
- entity: User,
260
- repository: {name: 'UserRepository'},
265
+ const BaseController = defineCrudController<Product>({
266
+ entity: Product,
267
+ repository: {name: 'ProductRepository'},
261
268
  controller: {
262
- basePath: '/users',
263
- readonly: false,
269
+ basePath: '/products',
270
+ readonly: false, // Nếu true, chỉ sinh ra các route GET/count/find-one
271
+ defaultLimit: 100
264
272
  },
273
+ doInjectCurrentUser: true, // Tự động inject context user vào audit log
274
+ doDeleteWithReturn: true // delete trả về object {id}
265
275
  });
266
276
 
267
- @api({basePath: '/users'})
268
- export class UserController extends BaseUserController {}
277
+ @api({basePath: '/products'})
278
+ export class ProductController extends BaseController {
279
+ // Bạn có thể kế thừa và viết đè hoặc bổ sung custom method tại đây
280
+ }
269
281
  ```
270
282
 
271
- When extending generated controllers, keep custom method paths relative to the class base path:
283
+ ### 2. defineKVController
284
+ Dành cho mô hình Key-Value lưu vào memory hoặc Redis:
285
+ - Endpoints sinh ra: `GET /{key}`, `POST /{key}`, `DELETE /{key}`.
272
286
 
273
- ```ts
274
- @get('/search')
275
- search() {}
276
- ```
287
+ ### 3. defineRelationCrudController / defineRelationViewController
288
+ Dành cho quan hệ (HasMany, BelongsTo, HasManyThrough). Sinh ra các REST API quản lý mối quan hệ giữa Source và Target model (ví dụ: `POST /users/{id}/posts`, `GET /users/{id}/posts`).
277
289
 
278
- ### Key-Value
290
+ ---
279
291
 
280
- Use `defineKVController` for `BaseKVEntity` models and key-value repositories:
292
+ ## 10. Hướng dẫn thiết kế Custom Controller - Service - Repository
281
293
 
282
- ```ts
283
- @api({basePath: '/sessions'})
284
- export class SessionController extends defineKVController<Session>({
285
- entity: Session,
286
- repository: {name: 'SessionRepository'},
287
- controller: {
288
- basePath: '/sessions',
289
- readonly: false,
290
- },
291
- }) {}
292
- ```
294
+ Framework tuân thủ nghiêm ngặt quy tắc phân tách trách nhiệm (Separation of Concerns):
295
+ 1. **Model**: Định nghĩa cấu trúc dữ liệu và các mixin.
296
+ 2. **Repository**: Thực hiện truy vấn DB qua SQL/Knex.
297
+ 3. **Service**: Chứa logic nghiệp vụ, tính toán, gọi API ngoài, điều phối nghiệp vụ.
298
+ 4. **Controller**: Xử lý HTTP routing, nhận request, kiểm tra định dạng và gọi Service.
293
299
 
294
- ### Relations
300
+ #### Bước 1: Khai báo Model
301
+ ```ts
302
+ import {BaseUserAuditTzEntity} from '@gennext/lb-infra';
303
+ import {model, property} from '@gennext/lb-infra/lb-repo';
295
304
 
296
- Use `defineRelationViewController` or `defineRelationCrudController` for relation endpoints:
305
+ @model({
306
+ settings: {
307
+ postgresql: {
308
+ schema: 'public',
309
+ table: 'order',
310
+ },
311
+ strict: true,
312
+ },
313
+ })
314
+ export class Order extends BaseUserAuditTzEntity {
315
+ @property({type: 'string', required: true})
316
+ code: string;
297
317
 
298
- ```ts
299
- @api({basePath: '/users'})
300
- export class UserPostRelationController extends defineRelationViewController<User, Post>({
301
- entities: {source: User, target: Post},
302
- relation: {name: 'posts', type: EntityRelations.HAS_MANY},
303
- endPoint: 'posts',
304
- }) {}
318
+ @property({type: 'number', required: true})
319
+ amount: number;
320
+ }
305
321
  ```
306
322
 
307
- ## gRPC Usage Example
323
+ #### Bước 2: Tạo Repository
324
+ ```ts
325
+ import {PostgresDataSource, TzCrudRepository} from '@gennext/lb-infra';
326
+ import {inject} from '@gennext/lb-infra/lb-core';
308
327
 
309
- The gRPC module is exported from `@gennext/lb-infra/grpc`.
328
+ export class OrderRepository extends TzCrudRepository<Order> {
329
+ constructor(
330
+ @inject('datasources.postgres') dataSource: PostgresDataSource
331
+ ) {
332
+ super(Order, dataSource);
333
+ }
334
+ }
335
+ ```
310
336
 
311
- The server flow is:
337
+ #### Bước 3: Tạo Service chứa Business Logic
338
+ ```ts
339
+ import {BaseCrudService, BindingKeys} from '@gennext/lb-infra';
340
+ import {inject} from '@gennext/lb-infra/lb-core';
341
+ import {OrderRepository} from '../repositories/order.repository';
342
+ import {Order} from '../models/order.model';
343
+
344
+ export class OrderService extends BaseCrudService<Order> {
345
+ constructor(
346
+ @inject(BindingKeys.repositoryFor('OrderRepository'))
347
+ private orderRepository: OrderRepository
348
+ ) {
349
+ // Gọi constructor của BaseCrudService nhận 1 object { scope, repository }
350
+ super({ scope: OrderService.name, repository: orderRepository });
351
+ }
312
352
 
313
- 1. Create a `.proto` file.
314
- 2. Create a controller method and decorate it with `@grpcMethod`.
315
- 3. Register the controller with `app.grpcController(...)`.
316
- 4. Configure `GrpcServerKeys.GRPC_OPTIONS`.
317
- 5. Register `GrpcServerComponent`.
353
+ // Phương thức chứa logic nghiệp vụ đặc thù
354
+ async createOrderWithValidation(data: Order): Promise<Order> {
355
+ if (data.amount <= 0) {
356
+ throw new Error('Order amount must be positive');
357
+ }
358
+ // Sử dụng context trống nếu không truyền currentUser
359
+ return this.orderRepository.create(data, {});
360
+ }
361
+ }
362
+ ```
318
363
 
319
- ### Proto File
364
+ #### Bước 4: Tạo Custom Controller
365
+ ```ts
366
+ import {BaseController, BindingKeys} from '@gennext/lb-infra';
367
+ import {api, post, requestBody} from '@gennext/lb-infra/lb-rest';
368
+ import {inject} from '@gennext/lb-infra/lb-core';
369
+ import {OrderService} from '../services/order.service';
370
+ import {Order} from '../models/order.model';
371
+
372
+ @api({basePath: '/orders'})
373
+ export class OrderController extends BaseController {
374
+ constructor(
375
+ @inject(BindingKeys.serviceFor('OrderService'))
376
+ private orderService: OrderService
377
+ ) {
378
+ super({scope: OrderController.name});
379
+ }
320
380
 
321
- Example file: `src/protos/user.proto`
381
+ @post('/')
382
+ async create(@requestBody() data: Order): Promise<Order> {
383
+ try {
384
+ return await this.orderService.createOrderWithValidation(data);
385
+ } catch (err) {
386
+ this.logger.error('Failed to create order: %s', err.message);
387
+ throw err;
388
+ }
389
+ }
390
+ }
391
+ ```
322
392
 
393
+ ---
394
+
395
+ ## 11. Các Components tích hợp sẵn
396
+
397
+ Framework tích hợp sẵn các LoopBack 4 Components hỗ trợ bootstrap nhanh:
398
+
399
+ 1. **`AuthenticateComponent`**
400
+ - *Mục đích*: Quản lý phiên đăng nhập và định danh (JWT, Basic, OAuth2).
401
+ - *Cấu hình*: Bind cấu hình qua `AuthenticateKeys.TOKEN_OPTIONS` và `AuthenticateKeys.REST_OPTIONS`.
402
+ 2. **`AuthorizeComponent`**
403
+ - *Mục đích*: Phân quyền chi tiết người dùng sử dụng Casbin authorization adapter.
404
+ 3. **`MigrationComponent`**
405
+ - *Mục đích*: Quản lý chạy tự động các tệp tin schema migration SQL/TS lúc boot ứng dụng.
406
+ 4. **`HealthCheckComponent`**
407
+ - *Mục đích*: Endpoint `/health` cung cấp thông tin trạng thái hoạt động của hệ thống.
408
+ 5. **`MailComponent`**
409
+ - *Mục đích*: Quản lý gửi mail tích hợp BullMQ để gửi bất đồng bộ qua SMTP / Mailgun.
410
+ 6. **`SocketIOComponent`**
411
+ - *Mục đích*: Quản lý Socket.IO server tích hợp Redis adapter phục vụ phân tán.
412
+ 7. **`GrpcServerComponent`**
413
+ - *Mục đích*: Khởi tạo và khởi chạy gRPC server của hệ thống.
414
+ 8. **`CrashReportComponent`**
415
+ - *Mục đích*: Ghi lại dấu vết lỗi hệ thống sang các bên thứ 3 (Sentry / MT service).
416
+ 9. **`StaticAssetComponent`**
417
+ - *Mục đích*: Hỗ trợ upload, quản lý file và tích hợp với Object Storage (MinIO/S3).
418
+
419
+ ---
420
+
421
+ ## 12. Hệ thống Helpers dùng chung
422
+
423
+ Các helper cung cấp giao diện API đồng bộ và dễ sử dụng:
424
+
425
+ * **Logger Helper**:
426
+ ```ts
427
+ import {LoggerFactory} from '@gennext/lb-infra';
428
+
429
+ const logger = LoggerFactory.getLogger('CustomScope');
430
+ logger.info('Message log: %s', 'value');
431
+ ```
432
+
433
+ * **Redis Helper**:
434
+ ```ts
435
+ import {RedisHelper} from '@gennext/lb-infra';
436
+
437
+ const redis = new RedisHelper({host: '127.0.0.1', port: 6379});
438
+ await redis.set('key', 'value');
439
+ ```
440
+
441
+ * **Queue Helpers**:
442
+ ```ts
443
+ // 1. QueueHelper (Hàng đợi trong bộ nhớ)
444
+ import {QueueHelper} from '@gennext/lb-infra';
445
+
446
+ const inMemoryQueue = new QueueHelper<string>({
447
+ identifier: 'my-in-memory-queue',
448
+ onMessage: async ({ queueElement }) => {
449
+ console.log('Processing:', queueElement.payload);
450
+ }
451
+ });
452
+ await inMemoryQueue.enqueue('task-payload');
453
+
454
+ // 2. BullMQHelper (Hàng đợi Redis-backed)
455
+ import {BullMQHelper} from '@gennext/lb-infra';
456
+ import Redis from 'ioredis';
457
+
458
+ const connection = new Redis({ host: '127.0.0.1', port: 6379 });
459
+
460
+ // Client Role: queue (để đẩy job vào hàng đợi)
461
+ const bullQueue = new BullMQHelper<string>({
462
+ queueName: 'email-tasks',
463
+ identifier: 'email-queue-client',
464
+ role: 'queue',
465
+ connection,
466
+ });
467
+ await bullQueue.queue.add('send-email', 'payload');
468
+
469
+ // Server Role: worker (để xử lý job)
470
+ const bullWorker = new BullMQHelper<string>({
471
+ queueName: 'email-tasks',
472
+ identifier: 'email-worker-server',
473
+ role: 'worker',
474
+ connection,
475
+ onWorkerData: async (job) => {
476
+ console.log('Processing job data:', job.data);
477
+ }
478
+ });
479
+ ```
480
+
481
+ * **Crypto Helper**:
482
+ ```ts
483
+ import {AES} from '@gennext/lb-infra';
484
+
485
+ const aes = AES.withAlgorithm('aes-256-cbc');
486
+ const encrypted = aes.encrypt('plain-text', 'secret-key');
487
+ ```
488
+
489
+ * **Storage Helper (MinIO)**:
490
+ ```ts
491
+ import {MinioHelper} from '@gennext/lb-infra';
492
+
493
+ const storage = new MinioHelper({
494
+ endPoint: '127.0.0.1',
495
+ port: 9000,
496
+ useSSL: false,
497
+ accessKey: 'minioadmin',
498
+ secretKey: 'minioadmin',
499
+ });
500
+
501
+ const files = [{
502
+ originalname: 'file.txt',
503
+ mimetype: 'text/plain',
504
+ buffer: Buffer.from('hello world'),
505
+ size: 11,
506
+ encoding: 'utf-8',
507
+ }];
508
+
509
+ const result = await storage.upload({
510
+ bucket: 'bucket-name',
511
+ files,
512
+ });
513
+ ```
514
+
515
+ ---
516
+
517
+ ## 13. Cơ chế mở rộng (Extension Point)
518
+
519
+ Consumer Application có thể mở rộng các chức năng của framework mà không cần sửa mã nguồn gốc bằng các cách sau:
520
+
521
+ - **Custom Mixins**: Tạo các mixin riêng bằng cách bọc quanh `BaseEntity` hoặc các model hiện có và truyền vào cấu hình của mình.
522
+ - **Custom Components**: Viết các LoopBack component tuân thủ giao diện `Component` của Loopback và cắm vào thông qua `this.component(MyComponent)`.
523
+ - **Custom Datasource**: Đăng ký các Connector mới bằng cách viết các lớp kế thừa `juggler.DataSource`.
524
+
525
+ ---
526
+
527
+ ## 14. Best Practices trong phát triển dự án
528
+
529
+ 1. **Sử dụng Package Aliases**: Luôn dùng `@gennext/lb-infra/lb-core`, `@gennext/lb-infra/lb-rest`, `@gennext/lb-infra/lb-repo` để tránh xung đột phiên bản của các thư viện LoopBack.
530
+ 2. **Quản lý Binding Keys đúng cách**: Phân biệt rõ hai loại binding key để tránh lỗi runtime khi gọi `@inject`:
531
+ - *Với repository/service NỘI BỘ của framework*: Sử dụng các key tĩnh định nghĩa sẵn như `BindingKeys.REPOSITORIES.User`, `BindingKeys.SERVICES.JWTToken`.
532
+ - *Với repository/service do ứng dụng TỰ TẠO*: Sử dụng các static helper dynamic để sinh key như `BindingKeys.repositoryFor('ProductRepository')` hoặc `BindingKeys.serviceFor('ProductService')`.
533
+ 3. **Phân tách tầng logic**: Luôn tuân thủ quy tắc: HTTP validation / Response formatting nằm ở Controller -> Logic xử lý nghiệp vụ ở Service -> Query Database / Transaction ở Repository.
534
+ 4. **Không đổi schema đa ngôn ngữ (i18n)**: Lưu trữ các trường đa ngôn ngữ trực tiếp dưới dạng literal text indexable thay vì dynamic path-lookup để tối ưu hiệu năng tìm kiếm Postgres.
535
+ 5. **Clamp limit truy vấn**: Luôn sử dụng helper `applyLimit` để đảm bảo hệ thống tự động clamp các limit quá lớn từ client về `App.MAX_QUERY_LIMIT` (giá trị thực tế là `1000`) nhằm tránh lỗi quá tải bộ nhớ.
536
+
537
+ ---
538
+
539
+ ## 15. Hướng dẫn sử dụng gRPC (5 bước)
540
+
541
+ Module gRPC được xuất từ `@gennext/lb-infra/grpc`. Quy trình triển khai dịch vụ gRPC gồm 5 bước:
542
+
543
+ ### Bước 1: Tạo file định nghĩa Proto
544
+ Tệp: `src/protos/user.proto`
323
545
  ```proto
324
546
  syntax = "proto3";
325
-
326
547
  package demo;
327
548
 
328
549
  service UserService {
@@ -336,25 +557,13 @@ message GetUserRequest {
336
557
  message GetUserResponse {
337
558
  int32 id = 1;
338
559
  string email = 2;
339
- string fullName = 3;
340
560
  }
341
561
  ```
342
562
 
343
- ### gRPC Controller
344
-
563
+ ### Bước 2: Tạo gRPC Controller
345
564
  ```ts
346
565
  import {BaseGrpcController, grpcController, grpcMethod} from '@gennext/lb-infra/grpc';
347
566
 
348
- type GetUserRequest = {
349
- id: number;
350
- };
351
-
352
- type GetUserResponse = {
353
- id: number;
354
- email: string;
355
- fullName: string;
356
- };
357
-
358
567
  @grpcController()
359
568
  export class UserGrpcController extends BaseGrpcController {
360
569
  constructor() {
@@ -366,32 +575,21 @@ export class UserGrpcController extends BaseGrpcController {
366
575
  service: 'demo.UserService',
367
576
  method: 'GetUser',
368
577
  })
369
- async getUser(request: GetUserRequest): Promise<GetUserResponse> {
578
+ async getUser(request: {id: number}): Promise<{id: number; email: string}> {
370
579
  return {
371
580
  id: request.id,
372
581
  email: `user-${request.id}@example.com`,
373
- fullName: `User ${request.id}`,
374
582
  };
375
583
  }
376
584
  }
377
585
  ```
378
586
 
379
- `service` must match the package and service path loaded from the proto definition. In the example above, `package demo; service UserService` becomes `demo.UserService`.
380
-
381
- `method` must match the RPC method name in the proto. If `method` is omitted, the TypeScript method name is used.
382
-
383
- ### Register Server In Application
384
-
587
+ ### Bước 3: Cấu hình gRPC Options & Register Component
385
588
  ```ts
386
589
  import {BaseApplication} from '@gennext/lb-infra';
387
- import {
388
- GrpcServerComponent,
389
- GrpcServerKeys,
390
- IGrpcServerOptions,
391
- } from '@gennext/lb-infra/grpc';
590
+ import {GrpcServerComponent, GrpcServerKeys, IGrpcServerOptions} from '@gennext/lb-infra/grpc';
392
591
  import {ServerCredentials} from '@grpc/grpc-js';
393
592
  import {join} from 'node:path';
394
-
395
593
  import {UserGrpcController} from './controllers/user-grpc.controller';
396
594
 
397
595
  export class MyApplication extends BaseApplication {
@@ -399,92 +597,34 @@ export class MyApplication extends BaseApplication {
399
597
  this.bind<IGrpcServerOptions>(GrpcServerKeys.GRPC_OPTIONS).to({
400
598
  identifier: 'my-grpc-server',
401
599
  protoFolder: join(__dirname, 'protos'),
402
- address: process.env.GRPC_ADDRESS ?? '0.0.0.0:50051',
600
+ address: '0.0.0.0:50051',
403
601
  credentials: ServerCredentials.createInsecure(),
404
602
  });
405
603
 
406
604
  this.grpcController(UserGrpcController);
407
605
  this.component(GrpcServerComponent);
408
606
  }
409
-
410
- getProjectRoot(): string {
411
- return __dirname;
412
- }
413
-
414
- validateEnv() {
415
- return {result: true};
416
- }
417
-
418
- declareModels(): Set<string> {
419
- return new Set([]);
420
- }
421
-
422
- preConfigure(): void {}
423
-
424
- postConfigure(): void {}
425
607
  }
426
608
  ```
427
609
 
428
- `GrpcServerComponent` starts the gRPC server immediately when the component is registered, except when `RUN_MODE=migrate`. Register gRPC controllers before registering the component so the server can discover them.
429
-
430
- ### Client Example
431
-
610
+ ### Bước 4: Gọi gRPC Client thông thường
432
611
  ```ts
433
- import * as grpc from '@grpc/grpc-js';
434
- import * as protoLoader from '@grpc/proto-loader';
435
612
  import {initializeGrpcClient} from '@gennext/lb-infra/grpc';
436
- import get from 'lodash/get';
437
- import {join} from 'node:path';
613
+ import * as grpc from '@grpc/grpc-js';
438
614
 
439
- type GetUserResponse = {
440
- id: number;
441
- email: string;
442
- fullName: string;
443
- };
444
-
445
- type UserServiceClient = grpc.Client & {
446
- GetUser(
447
- request: {id: number},
448
- callback: (error: grpc.ServiceError | null, response: GetUserResponse) => void,
449
- ): void;
450
- };
451
-
452
- const protoPath = join(__dirname, 'protos/user.proto');
453
- const packageDefinition = protoLoader.loadSync(protoPath);
454
- const proto = grpc.loadPackageDefinition(packageDefinition);
455
-
456
- const UserService = get(proto, 'demo.UserService') as unknown as new (
457
- address: string,
458
- credentials: grpc.ChannelCredentials,
459
- options?: grpc.ClientOptions,
460
- ) => UserServiceClient;
461
-
462
- const userClient = initializeGrpcClient<UserServiceClient>({
463
- serviceClass: UserService,
615
+ const userClient = initializeGrpcClient<any>({
616
+ serviceClass: UserService, // UserService loaded from proto
464
617
  address: '127.0.0.1:50051',
465
618
  credentials: grpc.ChannelCredentials.createInsecure(),
466
- autoConnect: true,
467
- });
468
-
469
- userClient.client.GetUser({id: 1}, (error, response) => {
470
- if (error) {
471
- console.error(error);
472
- return;
473
- }
474
-
475
- console.log(response);
476
619
  });
477
620
  ```
478
621
 
479
- ### Repository-Style Client
480
-
481
- For app code that prefers datasource/repository style, use `GrpcDataSource` and `GrpcRepository`:
482
-
622
+ ### Bước 5: Gọi gRPC dưới dạng Repository-Style
483
623
  ```ts
484
- import * as grpc from '@grpc/grpc-js';
485
624
  import {GrpcDataSource, GrpcRepository} from '@gennext/lb-infra/grpc';
625
+ import * as grpc from '@grpc/grpc-js';
486
626
 
487
- const dataSource = new GrpcDataSource<UserServiceClient>({
627
+ const dataSource = new GrpcDataSource<any>({
488
628
  dsConfig: {
489
629
  host: '127.0.0.1',
490
630
  port: 50051,
@@ -493,86 +633,18 @@ const dataSource = new GrpcDataSource<UserServiceClient>({
493
633
  },
494
634
  });
495
635
 
496
- class UserGrpcRepository extends GrpcRepository<UserServiceClient> {
636
+ class UserGrpcRepository extends GrpcRepository<any> {
497
637
  constructor() {
498
638
  super({dataSource, scope: UserGrpcRepository.name});
499
639
  }
500
640
 
501
- getUser(id: number): Promise<GetUserResponse> {
641
+ async getUser(id: number): Promise<any> {
502
642
  return new Promise((resolve, reject) => {
503
- this.getServiceClient().GetUser({id}, (error, response) => {
504
- if (error) {
505
- reject(error);
506
- return;
507
- }
508
-
643
+ this.getServiceClient().GetUser({id}, (error: any, response: any) => {
644
+ if (error) return reject(error);
509
645
  resolve(response);
510
646
  });
511
647
  });
512
648
  }
513
649
  }
514
650
  ```
515
-
516
- The current helper examples above use standard unary request/response calls.
517
-
518
- ## Components
519
-
520
- Available components include:
521
-
522
- - `AuthenticateComponent`: JWT/basic/oauth2 strategies, auth middleware, token services and auth controller generator.
523
- - `AuthorizeComponent`: Casbin authorization, role/permission models, adapters, provider and interceptor.
524
- - `MigrationComponent`: migration model, repository and runtime wiring.
525
- - `HealthCheckComponent`: application and datasource health checks.
526
- - `StaticAssetComponent`: MinIO/static asset controllers.
527
- - `MailComponent`: mail service, template engine, transports and queue executors.
528
- - `SocketIOComponent`: Socket.IO server/client helpers and Redis adapter support.
529
- - `GrpcServerComponent`: gRPC server support.
530
- - `CrashReportComponent`: crash report providers and services.
531
-
532
- ## Helpers
533
-
534
- Important helper groups:
535
-
536
- - Logger: `LoggerFactory`, `ApplicationLogger`
537
- - Redis: `RedisHelper`, `RedisClusterHelper`
538
- - Queue: `QueueHelper`, `BullMQHelper`, `MQTTClientHelper`
539
- - Network: HTTP request, TCP, TLS TCP and UDP helpers
540
- - Crypto: `AES`, `RSA`
541
- - Storage: `MinioHelper`, `DIContainerHelper`
542
- - Worker thread helpers
543
- - Testing helpers
544
- - `CronHelper`
545
-
546
- ## Datasources
547
-
548
- Built-in datasources:
549
-
550
- - `PostgresDataSource`
551
- - `RedisDataSource`
552
- - `KvMemDataSource`
553
-
554
- Common environment keys are defined in `src/common/environments.ts`, including server, PostgreSQL and datasource settings.
555
-
556
- ## Development Checklist
557
-
558
- Before opening a change:
559
-
560
- ```bash
561
- bun run --filter "@gennext/lb-infra" build
562
- bun run --filter "@gennext/lb-infra" lint
563
- bun run --filter "@gennext/lb-infra" test
564
- ```
565
-
566
- When adding or changing exports:
567
-
568
- 1. Update `src/index.ts` or the relevant module `index.ts`.
569
- 2. Update `package.json` `exports` when adding a new public subpath.
570
- 3. Build the package.
571
- 4. Verify the consumer app can import the new path.
572
-
573
- ## Notes For Consumer Apps
574
-
575
- - Prefer the package aliases `lb-core`, `lb-rest`, `lb-repo` and `lb-auth` to keep LoopBack dependency versions consistent.
576
- - Add `@api({basePath: ...})` on the final exported class when using generated controllers.
577
- - Bind required component options before starting the application, especially authentication token options and auth service bindings.
578
- - Ensure build output includes copied runtime assets when publishing or packaging.