@lark-apaas/fullstack-nestjs-core 1.0.3-alpha.8 → 1.0.3
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/README.md +239 -124
- package/dist/index.cjs +147 -27
- package/dist/index.d.cts +34 -12
- package/dist/index.d.ts +34 -12
- package/dist/index.js +142 -23
- package/package.json +4 -7
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
|
@@ -35,6 +35,7 @@ __export(index_exports, {
|
|
|
35
35
|
CsrfMiddleware: () => CsrfMiddleware,
|
|
36
36
|
CsrfTokenMiddleware: () => CsrfTokenMiddleware,
|
|
37
37
|
DevToolsModule: () => DevToolsModule,
|
|
38
|
+
DevToolsV2Module: () => DevToolsV2Module,
|
|
38
39
|
PlatformModule: () => PlatformModule,
|
|
39
40
|
UserContextMiddleware: () => UserContextMiddleware,
|
|
40
41
|
ViewContextMiddleware: () => ViewContextMiddleware,
|
|
@@ -89,7 +90,8 @@ var UserContextMiddleware = class {
|
|
|
89
90
|
req.userContext = {
|
|
90
91
|
userId: webUser?.user_id,
|
|
91
92
|
tenantId: webUser?.tenant_id,
|
|
92
|
-
appId: webUser?.app_id ?? ""
|
|
93
|
+
appId: webUser?.app_id ?? "",
|
|
94
|
+
loginUrl: webUser?.login_url ?? ""
|
|
93
95
|
};
|
|
94
96
|
next();
|
|
95
97
|
}
|
|
@@ -170,6 +172,12 @@ var ViewContextMiddleware = class {
|
|
|
170
172
|
use(req, res, next) {
|
|
171
173
|
const { userId, tenantId, appId } = req.userContext;
|
|
172
174
|
const csrfToken = req.csrfToken;
|
|
175
|
+
req.__platform_data__ = {
|
|
176
|
+
csrfToken: csrfToken ?? "",
|
|
177
|
+
userId: userId ?? "",
|
|
178
|
+
appId: appId ?? "",
|
|
179
|
+
tenantId
|
|
180
|
+
};
|
|
173
181
|
res.locals = {
|
|
174
182
|
...res.locals ?? {},
|
|
175
183
|
csrfToken: csrfToken ?? "",
|
|
@@ -317,9 +325,7 @@ var PlatformModule = class _PlatformModule {
|
|
|
317
325
|
};
|
|
318
326
|
}, "useFactory")
|
|
319
327
|
}),
|
|
320
|
-
import_nestjs_authnpaas.AuthNPaasModule.forRoot(
|
|
321
|
-
alwaysNeedLogin: options.alwaysNeedLogin ?? true
|
|
322
|
-
})
|
|
328
|
+
import_nestjs_authnpaas.AuthNPaasModule.forRoot()
|
|
323
329
|
],
|
|
324
330
|
providers: [
|
|
325
331
|
{
|
|
@@ -363,16 +369,14 @@ PlatformModule = _ts_decorate5([
|
|
|
363
369
|
|
|
364
370
|
// src/setup.ts
|
|
365
371
|
var import_nestjs_logger2 = require("@lark-apaas/nestjs-logger");
|
|
366
|
-
var import_path = require("path");
|
|
367
|
-
var import_hbs = require("hbs");
|
|
368
372
|
var import_cookie_parser = __toESM(require("cookie-parser"), 1);
|
|
369
373
|
|
|
370
|
-
// src/modules/devtool/index.ts
|
|
374
|
+
// src/modules/devtool-v2/index.ts
|
|
371
375
|
var import_swagger = require("@nestjs/swagger");
|
|
372
|
-
var
|
|
373
|
-
var
|
|
376
|
+
var import_node_fs4 = require("fs");
|
|
377
|
+
var import_node_path4 = require("path");
|
|
374
378
|
|
|
375
|
-
// src/modules/devtool/helper.ts
|
|
379
|
+
// src/modules/devtool-v2/helper.ts
|
|
376
380
|
var import_node_path = require("path");
|
|
377
381
|
var import_node_fs = require("fs");
|
|
378
382
|
function normalizeBasePath(rawBasePath) {
|
|
@@ -409,14 +413,14 @@ function ensureDirAndWrite(filePath, content) {
|
|
|
409
413
|
}
|
|
410
414
|
__name(ensureDirAndWrite, "ensureDirAndWrite");
|
|
411
415
|
|
|
412
|
-
// src/modules/devtool/plugins/client-config-generator.ts
|
|
416
|
+
// src/modules/devtool-v2/plugins/client-config-generator.ts
|
|
413
417
|
var import_node_fs2 = require("fs");
|
|
414
418
|
var import_node_path2 = require("path");
|
|
415
419
|
function generateClientConfig(options) {
|
|
416
420
|
const configFilePath = (0, import_node_path2.resolve)(options.outputPath, "client.config.ts");
|
|
417
421
|
const configContent = `// This file is auto-generated by @hey-api/openapi-ts
|
|
418
422
|
import axios from 'axios';
|
|
419
|
-
import type { CreateClientConfig } from './client
|
|
423
|
+
import type { CreateClientConfig } from './client';
|
|
420
424
|
export const createClientConfig: CreateClientConfig = (config) => ({
|
|
421
425
|
axios
|
|
422
426
|
});
|
|
@@ -426,10 +430,43 @@ export const createClientConfig: CreateClientConfig = (config) => ({
|
|
|
426
430
|
}
|
|
427
431
|
__name(generateClientConfig, "generateClientConfig");
|
|
428
432
|
|
|
429
|
-
// src/modules/devtool/
|
|
430
|
-
var
|
|
433
|
+
// src/modules/devtool-v2/plugins/add-ts-nocheck.ts
|
|
434
|
+
var import_node_fs3 = require("fs");
|
|
435
|
+
var import_node_path3 = require("path");
|
|
436
|
+
function addTsNocheckToGeneratedFiles(outputPath) {
|
|
437
|
+
const files = getAllTsFiles(outputPath);
|
|
438
|
+
for (const filePath of files) {
|
|
439
|
+
addTsNocheckToFile(filePath);
|
|
440
|
+
}
|
|
441
|
+
}
|
|
442
|
+
__name(addTsNocheckToGeneratedFiles, "addTsNocheckToGeneratedFiles");
|
|
443
|
+
function getAllTsFiles(dirPath) {
|
|
444
|
+
const files = [];
|
|
445
|
+
const entries = (0, import_node_fs3.readdirSync)(dirPath);
|
|
446
|
+
for (const entry of entries) {
|
|
447
|
+
const fullPath = (0, import_node_path3.join)(dirPath, entry);
|
|
448
|
+
const stat = (0, import_node_fs3.statSync)(fullPath);
|
|
449
|
+
if (stat.isDirectory()) {
|
|
450
|
+
files.push(...getAllTsFiles(fullPath));
|
|
451
|
+
} else if (stat.isFile() && entry.endsWith(".ts")) {
|
|
452
|
+
files.push(fullPath);
|
|
453
|
+
}
|
|
454
|
+
}
|
|
455
|
+
return files;
|
|
456
|
+
}
|
|
457
|
+
__name(getAllTsFiles, "getAllTsFiles");
|
|
458
|
+
function addTsNocheckToFile(filePath) {
|
|
459
|
+
const content = (0, import_node_fs3.readFileSync)(filePath, "utf-8");
|
|
460
|
+
const newContent = `// @ts-nocheck
|
|
461
|
+
${content}`;
|
|
462
|
+
(0, import_node_fs3.writeFileSync)(filePath, newContent, "utf-8");
|
|
463
|
+
}
|
|
464
|
+
__name(addTsNocheckToFile, "addTsNocheckToFile");
|
|
465
|
+
|
|
466
|
+
// src/modules/devtool-v2/index.ts
|
|
467
|
+
var DevToolsV2Module = class {
|
|
431
468
|
static {
|
|
432
|
-
__name(this, "
|
|
469
|
+
__name(this, "DevToolsV2Module");
|
|
433
470
|
}
|
|
434
471
|
static async mount(app, opts = {}) {
|
|
435
472
|
const options = resolveOptsWithDefaultValue(opts);
|
|
@@ -446,16 +483,13 @@ var DevToolsModule = class {
|
|
|
446
483
|
});
|
|
447
484
|
console.log(`[OpenAPI] Swagger UI \u5DF2\u6302\u8F7D\u81F3 ${options.docsPath}`);
|
|
448
485
|
}
|
|
449
|
-
const openapiPath = (0,
|
|
486
|
+
const openapiPath = (0, import_node_path4.resolve)(baseDirname, options.openapiOut);
|
|
450
487
|
ensureDirAndWrite(openapiPath, JSON.stringify(document, null, 2));
|
|
451
488
|
if (options.needGenerateClientSdk) {
|
|
452
|
-
const clientSdkOutPath = (0,
|
|
453
|
-
(0,
|
|
489
|
+
const clientSdkOutPath = (0, import_node_path4.resolve)(baseDirname, options.clientSdkOut);
|
|
490
|
+
(0, import_node_fs4.mkdirSync)(clientSdkOutPath, {
|
|
454
491
|
recursive: true
|
|
455
492
|
});
|
|
456
|
-
generateClientConfig({
|
|
457
|
-
outputPath: clientSdkOutPath
|
|
458
|
-
});
|
|
459
493
|
const { createClient } = await import("@hey-api/openapi-ts");
|
|
460
494
|
await createClient({
|
|
461
495
|
input: openapiPath,
|
|
@@ -467,28 +501,29 @@ var DevToolsModule = class {
|
|
|
467
501
|
"@hey-api/sdk",
|
|
468
502
|
{
|
|
469
503
|
name: "@hey-api/client-axios",
|
|
470
|
-
runtimeConfigPath: (0,
|
|
504
|
+
runtimeConfigPath: (0, import_node_path4.join)(clientSdkOutPath, "./client.config.ts")
|
|
471
505
|
}
|
|
472
506
|
]
|
|
473
507
|
});
|
|
508
|
+
addTsNocheckToGeneratedFiles(clientSdkOutPath);
|
|
474
509
|
ensureDirAndWrite(openapiPath, JSON.stringify(document, null, 2));
|
|
510
|
+
generateClientConfig({
|
|
511
|
+
outputPath: clientSdkOutPath
|
|
512
|
+
});
|
|
475
513
|
console.log("[OpenAPI] \u5BFC\u51FA openapi.json \u5E76\u751F\u6210 axios SDK \u2705");
|
|
476
514
|
}
|
|
477
515
|
}
|
|
478
516
|
};
|
|
479
517
|
|
|
480
518
|
// src/setup.ts
|
|
481
|
-
async function configureApp(app
|
|
519
|
+
async function configureApp(app) {
|
|
482
520
|
app.useLogger(app.get(import_nestjs_logger2.AppLogger));
|
|
483
521
|
app.flushLogs();
|
|
484
522
|
app.use((0, import_cookie_parser.default)());
|
|
485
523
|
const globalPrefix = process.env.CLIENT_BASE_PATH ?? "";
|
|
486
524
|
app.setGlobalPrefix(globalPrefix);
|
|
487
|
-
app.setBaseViewsDir((0, import_path.join)(process.cwd(), options?.viewsDir ?? "dist/client"));
|
|
488
|
-
app.setViewEngine("html");
|
|
489
|
-
app.engine("html", import_hbs.__express);
|
|
490
525
|
if (process.env.NODE_ENV !== "production") {
|
|
491
|
-
await
|
|
526
|
+
await DevToolsV2Module.mount(app, {
|
|
492
527
|
basePath: process.env.CLIENT_BASE_PATH,
|
|
493
528
|
docsPath: "/api_docs"
|
|
494
529
|
});
|
|
@@ -496,6 +531,90 @@ async function configureApp(app, options) {
|
|
|
496
531
|
}
|
|
497
532
|
__name(configureApp, "configureApp");
|
|
498
533
|
|
|
534
|
+
// src/modules/devtool/index.ts
|
|
535
|
+
var import_swagger2 = require("@nestjs/swagger");
|
|
536
|
+
var import_node_fs6 = require("fs");
|
|
537
|
+
var import_node_path6 = require("path");
|
|
538
|
+
|
|
539
|
+
// src/modules/devtool/helper.ts
|
|
540
|
+
var import_node_path5 = require("path");
|
|
541
|
+
var import_node_fs5 = require("fs");
|
|
542
|
+
function normalizeBasePath2(rawBasePath) {
|
|
543
|
+
const normalizedBasePath = rawBasePath.startsWith("/") ? rawBasePath : `/${rawBasePath}`;
|
|
544
|
+
return normalizedBasePath.endsWith("/") ? normalizedBasePath.slice(0, -1) : normalizedBasePath;
|
|
545
|
+
}
|
|
546
|
+
__name(normalizeBasePath2, "normalizeBasePath");
|
|
547
|
+
function resolveOptsWithDefaultValue2(options) {
|
|
548
|
+
const basePath = normalizeBasePath2(options.basePath || "/");
|
|
549
|
+
const docsPath = normalizeBasePath2(options.docsPath || `api/docs`);
|
|
550
|
+
return {
|
|
551
|
+
...options,
|
|
552
|
+
needSetupServer: options.needSetupServer ?? false,
|
|
553
|
+
basePath,
|
|
554
|
+
docsPath: `${basePath}${docsPath}`,
|
|
555
|
+
openapiOut: options.openapiOut || "./client/src/api/gen/openapi.json",
|
|
556
|
+
clientSdkOut: options.clientSdkOut || "./client/src/api/gen",
|
|
557
|
+
needGenerateClientSdk: options.needGenerateClientSdk ?? true,
|
|
558
|
+
swaggerOptions: {
|
|
559
|
+
title: options.swaggerOptions?.title ?? "NestJS Fullstack API",
|
|
560
|
+
version: options.swaggerOptions?.version ?? "1.0.0",
|
|
561
|
+
customSiteTitle: options.swaggerOptions?.customSiteTitle ?? "API Documentation",
|
|
562
|
+
customCss: options.swaggerOptions?.customCss ?? ".swagger-ui .topbar { display: none }"
|
|
563
|
+
}
|
|
564
|
+
};
|
|
565
|
+
}
|
|
566
|
+
__name(resolveOptsWithDefaultValue2, "resolveOptsWithDefaultValue");
|
|
567
|
+
function ensureDirAndWrite2(filePath, content) {
|
|
568
|
+
const dir = (0, import_node_path5.dirname)(filePath);
|
|
569
|
+
(0, import_node_fs5.mkdirSync)(dir, {
|
|
570
|
+
recursive: true
|
|
571
|
+
});
|
|
572
|
+
(0, import_node_fs5.writeFileSync)(filePath, content);
|
|
573
|
+
}
|
|
574
|
+
__name(ensureDirAndWrite2, "ensureDirAndWrite");
|
|
575
|
+
|
|
576
|
+
// src/modules/devtool/index.ts
|
|
577
|
+
var DevToolsModule = class {
|
|
578
|
+
static {
|
|
579
|
+
__name(this, "DevToolsModule");
|
|
580
|
+
}
|
|
581
|
+
static async mount(app, opts = {}) {
|
|
582
|
+
const options = resolveOptsWithDefaultValue2(opts);
|
|
583
|
+
const baseDirname = process.cwd();
|
|
584
|
+
const builder = new import_swagger2.DocumentBuilder().setTitle(options.swaggerOptions.title).setVersion(options.swaggerOptions.version);
|
|
585
|
+
const document = import_swagger2.SwaggerModule.createDocument(app, builder.build(), {
|
|
586
|
+
operationIdFactory: /* @__PURE__ */ __name((_c, m) => m, "operationIdFactory")
|
|
587
|
+
});
|
|
588
|
+
if (options.needSetupServer) {
|
|
589
|
+
import_swagger2.SwaggerModule.setup(options.docsPath, app, document, {
|
|
590
|
+
customSiteTitle: options.swaggerOptions.customSiteTitle,
|
|
591
|
+
customCss: options.swaggerOptions.customCss,
|
|
592
|
+
swaggerOptions: {
|
|
593
|
+
persistAuthorization: true
|
|
594
|
+
}
|
|
595
|
+
});
|
|
596
|
+
console.log(`[OpenAPI] Swagger UI \u5DF2\u6302\u8F7D\u81F3 ${options.docsPath}`);
|
|
597
|
+
}
|
|
598
|
+
const openapiPath = (0, import_node_path6.resolve)(baseDirname, options.openapiOut);
|
|
599
|
+
ensureDirAndWrite2(openapiPath, JSON.stringify(document, null, 2));
|
|
600
|
+
if (options.needGenerateClientSdk) {
|
|
601
|
+
const clientSdkOutPath = (0, import_node_path6.resolve)(baseDirname, options.clientSdkOut);
|
|
602
|
+
(0, import_node_fs6.mkdirSync)(clientSdkOutPath, {
|
|
603
|
+
recursive: true
|
|
604
|
+
});
|
|
605
|
+
const { generate } = await import("openapi-typescript-codegen");
|
|
606
|
+
await generate({
|
|
607
|
+
input: openapiPath,
|
|
608
|
+
output: clientSdkOutPath,
|
|
609
|
+
httpClient: "axios",
|
|
610
|
+
useOptions: false,
|
|
611
|
+
exportServices: true
|
|
612
|
+
});
|
|
613
|
+
console.log("[OpenAPI] \u5BFC\u51FA openapi.json \u5E76\u751F\u6210 axios SDK \u2705");
|
|
614
|
+
}
|
|
615
|
+
}
|
|
616
|
+
};
|
|
617
|
+
|
|
499
618
|
// src/index.ts
|
|
500
619
|
__reExport(index_exports, require("@lark-apaas/nestjs-authnpaas"), module.exports);
|
|
501
620
|
__reExport(index_exports, require("@lark-apaas/nestjs-datapaas"), module.exports);
|
|
@@ -504,6 +623,7 @@ __reExport(index_exports, require("@lark-apaas/nestjs-datapaas"), module.exports
|
|
|
504
623
|
CsrfMiddleware,
|
|
505
624
|
CsrfTokenMiddleware,
|
|
506
625
|
DevToolsModule,
|
|
626
|
+
DevToolsV2Module,
|
|
507
627
|
PlatformModule,
|
|
508
628
|
UserContextMiddleware,
|
|
509
629
|
ViewContextMiddleware,
|
package/dist/index.d.cts
CHANGED
|
@@ -7,12 +7,19 @@ export * from '@lark-apaas/nestjs-datapaas';
|
|
|
7
7
|
declare global {
|
|
8
8
|
namespace Express {
|
|
9
9
|
interface Request {
|
|
10
|
-
|
|
10
|
+
__platform_data__?: {
|
|
11
|
+
userId?: string;
|
|
12
|
+
tenantId?: number;
|
|
13
|
+
csrfToken?: string;
|
|
14
|
+
[key: string]: unknown;
|
|
15
|
+
};
|
|
11
16
|
userContext: {
|
|
12
17
|
userId?: string;
|
|
13
18
|
tenantId?: number;
|
|
14
19
|
appId?: string;
|
|
15
|
-
|
|
20
|
+
loginUrl?: string;
|
|
21
|
+
},
|
|
22
|
+
csrfToken?: string;
|
|
16
23
|
}
|
|
17
24
|
}
|
|
18
25
|
}
|
|
@@ -31,11 +38,6 @@ interface PlatformModuleOptions {
|
|
|
31
38
|
* 默认: '/api/*'
|
|
32
39
|
*/
|
|
33
40
|
csrfRoutes?: string | string[];
|
|
34
|
-
/**
|
|
35
|
-
* AuthN 能力,是否全部依赖登录
|
|
36
|
-
* 默认:true
|
|
37
|
-
*/
|
|
38
|
-
alwaysNeedLogin?: boolean;
|
|
39
41
|
}
|
|
40
42
|
|
|
41
43
|
declare class PlatformModule implements NestModule {
|
|
@@ -47,11 +49,9 @@ declare class PlatformModule implements NestModule {
|
|
|
47
49
|
configure(consumer: MiddlewareConsumer): void;
|
|
48
50
|
}
|
|
49
51
|
|
|
50
|
-
declare function configureApp(app: NestExpressApplication
|
|
51
|
-
viewsDir?: string;
|
|
52
|
-
}): Promise<void>;
|
|
52
|
+
declare function configureApp(app: NestExpressApplication): Promise<void>;
|
|
53
53
|
|
|
54
|
-
interface DevToolsOptions {
|
|
54
|
+
interface DevToolsOptions$1 {
|
|
55
55
|
basePath?: string;
|
|
56
56
|
docsPath?: string;
|
|
57
57
|
openapiOut?: string;
|
|
@@ -66,7 +66,29 @@ interface DevToolsOptions {
|
|
|
66
66
|
};
|
|
67
67
|
}
|
|
68
68
|
|
|
69
|
+
/**
|
|
70
|
+
* @deprecated 该模块已被废弃,不建议使用,建议使用 DevToolsV2Module
|
|
71
|
+
*/
|
|
69
72
|
declare class DevToolsModule {
|
|
73
|
+
static mount(app: INestApplication, opts?: DevToolsOptions$1): Promise<void>;
|
|
74
|
+
}
|
|
75
|
+
|
|
76
|
+
interface DevToolsOptions {
|
|
77
|
+
basePath?: string;
|
|
78
|
+
docsPath?: string;
|
|
79
|
+
openapiOut?: string;
|
|
80
|
+
needSetupServer?: boolean;
|
|
81
|
+
needGenerateClientSdk?: boolean;
|
|
82
|
+
clientSdkOut?: string;
|
|
83
|
+
swaggerOptions?: {
|
|
84
|
+
title?: string;
|
|
85
|
+
version?: string;
|
|
86
|
+
customSiteTitle?: string;
|
|
87
|
+
customCss?: string;
|
|
88
|
+
};
|
|
89
|
+
}
|
|
90
|
+
|
|
91
|
+
declare class DevToolsV2Module {
|
|
70
92
|
static mount(app: INestApplication, opts?: DevToolsOptions): Promise<void>;
|
|
71
93
|
}
|
|
72
94
|
|
|
@@ -101,4 +123,4 @@ declare class ViewContextMiddleware implements NestMiddleware {
|
|
|
101
123
|
use(req: Request, res: Response, next: NextFunction): void;
|
|
102
124
|
}
|
|
103
125
|
|
|
104
|
-
export { CsrfMiddleware, CsrfTokenMiddleware, DevToolsModule, PlatformModule, type PlatformModuleOptions, UserContextMiddleware, ViewContextMiddleware, configureApp };
|
|
126
|
+
export { CsrfMiddleware, CsrfTokenMiddleware, DevToolsModule, DevToolsV2Module, PlatformModule, type PlatformModuleOptions, UserContextMiddleware, ViewContextMiddleware, configureApp };
|
package/dist/index.d.ts
CHANGED
|
@@ -7,12 +7,19 @@ export * from '@lark-apaas/nestjs-datapaas';
|
|
|
7
7
|
declare global {
|
|
8
8
|
namespace Express {
|
|
9
9
|
interface Request {
|
|
10
|
-
|
|
10
|
+
__platform_data__?: {
|
|
11
|
+
userId?: string;
|
|
12
|
+
tenantId?: number;
|
|
13
|
+
csrfToken?: string;
|
|
14
|
+
[key: string]: unknown;
|
|
15
|
+
};
|
|
11
16
|
userContext: {
|
|
12
17
|
userId?: string;
|
|
13
18
|
tenantId?: number;
|
|
14
19
|
appId?: string;
|
|
15
|
-
|
|
20
|
+
loginUrl?: string;
|
|
21
|
+
},
|
|
22
|
+
csrfToken?: string;
|
|
16
23
|
}
|
|
17
24
|
}
|
|
18
25
|
}
|
|
@@ -31,11 +38,6 @@ interface PlatformModuleOptions {
|
|
|
31
38
|
* 默认: '/api/*'
|
|
32
39
|
*/
|
|
33
40
|
csrfRoutes?: string | string[];
|
|
34
|
-
/**
|
|
35
|
-
* AuthN 能力,是否全部依赖登录
|
|
36
|
-
* 默认:true
|
|
37
|
-
*/
|
|
38
|
-
alwaysNeedLogin?: boolean;
|
|
39
41
|
}
|
|
40
42
|
|
|
41
43
|
declare class PlatformModule implements NestModule {
|
|
@@ -47,11 +49,9 @@ declare class PlatformModule implements NestModule {
|
|
|
47
49
|
configure(consumer: MiddlewareConsumer): void;
|
|
48
50
|
}
|
|
49
51
|
|
|
50
|
-
declare function configureApp(app: NestExpressApplication
|
|
51
|
-
viewsDir?: string;
|
|
52
|
-
}): Promise<void>;
|
|
52
|
+
declare function configureApp(app: NestExpressApplication): Promise<void>;
|
|
53
53
|
|
|
54
|
-
interface DevToolsOptions {
|
|
54
|
+
interface DevToolsOptions$1 {
|
|
55
55
|
basePath?: string;
|
|
56
56
|
docsPath?: string;
|
|
57
57
|
openapiOut?: string;
|
|
@@ -66,7 +66,29 @@ interface DevToolsOptions {
|
|
|
66
66
|
};
|
|
67
67
|
}
|
|
68
68
|
|
|
69
|
+
/**
|
|
70
|
+
* @deprecated 该模块已被废弃,不建议使用,建议使用 DevToolsV2Module
|
|
71
|
+
*/
|
|
69
72
|
declare class DevToolsModule {
|
|
73
|
+
static mount(app: INestApplication, opts?: DevToolsOptions$1): Promise<void>;
|
|
74
|
+
}
|
|
75
|
+
|
|
76
|
+
interface DevToolsOptions {
|
|
77
|
+
basePath?: string;
|
|
78
|
+
docsPath?: string;
|
|
79
|
+
openapiOut?: string;
|
|
80
|
+
needSetupServer?: boolean;
|
|
81
|
+
needGenerateClientSdk?: boolean;
|
|
82
|
+
clientSdkOut?: string;
|
|
83
|
+
swaggerOptions?: {
|
|
84
|
+
title?: string;
|
|
85
|
+
version?: string;
|
|
86
|
+
customSiteTitle?: string;
|
|
87
|
+
customCss?: string;
|
|
88
|
+
};
|
|
89
|
+
}
|
|
90
|
+
|
|
91
|
+
declare class DevToolsV2Module {
|
|
70
92
|
static mount(app: INestApplication, opts?: DevToolsOptions): Promise<void>;
|
|
71
93
|
}
|
|
72
94
|
|
|
@@ -101,4 +123,4 @@ declare class ViewContextMiddleware implements NestMiddleware {
|
|
|
101
123
|
use(req: Request, res: Response, next: NextFunction): void;
|
|
102
124
|
}
|
|
103
125
|
|
|
104
|
-
export { CsrfMiddleware, CsrfTokenMiddleware, DevToolsModule, PlatformModule, type PlatformModuleOptions, UserContextMiddleware, ViewContextMiddleware, configureApp };
|
|
126
|
+
export { CsrfMiddleware, CsrfTokenMiddleware, DevToolsModule, DevToolsV2Module, PlatformModule, type PlatformModuleOptions, UserContextMiddleware, ViewContextMiddleware, configureApp };
|
package/dist/index.js
CHANGED
|
@@ -48,7 +48,8 @@ var UserContextMiddleware = class {
|
|
|
48
48
|
req.userContext = {
|
|
49
49
|
userId: webUser?.user_id,
|
|
50
50
|
tenantId: webUser?.tenant_id,
|
|
51
|
-
appId: webUser?.app_id ?? ""
|
|
51
|
+
appId: webUser?.app_id ?? "",
|
|
52
|
+
loginUrl: webUser?.login_url ?? ""
|
|
52
53
|
};
|
|
53
54
|
next();
|
|
54
55
|
}
|
|
@@ -129,6 +130,12 @@ var ViewContextMiddleware = class {
|
|
|
129
130
|
use(req, res, next) {
|
|
130
131
|
const { userId, tenantId, appId } = req.userContext;
|
|
131
132
|
const csrfToken = req.csrfToken;
|
|
133
|
+
req.__platform_data__ = {
|
|
134
|
+
csrfToken: csrfToken ?? "",
|
|
135
|
+
userId: userId ?? "",
|
|
136
|
+
appId: appId ?? "",
|
|
137
|
+
tenantId
|
|
138
|
+
};
|
|
132
139
|
res.locals = {
|
|
133
140
|
...res.locals ?? {},
|
|
134
141
|
csrfToken: csrfToken ?? "",
|
|
@@ -276,9 +283,7 @@ var PlatformModule = class _PlatformModule {
|
|
|
276
283
|
};
|
|
277
284
|
}, "useFactory")
|
|
278
285
|
}),
|
|
279
|
-
AuthNPaasModule.forRoot(
|
|
280
|
-
alwaysNeedLogin: options.alwaysNeedLogin ?? true
|
|
281
|
-
})
|
|
286
|
+
AuthNPaasModule.forRoot()
|
|
282
287
|
],
|
|
283
288
|
providers: [
|
|
284
289
|
{
|
|
@@ -322,16 +327,14 @@ PlatformModule = _ts_decorate5([
|
|
|
322
327
|
|
|
323
328
|
// src/setup.ts
|
|
324
329
|
import { AppLogger as AppLogger2 } from "@lark-apaas/nestjs-logger";
|
|
325
|
-
import { join as join2 } from "path";
|
|
326
|
-
import { __express as hbsExpressEngine } from "hbs";
|
|
327
330
|
import cookieParser from "cookie-parser";
|
|
328
331
|
|
|
329
|
-
// src/modules/devtool/index.ts
|
|
332
|
+
// src/modules/devtool-v2/index.ts
|
|
330
333
|
import { SwaggerModule, DocumentBuilder } from "@nestjs/swagger";
|
|
331
334
|
import { mkdirSync as mkdirSync2 } from "fs";
|
|
332
|
-
import { resolve as resolve2, join } from "path";
|
|
335
|
+
import { resolve as resolve2, join as join2 } from "path";
|
|
333
336
|
|
|
334
|
-
// src/modules/devtool/helper.ts
|
|
337
|
+
// src/modules/devtool-v2/helper.ts
|
|
335
338
|
import { dirname } from "path";
|
|
336
339
|
import { writeFileSync, mkdirSync } from "fs";
|
|
337
340
|
function normalizeBasePath(rawBasePath) {
|
|
@@ -368,14 +371,14 @@ function ensureDirAndWrite(filePath, content) {
|
|
|
368
371
|
}
|
|
369
372
|
__name(ensureDirAndWrite, "ensureDirAndWrite");
|
|
370
373
|
|
|
371
|
-
// src/modules/devtool/plugins/client-config-generator.ts
|
|
374
|
+
// src/modules/devtool-v2/plugins/client-config-generator.ts
|
|
372
375
|
import { writeFileSync as writeFileSync2 } from "fs";
|
|
373
376
|
import { resolve } from "path";
|
|
374
377
|
function generateClientConfig(options) {
|
|
375
378
|
const configFilePath = resolve(options.outputPath, "client.config.ts");
|
|
376
379
|
const configContent = `// This file is auto-generated by @hey-api/openapi-ts
|
|
377
380
|
import axios from 'axios';
|
|
378
|
-
import type { CreateClientConfig } from './client
|
|
381
|
+
import type { CreateClientConfig } from './client';
|
|
379
382
|
export const createClientConfig: CreateClientConfig = (config) => ({
|
|
380
383
|
axios
|
|
381
384
|
});
|
|
@@ -385,10 +388,43 @@ export const createClientConfig: CreateClientConfig = (config) => ({
|
|
|
385
388
|
}
|
|
386
389
|
__name(generateClientConfig, "generateClientConfig");
|
|
387
390
|
|
|
388
|
-
// src/modules/devtool/
|
|
389
|
-
|
|
391
|
+
// src/modules/devtool-v2/plugins/add-ts-nocheck.ts
|
|
392
|
+
import { readdirSync, readFileSync, writeFileSync as writeFileSync3, statSync } from "fs";
|
|
393
|
+
import { join } from "path";
|
|
394
|
+
function addTsNocheckToGeneratedFiles(outputPath) {
|
|
395
|
+
const files = getAllTsFiles(outputPath);
|
|
396
|
+
for (const filePath of files) {
|
|
397
|
+
addTsNocheckToFile(filePath);
|
|
398
|
+
}
|
|
399
|
+
}
|
|
400
|
+
__name(addTsNocheckToGeneratedFiles, "addTsNocheckToGeneratedFiles");
|
|
401
|
+
function getAllTsFiles(dirPath) {
|
|
402
|
+
const files = [];
|
|
403
|
+
const entries = readdirSync(dirPath);
|
|
404
|
+
for (const entry of entries) {
|
|
405
|
+
const fullPath = join(dirPath, entry);
|
|
406
|
+
const stat = statSync(fullPath);
|
|
407
|
+
if (stat.isDirectory()) {
|
|
408
|
+
files.push(...getAllTsFiles(fullPath));
|
|
409
|
+
} else if (stat.isFile() && entry.endsWith(".ts")) {
|
|
410
|
+
files.push(fullPath);
|
|
411
|
+
}
|
|
412
|
+
}
|
|
413
|
+
return files;
|
|
414
|
+
}
|
|
415
|
+
__name(getAllTsFiles, "getAllTsFiles");
|
|
416
|
+
function addTsNocheckToFile(filePath) {
|
|
417
|
+
const content = readFileSync(filePath, "utf-8");
|
|
418
|
+
const newContent = `// @ts-nocheck
|
|
419
|
+
${content}`;
|
|
420
|
+
writeFileSync3(filePath, newContent, "utf-8");
|
|
421
|
+
}
|
|
422
|
+
__name(addTsNocheckToFile, "addTsNocheckToFile");
|
|
423
|
+
|
|
424
|
+
// src/modules/devtool-v2/index.ts
|
|
425
|
+
var DevToolsV2Module = class {
|
|
390
426
|
static {
|
|
391
|
-
__name(this, "
|
|
427
|
+
__name(this, "DevToolsV2Module");
|
|
392
428
|
}
|
|
393
429
|
static async mount(app, opts = {}) {
|
|
394
430
|
const options = resolveOptsWithDefaultValue(opts);
|
|
@@ -412,9 +448,6 @@ var DevToolsModule = class {
|
|
|
412
448
|
mkdirSync2(clientSdkOutPath, {
|
|
413
449
|
recursive: true
|
|
414
450
|
});
|
|
415
|
-
generateClientConfig({
|
|
416
|
-
outputPath: clientSdkOutPath
|
|
417
|
-
});
|
|
418
451
|
const { createClient } = await import("@hey-api/openapi-ts");
|
|
419
452
|
await createClient({
|
|
420
453
|
input: openapiPath,
|
|
@@ -426,28 +459,29 @@ var DevToolsModule = class {
|
|
|
426
459
|
"@hey-api/sdk",
|
|
427
460
|
{
|
|
428
461
|
name: "@hey-api/client-axios",
|
|
429
|
-
runtimeConfigPath:
|
|
462
|
+
runtimeConfigPath: join2(clientSdkOutPath, "./client.config.ts")
|
|
430
463
|
}
|
|
431
464
|
]
|
|
432
465
|
});
|
|
466
|
+
addTsNocheckToGeneratedFiles(clientSdkOutPath);
|
|
433
467
|
ensureDirAndWrite(openapiPath, JSON.stringify(document, null, 2));
|
|
468
|
+
generateClientConfig({
|
|
469
|
+
outputPath: clientSdkOutPath
|
|
470
|
+
});
|
|
434
471
|
console.log("[OpenAPI] \u5BFC\u51FA openapi.json \u5E76\u751F\u6210 axios SDK \u2705");
|
|
435
472
|
}
|
|
436
473
|
}
|
|
437
474
|
};
|
|
438
475
|
|
|
439
476
|
// src/setup.ts
|
|
440
|
-
async function configureApp(app
|
|
477
|
+
async function configureApp(app) {
|
|
441
478
|
app.useLogger(app.get(AppLogger2));
|
|
442
479
|
app.flushLogs();
|
|
443
480
|
app.use(cookieParser());
|
|
444
481
|
const globalPrefix = process.env.CLIENT_BASE_PATH ?? "";
|
|
445
482
|
app.setGlobalPrefix(globalPrefix);
|
|
446
|
-
app.setBaseViewsDir(join2(process.cwd(), options?.viewsDir ?? "dist/client"));
|
|
447
|
-
app.setViewEngine("html");
|
|
448
|
-
app.engine("html", hbsExpressEngine);
|
|
449
483
|
if (process.env.NODE_ENV !== "production") {
|
|
450
|
-
await
|
|
484
|
+
await DevToolsV2Module.mount(app, {
|
|
451
485
|
basePath: process.env.CLIENT_BASE_PATH,
|
|
452
486
|
docsPath: "/api_docs"
|
|
453
487
|
});
|
|
@@ -455,6 +489,90 @@ async function configureApp(app, options) {
|
|
|
455
489
|
}
|
|
456
490
|
__name(configureApp, "configureApp");
|
|
457
491
|
|
|
492
|
+
// src/modules/devtool/index.ts
|
|
493
|
+
import { SwaggerModule as SwaggerModule2, DocumentBuilder as DocumentBuilder2 } from "@nestjs/swagger";
|
|
494
|
+
import { mkdirSync as mkdirSync4 } from "fs";
|
|
495
|
+
import { resolve as resolve3 } from "path";
|
|
496
|
+
|
|
497
|
+
// src/modules/devtool/helper.ts
|
|
498
|
+
import { dirname as dirname2 } from "path";
|
|
499
|
+
import { writeFileSync as writeFileSync4, mkdirSync as mkdirSync3 } from "fs";
|
|
500
|
+
function normalizeBasePath2(rawBasePath) {
|
|
501
|
+
const normalizedBasePath = rawBasePath.startsWith("/") ? rawBasePath : `/${rawBasePath}`;
|
|
502
|
+
return normalizedBasePath.endsWith("/") ? normalizedBasePath.slice(0, -1) : normalizedBasePath;
|
|
503
|
+
}
|
|
504
|
+
__name(normalizeBasePath2, "normalizeBasePath");
|
|
505
|
+
function resolveOptsWithDefaultValue2(options) {
|
|
506
|
+
const basePath = normalizeBasePath2(options.basePath || "/");
|
|
507
|
+
const docsPath = normalizeBasePath2(options.docsPath || `api/docs`);
|
|
508
|
+
return {
|
|
509
|
+
...options,
|
|
510
|
+
needSetupServer: options.needSetupServer ?? false,
|
|
511
|
+
basePath,
|
|
512
|
+
docsPath: `${basePath}${docsPath}`,
|
|
513
|
+
openapiOut: options.openapiOut || "./client/src/api/gen/openapi.json",
|
|
514
|
+
clientSdkOut: options.clientSdkOut || "./client/src/api/gen",
|
|
515
|
+
needGenerateClientSdk: options.needGenerateClientSdk ?? true,
|
|
516
|
+
swaggerOptions: {
|
|
517
|
+
title: options.swaggerOptions?.title ?? "NestJS Fullstack API",
|
|
518
|
+
version: options.swaggerOptions?.version ?? "1.0.0",
|
|
519
|
+
customSiteTitle: options.swaggerOptions?.customSiteTitle ?? "API Documentation",
|
|
520
|
+
customCss: options.swaggerOptions?.customCss ?? ".swagger-ui .topbar { display: none }"
|
|
521
|
+
}
|
|
522
|
+
};
|
|
523
|
+
}
|
|
524
|
+
__name(resolveOptsWithDefaultValue2, "resolveOptsWithDefaultValue");
|
|
525
|
+
function ensureDirAndWrite2(filePath, content) {
|
|
526
|
+
const dir = dirname2(filePath);
|
|
527
|
+
mkdirSync3(dir, {
|
|
528
|
+
recursive: true
|
|
529
|
+
});
|
|
530
|
+
writeFileSync4(filePath, content);
|
|
531
|
+
}
|
|
532
|
+
__name(ensureDirAndWrite2, "ensureDirAndWrite");
|
|
533
|
+
|
|
534
|
+
// src/modules/devtool/index.ts
|
|
535
|
+
var DevToolsModule = class {
|
|
536
|
+
static {
|
|
537
|
+
__name(this, "DevToolsModule");
|
|
538
|
+
}
|
|
539
|
+
static async mount(app, opts = {}) {
|
|
540
|
+
const options = resolveOptsWithDefaultValue2(opts);
|
|
541
|
+
const baseDirname = process.cwd();
|
|
542
|
+
const builder = new DocumentBuilder2().setTitle(options.swaggerOptions.title).setVersion(options.swaggerOptions.version);
|
|
543
|
+
const document = SwaggerModule2.createDocument(app, builder.build(), {
|
|
544
|
+
operationIdFactory: /* @__PURE__ */ __name((_c, m) => m, "operationIdFactory")
|
|
545
|
+
});
|
|
546
|
+
if (options.needSetupServer) {
|
|
547
|
+
SwaggerModule2.setup(options.docsPath, app, document, {
|
|
548
|
+
customSiteTitle: options.swaggerOptions.customSiteTitle,
|
|
549
|
+
customCss: options.swaggerOptions.customCss,
|
|
550
|
+
swaggerOptions: {
|
|
551
|
+
persistAuthorization: true
|
|
552
|
+
}
|
|
553
|
+
});
|
|
554
|
+
console.log(`[OpenAPI] Swagger UI \u5DF2\u6302\u8F7D\u81F3 ${options.docsPath}`);
|
|
555
|
+
}
|
|
556
|
+
const openapiPath = resolve3(baseDirname, options.openapiOut);
|
|
557
|
+
ensureDirAndWrite2(openapiPath, JSON.stringify(document, null, 2));
|
|
558
|
+
if (options.needGenerateClientSdk) {
|
|
559
|
+
const clientSdkOutPath = resolve3(baseDirname, options.clientSdkOut);
|
|
560
|
+
mkdirSync4(clientSdkOutPath, {
|
|
561
|
+
recursive: true
|
|
562
|
+
});
|
|
563
|
+
const { generate } = await import("openapi-typescript-codegen");
|
|
564
|
+
await generate({
|
|
565
|
+
input: openapiPath,
|
|
566
|
+
output: clientSdkOutPath,
|
|
567
|
+
httpClient: "axios",
|
|
568
|
+
useOptions: false,
|
|
569
|
+
exportServices: true
|
|
570
|
+
});
|
|
571
|
+
console.log("[OpenAPI] \u5BFC\u51FA openapi.json \u5E76\u751F\u6210 axios SDK \u2705");
|
|
572
|
+
}
|
|
573
|
+
}
|
|
574
|
+
};
|
|
575
|
+
|
|
458
576
|
// src/index.ts
|
|
459
577
|
export * from "@lark-apaas/nestjs-authnpaas";
|
|
460
578
|
export * from "@lark-apaas/nestjs-datapaas";
|
|
@@ -462,6 +580,7 @@ export {
|
|
|
462
580
|
CsrfMiddleware,
|
|
463
581
|
CsrfTokenMiddleware,
|
|
464
582
|
DevToolsModule,
|
|
583
|
+
DevToolsV2Module,
|
|
465
584
|
PlatformModule,
|
|
466
585
|
UserContextMiddleware,
|
|
467
586
|
ViewContextMiddleware,
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@lark-apaas/fullstack-nestjs-core",
|
|
3
|
-
"version": "1.0.3
|
|
3
|
+
"version": "1.0.3",
|
|
4
4
|
"description": "FullStack Nestjs Core",
|
|
5
5
|
"type": "module",
|
|
6
6
|
"main": "./dist/index.js",
|
|
@@ -19,9 +19,7 @@
|
|
|
19
19
|
"node": ">=18.0.0"
|
|
20
20
|
},
|
|
21
21
|
"files": [
|
|
22
|
-
"dist"
|
|
23
|
-
"templates",
|
|
24
|
-
"postinstall.config.mjs"
|
|
22
|
+
"dist"
|
|
25
23
|
],
|
|
26
24
|
"exports": {
|
|
27
25
|
".": {
|
|
@@ -40,12 +38,12 @@
|
|
|
40
38
|
"lint:fix": "eslint src --ext .ts --fix"
|
|
41
39
|
},
|
|
42
40
|
"dependencies": {
|
|
43
|
-
"@hey-api/openapi-ts": "^0.
|
|
41
|
+
"@hey-api/openapi-ts": "^0.87.0",
|
|
44
42
|
"@lark-apaas/nestjs-authnpaas": "^1.0.0",
|
|
45
43
|
"@lark-apaas/nestjs-datapaas": "^1.0.1",
|
|
46
44
|
"@lark-apaas/nestjs-logger": "^1.0.0",
|
|
47
45
|
"cookie-parser": "^1.4.7",
|
|
48
|
-
"
|
|
46
|
+
"openapi-typescript-codegen": "^0.29.0"
|
|
49
47
|
},
|
|
50
48
|
"devDependencies": {
|
|
51
49
|
"@nestjs/common": "^10.4.20",
|
|
@@ -53,7 +51,6 @@
|
|
|
53
51
|
"@nestjs/swagger": "^7.4.2",
|
|
54
52
|
"@types/cookie-parser": "^1.4.9",
|
|
55
53
|
"@types/express": "^5.0.3",
|
|
56
|
-
"@types/hbs": "^4.0.5",
|
|
57
54
|
"class-transformer": "^0.5.1",
|
|
58
55
|
"class-validator": "^0.14.2",
|
|
59
56
|
"drizzle-orm": "0.44.6",
|