@dangao/bun-server 1.0.1 → 1.1.2

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 (262) hide show
  1. package/dist/controller/controller.d.ts +1 -1
  2. package/dist/controller/controller.d.ts.map +1 -1
  3. package/dist/core/application.d.ts.map +1 -1
  4. package/dist/database/database-extension.d.ts.map +1 -1
  5. package/dist/database/database-module.d.ts.map +1 -1
  6. package/dist/database/orm/transaction-decorator.d.ts +1 -0
  7. package/dist/database/orm/transaction-decorator.d.ts.map +1 -1
  8. package/dist/database/orm/transaction-interceptor.d.ts +12 -3
  9. package/dist/database/orm/transaction-interceptor.d.ts.map +1 -1
  10. package/dist/index.d.ts +1 -0
  11. package/dist/index.d.ts.map +1 -1
  12. package/dist/index.js +678 -310
  13. package/dist/interceptor/base-interceptor.d.ts +94 -0
  14. package/dist/interceptor/base-interceptor.d.ts.map +1 -0
  15. package/dist/interceptor/builtin/cache-interceptor.d.ts +69 -0
  16. package/dist/interceptor/builtin/cache-interceptor.d.ts.map +1 -0
  17. package/dist/interceptor/builtin/index.d.ts +4 -0
  18. package/dist/interceptor/builtin/index.d.ts.map +1 -0
  19. package/dist/interceptor/builtin/log-interceptor.d.ts +56 -0
  20. package/dist/interceptor/builtin/log-interceptor.d.ts.map +1 -0
  21. package/dist/interceptor/builtin/permission-interceptor.d.ts +70 -0
  22. package/dist/interceptor/builtin/permission-interceptor.d.ts.map +1 -0
  23. package/dist/interceptor/index.d.ts +7 -0
  24. package/dist/interceptor/index.d.ts.map +1 -0
  25. package/dist/interceptor/interceptor-chain.d.ts +22 -0
  26. package/dist/interceptor/interceptor-chain.d.ts.map +1 -0
  27. package/dist/interceptor/interceptor-registry.d.ts +59 -0
  28. package/dist/interceptor/interceptor-registry.d.ts.map +1 -0
  29. package/dist/interceptor/metadata.d.ts +12 -0
  30. package/dist/interceptor/metadata.d.ts.map +1 -0
  31. package/dist/interceptor/types.d.ts +42 -0
  32. package/dist/interceptor/types.d.ts.map +1 -0
  33. package/dist/middleware/decorators.d.ts +2 -1
  34. package/dist/middleware/decorators.d.ts.map +1 -1
  35. package/dist/router/decorators.d.ts.map +1 -1
  36. package/dist/router/registry.d.ts +2 -1
  37. package/dist/router/registry.d.ts.map +1 -1
  38. package/dist/router/route.d.ts +3 -2
  39. package/dist/router/route.d.ts.map +1 -1
  40. package/dist/router/router.d.ts +2 -1
  41. package/dist/router/router.d.ts.map +1 -1
  42. package/dist/websocket/decorators.d.ts +2 -1
  43. package/dist/websocket/decorators.d.ts.map +1 -1
  44. package/package.json +5 -3
  45. package/readme.md +163 -2
  46. package/src/auth/controller.ts +148 -0
  47. package/src/auth/decorators.ts +81 -0
  48. package/src/auth/index.ts +12 -0
  49. package/src/auth/jwt.ts +169 -0
  50. package/src/auth/oauth2.ts +244 -0
  51. package/src/auth/types.ts +248 -0
  52. package/src/cache/cache-module.ts +67 -0
  53. package/src/cache/decorators.ts +202 -0
  54. package/src/cache/index.ts +27 -0
  55. package/src/cache/service.ts +151 -0
  56. package/src/cache/types.ts +420 -0
  57. package/src/config/config-module.ts +76 -0
  58. package/src/config/index.ts +8 -0
  59. package/src/config/service.ts +93 -0
  60. package/src/config/types.ts +27 -0
  61. package/src/controller/controller.ts +278 -0
  62. package/src/controller/decorators.ts +84 -0
  63. package/src/controller/index.ts +7 -0
  64. package/src/controller/metadata.ts +27 -0
  65. package/src/controller/param-binder.ts +157 -0
  66. package/src/core/application.ts +239 -0
  67. package/src/core/context.ts +228 -0
  68. package/src/core/index.ts +4 -0
  69. package/src/core/server.ts +128 -0
  70. package/src/core/types.ts +2 -0
  71. package/src/database/connection-manager.ts +239 -0
  72. package/src/database/connection-pool.ts +322 -0
  73. package/src/database/database-extension.ts +83 -0
  74. package/src/database/database-module.ts +121 -0
  75. package/src/database/health-indicator.ts +51 -0
  76. package/src/database/index.ts +47 -0
  77. package/src/database/orm/decorators.ts +155 -0
  78. package/src/database/orm/drizzle-repository.ts +39 -0
  79. package/src/database/orm/index.ts +23 -0
  80. package/src/database/orm/repository-decorator.ts +39 -0
  81. package/src/database/orm/repository.ts +103 -0
  82. package/src/database/orm/service.ts +49 -0
  83. package/src/database/orm/transaction-decorator.ts +76 -0
  84. package/src/database/orm/transaction-interceptor.ts +263 -0
  85. package/src/database/orm/transaction-manager.ts +276 -0
  86. package/src/database/orm/transaction-types.ts +140 -0
  87. package/src/database/orm/types.ts +99 -0
  88. package/src/database/service.ts +221 -0
  89. package/src/database/types.ts +171 -0
  90. package/src/di/container.ts +398 -0
  91. package/src/di/decorators.ts +228 -0
  92. package/src/di/index.ts +4 -0
  93. package/src/di/module-registry.ts +188 -0
  94. package/src/di/module.ts +65 -0
  95. package/src/di/types.ts +67 -0
  96. package/src/error/error-codes.ts +222 -0
  97. package/src/error/filter.ts +43 -0
  98. package/src/error/handler.ts +66 -0
  99. package/src/error/http-exception.ts +115 -0
  100. package/src/error/i18n.ts +217 -0
  101. package/src/error/index.ts +16 -0
  102. package/src/extensions/index.ts +5 -0
  103. package/src/extensions/logger-extension.ts +31 -0
  104. package/src/extensions/logger-module.ts +69 -0
  105. package/src/extensions/types.ts +14 -0
  106. package/src/files/index.ts +5 -0
  107. package/src/files/static-middleware.ts +53 -0
  108. package/src/files/storage.ts +67 -0
  109. package/src/files/types.ts +33 -0
  110. package/src/files/upload-middleware.ts +45 -0
  111. package/src/health/controller.ts +76 -0
  112. package/src/health/health-module.ts +51 -0
  113. package/src/health/index.ts +12 -0
  114. package/src/health/types.ts +28 -0
  115. package/src/index.ts +292 -0
  116. package/src/interceptor/base-interceptor.ts +203 -0
  117. package/src/interceptor/builtin/cache-interceptor.ts +169 -0
  118. package/src/interceptor/builtin/index.ts +28 -0
  119. package/src/interceptor/builtin/log-interceptor.ts +178 -0
  120. package/src/interceptor/builtin/permission-interceptor.ts +173 -0
  121. package/src/interceptor/index.ts +26 -0
  122. package/src/interceptor/interceptor-chain.ts +79 -0
  123. package/src/interceptor/interceptor-registry.ts +132 -0
  124. package/src/interceptor/metadata.ts +40 -0
  125. package/src/interceptor/types.ts +52 -0
  126. package/src/metrics/collector.ts +209 -0
  127. package/src/metrics/controller.ts +40 -0
  128. package/src/metrics/index.ts +15 -0
  129. package/src/metrics/metrics-module.ts +58 -0
  130. package/src/metrics/middleware.ts +46 -0
  131. package/src/metrics/prometheus.ts +79 -0
  132. package/src/metrics/types.ts +103 -0
  133. package/src/middleware/builtin/cors.ts +60 -0
  134. package/src/middleware/builtin/error-handler.ts +90 -0
  135. package/src/middleware/builtin/file-upload.ts +42 -0
  136. package/src/middleware/builtin/index.ts +14 -0
  137. package/src/middleware/builtin/logger.ts +91 -0
  138. package/src/middleware/builtin/rate-limit.ts +252 -0
  139. package/src/middleware/builtin/static-file.ts +88 -0
  140. package/src/middleware/decorators.ts +92 -0
  141. package/src/middleware/index.ts +11 -0
  142. package/src/middleware/middleware.ts +13 -0
  143. package/src/middleware/pipeline.ts +93 -0
  144. package/src/queue/decorators.ts +110 -0
  145. package/src/queue/index.ts +26 -0
  146. package/src/queue/queue-module.ts +64 -0
  147. package/src/queue/service.ts +302 -0
  148. package/src/queue/types.ts +341 -0
  149. package/src/request/body-parser.ts +133 -0
  150. package/src/request/file-handler.ts +46 -0
  151. package/src/request/index.ts +5 -0
  152. package/src/request/request.ts +107 -0
  153. package/src/request/response.ts +150 -0
  154. package/src/router/decorators.ts +123 -0
  155. package/src/router/index.ts +6 -0
  156. package/src/router/registry.ts +99 -0
  157. package/src/router/route.ts +141 -0
  158. package/src/router/router.ts +242 -0
  159. package/src/router/types.ts +27 -0
  160. package/src/security/access-decision-manager.ts +34 -0
  161. package/src/security/authentication-manager.ts +47 -0
  162. package/src/security/context.ts +92 -0
  163. package/src/security/filter.ts +162 -0
  164. package/src/security/index.ts +8 -0
  165. package/src/security/providers/index.ts +3 -0
  166. package/src/security/providers/jwt-provider.ts +60 -0
  167. package/src/security/providers/oauth2-provider.ts +70 -0
  168. package/src/security/security-module.ts +145 -0
  169. package/src/security/types.ts +165 -0
  170. package/src/session/decorators.ts +45 -0
  171. package/src/session/index.ts +19 -0
  172. package/src/session/middleware.ts +143 -0
  173. package/src/session/service.ts +218 -0
  174. package/src/session/session-module.ts +69 -0
  175. package/src/session/types.ts +373 -0
  176. package/src/swagger/decorators.ts +133 -0
  177. package/src/swagger/generator.ts +234 -0
  178. package/src/swagger/index.ts +7 -0
  179. package/src/swagger/swagger-extension.ts +41 -0
  180. package/src/swagger/swagger-module.ts +83 -0
  181. package/src/swagger/types.ts +188 -0
  182. package/src/swagger/ui.ts +98 -0
  183. package/src/testing/harness.ts +96 -0
  184. package/src/validation/decorators.ts +95 -0
  185. package/src/validation/errors.ts +28 -0
  186. package/src/validation/index.ts +14 -0
  187. package/src/validation/types.ts +35 -0
  188. package/src/validation/validator.ts +63 -0
  189. package/src/websocket/decorators.ts +53 -0
  190. package/src/websocket/index.ts +12 -0
  191. package/src/websocket/registry.ts +133 -0
  192. package/tests/cache/cache-module.test.ts +212 -0
  193. package/tests/config/config-module.test.ts +151 -0
  194. package/tests/controller/controller.test.ts +189 -0
  195. package/tests/controller/path-combination.test.ts +207 -0
  196. package/tests/core/application.test.ts +57 -0
  197. package/tests/core/context-body.test.ts +44 -0
  198. package/tests/core/context.test.ts +86 -0
  199. package/tests/core/edge-cases.test.ts +432 -0
  200. package/tests/database/database-module.test.ts +385 -0
  201. package/tests/database/orm.test.ts +164 -0
  202. package/tests/database/postgres-mysql-integration.test.ts +395 -0
  203. package/tests/database/transaction.test.ts +238 -0
  204. package/tests/di/container.test.ts +264 -0
  205. package/tests/di/module.test.ts +128 -0
  206. package/tests/error/error-codes.test.ts +121 -0
  207. package/tests/error/error-handler.test.ts +68 -0
  208. package/tests/error/error-handling.test.ts +254 -0
  209. package/tests/error/http-exception.test.ts +37 -0
  210. package/tests/error/i18n-integration.test.ts +175 -0
  211. package/tests/extensions/logger-extension.test.ts +40 -0
  212. package/tests/files/static-middleware.test.ts +67 -0
  213. package/tests/files/upload-middleware.test.ts +43 -0
  214. package/tests/health/health-module.test.ts +116 -0
  215. package/tests/integration/application-router.test.ts +85 -0
  216. package/tests/integration/body-parsing.test.ts +88 -0
  217. package/tests/integration/cache-e2e.test.ts +114 -0
  218. package/tests/integration/oauth2-e2e.test.ts +615 -0
  219. package/tests/integration/session-e2e.test.ts +207 -0
  220. package/tests/interceptor/builtin/cache-interceptor.test.ts +137 -0
  221. package/tests/interceptor/builtin/permission-interceptor.test.ts +182 -0
  222. package/tests/interceptor/interceptor-advanced-integration.test.ts +592 -0
  223. package/tests/interceptor/interceptor-arg-modification.test.ts +76 -0
  224. package/tests/interceptor/interceptor-chain.test.ts +199 -0
  225. package/tests/interceptor/interceptor-integration.test.ts +230 -0
  226. package/tests/interceptor/interceptor-registry.test.ts +200 -0
  227. package/tests/interceptor/perf/interceptor-performance.test.ts +341 -0
  228. package/tests/metrics/metrics-module.test.ts +178 -0
  229. package/tests/middleware/builtin.test.ts +206 -0
  230. package/tests/middleware/file-upload.test.ts +41 -0
  231. package/tests/middleware/middleware.test.ts +120 -0
  232. package/tests/middleware/pipeline.test.ts +72 -0
  233. package/tests/middleware/rate-limit.test.ts +314 -0
  234. package/tests/middleware/static-file.test.ts +62 -0
  235. package/tests/perf/harness.test.ts +48 -0
  236. package/tests/perf/optimization.test.ts +183 -0
  237. package/tests/perf/regression.test.ts +120 -0
  238. package/tests/queue/queue-module.test.ts +217 -0
  239. package/tests/request/body-parser.test.ts +96 -0
  240. package/tests/request/response.test.ts +99 -0
  241. package/tests/router/decorators.test.ts +46 -0
  242. package/tests/router/registry.test.ts +51 -0
  243. package/tests/router/route.test.ts +71 -0
  244. package/tests/router/router-normalization.test.ts +106 -0
  245. package/tests/router/router.test.ts +133 -0
  246. package/tests/security/access-decision-manager.test.ts +84 -0
  247. package/tests/security/authentication-manager.test.ts +81 -0
  248. package/tests/security/context.test.ts +302 -0
  249. package/tests/security/filter.test.ts +225 -0
  250. package/tests/security/jwt-provider.test.ts +106 -0
  251. package/tests/security/oauth2-provider.test.ts +269 -0
  252. package/tests/security/security-module.test.ts +143 -0
  253. package/tests/session/session-module.test.ts +307 -0
  254. package/tests/stress/di-stress.test.ts +30 -0
  255. package/tests/swagger/decorators.test.ts +153 -0
  256. package/tests/swagger/generator.test.ts +202 -0
  257. package/tests/swagger/swagger-extension.test.ts +72 -0
  258. package/tests/swagger/swagger-module.test.ts +79 -0
  259. package/tests/utils/test-port.ts +10 -0
  260. package/tests/validation/controller-validation.test.ts +64 -0
  261. package/tests/validation/validation.test.ts +42 -0
  262. package/tests/websocket/gateway.test.ts +68 -0
