@dangao/bun-server 1.7.1 → 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.
- package/README.md +129 -21
- package/dist/di/decorators.d.ts +37 -0
- package/dist/di/decorators.d.ts.map +1 -1
- package/dist/di/index.d.ts +1 -1
- package/dist/di/index.d.ts.map +1 -1
- package/dist/di/module-registry.d.ts +17 -0
- package/dist/di/module-registry.d.ts.map +1 -1
- package/dist/events/decorators.d.ts +52 -0
- package/dist/events/decorators.d.ts.map +1 -0
- package/dist/events/event-module.d.ts +97 -0
- package/dist/events/event-module.d.ts.map +1 -0
- package/dist/events/index.d.ts +5 -0
- package/dist/events/index.d.ts.map +1 -0
- package/dist/events/service.d.ts +76 -0
- package/dist/events/service.d.ts.map +1 -0
- package/dist/events/types.d.ts +184 -0
- package/dist/events/types.d.ts.map +1 -0
- package/dist/index.d.ts +5 -3
- package/dist/index.d.ts.map +1 -1
- package/dist/index.js +1511 -11
- package/dist/security/filter.d.ts +23 -0
- package/dist/security/filter.d.ts.map +1 -1
- package/dist/security/guards/builtin/auth-guard.d.ts +44 -0
- package/dist/security/guards/builtin/auth-guard.d.ts.map +1 -0
- package/dist/security/guards/builtin/index.d.ts +3 -0
- package/dist/security/guards/builtin/index.d.ts.map +1 -0
- package/dist/security/guards/builtin/roles-guard.d.ts +66 -0
- package/dist/security/guards/builtin/roles-guard.d.ts.map +1 -0
- package/dist/security/guards/decorators.d.ts +50 -0
- package/dist/security/guards/decorators.d.ts.map +1 -0
- package/dist/security/guards/execution-context.d.ts +56 -0
- package/dist/security/guards/execution-context.d.ts.map +1 -0
- package/dist/security/guards/guard-registry.d.ts +67 -0
- package/dist/security/guards/guard-registry.d.ts.map +1 -0
- package/dist/security/guards/index.d.ts +7 -0
- package/dist/security/guards/index.d.ts.map +1 -0
- package/dist/security/guards/reflector.d.ts +57 -0
- package/dist/security/guards/reflector.d.ts.map +1 -0
- package/dist/security/guards/types.d.ts +126 -0
- package/dist/security/guards/types.d.ts.map +1 -0
- package/dist/security/index.d.ts +1 -0
- package/dist/security/index.d.ts.map +1 -1
- package/dist/security/security-module.d.ts +20 -0
- package/dist/security/security-module.d.ts.map +1 -1
- package/dist/validation/class-validator.d.ts +108 -0
- package/dist/validation/class-validator.d.ts.map +1 -0
- package/dist/validation/custom-validator.d.ts +130 -0
- package/dist/validation/custom-validator.d.ts.map +1 -0
- package/dist/validation/errors.d.ts +22 -2
- package/dist/validation/errors.d.ts.map +1 -1
- package/dist/validation/index.d.ts +7 -1
- package/dist/validation/index.d.ts.map +1 -1
- package/dist/validation/rules/array.d.ts +33 -0
- package/dist/validation/rules/array.d.ts.map +1 -0
- package/dist/validation/rules/common.d.ts +90 -0
- package/dist/validation/rules/common.d.ts.map +1 -0
- package/dist/validation/rules/conditional.d.ts +30 -0
- package/dist/validation/rules/conditional.d.ts.map +1 -0
- package/dist/validation/rules/index.d.ts +5 -0
- package/dist/validation/rules/index.d.ts.map +1 -0
- package/dist/validation/rules/object.d.ts +30 -0
- package/dist/validation/rules/object.d.ts.map +1 -0
- package/dist/validation/types.d.ts +52 -1
- package/dist/validation/types.d.ts.map +1 -1
- package/docs/events.md +494 -0
- package/docs/guards.md +376 -0
- package/docs/guide.md +309 -1
- package/docs/request-lifecycle.md +444 -0
- package/docs/validation.md +407 -0
- package/docs/zh/events.md +494 -0
- package/docs/zh/guards.md +376 -0
- package/docs/zh/guide.md +309 -1
- package/docs/zh/request-lifecycle.md +444 -0
- package/docs/zh/validation.md +407 -0
- package/package.json +1 -1
- package/src/di/decorators.ts +46 -0
- package/src/di/index.ts +10 -1
- package/src/di/module-registry.ts +39 -0
- package/src/events/decorators.ts +103 -0
- package/src/events/event-module.ts +272 -0
- package/src/events/index.ts +32 -0
- package/src/events/service.ts +352 -0
- package/src/events/types.ts +223 -0
- package/src/index.ts +133 -1
- package/src/security/filter.ts +88 -8
- package/src/security/guards/builtin/auth-guard.ts +68 -0
- package/src/security/guards/builtin/index.ts +3 -0
- package/src/security/guards/builtin/roles-guard.ts +165 -0
- package/src/security/guards/decorators.ts +124 -0
- package/src/security/guards/execution-context.ts +152 -0
- package/src/security/guards/guard-registry.ts +164 -0
- package/src/security/guards/index.ts +7 -0
- package/src/security/guards/reflector.ts +99 -0
- package/src/security/guards/types.ts +144 -0
- package/src/security/index.ts +1 -0
- package/src/security/security-module.ts +72 -2
- package/src/validation/class-validator.ts +322 -0
- package/src/validation/custom-validator.ts +289 -0
- package/src/validation/errors.ts +50 -2
- package/src/validation/index.ts +103 -1
- package/src/validation/rules/array.ts +118 -0
- package/src/validation/rules/common.ts +286 -0
- package/src/validation/rules/conditional.ts +52 -0
- package/src/validation/rules/index.ts +51 -0
- package/src/validation/rules/object.ts +86 -0
- package/src/validation/types.ts +61 -1
- package/tests/di/global-module.test.ts +487 -0
- package/tests/events/event-decorators.test.ts +173 -0
- package/tests/events/event-emitter.test.ts +373 -0
- package/tests/events/event-module.test.ts +373 -0
- package/tests/security/guards/guards-integration.test.ts +371 -0
- package/tests/security/guards/guards.test.ts +775 -0
- package/tests/security/security-module.test.ts +2 -2
- package/tests/validation/class-validator.test.ts +349 -0
- package/tests/validation/custom-validator.test.ts +335 -0
- package/tests/validation/rules.test.ts +543 -0
|
@@ -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
|
+
|
|
@@ -0,0 +1,152 @@
|
|
|
1
|
+
import 'reflect-metadata';
|
|
2
|
+
import type { ServerWebSocket } from 'bun';
|
|
3
|
+
import type { Context } from '../../core/context';
|
|
4
|
+
import type { ResponseBuilder } from '../../request/response';
|
|
5
|
+
import type { Constructor } from '../../core/types';
|
|
6
|
+
import type {
|
|
7
|
+
ExecutionContext,
|
|
8
|
+
HttpArgumentsHost,
|
|
9
|
+
WsArgumentsHost,
|
|
10
|
+
} from './types';
|
|
11
|
+
|
|
12
|
+
/**
|
|
13
|
+
* HTTP 参数主机实现
|
|
14
|
+
*/
|
|
15
|
+
class HttpArgumentsHostImpl implements HttpArgumentsHost {
|
|
16
|
+
public constructor(
|
|
17
|
+
private readonly ctx: Context,
|
|
18
|
+
private readonly responseBuilder?: ResponseBuilder,
|
|
19
|
+
) {}
|
|
20
|
+
|
|
21
|
+
/**
|
|
22
|
+
* 获取请求上下文
|
|
23
|
+
*/
|
|
24
|
+
public getRequest(): Context {
|
|
25
|
+
return this.ctx;
|
|
26
|
+
}
|
|
27
|
+
|
|
28
|
+
/**
|
|
29
|
+
* 获取响应构建器
|
|
30
|
+
*/
|
|
31
|
+
public getResponse(): ResponseBuilder | undefined {
|
|
32
|
+
return this.responseBuilder;
|
|
33
|
+
}
|
|
34
|
+
}
|
|
35
|
+
|
|
36
|
+
/**
|
|
37
|
+
* WebSocket 参数主机实现
|
|
38
|
+
*/
|
|
39
|
+
class WsArgumentsHostImpl implements WsArgumentsHost {
|
|
40
|
+
public constructor(
|
|
41
|
+
private readonly client: ServerWebSocket<unknown>,
|
|
42
|
+
private readonly data: unknown,
|
|
43
|
+
) {}
|
|
44
|
+
|
|
45
|
+
/**
|
|
46
|
+
* 获取 WebSocket 客户端
|
|
47
|
+
*/
|
|
48
|
+
public getClient(): ServerWebSocket<unknown> {
|
|
49
|
+
return this.client;
|
|
50
|
+
}
|
|
51
|
+
|
|
52
|
+
/**
|
|
53
|
+
* 获取消息数据
|
|
54
|
+
*/
|
|
55
|
+
public getData(): unknown {
|
|
56
|
+
return this.data;
|
|
57
|
+
}
|
|
58
|
+
}
|
|
59
|
+
|
|
60
|
+
/**
|
|
61
|
+
* 执行上下文实现
|
|
62
|
+
*/
|
|
63
|
+
export class ExecutionContextImpl implements ExecutionContext {
|
|
64
|
+
private readonly httpHost: HttpArgumentsHost;
|
|
65
|
+
private wsHost?: WsArgumentsHost;
|
|
66
|
+
|
|
67
|
+
public constructor(
|
|
68
|
+
private readonly ctx: Context,
|
|
69
|
+
private readonly controllerClass: Constructor<unknown>,
|
|
70
|
+
private readonly methodName: string,
|
|
71
|
+
private readonly handler: Function,
|
|
72
|
+
private readonly args: unknown[] = [],
|
|
73
|
+
responseBuilder?: ResponseBuilder,
|
|
74
|
+
) {
|
|
75
|
+
this.httpHost = new HttpArgumentsHostImpl(ctx, responseBuilder);
|
|
76
|
+
}
|
|
77
|
+
|
|
78
|
+
/**
|
|
79
|
+
* 设置 WebSocket 上下文
|
|
80
|
+
* @param client - WebSocket 客户端
|
|
81
|
+
* @param data - 消息数据
|
|
82
|
+
*/
|
|
83
|
+
public setWsContext(client: ServerWebSocket<unknown>, data: unknown): void {
|
|
84
|
+
this.wsHost = new WsArgumentsHostImpl(client, data);
|
|
85
|
+
}
|
|
86
|
+
|
|
87
|
+
/**
|
|
88
|
+
* 获取 HTTP 上下文
|
|
89
|
+
*/
|
|
90
|
+
public switchToHttp(): HttpArgumentsHost {
|
|
91
|
+
return this.httpHost;
|
|
92
|
+
}
|
|
93
|
+
|
|
94
|
+
/**
|
|
95
|
+
* 获取 WebSocket 上下文
|
|
96
|
+
*/
|
|
97
|
+
public switchToWs(): WsArgumentsHost {
|
|
98
|
+
if (!this.wsHost) {
|
|
99
|
+
throw new Error('WebSocket context is not available');
|
|
100
|
+
}
|
|
101
|
+
return this.wsHost;
|
|
102
|
+
}
|
|
103
|
+
|
|
104
|
+
/**
|
|
105
|
+
* 获取当前处理的控制器类
|
|
106
|
+
*/
|
|
107
|
+
public getClass(): Constructor<unknown> {
|
|
108
|
+
return this.controllerClass;
|
|
109
|
+
}
|
|
110
|
+
|
|
111
|
+
/**
|
|
112
|
+
* 获取当前处理的方法
|
|
113
|
+
*/
|
|
114
|
+
public getHandler(): Function {
|
|
115
|
+
return this.handler;
|
|
116
|
+
}
|
|
117
|
+
|
|
118
|
+
/**
|
|
119
|
+
* 获取方法名
|
|
120
|
+
*/
|
|
121
|
+
public getMethodName(): string {
|
|
122
|
+
return this.methodName;
|
|
123
|
+
}
|
|
124
|
+
|
|
125
|
+
/**
|
|
126
|
+
* 获取方法或类的元数据
|
|
127
|
+
* @param key - 元数据键
|
|
128
|
+
* @returns 元数据值
|
|
129
|
+
*/
|
|
130
|
+
public getMetadata<T>(key: string | symbol): T | undefined {
|
|
131
|
+
// 先尝试从方法获取
|
|
132
|
+
const methodMetadata = Reflect.getMetadata(
|
|
133
|
+
key,
|
|
134
|
+
this.controllerClass.prototype,
|
|
135
|
+
this.methodName,
|
|
136
|
+
);
|
|
137
|
+
if (methodMetadata !== undefined) {
|
|
138
|
+
return methodMetadata as T;
|
|
139
|
+
}
|
|
140
|
+
|
|
141
|
+
// 如果方法没有,尝试从类获取
|
|
142
|
+
return Reflect.getMetadata(key, this.controllerClass) as T | undefined;
|
|
143
|
+
}
|
|
144
|
+
|
|
145
|
+
/**
|
|
146
|
+
* 获取请求参数
|
|
147
|
+
*/
|
|
148
|
+
public getArgs(): unknown[] {
|
|
149
|
+
return this.args;
|
|
150
|
+
}
|
|
151
|
+
}
|
|
152
|
+
|
|
@@ -0,0 +1,164 @@
|
|
|
1
|
+
import type { Container } from '../../di/container';
|
|
2
|
+
import type { Constructor } from '../../core/types';
|
|
3
|
+
import type {
|
|
4
|
+
CanActivate,
|
|
5
|
+
GuardType,
|
|
6
|
+
ExecutionContext,
|
|
7
|
+
} from './types';
|
|
8
|
+
import { getGuardsMetadata } from './decorators';
|
|
9
|
+
import { ForbiddenException } from '../../error/http-exception';
|
|
10
|
+
import { ErrorCode } from '../../error/error-codes';
|
|
11
|
+
|
|
12
|
+
/**
|
|
13
|
+
* 守卫注册表
|
|
14
|
+
* 管理全局守卫和执行守卫链
|
|
15
|
+
*/
|
|
16
|
+
export class GuardRegistry {
|
|
17
|
+
/**
|
|
18
|
+
* 全局守卫列表
|
|
19
|
+
*/
|
|
20
|
+
private globalGuards: GuardType[] = [];
|
|
21
|
+
|
|
22
|
+
/**
|
|
23
|
+
* 守卫实例缓存(用于缓存从 DI 容器解析的守卫实例)
|
|
24
|
+
*/
|
|
25
|
+
private guardInstances = new Map<Constructor<CanActivate>, CanActivate>();
|
|
26
|
+
|
|
27
|
+
/**
|
|
28
|
+
* 注册全局守卫
|
|
29
|
+
* @param guards - 守卫类或实例
|
|
30
|
+
*/
|
|
31
|
+
public addGlobalGuards(...guards: GuardType[]): void {
|
|
32
|
+
this.globalGuards.push(...guards);
|
|
33
|
+
}
|
|
34
|
+
|
|
35
|
+
/**
|
|
36
|
+
* 获取全局守卫
|
|
37
|
+
* @returns 全局守卫列表
|
|
38
|
+
*/
|
|
39
|
+
public getGlobalGuards(): GuardType[] {
|
|
40
|
+
return [...this.globalGuards];
|
|
41
|
+
}
|
|
42
|
+
|
|
43
|
+
/**
|
|
44
|
+
* 清除所有全局守卫
|
|
45
|
+
*/
|
|
46
|
+
public clearGlobalGuards(): void {
|
|
47
|
+
this.globalGuards = [];
|
|
48
|
+
this.guardInstances.clear();
|
|
49
|
+
}
|
|
50
|
+
|
|
51
|
+
/**
|
|
52
|
+
* 解析守卫实例
|
|
53
|
+
* @param guard - 守卫类或实例
|
|
54
|
+
* @param container - DI 容器
|
|
55
|
+
* @returns 守卫实例
|
|
56
|
+
*/
|
|
57
|
+
private resolveGuard(guard: GuardType, container: Container): CanActivate {
|
|
58
|
+
// 如果已经是实例,直接返回
|
|
59
|
+
if (typeof guard !== 'function') {
|
|
60
|
+
return guard;
|
|
61
|
+
}
|
|
62
|
+
|
|
63
|
+
// 检查缓存
|
|
64
|
+
const cached = this.guardInstances.get(guard as Constructor<CanActivate>);
|
|
65
|
+
if (cached) {
|
|
66
|
+
return cached;
|
|
67
|
+
}
|
|
68
|
+
|
|
69
|
+
// 尝试从 DI 容器解析
|
|
70
|
+
try {
|
|
71
|
+
if (container.isRegistered(guard)) {
|
|
72
|
+
const instance = container.resolve<CanActivate>(guard);
|
|
73
|
+
this.guardInstances.set(guard as Constructor<CanActivate>, instance);
|
|
74
|
+
return instance;
|
|
75
|
+
}
|
|
76
|
+
} catch {
|
|
77
|
+
// 如果容器解析失败,继续尝试手动实例化
|
|
78
|
+
}
|
|
79
|
+
|
|
80
|
+
// 手动实例化(不支持依赖注入)
|
|
81
|
+
const GuardClass = guard as Constructor<CanActivate>;
|
|
82
|
+
const instance = new GuardClass();
|
|
83
|
+
this.guardInstances.set(guard as Constructor<CanActivate>, instance);
|
|
84
|
+
return instance;
|
|
85
|
+
}
|
|
86
|
+
|
|
87
|
+
/**
|
|
88
|
+
* 获取控制器级别的守卫
|
|
89
|
+
* @param controllerClass - 控制器类
|
|
90
|
+
* @returns 守卫列表
|
|
91
|
+
*/
|
|
92
|
+
public getControllerGuards(controllerClass: Constructor<unknown>): GuardType[] {
|
|
93
|
+
return getGuardsMetadata(controllerClass);
|
|
94
|
+
}
|
|
95
|
+
|
|
96
|
+
/**
|
|
97
|
+
* 获取方法级别的守卫
|
|
98
|
+
* @param controllerClass - 控制器类
|
|
99
|
+
* @param methodName - 方法名
|
|
100
|
+
* @returns 守卫列表
|
|
101
|
+
*/
|
|
102
|
+
public getMethodGuards(
|
|
103
|
+
controllerClass: Constructor<unknown>,
|
|
104
|
+
methodName: string,
|
|
105
|
+
): GuardType[] {
|
|
106
|
+
return getGuardsMetadata(controllerClass.prototype, methodName);
|
|
107
|
+
}
|
|
108
|
+
|
|
109
|
+
/**
|
|
110
|
+
* 收集所有守卫(全局 + 控制器 + 方法)
|
|
111
|
+
* @param controllerClass - 控制器类
|
|
112
|
+
* @param methodName - 方法名
|
|
113
|
+
* @returns 按顺序排列的守卫列表
|
|
114
|
+
*/
|
|
115
|
+
public collectGuards(
|
|
116
|
+
controllerClass: Constructor<unknown>,
|
|
117
|
+
methodName: string,
|
|
118
|
+
): GuardType[] {
|
|
119
|
+
const globalGuards = this.getGlobalGuards();
|
|
120
|
+
const controllerGuards = this.getControllerGuards(controllerClass);
|
|
121
|
+
const methodGuards = this.getMethodGuards(controllerClass, methodName);
|
|
122
|
+
|
|
123
|
+
// 执行顺序:全局 -> 控制器 -> 方法
|
|
124
|
+
return [...globalGuards, ...controllerGuards, ...methodGuards];
|
|
125
|
+
}
|
|
126
|
+
|
|
127
|
+
/**
|
|
128
|
+
* 执行守卫链
|
|
129
|
+
* @param context - 执行上下文
|
|
130
|
+
* @param container - DI 容器
|
|
131
|
+
* @returns 是否允许访问
|
|
132
|
+
* @throws ForbiddenException 如果守卫拒绝访问
|
|
133
|
+
*/
|
|
134
|
+
public async executeGuards(
|
|
135
|
+
context: ExecutionContext,
|
|
136
|
+
container: Container,
|
|
137
|
+
): Promise<boolean> {
|
|
138
|
+
const controllerClass = context.getClass();
|
|
139
|
+
const methodName = context.getMethodName();
|
|
140
|
+
const guards = this.collectGuards(controllerClass, methodName);
|
|
141
|
+
|
|
142
|
+
if (guards.length === 0) {
|
|
143
|
+
return true;
|
|
144
|
+
}
|
|
145
|
+
|
|
146
|
+
for (const guard of guards) {
|
|
147
|
+
const guardInstance = this.resolveGuard(guard, container);
|
|
148
|
+
const result = await guardInstance.canActivate(context);
|
|
149
|
+
|
|
150
|
+
if (!result) {
|
|
151
|
+
// 获取守卫名称用于错误消息
|
|
152
|
+
const guardName = typeof guard === 'function' ? guard.name : guard.constructor.name;
|
|
153
|
+
throw new ForbiddenException(
|
|
154
|
+
`Access denied by guard: ${guardName}`,
|
|
155
|
+
{ guard: guardName },
|
|
156
|
+
ErrorCode.AUTH_INSUFFICIENT_PERMISSIONS,
|
|
157
|
+
);
|
|
158
|
+
}
|
|
159
|
+
}
|
|
160
|
+
|
|
161
|
+
return true;
|
|
162
|
+
}
|
|
163
|
+
}
|
|
164
|
+
|
|
@@ -0,0 +1,99 @@
|
|
|
1
|
+
import 'reflect-metadata';
|
|
2
|
+
import type { Constructor } from '../../core/types';
|
|
3
|
+
|
|
4
|
+
/**
|
|
5
|
+
* Reflector 工具类
|
|
6
|
+
* 用于获取类和方法的元数据
|
|
7
|
+
*/
|
|
8
|
+
export class Reflector {
|
|
9
|
+
/**
|
|
10
|
+
* 获取元数据
|
|
11
|
+
* 优先从方法获取,如果方法没有则从类获取
|
|
12
|
+
*
|
|
13
|
+
* @param metadataKey - 元数据键
|
|
14
|
+
* @param target - 目标类或方法
|
|
15
|
+
* @returns 元数据值
|
|
16
|
+
*/
|
|
17
|
+
public get<T>(
|
|
18
|
+
metadataKey: string | symbol,
|
|
19
|
+
target: Function | Object,
|
|
20
|
+
): T | undefined {
|
|
21
|
+
return Reflect.getMetadata(metadataKey, target) as T | undefined;
|
|
22
|
+
}
|
|
23
|
+
|
|
24
|
+
/**
|
|
25
|
+
* 从类获取元数据
|
|
26
|
+
* @param metadataKey - 元数据键
|
|
27
|
+
* @param target - 目标类
|
|
28
|
+
* @returns 元数据值
|
|
29
|
+
*/
|
|
30
|
+
public getFromClass<T>(
|
|
31
|
+
metadataKey: string | symbol,
|
|
32
|
+
target: Constructor<unknown>,
|
|
33
|
+
): T | undefined {
|
|
34
|
+
return Reflect.getMetadata(metadataKey, target) as T | undefined;
|
|
35
|
+
}
|
|
36
|
+
|
|
37
|
+
/**
|
|
38
|
+
* 从方法获取元数据
|
|
39
|
+
* @param metadataKey - 元数据键
|
|
40
|
+
* @param target - 目标类原型
|
|
41
|
+
* @param propertyKey - 方法名
|
|
42
|
+
* @returns 元数据值
|
|
43
|
+
*/
|
|
44
|
+
public getFromMethod<T>(
|
|
45
|
+
metadataKey: string | symbol,
|
|
46
|
+
target: Object,
|
|
47
|
+
propertyKey: string | symbol,
|
|
48
|
+
): T | undefined {
|
|
49
|
+
return Reflect.getMetadata(metadataKey, target, propertyKey) as T | undefined;
|
|
50
|
+
}
|
|
51
|
+
|
|
52
|
+
/**
|
|
53
|
+
* 获取元数据(支持合并类和方法的元数据)
|
|
54
|
+
* 对于数组类型,将类和方法的元数据合并
|
|
55
|
+
*
|
|
56
|
+
* @param metadataKey - 元数据键
|
|
57
|
+
* @param target - 目标类
|
|
58
|
+
* @param propertyKey - 方法名
|
|
59
|
+
* @returns 合并后的元数据值
|
|
60
|
+
*/
|
|
61
|
+
public getAllAndMerge<T extends unknown[]>(
|
|
62
|
+
metadataKey: string | symbol,
|
|
63
|
+
target: Constructor<unknown>,
|
|
64
|
+
propertyKey: string | symbol,
|
|
65
|
+
): T {
|
|
66
|
+
const classMetadata = this.getFromClass<T>(metadataKey, target) || ([] as unknown as T);
|
|
67
|
+
const methodMetadata = this.getFromMethod<T>(metadataKey, target.prototype, propertyKey) || ([] as unknown as T);
|
|
68
|
+
|
|
69
|
+
// 合并数组
|
|
70
|
+
return [...classMetadata, ...methodMetadata] as T;
|
|
71
|
+
}
|
|
72
|
+
|
|
73
|
+
/**
|
|
74
|
+
* 获取元数据(方法优先)
|
|
75
|
+
* 如果方法有元数据则返回方法的,否则返回类的
|
|
76
|
+
*
|
|
77
|
+
* @param metadataKey - 元数据键
|
|
78
|
+
* @param target - 目标类
|
|
79
|
+
* @param propertyKey - 方法名
|
|
80
|
+
* @returns 元数据值
|
|
81
|
+
*/
|
|
82
|
+
public getAllAndOverride<T>(
|
|
83
|
+
metadataKey: string | symbol,
|
|
84
|
+
target: Constructor<unknown>,
|
|
85
|
+
propertyKey: string | symbol,
|
|
86
|
+
): T | undefined {
|
|
87
|
+
const methodMetadata = this.getFromMethod<T>(metadataKey, target.prototype, propertyKey);
|
|
88
|
+
if (methodMetadata !== undefined) {
|
|
89
|
+
return methodMetadata;
|
|
90
|
+
}
|
|
91
|
+
return this.getFromClass<T>(metadataKey, target);
|
|
92
|
+
}
|
|
93
|
+
}
|
|
94
|
+
|
|
95
|
+
/**
|
|
96
|
+
* Reflector Token
|
|
97
|
+
*/
|
|
98
|
+
export const REFLECTOR_TOKEN = Symbol('@dangao/bun-server:reflector');
|
|
99
|
+
|