@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 CHANGED
@@ -1,12 +1,15 @@
1
1
  # @lark-apaas/fullstack-nestjs-core
2
2
 
3
- FullStack NestJS Core 是一个为 NestJS 应用提供核心功能的工具包,包括 CSRF 保护、用户上下文管理和开发工具等。
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
- - **开发工具**: 自动生成 Swagger 文档、OpenAPI JSON 和客户端 SDK
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. CSRF 保护
33
+ ### 方案 1: 使用 PlatformModule(推荐)
31
34
 
32
- CSRF 保护由两个中间件组成:`CsrfTokenMiddleware` 用于生成 token,`CsrfMiddleware` 用于验证 token。
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 { Module, NestModule, MiddlewareConsumer } from '@nestjs/common';
38
- import { CsrfTokenMiddleware, CsrfMiddleware } from '@lark-apaas/fullstack-nestjs-core';
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
- @Module({})
41
- export class AppModule implements NestModule {
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
- consumer
49
- .apply(CsrfMiddleware)
50
- .forRoutes('*'); // 所有路由都验证 CSRF token
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
- 如需自定义配置,可在应用中间件前调用 `configure` 方法:
92
+ 如果需要更细粒度的控制,可以单独使用各个模块:
58
93
 
59
94
  ```typescript
60
95
  import { Module, NestModule, MiddlewareConsumer } from '@nestjs/common';
61
- import { CsrfTokenMiddleware, CsrfMiddleware } from '@lark-apaas/fullstack-nestjs-core';
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
- // 自定义配置 CSRF Token 生成中间件
67
- CsrfTokenMiddleware.configure({
68
- cookieKey: 'CSRF-TOKEN', // 默认: 'suda-csrf-token'
69
- cookieMaxAge: 86400000, // 默认: 2592000000 (30天)
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(CsrfMiddleware)
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
- **注意**: 如果使用自定义的 `cookieKey`,需要确保 `CsrfTokenMiddleware` 和 `CsrfMiddleware` 配置的 `cookieKey` 一致。
131
+ ## 核心模块
92
132
 
93
- ### 2. 用户上下文
133
+ ### PlatformModule
94
134
 
95
- 自动从请求头中提取用户信息并注入到请求对象中。
135
+ 全局平台模块,集成了所有核心功能。
96
136
 
97
- ```typescript
98
- import { Module, NestModule, MiddlewareConsumer } from '@nestjs/common';
99
- import { UserContextMiddleware } from '@lark-apaas/fullstack-nestjs-core';
137
+ #### 配置选项
100
138
 
101
- @Module({})
102
- export class AppModule implements NestModule {
103
- configure(consumer: MiddlewareConsumer) {
104
- consumer
105
- .apply(UserContextMiddleware)
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
- import { Controller, Get, Req } from '@nestjs/common';
115
- import { Request } from 'express';
116
-
117
- @Controller()
118
- export class AppController {
119
- @Get()
120
- getUser(@Req() req: Request) {
121
- // 访问用户上下文
122
- const { userId, tenantId, appId } = req.userContext;
123
- return { userId, tenantId, appId };
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
- ### 3. 开发工具模块
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
- 在开发环境中自动生成 Swagger 文档和客户端 SDK。
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 { DevToolsModule } from '@lark-apaas/fullstack-nestjs-core';
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
- await DevToolsModule.mount(app, {
142
- docsPath: 'api-docs',
143
- openapiOut: './openapi.json',
144
- needSetupServer: true,
145
- needGenerateClientSdk: true,
146
- clientSdkOut: './src/sdk',
147
- swaggerOptions: {
148
- title: 'My API',
149
- version: '1.0.0',
150
- customSiteTitle: 'My API Documentation',
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
- ## API 文档
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
- #### 配置选项 (CsrfTokenOptions)
246
+ #### 配置选项
166
247
 
167
248
  | 选项 | 类型 | 默认值 | 描述 |
168
249
  |------|------|--------|------|
169
- | cookieKey | string | `'suda-csrf-token'` | Cookie 中存储 CSRF Token 的键名 |
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
- - `configure(opts: CsrfTokenOptions)`: 配置中间件选项
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
- #### 配置选项 (CsrfOptions)
272
+ #### 配置选项
182
273
 
183
274
  | 选项 | 类型 | 默认值 | 描述 |
184
275
  |------|------|--------|------|
185
- | headerKey | string | `'x-suda-csrf-token'` | 请求头中 CSRF Token 的键名 |
186
- | cookieKey | string | `'suda-csrf-token'` | Cookie 中 CSRF Token 的键名 |
276
+ | headerKey | string | `'x-suda-csrf-token'` | 请求头中 Token 的键名 |
277
+ | cookieKey | string | `'suda-csrf-token'` | Cookie 中 Token 的键名 |
187
278
 
188
- #### 静态方法
279
+ #### 使用示例
189
280
 
190
- - `configure(opts: CsrfOptions)`: 配置中间件选项
191
-
192
- #### 验证规则
193
-
194
- 1. 检查 Cookie 中是否存在 CSRF Token
195
- 2. 检查请求头中是否存在 CSRF Token
196
- 3. 验证两者是否匹配
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
- 如果任何一步验证失败,将返回 403 Forbidden。
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
- ### DevToolsModule
321
+ ### ViewContextMiddleware
215
322
 
216
- 开发工具模块,用于生成 API 文档和客户端 SDK。
323
+ 将用户上下文和 CSRF Token 注入到 `res.locals`,用于模板渲染。
217
324
 
218
- #### 配置选项 (DevToolsOptions)
325
+ #### 注入的变量
219
326
 
220
- | 选项 | 类型 | 默认值 | 描述 |
221
- |------|------|--------|------|
222
- | basePath | string | - | 基础路径 |
223
- | docsPath | string | - | Swagger UI 的挂载路径 |
224
- | openapiOut | string | - | OpenAPI JSON 文件的输出路径 |
225
- | needSetupServer | boolean | - | 是否需要挂载 Swagger UI |
226
- | needGenerateClientSdk | boolean | - | 是否生成客户端 SDK |
227
- | clientSdkOut | string | - | 客户端 SDK 的输出目录 |
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
- - `mount(app: INestApplication, opts?: DevToolsOptions)`: 挂载开发工具到应用
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 import_node_fs3 = require("fs");
374
- var import_node_path3 = require("path");
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, import_node_path3.resolve)(baseDirname, options.openapiOut);
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, import_node_path3.resolve)(baseDirname, options.clientSdkOut);
454
- (0, import_node_fs3.mkdirSync)(clientSdkOutPath, {
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, import_node_path3.join)(clientSdkOutPath, "./client.config.ts")
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, options) {
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 import_node_fs5 = require("fs");
503
- var import_node_path5 = require("path");
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 import_node_path4 = require("path");
507
- var import_node_fs4 = require("fs");
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, import_node_path4.dirname)(filePath);
535
- (0, import_node_fs4.mkdirSync)(dir, {
569
+ const dir = (0, import_node_path5.dirname)(filePath);
570
+ (0, import_node_fs5.mkdirSync)(dir, {
536
571
  recursive: true
537
572
  });
538
- (0, import_node_fs4.writeFileSync)(filePath, content);
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, import_node_path5.resolve)(baseDirname, options.openapiOut);
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, import_node_path5.resolve)(baseDirname, options.clientSdkOut);
568
- (0, import_node_fs5.mkdirSync)(clientSdkOutPath, {
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, options?: {
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, options?: {
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: join(clientSdkOutPath, "./client.config.ts")
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, options) {
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 writeFileSync3, mkdirSync as mkdirSync3 } from "fs";
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
- writeFileSync3(filePath, content);
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.12",
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",