@dangao/bun-server 1.7.0 → 1.8.0

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 (144) hide show
  1. package/README.md +196 -19
  2. package/dist/cache/cache-module.d.ts +18 -0
  3. package/dist/cache/cache-module.d.ts.map +1 -1
  4. package/dist/cache/index.d.ts +3 -1
  5. package/dist/cache/index.d.ts.map +1 -1
  6. package/dist/cache/interceptors.d.ts +41 -0
  7. package/dist/cache/interceptors.d.ts.map +1 -0
  8. package/dist/cache/service-proxy.d.ts +62 -0
  9. package/dist/cache/service-proxy.d.ts.map +1 -0
  10. package/dist/controller/controller.d.ts +8 -0
  11. package/dist/controller/controller.d.ts.map +1 -1
  12. package/dist/core/application.d.ts +5 -0
  13. package/dist/core/application.d.ts.map +1 -1
  14. package/dist/di/container.d.ts +18 -1
  15. package/dist/di/container.d.ts.map +1 -1
  16. package/dist/di/decorators.d.ts +37 -0
  17. package/dist/di/decorators.d.ts.map +1 -1
  18. package/dist/di/index.d.ts +2 -2
  19. package/dist/di/index.d.ts.map +1 -1
  20. package/dist/di/module-registry.d.ts +17 -0
  21. package/dist/di/module-registry.d.ts.map +1 -1
  22. package/dist/di/types.d.ts +22 -0
  23. package/dist/di/types.d.ts.map +1 -1
  24. package/dist/events/decorators.d.ts +52 -0
  25. package/dist/events/decorators.d.ts.map +1 -0
  26. package/dist/events/event-module.d.ts +97 -0
  27. package/dist/events/event-module.d.ts.map +1 -0
  28. package/dist/events/index.d.ts +5 -0
  29. package/dist/events/index.d.ts.map +1 -0
  30. package/dist/events/service.d.ts +76 -0
  31. package/dist/events/service.d.ts.map +1 -0
  32. package/dist/events/types.d.ts +184 -0
  33. package/dist/events/types.d.ts.map +1 -0
  34. package/dist/index.d.ts +6 -4
  35. package/dist/index.d.ts.map +1 -1
  36. package/dist/index.js +4641 -2840
  37. package/dist/security/filter.d.ts +23 -0
  38. package/dist/security/filter.d.ts.map +1 -1
  39. package/dist/security/guards/builtin/auth-guard.d.ts +44 -0
  40. package/dist/security/guards/builtin/auth-guard.d.ts.map +1 -0
  41. package/dist/security/guards/builtin/index.d.ts +3 -0
  42. package/dist/security/guards/builtin/index.d.ts.map +1 -0
  43. package/dist/security/guards/builtin/roles-guard.d.ts +66 -0
  44. package/dist/security/guards/builtin/roles-guard.d.ts.map +1 -0
  45. package/dist/security/guards/decorators.d.ts +50 -0
  46. package/dist/security/guards/decorators.d.ts.map +1 -0
  47. package/dist/security/guards/execution-context.d.ts +56 -0
  48. package/dist/security/guards/execution-context.d.ts.map +1 -0
  49. package/dist/security/guards/guard-registry.d.ts +67 -0
  50. package/dist/security/guards/guard-registry.d.ts.map +1 -0
  51. package/dist/security/guards/index.d.ts +7 -0
  52. package/dist/security/guards/index.d.ts.map +1 -0
  53. package/dist/security/guards/reflector.d.ts +57 -0
  54. package/dist/security/guards/reflector.d.ts.map +1 -0
  55. package/dist/security/guards/types.d.ts +126 -0
  56. package/dist/security/guards/types.d.ts.map +1 -0
  57. package/dist/security/index.d.ts +1 -0
  58. package/dist/security/index.d.ts.map +1 -1
  59. package/dist/security/security-module.d.ts +20 -0
  60. package/dist/security/security-module.d.ts.map +1 -1
  61. package/dist/validation/class-validator.d.ts +108 -0
  62. package/dist/validation/class-validator.d.ts.map +1 -0
  63. package/dist/validation/custom-validator.d.ts +130 -0
  64. package/dist/validation/custom-validator.d.ts.map +1 -0
  65. package/dist/validation/errors.d.ts +22 -2
  66. package/dist/validation/errors.d.ts.map +1 -1
  67. package/dist/validation/index.d.ts +7 -1
  68. package/dist/validation/index.d.ts.map +1 -1
  69. package/dist/validation/rules/array.d.ts +33 -0
  70. package/dist/validation/rules/array.d.ts.map +1 -0
  71. package/dist/validation/rules/common.d.ts +90 -0
  72. package/dist/validation/rules/common.d.ts.map +1 -0
  73. package/dist/validation/rules/conditional.d.ts +30 -0
  74. package/dist/validation/rules/conditional.d.ts.map +1 -0
  75. package/dist/validation/rules/index.d.ts +5 -0
  76. package/dist/validation/rules/index.d.ts.map +1 -0
  77. package/dist/validation/rules/object.d.ts +30 -0
  78. package/dist/validation/rules/object.d.ts.map +1 -0
  79. package/dist/validation/types.d.ts +52 -1
  80. package/dist/validation/types.d.ts.map +1 -1
  81. package/docs/events.md +494 -0
  82. package/docs/guards.md +376 -0
  83. package/docs/guide.md +309 -1
  84. package/docs/request-lifecycle.md +444 -0
  85. package/docs/symbol-interface-pattern.md +431 -0
  86. package/docs/validation.md +407 -0
  87. package/docs/zh/events.md +494 -0
  88. package/docs/zh/guards.md +376 -0
  89. package/docs/zh/guide.md +309 -1
  90. package/docs/zh/request-lifecycle.md +444 -0
  91. package/docs/zh/symbol-interface-pattern.md +431 -0
  92. package/docs/zh/validation.md +407 -0
  93. package/package.json +1 -1
  94. package/src/cache/cache-module.ts +37 -0
  95. package/src/cache/index.ts +16 -1
  96. package/src/cache/interceptors.ts +295 -0
  97. package/src/cache/service-proxy.ts +219 -0
  98. package/src/controller/controller.ts +30 -6
  99. package/src/core/application.ts +25 -1
  100. package/src/di/container.ts +57 -7
  101. package/src/di/decorators.ts +46 -0
  102. package/src/di/index.ts +17 -2
  103. package/src/di/module-registry.ts +39 -0
  104. package/src/di/types.ts +29 -0
  105. package/src/events/decorators.ts +103 -0
  106. package/src/events/event-module.ts +272 -0
  107. package/src/events/index.ts +32 -0
  108. package/src/events/service.ts +352 -0
  109. package/src/events/types.ts +223 -0
  110. package/src/index.ts +140 -1
  111. package/src/security/filter.ts +88 -8
  112. package/src/security/guards/builtin/auth-guard.ts +68 -0
  113. package/src/security/guards/builtin/index.ts +3 -0
  114. package/src/security/guards/builtin/roles-guard.ts +165 -0
  115. package/src/security/guards/decorators.ts +124 -0
  116. package/src/security/guards/execution-context.ts +152 -0
  117. package/src/security/guards/guard-registry.ts +164 -0
  118. package/src/security/guards/index.ts +7 -0
  119. package/src/security/guards/reflector.ts +99 -0
  120. package/src/security/guards/types.ts +144 -0
  121. package/src/security/index.ts +1 -0
  122. package/src/security/security-module.ts +72 -2
  123. package/src/validation/class-validator.ts +322 -0
  124. package/src/validation/custom-validator.ts +289 -0
  125. package/src/validation/errors.ts +50 -2
  126. package/src/validation/index.ts +103 -1
  127. package/src/validation/rules/array.ts +118 -0
  128. package/src/validation/rules/common.ts +286 -0
  129. package/src/validation/rules/conditional.ts +52 -0
  130. package/src/validation/rules/index.ts +51 -0
  131. package/src/validation/rules/object.ts +86 -0
  132. package/src/validation/types.ts +61 -1
  133. package/tests/cache/cache-decorators.test.ts +284 -0
  134. package/tests/controller/path-combination.test.ts +353 -0
  135. package/tests/di/global-module.test.ts +487 -0
  136. package/tests/events/event-decorators.test.ts +173 -0
  137. package/tests/events/event-emitter.test.ts +373 -0
  138. package/tests/events/event-module.test.ts +373 -0
  139. package/tests/security/guards/guards-integration.test.ts +371 -0
  140. package/tests/security/guards/guards.test.ts +775 -0
  141. package/tests/security/security-module.test.ts +2 -2
  142. package/tests/validation/class-validator.test.ts +349 -0
  143. package/tests/validation/custom-validator.test.ts +335 -0
  144. package/tests/validation/rules.test.ts +543 -0
