@lark-apaas/fullstack-nestjs-core 1.0.3-alpha.12 → 1.0.3-alpha.14
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 +239 -124
- package/dist/index.cjs +57 -22
- package/dist/index.d.cts +1 -16
- package/dist/index.d.ts +1 -16
- package/dist/index.js +45 -10
- package/package.json +1 -3
package/README.md
CHANGED
|
@@ -1,12 +1,15 @@
|
|
|
1
1
|
# @lark-apaas/fullstack-nestjs-core
|
|
2
2
|
|
|
3
|
-
FullStack NestJS Core 是一个为 NestJS
|
|
3
|
+
FullStack NestJS Core 是一个为 NestJS 全栈应用提供核心功能的工具包,包括平台集成、CSRF 保护、用户上下文管理和开发工具等。
|
|
4
4
|
|
|
5
5
|
## 特性
|
|
6
6
|
|
|
7
|
+
- **平台模块**: 一站式集成 Config、Logger、Database、Auth 等核心功能
|
|
7
8
|
- **CSRF 保护**: 提供完整的 CSRF Token 生成和验证机制
|
|
8
9
|
- **用户上下文**: 自动从请求头中提取并注入用户上下文信息
|
|
9
|
-
-
|
|
10
|
+
- **视图上下文**: 将用户信息和 CSRF Token 注入到模板渲染上下文
|
|
11
|
+
- **开发工具**: 自动生成 Swagger 文档、OpenAPI JSON 和 TypeScript 客户端 SDK
|
|
12
|
+
- **应用配置**: 一键配置 Logger、Cookie Parser、全局前缀等
|
|
10
13
|
|
|
11
14
|
## 安装
|
|
12
15
|
|
|
@@ -27,226 +30,330 @@ yarn add @lark-apaas/fullstack-nestjs-core
|
|
|
27
30
|
|
|
28
31
|
## 快速开始
|
|
29
32
|
|
|
30
|
-
### 1
|
|
33
|
+
### 方案 1: 使用 PlatformModule(推荐)
|
|
31
34
|
|
|
32
|
-
|
|
35
|
+
最简单的方式是使用 `PlatformModule`,它会自动集成所有核心功能:
|
|
33
36
|
|
|
34
|
-
|
|
37
|
+
```typescript
|
|
38
|
+
import { Module } from '@nestjs/common';
|
|
39
|
+
import { PlatformModule } from '@lark-apaas/fullstack-nestjs-core';
|
|
40
|
+
|
|
41
|
+
@Module({
|
|
42
|
+
imports: [
|
|
43
|
+
PlatformModule.forRoot({
|
|
44
|
+
enableCsrf: true, // 是否启用 CSRF 保护,默认 true
|
|
45
|
+
csrfRoutes: '/api/*', // CSRF 保护的路由,默认 '/api/*'
|
|
46
|
+
alwaysNeedLogin: true, // 是否所有路由都需要登录,默认 true
|
|
47
|
+
}),
|
|
48
|
+
],
|
|
49
|
+
})
|
|
50
|
+
export class AppModule {}
|
|
51
|
+
```
|
|
52
|
+
|
|
53
|
+
`PlatformModule` 自动集成:
|
|
54
|
+
- ✅ ConfigModule (环境变量配置)
|
|
55
|
+
- ✅ LoggerModule (日志系统)
|
|
56
|
+
- ✅ DataPaasModule (数据库连接)
|
|
57
|
+
- ✅ AuthNPaasModule (认证系统)
|
|
58
|
+
- ✅ UserContextMiddleware (用户上下文)
|
|
59
|
+
- ✅ CsrfTokenMiddleware + CsrfMiddleware (CSRF 保护)
|
|
60
|
+
- ✅ ViewContextMiddleware (视图上下文)
|
|
61
|
+
- ✅ ValidationPipe (全局验证管道)
|
|
62
|
+
|
|
63
|
+
### 方案 2: 使用 configureApp 辅助函数
|
|
64
|
+
|
|
65
|
+
在 `main.ts` 中使用 `configureApp` 快速配置应用:
|
|
35
66
|
|
|
36
67
|
```typescript
|
|
37
|
-
import {
|
|
38
|
-
import {
|
|
68
|
+
import { NestFactory } from '@nestjs/core';
|
|
69
|
+
import { NestExpressApplication } from '@nestjs/platform-express';
|
|
70
|
+
import { configureApp } from '@lark-apaas/fullstack-nestjs-core';
|
|
71
|
+
import { AppModule } from './app.module';
|
|
39
72
|
|
|
40
|
-
|
|
41
|
-
|
|
42
|
-
configure(consumer: MiddlewareConsumer) {
|
|
43
|
-
// 直接应用中间件,使用默认配置
|
|
44
|
-
consumer
|
|
45
|
-
.apply(CsrfTokenMiddleware)
|
|
46
|
-
.forRoutes('*'); // 所有路由都生成 token
|
|
73
|
+
async function bootstrap() {
|
|
74
|
+
const app = await NestFactory.create<NestExpressApplication>(AppModule);
|
|
47
75
|
|
|
48
|
-
|
|
49
|
-
|
|
50
|
-
|
|
51
|
-
|
|
76
|
+
// 一键配置:Logger、CookieParser、GlobalPrefix、DevTools
|
|
77
|
+
await configureApp(app);
|
|
78
|
+
|
|
79
|
+
await app.listen(3000);
|
|
52
80
|
}
|
|
81
|
+
bootstrap();
|
|
53
82
|
```
|
|
54
83
|
|
|
55
|
-
|
|
84
|
+
`configureApp` 会自动:
|
|
85
|
+
- ✅ 注册 AppLogger
|
|
86
|
+
- ✅ 注册 cookie-parser 中间件
|
|
87
|
+
- ✅ 根据 `CLIENT_BASE_PATH` 环境变量设置全局前缀
|
|
88
|
+
- ✅ 在非生产环境自动挂载 `DevToolsV2Module`
|
|
89
|
+
|
|
90
|
+
### 方案 3: 手动配置各个模块
|
|
56
91
|
|
|
57
|
-
|
|
92
|
+
如果需要更细粒度的控制,可以单独使用各个模块:
|
|
58
93
|
|
|
59
94
|
```typescript
|
|
60
95
|
import { Module, NestModule, MiddlewareConsumer } from '@nestjs/common';
|
|
61
|
-
import {
|
|
96
|
+
import {
|
|
97
|
+
CsrfTokenMiddleware,
|
|
98
|
+
CsrfMiddleware,
|
|
99
|
+
UserContextMiddleware,
|
|
100
|
+
ViewContextMiddleware,
|
|
101
|
+
} from '@lark-apaas/fullstack-nestjs-core';
|
|
62
102
|
|
|
63
103
|
@Module({})
|
|
64
104
|
export class AppModule implements NestModule {
|
|
65
105
|
configure(consumer: MiddlewareConsumer) {
|
|
66
|
-
//
|
|
67
|
-
|
|
68
|
-
|
|
69
|
-
|
|
70
|
-
cookiePath: '/', // 默认: '/'
|
|
71
|
-
});
|
|
72
|
-
|
|
73
|
-
// 自定义配置 CSRF 验证中间件
|
|
74
|
-
CsrfMiddleware.configure({
|
|
75
|
-
headerKey: 'X-CSRF-TOKEN', // 默认: 'x-suda-csrf-token'
|
|
76
|
-
cookieKey: 'CSRF-TOKEN', // 默认: 'suda-csrf-token'
|
|
77
|
-
});
|
|
106
|
+
// 用户上下文中间件
|
|
107
|
+
consumer
|
|
108
|
+
.apply(UserContextMiddleware)
|
|
109
|
+
.forRoutes('*');
|
|
78
110
|
|
|
79
|
-
//
|
|
111
|
+
// CSRF Token 生成(用于视图渲染)
|
|
80
112
|
consumer
|
|
81
113
|
.apply(CsrfTokenMiddleware)
|
|
114
|
+
.exclude('/api/(.*)')
|
|
82
115
|
.forRoutes('*');
|
|
83
116
|
|
|
117
|
+
// 视图上下文注入
|
|
84
118
|
consumer
|
|
85
|
-
.apply(
|
|
119
|
+
.apply(ViewContextMiddleware)
|
|
120
|
+
.exclude('/api/(.*)')
|
|
86
121
|
.forRoutes('*');
|
|
122
|
+
|
|
123
|
+
// CSRF 验证(用于 API 保护)
|
|
124
|
+
consumer
|
|
125
|
+
.apply(CsrfMiddleware)
|
|
126
|
+
.forRoutes('/api/*');
|
|
87
127
|
}
|
|
88
128
|
}
|
|
89
129
|
```
|
|
90
130
|
|
|
91
|
-
|
|
131
|
+
## 核心模块
|
|
92
132
|
|
|
93
|
-
###
|
|
133
|
+
### PlatformModule
|
|
94
134
|
|
|
95
|
-
|
|
135
|
+
全局平台模块,集成了所有核心功能。
|
|
96
136
|
|
|
97
|
-
|
|
98
|
-
import { Module, NestModule, MiddlewareConsumer } from '@nestjs/common';
|
|
99
|
-
import { UserContextMiddleware } from '@lark-apaas/fullstack-nestjs-core';
|
|
137
|
+
#### 配置选项
|
|
100
138
|
|
|
101
|
-
|
|
102
|
-
|
|
103
|
-
|
|
104
|
-
|
|
105
|
-
|
|
106
|
-
.forRoutes('*');
|
|
107
|
-
}
|
|
108
|
-
}
|
|
109
|
-
```
|
|
139
|
+
| 选项 | 类型 | 默认值 | 描述 |
|
|
140
|
+
|------|------|--------|------|
|
|
141
|
+
| enableCsrf | boolean | `true` | 是否启用 CSRF 保护 |
|
|
142
|
+
| csrfRoutes | string \| string[] | `'/api/*'` | CSRF 保护应用的路由 |
|
|
143
|
+
| alwaysNeedLogin | boolean | `true` | 是否所有路由都需要登录 |
|
|
110
144
|
|
|
111
|
-
|
|
145
|
+
#### 使用示例
|
|
112
146
|
|
|
113
147
|
```typescript
|
|
114
|
-
|
|
115
|
-
|
|
116
|
-
|
|
117
|
-
|
|
118
|
-
|
|
119
|
-
|
|
120
|
-
|
|
121
|
-
|
|
122
|
-
|
|
123
|
-
|
|
124
|
-
}
|
|
125
|
-
}
|
|
148
|
+
@Module({
|
|
149
|
+
imports: [
|
|
150
|
+
PlatformModule.forRoot({
|
|
151
|
+
enableCsrf: true,
|
|
152
|
+
csrfRoutes: ['/api/*', '/admin/*'],
|
|
153
|
+
alwaysNeedLogin: false,
|
|
154
|
+
}),
|
|
155
|
+
],
|
|
156
|
+
})
|
|
157
|
+
export class AppModule {}
|
|
126
158
|
```
|
|
127
159
|
|
|
128
|
-
###
|
|
160
|
+
### DevToolsV2Module(推荐)
|
|
161
|
+
|
|
162
|
+
使用 `@hey-api/openapi-ts` 生成高质量的 TypeScript 客户端 SDK。
|
|
163
|
+
|
|
164
|
+
**新特性:**
|
|
165
|
+
- ✅ 使用最新的 `@hey-api/openapi-ts` 生成器
|
|
166
|
+
- ✅ 自动注入 `baseURL` 配置到生成的客户端
|
|
167
|
+
- ✅ 自动给所有生成的文件添加 `// @ts-nocheck` 注释
|
|
168
|
+
|
|
169
|
+
#### 配置选项
|
|
129
170
|
|
|
130
|
-
|
|
171
|
+
| 选项 | 类型 | 默认值 | 描述 |
|
|
172
|
+
|------|------|--------|------|
|
|
173
|
+
| basePath | string | `'/'` | API 基础路径 |
|
|
174
|
+
| docsPath | string | `'/api/docs'` | Swagger UI 的挂载路径 |
|
|
175
|
+
| openapiOut | string | `'./client/src/api/gen/openapi.json'` | OpenAPI JSON 输出路径 |
|
|
176
|
+
| needSetupServer | boolean | `false` | 是否挂载 Swagger UI 服务器 |
|
|
177
|
+
| needGenerateClientSdk | boolean | `true` | 是否生成客户端 SDK |
|
|
178
|
+
| clientSdkOut | string | `'./client/src/api/gen'` | 客户端 SDK 输出目录 |
|
|
179
|
+
| swaggerOptions | object | - | Swagger 文档配置 |
|
|
180
|
+
|
|
181
|
+
#### 使用示例
|
|
131
182
|
|
|
132
183
|
```typescript
|
|
133
184
|
import { NestFactory } from '@nestjs/core';
|
|
134
|
-
import {
|
|
185
|
+
import { DevToolsV2Module } from '@lark-apaas/fullstack-nestjs-core';
|
|
135
186
|
import { AppModule } from './app.module';
|
|
136
187
|
|
|
137
188
|
async function bootstrap() {
|
|
138
189
|
const app = await NestFactory.create(AppModule);
|
|
139
190
|
|
|
140
|
-
//
|
|
141
|
-
|
|
142
|
-
|
|
143
|
-
|
|
144
|
-
|
|
145
|
-
|
|
146
|
-
|
|
147
|
-
|
|
148
|
-
|
|
149
|
-
|
|
150
|
-
|
|
151
|
-
}
|
|
152
|
-
}
|
|
191
|
+
// 在开发环境挂载开发工具
|
|
192
|
+
if (process.env.NODE_ENV !== 'production') {
|
|
193
|
+
await DevToolsV2Module.mount(app, {
|
|
194
|
+
basePath: '/app',
|
|
195
|
+
docsPath: '/api_docs',
|
|
196
|
+
needSetupServer: true,
|
|
197
|
+
needGenerateClientSdk: true,
|
|
198
|
+
swaggerOptions: {
|
|
199
|
+
title: 'My API',
|
|
200
|
+
version: '1.0.0',
|
|
201
|
+
},
|
|
202
|
+
});
|
|
203
|
+
}
|
|
153
204
|
|
|
154
205
|
await app.listen(3000);
|
|
155
206
|
}
|
|
156
207
|
bootstrap();
|
|
157
208
|
```
|
|
158
209
|
|
|
159
|
-
|
|
210
|
+
#### 生成的文件
|
|
211
|
+
|
|
212
|
+
```
|
|
213
|
+
client/src/api/gen/
|
|
214
|
+
├── openapi.json # OpenAPI 规范文件
|
|
215
|
+
├── client.config.ts # 客户端配置(自动生成 baseURL)
|
|
216
|
+
├── types.gen.ts # TypeScript 类型定义
|
|
217
|
+
├── sdk.gen.ts # SDK 函数
|
|
218
|
+
└── client/
|
|
219
|
+
└── client.gen.ts # Axios 客户端
|
|
220
|
+
```
|
|
221
|
+
|
|
222
|
+
所有生成的 `.ts` 文件都会自动添加 `// @ts-nocheck` 注释,避免类型检查错误。
|
|
223
|
+
|
|
224
|
+
### DevToolsModule(旧版)
|
|
225
|
+
|
|
226
|
+
使用 `openapi-typescript-codegen` 生成客户端 SDK(不推荐,建议迁移到 `DevToolsV2Module`)。
|
|
227
|
+
|
|
228
|
+
```typescript
|
|
229
|
+
import { DevToolsModule } from '@lark-apaas/fullstack-nestjs-core';
|
|
230
|
+
|
|
231
|
+
await DevToolsModule.mount(app, {
|
|
232
|
+
docsPath: 'api-docs',
|
|
233
|
+
openapiOut: './openapi.json',
|
|
234
|
+
needSetupServer: true,
|
|
235
|
+
needGenerateClientSdk: true,
|
|
236
|
+
clientSdkOut: './src/sdk',
|
|
237
|
+
});
|
|
238
|
+
```
|
|
239
|
+
|
|
240
|
+
## 中间件
|
|
160
241
|
|
|
161
242
|
### CsrfTokenMiddleware
|
|
162
243
|
|
|
163
|
-
生成 CSRF Token 并设置到 Cookie
|
|
244
|
+
生成 CSRF Token 并设置到 Cookie 中,用于视图渲染场景。
|
|
164
245
|
|
|
165
|
-
#### 配置选项
|
|
246
|
+
#### 配置选项
|
|
166
247
|
|
|
167
248
|
| 选项 | 类型 | 默认值 | 描述 |
|
|
168
249
|
|------|------|--------|------|
|
|
169
|
-
| cookieKey | string | `'suda-csrf-token'` | Cookie 中存储
|
|
170
|
-
| cookieMaxAge | number | `2592000000` (30天) | Cookie
|
|
171
|
-
| cookiePath | string | `'/'` | Cookie
|
|
250
|
+
| cookieKey | string | `'suda-csrf-token'` | Cookie 中存储 Token 的键名 |
|
|
251
|
+
| cookieMaxAge | number | `2592000000` (30天) | Cookie 过期时间(毫秒) |
|
|
252
|
+
| cookiePath | string | `'/'` | Cookie 路径 |
|
|
172
253
|
|
|
173
|
-
####
|
|
254
|
+
#### 使用示例
|
|
174
255
|
|
|
175
|
-
|
|
256
|
+
```typescript
|
|
257
|
+
CsrfTokenMiddleware.configure({
|
|
258
|
+
cookieKey: 'my-csrf-token',
|
|
259
|
+
cookieMaxAge: 86400000, // 1天
|
|
260
|
+
});
|
|
261
|
+
|
|
262
|
+
consumer
|
|
263
|
+
.apply(CsrfTokenMiddleware)
|
|
264
|
+
.exclude('/api/(.*)')
|
|
265
|
+
.forRoutes('*');
|
|
266
|
+
```
|
|
176
267
|
|
|
177
268
|
### CsrfMiddleware
|
|
178
269
|
|
|
179
|
-
验证请求中的 CSRF Token
|
|
270
|
+
验证请求中的 CSRF Token,用于保护 API 接口。
|
|
180
271
|
|
|
181
|
-
#### 配置选项
|
|
272
|
+
#### 配置选项
|
|
182
273
|
|
|
183
274
|
| 选项 | 类型 | 默认值 | 描述 |
|
|
184
275
|
|------|------|--------|------|
|
|
185
|
-
| headerKey | string | `'x-suda-csrf-token'` | 请求头中
|
|
186
|
-
| cookieKey | string | `'suda-csrf-token'` | Cookie 中
|
|
276
|
+
| headerKey | string | `'x-suda-csrf-token'` | 请求头中 Token 的键名 |
|
|
277
|
+
| cookieKey | string | `'suda-csrf-token'` | Cookie 中 Token 的键名 |
|
|
187
278
|
|
|
188
|
-
####
|
|
279
|
+
#### 使用示例
|
|
189
280
|
|
|
190
|
-
|
|
191
|
-
|
|
192
|
-
|
|
193
|
-
|
|
194
|
-
|
|
195
|
-
|
|
196
|
-
|
|
281
|
+
```typescript
|
|
282
|
+
CsrfMiddleware.configure({
|
|
283
|
+
headerKey: 'x-my-csrf-token',
|
|
284
|
+
cookieKey: 'my-csrf-token',
|
|
285
|
+
});
|
|
286
|
+
|
|
287
|
+
consumer
|
|
288
|
+
.apply(CsrfMiddleware)
|
|
289
|
+
.forRoutes('/api/*');
|
|
290
|
+
```
|
|
197
291
|
|
|
198
|
-
|
|
292
|
+
**注意**: 确保 `CsrfTokenMiddleware` 和 `CsrfMiddleware` 的 `cookieKey` 配置一致。
|
|
199
293
|
|
|
200
294
|
### UserContextMiddleware
|
|
201
295
|
|
|
202
|
-
|
|
296
|
+
从请求头中提取用户上下文信息并注入到 `req.userContext`。
|
|
203
297
|
|
|
204
298
|
#### 注入的上下文
|
|
205
299
|
|
|
206
300
|
```typescript
|
|
207
301
|
req.userContext = {
|
|
208
|
-
userId: string | undefined; // 用户 ID
|
|
209
|
-
tenantId: string | undefined; // 租户 ID
|
|
210
|
-
appId: string; // 应用 ID
|
|
302
|
+
userId: string | undefined; // 用户 ID(来自 x-user-id 请求头)
|
|
303
|
+
tenantId: string | undefined; // 租户 ID(来自 x-tenant-id 请求头)
|
|
304
|
+
appId: string; // 应用 ID(来自 x-app-id 请求头)
|
|
305
|
+
}
|
|
306
|
+
```
|
|
307
|
+
|
|
308
|
+
#### 使用示例
|
|
309
|
+
|
|
310
|
+
```typescript
|
|
311
|
+
@Controller()
|
|
312
|
+
export class AppController {
|
|
313
|
+
@Get('profile')
|
|
314
|
+
getProfile(@Req() req: Request) {
|
|
315
|
+
const { userId, tenantId, appId } = req.userContext;
|
|
316
|
+
return { userId, tenantId, appId };
|
|
317
|
+
}
|
|
211
318
|
}
|
|
212
319
|
```
|
|
213
320
|
|
|
214
|
-
###
|
|
321
|
+
### ViewContextMiddleware
|
|
215
322
|
|
|
216
|
-
|
|
323
|
+
将用户上下文和 CSRF Token 注入到 `res.locals`,用于模板渲染。
|
|
217
324
|
|
|
218
|
-
####
|
|
325
|
+
#### 注入的变量
|
|
219
326
|
|
|
220
|
-
|
|
221
|
-
|
|
222
|
-
|
|
223
|
-
|
|
224
|
-
|
|
225
|
-
|
|
226
|
-
|
|
227
|
-
|
|
228
|
-
| swaggerOptions | object | - | Swagger 文档配置 |
|
|
229
|
-
| swaggerOptions.title | string | - | Swagger 文档标题 |
|
|
230
|
-
| swaggerOptions.version | string | - | API 版本号 |
|
|
231
|
-
| swaggerOptions.customSiteTitle | string | - | 自定义站点标题 |
|
|
232
|
-
| swaggerOptions.customCss | string | - | 自定义 CSS 样式 |
|
|
327
|
+
```typescript
|
|
328
|
+
res.locals = {
|
|
329
|
+
csrfToken: string; // CSRF Token
|
|
330
|
+
userId: string; // 用户 ID
|
|
331
|
+
tenantId: string; // 租户 ID
|
|
332
|
+
appId: string; // 应用 ID
|
|
333
|
+
}
|
|
334
|
+
```
|
|
233
335
|
|
|
234
|
-
####
|
|
336
|
+
#### 使用示例
|
|
235
337
|
|
|
236
|
-
|
|
338
|
+
```typescript
|
|
339
|
+
// 在模板引擎中可以直接使用这些变量
|
|
340
|
+
// EJS 示例:
|
|
341
|
+
<input type="hidden" name="_csrf" value="<%= csrfToken %>">
|
|
342
|
+
<div>User ID: <%= userId %></div>
|
|
343
|
+
```
|
|
237
344
|
|
|
238
345
|
## TypeScript 支持
|
|
239
346
|
|
|
240
347
|
本包完全使用 TypeScript 编写,并提供完整的类型定义。
|
|
241
348
|
|
|
242
|
-
扩展 Express Request
|
|
349
|
+
### 扩展 Express Request 类型
|
|
243
350
|
|
|
244
351
|
```typescript
|
|
245
352
|
declare global {
|
|
246
353
|
namespace Express {
|
|
247
354
|
interface Request {
|
|
248
355
|
csrfToken?: string;
|
|
249
|
-
userContext
|
|
356
|
+
userContext: {
|
|
250
357
|
userId?: string;
|
|
251
358
|
tenantId?: string;
|
|
252
359
|
appId: string;
|
|
@@ -256,6 +363,13 @@ declare global {
|
|
|
256
363
|
}
|
|
257
364
|
```
|
|
258
365
|
|
|
366
|
+
## 环境变量
|
|
367
|
+
|
|
368
|
+
| 变量名 | 描述 | 默认值 |
|
|
369
|
+
|--------|------|--------|
|
|
370
|
+
| CLIENT_BASE_PATH | 应用的全局路径前缀 | - |
|
|
371
|
+
| NODE_ENV | 运行环境 | - |
|
|
372
|
+
|
|
259
373
|
## 许可证
|
|
260
374
|
|
|
261
375
|
MIT
|
|
@@ -266,3 +380,4 @@ MIT
|
|
|
266
380
|
- nestjs
|
|
267
381
|
- server
|
|
268
382
|
- typescript
|
|
383
|
+
- fullstack
|
package/dist/index.cjs
CHANGED
|
@@ -171,6 +171,12 @@ var ViewContextMiddleware = class {
|
|
|
171
171
|
use(req, res, next) {
|
|
172
172
|
const { userId, tenantId, appId } = req.userContext;
|
|
173
173
|
const csrfToken = req.csrfToken;
|
|
174
|
+
req.__platform_data__ = {
|
|
175
|
+
csrfToken: csrfToken ?? "",
|
|
176
|
+
userId: userId ?? "",
|
|
177
|
+
appId: appId ?? "",
|
|
178
|
+
tenantId
|
|
179
|
+
};
|
|
174
180
|
res.locals = {
|
|
175
181
|
...res.locals ?? {},
|
|
176
182
|
csrfToken: csrfToken ?? "",
|
|
@@ -364,14 +370,12 @@ PlatformModule = _ts_decorate5([
|
|
|
364
370
|
|
|
365
371
|
// src/setup.ts
|
|
366
372
|
var import_nestjs_logger2 = require("@lark-apaas/nestjs-logger");
|
|
367
|
-
var import_path = require("path");
|
|
368
|
-
var import_hbs = require("hbs");
|
|
369
373
|
var import_cookie_parser = __toESM(require("cookie-parser"), 1);
|
|
370
374
|
|
|
371
375
|
// src/modules/devtool-v2/index.ts
|
|
372
376
|
var import_swagger = require("@nestjs/swagger");
|
|
373
|
-
var
|
|
374
|
-
var
|
|
377
|
+
var import_node_fs4 = require("fs");
|
|
378
|
+
var import_node_path4 = require("path");
|
|
375
379
|
|
|
376
380
|
// src/modules/devtool-v2/helper.ts
|
|
377
381
|
var import_node_path = require("path");
|
|
@@ -427,6 +431,39 @@ export const createClientConfig: CreateClientConfig = (config) => ({
|
|
|
427
431
|
}
|
|
428
432
|
__name(generateClientConfig, "generateClientConfig");
|
|
429
433
|
|
|
434
|
+
// src/modules/devtool-v2/plugins/add-ts-nocheck.ts
|
|
435
|
+
var import_node_fs3 = require("fs");
|
|
436
|
+
var import_node_path3 = require("path");
|
|
437
|
+
function addTsNocheckToGeneratedFiles(outputPath) {
|
|
438
|
+
const files = getAllTsFiles(outputPath);
|
|
439
|
+
for (const filePath of files) {
|
|
440
|
+
addTsNocheckToFile(filePath);
|
|
441
|
+
}
|
|
442
|
+
}
|
|
443
|
+
__name(addTsNocheckToGeneratedFiles, "addTsNocheckToGeneratedFiles");
|
|
444
|
+
function getAllTsFiles(dirPath) {
|
|
445
|
+
const files = [];
|
|
446
|
+
const entries = (0, import_node_fs3.readdirSync)(dirPath);
|
|
447
|
+
for (const entry of entries) {
|
|
448
|
+
const fullPath = (0, import_node_path3.join)(dirPath, entry);
|
|
449
|
+
const stat = (0, import_node_fs3.statSync)(fullPath);
|
|
450
|
+
if (stat.isDirectory()) {
|
|
451
|
+
files.push(...getAllTsFiles(fullPath));
|
|
452
|
+
} else if (stat.isFile() && entry.endsWith(".ts")) {
|
|
453
|
+
files.push(fullPath);
|
|
454
|
+
}
|
|
455
|
+
}
|
|
456
|
+
return files;
|
|
457
|
+
}
|
|
458
|
+
__name(getAllTsFiles, "getAllTsFiles");
|
|
459
|
+
function addTsNocheckToFile(filePath) {
|
|
460
|
+
const content = (0, import_node_fs3.readFileSync)(filePath, "utf-8");
|
|
461
|
+
const newContent = `// @ts-nocheck
|
|
462
|
+
${content}`;
|
|
463
|
+
(0, import_node_fs3.writeFileSync)(filePath, newContent, "utf-8");
|
|
464
|
+
}
|
|
465
|
+
__name(addTsNocheckToFile, "addTsNocheckToFile");
|
|
466
|
+
|
|
430
467
|
// src/modules/devtool-v2/index.ts
|
|
431
468
|
var DevToolsV2Module = class {
|
|
432
469
|
static {
|
|
@@ -447,11 +484,11 @@ var DevToolsV2Module = class {
|
|
|
447
484
|
});
|
|
448
485
|
console.log(`[OpenAPI] Swagger UI \u5DF2\u6302\u8F7D\u81F3 ${options.docsPath}`);
|
|
449
486
|
}
|
|
450
|
-
const openapiPath = (0,
|
|
487
|
+
const openapiPath = (0, import_node_path4.resolve)(baseDirname, options.openapiOut);
|
|
451
488
|
ensureDirAndWrite(openapiPath, JSON.stringify(document, null, 2));
|
|
452
489
|
if (options.needGenerateClientSdk) {
|
|
453
|
-
const clientSdkOutPath = (0,
|
|
454
|
-
(0,
|
|
490
|
+
const clientSdkOutPath = (0, import_node_path4.resolve)(baseDirname, options.clientSdkOut);
|
|
491
|
+
(0, import_node_fs4.mkdirSync)(clientSdkOutPath, {
|
|
455
492
|
recursive: true
|
|
456
493
|
});
|
|
457
494
|
const { createClient } = await import("@hey-api/openapi-ts");
|
|
@@ -465,10 +502,11 @@ var DevToolsV2Module = class {
|
|
|
465
502
|
"@hey-api/sdk",
|
|
466
503
|
{
|
|
467
504
|
name: "@hey-api/client-axios",
|
|
468
|
-
runtimeConfigPath: (0,
|
|
505
|
+
runtimeConfigPath: (0, import_node_path4.join)(clientSdkOutPath, "./client.config.ts")
|
|
469
506
|
}
|
|
470
507
|
]
|
|
471
508
|
});
|
|
509
|
+
addTsNocheckToGeneratedFiles(clientSdkOutPath);
|
|
472
510
|
ensureDirAndWrite(openapiPath, JSON.stringify(document, null, 2));
|
|
473
511
|
generateClientConfig({
|
|
474
512
|
outputPath: clientSdkOutPath
|
|
@@ -479,15 +517,12 @@ var DevToolsV2Module = class {
|
|
|
479
517
|
};
|
|
480
518
|
|
|
481
519
|
// src/setup.ts
|
|
482
|
-
async function configureApp(app
|
|
520
|
+
async function configureApp(app) {
|
|
483
521
|
app.useLogger(app.get(import_nestjs_logger2.AppLogger));
|
|
484
522
|
app.flushLogs();
|
|
485
523
|
app.use((0, import_cookie_parser.default)());
|
|
486
524
|
const globalPrefix = process.env.CLIENT_BASE_PATH ?? "";
|
|
487
525
|
app.setGlobalPrefix(globalPrefix);
|
|
488
|
-
app.setBaseViewsDir((0, import_path.join)(process.cwd(), options?.viewsDir ?? "dist/client"));
|
|
489
|
-
app.setViewEngine("html");
|
|
490
|
-
app.engine("html", import_hbs.__express);
|
|
491
526
|
if (process.env.NODE_ENV !== "production") {
|
|
492
527
|
await DevToolsV2Module.mount(app, {
|
|
493
528
|
basePath: process.env.CLIENT_BASE_PATH,
|
|
@@ -499,12 +534,12 @@ __name(configureApp, "configureApp");
|
|
|
499
534
|
|
|
500
535
|
// src/modules/devtool/index.ts
|
|
501
536
|
var import_swagger2 = require("@nestjs/swagger");
|
|
502
|
-
var
|
|
503
|
-
var
|
|
537
|
+
var import_node_fs6 = require("fs");
|
|
538
|
+
var import_node_path6 = require("path");
|
|
504
539
|
|
|
505
540
|
// src/modules/devtool/helper.ts
|
|
506
|
-
var
|
|
507
|
-
var
|
|
541
|
+
var import_node_path5 = require("path");
|
|
542
|
+
var import_node_fs5 = require("fs");
|
|
508
543
|
function normalizeBasePath2(rawBasePath) {
|
|
509
544
|
const normalizedBasePath = rawBasePath.startsWith("/") ? rawBasePath : `/${rawBasePath}`;
|
|
510
545
|
return normalizedBasePath.endsWith("/") ? normalizedBasePath.slice(0, -1) : normalizedBasePath;
|
|
@@ -531,11 +566,11 @@ function resolveOptsWithDefaultValue2(options) {
|
|
|
531
566
|
}
|
|
532
567
|
__name(resolveOptsWithDefaultValue2, "resolveOptsWithDefaultValue");
|
|
533
568
|
function ensureDirAndWrite2(filePath, content) {
|
|
534
|
-
const dir = (0,
|
|
535
|
-
(0,
|
|
569
|
+
const dir = (0, import_node_path5.dirname)(filePath);
|
|
570
|
+
(0, import_node_fs5.mkdirSync)(dir, {
|
|
536
571
|
recursive: true
|
|
537
572
|
});
|
|
538
|
-
(0,
|
|
573
|
+
(0, import_node_fs5.writeFileSync)(filePath, content);
|
|
539
574
|
}
|
|
540
575
|
__name(ensureDirAndWrite2, "ensureDirAndWrite");
|
|
541
576
|
|
|
@@ -561,11 +596,11 @@ var DevToolsModule = class {
|
|
|
561
596
|
});
|
|
562
597
|
console.log(`[OpenAPI] Swagger UI \u5DF2\u6302\u8F7D\u81F3 ${options.docsPath}`);
|
|
563
598
|
}
|
|
564
|
-
const openapiPath = (0,
|
|
599
|
+
const openapiPath = (0, import_node_path6.resolve)(baseDirname, options.openapiOut);
|
|
565
600
|
ensureDirAndWrite2(openapiPath, JSON.stringify(document, null, 2));
|
|
566
601
|
if (options.needGenerateClientSdk) {
|
|
567
|
-
const clientSdkOutPath = (0,
|
|
568
|
-
(0,
|
|
602
|
+
const clientSdkOutPath = (0, import_node_path6.resolve)(baseDirname, options.clientSdkOut);
|
|
603
|
+
(0, import_node_fs6.mkdirSync)(clientSdkOutPath, {
|
|
569
604
|
recursive: true
|
|
570
605
|
});
|
|
571
606
|
const { generate } = await import("openapi-typescript-codegen");
|
package/dist/index.d.cts
CHANGED
|
@@ -4,19 +4,6 @@ import { Request, Response, NextFunction } from 'express';
|
|
|
4
4
|
export * from '@lark-apaas/nestjs-authnpaas';
|
|
5
5
|
export * from '@lark-apaas/nestjs-datapaas';
|
|
6
6
|
|
|
7
|
-
declare global {
|
|
8
|
-
namespace Express {
|
|
9
|
-
interface Request {
|
|
10
|
-
csrfToken?: string;
|
|
11
|
-
userContext: {
|
|
12
|
-
userId?: string;
|
|
13
|
-
tenantId?: number;
|
|
14
|
-
appId?: string;
|
|
15
|
-
}
|
|
16
|
-
}
|
|
17
|
-
}
|
|
18
|
-
}
|
|
19
|
-
|
|
20
7
|
/**
|
|
21
8
|
* PlatformModule 配置选项
|
|
22
9
|
*/
|
|
@@ -47,9 +34,7 @@ declare class PlatformModule implements NestModule {
|
|
|
47
34
|
configure(consumer: MiddlewareConsumer): void;
|
|
48
35
|
}
|
|
49
36
|
|
|
50
|
-
declare function configureApp(app: NestExpressApplication
|
|
51
|
-
viewsDir?: string;
|
|
52
|
-
}): Promise<void>;
|
|
37
|
+
declare function configureApp(app: NestExpressApplication): Promise<void>;
|
|
53
38
|
|
|
54
39
|
interface DevToolsOptions$1 {
|
|
55
40
|
basePath?: string;
|
package/dist/index.d.ts
CHANGED
|
@@ -4,19 +4,6 @@ import { Request, Response, NextFunction } from 'express';
|
|
|
4
4
|
export * from '@lark-apaas/nestjs-authnpaas';
|
|
5
5
|
export * from '@lark-apaas/nestjs-datapaas';
|
|
6
6
|
|
|
7
|
-
declare global {
|
|
8
|
-
namespace Express {
|
|
9
|
-
interface Request {
|
|
10
|
-
csrfToken?: string;
|
|
11
|
-
userContext: {
|
|
12
|
-
userId?: string;
|
|
13
|
-
tenantId?: number;
|
|
14
|
-
appId?: string;
|
|
15
|
-
}
|
|
16
|
-
}
|
|
17
|
-
}
|
|
18
|
-
}
|
|
19
|
-
|
|
20
7
|
/**
|
|
21
8
|
* PlatformModule 配置选项
|
|
22
9
|
*/
|
|
@@ -47,9 +34,7 @@ declare class PlatformModule implements NestModule {
|
|
|
47
34
|
configure(consumer: MiddlewareConsumer): void;
|
|
48
35
|
}
|
|
49
36
|
|
|
50
|
-
declare function configureApp(app: NestExpressApplication
|
|
51
|
-
viewsDir?: string;
|
|
52
|
-
}): Promise<void>;
|
|
37
|
+
declare function configureApp(app: NestExpressApplication): Promise<void>;
|
|
53
38
|
|
|
54
39
|
interface DevToolsOptions$1 {
|
|
55
40
|
basePath?: string;
|
package/dist/index.js
CHANGED
|
@@ -129,6 +129,12 @@ var ViewContextMiddleware = class {
|
|
|
129
129
|
use(req, res, next) {
|
|
130
130
|
const { userId, tenantId, appId } = req.userContext;
|
|
131
131
|
const csrfToken = req.csrfToken;
|
|
132
|
+
req.__platform_data__ = {
|
|
133
|
+
csrfToken: csrfToken ?? "",
|
|
134
|
+
userId: userId ?? "",
|
|
135
|
+
appId: appId ?? "",
|
|
136
|
+
tenantId
|
|
137
|
+
};
|
|
132
138
|
res.locals = {
|
|
133
139
|
...res.locals ?? {},
|
|
134
140
|
csrfToken: csrfToken ?? "",
|
|
@@ -322,14 +328,12 @@ PlatformModule = _ts_decorate5([
|
|
|
322
328
|
|
|
323
329
|
// src/setup.ts
|
|
324
330
|
import { AppLogger as AppLogger2 } from "@lark-apaas/nestjs-logger";
|
|
325
|
-
import { join as join2 } from "path";
|
|
326
|
-
import { __express as hbsExpressEngine } from "hbs";
|
|
327
331
|
import cookieParser from "cookie-parser";
|
|
328
332
|
|
|
329
333
|
// src/modules/devtool-v2/index.ts
|
|
330
334
|
import { SwaggerModule, DocumentBuilder } from "@nestjs/swagger";
|
|
331
335
|
import { mkdirSync as mkdirSync2 } from "fs";
|
|
332
|
-
import { resolve as resolve2, join } from "path";
|
|
336
|
+
import { resolve as resolve2, join as join2 } from "path";
|
|
333
337
|
|
|
334
338
|
// src/modules/devtool-v2/helper.ts
|
|
335
339
|
import { dirname } from "path";
|
|
@@ -385,6 +389,39 @@ export const createClientConfig: CreateClientConfig = (config) => ({
|
|
|
385
389
|
}
|
|
386
390
|
__name(generateClientConfig, "generateClientConfig");
|
|
387
391
|
|
|
392
|
+
// src/modules/devtool-v2/plugins/add-ts-nocheck.ts
|
|
393
|
+
import { readdirSync, readFileSync, writeFileSync as writeFileSync3, statSync } from "fs";
|
|
394
|
+
import { join } from "path";
|
|
395
|
+
function addTsNocheckToGeneratedFiles(outputPath) {
|
|
396
|
+
const files = getAllTsFiles(outputPath);
|
|
397
|
+
for (const filePath of files) {
|
|
398
|
+
addTsNocheckToFile(filePath);
|
|
399
|
+
}
|
|
400
|
+
}
|
|
401
|
+
__name(addTsNocheckToGeneratedFiles, "addTsNocheckToGeneratedFiles");
|
|
402
|
+
function getAllTsFiles(dirPath) {
|
|
403
|
+
const files = [];
|
|
404
|
+
const entries = readdirSync(dirPath);
|
|
405
|
+
for (const entry of entries) {
|
|
406
|
+
const fullPath = join(dirPath, entry);
|
|
407
|
+
const stat = statSync(fullPath);
|
|
408
|
+
if (stat.isDirectory()) {
|
|
409
|
+
files.push(...getAllTsFiles(fullPath));
|
|
410
|
+
} else if (stat.isFile() && entry.endsWith(".ts")) {
|
|
411
|
+
files.push(fullPath);
|
|
412
|
+
}
|
|
413
|
+
}
|
|
414
|
+
return files;
|
|
415
|
+
}
|
|
416
|
+
__name(getAllTsFiles, "getAllTsFiles");
|
|
417
|
+
function addTsNocheckToFile(filePath) {
|
|
418
|
+
const content = readFileSync(filePath, "utf-8");
|
|
419
|
+
const newContent = `// @ts-nocheck
|
|
420
|
+
${content}`;
|
|
421
|
+
writeFileSync3(filePath, newContent, "utf-8");
|
|
422
|
+
}
|
|
423
|
+
__name(addTsNocheckToFile, "addTsNocheckToFile");
|
|
424
|
+
|
|
388
425
|
// src/modules/devtool-v2/index.ts
|
|
389
426
|
var DevToolsV2Module = class {
|
|
390
427
|
static {
|
|
@@ -423,10 +460,11 @@ var DevToolsV2Module = class {
|
|
|
423
460
|
"@hey-api/sdk",
|
|
424
461
|
{
|
|
425
462
|
name: "@hey-api/client-axios",
|
|
426
|
-
runtimeConfigPath:
|
|
463
|
+
runtimeConfigPath: join2(clientSdkOutPath, "./client.config.ts")
|
|
427
464
|
}
|
|
428
465
|
]
|
|
429
466
|
});
|
|
467
|
+
addTsNocheckToGeneratedFiles(clientSdkOutPath);
|
|
430
468
|
ensureDirAndWrite(openapiPath, JSON.stringify(document, null, 2));
|
|
431
469
|
generateClientConfig({
|
|
432
470
|
outputPath: clientSdkOutPath
|
|
@@ -437,15 +475,12 @@ var DevToolsV2Module = class {
|
|
|
437
475
|
};
|
|
438
476
|
|
|
439
477
|
// src/setup.ts
|
|
440
|
-
async function configureApp(app
|
|
478
|
+
async function configureApp(app) {
|
|
441
479
|
app.useLogger(app.get(AppLogger2));
|
|
442
480
|
app.flushLogs();
|
|
443
481
|
app.use(cookieParser());
|
|
444
482
|
const globalPrefix = process.env.CLIENT_BASE_PATH ?? "";
|
|
445
483
|
app.setGlobalPrefix(globalPrefix);
|
|
446
|
-
app.setBaseViewsDir(join2(process.cwd(), options?.viewsDir ?? "dist/client"));
|
|
447
|
-
app.setViewEngine("html");
|
|
448
|
-
app.engine("html", hbsExpressEngine);
|
|
449
484
|
if (process.env.NODE_ENV !== "production") {
|
|
450
485
|
await DevToolsV2Module.mount(app, {
|
|
451
486
|
basePath: process.env.CLIENT_BASE_PATH,
|
|
@@ -462,7 +497,7 @@ import { resolve as resolve3 } from "path";
|
|
|
462
497
|
|
|
463
498
|
// src/modules/devtool/helper.ts
|
|
464
499
|
import { dirname as dirname2 } from "path";
|
|
465
|
-
import { writeFileSync as
|
|
500
|
+
import { writeFileSync as writeFileSync4, mkdirSync as mkdirSync3 } from "fs";
|
|
466
501
|
function normalizeBasePath2(rawBasePath) {
|
|
467
502
|
const normalizedBasePath = rawBasePath.startsWith("/") ? rawBasePath : `/${rawBasePath}`;
|
|
468
503
|
return normalizedBasePath.endsWith("/") ? normalizedBasePath.slice(0, -1) : normalizedBasePath;
|
|
@@ -493,7 +528,7 @@ function ensureDirAndWrite2(filePath, content) {
|
|
|
493
528
|
mkdirSync3(dir, {
|
|
494
529
|
recursive: true
|
|
495
530
|
});
|
|
496
|
-
|
|
531
|
+
writeFileSync4(filePath, content);
|
|
497
532
|
}
|
|
498
533
|
__name(ensureDirAndWrite2, "ensureDirAndWrite");
|
|
499
534
|
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@lark-apaas/fullstack-nestjs-core",
|
|
3
|
-
"version": "1.0.3-alpha.
|
|
3
|
+
"version": "1.0.3-alpha.14",
|
|
4
4
|
"description": "FullStack Nestjs Core",
|
|
5
5
|
"type": "module",
|
|
6
6
|
"main": "./dist/index.js",
|
|
@@ -45,7 +45,6 @@
|
|
|
45
45
|
"@lark-apaas/nestjs-datapaas": "^1.0.1",
|
|
46
46
|
"@lark-apaas/nestjs-logger": "^1.0.0",
|
|
47
47
|
"cookie-parser": "^1.4.7",
|
|
48
|
-
"hbs": "^4.2.0",
|
|
49
48
|
"openapi-typescript-codegen": "^0.29.0"
|
|
50
49
|
},
|
|
51
50
|
"devDependencies": {
|
|
@@ -54,7 +53,6 @@
|
|
|
54
53
|
"@nestjs/swagger": "^7.4.2",
|
|
55
54
|
"@types/cookie-parser": "^1.4.9",
|
|
56
55
|
"@types/express": "^5.0.3",
|
|
57
|
-
"@types/hbs": "^4.0.5",
|
|
58
56
|
"class-transformer": "^0.5.1",
|
|
59
57
|
"class-validator": "^0.14.2",
|
|
60
58
|
"drizzle-orm": "0.44.6",
|