@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
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@dangao/bun-server",
3
- "version": "1.0.0",
3
+ "version": "1.0.3",
4
4
  "type": "module",
5
5
  "main": "./dist/index.js",
6
6
  "types": "./dist/index.d.ts",
@@ -26,7 +26,9 @@
26
26
  "files": [
27
27
  "dist",
28
28
  "readme.md",
29
- "LICENSE"
29
+ "LICENSE",
30
+ "src",
31
+ "tests"
30
32
  ],
31
33
  "author": "dangaogit",
32
34
  "license": "MIT",
package/readme.md CHANGED
@@ -14,6 +14,7 @@
14
14
  - [Benchmark Suite](#benchmark-suite)
15
15
  - [Docs & Localization](#docs--localization)
16
16
  - [Roadmap](#roadmap)
17
+ - [AI-Assisted Development](#ai-assisted-development)
17
18
  - [Engineering Guidelines](#engineering-guidelines)
18
19
  - [Contributing](#contributing)
19
20
  - [License](#license)
@@ -29,6 +30,9 @@
29
30
  provider that scales from MVP to enterprise.
30
31
  - **Well-tested**: unit, integration, stress and benchmark suites ship with the
31
32
  repo.
33
+ - **AI-friendly**: source code and tests are included in the npm package,
34
+ enabling AI tools (like Cursor) to provide better code analysis, suggestions,
35
+ and understanding of the framework internals.
32
36
 
33
37
  ## Features
34
38
 
@@ -70,7 +74,6 @@ bun install
70
74
  ### Hello World
71
75
 
72
76
  ```ts
73
- import "reflect-metadata";
74
77
  import { Application, Controller, GET, Injectable } from "@dangao/bun-server";
75
78
 
76
79
  @Injectable()
@@ -108,6 +111,127 @@ bun --cwd=packages/@dangao/bun-server run bench:di
108
111
  > Running `bun test` from the repo root fails because Bun only scans the current
109
112
  > workspace. Use the commands above or `cd packages/@dangao/bun-server` first.
110
113
 
114
+ ### Advanced Example: Interface + Symbol + Module
115
+
116
+ This example demonstrates using interfaces with Symbol tokens and module-based
117
+ dependency injection:
118
+
119
+ ```ts
120
+ import {
121
+ Application,
122
+ Body,
123
+ CONFIG_SERVICE_TOKEN,
124
+ ConfigModule,
125
+ ConfigService,
126
+ Controller,
127
+ GET,
128
+ Inject,
129
+ Injectable,
130
+ Module,
131
+ Param,
132
+ POST,
133
+ } from "@dangao/bun-server";
134
+
135
+ // Define service interface
136
+ interface UserService {
137
+ find(id: string): Promise<{ id: string; name: string } | undefined>;
138
+ create(name: string): { id: string; name: string };
139
+ }
140
+
141
+ // Create Symbol token for DI
142
+ const UserService = Symbol("UserService");
143
+
144
+ // Implement the interface
145
+ @Injectable()
146
+ class UserServiceImpl implements UserService {
147
+ private readonly users = new Map<string, { id: string; name: string }>([
148
+ ["1", { id: "1", name: "Alice" }],
149
+ ]);
150
+
151
+ public async find(id: string) {
152
+ return this.users.get(id);
153
+ }
154
+
155
+ public create(name: string) {
156
+ const id = String(this.users.size + 1);
157
+ const user = { id, name };
158
+ this.users.set(id, user);
159
+ return user;
160
+ }
161
+ }
162
+
163
+ @Controller("/api/users")
164
+ class UserController {
165
+ public constructor(
166
+ private readonly service: UserService,
167
+ @Inject(CONFIG_SERVICE_TOKEN) private readonly config: ConfigService,
168
+ ) {}
169
+
170
+ @GET("/:id")
171
+ public async getUser(@Param("id") id: string) {
172
+ const user = await this.service.find(id);
173
+ if (!user) {
174
+ return { error: "Not Found" };
175
+ }
176
+ return user;
177
+ }
178
+
179
+ @POST("/")
180
+ public createUser(@Body("name") name: string) {
181
+ return this.service.create(name);
182
+ }
183
+ }
184
+
185
+ // Define module with Symbol-based provider
186
+ @Module({
187
+ controllers: [UserController],
188
+ providers: [
189
+ {
190
+ provide: UserService,
191
+ useClass: UserServiceImpl,
192
+ },
193
+ ],
194
+ exports: [UserService],
195
+ })
196
+ class UserModule {}
197
+
198
+ // Configure modules
199
+ ConfigModule.forRoot({
200
+ defaultConfig: {
201
+ app: {
202
+ name: "Advanced App",
203
+ port: 3100,
204
+ },
205
+ },
206
+ });
207
+
208
+ // Register module and start application
209
+ @Module({
210
+ imports: [ConfigModule],
211
+ controllers: [UserController],
212
+ providers: [
213
+ {
214
+ provide: UserService,
215
+ useClass: UserServiceImpl,
216
+ },
217
+ ],
218
+ })
219
+ class AppModule {}
220
+
221
+ const app = new Application({ port: 3100 });
222
+ app.registerModule(AppModule);
223
+ app.listen();
224
+ ```
225
+
226
+ **Key points:**
227
+
228
+ - **Interface-based design**: Define contracts with TypeScript interfaces
229
+ - **Symbol tokens**: Use `Symbol()` for type-safe dependency injection tokens
230
+ - **Module providers**: Register providers using
231
+ `provide: Symbol, useClass: Implementation`
232
+ - **Type-safe injection**: Inject services using `@Inject(Symbol)` with
233
+ interface types
234
+
111
235
  ## Examples & Extensions
112
236
 
113
237
  - `examples/basic-app.ts`: minimal DI + Logger + Middleware showcase.
@@ -137,7 +261,9 @@ Or use `bun run bench*` scripts for convenience.
137
261
  ## Docs & Localization
138
262
 
139
263
  - **English** (default): `docs/api.md`, `docs/guide.md`,
140
- `docs/best-practices.md`, `docs/migration.md`, `docs/extensions.md`.
264
+ `docs/best-practices.md`, `docs/migration.md`, `docs/extensions.md`,
265
+ `docs/deployment.md`, `docs/performance.md`, `docs/troubleshooting.md`,
266
+ `docs/error-handling.md`.
141
267
  - **Chinese**: mirrored under `docs/zh/`. If something is missing, please fall
142
268
  back to the English source.
143
269
 
@@ -146,6 +272,41 @@ Or use `bun run bench*` scripts for convenience.
146
272
  Detailed milestones and history are tracked in
147
273
  [`.roadmap/v0.3.0.md`](./.roadmap/v0.3.0.md).
148
274
 
275
+ ## AI-Assisted Development
276
+
277
+ Bun Server is designed to work seamlessly with AI coding assistants like Cursor,
278
+ GitHub Copilot, and others. The framework includes source code and tests in the
279
+ npm package distribution, enabling AI tools to:
280
+
281
+ - **Understand framework internals**: AI can analyze the actual implementation
282
+ code, not just type definitions, providing more accurate suggestions.
283
+ - **Provide context-aware help**: When you ask about framework features, AI can
284
+ reference the actual source code to give precise answers.
285
+ - **Suggest best practices**: AI can learn from the framework's patterns and
286
+ suggest similar approaches in your code.
287
+ - **Debug more effectively**: AI can trace through the framework code to help
288
+ diagnose issues.
289
+
290
+ ### Best Practices for AI-Assisted Development
291
+
292
+ 1. **Reference framework source**: When working with Bun Server, AI tools can
293
+ access the source code at `node_modules/@dangao/bun-server/src/` to
294
+ understand implementation details.
295
+
296
+ 2. **Use type hints**: The framework provides comprehensive TypeScript types.
297
+ Leverage these in your code to help AI understand your intent better.
298
+
299
+ 3. **Follow framework patterns**: The included source code serves as a reference
300
+ for framework patterns. Ask AI to suggest code that follows similar patterns.
301
+
302
+ 4. **Leverage test examples**: The included test files demonstrate usage
303
+ patterns and edge cases. Reference these when asking AI for implementation
304
+ help.
305
+
306
+ 5. **Ask specific questions**: Since AI can access the framework source, you can
307
+ ask specific questions like "How does the DI container resolve dependencies?"
308
+ and get accurate answers based on the actual code.
309
+
149
310
  ## Engineering Guidelines
150
311
 
151
312
  - Comments & log messages **must be in English** to keep the codebase
@@ -0,0 +1,148 @@
1
+ import { Controller, Query, Body } from '../controller';
2
+ import { GET, POST } from '../router/decorators';
3
+ import { Injectable, Inject } from '../di';
4
+ import { ResponseBuilder } from '../request';
5
+ import { OAuth2Service } from './oauth2';
6
+ import { Auth } from './decorators';
7
+ import type {
8
+ OAuth2AuthorizationRequest,
9
+ OAuth2TokenRequest,
10
+ OAuth2TokenResponse,
11
+ } from './types';
12
+
13
+ /**
14
+ * OAuth2 令牌 Token
15
+ */
16
+ export const OAUTH2_SERVICE_TOKEN = Symbol('OAUTH2_SERVICE');
17
+ export const JWT_UTIL_TOKEN = Symbol('JWT_UTIL');
18
+
19
+ /**
20
+ * OAuth2 控制器
21
+ */
22
+ @Controller('/oauth2')
23
+ @Injectable()
24
+ export class OAuth2Controller {
25
+ public constructor(
26
+ @Inject(OAUTH2_SERVICE_TOKEN) private readonly oauth2Service: OAuth2Service,
27
+ ) {}
28
+
29
+ /**
30
+ * 授权端点
31
+ * GET /oauth2/authorize?client_id=...&redirect_uri=...&response_type=code&state=...
32
+ */
33
+ @GET('/authorize')
34
+ public authorize(
35
+ @Query('client_id') clientId: string | null,
36
+ @Query('redirect_uri') redirectUri: string | null,
37
+ @Query('response_type') responseType: string | null,
38
+ @Query('state') state?: string | null,
39
+ @Query('scope') scope?: string | null,
40
+ ) {
41
+ // 验证必需参数
42
+ if (!clientId || !redirectUri) {
43
+ throw new Error('Missing required parameters: client_id and redirect_uri are required');
44
+ }
45
+
46
+ if (responseType !== 'code') {
47
+ throw new Error(`Unsupported response_type: ${responseType}. Only 'code' is supported.`);
48
+ }
49
+
50
+ const request: OAuth2AuthorizationRequest = {
51
+ clientId,
52
+ redirectUri,
53
+ responseType: 'code',
54
+ scope: scope || undefined,
55
+ state: state || undefined,
56
+ };
57
+
58
+ // 验证请求
59
+ const validation = this.oauth2Service.validateAuthorizationRequest(request);
60
+ if (!validation.valid) {
61
+ throw new Error(`Invalid authorization request: ${validation.error}`);
62
+ }
63
+
64
+ // 这里应该显示授权页面,让用户登录并授权
65
+ // 为了简化,我们假设用户已经登录,使用默认用户 ID
66
+ const userId = 'user-1';
67
+
68
+ // 生成授权码
69
+ const code = this.oauth2Service.generateAuthorizationCode(
70
+ request.clientId,
71
+ request.redirectUri,
72
+ userId,
73
+ request.scope,
74
+ );
75
+
76
+ // 构建重定向 URL
77
+ const redirectUrl = new URL(request.redirectUri);
78
+ redirectUrl.searchParams.set('code', code);
79
+ if (request.state) {
80
+ redirectUrl.searchParams.set('state', request.state);
81
+ }
82
+
83
+ // 返回重定向 URL(框架会处理)
84
+ // 注意:实际实现中可能需要使用 ResponseBuilder.redirect()
85
+ return ResponseBuilder.redirect(redirectUrl.toString());
86
+ }
87
+
88
+ /**
89
+ * 令牌端点
90
+ * POST /oauth2/token
91
+ */
92
+ @POST('/token')
93
+ public async token(@Body() body: Record<string, string>): Promise<OAuth2TokenResponse | any> {
94
+ const request: OAuth2TokenRequest = {
95
+ code: body.code || '',
96
+ clientId: body.client_id || '',
97
+ clientSecret: body.client_secret || '',
98
+ redirectUri: body.redirect_uri || '',
99
+ grantType: (body.grant_type as 'authorization_code' | 'refresh_token') || 'authorization_code',
100
+ refreshToken: body.refresh_token,
101
+ };
102
+
103
+ if (request.grantType === 'authorization_code') {
104
+ const tokenResponse = await this.oauth2Service.exchangeCodeForToken(request);
105
+ if (!tokenResponse) {
106
+ return {
107
+ error: 'invalid_grant',
108
+ error_description: 'Invalid authorization code',
109
+ };
110
+ }
111
+ return tokenResponse;
112
+ }
113
+
114
+ if (request.grantType === 'refresh_token' && request.refreshToken) {
115
+ const tokenResponse = await this.oauth2Service.refreshToken(request.refreshToken);
116
+ if (!tokenResponse) {
117
+ return {
118
+ error: 'invalid_grant',
119
+ error_description: 'Invalid refresh token',
120
+ };
121
+ }
122
+ return tokenResponse;
123
+ }
124
+
125
+ return {
126
+ error: 'unsupported_grant_type',
127
+ error_description: 'Unsupported grant type',
128
+ };
129
+ }
130
+
131
+ /**
132
+ * 用户信息端点
133
+ * GET /oauth2/userinfo
134
+ * 注意:此端点需要认证,应该通过认证中间件保护
135
+ */
136
+ @GET('/userinfo')
137
+ @Auth()
138
+ public userinfo() {
139
+ // 用户信息应该通过认证中间件注入到 Context
140
+ // 这里简化处理,返回占位符
141
+ return {
142
+ sub: 'user-1',
143
+ username: 'alice',
144
+ roles: ['user'],
145
+ };
146
+ }
147
+ }
148
+
@@ -0,0 +1,81 @@
1
+ import 'reflect-metadata';
2
+
3
+ /**
4
+ * 认证元数据键
5
+ */
6
+ const AUTH_METADATA_KEY = Symbol('@dangao/bun-server:auth');
7
+
8
+ /**
9
+ * 认证配置
10
+ */
11
+ export interface AuthConfig {
12
+ /**
13
+ * 是否需要认证
14
+ * @default true
15
+ */
16
+ required?: boolean;
17
+ /**
18
+ * 允许的角色列表
19
+ */
20
+ roles?: string[];
21
+ /**
22
+ * 是否允许匿名访问
23
+ * @default false
24
+ */
25
+ allowAnonymous?: boolean;
26
+ }
27
+
28
+ /**
29
+ * 认证装饰器
30
+ * 用于标记需要认证的路由
31
+ */
32
+ export function Auth(config: AuthConfig = {}): MethodDecorator {
33
+ return (target: Object, propertyKey: string | symbol) => {
34
+ const metadata = Reflect.getMetadata(AUTH_METADATA_KEY, target) || {};
35
+ metadata[propertyKey] = {
36
+ required: config.required !== false,
37
+ roles: config.roles || [],
38
+ allowAnonymous: config.allowAnonymous || false,
39
+ };
40
+ Reflect.defineMetadata(AUTH_METADATA_KEY, metadata, target);
41
+ };
42
+ }
43
+
44
+ /**
45
+ * 获取认证元数据
46
+ */
47
+ export function getAuthMetadata(
48
+ target: Object,
49
+ propertyKey: string | symbol,
50
+ ): AuthConfig | undefined {
51
+ const metadata = Reflect.getMetadata(AUTH_METADATA_KEY, target);
52
+ return metadata?.[propertyKey];
53
+ }
54
+
55
+ /**
56
+ * 检查是否需要认证
57
+ */
58
+ export function requiresAuth(target: Object, propertyKey: string | symbol): boolean {
59
+ const config = getAuthMetadata(target, propertyKey);
60
+ // 如果没有配置,默认不需要认证;只有显式标注 @Auth 时才进入认证流程
61
+ if (!config) {
62
+ return false;
63
+ }
64
+ return config.required !== false;
65
+ }
66
+
67
+ /**
68
+ * 检查角色权限
69
+ */
70
+ export function checkRoles(
71
+ target: Object,
72
+ propertyKey: string | symbol,
73
+ userRoles: string[] = [],
74
+ ): boolean {
75
+ const config = getAuthMetadata(target, propertyKey);
76
+ if (!config?.roles || config.roles.length === 0) {
77
+ return true;
78
+ }
79
+ return config.roles.some((role) => userRoles.includes(role));
80
+ }
81
+
@@ -0,0 +1,12 @@
1
+ // 核心实现(保留)
2
+ export * from './types';
3
+ export * from './jwt';
4
+ export * from './oauth2';
5
+ export * from './decorators';
6
+ export * from './controller';
7
+
8
+ // 已废弃,请使用 SecurityModule
9
+ // export * from './middleware';
10
+ // export { AuthModule } from './auth-module';
11
+ // export { AuthExtension } from './auth-extension';
12
+
@@ -0,0 +1,169 @@
1
+ import type { JWTConfig, JWTPayload } from './types';
2
+
3
+ /**
4
+ * JWT 工具类
5
+ */
6
+ export class JWTUtil {
7
+ private readonly config: Required<JWTConfig>;
8
+
9
+ public constructor(config: JWTConfig) {
10
+ this.config = {
11
+ secret: config.secret,
12
+ accessTokenExpiresIn: config.accessTokenExpiresIn ?? 3600,
13
+ refreshTokenExpiresIn: config.refreshTokenExpiresIn ?? 86400 * 7,
14
+ algorithm: config.algorithm ?? 'HS256',
15
+ };
16
+ }
17
+
18
+ /**
19
+ * 生成访问令牌
20
+ * @param payload - JWT 载荷
21
+ * @returns 访问令牌
22
+ */
23
+ public generateAccessToken(payload: Omit<JWTPayload, 'exp' | 'iat'>): string {
24
+ const now = Math.floor(Date.now() / 1000);
25
+ const tokenPayload: JWTPayload = {
26
+ ...payload,
27
+ sub: payload.sub as string,
28
+ iat: now,
29
+ exp: now + this.config.accessTokenExpiresIn,
30
+ };
31
+ return this.sign(tokenPayload);
32
+ }
33
+
34
+ /**
35
+ * 生成刷新令牌
36
+ * @param payload - JWT 载荷
37
+ * @returns 刷新令牌
38
+ */
39
+ public generateRefreshToken(payload: Omit<JWTPayload, 'exp' | 'iat'>): string {
40
+ const now = Math.floor(Date.now() / 1000);
41
+ const tokenPayload: JWTPayload = {
42
+ ...payload,
43
+ sub: payload.sub as string,
44
+ iat: now,
45
+ exp: now + this.config.refreshTokenExpiresIn,
46
+ };
47
+ return this.sign(tokenPayload);
48
+ }
49
+
50
+ /**
51
+ * 验证令牌
52
+ * @param token - JWT 令牌
53
+ * @returns 载荷或 null(如果无效)
54
+ */
55
+ public verify(token: string): JWTPayload | null {
56
+ try {
57
+ const parts = token.split('.');
58
+ if (parts.length !== 3) {
59
+ return null;
60
+ }
61
+
62
+ // 解析载荷
63
+ const payload = JSON.parse(
64
+ Buffer.from(parts[1], 'base64url').toString('utf-8'),
65
+ ) as JWTPayload;
66
+
67
+ // 验证签名
68
+ const signature = this.signature(parts[0] + '.' + parts[1]);
69
+ if (signature !== parts[2]) {
70
+ return null;
71
+ }
72
+
73
+ // 验证过期时间
74
+ if (payload.exp && payload.exp < Math.floor(Date.now() / 1000)) {
75
+ return null;
76
+ }
77
+
78
+ return payload;
79
+ } catch {
80
+ return null;
81
+ }
82
+ }
83
+
84
+ /**
85
+ * 签名 JWT
86
+ */
87
+ private sign(payload: JWTPayload): string {
88
+ const header = {
89
+ alg: this.config.algorithm,
90
+ typ: 'JWT',
91
+ };
92
+
93
+ const encodedHeader = this.base64UrlEncode(JSON.stringify(header));
94
+ const encodedPayload = this.base64UrlEncode(JSON.stringify(payload));
95
+ const signature = this.signature(encodedHeader + '.' + encodedPayload);
96
+
97
+ return `${encodedHeader}.${encodedPayload}.${signature}`;
98
+ }
99
+
100
+ /**
101
+ * 生成签名
102
+ */
103
+ private signature(data: string): string {
104
+ // 使用 Bun 的 CryptoHasher
105
+ const encoder = new TextEncoder();
106
+ const keyData = encoder.encode(this.config.secret);
107
+ const messageData = encoder.encode(data);
108
+
109
+ // 使用 HMAC-SHA256
110
+ const hash = this.hmacSha256(keyData, messageData);
111
+ return this.base64UrlEncode(Buffer.from(hash).toString('base64'));
112
+ }
113
+
114
+ /**
115
+ * HMAC-SHA256 实现
116
+ */
117
+ private hmacSha256(key: Uint8Array, data: Uint8Array): Uint8Array {
118
+ const blockSize = 64;
119
+ let keyBuffer: Uint8Array;
120
+
121
+ if (key.length > blockSize) {
122
+ // 如果密钥长度超过块大小,先哈希
123
+ const hasher = new Bun.CryptoHasher('sha256');
124
+ hasher.update(key);
125
+ keyBuffer = new Uint8Array(hasher.digest());
126
+ } else {
127
+ keyBuffer = new Uint8Array(blockSize);
128
+ keyBuffer.set(key);
129
+ }
130
+
131
+ // 创建 o_key_pad 和 i_key_pad
132
+ const oKeyPad = new Uint8Array(blockSize);
133
+ const iKeyPad = new Uint8Array(blockSize);
134
+
135
+ for (let i = 0; i < blockSize; i++) {
136
+ oKeyPad[i] = keyBuffer[i] ^ 0x5c;
137
+ iKeyPad[i] = keyBuffer[i] ^ 0x36;
138
+ }
139
+
140
+ // 计算 inner hash
141
+ const innerData = new Uint8Array(iKeyPad.length + data.length);
142
+ innerData.set(iKeyPad);
143
+ innerData.set(data, iKeyPad.length);
144
+ const innerHasher = new Bun.CryptoHasher('sha256');
145
+ innerHasher.update(innerData);
146
+ const innerHash = new Uint8Array(innerHasher.digest());
147
+
148
+ // 计算 outer hash
149
+ const outerData = new Uint8Array(oKeyPad.length + innerHash.length);
150
+ outerData.set(oKeyPad);
151
+ outerData.set(innerHash, oKeyPad.length);
152
+ const outerHasher = new Bun.CryptoHasher('sha256');
153
+ outerHasher.update(outerData);
154
+
155
+ return new Uint8Array(outerHasher.digest());
156
+ }
157
+
158
+ /**
159
+ * Base64URL 编码
160
+ */
161
+ private base64UrlEncode(str: string): string {
162
+ return Buffer.from(str)
163
+ .toString('base64')
164
+ .replace(/\+/g, '-')
165
+ .replace(/\//g, '_')
166
+ .replace(/=/g, '');
167
+ }
168
+ }
169
+