package/src/index.ts CHANGED
@@ -25,7 +25,7 @@ export type {
25
25
  ControllerMetadata,
26
26
  } from './controller';
27
27
  export { Container } from './di/container';
28
- export { Injectable, Inject } from './di/decorators';
28
+ export { Injectable, Inject, Global, isGlobalModule, GLOBAL_MODULE_METADATA_KEY } from './di/decorators';
29
29
  export { Lifecycle, type ProviderConfig, type DependencyMetadata } from './di/types';
30
30
  export {
31
31
  Module,
@@ -71,15 +71,97 @@ export {
71
71
  type RateLimitOptions,
72
72
  type RateLimitStore,
73
73
  } from './middleware/builtin';
74
+ // Validation 模块
74
75
  export {
76
+ // 基础装饰器
75
77
  Validate,
76
78
  IsString,
77
79
  IsNumber,
78
80
  IsEmail,
79
81
  IsOptional,
80
82
  MinLength,
83
+ getValidationMetadata,
84
+ // 类型
85
+ type ValidationRuleDefinition,
86
+ type ValidationMetadata,
87
+ type ClassValidationMetadata,
88
+ type ValidationOptions,
89
+ // 验证器
90
+ validateParameters,
91
+ // 错误处理
81
92
  ValidationError,
82
93
  type ValidationIssue,
94
+ // 对象规则
95
+ IsObject,
96
+ IsNotEmpty,
97
+ IsNotEmptyObject,
98
+ ValidateNested,
99
+ type ObjectRuleOptions,
100
+ type ValidateNestedOptions,
101
+ // 数组规则
102
+ IsArray,
103
+ ArrayMinSize,
104
+ ArrayMaxSize,
105
+ ArrayUnique,
106
+ ArrayContains,
107
+ ArrayNotContains,
108
+ ArrayNotEmpty,
109
+ type ArrayRuleOptions,
110
+ // 通用规则
111
+ IsBoolean,
112
+ IsInt,
113
+ IsPositive,
114
+ IsNegative,
115
+ Min,
116
+ Max,
117
+ IsDate,
118
+ IsUUID,
119
+ Length,
120
+ MaxLength,
121
+ Matches,
122
+ IsIn,
123
+ IsNotIn,
124
+ IsUrl,
125
+ IsJSON,
126
+ Equals,
127
+ NotEquals,
128
+ IsDefined,
129
+ IsAlphanumeric,
130
+ IsAlpha,
131
+ IsNumberString,
132
+ type RuleOptions,
133
+ type UUIDVersion,
134
+ // 条件和转换规则
135
+ ValidateIf,
136
+ Transform,
137
+ type ConditionalRuleOptions,
138
+ // 自定义验证器
139
+ createCustomValidator,
140
+ createSimpleValidator,
141
+ createRegexValidator,
142
+ IsPhoneNumber,
143
+ IsIdCard,
144
+ IsIPv4,
145
+ IsPort,
146
+ IsPostalCode,
147
+ IsCreditCard,
148
+ IsHexColor,
149
+ IsMacAddress,
150
+ IsSemVer,
151
+ IsDivisibleBy,
152
+ IsBetween,
153
+ Contains,
154
+ NotContains,
155
+ type CustomValidatorOptions,
156
+ // 类级别验证
157
+ ValidateClass,
158
+ Property,
159
+ NestedProperty,
160
+ ArrayNestedProperty,
161
+ validateObject,
162
+ validateObjectSync,
163
+ getClassValidationMetadata,
164
+ isValidateClass,
83
165
  } from './validation';
84
166
  export {
85
167
  HttpException,
@@ -136,6 +218,8 @@ export {
136
218
  JwtAuthenticationProvider,
137
219
  OAuth2AuthenticationProvider,
138
220
  createSecurityFilter,
221
+ getGuardRegistry,
222
+ registerReflector,
139
223
  type SecurityModuleConfig,
140
224
  type SecurityConfig,
141
225
  type SecurityContext,
@@ -146,6 +230,32 @@ export {
146
230
  type Credentials,
147
231
  type AccessDecisionManager,
148
232
  } from './security';
233
+ // Guards 子模块
234
+ export {
235
+ UseGuards,
236
+ Roles,
237
+ getGuardsMetadata,
238
+ getRolesMetadata,
239
+ AuthGuard,
240
+ OptionalAuthGuard,
241
+ RolesGuard,
242
+ createRolesGuard,
243
+ GuardRegistry,
244
+ ExecutionContextImpl,
245
+ Reflector,
246
+ GUARDS_METADATA_KEY,
247
+ GUARD_REGISTRY_TOKEN,
248
+ ROLES_METADATA_KEY,
249
+ REFLECTOR_TOKEN,
250
+ type CanActivate,
251
+ type ExecutionContext,
252
+ type HttpArgumentsHost,
253
+ type WsArgumentsHost,
254
+ type GuardType,
255
+ type GuardMetadata,
256
+ type GuardOptions,
257
+ type RolesGuardOptions,
258
+ } from './security/guards';
149
259
  export {
150
260
  ConfigModule,
151
261
  ConfigService,
@@ -254,10 +364,17 @@ export {
254
364
  Cacheable,
255
365
  CacheEvict,
256
366
  CachePut,
367
+ EnableCacheProxy,
368
+ CacheServiceProxy,
369
+ CachePostProcessor,
370
+ CacheableInterceptor,
371
+ CacheEvictInterceptor,
372
+ CachePutInterceptor,
257
373
  MemoryCacheStore,
258
374
  RedisCacheStore,
259
375
  CACHE_SERVICE_TOKEN,
260
376
  CACHE_OPTIONS_TOKEN,
377
+ CACHE_POST_PROCESSOR_TOKEN,
261
378
  } from './cache';
262
379
  export type {
263
380
  CacheModuleOptions,
@@ -387,4 +504,26 @@ export {
387
504
  type ServiceInstanceHealth,
388
505
  type MonitoringOptions,
389
506
  } from './microservice';
507
+ // Events 模块
508
+ export {
509
+ EventModule,
510
+ EventEmitterService,
511
+ EventListenerScanner,
512
+ OnEvent,
513
+ getOnEventMetadata,
514
+ isEventListenerClass,
515
+ EVENT_EMITTER_TOKEN,
516
+ EVENT_OPTIONS_TOKEN,
517
+ EVENT_LISTENER_SCANNER_TOKEN,
518
+ ON_EVENT_METADATA_KEY,
519
+ EVENT_LISTENER_CLASS_METADATA_KEY,
520
+ type EventEmitter,
521
+ type EventListener,
522
+ type EventMetadata,
523
+ type EventModuleOptions,
524
+ type ListenerOptions,
525
+ type OnEventMethodMetadata,
526
+ type OnEventOptions,
527
+ type RegisteredListener,
528
+ } from './events';
390
529
 
@@ -1,5 +1,6 @@
1
1
  import type { Context } from '../core/context';
2
2
  import type { Middleware } from '../middleware';
3
+ import type { Container } from '../di/container';
3
4
  import { SecurityContextHolder } from './context';
4
5
  import { AuthenticationManager } from './authentication-manager';
5
6
  import { RoleBasedAccessDecisionManager } from './access-decision-manager';
@@ -10,6 +11,10 @@ import {
10
11
  } from '../error/http-exception';
11
12
  import { ErrorCode } from '../error/error-codes';
12
13
  import { requiresAuth, getAuthMetadata } from '../auth/decorators';
14
+ import { GuardRegistry } from './guards/guard-registry';
15
+ import { ExecutionContextImpl } from './guards/execution-context';
16
+ import { Reflector, REFLECTOR_TOKEN } from './guards/reflector';
17
+ import { GUARD_REGISTRY_TOKEN } from './guards/types';
13
18
 
14
19
  /**
15
20
  * 安全过滤器配置
@@ -27,6 +32,14 @@ export interface SecurityFilterConfig extends SecurityConfig {
27
32
  * 令牌提取函数
28
33
  */
29
34
  extractToken?: (ctx: Context) => string | null;
35
+ /**
36
+ * DI 容器(用于解析守卫)
37
+ */
38
+ container?: Container;
39
+ /**
40
+ * 守卫注册表
41
+ */
42
+ guardRegistry?: GuardRegistry;
30
43
  }
31
44
 
32
45
  /**
@@ -39,8 +52,30 @@ export function createSecurityFilter(config: SecurityFilterConfig): Middleware {
39
52
  excludePaths = [],
40
53
  defaultAuthRequired = true,
41
54
  extractToken,
55
+ container: initialContainer,
56
+ guardRegistry,
42
57
  } = config;
43
58
 
59
+ // 创建或使用传入的守卫注册表
60
+ const registry = guardRegistry || new GuardRegistry();
61
+
62
+ // 延迟获取容器的函数
63
+ let cachedContainer: Container | null = initialContainer || null;
64
+ const getContainer = (): Container | null => {
65
+ if (cachedContainer) {
66
+ return cachedContainer;
67
+ }
68
+ // 尝试从 ControllerRegistry 获取容器
69
+ try {
70
+ // 动态导入避免循环依赖
71
+ const { ControllerRegistry } = require('../controller/controller');
72
+ cachedContainer = ControllerRegistry.getInstance().getContainer();
73
+ return cachedContainer;
74
+ } catch {
75
+ return null;
76
+ }
77
+ };
78
+
44
79
  return async (ctx: Context, next) => {
45
80
  return SecurityContextHolder.runWithContext(async () => {
46
81
  // 检查是否在排除列表中
@@ -74,6 +109,14 @@ export function createSecurityFilter(config: SecurityFilterConfig): Middleware {
74
109
  }
75
110
  }
76
111
 
112
+ // 将安全上下文附加到 Context(在守卫执行前)
113
+ (ctx as any).security = securityContext;
114
+ (ctx as any).auth = {
115
+ isAuthenticated: securityContext.isAuthenticated(),
116
+ user: securityContext.getPrincipal(),
117
+ payload: (securityContext.authentication?.details as any),
118
+ };
119
+
77
120
  // 检查是否需要认证
78
121
  const handler = (ctx as any).routeHandler;
79
122
  if (handler) {
@@ -82,6 +125,23 @@ export function createSecurityFilter(config: SecurityFilterConfig): Middleware {
82
125
  (controllerClass && controllerClass.prototype) || controllerClass;
83
126
  const method = handler.method;
84
127
 
128
+ // 执行守卫链(在 @Auth 检查之前)
129
+ // Guards 在中间件之后、拦截器之前执行
130
+ const container = getContainer();
131
+ // 只有当 controllerClass 是一个有效的类(构造函数)时才执行守卫
132
+ // 测试中可能传入原型而不是类,这种情况跳过守卫执行
133
+ if (container && typeof controllerClass === 'function') {
134
+ const methodHandler = controllerTarget[method];
135
+ const executionContext = new ExecutionContextImpl(
136
+ ctx,
137
+ controllerClass,
138
+ method,
139
+ methodHandler || (() => {}),
140
+ );
141
+ await registry.executeGuards(executionContext, container);
142
+ }
143
+
144
+ // 传统的 @Auth 装饰器检查(向后兼容)
85
145
  if (requiresAuth(controllerTarget, method)) {
86
146
  const authentication = securityContext.authentication;
87
147
  if (!authentication || !authentication.authenticated) {
@@ -118,14 +178,6 @@ export function createSecurityFilter(config: SecurityFilterConfig): Middleware {
118
178
  );
119
179
  }
120
180
 
121
- // 将安全上下文附加到 Context
122
- (ctx as any).security = securityContext;
123
- (ctx as any).auth = {
124
- isAuthenticated: securityContext.isAuthenticated(),
125
- user: securityContext.getPrincipal(),
126
- payload: (securityContext.authentication?.details as any),
127
- };
128
-
129
181
  return await next();
130
182
  } finally {
131
183
  // 清理当前请求内的认证信息,防止泄漏到下一个请求
@@ -135,6 +187,34 @@ export function createSecurityFilter(config: SecurityFilterConfig): Middleware {
135
187
  };
136
188
  }
137
189
 
190
+ /**
191
+ * 获取守卫注册表
192
+ * @param container - DI 容器
193
+ * @returns 守卫注册表实例
194
+ */
195
+ export function getGuardRegistry(container: Container): GuardRegistry {
196
+ if (container.isRegistered(GUARD_REGISTRY_TOKEN)) {
197
+ return container.resolve<GuardRegistry>(GUARD_REGISTRY_TOKEN);
198
+ }
199
+ const registry = new GuardRegistry();
200
+ container.registerInstance(GUARD_REGISTRY_TOKEN, registry);
201
+ return registry;
202
+ }
203
+
204
+ /**
205
+ * 注册 Reflector 到 DI 容器
206
+ * @param container - DI 容器
207
+ * @returns Reflector 实例
208
+ */
209
+ export function registerReflector(container: Container): Reflector {
210
+ if (container.isRegistered(REFLECTOR_TOKEN)) {
211
+ return container.resolve<Reflector>(REFLECTOR_TOKEN);
212
+ }
213
+ const reflector = new Reflector();
214
+ container.registerInstance(REFLECTOR_TOKEN, reflector);
215
+ return reflector;
216
+ }
217
+
138
218
  /**
139
219
  * 从请求头提取令牌
140
220
  */
@@ -0,0 +1,68 @@
1
+ import { Injectable } from '../../../di/decorators';
2
+ import type { CanActivate, ExecutionContext } from '../types';
3
+ import { SecurityContextHolder } from '../../context';
4
+ import { UnauthorizedException } from '../../../error/http-exception';
5
+ import { ErrorCode } from '../../../error/error-codes';
6
+
7
+ /**
8
+ * 认证守卫
9
+ * 检查请求是否已认证
10
+ *
11
+ * @example
12
+ * @Controller('/api/users')
13
+ * @UseGuards(AuthGuard)
14
+ * class UserController {
15
+ * @GET('/profile')
16
+ * getProfile() {
17
+ * // 只有已认证的用户才能访问
18
+ * }
19
+ * }
20
+ */
21
+ @Injectable()
22
+ export class AuthGuard implements CanActivate {
23
+ /**
24
+ * 判断是否允许访问
25
+ * @param context - 执行上下文
26
+ * @returns 如果已认证则返回 true,否则抛出 UnauthorizedException
27
+ */
28
+ public canActivate(context: ExecutionContext): boolean {
29
+ const securityContext = SecurityContextHolder.getContext();
30
+
31
+ if (!securityContext.isAuthenticated()) {
32
+ throw new UnauthorizedException(
33
+ 'Authentication required',
34
+ undefined,
35
+ ErrorCode.AUTH_REQUIRED,
36
+ );
37
+ }
38
+
39
+ return true;
40
+ }
41
+ }
42
+
43
+ /**
44
+ * 可选认证守卫
45
+ * 如果有 token 则验证,没有 token 也允许访问
46
+ *
47
+ * @example
48
+ * @GET('/public')
49
+ * @UseGuards(OptionalAuthGuard)
50
+ * publicEndpoint() {
51
+ * // 未认证也可以访问,但如果有 token 会被验证
52
+ * }
53
+ */
54
+ @Injectable()
55
+ export class OptionalAuthGuard implements CanActivate {
56
+ /**
57
+ * 判断是否允许访问
58
+ * 总是返回 true,但会检查认证状态
59
+ * @param context - 执行上下文
60
+ * @returns 总是返回 true
61
+ */
62
+ public canActivate(context: ExecutionContext): boolean {
63
+ // 可选认证:不强制要求认证,但如果有认证信息会被使用
64
+ // SecurityFilter 已经处理了 token 解析和认证
65
+ return true;
66
+ }
67
+ }
68
+
@@ -0,0 +1,3 @@
1
+ export { AuthGuard, OptionalAuthGuard } from './auth-guard';
2
+ export { RolesGuard, createRolesGuard, type RolesGuardOptions } from './roles-guard';
3
+
@@ -0,0 +1,165 @@
1
+ import { Injectable, Inject } from '../../../di/decorators';
2
+ import type { CanActivate, ExecutionContext } from '../types';
3
+ import { ROLES_METADATA_KEY } from '../types';
4
+ import { SecurityContextHolder } from '../../context';
5
+ import { ForbiddenException } from '../../../error/http-exception';
6
+ import { ErrorCode } from '../../../error/error-codes';
7
+ import { Reflector, REFLECTOR_TOKEN } from '../reflector';
8
+
9
+ /**
10
+ * 角色守卫
11
+ * 检查用户是否具有所需角色
12
+ *
13
+ * @example
14
+ * @Controller('/api/admin')
15
+ * @UseGuards(AuthGuard, RolesGuard)
16
+ * class AdminController {
17
+ * @GET('/dashboard')
18
+ * @Roles('admin')
19
+ * dashboard() {
20
+ * // 只有 admin 角色才能访问
21
+ * }
22
+ *
23
+ * @GET('/super')
24
+ * @Roles('admin', 'superadmin')
25
+ * superAdmin() {
26
+ * // admin 或 superadmin 角色可以访问
27
+ * }
28
+ * }
29
+ */
30
+ @Injectable()
31
+ export class RolesGuard implements CanActivate {
32
+ private readonly reflector: Reflector;
33
+
34
+ public constructor(
35
+ @Inject(REFLECTOR_TOKEN) reflector?: Reflector,
36
+ ) {
37
+ this.reflector = reflector || new Reflector();
38
+ }
39
+
40
+ /**
41
+ * 判断是否允许访问
42
+ * @param context - 执行上下文
43
+ * @returns 如果用户具有所需角色则返回 true
44
+ */
45
+ public canActivate(context: ExecutionContext): boolean {
46
+ // 获取方法和类上定义的角色
47
+ const requiredRoles = this.reflector.getAllAndMerge<string[]>(
48
+ ROLES_METADATA_KEY,
49
+ context.getClass(),
50
+ context.getMethodName(),
51
+ );
52
+
53
+ // 如果没有定义角色要求,则允许访问
54
+ if (!requiredRoles || requiredRoles.length === 0) {
55
+ return true;
56
+ }
57
+
58
+ // 获取当前用户的角色
59
+ const securityContext = SecurityContextHolder.getContext();
60
+ const authentication = securityContext.authentication;
61
+
62
+ if (!authentication || !authentication.authenticated) {
63
+ throw new ForbiddenException(
64
+ 'Access denied: authentication required for role check',
65
+ { requiredRoles },
66
+ ErrorCode.AUTH_INSUFFICIENT_PERMISSIONS,
67
+ );
68
+ }
69
+
70
+ const userRoles = authentication.authorities || [];
71
+
72
+ // 检查用户是否具有任一所需角色
73
+ const hasRole = requiredRoles.some((role) => userRoles.includes(role));
74
+
75
+ if (!hasRole) {
76
+ throw new ForbiddenException(
77
+ `Access denied: required roles [${requiredRoles.join(', ')}], but user has [${userRoles.join(', ')}]`,
78
+ { requiredRoles, userRoles },
79
+ ErrorCode.AUTH_INSUFFICIENT_PERMISSIONS,
80
+ );
81
+ }
82
+
83
+ return true;
84
+ }
85
+ }
86
+
87
+ /**
88
+ * 创建自定义角色守卫
89
+ * 支持自定义角色验证逻辑
90
+ *
91
+ * @example
92
+ * const CustomRolesGuard = createRolesGuard({
93
+ * // 所有角色都必须匹配
94
+ * matchAll: true,
95
+ * // 自定义角色获取逻辑
96
+ * getRoles: (context) => {
97
+ * const user = context.switchToHttp().getRequest().auth?.user;
98
+ * return user?.roles || [];
99
+ * },
100
+ * });
101
+ */
102
+ export interface RolesGuardOptions {
103
+ /**
104
+ * 是否需要匹配所有角色
105
+ * @default false (只需匹配其中一个)
106
+ */
107
+ matchAll?: boolean;
108
+
109
+ /**
110
+ * 自定义获取用户角色的函数
111
+ */
112
+ getRoles?: (context: ExecutionContext) => string[];
113
+ }
114
+
115
+ /**
116
+ * 创建自定义角色守卫工厂
117
+ * @param options - 配置选项
118
+ * @returns 自定义角色守卫类
119
+ */
120
+ export function createRolesGuard(options: RolesGuardOptions = {}): new () => CanActivate {
121
+ const { matchAll = false, getRoles } = options;
122
+
123
+ @Injectable()
124
+ class CustomRolesGuard implements CanActivate {
125
+ private readonly reflector = new Reflector();
126
+
127
+ public canActivate(context: ExecutionContext): boolean {
128
+ const requiredRoles = this.reflector.getAllAndMerge<string[]>(
129
+ ROLES_METADATA_KEY,
130
+ context.getClass(),
131
+ context.getMethodName(),
132
+ );
133
+
134
+ if (!requiredRoles || requiredRoles.length === 0) {
135
+ return true;
136
+ }
137
+
138
+ let userRoles: string[];
139
+
140
+ if (getRoles) {
141
+ userRoles = getRoles(context);
142
+ } else {
143
+ const securityContext = SecurityContextHolder.getContext();
144
+ userRoles = securityContext.authentication?.authorities || [];
145
+ }
146
+
147
+ const hasRole = matchAll
148
+ ? requiredRoles.every((role) => userRoles.includes(role))
149
+ : requiredRoles.some((role) => userRoles.includes(role));
150
+
151
+ if (!hasRole) {
152
+ throw new ForbiddenException(
153
+ `Access denied: required roles [${requiredRoles.join(', ')}], user has [${userRoles.join(', ')}]`,
154
+ { requiredRoles, userRoles, matchAll },
155
+ ErrorCode.AUTH_INSUFFICIENT_PERMISSIONS,
156
+ );
157
+ }
158
+
159
+ return true;
160
+ }
161
+ }
162
+
163
+ return CustomRolesGuard;
164
+ }
165
+
@@ -0,0 +1,124 @@
1
+ import 'reflect-metadata';
2
+ import type { GuardType } from './types';
3
+ import { GUARDS_METADATA_KEY, ROLES_METADATA_KEY } from './types';
4
+
5
+ /**
6
+ * 守卫装饰器
7
+ * 可用于控制器或方法级别
8
+ *
9
+ * @example
10
+ * // 控制器级别
11
+ * @Controller('/api/admin')
12
+ * @UseGuards(AuthGuard, RolesGuard)
13
+ * class AdminController {}
14
+ *
15
+ * @example
16
+ * // 方法级别
17
+ * @GET('/profile')
18
+ * @UseGuards(AuthGuard)
19
+ * getProfile() {}
20
+ *
21
+ * @param guards - 守卫类或守卫实例
22
+ * @returns 类或方法装饰器
23
+ */
24
+ export function UseGuards(
25
+ ...guards: GuardType[]
26
+ ): ClassDecorator & MethodDecorator {
27
+ return (
28
+ target: Object | Function,
29
+ propertyKey?: string | symbol,
30
+ descriptor?: PropertyDescriptor,
31
+ ) => {
32
+ if (propertyKey !== undefined) {
33
+ // 方法装饰器
34
+ const existingGuards: GuardType[] =
35
+ Reflect.getMetadata(GUARDS_METADATA_KEY, target, propertyKey) || [];
36
+ Reflect.defineMetadata(
37
+ GUARDS_METADATA_KEY,
38
+ [...existingGuards, ...guards],
39
+ target,
40
+ propertyKey,
41
+ );
42
+ } else {
43
+ // 类装饰器
44
+ const existingGuards: GuardType[] =
45
+ Reflect.getMetadata(GUARDS_METADATA_KEY, target) || [];
46
+ Reflect.defineMetadata(
47
+ GUARDS_METADATA_KEY,
48
+ [...existingGuards, ...guards],
49
+ target,
50
+ );
51
+ }
52
+ };
53
+ }
54
+
55
+ /**
56
+ * 角色装饰器
57
+ * 用于标记需要特定角色的方法或控制器
58
+ *
59
+ * @example
60
+ * @GET('/admin')
61
+ * @Roles('admin', 'superadmin')
62
+ * adminOnly() {}
63
+ *
64
+ * @param roles - 允许访问的角色列表
65
+ * @returns 类或方法装饰器
66
+ */
67
+ export function Roles(
68
+ ...roles: string[]
69
+ ): ClassDecorator & MethodDecorator {
70
+ return (
71
+ target: Object | Function,
72
+ propertyKey?: string | symbol,
73
+ descriptor?: PropertyDescriptor,
74
+ ) => {
75
+ if (propertyKey !== undefined) {
76
+ // 方法装饰器
77
+ Reflect.defineMetadata(ROLES_METADATA_KEY, roles, target, propertyKey);
78
+ } else {
79
+ // 类装饰器
80
+ Reflect.defineMetadata(ROLES_METADATA_KEY, roles, target);
81
+ }
82
+ };
83
+ }
84
+
85
+ /**
86
+ * 获取守卫元数据
87
+ * @param target - 目标对象(类或原型)
88
+ * @param propertyKey - 方法名(可选)
89
+ * @returns 守卫列表
90
+ */
91
+ export function getGuardsMetadata(
92
+ target: Object | Function,
93
+ propertyKey?: string | symbol,
94
+ ): GuardType[] {
95
+ // 安全检查:确保 target 是有效的对象或函数
96
+ if (target === null || target === undefined) {
97
+ return [];
98
+ }
99
+ if (typeof target !== 'object' && typeof target !== 'function') {
100
+ return [];
101
+ }
102
+
103
+ if (propertyKey !== undefined) {
104
+ return Reflect.getMetadata(GUARDS_METADATA_KEY, target, propertyKey) || [];
105
+ }
106
+ return Reflect.getMetadata(GUARDS_METADATA_KEY, target) || [];
107
+ }
108
+
109
+ /**
110
+ * 获取角色元数据
111
+ * @param target - 目标对象(类或原型)
112
+ * @param propertyKey - 方法名(可选)
113
+ * @returns 角色列表
114
+ */
115
+ export function getRolesMetadata(
116
+ target: Object | Function,
117
+ propertyKey?: string | symbol,
118
+ ): string[] {
119
+ if (propertyKey !== undefined) {
120
+ return Reflect.getMetadata(ROLES_METADATA_KEY, target, propertyKey) || [];
121
+ }
122
+ return Reflect.getMetadata(ROLES_METADATA_KEY, target) || [];
123
+ }
124
+