@@ -0,0 +1,103 @@
1
+ /**
2
+ * 指标类型
3
+ */
4
+ export type MetricType = 'counter' | 'gauge' | 'histogram' | 'summary';
5
+
6
+ /**
7
+ * 指标标签
8
+ */
9
+ export type MetricLabels = Record<string, string>;
10
+
11
+ /**
12
+ * 指标值
13
+ */
14
+ export type MetricValue = number;
15
+
16
+ /**
17
+ * 指标数据点
18
+ */
19
+ export interface MetricDataPoint {
20
+ /**
21
+ * 指标名称
22
+ */
23
+ name: string;
24
+
25
+ /**
26
+ * 指标类型
27
+ */
28
+ type: MetricType;
29
+
30
+ /**
31
+ * 指标值
32
+ */
33
+ value: MetricValue;
34
+
35
+ /**
36
+ * 标签
37
+ */
38
+ labels?: MetricLabels;
39
+
40
+ /**
41
+ * 帮助文本
42
+ */
43
+ help?: string;
44
+ }
45
+
46
+ /**
47
+ * 自定义指标
48
+ */
49
+ export interface CustomMetric {
50
+ /**
51
+ * 指标名称
52
+ */
53
+ name: string;
54
+
55
+ /**
56
+ * 指标类型
57
+ */
58
+ type: MetricType;
59
+
60
+ /**
61
+ * 帮助文本
62
+ */
63
+ help?: string;
64
+
65
+ /**
66
+ * 获取当前值
67
+ * @param labels - 标签
68
+ * @returns 指标值
69
+ */
70
+ getValue(labels?: MetricLabels): MetricValue | Promise<MetricValue>;
71
+ }
72
+
73
+ /**
74
+ * MetricsModule 配置选项
75
+ */
76
+ export interface MetricsModuleOptions {
77
+ /**
78
+ * 是否启用 HTTP 请求指标收集
79
+ * @default true
80
+ */
81
+ enableHttpMetrics?: boolean;
82
+
83
+ /**
84
+ * 自定义指标列表
85
+ */
86
+ customMetrics?: CustomMetric[];
87
+
88
+ /**
89
+ * Metrics 端点路径
90
+ * @default '/metrics'
91
+ */
92
+ path?: string;
93
+ }
94
+
95
+ /**
96
+ * Metrics 服务 Token
97
+ */
98
+ export const METRICS_SERVICE_TOKEN = Symbol('@dangao/bun-server:metrics:service');
99
+
100
+ /**
101
+ * Metrics 选项 Token
102
+ */
103
+ export const METRICS_OPTIONS_TOKEN = Symbol('@dangao/bun-server:metrics:options');
@@ -0,0 +1,60 @@
1
+ import type { Middleware } from '../middleware';
2
+
3
+ export interface CorsOptions {
4
+ origin?: string | string[] | '*';
5
+ methods?: string[];
6
+ allowedHeaders?: string[];
7
+ exposedHeaders?: string[];
8
+ credentials?: boolean;
9
+ maxAge?: number;
10
+ }
11
+
12
+ function getOriginHeader(option: CorsOptions['origin'], requestOrigin: string | null): string {
13
+ if (!option || option === '*') {
14
+ return '*';
15
+ }
16
+
17
+ if (Array.isArray(option)) {
18
+ if (requestOrigin && option.includes(requestOrigin)) {
19
+ return requestOrigin;
20
+ }
21
+ return option[0];
22
+ }
23
+
24
+ return option;
25
+ }
26
+
27
+ /**
28
+ * CORS 中间件
29
+ */
30
+ export function createCorsMiddleware(options: CorsOptions = {}): Middleware {
31
+ const methods = options.methods ?? ['GET', 'POST', 'PUT', 'PATCH', 'DELETE', 'OPTIONS'];
32
+ const allowedHeaders = options.allowedHeaders ?? ['Content-Type', 'Authorization'];
33
+ const exposedHeaders = options.exposedHeaders ?? [];
34
+ const credentials = options.credentials ?? true;
35
+ const maxAge = options.maxAge ?? 600;
36
+
37
+ return async (context, next) => {
38
+ const requestOrigin = context.getHeader('Origin');
39
+ const originHeader = getOriginHeader(options.origin ?? '*', requestOrigin);
40
+
41
+ context.setHeader('Access-Control-Allow-Origin', originHeader);
42
+ if (credentials) {
43
+ context.setHeader('Access-Control-Allow-Credentials', 'true');
44
+ }
45
+ context.setHeader('Access-Control-Allow-Methods', methods.join(','));
46
+ context.setHeader('Access-Control-Allow-Headers', allowedHeaders.join(','));
47
+ if (exposedHeaders.length > 0) {
48
+ context.setHeader('Access-Control-Expose-Headers', exposedHeaders.join(','));
49
+ }
50
+ context.setHeader('Access-Control-Max-Age', maxAge.toString());
51
+
52
+ if (context.method === 'OPTIONS') {
53
+ return new Response(null, { status: 204, headers: context.responseHeaders });
54
+ }
55
+
56
+ return await next();
57
+ };
58
+ }
59
+
60
+
@@ -0,0 +1,90 @@
1
+ import type { Middleware } from "../middleware";
2
+ import { ValidationError } from "../../validation";
3
+ import { LoggerManager } from "@dangao/logsmith";
4
+ import { HttpException } from "../../error";
5
+ import { handleError } from "../../error/handler";
6
+ import { ErrorMessageI18n } from "../../error/i18n";
7
+
8
+ export interface ErrorHandlerOptions {
9
+ /**
10
+ * 自定义错误日志函数
11
+ */
12
+ logger?: (error: unknown, context: { method: string; path: string }) => void;
13
+
14
+ /**
15
+ * 是否返回详细错误信息(默认 false,用于生产环境隐藏细节)
16
+ */
17
+ exposeError?: boolean;
18
+
19
+ /**
20
+ * 默认状态码
21
+ */
22
+ statusCode?: number;
23
+ }
24
+
25
+ /**
26
+ * 错误处理中间件:捕获下游异常并统一生成响应
27
+ */
28
+ export function createErrorHandlingMiddleware(
29
+ options: ErrorHandlerOptions = {},
30
+ ): Middleware {
31
+ const log = options.logger ??
32
+ ((error: unknown, context: { method: string; path: string }) => {
33
+ LoggerManager.getLogger().error("[Error]", { ...context, error });
34
+ });
35
+ const expose = options.exposeError ?? false;
36
+ const defaultStatus = options.statusCode ?? 500;
37
+
38
+ return async (context, next) => {
39
+ const logger = LoggerManager.getLogger();
40
+
41
+ try {
42
+ return await next();
43
+ } catch (error) {
44
+ log(error, { method: context.method, path: context.path });
45
+ logger.error("Unhandled error", {
46
+ method: context.method,
47
+ path: context.path,
48
+ error,
49
+ });
50
+
51
+ if (error instanceof Response) {
52
+ return error as Response;
53
+ }
54
+
55
+ if (error instanceof ValidationError) {
56
+ return context.createResponse(
57
+ {
58
+ error: error.message,
59
+ issues: error.issues,
60
+ },
61
+ {
62
+ status: 400,
63
+ },
64
+ );
65
+ }
66
+
67
+ if (error instanceof HttpException) {
68
+ // 统一使用 handleError 处理,它已经包含了错误码和国际化逻辑
69
+ return await handleError(error, context);
70
+ }
71
+
72
+ if (error instanceof Error && !expose) {
73
+ return await handleError(error, context);
74
+ }
75
+
76
+ if (error instanceof Error) {
77
+ return context.createResponse(
78
+ {
79
+ error: error.message,
80
+ },
81
+ {
82
+ status: defaultStatus,
83
+ },
84
+ );
85
+ }
86
+
87
+ return await handleError(error, context);
88
+ }
89
+ };
90
+ }
@@ -0,0 +1,42 @@
1
+ import type { Middleware } from '../middleware';
2
+ import { FileHandler } from '../../request/file-handler';
3
+
4
+ export interface FileUploadOptions {
5
+ maxSize?: number;
6
+ }
7
+
8
+ /**
9
+ * 简单的文件上传中间件:解析 multipart/form-data 并将文件附加到 context.body
10
+ */
11
+ export function createFileUploadMiddleware(options: FileUploadOptions = {}): Middleware {
12
+ const maxSize = options.maxSize ?? 10 * 1024 * 1024;
13
+
14
+ return async (context, next) => {
15
+ const contentType = context.getHeader('Content-Type');
16
+ if (!contentType || !contentType.includes('multipart/form-data')) {
17
+ return await next();
18
+ }
19
+
20
+ const formData = await FileHandler.parseFormData(context);
21
+ const files = await FileHandler.getFiles(formData);
22
+
23
+ // 限制大小
24
+ for (const fileList of Object.values(files)) {
25
+ for (const file of fileList) {
26
+ if (file.size > maxSize) {
27
+ context.setStatus(413);
28
+ return context.createResponse({ error: `File ${file.name} exceeds max size` });
29
+ }
30
+ }
31
+ }
32
+
33
+ context.body = {
34
+ fields: Object.fromEntries(formData.entries()),
35
+ files,
36
+ };
37
+
38
+ return await next();
39
+ };
40
+ }
41
+
42
+
@@ -0,0 +1,14 @@
1
+ export { createLoggerMiddleware, createRequestLoggingMiddleware } from './logger';
2
+ export { createErrorHandlingMiddleware } from './error-handler';
3
+ export { createCorsMiddleware } from './cors';
4
+ export { createFileUploadMiddleware } from './file-upload';
5
+ export { createStaticFileMiddleware } from './static-file';
6
+ export {
7
+ createRateLimitMiddleware,
8
+ createTokenKeyGenerator,
9
+ createUserKeyGenerator,
10
+ type RateLimitOptions,
11
+ type RateLimitStore,
12
+ } from './rate-limit';
13
+
14
+
@@ -0,0 +1,91 @@
1
+ import type { Middleware } from "../middleware";
2
+ import { LoggerManager } from "@dangao/logsmith";
3
+
4
+ export interface LoggerMiddlewareOptions {
5
+ /**
6
+ * 自定义日志函数
7
+ */
8
+ logger?: (message: string, details?: Record<string, unknown>) => void;
9
+
10
+ /**
11
+ * 日志前缀
12
+ */
13
+ prefix?: string;
14
+ }
15
+
16
+ /**
17
+ * 简单日志中间件:记录请求方法与路径
18
+ */
19
+ export function createLoggerMiddleware(
20
+ options: LoggerMiddlewareOptions = {},
21
+ ): Middleware {
22
+ const log = options.logger ??
23
+ ((message: string, details?: Record<string, unknown>) => {
24
+ const logger = LoggerManager.getLogger();
25
+ if (details) {
26
+ logger.info(message, details);
27
+ } else {
28
+ logger.info(message);
29
+ }
30
+ });
31
+ const prefix = options.prefix ?? "[Logger]";
32
+
33
+ return async (context, next) => {
34
+ let response: Response | undefined;
35
+ try {
36
+ response = await next();
37
+ return response;
38
+ } finally {
39
+ const status = response?.status ?? context.statusCode ?? 200;
40
+ log(`${prefix} ${context.method} ${context.path} ${status}`);
41
+ }
42
+ };
43
+ }
44
+
45
+ export interface RequestLoggingOptions extends LoggerMiddlewareOptions {
46
+ /**
47
+ * 是否在响应头中附加请求耗时
48
+ */
49
+ setHeader?: boolean;
50
+ }
51
+
52
+ /**
53
+ * 请求日志中间件:记录耗时与状态码
54
+ */
55
+ export function createRequestLoggingMiddleware(
56
+ options: RequestLoggingOptions = {},
57
+ ): Middleware {
58
+ const log = options.logger ??
59
+ ((message: string, details?: Record<string, unknown>) => {
60
+ const logger = LoggerManager.getLogger();
61
+ logger.info(message, details);
62
+ });
63
+ const prefix = options.prefix ?? "[Request]";
64
+ const setHeader = options.setHeader ?? true;
65
+
66
+ return async (context, next) => {
67
+ const start = performance.now();
68
+ try {
69
+ const response = await next();
70
+ const duration = performance.now() - start;
71
+ log(
72
+ `${prefix} ${context.method} ${context.path} ${response.status} ${
73
+ duration.toFixed(2)
74
+ }ms`,
75
+ );
76
+ if (setHeader) {
77
+ context.setHeader("x-request-duration", duration.toFixed(2));
78
+ }
79
+ return response;
80
+ } catch (error) {
81
+ const duration = performance.now() - start;
82
+ log(
83
+ `${prefix} ${context.method} ${context.path} error ${
84
+ duration.toFixed(2)
85
+ }ms`,
86
+ error instanceof Error ? { error: error.message } : undefined,
87
+ );
88
+ throw error;
89
+ }
90
+ };
91
+ }
@@ -0,0 +1,252 @@
1
+ import type { Context } from '../../core/context';
2
+ import type { Middleware } from '../middleware';
3
+
4
+ /**
5
+ * 速率限制存储接口
6
+ */
7
+ export interface RateLimitStore {
8
+ /**
9
+ * 获取当前计数
10
+ * @param key - 存储键
11
+ * @returns 当前计数
12
+ */
13
+ get(key: string): Promise<number>;
14
+
15
+ /**
16
+ * 增加计数
17
+ * @param key - 存储键
18
+ * @param windowMs - 时间窗口(毫秒)
19
+ * @returns 增加后的计数
20
+ */
21
+ increment(key: string, windowMs: number): Promise<number>;
22
+
23
+ /**
24
+ * 重置计数
25
+ * @param key - 存储键
26
+ */
27
+ reset(key: string): Promise<void>;
28
+ }
29
+
30
+ /**
31
+ * 内存存储实现(使用 Map)
32
+ */
33
+ export class MemoryRateLimitStore implements RateLimitStore {
34
+ private store: Map<string, { count: number; resetTime: number }> = new Map();
35
+
36
+ public async get(key: string): Promise<number> {
37
+ const entry = this.store.get(key);
38
+ if (!entry) {
39
+ return 0;
40
+ }
41
+
42
+ // 如果已过期,返回 0
43
+ if (Date.now() > entry.resetTime) {
44
+ this.store.delete(key);
45
+ return 0;
46
+ }
47
+
48
+ return entry.count;
49
+ }
50
+
51
+ public async increment(key: string, windowMs: number): Promise<number> {
52
+ const now = Date.now();
53
+ const entry = this.store.get(key);
54
+
55
+ if (!entry || now > entry.resetTime) {
56
+ // 创建新条目或重置过期条目
57
+ const resetTime = now + windowMs;
58
+ this.store.set(key, { count: 1, resetTime });
59
+ return 1;
60
+ }
61
+
62
+ // 增加计数
63
+ entry.count++;
64
+ return entry.count;
65
+ }
66
+
67
+ public async reset(key: string): Promise<void> {
68
+ this.store.delete(key);
69
+ }
70
+
71
+ /**
72
+ * 清理过期条目(可选,用于内存管理)
73
+ */
74
+ public cleanup(): void {
75
+ const now = Date.now();
76
+ for (const [key, entry] of this.store.entries()) {
77
+ if (now > entry.resetTime) {
78
+ this.store.delete(key);
79
+ }
80
+ }
81
+ }
82
+ }
83
+
84
+ /**
85
+ * 速率限制选项
86
+ */
87
+ export interface RateLimitOptions {
88
+ /**
89
+ * 时间窗口内的最大请求数
90
+ */
91
+ max: number;
92
+
93
+ /**
94
+ * 时间窗口(毫秒)
95
+ * @default 60000 (1 分钟)
96
+ */
97
+ windowMs?: number;
98
+
99
+ /**
100
+ * 存储实现(默认使用内存存储)
101
+ */
102
+ store?: RateLimitStore;
103
+
104
+ /**
105
+ * 获取限流键的函数
106
+ * @param context - 请求上下文
107
+ * @returns 限流键
108
+ */
109
+ keyGenerator?: (context: Context) => string | Promise<string>;
110
+
111
+ /**
112
+ * 是否跳过成功响应(只对错误响应计数)
113
+ * @default false
114
+ */
115
+ skipSuccessfulRequests?: boolean;
116
+
117
+ /**
118
+ * 是否跳过失败响应(只对成功响应计数)
119
+ * @default false
120
+ */
121
+ skipFailedRequests?: boolean;
122
+
123
+ /**
124
+ * 自定义错误消息
125
+ */
126
+ message?: string;
127
+
128
+ /**
129
+ * 自定义错误状态码
130
+ * @default 429
131
+ */
132
+ statusCode?: number;
133
+
134
+ /**
135
+ * 是否在响应头中包含限流信息
136
+ * @default true
137
+ */
138
+ standardHeaders?: boolean;
139
+
140
+ /**
141
+ * 是否启用 X-RateLimit-* 响应头
142
+ * @default true
143
+ */
144
+ legacyHeaders?: boolean;
145
+ }
146
+
147
+ /**
148
+ * 默认键生成器:基于 IP 地址
149
+ */
150
+ function defaultKeyGenerator(context: Context): string {
151
+ return `rate-limit:${context.getClientIp()}`;
152
+ }
153
+
154
+ /**
155
+ * 创建速率限制中间件
156
+ */
157
+ export function createRateLimitMiddleware(options: RateLimitOptions): Middleware {
158
+ const {
159
+ max,
160
+ windowMs = 60000, // 默认 1 分钟
161
+ store = new MemoryRateLimitStore(),
162
+ keyGenerator = defaultKeyGenerator,
163
+ skipSuccessfulRequests = false,
164
+ skipFailedRequests = false,
165
+ message = 'Too Many Requests',
166
+ statusCode = 429,
167
+ standardHeaders = true,
168
+ legacyHeaders = true,
169
+ } = options;
170
+
171
+ return async (context: Context, next) => {
172
+ // 生成限流键
173
+ const key = await keyGenerator(context);
174
+ const currentCount = await store.increment(key, windowMs);
175
+
176
+ // 计算剩余请求数和重置时间
177
+ const remaining = Math.max(0, max - currentCount);
178
+ const resetTime = Date.now() + windowMs;
179
+
180
+ // 设置响应头
181
+ if (standardHeaders) {
182
+ context.setHeader('RateLimit-Limit', max.toString());
183
+ context.setHeader('RateLimit-Remaining', remaining.toString());
184
+ context.setHeader('RateLimit-Reset', Math.ceil(resetTime / 1000).toString());
185
+ }
186
+
187
+ if (legacyHeaders) {
188
+ context.setHeader('X-RateLimit-Limit', max.toString());
189
+ context.setHeader('X-RateLimit-Remaining', remaining.toString());
190
+ context.setHeader('X-RateLimit-Reset', Math.ceil(resetTime / 1000).toString());
191
+ }
192
+
193
+ // 检查是否超过限制
194
+ if (currentCount > max) {
195
+ context.setStatus(statusCode);
196
+ return context.createResponse({
197
+ error: message,
198
+ retryAfter: Math.ceil(windowMs / 1000),
199
+ });
200
+ }
201
+
202
+ // 执行下一个中间件或路由处理器
203
+ const response = await next();
204
+
205
+ // 根据选项决定是否计数
206
+ const shouldSkip =
207
+ (skipSuccessfulRequests && response.status >= 200 && response.status < 300) ||
208
+ (skipFailedRequests && response.status >= 400);
209
+
210
+ if (shouldSkip) {
211
+ // 如果跳过,需要减少计数(因为之前已经增加了)
212
+ const current = await store.get(key);
213
+ if (current > 0) {
214
+ // 注意:这里不能直接减少,因为滑动窗口算法不支持
215
+ // 所以这个选项在滑动窗口算法下效果有限
216
+ // 更好的做法是在请求成功后不增加计数,但这需要重构
217
+ }
218
+ }
219
+
220
+ return response;
221
+ };
222
+ }
223
+
224
+ /**
225
+ * 基于 Token/User 的键生成器
226
+ */
227
+ export function createTokenKeyGenerator(tokenHeader: string = 'Authorization'): (context: Context) => string {
228
+ return (context: Context) => {
229
+ const token = context.getHeader(tokenHeader);
230
+ if (token) {
231
+ // 提取 token(可能包含 Bearer 前缀)
232
+ const tokenValue = token.startsWith('Bearer ') ? token.substring(7) : token;
233
+ return `rate-limit:token:${tokenValue}`;
234
+ }
235
+ // 如果没有 token,回退到 IP
236
+ return defaultKeyGenerator(context);
237
+ };
238
+ }
239
+
240
+ /**
241
+ * 基于用户 ID 的键生成器(需要从认证上下文获取)
242
+ */
243
+ export function createUserKeyGenerator(getUserId: (context: Context) => string | null | undefined): (context: Context) => string {
244
+ return (context: Context) => {
245
+ const userId = getUserId(context);
246
+ if (userId) {
247
+ return `rate-limit:user:${userId}`;
248
+ }
249
+ // 如果没有用户 ID,回退到 IP
250
+ return defaultKeyGenerator(context);
251
+ };
252
+ }