@gennext/lb-infra 0.2.1 → 0.2.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 +181 -223
- package/dist/base/applications/base.application.d.ts +12 -21
- package/dist/base/applications/base.application.d.ts.map +1 -1
- package/dist/base/applications/base.application.js +25 -27
- package/dist/base/applications/base.application.js.map +1 -1
- package/dist/base/hono/hono-adapter.d.ts +10 -0
- package/dist/base/hono/hono-adapter.d.ts.map +1 -0
- package/dist/base/hono/hono-adapter.js +27 -0
- package/dist/base/hono/hono-adapter.js.map +1 -0
- package/dist/base/index.js +1 -1
- package/dist/base/index.js.map +1 -1
- package/dist/base/models/base.model.d.ts +2 -1
- package/dist/base/models/base.model.d.ts.map +1 -1
- package/dist/base/models/base.model.js +2 -1
- package/dist/base/models/base.model.js.map +1 -1
- package/dist/base/models/column-defs.d.ts +2 -2
- package/dist/base/models/decorators.d.ts +15 -0
- package/dist/base/models/decorators.d.ts.map +1 -0
- package/dist/base/models/decorators.js +19 -0
- package/dist/base/models/decorators.js.map +1 -0
- package/dist/base/models/index.d.ts +2 -0
- package/dist/base/models/index.d.ts.map +1 -1
- package/dist/base/models/index.js +2 -0
- package/dist/base/models/index.js.map +1 -1
- package/dist/base/models/model.helper.d.ts +7 -0
- package/dist/base/models/model.helper.d.ts.map +1 -0
- package/dist/base/models/model.helper.js +16 -0
- package/dist/base/models/model.helper.js.map +1 -0
- package/dist/base/repository/default-crud.repository.d.ts +3 -3
- package/dist/base/repository/default-crud.repository.d.ts.map +1 -1
- package/dist/base/repository/default-crud.repository.js +20 -17
- package/dist/base/repository/default-crud.repository.js.map +1 -1
- package/dist/base/repository/filter.builder.d.ts +7 -0
- package/dist/base/repository/filter.builder.d.ts.map +1 -0
- package/dist/base/repository/filter.builder.js +74 -0
- package/dist/base/repository/filter.builder.js.map +1 -0
- package/dist/base/repository/readable.repository.d.ts +10 -8
- package/dist/base/repository/readable.repository.d.ts.map +1 -1
- package/dist/base/repository/readable.repository.js +26 -19
- package/dist/base/repository/readable.repository.js.map +1 -1
- package/dist/base/repository/soft-deletable.repository.d.ts +3 -2
- package/dist/base/repository/soft-deletable.repository.d.ts.map +1 -1
- package/dist/base/repository/soft-deletable.repository.js +26 -15
- package/dist/base/repository/soft-deletable.repository.js.map +1 -1
- package/dist/boot/base/base-artifact-booter.d.ts +3 -0
- package/dist/boot/base/base-artifact-booter.d.ts.map +1 -1
- package/dist/boot/base/base-artifact-booter.js +3 -0
- package/dist/boot/base/base-artifact-booter.js.map +1 -1
- package/dist/boot/boot.mixin.d.ts.map +1 -1
- package/dist/boot/boot.mixin.js +10 -4
- package/dist/boot/boot.mixin.js.map +1 -1
- package/dist/boot/booters/controller.booter.d.ts.map +1 -1
- package/dist/boot/booters/controller.booter.js +5 -1
- package/dist/boot/booters/controller.booter.js.map +1 -1
- package/dist/common/constants.d.ts +2 -0
- package/dist/common/constants.d.ts.map +1 -1
- package/dist/common/constants.js +2 -0
- package/dist/common/constants.js.map +1 -1
- package/dist/helpers/logger/config.d.ts.map +1 -1
- package/dist/helpers/logger/config.js +1 -2
- package/dist/helpers/logger/config.js.map +1 -1
- package/dist/index.d.ts +1 -1
- package/dist/index.d.ts.map +1 -1
- package/dist/index.js +2 -2
- package/dist/index.js.map +1 -1
- package/dist/utilities/migration.utility.d.ts +13 -0
- package/dist/utilities/migration.utility.d.ts.map +1 -1
- package/dist/utilities/migration.utility.js +39 -0
- package/dist/utilities/migration.utility.js.map +1 -1
- package/package.json +9 -1
- package/dist/base/loopback/filter/index.d.ts +0 -2
- package/dist/base/loopback/filter/index.d.ts.map +0 -1
- package/dist/base/loopback/filter/index.js +0 -5
- package/dist/base/loopback/filter/index.js.map +0 -1
- package/dist/base/loopback/http-server/index.d.ts +0 -2
- package/dist/base/loopback/http-server/index.d.ts.map +0 -1
- package/dist/base/loopback/http-server/index.js +0 -5
- package/dist/base/loopback/http-server/index.js.map +0 -1
package/README.md
CHANGED
|
@@ -1,310 +1,261 @@
|
|
|
1
|
-
# @gennext/lb-infra
|
|
1
|
+
# 📘 Cẩm nang Kĩ thuật: @gennext/lb-infra
|
|
2
2
|
|
|
3
|
-
|
|
4
|
-
|
|
5
|
-
## 1. Giới thiệu
|
|
6
|
-
`@gennext/lb-infra` là nền tảng xây dựng backend hiện đại tại Gennex, kết hợp các công nghệ:
|
|
7
|
-
- **Hono**: Web framework siêu nhẹ, hỗ trợ TypeScript và OpenAPI native.
|
|
8
|
-
- **Drizzle ORM**: ORM thế hệ mới, type-safe tuyệt đối cho PostgreSQL.
|
|
9
|
-
- **Zod**: Thư viện validation và định nghĩa schema mạnh mẽ.
|
|
10
|
-
- **Winston**: Hệ thống logging đa kênh (Console, File, UDP).
|
|
11
|
-
|
|
12
|
-
**Khi nào nên dùng:** Phù hợp cho các dự án yêu cầu tốc độ phản hồi cao, cấu trúc mã nguồn tường minh (Controller-Service-Repository) và tự động hóa tài liệu API (Swagger/OpenAPI).
|
|
3
|
+
Hạ tầng Backend Hybrid chuẩn Gennex: Kết hợp sức mạnh kiến trúc của **LoopBack 4** và hiệu năng của **Hono/Drizzle**.
|
|
13
4
|
|
|
14
5
|
---
|
|
15
6
|
|
|
16
|
-
##
|
|
7
|
+
## 🏗 1. Tổng quan kiến trúc & Cài đặt
|
|
17
8
|
|
|
18
|
-
|
|
19
|
-
```bash
|
|
20
|
-
bun add @gennext/lb-infra
|
|
21
|
-
```
|
|
9
|
+
### 1.1 Hybrid Platform
|
|
22
10
|
|
|
23
|
-
|
|
24
|
-
```bash
|
|
25
|
-
bun add hono @hono/zod-openapi zod drizzle-orm pg winston
|
|
26
|
-
# Nếu dùng Auth/Authorization
|
|
27
|
-
bun add jose casbin
|
|
28
|
-
```
|
|
11
|
+
Thư viện cung cấp hai lộ trình phát triển:
|
|
29
12
|
|
|
30
|
-
|
|
13
|
+
- **Hono Path (Modern):** Cho các service yêu cầu tốc độ cao, hỗ trợ OpenAPI native qua Zod.
|
|
14
|
+
- **LoopBack Path (Standard):** Cho các ứng dụng phức tạp cần DI (Dependency Injection) và tính tương thích ngược.
|
|
31
15
|
|
|
32
|
-
|
|
33
|
-
Sử dụng `BaseApplication` kết hợp với `bootMixin` để tự động quét và đăng ký các thành phần.
|
|
16
|
+
### 1.2 Cấu trúc Export (Sub-paths)
|
|
34
17
|
|
|
35
|
-
|
|
36
|
-
import { BaseApplication, bootMixin, bootStrapApplication, IApplicationConfigs } from '@gennext/lb-infra';
|
|
18
|
+
Được cấu hình trong `package.json`, giúp developer import sạch sẽ:
|
|
37
19
|
|
|
38
|
-
|
|
39
|
-
|
|
40
|
-
|
|
41
|
-
|
|
42
|
-
|
|
43
|
-
|
|
44
|
-
}
|
|
45
|
-
}
|
|
20
|
+
- `@gennext/lb-infra`: Entry chính (Hono, Drizzle, Utilities).
|
|
21
|
+
- `@gennext/lb-infra/lb-core`: LoopBack Core (Binding, Context, Metadata).
|
|
22
|
+
- `@gennext/lb-infra/lb-rest`: LoopBack Rest (Decorators, Sequence).
|
|
23
|
+
- `@gennext/lb-infra/lb-repo`: LoopBack Repository (Juggler, Filter).
|
|
24
|
+
- `@gennext/lb-infra/socket-io`: WebSocket component.
|
|
25
|
+
- `@gennext/lb-infra/grpc`: gRPC component.
|
|
46
26
|
|
|
47
|
-
|
|
48
|
-
port: Number(process.env.PORT) || 3000,
|
|
49
|
-
path: { base: '/api' }
|
|
50
|
-
};
|
|
27
|
+
### 1.3 Pin Dependencies (Không cần cài thêm)
|
|
51
28
|
|
|
52
|
-
|
|
53
|
-
|
|
29
|
+
Thư viện đã đóng gói sẵn và re-export các "vũ khí" sau:
|
|
30
|
+
|
|
31
|
+
- **Framework:** `Hono`, `@hono/zod-openapi`, `zod`.
|
|
32
|
+
- **Database:** `Drizzle ORM` (core + pg), `pg` (node-postgres).
|
|
33
|
+
- **Utilities:** `axios`, `lodash`, `dayjs` (thông qua `date.utility`).
|
|
54
34
|
|
|
55
35
|
---
|
|
56
36
|
|
|
57
|
-
##
|
|
58
|
-
Sử dụng Drizzle để định nghĩa schema và các Repository để thao tác dữ liệu.
|
|
37
|
+
## 🚀 2. Application Kernel
|
|
59
38
|
|
|
60
|
-
###
|
|
61
|
-
Sử dụng các helper trong `column-defs.ts` để chuẩn hóa cấu trúc bảng.
|
|
39
|
+
### 2.1 Khởi tạo với `BaseApplication`
|
|
62
40
|
|
|
63
|
-
|
|
64
|
-
import { pgTable, text, integer } from 'drizzle-orm/pg-core';
|
|
65
|
-
import { generateIdColumnDefs, generateSoftDeleteColumnDefs, generateTzColumnDefs } from '@gennext/lb-infra';
|
|
41
|
+
Mọi ứng dụng phải kế thừa từ class này.
|
|
66
42
|
|
|
67
|
-
|
|
68
|
-
...generateIdColumnDefs({ id: { dataType: 'string' } }),
|
|
69
|
-
orderNumber: text('order_number').notNull(),
|
|
70
|
-
amount: integer('amount'),
|
|
71
|
-
...generateTzColumnDefs(),
|
|
72
|
-
...generateSoftDeleteColumnDefs(),
|
|
73
|
-
});
|
|
74
|
-
```
|
|
43
|
+
**Interface `IApplicationConfigs`:**
|
|
75
44
|
|
|
76
|
-
|
|
77
|
-
|
|
45
|
+
- `port`: (Bắt buộc) Cổng chạy server.
|
|
46
|
+
- `host?`: Hostname (Mặc định `0.0.0.0`).
|
|
47
|
+
- `path?`: `{ base: string, isStrict?: boolean }` - Prefix cho API.
|
|
48
|
+
- `debug?`: `{ shouldShowRoutes?: boolean }` - In ra danh sách route khi startup.
|
|
78
49
|
|
|
79
|
-
|
|
80
|
-
import { SoftDeletableRepository } from '@gennext/lb-infra';
|
|
81
|
-
import { orders } from '../models/order.schema';
|
|
50
|
+
**Vòng đời (Lifecycle) trong `start()`:**
|
|
82
51
|
|
|
83
|
-
|
|
84
|
-
|
|
85
|
-
|
|
86
|
-
|
|
87
|
-
}
|
|
88
|
-
```
|
|
52
|
+
1. `preConfigure()`: Thiết lập biến môi trường, kết nối DB sớm.
|
|
53
|
+
2. `setupMiddlewares()`: Đăng ký Logger, CORS, v.v.
|
|
54
|
+
3. `boot()`: Tự động đăng ký Controllers/Services (khi dùng `bootMixin`).
|
|
55
|
+
4. `postConfigure()`: Chạy các tác vụ sau khi server đã lắng nghe.
|
|
89
56
|
|
|
90
|
-
|
|
57
|
+
### 2.2 Dependency Injection (DI)
|
|
91
58
|
|
|
92
|
-
|
|
93
|
-
Các service kế thừa `BaseService` và sử dụng `this.logger` (HFLogger pattern) để ghi log theo phương thức.
|
|
59
|
+
Hệ thống sử dụng Map-based Binding:
|
|
94
60
|
|
|
95
|
-
|
|
96
|
-
|
|
97
|
-
|
|
98
|
-
export class OrderService extends BaseService {
|
|
99
|
-
async processOrder(orderId: string) {
|
|
100
|
-
const logger = this.logger.for('processOrder');
|
|
101
|
-
logger.info('Starting process', { orderId });
|
|
102
|
-
|
|
103
|
-
try {
|
|
104
|
-
// Logic xử lý đơn hàng...
|
|
105
|
-
} catch (error) {
|
|
106
|
-
logger.error('Failed to process', error);
|
|
107
|
-
throw error;
|
|
108
|
-
}
|
|
109
|
-
}
|
|
110
|
-
}
|
|
111
|
-
```
|
|
61
|
+
- **Bind:** `app.bind({ key: 'services.UserService' }).toClass(UserService)`
|
|
62
|
+
- **Get:** `const service = app.get<UserService>({ key: 'services.UserService' })`
|
|
63
|
+
- **Namespaces:** Nên dùng `BindingNamespaces.SERVICE`, `.CONTROLLER`, `.REPOSITORY`, `.DATASOURCE`.
|
|
112
64
|
|
|
113
65
|
---
|
|
114
66
|
|
|
115
|
-
##
|
|
116
|
-
Controller quản lý các route và tích hợp Zod để tự động tạo tài liệu OpenAPI qua `defineRoute` hoặc `bindRoute`.
|
|
67
|
+
## 🪵 3. Hệ thống Logger & Context
|
|
117
68
|
|
|
118
|
-
|
|
119
|
-
import { BaseController, controller } from '@gennext/lb-infra';
|
|
120
|
-
import { z } from 'zod';
|
|
69
|
+
### 3.1 Logger nâng cao
|
|
121
70
|
|
|
122
|
-
|
|
123
|
-
id: z.string().uuid(),
|
|
124
|
-
amount: z.number().min(1)
|
|
125
|
-
});
|
|
71
|
+
Được xây dựng trên Winston, hỗ trợ ghi log đa kênh.
|
|
126
72
|
|
|
127
|
-
|
|
128
|
-
export class OrderController extends BaseController {
|
|
129
|
-
async binding() {
|
|
130
|
-
this.bindRoute({
|
|
131
|
-
configs: {
|
|
132
|
-
path: '/',
|
|
133
|
-
method: 'post',
|
|
134
|
-
request: {
|
|
135
|
-
body: { content: { 'application/json': { schema: OrderSchema } } }
|
|
136
|
-
},
|
|
137
|
-
responses: {
|
|
138
|
-
200: {
|
|
139
|
-
description: 'Order created',
|
|
140
|
-
content: { 'application/json': { schema: OrderSchema } }
|
|
141
|
-
}
|
|
142
|
-
}
|
|
143
|
-
}
|
|
144
|
-
}).to({
|
|
145
|
-
handler: async (c) => {
|
|
146
|
-
const body = await c.req.json();
|
|
147
|
-
return c.json(body);
|
|
148
|
-
}
|
|
149
|
-
});
|
|
150
|
-
}
|
|
151
|
-
}
|
|
152
|
-
```
|
|
73
|
+
**Các Method:**
|
|
153
74
|
|
|
154
|
-
|
|
75
|
+
- `debug(msg, ...args)`: ⚠️ Chỉ hoạt động nếu `NODE_ENV` là local/dev/alpha... **VÀ** biến `DEBUG=true`.
|
|
76
|
+
- `withScope(scope)`: Gắn prefix cho log.
|
|
77
|
+
- `for(method)`: Child logger cho từng hàm cụ thể (có cache để tối ưu).
|
|
155
78
|
|
|
156
|
-
|
|
157
|
-
|
|
79
|
+
**UDP Logging (`DgramTransport`):**
|
|
80
|
+
Gửi log qua socket UDP (ví dụ đến Logstash/Graylog).
|
|
81
|
+
Cấu hình qua `IDgramTransportOptions`: `host`, `port`, `levels` (các mức log muốn gửi đi).
|
|
158
82
|
|
|
159
|
-
|
|
160
|
-
import { PostgresDataSource, RedisDataSource } from '@gennext/lb-infra/datasources';
|
|
83
|
+
### 3.2 RequestContext & HttpAccessLogger
|
|
161
84
|
|
|
162
|
-
|
|
163
|
-
static dataSourceName = 'db';
|
|
164
|
-
}
|
|
85
|
+
Dùng để theo vết request ID xuyên suốt các tầng.
|
|
165
86
|
|
|
166
|
-
|
|
167
|
-
|
|
168
|
-
}
|
|
169
|
-
```
|
|
87
|
+
- **Tự động hóa:** Middleware tự lấy `x-request-id` từ header hoặc tự sinh mới.
|
|
88
|
+
- **Data Masking:** Tự động ẩn thông tin nhạy cảm trong log: `authorization`, `cookie`, `session`, `password`.
|
|
170
89
|
|
|
171
90
|
---
|
|
172
91
|
|
|
173
|
-
##
|
|
174
|
-
Hệ thống log mạnh mẽ hỗ trợ `RequestContext` để theo vết Request ID xuyên suốt các layer.
|
|
92
|
+
## 🗄️ 4. Data Layer (Drizzle ORM)
|
|
175
93
|
|
|
176
|
-
|
|
177
|
-
import { LoggerFactory, RequestContext } from '@gennext/lb-infra';
|
|
94
|
+
### 4.1 Schema Helpers (`column-defs.ts`)
|
|
178
95
|
|
|
179
|
-
|
|
96
|
+
Giúp định nghĩa bảng chuẩn Gennex:
|
|
180
97
|
|
|
181
|
-
|
|
182
|
-
|
|
183
|
-
|
|
184
|
-
});
|
|
185
|
-
```
|
|
98
|
+
- `generateIdColumnDefs()`: Cột `id` (Serial hoặc UUID).
|
|
99
|
+
- `generateTzColumnDefs()`: Cột `created_at`, `modified_at` tự động cập nhật.
|
|
100
|
+
- `generateSoftDeleteColumnDefs()`: Cột `deleted_at`.
|
|
186
101
|
|
|
187
|
-
|
|
102
|
+
### 4.2 Tầng Repository (Tiering)
|
|
188
103
|
|
|
189
|
-
|
|
190
|
-
|
|
104
|
+
- **ReadableRepository**: Tìm kiếm cơ bản.
|
|
105
|
+
- **DefaultCRUDRepository**: Thao tác Thêm/Sửa/Xóa.
|
|
106
|
+
- **SoftDeletableRepository**: 💡 Tự động thêm điều kiện `deleted_at IS NULL` vào mọi câu query.
|
|
107
|
+
- `hardDeleteById()`: Xóa vĩnh viễn.
|
|
108
|
+
- `restore()`: Khôi phục bản ghi đã xóa.
|
|
191
109
|
|
|
192
|
-
|
|
193
|
-
import { RedisHelper } from '@gennext/lb-infra/helpers/redis';
|
|
110
|
+
---
|
|
194
111
|
|
|
195
|
-
|
|
112
|
+
## ⚡ 5. Redis & Caching
|
|
196
113
|
|
|
197
|
-
|
|
114
|
+
### 5.1 RedisManager
|
|
198
115
|
|
|
199
|
-
|
|
200
|
-
await redis.jSet({
|
|
201
|
-
key: 'user:data',
|
|
202
|
-
path: '$',
|
|
203
|
-
value: { settings: { lang: 'vi' } }
|
|
204
|
-
});
|
|
116
|
+
Quản lý tập trung các instance Redis. Hỗ trợ cả **Standalone** và **Cluster mode**.
|
|
205
117
|
|
|
206
|
-
|
|
207
|
-
|
|
118
|
+
### 5.2 DefaultRedisHelper API
|
|
119
|
+
|
|
120
|
+
- `set({ key, value, options: { ttl } })`: Lưu object (tự serialize JSON).
|
|
121
|
+
- `getObject({ key })`: Lấy và tự parse JSON.
|
|
122
|
+
- `publish({ topics, payload, useCompress })`: Pub/Sub có hỗ trợ nén zlib để tiết kiệm băng thông.
|
|
208
123
|
|
|
209
124
|
---
|
|
210
125
|
|
|
211
|
-
##
|
|
212
|
-
Tích hợp sẵn các chiến lược xác thực phổ biến.
|
|
126
|
+
## 🌐 6. HTTP Layer (Hono + OpenAPI)
|
|
213
127
|
|
|
214
|
-
|
|
215
|
-
|
|
128
|
+
### 6.1 BaseController
|
|
129
|
+
|
|
130
|
+
Sử dụng `bindRoute` để có được sự hỗ trợ tuyệt đối từ TypeScript và tự sinh Swagger.
|
|
216
131
|
|
|
217
|
-
|
|
218
|
-
this.
|
|
219
|
-
|
|
132
|
+
```typescript
|
|
133
|
+
this.bindRoute({
|
|
134
|
+
configs: {
|
|
135
|
+
path: '/:id',
|
|
136
|
+
method: 'get',
|
|
137
|
+
request: { params: zod.object({ id: zod.string() }) },
|
|
138
|
+
responses: { 200: jsonResponse({ schema: UserSchema, description: 'User info' }) },
|
|
139
|
+
},
|
|
140
|
+
}).to({
|
|
141
|
+
handler: async c => {
|
|
142
|
+
const id = c.req.param('id');
|
|
143
|
+
// ... logic
|
|
144
|
+
},
|
|
145
|
+
});
|
|
220
146
|
```
|
|
221
147
|
|
|
222
148
|
---
|
|
223
149
|
|
|
224
|
-
##
|
|
225
|
-
Phân quyền dựa trên thuộc tính (RBAC/ABAC) sử dụng Casbin.
|
|
226
|
-
|
|
227
|
-
```typescript
|
|
228
|
-
import { AuthorizeComponent, DrizzleCasbinAdapter } from '@gennext/lb-infra';
|
|
150
|
+
## 🧰 7. Bộ tiện ích (Utilities Deep Dive)
|
|
229
151
|
|
|
230
|
-
|
|
231
|
-
|
|
232
|
-
|
|
152
|
+
| Utility | Method tiêu biểu | Mô tả |
|
|
153
|
+
| :---------- | :-------------------------------------------------------------- | :------------------------------------ |
|
|
154
|
+
| **parse** | `getUID()`, `keysToCamel()`, `toBoolean()`, `int()`, `float()` | Xử lý kiểu dữ liệu và format key. |
|
|
155
|
+
| **crypto** | `hashPassword()`, `comparePassword()`, `encrypt()`, `decrypt()` | Bảo mật mật khẩu và mã hóa AES. |
|
|
156
|
+
| **error** | `getError()`, `ValidationError()`, `NotImplementedError()` | Tạo lỗi chuẩn hóa có Status Code. |
|
|
157
|
+
| **model** | `buildRequestBody()`, `buildPaginationResponse()` | Tự động sinh OpenAPI schema từ Model. |
|
|
158
|
+
| **promise** | `delay(ms)` | Hàm ngủ async. |
|
|
159
|
+
| **axios** | `get()`, `post()`, `put()`, `del()` | Wrapper axios kèm xử lý lỗi chuẩn. |
|
|
233
160
|
|
|
234
161
|
---
|
|
235
162
|
|
|
236
|
-
##
|
|
163
|
+
## 🔑 8. Auth & Security
|
|
237
164
|
|
|
238
|
-
###
|
|
239
|
-
```typescript
|
|
240
|
-
import { BaseGrpcController } from '@gennext/lb-infra/components/grpc';
|
|
165
|
+
### 8.1 Authentication
|
|
241
166
|
|
|
242
|
-
|
|
243
|
-
|
|
244
|
-
|
|
245
|
-
|
|
167
|
+
- Đăng ký `AuthenticateComponent`.
|
|
168
|
+
- Hỗ trợ `JwtStrategy` (đọc secret từ `APP_ENV_APPLICATION_SECRET`).
|
|
169
|
+
|
|
170
|
+
### 8.2 Authorization (Casbin)
|
|
246
171
|
|
|
247
|
-
|
|
248
|
-
Sử dụng `SocketIOServerHelper` để quản lý kết nối thời gian thực.
|
|
172
|
+
⚠️ Cần cài `casbin`.
|
|
249
173
|
|
|
250
|
-
|
|
251
|
-
|
|
174
|
+
- `CasbinDrizzleAdapter`: Lưu Policy vào bảng DB do Drizzle quản lý.
|
|
175
|
+
- `CasbinEnforcer`: Check quyền bằng cú pháp: `enforcer.enforce(user, object, action)`.
|
|
252
176
|
|
|
253
177
|
---
|
|
254
178
|
|
|
255
|
-
##
|
|
256
|
-
|
|
179
|
+
## 🧪 9. Ví dụ: Full User Flow
|
|
180
|
+
|
|
181
|
+
```typescript
|
|
182
|
+
// --- 1. Model ---
|
|
183
|
+
export const users = pgTable('users', {
|
|
184
|
+
...generateIdColumnDefs({ id: { dataType: 'string' } }),
|
|
185
|
+
email: text('email').notNull().unique(),
|
|
186
|
+
...generateTzColumnDefs(),
|
|
187
|
+
...generateSoftDeleteColumnDefs(),
|
|
188
|
+
});
|
|
189
|
+
|
|
190
|
+
// --- 2. Repository ---
|
|
191
|
+
export class UserRepository extends SoftDeletableRepository<typeof users> {
|
|
192
|
+
constructor() {
|
|
193
|
+
super(users);
|
|
194
|
+
}
|
|
195
|
+
}
|
|
257
196
|
|
|
258
|
-
|
|
259
|
-
|
|
260
|
-
|
|
261
|
-
|
|
262
|
-
|
|
263
|
-
|
|
197
|
+
// --- 3. Controller ---
|
|
198
|
+
@controller({ path: '/users' })
|
|
199
|
+
export class UserController extends BaseController {
|
|
200
|
+
async binding() {
|
|
201
|
+
this.bindRoute({
|
|
202
|
+
configs: {
|
|
203
|
+
path: '/',
|
|
204
|
+
method: 'get',
|
|
205
|
+
responses: { 200: { description: 'List' } },
|
|
206
|
+
},
|
|
207
|
+
}).to({
|
|
208
|
+
handler: async c => {
|
|
209
|
+
const repo = app.get<UserRepository>({ key: 'repositories.UserRepository' });
|
|
210
|
+
return c.json(await repo.findAll());
|
|
211
|
+
},
|
|
212
|
+
});
|
|
213
|
+
}
|
|
214
|
+
}
|
|
215
|
+
```
|
|
264
216
|
|
|
265
217
|
---
|
|
266
218
|
|
|
267
|
-
##
|
|
219
|
+
## 🛠️ 10. Scripts
|
|
268
220
|
|
|
269
|
-
|
|
270
|
-
|
|
271
|
-
|
|
272
|
-
| `BaseService` | `this.logger` (HFLogger), `this.get(key)` (DI) |
|
|
273
|
-
| `BaseController` | `this.logger`, `this.bindRoute()`, `this.defineRoute()` |
|
|
274
|
-
| `BaseDataSource` | `this.name`, `this.connector` |
|
|
221
|
+
- `bun run build`: Build project (TSC + tsc-alias).
|
|
222
|
+
- `bun run migrate:push`: Đẩy schema Drizzle lên Database.
|
|
223
|
+
- `bun run lint`: Kiểm tra code style các file mới thay đổi.
|
|
275
224
|
|
|
276
225
|
---
|
|
277
226
|
|
|
278
|
-
##
|
|
279
|
-
|
|
280
|
-
| Tên biến
|
|
281
|
-
|
|
|
282
|
-
| `APP_ENV_APPLICATION_NAME`
|
|
283
|
-
| `APP_ENV_POSTGRES_HOST`
|
|
284
|
-
| `APP_ENV_POSTGRES_PORT`
|
|
285
|
-
| `APP_ENV_POSTGRES_USERNAME`
|
|
286
|
-
| `APP_ENV_POSTGRES_PASSWORD`
|
|
287
|
-
| `APP_ENV_POSTGRES_DATABASE`
|
|
288
|
-
| `APP_ENV_REDIS_DATASOURCE_HOST` | Địa chỉ Redis
|
|
289
|
-
| `APP_ENV_REDIS_DATASOURCE_PORT` | Cổng Redis
|
|
290
|
-
| `APP_ENV_LOGGER_FOLDER_PATH`
|
|
291
|
-
| `DEBUG`
|
|
227
|
+
## 11. Biến môi trường
|
|
228
|
+
|
|
229
|
+
| Tên biến | Mô tả | Giá trị mặc định |
|
|
230
|
+
| :------------------------------ | :------------------------------- | :--------------- |
|
|
231
|
+
| `APP_ENV_APPLICATION_NAME` | Tên ứng dụng xuất hiện trong log | `APP` |
|
|
232
|
+
| `APP_ENV_POSTGRES_HOST` | Địa chỉ PostgreSQL | `localhost` |
|
|
233
|
+
| `APP_ENV_POSTGRES_PORT` | Cổng PostgreSQL | `5432` |
|
|
234
|
+
| `APP_ENV_POSTGRES_USERNAME` | Username DB | `postgres` |
|
|
235
|
+
| `APP_ENV_POSTGRES_PASSWORD` | Password DB | `password` |
|
|
236
|
+
| `APP_ENV_POSTGRES_DATABASE` | Tên Database | `postgres` |
|
|
237
|
+
| `APP_ENV_REDIS_DATASOURCE_HOST` | Địa chỉ Redis | `0.0.0.0` |
|
|
238
|
+
| `APP_ENV_REDIS_DATASOURCE_PORT` | Cổng Redis | `6379` |
|
|
239
|
+
| `APP_ENV_LOGGER_FOLDER_PATH` | Đường dẫn lưu file log | `./app_data` |
|
|
240
|
+
| `DEBUG` | Kích hoạt log debug | `false` |
|
|
292
241
|
|
|
293
242
|
---
|
|
294
243
|
|
|
295
|
-
##
|
|
244
|
+
## 12. Ví dụ end-to-end: Order Service
|
|
296
245
|
|
|
297
246
|
```typescript
|
|
298
247
|
// --- 1. Model (order.schema.ts) ---
|
|
299
248
|
export const orders = pgTable('orders', {
|
|
300
249
|
...generateIdColumnDefs({ id: { dataType: 'string' } }),
|
|
301
250
|
item: text('item').notNull(),
|
|
302
|
-
...generateTzColumnDefs()
|
|
251
|
+
...generateTzColumnDefs(),
|
|
303
252
|
});
|
|
304
253
|
|
|
305
254
|
// --- 2. Repository (order.repository.ts) ---
|
|
306
255
|
export class OrderRepository extends DefaultCrudRepository<typeof orders> {
|
|
307
|
-
constructor() {
|
|
256
|
+
constructor() {
|
|
257
|
+
super(orders);
|
|
258
|
+
}
|
|
308
259
|
}
|
|
309
260
|
|
|
310
261
|
// --- 3. Service (order.service.ts) ---
|
|
@@ -320,21 +271,28 @@ export class OrderService extends BaseService {
|
|
|
320
271
|
export class OrderController extends BaseController {
|
|
321
272
|
async binding() {
|
|
322
273
|
this.bindRoute({
|
|
323
|
-
path: '/',
|
|
324
|
-
|
|
274
|
+
path: '/',
|
|
275
|
+
method: 'post',
|
|
276
|
+
responses: { 200: { description: 'Success' } },
|
|
325
277
|
}).to({
|
|
326
|
-
handler: async
|
|
278
|
+
handler: async c => {
|
|
327
279
|
const service = this.get<OrderService>('services.OrderService');
|
|
328
280
|
return c.json(await service.create('Product A'));
|
|
329
|
-
}
|
|
281
|
+
},
|
|
330
282
|
});
|
|
331
283
|
}
|
|
332
284
|
}
|
|
333
285
|
|
|
334
286
|
// --- 5. Application (main.ts) ---
|
|
335
287
|
class OrderApp extends bootMixin(BaseApplication) {
|
|
336
|
-
getAppInfo() {
|
|
288
|
+
getAppInfo() {
|
|
289
|
+
return { name: 'OrderAPI', version: '1.0' };
|
|
290
|
+
}
|
|
337
291
|
}
|
|
338
292
|
|
|
339
293
|
bootStrapApplication(OrderApp, { port: 8080 });
|
|
340
294
|
```
|
|
295
|
+
|
|
296
|
+
---
|
|
297
|
+
|
|
298
|
+
© 2024 Gennex Technology. Toàn quyền bảo lưu.
|
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
import type { OpenAPIHono } from '@hono/zod-openapi';
|
|
2
|
-
import
|
|
2
|
+
import { Application, Binding } from '@loopback/core';
|
|
3
3
|
import type { ValueOrPromise } from '../../common';
|
|
4
|
-
import type {
|
|
4
|
+
import type { Logger } from '../../helpers';
|
|
5
5
|
export interface IApplicationConfigs {
|
|
6
6
|
host?: string;
|
|
7
7
|
port: number;
|
|
@@ -24,10 +24,9 @@ export interface IApplicationInfo {
|
|
|
24
24
|
export interface IMiddlewareConfigs {
|
|
25
25
|
[key: string]: any;
|
|
26
26
|
}
|
|
27
|
-
export declare abstract class BaseApplication {
|
|
27
|
+
export declare abstract class BaseApplication extends Application {
|
|
28
28
|
protected logger: Logger;
|
|
29
|
-
protected
|
|
30
|
-
protected bindings: Map<string, any>;
|
|
29
|
+
protected honoApp: OpenAPIHono;
|
|
31
30
|
protected configs: IApplicationConfigs;
|
|
32
31
|
constructor(configs: IApplicationConfigs);
|
|
33
32
|
abstract getAppInfo(): ValueOrPromise<IApplicationInfo>;
|
|
@@ -35,22 +34,7 @@ export declare abstract class BaseApplication {
|
|
|
35
34
|
preConfigure(): ValueOrPromise<void>;
|
|
36
35
|
setupMiddlewares(): Promise<void>;
|
|
37
36
|
postConfigure(): ValueOrPromise<void>;
|
|
38
|
-
|
|
39
|
-
repository(_RepositoryClass: new (...args: any[]) => any): void;
|
|
40
|
-
dataSource(_DataSourceClass: new (...args: any[]) => any): void;
|
|
41
|
-
service(_ServiceClass: new (...args: any[]) => any): void;
|
|
42
|
-
component(_ComponentClass: new (...args: any[]) => any): void;
|
|
43
|
-
bind<T>(opts: {
|
|
44
|
-
key: string;
|
|
45
|
-
}): {
|
|
46
|
-
toValue: (v: T) => void;
|
|
47
|
-
toClass: (c: ClassType<T>) => void;
|
|
48
|
-
};
|
|
49
|
-
get<T>(opts: {
|
|
50
|
-
key: string;
|
|
51
|
-
isOptional?: boolean;
|
|
52
|
-
}): T;
|
|
53
|
-
getServer(): OpenAPIHono;
|
|
37
|
+
getHono(): OpenAPIHono;
|
|
54
38
|
start(): Promise<void>;
|
|
55
39
|
stop(): Promise<void>;
|
|
56
40
|
static(_opts: {
|
|
@@ -58,5 +42,12 @@ export declare abstract class BaseApplication {
|
|
|
58
42
|
mountPath?: string;
|
|
59
43
|
}): void;
|
|
60
44
|
protected applyMiddlewares(_configs: IMiddlewareConfigs): Promise<void>;
|
|
45
|
+
bindCustom<T>(opts: {
|
|
46
|
+
key: string;
|
|
47
|
+
}): Binding<T>;
|
|
48
|
+
getCustom<T>(opts: {
|
|
49
|
+
key: string;
|
|
50
|
+
isOptional?: boolean;
|
|
51
|
+
}): ValueOrPromise<T | undefined>;
|
|
61
52
|
}
|
|
62
53
|
//# sourceMappingURL=base.application.d.ts.map
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"base.application.d.ts","sourceRoot":"","sources":["../../../src/base/applications/base.application.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,WAAW,EAAE,MAAM,mBAAmB,CAAC;AACrD,OAAO,
|
|
1
|
+
{"version":3,"file":"base.application.d.ts","sourceRoot":"","sources":["../../../src/base/applications/base.application.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,WAAW,EAAE,MAAM,mBAAmB,CAAC;AACrD,OAAO,EAAE,WAAW,EAAE,OAAO,EAAE,MAAM,gBAAgB,CAAC;AAEtD,OAAO,KAAK,EAAE,cAAc,EAAE,MAAM,cAAc,CAAC;AACnD,OAAO,KAAK,EAAE,MAAM,EAAE,MAAM,eAAe,CAAC;AAG5C,MAAM,WAAW,mBAAmB;IAClC,IAAI,CAAC,EAAE,MAAM,CAAC;IACd,IAAI,EAAE,MAAM,CAAC;IACb,IAAI,CAAC,EAAE;QAAE,IAAI,EAAE,MAAM,CAAC;QAAC,QAAQ,CAAC,EAAE,OAAO,CAAA;KAAE,CAAC;IAC5C,KAAK,CAAC,EAAE;QAAE,gBAAgB,CAAC,EAAE,OAAO,CAAA;KAAE,CAAC;IACvC,KAAK,CAAC,EAAE;QAAE,OAAO,CAAC,EAAE,MAAM,CAAA;KAAE,CAAC;CAC9B;AAED,MAAM,WAAW,gBAAgB;IAC/B,IAAI,EAAE,MAAM,CAAC;IACb,OAAO,EAAE,MAAM,CAAC;IAChB,WAAW,CAAC,EAAE,MAAM,CAAC;CACtB;AAED,MAAM,WAAW,kBAAkB;IACjC,CAAC,GAAG,EAAE,MAAM,GAAG,GAAG,CAAC;CACpB;AAED,8BAAsB,eAAgB,SAAQ,WAAW;IACvD,SAAS,CAAC,MAAM,EAAE,MAAM,CAAC;IACzB,SAAS,CAAC,OAAO,EAAE,WAAW,CAAC;IAC/B,SAAS,CAAC,OAAO,EAAE,mBAAmB,CAAC;gBAE3B,OAAO,EAAE,mBAAmB;IAQxC,QAAQ,CAAC,UAAU,IAAI,cAAc,CAAC,gBAAgB,CAAC;IAEvD,eAAe,IAAI,IAAI;IACvB,YAAY,IAAI,cAAc,CAAC,IAAI,CAAC;IAC9B,gBAAgB,IAAI,OAAO,CAAC,IAAI,CAAC;IACvC,aAAa,IAAI,cAAc,CAAC,IAAI,CAAC;IAErC,OAAO,IAAI,WAAW;IAIP,KAAK,IAAI,OAAO,CAAC,IAAI,CAAC;IAetB,IAAI,IAAI,OAAO,CAAC,IAAI,CAAC;IAIpC,MAAM,CAAC,KAAK,EAAE;QAAE,UAAU,EAAE,MAAM,CAAC;QAAC,SAAS,CAAC,EAAE,MAAM,CAAA;KAAE,GAAG,IAAI;cAE/C,gBAAgB,CAAC,QAAQ,EAAE,kBAAkB,GAAG,OAAO,CAAC,IAAI,CAAC;IAI7E,UAAU,CAAC,CAAC,EAAE,IAAI,EAAE;QAAE,GAAG,EAAE,MAAM,CAAA;KAAE,GAAG,OAAO,CAAC,CAAC,CAAC;IAKhD,SAAS,CAAC,CAAC,EAAE,IAAI,EAAE;QAAE,GAAG,EAAE,MAAM,CAAC;QAAC,UAAU,CAAC,EAAE,OAAO,CAAA;KAAE,GAAG,cAAc,CAAC,CAAC,GAAG,SAAS,CAAC;CAMzF"}
|