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.
- package/package.json +1 -1
- package/templates/base/bun.lock +683 -0
- package/templates/base/package.json +2 -2
- package/templates/base/src/app.controller.ts +4 -2
- package/templates/base/src/common/middleware/index.ts +66 -0
- package/templates/base/src/index.ts +12 -13
- package/templates/base_scalar/bun.lock +1194 -0
- package/templates/base_scalar/package.json +3 -3
- package/templates/base_scalar/src/app.controller.ts +4 -2
- package/templates/base_scalar/src/common/middleware/index.ts +68 -0
- package/templates/base_scalar/src/index.ts +12 -13
- package/templates/cqrs/package.json +2 -2
- package/templates/cqrs/src/common/middleware/index.ts +73 -0
- package/templates/cqrs/src/index.ts +10 -13
- package/templates/cqrs_scalar/package.json +3 -3
- package/templates/cqrs_scalar/src/common/middleware/index.ts +59 -0
- package/templates/cqrs_scalar/src/index.ts +10 -13
- package/templates/base/src/common/filters/http-exception.filter.ts +0 -34
- package/templates/base/src/common/interceptors/response.interceptor.ts +0 -38
- package/templates/base_scalar/src/common/filters/http-exception.filter.ts +0 -34
- package/templates/base_scalar/src/common/interceptors/response.interceptor.ts +0 -38
- package/templates/cqrs/src/common/filters/http-exception.filter.ts +0 -34
- package/templates/cqrs/src/common/interceptors/response.interceptor.ts +0 -38
- package/templates/cqrs_scalar/src/common/filters/http-exception.filter.ts +0 -34
- 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.
|
33
|
+
"@hestjs/core": "^0.2.0",
|
34
34
|
"@hestjs/logger": "^0.1.5",
|
35
|
-
"@hestjs/scalar": "^0.1.
|
35
|
+
"@hestjs/scalar": "^0.1.6",
|
36
36
|
"@hestjs/validation": "^0.1.5",
|
37
|
-
"hono": "^4.8.
|
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,
|
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
|
-
|
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 {
|
4
|
+
import { Hono } from 'hono';
|
5
5
|
import { cors } from 'hono/cors';
|
6
6
|
import { AppModule } from './app.module';
|
7
|
-
import {
|
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
|
-
|
15
|
-
|
16
|
-
|
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:
|
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.
|
33
|
+
"@hestjs/core": "^0.2.0",
|
34
34
|
"@hestjs/validation": "^0.1.5",
|
35
|
-
"@hestjs/cqrs": "^0.1.
|
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 {
|
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 {
|
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
|
-
|
14
|
-
|
15
|
-
|
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:
|
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.
|
33
|
+
"@hestjs/core": "^0.2.0",
|
34
34
|
"@hestjs/validation": "^0.1.5",
|
35
|
-
"@hestjs/cqrs": "^0.1.
|
36
|
-
"@hestjs/scalar": "^0.1.
|
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 {
|
3
|
+
import { Hono } from 'hono';
|
4
4
|
import { cors } from 'hono/cors';
|
5
5
|
import { AppModule } from './app.module';
|
6
|
-
import {
|
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
|
-
|
14
|
-
|
15
|
-
|
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:
|
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
|
-
}
|