@dangao/bun-server 1.0.0 → 1.0.3

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (200) hide show
  1. package/package.json +4 -2
  2. package/readme.md +163 -2
  3. package/src/auth/controller.ts +148 -0
  4. package/src/auth/decorators.ts +81 -0
  5. package/src/auth/index.ts +12 -0
  6. package/src/auth/jwt.ts +169 -0
  7. package/src/auth/oauth2.ts +244 -0
  8. package/src/auth/types.ts +248 -0
  9. package/src/cache/cache-module.ts +67 -0
  10. package/src/cache/decorators.ts +202 -0
  11. package/src/cache/index.ts +27 -0
  12. package/src/cache/service.ts +151 -0
  13. package/src/cache/types.ts +420 -0
  14. package/src/config/config-module.ts +76 -0
  15. package/src/config/index.ts +8 -0
  16. package/src/config/service.ts +93 -0
  17. package/src/config/types.ts +27 -0
  18. package/src/controller/controller.ts +251 -0
  19. package/src/controller/decorators.ts +84 -0
  20. package/src/controller/index.ts +7 -0
  21. package/src/controller/metadata.ts +27 -0
  22. package/src/controller/param-binder.ts +157 -0
  23. package/src/core/application.ts +233 -0
  24. package/src/core/context.ts +228 -0
  25. package/src/core/index.ts +4 -0
  26. package/src/core/server.ts +128 -0
  27. package/src/core/types.ts +2 -0
  28. package/src/database/connection-manager.ts +239 -0
  29. package/src/database/connection-pool.ts +322 -0
  30. package/src/database/database-extension.ts +62 -0
  31. package/src/database/database-module.ts +115 -0
  32. package/src/database/health-indicator.ts +51 -0
  33. package/src/database/index.ts +47 -0
  34. package/src/database/orm/decorators.ts +155 -0
  35. package/src/database/orm/drizzle-repository.ts +39 -0
  36. package/src/database/orm/index.ts +23 -0
  37. package/src/database/orm/repository-decorator.ts +39 -0
  38. package/src/database/orm/repository.ts +103 -0
  39. package/src/database/orm/service.ts +49 -0
  40. package/src/database/orm/transaction-decorator.ts +45 -0
  41. package/src/database/orm/transaction-interceptor.ts +243 -0
  42. package/src/database/orm/transaction-manager.ts +276 -0
  43. package/src/database/orm/transaction-types.ts +140 -0
  44. package/src/database/orm/types.ts +99 -0
  45. package/src/database/service.ts +221 -0
  46. package/src/database/types.ts +171 -0
  47. package/src/di/container.ts +398 -0
  48. package/src/di/decorators.ts +228 -0
  49. package/src/di/index.ts +4 -0
  50. package/src/di/module-registry.ts +188 -0
  51. package/src/di/module.ts +65 -0
  52. package/src/di/types.ts +67 -0
  53. package/src/error/error-codes.ts +222 -0
  54. package/src/error/filter.ts +43 -0
  55. package/src/error/handler.ts +66 -0
  56. package/src/error/http-exception.ts +115 -0
  57. package/src/error/i18n.ts +217 -0
  58. package/src/error/index.ts +16 -0
  59. package/src/extensions/index.ts +5 -0
  60. package/src/extensions/logger-extension.ts +31 -0
  61. package/src/extensions/logger-module.ts +69 -0
  62. package/src/extensions/types.ts +14 -0
  63. package/src/files/index.ts +5 -0
  64. package/src/files/static-middleware.ts +53 -0
  65. package/src/files/storage.ts +67 -0
  66. package/src/files/types.ts +33 -0
  67. package/src/files/upload-middleware.ts +45 -0
  68. package/src/health/controller.ts +76 -0
  69. package/src/health/health-module.ts +51 -0
  70. package/src/health/index.ts +12 -0
  71. package/src/health/types.ts +28 -0
  72. package/src/index.ts +270 -0
  73. package/src/metrics/collector.ts +209 -0
  74. package/src/metrics/controller.ts +40 -0
  75. package/src/metrics/index.ts +15 -0
  76. package/src/metrics/metrics-module.ts +58 -0
  77. package/src/metrics/middleware.ts +46 -0
  78. package/src/metrics/prometheus.ts +79 -0
  79. package/src/metrics/types.ts +103 -0
  80. package/src/middleware/builtin/cors.ts +60 -0
  81. package/src/middleware/builtin/error-handler.ts +90 -0
  82. package/src/middleware/builtin/file-upload.ts +42 -0
  83. package/src/middleware/builtin/index.ts +14 -0
  84. package/src/middleware/builtin/logger.ts +91 -0
  85. package/src/middleware/builtin/rate-limit.ts +252 -0
  86. package/src/middleware/builtin/static-file.ts +88 -0
  87. package/src/middleware/decorators.ts +91 -0
  88. package/src/middleware/index.ts +11 -0
  89. package/src/middleware/middleware.ts +13 -0
  90. package/src/middleware/pipeline.ts +93 -0
  91. package/src/queue/decorators.ts +110 -0
  92. package/src/queue/index.ts +26 -0
  93. package/src/queue/queue-module.ts +64 -0
  94. package/src/queue/service.ts +302 -0
  95. package/src/queue/types.ts +341 -0
  96. package/src/request/body-parser.ts +133 -0
  97. package/src/request/file-handler.ts +46 -0
  98. package/src/request/index.ts +5 -0
  99. package/src/request/request.ts +107 -0
  100. package/src/request/response.ts +150 -0
  101. package/src/router/decorators.ts +122 -0
  102. package/src/router/index.ts +6 -0
  103. package/src/router/registry.ts +98 -0
  104. package/src/router/route.ts +140 -0
  105. package/src/router/router.ts +241 -0
  106. package/src/router/types.ts +27 -0
  107. package/src/security/access-decision-manager.ts +34 -0
  108. package/src/security/authentication-manager.ts +47 -0
  109. package/src/security/context.ts +92 -0
  110. package/src/security/filter.ts +162 -0
  111. package/src/security/index.ts +8 -0
  112. package/src/security/providers/index.ts +3 -0
  113. package/src/security/providers/jwt-provider.ts +60 -0
  114. package/src/security/providers/oauth2-provider.ts +70 -0
  115. package/src/security/security-module.ts +145 -0
  116. package/src/security/types.ts +165 -0
  117. package/src/session/decorators.ts +45 -0
  118. package/src/session/index.ts +19 -0
  119. package/src/session/middleware.ts +143 -0
  120. package/src/session/service.ts +218 -0
  121. package/src/session/session-module.ts +69 -0
  122. package/src/session/types.ts +373 -0
  123. package/src/swagger/decorators.ts +133 -0
  124. package/src/swagger/generator.ts +234 -0
  125. package/src/swagger/index.ts +7 -0
  126. package/src/swagger/swagger-extension.ts +41 -0
  127. package/src/swagger/swagger-module.ts +83 -0
  128. package/src/swagger/types.ts +188 -0
  129. package/src/swagger/ui.ts +98 -0
  130. package/src/testing/harness.ts +96 -0
  131. package/src/validation/decorators.ts +95 -0
  132. package/src/validation/errors.ts +28 -0
  133. package/src/validation/index.ts +14 -0
  134. package/src/validation/types.ts +35 -0
  135. package/src/validation/validator.ts +63 -0
  136. package/src/websocket/decorators.ts +51 -0
  137. package/src/websocket/index.ts +12 -0
  138. package/src/websocket/registry.ts +133 -0
  139. package/tests/cache/cache-module.test.ts +212 -0
  140. package/tests/config/config-module.test.ts +151 -0
  141. package/tests/controller/controller.test.ts +189 -0
  142. package/tests/core/application.test.ts +57 -0
  143. package/tests/core/context-body.test.ts +44 -0
  144. package/tests/core/context.test.ts +86 -0
  145. package/tests/core/edge-cases.test.ts +432 -0
  146. package/tests/database/database-module.test.ts +385 -0
  147. package/tests/database/orm.test.ts +164 -0
  148. package/tests/database/postgres-mysql-integration.test.ts +395 -0
  149. package/tests/database/transaction.test.ts +238 -0
  150. package/tests/di/container.test.ts +264 -0
  151. package/tests/di/module.test.ts +128 -0
  152. package/tests/error/error-codes.test.ts +121 -0
  153. package/tests/error/error-handler.test.ts +68 -0
  154. package/tests/error/error-handling.test.ts +254 -0
  155. package/tests/error/http-exception.test.ts +37 -0
  156. package/tests/error/i18n-integration.test.ts +175 -0
  157. package/tests/extensions/logger-extension.test.ts +40 -0
  158. package/tests/files/static-middleware.test.ts +67 -0
  159. package/tests/files/upload-middleware.test.ts +43 -0
  160. package/tests/health/health-module.test.ts +116 -0
  161. package/tests/integration/application-router.test.ts +85 -0
  162. package/tests/integration/body-parsing.test.ts +88 -0
  163. package/tests/integration/cache-e2e.test.ts +114 -0
  164. package/tests/integration/oauth2-e2e.test.ts +615 -0
  165. package/tests/integration/session-e2e.test.ts +207 -0
  166. package/tests/metrics/metrics-module.test.ts +178 -0
  167. package/tests/middleware/builtin.test.ts +206 -0
  168. package/tests/middleware/file-upload.test.ts +41 -0
  169. package/tests/middleware/middleware.test.ts +120 -0
  170. package/tests/middleware/pipeline.test.ts +72 -0
  171. package/tests/middleware/rate-limit.test.ts +314 -0
  172. package/tests/middleware/static-file.test.ts +62 -0
  173. package/tests/perf/harness.test.ts +48 -0
  174. package/tests/perf/optimization.test.ts +183 -0
  175. package/tests/perf/regression.test.ts +120 -0
  176. package/tests/queue/queue-module.test.ts +217 -0
  177. package/tests/request/body-parser.test.ts +96 -0
  178. package/tests/request/response.test.ts +99 -0
  179. package/tests/router/decorators.test.ts +48 -0
  180. package/tests/router/registry.test.ts +51 -0
  181. package/tests/router/route.test.ts +71 -0
  182. package/tests/router/router-normalization.test.ts +106 -0
  183. package/tests/router/router.test.ts +133 -0
  184. package/tests/security/access-decision-manager.test.ts +84 -0
  185. package/tests/security/authentication-manager.test.ts +81 -0
  186. package/tests/security/context.test.ts +302 -0
  187. package/tests/security/filter.test.ts +225 -0
  188. package/tests/security/jwt-provider.test.ts +106 -0
  189. package/tests/security/oauth2-provider.test.ts +269 -0
  190. package/tests/security/security-module.test.ts +143 -0
  191. package/tests/session/session-module.test.ts +307 -0
  192. package/tests/stress/di-stress.test.ts +30 -0
  193. package/tests/swagger/decorators.test.ts +153 -0
  194. package/tests/swagger/generator.test.ts +202 -0
  195. package/tests/swagger/swagger-extension.test.ts +72 -0
  196. package/tests/swagger/swagger-module.test.ts +79 -0
  197. package/tests/utils/test-port.ts +10 -0
  198. package/tests/validation/controller-validation.test.ts +64 -0
  199. package/tests/validation/validation.test.ts +42 -0
  200. package/tests/websocket/gateway.test.ts +68 -0
