@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,234 @@
1
+ import type { SwaggerDocument, SwaggerOptions, SwaggerPathItem } from './types';
2
+ import { ControllerRegistry } from '../controller/controller';
3
+ import { getControllerMetadata, getRouteMetadata } from '../controller/metadata';
4
+ import {
5
+ getApiTags,
6
+ getApiOperation,
7
+ getApiParams,
8
+ getApiBody,
9
+ getApiResponses,
10
+ } from './decorators';
11
+
12
+ /**
13
+ * Swagger 文档生成器
14
+ */
15
+ export class SwaggerGenerator {
16
+ private readonly options: SwaggerOptions;
17
+
18
+ public constructor(options: SwaggerOptions) {
19
+ this.options = options;
20
+ }
21
+
22
+ /**
23
+ * 生成 Swagger 文档
24
+ */
25
+ public generate(): SwaggerDocument {
26
+ const document: SwaggerDocument = {
27
+ openapi: '3.0.0',
28
+ info: {
29
+ title: this.options.info.title,
30
+ version: this.options.info.version,
31
+ description: this.options.info.description,
32
+ contact: this.options.info.contact,
33
+ license: this.options.info.license,
34
+ },
35
+ servers: this.options.servers,
36
+ tags: this.options.tags,
37
+ paths: {},
38
+ };
39
+
40
+ // 从 ControllerRegistry 获取所有控制器
41
+ const controllerRegistry = ControllerRegistry.getInstance();
42
+ const controllers = controllerRegistry.getRegisteredControllers();
43
+
44
+ if (controllers.length === 0) {
45
+ return document;
46
+ }
47
+
48
+ for (const controllerClass of controllers) {
49
+ const controllerMetadata = getControllerMetadata(controllerClass);
50
+ if (!controllerMetadata) {
51
+ console.log(`[SwaggerGenerator] No metadata for controller: ${controllerClass.name}`);
52
+ continue;
53
+ }
54
+
55
+ const basePath = controllerMetadata.path;
56
+ const prototype = controllerClass.prototype;
57
+
58
+ // 获取路由元数据 - 从原型获取
59
+ const routes = getRouteMetadata(prototype);
60
+
61
+ // 如果没有路由,跳过这个控制器
62
+ if (!routes || routes.length === 0) {
63
+ continue;
64
+ }
65
+
66
+ // 获取控制器级别的标签
67
+ const controllerTags = getApiTags(controllerClass);
68
+
69
+ for (const route of routes) {
70
+ // 如果没有 propertyKey,尝试从 handler 函数名或原型中查找
71
+ let propertyKey = route.propertyKey;
72
+ if (!propertyKey && route.handler) {
73
+ // 尝试从 handler 函数名获取
74
+ propertyKey = route.handler.name;
75
+ // 如果还是没有,从原型中查找
76
+ if (!propertyKey || propertyKey === '') {
77
+ const propertyNames = Object.getOwnPropertyNames(prototype);
78
+ for (const key of propertyNames) {
79
+ if (key === 'constructor') continue;
80
+ const descriptor = Object.getOwnPropertyDescriptor(prototype, key);
81
+ if (descriptor && descriptor.value === route.handler) {
82
+ propertyKey = key;
83
+ break;
84
+ }
85
+ }
86
+ }
87
+ }
88
+
89
+ if (!propertyKey) {
90
+ continue;
91
+ }
92
+
93
+ const method = route.method.toLowerCase() as 'get' | 'post' | 'put' | 'delete' | 'patch';
94
+ // 组合基础路径和方法路径
95
+ let methodPath = route.path;
96
+ // 如果方法路径不是以 / 开头,需要添加
97
+ if (methodPath && !methodPath.startsWith('/')) {
98
+ methodPath = '/' + methodPath;
99
+ }
100
+ // 将路径参数格式从 :param 转换为 {param}(OpenAPI 格式)
101
+ const swaggerPath = (basePath + methodPath).replace(/:([^/]+)/g, '{$1}');
102
+ const fullPath = this.normalizePath(swaggerPath);
103
+
104
+ // 获取方法级别的元数据
105
+ const operationMetadata = getApiOperation(prototype, propertyKey);
106
+ const methodTags = getApiTags(prototype, propertyKey);
107
+ const params = getApiParams(prototype, propertyKey);
108
+ const body = getApiBody(prototype, propertyKey);
109
+ const responses = getApiResponses(prototype, propertyKey);
110
+
111
+ // 合并标签
112
+ const tags = [...new Set([...controllerTags, ...methodTags])];
113
+
114
+ const pathItem: SwaggerPathItem = {
115
+ summary: operationMetadata?.summary,
116
+ description: operationMetadata?.description,
117
+ operationId: operationMetadata?.operationId || propertyKey,
118
+ tags: tags.length > 0 ? tags : undefined,
119
+ deprecated: operationMetadata?.deprecated,
120
+ };
121
+
122
+ // 处理参数
123
+ const pathParams: Array<{
124
+ name: string;
125
+ in: 'query' | 'path' | 'header' | 'cookie';
126
+ description?: string;
127
+ required?: boolean;
128
+ schema?: {
129
+ type?: string;
130
+ format?: string;
131
+ enum?: unknown[];
132
+ default?: unknown;
133
+ };
134
+ }> = [];
135
+
136
+ // 自动从路径中提取路径参数
137
+ const pathParamMatches = fullPath.matchAll(/\{([^}]+)\}/g);
138
+ for (const match of pathParamMatches) {
139
+ const paramName = match[1];
140
+ // 检查是否已经有手动定义的参数
141
+ const existingParam = params.find((p) => p.metadata.name === paramName && p.metadata.in === 'path');
142
+ if (!existingParam) {
143
+ // 自动添加路径参数
144
+ pathParams.push({
145
+ name: paramName,
146
+ in: 'path',
147
+ required: true,
148
+ schema: { type: 'string' },
149
+ });
150
+ }
151
+ }
152
+
153
+ // 添加手动定义的参数
154
+ for (const param of params) {
155
+ pathParams.push({
156
+ name: param.metadata.name,
157
+ in: param.metadata.in,
158
+ description: param.metadata.description,
159
+ required: param.metadata.required,
160
+ schema: param.metadata.schema,
161
+ });
162
+ }
163
+
164
+ if (pathParams.length > 0) {
165
+ pathItem.parameters = pathParams;
166
+ }
167
+
168
+ // 处理请求体
169
+ if (body) {
170
+ pathItem.requestBody = {
171
+ description: body.description,
172
+ required: body.required,
173
+ content: {
174
+ 'application/json': {
175
+ schema: body.schema,
176
+ examples: body.examples,
177
+ },
178
+ },
179
+ };
180
+ }
181
+
182
+ // 处理响应
183
+ if (responses.length > 0) {
184
+ pathItem.responses = {};
185
+ for (const response of responses) {
186
+ pathItem.responses[String(response.status)] = {
187
+ description: response.description,
188
+ content: {
189
+ 'application/json': {
190
+ schema: response.schema,
191
+ examples: response.examples,
192
+ },
193
+ },
194
+ };
195
+ }
196
+ } else {
197
+ // 默认响应
198
+ pathItem.responses = {
199
+ '200': {
200
+ description: 'Success',
201
+ },
202
+ };
203
+ }
204
+
205
+ // 初始化路径对象
206
+ if (!document.paths[fullPath]) {
207
+ document.paths[fullPath] = {};
208
+ }
209
+
210
+ document.paths[fullPath][method] = pathItem;
211
+ }
212
+ }
213
+
214
+ return document;
215
+ }
216
+
217
+ /**
218
+ * 规范化路径
219
+ */
220
+ private normalizePath(path: string): string {
221
+ // 移除重复的斜杠
222
+ path = path.replace(/\/+/g, '/');
223
+ // 确保以 / 开头
224
+ if (!path.startsWith('/')) {
225
+ path = '/' + path;
226
+ }
227
+ // 移除末尾的斜杠(除非是根路径)
228
+ if (path.length > 1 && path.endsWith('/')) {
229
+ path = path.slice(0, -1);
230
+ }
231
+ return path;
232
+ }
233
+ }
234
+
@@ -0,0 +1,7 @@
1
+ export * from './types';
2
+ export * from './decorators';
3
+ export * from './generator';
4
+ export * from './swagger-extension';
5
+ export * from './swagger-module';
6
+ export * from './ui';
7
+
@@ -0,0 +1,41 @@
1
+ import type { Container } from '../di/container';
2
+ import type { ApplicationExtension } from '../extensions/types';
3
+ import { SwaggerGenerator } from './generator';
4
+ import type { SwaggerOptions } from './types';
5
+
6
+ /**
7
+ * Swagger 扩展
8
+ */
9
+ export class SwaggerExtension implements ApplicationExtension {
10
+ private readonly options: SwaggerOptions;
11
+ private generator?: SwaggerGenerator;
12
+
13
+ public constructor(options: SwaggerOptions) {
14
+ this.options = options;
15
+ }
16
+
17
+ /**
18
+ * 注册扩展
19
+ */
20
+ public register(_container: Container): void {
21
+ this.generator = new SwaggerGenerator(this.options);
22
+ }
23
+
24
+ /**
25
+ * 获取 Swagger 文档生成器
26
+ */
27
+ public getGenerator(): SwaggerGenerator {
28
+ if (!this.generator) {
29
+ this.generator = new SwaggerGenerator(this.options);
30
+ }
31
+ return this.generator;
32
+ }
33
+
34
+ /**
35
+ * 生成 Swagger JSON
36
+ */
37
+ public generateJSON(): string {
38
+ return JSON.stringify(this.getGenerator().generate(), null, 2);
39
+ }
40
+ }
41
+
@@ -0,0 +1,83 @@
1
+ import { Module, MODULE_METADATA_KEY } from '../di/module';
2
+ import { SwaggerExtension } from './swagger-extension';
3
+ import { createSwaggerUIMiddleware } from './ui';
4
+ import type { SwaggerOptions } from './types';
5
+
6
+ /**
7
+ * Swagger 模块配置
8
+ */
9
+ export interface SwaggerModuleOptions extends SwaggerOptions {
10
+ /**
11
+ * Swagger UI 路径
12
+ * @default '/swagger'
13
+ */
14
+ uiPath?: string;
15
+ /**
16
+ * Swagger JSON 路径
17
+ * @default '/swagger.json'
18
+ */
19
+ jsonPath?: string;
20
+ /**
21
+ * Swagger UI 标题
22
+ */
23
+ uiTitle?: string;
24
+ /**
25
+ * 是否启用 Swagger UI
26
+ * @default true
27
+ */
28
+ enableUI?: boolean;
29
+ }
30
+
31
+ /**
32
+ * Swagger 模块
33
+ * 提供 API 文档生成和 Swagger UI
34
+ */
35
+ @Module({
36
+ extensions: [
37
+ // 将在运行时根据配置创建
38
+ ],
39
+ middlewares: [
40
+ // 将在运行时根据配置创建
41
+ ],
42
+ })
43
+ export class SwaggerModule {
44
+ /**
45
+ * 创建 Swagger 模块
46
+ * @param options - 模块配置
47
+ */
48
+ public static forRoot(options: SwaggerModuleOptions): typeof SwaggerModule {
49
+ const extensions: any[] = [];
50
+ const middlewares: any[] = [];
51
+
52
+ // 创建 Swagger 扩展
53
+ const swaggerExtension = new SwaggerExtension({
54
+ info: options.info,
55
+ servers: options.servers,
56
+ basePath: options.basePath,
57
+ tags: options.tags,
58
+ });
59
+ extensions.push(swaggerExtension);
60
+
61
+ // 如果启用 UI,添加中间件
62
+ if (options.enableUI !== false) {
63
+ const uiMiddleware = createSwaggerUIMiddleware(swaggerExtension, {
64
+ uiPath: options.uiPath || '/swagger',
65
+ jsonPath: options.jsonPath || '/swagger.json',
66
+ title: options.uiTitle || options.info.title || 'API Documentation',
67
+ });
68
+ middlewares.push(uiMiddleware);
69
+ }
70
+
71
+ // 动态更新模块元数据
72
+ const existingMetadata = Reflect.getMetadata(MODULE_METADATA_KEY, SwaggerModule) || {};
73
+ const metadata = {
74
+ ...existingMetadata,
75
+ extensions,
76
+ middlewares,
77
+ };
78
+ Reflect.defineMetadata(MODULE_METADATA_KEY, metadata, SwaggerModule);
79
+
80
+ return SwaggerModule;
81
+ }
82
+ }
83
+
@@ -0,0 +1,188 @@
1
+ /**
2
+ * Swagger/OpenAPI 类型定义
3
+ */
4
+
5
+ /**
6
+ * Swagger 信息配置
7
+ */
8
+ export interface SwaggerInfo {
9
+ title: string;
10
+ version: string;
11
+ description?: string;
12
+ contact?: {
13
+ name?: string;
14
+ email?: string;
15
+ url?: string;
16
+ };
17
+ license?: {
18
+ name: string;
19
+ url?: string;
20
+ };
21
+ }
22
+
23
+ /**
24
+ * Swagger 配置
25
+ */
26
+ export interface SwaggerOptions {
27
+ info: SwaggerInfo;
28
+ servers?: Array<{
29
+ url: string;
30
+ description?: string;
31
+ }>;
32
+ basePath?: string;
33
+ tags?: Array<{
34
+ name: string;
35
+ description?: string;
36
+ }>;
37
+ }
38
+
39
+ /**
40
+ * API 操作元数据
41
+ */
42
+ export interface ApiOperationMetadata {
43
+ summary?: string;
44
+ description?: string;
45
+ operationId?: string;
46
+ tags?: string[];
47
+ deprecated?: boolean;
48
+ }
49
+
50
+ /**
51
+ * API 参数元数据
52
+ */
53
+ export interface ApiParamMetadata {
54
+ name: string;
55
+ description?: string;
56
+ required?: boolean;
57
+ schema?: {
58
+ type?: string;
59
+ format?: string;
60
+ enum?: unknown[];
61
+ default?: unknown;
62
+ };
63
+ in: 'query' | 'path' | 'header' | 'cookie';
64
+ }
65
+
66
+ /**
67
+ * API 请求体元数据
68
+ */
69
+ export interface ApiBodyMetadata {
70
+ description?: string;
71
+ required?: boolean;
72
+ schema?: {
73
+ type?: string;
74
+ properties?: Record<string, unknown>;
75
+ required?: string[];
76
+ };
77
+ examples?: Record<string, unknown>;
78
+ }
79
+
80
+ /**
81
+ * API 响应元数据
82
+ */
83
+ export interface ApiResponseMetadata {
84
+ status: number;
85
+ description?: string;
86
+ schema?: {
87
+ type?: string;
88
+ properties?: Record<string, unknown>;
89
+ };
90
+ examples?: Record<string, unknown>;
91
+ }
92
+
93
+ /**
94
+ * API 标签元数据
95
+ */
96
+ export interface ApiTagMetadata {
97
+ name: string;
98
+ description?: string;
99
+ }
100
+
101
+ /**
102
+ * Swagger 路径项
103
+ */
104
+ export interface SwaggerPathItem {
105
+ summary?: string;
106
+ description?: string;
107
+ operationId?: string;
108
+ tags?: string[];
109
+ parameters?: Array<{
110
+ name: string;
111
+ in: 'query' | 'path' | 'header' | 'cookie';
112
+ description?: string;
113
+ required?: boolean;
114
+ schema?: {
115
+ type?: string;
116
+ format?: string;
117
+ enum?: unknown[];
118
+ default?: unknown;
119
+ };
120
+ }>;
121
+ requestBody?: {
122
+ description?: string;
123
+ required?: boolean;
124
+ content?: {
125
+ 'application/json'?: {
126
+ schema?: {
127
+ type?: string;
128
+ properties?: Record<string, unknown>;
129
+ required?: string[];
130
+ };
131
+ examples?: Record<string, unknown>;
132
+ };
133
+ };
134
+ };
135
+ responses?: Record<
136
+ string,
137
+ {
138
+ description?: string;
139
+ content?: {
140
+ 'application/json'?: {
141
+ schema?: {
142
+ type?: string;
143
+ properties?: Record<string, unknown>;
144
+ };
145
+ examples?: Record<string, unknown>;
146
+ };
147
+ };
148
+ }
149
+ >;
150
+ deprecated?: boolean;
151
+ }
152
+
153
+ /**
154
+ * Swagger 文档结构
155
+ */
156
+ export interface SwaggerDocument {
157
+ openapi: string;
158
+ info: {
159
+ title: string;
160
+ version: string;
161
+ description?: string;
162
+ contact?: {
163
+ name?: string;
164
+ email?: string;
165
+ url?: string;
166
+ };
167
+ license?: {
168
+ name: string;
169
+ url?: string;
170
+ };
171
+ };
172
+ servers?: Array<{
173
+ url: string;
174
+ description?: string;
175
+ }>;
176
+ tags?: Array<{
177
+ name: string;
178
+ description?: string;
179
+ }>;
180
+ paths: Record<string, {
181
+ get?: SwaggerPathItem;
182
+ post?: SwaggerPathItem;
183
+ put?: SwaggerPathItem;
184
+ delete?: SwaggerPathItem;
185
+ patch?: SwaggerPathItem;
186
+ }>;
187
+ }
188
+
@@ -0,0 +1,98 @@
1
+ import type { Context } from '../core/context';
2
+ import type { Middleware, NextFunction } from '../middleware';
3
+ import { SwaggerExtension } from './swagger-extension';
4
+
5
+ /**
6
+ * Swagger UI HTML 模板
7
+ */
8
+ const SWAGGER_UI_HTML = (jsonUrl: string, title: string) => `<!DOCTYPE html>
9
+ <html lang="en">
10
+ <head>
11
+ <meta charset="UTF-8">
12
+ <meta name="viewport" content="width=device-width, initial-scale=1.0">
13
+ <title>${title} - Swagger UI</title>
14
+ <link rel="stylesheet" type="text/css" href="https://unpkg.com/swagger-ui-dist@5.9.0/swagger-ui.css" />
15
+ <style>
16
+ html {
17
+ box-sizing: border-box;
18
+ overflow: -moz-scrollbars-vertical;
19
+ overflow-y: scroll;
20
+ }
21
+ *, *:before, *:after {
22
+ box-sizing: inherit;
23
+ }
24
+ body {
25
+ margin:0;
26
+ background: #fafafa;
27
+ }
28
+ </style>
29
+ </head>
30
+ <body>
31
+ <div id="swagger-ui"></div>
32
+ <script src="https://unpkg.com/swagger-ui-dist@5.9.0/swagger-ui-bundle.js"></script>
33
+ <script src="https://unpkg.com/swagger-ui-dist@5.9.0/swagger-ui-standalone-preset.js"></script>
34
+ <script>
35
+ window.onload = function() {
36
+ const ui = SwaggerUIBundle({
37
+ url: "${jsonUrl}",
38
+ dom_id: '#swagger-ui',
39
+ deepLinking: true,
40
+ presets: [
41
+ SwaggerUIBundle.presets.apis,
42
+ SwaggerUIStandalonePreset
43
+ ],
44
+ plugins: [
45
+ SwaggerUIBundle.plugins.DownloadUrl
46
+ ],
47
+ layout: "StandaloneLayout"
48
+ });
49
+ };
50
+ </script>
51
+ </body>
52
+ </html>`;
53
+
54
+ /**
55
+ * 创建 Swagger UI 中间件
56
+ * @param extension - Swagger 扩展实例
57
+ * @param options - 配置选项
58
+ */
59
+ export function createSwaggerUIMiddleware(
60
+ extension: SwaggerExtension,
61
+ options: {
62
+ uiPath?: string;
63
+ jsonPath?: string;
64
+ title?: string;
65
+ } = {},
66
+ ): Middleware {
67
+ const uiPath = options.uiPath || '/swagger';
68
+ const jsonPath = options.jsonPath || '/swagger.json';
69
+ const title = options.title || 'API Documentation';
70
+
71
+ return async (ctx: Context, next: NextFunction): Promise<Response> => {
72
+ const url = new URL(ctx.request.url);
73
+ const pathname = url.pathname;
74
+
75
+ // Swagger UI 页面
76
+ if (pathname === uiPath || pathname === `${uiPath}/`) {
77
+ const html = SWAGGER_UI_HTML(jsonPath, title);
78
+ return new Response(html, {
79
+ headers: {
80
+ 'Content-Type': 'text/html; charset=utf-8',
81
+ },
82
+ });
83
+ }
84
+
85
+ // Swagger JSON
86
+ if (pathname === jsonPath) {
87
+ const json = extension.generateJSON();
88
+ return new Response(json, {
89
+ headers: {
90
+ 'Content-Type': 'application/json; charset=utf-8',
91
+ },
92
+ });
93
+ }
94
+
95
+ return await next();
96
+ };
97
+ }
98
+