create-hest-app 0.1.5 → 0.1.7

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (25) hide show
  1. package/package.json +1 -1
  2. package/templates/base/bun.lock +683 -0
  3. package/templates/base/package.json +2 -2
  4. package/templates/base/src/app.controller.ts +4 -2
  5. package/templates/base/src/common/middleware/index.ts +66 -0
  6. package/templates/base/src/index.ts +12 -13
  7. package/templates/base_scalar/bun.lock +1194 -0
  8. package/templates/base_scalar/package.json +3 -3
  9. package/templates/base_scalar/src/app.controller.ts +4 -2
  10. package/templates/base_scalar/src/common/middleware/index.ts +68 -0
  11. package/templates/base_scalar/src/index.ts +12 -13
  12. package/templates/cqrs/package.json +2 -2
  13. package/templates/cqrs/src/common/middleware/index.ts +73 -0
  14. package/templates/cqrs/src/index.ts +10 -13
  15. package/templates/cqrs_scalar/package.json +3 -3
  16. package/templates/cqrs_scalar/src/common/middleware/index.ts +59 -0
  17. package/templates/cqrs_scalar/src/index.ts +10 -13
  18. package/templates/base/src/common/filters/http-exception.filter.ts +0 -34
  19. package/templates/base/src/common/interceptors/response.interceptor.ts +0 -38
  20. package/templates/base_scalar/src/common/filters/http-exception.filter.ts +0 -34
  21. package/templates/base_scalar/src/common/interceptors/response.interceptor.ts +0 -38
  22. package/templates/cqrs/src/common/filters/http-exception.filter.ts +0 -34
  23. package/templates/cqrs/src/common/interceptors/response.interceptor.ts +0 -38
  24. package/templates/cqrs_scalar/src/common/filters/http-exception.filter.ts +0 -34
  25. package/templates/cqrs_scalar/src/common/interceptors/response.interceptor.ts +0 -37
@@ -30,11 +30,11 @@
30
30
  "author": "aqz236",
31
31
  "license": "MIT",
32
32
  "dependencies": {
33
- "@hestjs/core": "^0.1.10",
33
+ "@hestjs/core": "^0.2.0",
34
34
  "@hestjs/logger": "^0.1.5",
35
- "@hestjs/scalar": "^0.1.4",
35
+ "@hestjs/scalar": "^0.1.6",
36
36
  "@hestjs/validation": "^0.1.5",
37
- "hono": "^4.8.9",
37
+ "hono": "^4.8.10",
38
38
  "reflect-metadata": "^0.2.2"
39
39
  },