@@ -0,0 +1,373 @@
1
+ /**
2
+ * Session 数据
3
+ */
4
+ export interface SessionData {
5
+ [key: string]: unknown;
6
+ }
7
+
8
+ /**
9
+ * Session 信息
10
+ */
11
+ export interface Session {
12
+ /**
13
+ * Session ID
14
+ */
15
+ id: string;
16
+
17
+ /**
18
+ * Session 数据
19
+ */
20
+ data: SessionData;
21
+
22
+ /**
23
+ * 创建时间
24
+ */
25
+ createdAt: number;
26
+
27
+ /**
28
+ * 最后访问时间
29
+ */
30
+ lastAccessedAt: number;
31
+
32
+ /**
33
+ * 过期时间(毫秒时间戳)
34
+ */
35
+ expiresAt: number;
36
+ }
37
+
38
+ /**
39
+ * Session 存储接口
40
+ */
41
+ export interface SessionStore {
42
+ /**
43
+ * 获取 Session
44
+ * @param sessionId - Session ID
45
+ * @returns Session,如果不存在或已过期则返回 undefined
46
+ */
47
+ get(sessionId: string): Promise<Session | undefined>;
48
+
49
+ /**
50
+ * 设置 Session
51
+ * @param session - Session 信息
52
+ * @param maxAge - 最大存活时间(毫秒)
53
+ * @returns 是否设置成功
54
+ */
55
+ set(session: Session, maxAge: number): Promise<boolean>;
56
+
57
+ /**
58
+ * 删除 Session
59
+ * @param sessionId - Session ID
60
+ * @returns 是否删除成功
61
+ */
62
+ delete(sessionId: string): Promise<boolean>;
63
+
64
+ /**
65
+ * 检查 Session 是否存在
66
+ * @param sessionId - Session ID
67
+ * @returns 是否存在
68
+ */
69
+ has(sessionId: string): Promise<boolean>;
70
+
71
+ /**
72
+ * 更新 Session 的最后访问时间
73
+ * @param sessionId - Session ID
74
+ * @returns 是否更新成功
75
+ */
76
+ touch(sessionId: string): Promise<boolean>;
77
+
78
+ /**
79
+ * 清空所有 Session
80
+ * @returns 是否清空成功
81
+ */
82
+ clear(): Promise<boolean>;
83
+ }
84
+
85
+ /**
86
+ * 内存 Session 存储实现
87
+ */
88
+ export class MemorySessionStore implements SessionStore {
89
+ private store: Map<string, Session> = new Map();
90
+ private cleanupInterval?: ReturnType<typeof setInterval>;
91
+
92
+ public constructor(options?: { cleanupInterval?: number }) {
93
+ // 定期清理过期 Session
94
+ if (options?.cleanupInterval !== undefined) {
95
+ this.cleanupInterval = setInterval(() => {
96
+ this.cleanup();
97
+ }, options.cleanupInterval);
98
+ }
99
+ }
100
+
101
+ public async get(sessionId: string): Promise<Session | undefined> {
102
+ const session = this.store.get(sessionId);
103
+ if (!session) {
104
+ return undefined;
105
+ }
106
+
107
+ // 检查是否过期
108
+ if (Date.now() > session.expiresAt) {
109
+ this.store.delete(sessionId);
110
+ return undefined;
111
+ }
112
+
113
+ return session;
114
+ }
115
+
116
+ public async set(session: Session, maxAge: number): Promise<boolean> {
117
+ const now = Date.now();
118
+ session.expiresAt = now + maxAge;
119
+ session.lastAccessedAt = now;
120
+ this.store.set(session.id, session);
121
+ return true;
122
+ }
123
+
124
+ public async delete(sessionId: string): Promise<boolean> {
125
+ return this.store.delete(sessionId);
126
+ }
127
+
128
+ public async has(sessionId: string): Promise<boolean> {
129
+ const session = this.store.get(sessionId);
130
+ if (!session) {
131
+ return false;
132
+ }
133
+
134
+ // 检查是否过期
135
+ if (Date.now() > session.expiresAt) {
136
+ this.store.delete(sessionId);
137
+ return false;
138
+ }
139
+
140
+ return true;
141
+ }
142
+
143
+ public async touch(sessionId: string): Promise<boolean> {
144
+ const session = this.store.get(sessionId);
145
+ if (!session) {
146
+ return false;
147
+ }
148
+
149
+ // 检查是否过期
150
+ if (Date.now() > session.expiresAt) {
151
+ this.store.delete(sessionId);
152
+ return false;
153
+ }
154
+
155
+ // 更新最后访问时间
156
+ session.lastAccessedAt = Date.now();
157
+ return true;
158
+ }
159
+
160
+ public async clear(): Promise<boolean> {
161
+ this.store.clear();
162
+ return true;
163
+ }
164
+
165
+ /**
166
+ * 清理过期 Session
167
+ */
168
+ private cleanup(): void {
169
+ const now = Date.now();
170
+ for (const [sessionId, session] of this.store.entries()) {
171
+ if (now > session.expiresAt) {
172
+ this.store.delete(sessionId);
173
+ }
174
+ }
175
+ }
176
+
177
+ /**
178
+ * 销毁存储,清理定时器
179
+ */
180
+ public destroy(): void {
181
+ if (this.cleanupInterval) {
182
+ clearInterval(this.cleanupInterval);
183
+ this.cleanupInterval = undefined;
184
+ }
185
+ this.store.clear();
186
+ }
187
+ }
188
+
189
+ /**
190
+ * Redis Session 存储实现(需要 redis 包)
191
+ */
192
+ export interface RedisSessionStoreOptions {
193
+ /**
194
+ * Redis 客户端(需要用户提供)
195
+ */
196
+ client: {
197
+ get(key: string): Promise<string | null>;
198
+ set(
199
+ key: string,
200
+ value: string,
201
+ options?: { PX?: number },
202
+ ): Promise<void>;
203
+ del(key: string): Promise<void>;
204
+ exists(key: string): Promise<number>;
205
+ expire(key: string, seconds: number): Promise<void>;
206
+ };
207
+ /**
208
+ * 键前缀
209
+ * @default 'session:'
210
+ */
211
+ keyPrefix?: string;
212
+ }
213
+
214
+ export class RedisSessionStore implements SessionStore {
215
+ private client: RedisSessionStoreOptions['client'];
216
+ private keyPrefix: string;
217
+
218
+ public constructor(options: RedisSessionStoreOptions) {
219
+ this.client = options.client;
220
+ this.keyPrefix = options.keyPrefix ?? 'session:';
221
+ }
222
+
223
+ private getKey(sessionId: string): string {
224
+ return `${this.keyPrefix}${sessionId}`;
225
+ }
226
+
227
+ public async get(sessionId: string): Promise<Session | undefined> {
228
+ try {
229
+ const value = await this.client.get(this.getKey(sessionId));
230
+ if (value === null) {
231
+ return undefined;
232
+ }
233
+ return JSON.parse(value) as Session;
234
+ } catch {
235
+ return undefined;
236
+ }
237
+ }
238
+
239
+ public async set(session: Session, maxAge: number): Promise<boolean> {
240
+ try {
241
+ const serialized = JSON.stringify(session);
242
+ await this.client.set(this.getKey(session.id), serialized, {
243
+ PX: maxAge,
244
+ });
245
+ return true;
246
+ } catch {
247
+ return false;
248
+ }
249
+ }
250
+
251
+ public async delete(sessionId: string): Promise<boolean> {
252
+ try {
253
+ await this.client.del(this.getKey(sessionId));
254
+ return true;
255
+ } catch {
256
+ return false;
257
+ }
258
+ }
259
+
260
+ public async has(sessionId: string): Promise<boolean> {
261
+ try {
262
+ const result = await this.client.exists(this.getKey(sessionId));
263
+ return result === 1;
264
+ } catch {
265
+ return false;
266
+ }
267
+ }
268
+
269
+ public async touch(sessionId: string): Promise<boolean> {
270
+ try {
271
+ const session = await this.get(sessionId);
272
+ if (!session) {
273
+ return false;
274
+ }
275
+
276
+ // 更新最后访问时间
277
+ session.lastAccessedAt = Date.now();
278
+ const remainingTime = session.expiresAt - Date.now();
279
+ if (remainingTime > 0) {
280
+ await this.client.expire(this.getKey(sessionId), Math.floor(remainingTime / 1000));
281
+ return true;
282
+ }
283
+
284
+ return false;
285
+ } catch {
286
+ return false;
287
+ }
288
+ }
289
+
290
+ public async clear(): Promise<boolean> {
291
+ // Redis 没有直接清空所有 session 的方法
292
+ // 实际实现中可能需要使用 SCAN 命令遍历所有键
293
+ // 这里简化处理,返回 false
294
+ return false;
295
+ }
296
+ }
297
+
298
+ /**
299
+ * SessionModule 配置选项
300
+ */
301
+ export interface SessionModuleOptions {
302
+ /**
303
+ * Session 存储实现
304
+ * @default MemorySessionStore
305
+ */
306
+ store?: SessionStore;
307
+
308
+ /**
309
+ * Session 名称(Cookie 名称)
310
+ * @default 'sessionId'
311
+ */
312
+ name?: string;
313
+
314
+ /**
315
+ * Session 最大存活时间(毫秒)
316
+ * @default 86400000 (24 小时)
317
+ */
318
+ maxAge?: number;
319
+
320
+ /**
321
+ * 是否在每次访问时更新过期时间
322
+ * @default true
323
+ */
324
+ rolling?: boolean;
325
+
326
+ /**
327
+ * Cookie 选项
328
+ */
329
+ cookie?: {
330
+ /**
331
+ * 是否只在 HTTPS 下发送
332
+ * @default false
333
+ */
334
+ secure?: boolean;
335
+
336
+ /**
337
+ * 是否只能通过 HTTP 访问(不能通过 JavaScript)
338
+ * @default true
339
+ */
340
+ httpOnly?: boolean;
341
+
342
+ /**
343
+ * Cookie 路径
344
+ * @default '/'
345
+ */
346
+ path?: string;
347
+
348
+ /**
349
+ * Cookie 域名
350
+ */
351
+ domain?: string;
352
+
353
+ /**
354
+ * SameSite 属性
355
+ * @default 'lax'
356
+ */
357
+ sameSite?: 'strict' | 'lax' | 'none';
358
+ };
359
+ }
360
+
361
+ /**
362
+ * SessionService Token
363
+ */
364
+ export const SESSION_SERVICE_TOKEN = Symbol(
365
+ '@dangao/bun-server:session:service',
366
+ );
367
+
368
+ /**
369
+ * SessionModule Options Token
370
+ */
371
+ export const SESSION_OPTIONS_TOKEN = Symbol(
372
+ '@dangao/bun-server:session:options',
373
+ );
@@ -0,0 +1,133 @@
1
+ import 'reflect-metadata';
2
+ import type {
3
+ ApiBodyMetadata,
4
+ ApiOperationMetadata,
5
+ ApiParamMetadata,
6
+ ApiResponseMetadata,
7
+ } from './types';
8
+
9
+ /**
10
+ * Swagger 元数据键
11
+ */
12
+ const API_TAG_METADATA_KEY = Symbol('swagger:api-tag');
13
+ const API_OPERATION_METADATA_KEY = Symbol('swagger:api-operation');
14
+ const API_PARAM_METADATA_KEY = Symbol('swagger:api-param');
15
+ const API_BODY_METADATA_KEY = Symbol('swagger:api-body');
16
+ const API_RESPONSE_METADATA_KEY = Symbol('swagger:api-response');
17
+
18
+ /**
19
+ * API 标签装饰器
20
+ * 用于标记控制器或操作的标签
21
+ * @param metadata - 标签元数据
22
+ */
23
+ export function ApiTags(...tags: string[]): ClassDecorator & MethodDecorator {
24
+ return function (target: unknown, propertyKey?: string | symbol) {
25
+ if (propertyKey === undefined) {
26
+ // 类装饰器
27
+ Reflect.defineMetadata(API_TAG_METADATA_KEY, tags, target as Object);
28
+ } else {
29
+ // 方法装饰器
30
+ const existingTags: string[] =
31
+ Reflect.getMetadata(API_TAG_METADATA_KEY, target as Object, propertyKey) || [];
32
+ Reflect.defineMetadata(API_TAG_METADATA_KEY, [...existingTags, ...tags], target as Object, propertyKey);
33
+ }
34
+ };
35
+ }
36
+
37
+ /**
38
+ * API 操作装饰器
39
+ * 用于描述 API 操作
40
+ * @param metadata - 操作元数据
41
+ */
42
+ export function ApiOperation(metadata: ApiOperationMetadata): MethodDecorator {
43
+ return function (target: unknown, propertyKey: string | symbol) {
44
+ Reflect.defineMetadata(API_OPERATION_METADATA_KEY, metadata, target as Object, propertyKey);
45
+ };
46
+ }
47
+
48
+ /**
49
+ * API 参数装饰器
50
+ * 用于描述 API 参数
51
+ * @param metadata - 参数元数据
52
+ */
53
+ export function ApiParam(metadata: ApiParamMetadata): ParameterDecorator {
54
+ return function (target: unknown, propertyKey: string | symbol | undefined, parameterIndex: number) {
55
+ const existingParams: Array<{ index: number; metadata: ApiParamMetadata }> =
56
+ Reflect.getMetadata(API_PARAM_METADATA_KEY, target as Object, propertyKey!) || [];
57
+ existingParams.push({ index: parameterIndex, metadata });
58
+ Reflect.defineMetadata(API_PARAM_METADATA_KEY, existingParams, target as Object, propertyKey!);
59
+ };
60
+ }
61
+
62
+ /**
63
+ * API 请求体装饰器
64
+ * 用于描述请求体
65
+ * @param metadata - 请求体元数据
66
+ */
67
+ export function ApiBody(metadata: ApiBodyMetadata): MethodDecorator {
68
+ return function (target: unknown, propertyKey: string | symbol) {
69
+ Reflect.defineMetadata(API_BODY_METADATA_KEY, metadata, target as Object, propertyKey);
70
+ };
71
+ }
72
+
73
+ /**
74
+ * API 响应装饰器
75
+ * 用于描述 API 响应
76
+ * @param metadata - 响应元数据
77
+ */
78
+ export function ApiResponse(metadata: ApiResponseMetadata): MethodDecorator {
79
+ return function (target: unknown, propertyKey: string | symbol) {
80
+ const existingResponses: ApiResponseMetadata[] =
81
+ Reflect.getMetadata(API_RESPONSE_METADATA_KEY, target as Object, propertyKey) || [];
82
+ existingResponses.push(metadata);
83
+ Reflect.defineMetadata(API_RESPONSE_METADATA_KEY, existingResponses, target as Object, propertyKey);
84
+ };
85
+ }
86
+
87
+ /**
88
+ * 获取 API 标签
89
+ */
90
+ export function getApiTags(target: unknown, propertyKey?: string | symbol): string[] {
91
+ if (propertyKey === undefined) {
92
+ return Reflect.getMetadata(API_TAG_METADATA_KEY, target as Object) || [];
93
+ }
94
+ return Reflect.getMetadata(API_TAG_METADATA_KEY, target as Object, propertyKey) || [];
95
+ }
96
+
97
+ /**
98
+ * 获取 API 操作元数据
99
+ */
100
+ export function getApiOperation(
101
+ target: unknown,
102
+ propertyKey: string | symbol,
103
+ ): ApiOperationMetadata | undefined {
104
+ return Reflect.getMetadata(API_OPERATION_METADATA_KEY, target as Object, propertyKey);
105
+ }
106
+
107
+ /**
108
+ * 获取 API 参数元数据
109
+ */
110
+ export function getApiParams(
111
+ target: unknown,
112
+ propertyKey: string | symbol,
113
+ ): Array<{ index: number; metadata: ApiParamMetadata }> {
114
+ return Reflect.getMetadata(API_PARAM_METADATA_KEY, target as Object, propertyKey) || [];
115
+ }
116
+
117
+ /**
118
+ * 获取 API 请求体元数据
119
+ */
120
+ export function getApiBody(target: unknown, propertyKey: string | symbol): ApiBodyMetadata | undefined {
121
+ return Reflect.getMetadata(API_BODY_METADATA_KEY, target as Object, propertyKey);
122
+ }
123
+
124
+ /**
125
+ * 获取 API 响应元数据
126
+ */
127
+ export function getApiResponses(
128
+ target: unknown,
129
+ propertyKey: string | symbol,
130
+ ): ApiResponseMetadata[] {
131
+ return Reflect.getMetadata(API_RESPONSE_METADATA_KEY, target as Object, propertyKey) || [];
132
+ }
133
+