40
40
  "devDependencies": {
@@ -1,4 +1,4 @@
1
- import { Controller, Get, NotFoundException, Param, Post } from '@hestjs/core';
1
+ import { Controller, Get, Param, Post } from '@hestjs/core';
2
2
  import {
3
3
  ApiBody,
4
4
  ApiOperation,
@@ -106,7 +106,9 @@ export class AppController {
106
106
  getUser(@Param('id') id: string) {
107
107
  const user = this.appService.getUser(id);
108
108
  if (!user) {
109
- throw new NotFoundException(`User with id ${id} not found`);
109
+ const error = new Error(`User with id ${id} not found`) as Error & { status: number };
110
+ error.status = 404;
111
+ throw error;
110
112
  }
111
113
  return user;
112
114
  }
@@ -0,0 +1,68 @@
1
+ import type { Context, Next } from 'hono';
2
+ import { logger } from '@hestjs/logger';
3
+
4
+ /**
5
+ * Hono 异常处理中间件
6
+ * 替代原来的全局异常过滤器
7
+ */
8
+ export const exceptionMiddleware = async (c: Context, next: Next) => {
9
+ try {
10
+ await next();
11
+ } catch (error: unknown) {
12
+ const err = error as any;
13
+ const status = err.status || 500;
14
+ const response = {
15
+ statusCode: status,
16
+ timestamp: new Date().toISOString(),
17
+ path: c.req.url,
18
+ message: err.message || 'Internal Server Error',
19
+ error: err.error || 'Exception',
20
+ };
21
+
22
+ logger.error(`🔥 HTTP Exception [${status}]: ${err.message}`, {
23
+ requestUrl: c.req.url,
24
+ stack: err.stack,
25
+ });
26
+
27
+ return c.json(response, status);
28
+ }
29
+ };
30
+
31
+ /**
32
+ * 响应包装中间件
33
+ * 替代原来的响应拦截器
34
+ */
35
+ export const responseMiddleware = async (c: Context, next: Next) => {
36
+ const start = Date.now();
37
+
38
+ await next();
39
+
40
+ // 跳过文档相关的路径
41
+ if (c.req.path === '/openapi.json' || c.req.path === '/docs' || c.req.path.startsWith('/api-docs')) {
42
+ return;
43
+ }
44
+
45
+ // 只包装JSON响应,且响应状态为2xx
46
+ const contentType = c.res.headers.get('content-type');
47
+ if (contentType?.includes('application/json') && c.res.status >= 200 && c.res.status < 300) {
48
+ try {
49
+ const duration = Date.now() - start;
50
+
51
+ // 克隆响应以避免消耗原始响应体
52
+ const responseClone = c.res.clone();
53
+ const originalResponse = await responseClone.json();
54
+
55
+ const wrappedResponse = {
56
+ success: true,
57
+ data: originalResponse,
58
+ timestamp: new Date().toISOString(),
59
+ duration: `${duration}ms`,
60
+ };
61
+
62
+ return c.json(wrappedResponse);
63
+ } catch (error) {
64
+ // 如果无法解析JSON,就保持原响应
65
+ console.warn('Failed to wrap response:', error);
66
+ }
67
+ }
68
+ };
@@ -1,26 +1,23 @@
1
1
  import { HestFactory } from '@hestjs/core';
2
2
  import { logger } from '@hestjs/logger';
3
3
  import '@hestjs/scalar'; // 导入scalar扩展
4
- import { ValidationInterceptor } from '@hestjs/validation';
4
+ import { Hono } from 'hono';
5
5
  import { cors } from 'hono/cors';
6
6
  import { AppModule } from './app.module';
7
- import { HttpExceptionFilter } from './common/filters/http-exception.filter';
8
- import { ResponseInterceptor } from './common/interceptors/response.interceptor';
7
+ import { exceptionMiddleware, responseMiddleware } from './common/middleware';
9
8
 
10
9
  async function bootstrap() {
11
10
  try {
12
11
  logger.info('🚀 Starting HestJS application...');
13
12
 
14
- const app = await HestFactory.create(AppModule);
15
- app.hono().use(cors()); // 使用 Hono 的 CORS 中间件
16
- // app.hono().use('*', log()); // 使用 Hono 的日志中间件
13
+ // 创建 Hono 实例
14
+ const hono = new Hono();
15
+ hono.use(cors()); // 使用 Hono 的 CORS 中间件
16
+ hono.use('*', exceptionMiddleware); // 使用异常处理中间件替代全局过滤器
17
+ hono.use('*', responseMiddleware); // 使用响应包装中间件替代拦截器
18
+ // hono.use('*', log()); // 使用 Hono 的日志中间件
17
19
 
18
- // 全局拦截器 - 验证拦截器应该在响应拦截器之前
19
- app.useGlobalInterceptors(new ValidationInterceptor());
20
- app.useGlobalInterceptors(new ResponseInterceptor());
21
-
22
- // 全局异常过滤器
23
- app.useGlobalFilters(new HttpExceptionFilter());
20
+ const app = await HestFactory.create(hono, AppModule);
24
21
 
25
22
  // 设置OpenAPI规范端点
26
23
  app.useSwagger(
@@ -53,9 +50,11 @@ async function bootstrap() {
53
50
 
54
51
  Bun.serve({
55
52
  port: 3002,
56
- fetch: app.hono().fetch,
53
+ fetch: hono.fetch,
57
54
  reusePort: true, // 启用端口复用
58
55
  });
56
+
57
+ logger.info(`🎉 Server is running on http://localhost:3002`);
59
58
  } catch (error) {
60
59
  logger.error('❌ Failed to start application:', error);
61
60
  process.exit(1);
@@ -30,9 +30,9 @@
30
30
  "author": "aqz236",
31
31
  "license": "MIT",
32
32
  "dependencies": {
33
- "@hestjs/core": "^0.1.10",
33
+ "@hestjs/core": "^0.2.0",
34
34
  "@hestjs/validation": "^0.1.5",
35
- "@hestjs/cqrs": "^0.1.3",
35
+ "@hestjs/cqrs": "^0.1.5",
36
36
  "hono": "^4.8.9",
37
37
  "reflect-metadata": "^0.2.2"
38
38
  },
@@ -0,0 +1,73 @@
1
+ import type { Context, Next } from 'hono';
2
+ import type { StatusCode } from 'hono/utils/http-status';
3
+ import { logger } from '@hestjs/logger';
4
+
5
+ /**
6
+ * Hono 异常处理中间件
7
+ */
8
+ export const exceptionMiddleware = async (c: Context, next: Next) => {
9
+ try {
10
+ await next();
11
+ } catch (error: unknown) {
12
+ const err = error as { status?: number; message?: string; error?: string; stack?: string };
13
+ const statusCode = err.status || 500;
14
+
15
+ const validStatus: StatusCode = (statusCode >= 100 && statusCode < 600)
16
+ ? statusCode as StatusCode
17
+ : 500;
18
+
19
+ const response = {
20
+ statusCode: validStatus,
21
+ timestamp: new Date().toISOString(),
22
+ path: c.req.url,
23
+ message: err.message || 'Internal Server Error',
24
+ error: err.error || 'Exception',
25
+ };
26
+
27
+ logger.error(`🔥 HTTP Exception [${validStatus}]: ${err.message}`, {
28
+ requestUrl: c.req.url,
29
+ stack: err.stack,
30
+ });
31
+
32
+ c.status(validStatus);
33
+ return c.json(response);
34
+ }
35
+ };
36
+
37
+ /**
38
+ * 响应包装中间件
39
+ */
40
+ export const responseMiddleware = async (c: Context, next: Next) => {
41
+ const start = Date.now();
42
+
43
+ await next();
44
+
45
+ // 跳过文档相关的路径
46
+ if (c.req.path === '/openapi.json' || c.req.path === '/docs' || c.req.path.startsWith('/api-docs')) {
47
+ return;
48
+ }
49
+
50
+ // 只包装JSON响应,且响应状态为2xx
51
+ const contentType = c.res.headers.get('content-type');
52
+ if (contentType?.includes('application/json') && c.res.status >= 200 && c.res.status < 300) {
53
+ try {
54
+ const duration = Date.now() - start;
55
+
56
+ // 克隆响应以避免消耗原始响应体
57
+ const responseClone = c.res.clone();
58
+ const originalResponse = await responseClone.json();
59
+
60
+ const wrappedResponse = {
61
+ success: true,
62
+ data: originalResponse,
63
+ timestamp: new Date().toISOString(),
64
+ duration: `${duration}ms`,
65
+ };
66
+
67
+ return c.json(wrappedResponse);
68
+ } catch (error) {
69
+ // 如果无法解析JSON,就保持原响应
70
+ console.warn('Failed to wrap response:', error);
71
+ }
72
+ }
73
+ };
@@ -1,29 +1,26 @@
1
1
  import { HestFactory, logger } from '@hestjs/core';
2
- import { ValidationInterceptor } from '@hestjs/validation';
2
+ import { Hono } from 'hono';
3
3
  import { cors } from 'hono/cors';
4
4
  import { logger as log } from 'hono/logger';
5
5
  import { AppModule } from './app.module';
6
- import { HttpExceptionFilter } from './common/filters/http-exception.filter';
7
- import { ResponseInterceptor } from './common/interceptors/response.interceptor';
6
+ import { exceptionMiddleware, responseMiddleware } from './common/middleware';
8
7
 
9
8
  async function bootstrap() {
10
9
  try {
11
10
  logger.info('🚀 Starting HestJS application...');
12
11
 
13
- const app = await HestFactory.create(AppModule);
14
- app.hono().use(cors()); // 使用 Hono 的 CORS 中间件
15
- app.hono().use('*', log()); // 使用 Hono 的日志中间件
12
+ // 创建 Hono 实例
13
+ const hono = new Hono();
14
+ hono.use(cors()); // 使用 Hono 的 CORS 中间件
15
+ hono.use('*', log()); // 使用 Hono 的日志中间件
16
+ hono.use('*', exceptionMiddleware); // 异常处理中间件
17
+ hono.use('*', responseMiddleware); // 响应包装中间件
16
18
 
17
- // 全局拦截器 - 验证拦截器应该在响应拦截器之前
18
- app.useGlobalInterceptors(new ValidationInterceptor());
19
- app.useGlobalInterceptors(new ResponseInterceptor());
20
-
21
- // 全局异常过滤器
22
- app.useGlobalFilters(new HttpExceptionFilter());
19
+ await HestFactory.create(hono, AppModule);
23
20
 
24
21
  const server = Bun.serve({
25
22
  port: 3002,
26
- fetch: app.hono().fetch,
23
+ fetch: hono.fetch,
27
24
  reusePort: true, // 启用端口复用
28
25
  });
29
26
 
@@ -30,10 +30,10 @@
30
30
  "author": "aqz236",
31
31
  "license": "MIT",
32
32
  "dependencies": {
33
- "@hestjs/core": "^0.1.10",
33
+ "@hestjs/core": "^0.2.0",
34
34
  "@hestjs/validation": "^0.1.5",
35
- "@hestjs/cqrs": "^0.1.3",
36
- "@hestjs/scalar": "^0.1.4",
35
+ "@hestjs/cqrs": "^0.1.5",
36
+ "@hestjs/scalar": "^0.1.6",
37
37
  "hono": "^4.8.9",
38
38
  "reflect-metadata": "^0.2.2"
39
39
  },
@@ -0,0 +1,59 @@
1
+ import type { Context, Next } from 'hono';
2
+ import type { StatusCode } from 'hono/utils/http-status';
3
+ import { logger } from '@hestjs/logger';
4
+
5
+ /**
6
+ * Hono 异常处理中间件
7
+ */
8
+ export const exceptionMiddleware = async (c: Context, next: Next) => {
9
+ try {
10
+ await next();
11
+ } catch (error: unknown) {
12
+ const err = error as { status?: number; message?: string; error?: string; stack?: string };
13
+ const statusCode = err.status || 500;
14
+
15
+ const validStatus: StatusCode = (statusCode >= 100 && statusCode < 600)
16
+ ? statusCode as StatusCode
17
+ : 500;
18
+
19
+ const response = {
20
+ statusCode: validStatus,
21
+ timestamp: new Date().toISOString(),
22
+ path: c.req.url,
23
+ message: err.message || 'Internal Server Error',
24
+ error: err.error || 'Exception',
25
+ };
26
+
27
+ logger.error(`🔥 HTTP Exception [${validStatus}]: ${err.message}`, {
28
+ requestUrl: c.req.url,
29
+ stack: err.stack,
30
+ });
31
+
32
+ c.status(validStatus);
33
+ return c.json(response);
34
+ }
35
+ };
36
+
37
+ /**
38
+ * 响应包装中间件
39
+ */
40
+ export const responseMiddleware = async (c: Context, next: Next) => {
41
+ const start = Date.now();
42
+
43
+ await next();
44
+
45
+ // 只包装JSON响应
46
+ if (c.res.headers.get('content-type')?.includes('application/json')) {
47
+ const duration = Date.now() - start;
48
+ const originalResponse = await c.res.json();
49
+
50
+ const wrappedResponse = {
51
+ success: true,
52
+ data: originalResponse,
53
+ timestamp: new Date().toISOString(),
54
+ duration: `${duration}ms`,
55
+ };
56
+
57
+ return c.json(wrappedResponse);
58
+ }
59
+ };
@@ -1,25 +1,22 @@
1
1
  import { HestFactory, logger } from '@hestjs/core';
2
2
  import '@hestjs/scalar'; // 导入scalar扩展
3
- import { ValidationInterceptor } from '@hestjs/validation';
3
+ import { Hono } from 'hono';
4
4
  import { cors } from 'hono/cors';
5
5
  import { AppModule } from './app.module';
6
- import { HttpExceptionFilter } from './common/filters/http-exception.filter';
7
- import { ResponseInterceptor } from './common/interceptors/response.interceptor';
6
+ import { exceptionMiddleware, responseMiddleware } from './common/middleware';
8
7
 
9
8
  async function bootstrap() {
10
9
  try {
11
10
  logger.info('🚀 Starting HestJS application...');
12
11
 
13
- const app = await HestFactory.create(AppModule);
14
- app.hono().use(cors()); // 使用 Hono 的 CORS 中间件
15
- // app.hono().use('*', log()); // 使用 Hono 的日志中间件
12
+ // 创建 Hono 实例
13
+ const hono = new Hono();
14
+ hono.use(cors()); // 使用 Hono 的 CORS 中间件
15
+ hono.use('*', exceptionMiddleware); // 异常处理中间件
16
+ hono.use('*', responseMiddleware); // 响应包装中间件
17
+ // hono.use('*', log()); // 使用 Hono 的日志中间件
16
18
 
17
- // 全局拦截器 - 验证拦截器应该在响应拦截器之前
18
- app.useGlobalInterceptors(new ValidationInterceptor());
19
- app.useGlobalInterceptors(new ResponseInterceptor());
20
-
21
- // 全局异常过滤器
22
- app.useGlobalFilters(new HttpExceptionFilter());
19
+ const app = await HestFactory.create(hono, AppModule);
23
20
 
24
21
  // 设置OpenAPI规范端点
25
22
  app.useSwagger(
@@ -52,7 +49,7 @@ async function bootstrap() {
52
49
 
53
50
  const server = Bun.serve({
54
51
  port: 3002,
55
- fetch: app.hono().fetch,
52
+ fetch: hono.fetch,
56
53
  reusePort: true, // 启用端口复用
57
54
  });
58
55
 
@@ -1,34 +0,0 @@
1
- import type {
2
- ArgumentsHost,
3
- ExceptionFilter,
4
- HttpException,
5
- } from '@hestjs/core';
6
- import { createLogger } from '@hestjs/core';
7
-
8
- const logger = createLogger('HttpExceptionFilter');
9
-
10
- /**
11
- * 自定义 HTTP 异常过滤器
12
- */
13
- export class HttpExceptionFilter implements ExceptionFilter<HttpException> {
14
- catch(exception: HttpException, host: ArgumentsHost) {
15
- const ctx = host.getContext();
16
- const request = host.getRequest();
17
-
18
- const status = exception.status;
19
- const response = {
20
- statusCode: status,
21
- timestamp: new Date().toISOString(),
22
- path: request.url,
23
- message: exception.message,
24
- error: exception.error || 'Http Exception',
25
- };
26
-
27
- logger.error(`🔥 HTTP Exception [${status}]: ${exception.message}`, {
28
- requestUrl: request.url,
29
- stack: exception.stack,
30
- });
31
-
32
- return ctx.json(response, status);
33
- }
34
- }
@@ -1,38 +0,0 @@
1
- import type { CallHandler, ExecutionContext, Interceptor } from '@hestjs/core';
2
- import { createLogger } from '@hestjs/core';
3
-
4
- const logger = createLogger('ResponseInterceptor');
5
-
6
- /**
7
- * 响应转换拦截器
8
- */
9
- export class ResponseInterceptor implements Interceptor {
10
- async intercept(
11
- context: ExecutionContext,
12
- next: CallHandler,
13
- ): Promise<{
14
- success: boolean;
15
- data: unknown;
16
- timestamp: string;
17
- duration: string;
18
- }> {
19
- const startTime = Date.now();
20
- const request = context.switchToHttp().getRequest();
21
-
22
- logger.info(`🚀 Request: ${request.method} ${request.url}`);
23
-
24
- const result = await next.handle();
25
-
26
- const duration = Date.now() - startTime;
27
- logger.info(
28
- `✅ Response: ${request.method} ${request.url} - ${duration}ms`,
29
- );
30
-
31
- return {
32
- success: true,
33
- data: result,
34
- timestamp: new Date().toISOString(),
35
- duration: `${duration}ms`,
36
- };
37
- }
38
- }
@@ -1,34 +0,0 @@
1
- import type {
2
- ArgumentsHost,
3
- ExceptionFilter,
4
- HttpException,
5
- } from '@hestjs/core';
6
- import { createLogger } from '@hestjs/core';
7
-
8
- const logger = createLogger('HttpExceptionFilter');
9
-
10
- /**
11
- * 自定义 HTTP 异常过滤器
12
- */
13
- export class HttpExceptionFilter implements ExceptionFilter<HttpException> {
14
- catch(exception: HttpException, host: ArgumentsHost) {
15
- const ctx = host.getContext();
16
- const request = host.getRequest();
17
-
18
- const status = exception.status;
19
- const response = {
20
- statusCode: status,
21
- timestamp: new Date().toISOString(),
22
- path: request.url,
23
- message: exception.message,
24
- error: exception.error || 'Http Exception',
25
- };
26
-
27
- logger.error(`🔥 HTTP Exception [${status}]: ${exception.message}`, {
28
- requestUrl: request.url,
29
- stack: exception.stack,
30
- });
31
-
32
- return ctx.json(response, status);
33
- }
34
- }
@@ -1,38 +0,0 @@
1
- import type { CallHandler, ExecutionContext, Interceptor } from '@hestjs/core';
2
- import { createLogger } from '@hestjs/core';
3
-
4
- const logger = createLogger('ResponseInterceptor');
5
-
6
- /**
7
- * 响应转换拦截器
8
- */
9
- export class ResponseInterceptor implements Interceptor {
10
- async intercept(
11
- context: ExecutionContext,
12
- next: CallHandler,
13
- ): Promise<{
14
- success: boolean;
15
- data: unknown;
16
- timestamp: string;
17
- duration: string;
18
- }> {
19
- const startTime = Date.now();
20
- const request = context.switchToHttp().getRequest();
21
-
22
- logger.info(`🚀 Request: ${request.method} ${request.url}`);
23
-
24
- const result = await next.handle();
25
-
26
- const duration = Date.now() - startTime;
27
- logger.info(
28
- `✅ Response: ${request.method} ${request.url} - ${duration}ms`,
29
- );
30
-
31
- return {
32
- success: true,
33
- data: result,
34
- timestamp: new Date().toISOString(),
35
- duration: `${duration}ms`,
36
- };
37
- }
38
- }
@@ -1,34 +0,0 @@
1
- import type {
2
- ArgumentsHost,
3
- ExceptionFilter,
4
- HttpException,
5
- } from '@hestjs/core';
6
- import { createLogger } from '@hestjs/core';
7
-
8
- const logger = createLogger('HttpExceptionFilter');
9
-
10
- /**
11
- * 自定义 HTTP 异常过滤器
12
- */
13
- export class HttpExceptionFilter implements ExceptionFilter<HttpException> {
14
- catch(exception: HttpException, host: ArgumentsHost) {
15
- const ctx = host.getContext();
16
- const request = host.getRequest();
17
-
18
- const status = exception.status;
19
- const response = {
20
- statusCode: status,
21
- timestamp: new Date().toISOString(),
22
- path: request.url,
23
- message: exception.message,
24
- error: exception.error || 'Http Exception',
25
- };
26
-
27
- logger.error(`🔥 HTTP Exception [${status}]: ${exception.message}`, {
28
- requestUrl: request.url,
29
- stack: exception.stack,
30
- });
31
-
32
- return ctx.json(response, status);
33
- }
34
- }
@@ -1,38 +0,0 @@
1
- import type { CallHandler, ExecutionContext, Interceptor } from '@hestjs/core';
2
- import { createLogger } from '@hestjs/core';
3
-
4
- const logger = createLogger('ResponseInterceptor');
5
-
6
- /**
7
- * 响应转换拦截器
8
- */
9
- export class ResponseInterceptor implements Interceptor {
10
- async intercept(
11
- context: ExecutionContext,
12
- next: CallHandler,
13
- ): Promise<{
14
- success: boolean;
15
- data: unknown;
16
- timestamp: string;
17
- duration: string;
18
- }> {
19
- const startTime = Date.now();
20
- const request = context.switchToHttp().getRequest();
21
-
22
- logger.info(`🚀 Request: ${request.method} ${request.url}`);
23
-
24
- const result = await next.handle();
25
-
26
- const duration = Date.now() - startTime;
27
- logger.info(
28
- `✅ Response: ${request.method} ${request.url} - ${duration}ms`,
29
- );
30
-
31
- return {
32
- success: true,
33
- data: result,
34
- timestamp: new Date().toISOString(),
35
- duration: `${duration}ms`,
36
- };
37
- }
38
- }
@@ -1,34 +0,0 @@
1
- import type {
2
- ArgumentsHost,
3
- ExceptionFilter,
4
- HttpException,
5
- } from '@hestjs/core';
6
- import { createLogger } from '@hestjs/core';
7
-
8
- const logger = createLogger('HttpExceptionFilter');
9
-
10
- /**
11
- * 自定义 HTTP 异常过滤器
12
- */
13
- export class HttpExceptionFilter implements ExceptionFilter<HttpException> {
14
- catch(exception: HttpException, host: ArgumentsHost) {
15
- const ctx = host.getContext();
16
- const request = host.getRequest();
17
-
18
- const status = exception.status;
19
- const response = {
20
- statusCode: status,
21
- timestamp: new Date().toISOString(),
22
- path: request.url,
23
- message: exception.message,
24
- error: exception.error || 'Http Exception',
25
- };
26
-
27
- logger.error(`🔥 HTTP Exception [${status}]: ${exception.message}`, {
28
- requestUrl: request.url,
29
- stack: exception.stack,
30
- });
31
-
32
- return ctx.json(response, status);
33
- }
34
- }