@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.
- package/README.md +196 -19
- package/dist/cache/cache-module.d.ts +18 -0
- package/dist/cache/cache-module.d.ts.map +1 -1
- package/dist/cache/index.d.ts +3 -1
- package/dist/cache/index.d.ts.map +1 -1
- package/dist/cache/interceptors.d.ts +41 -0
- package/dist/cache/interceptors.d.ts.map +1 -0
- package/dist/cache/service-proxy.d.ts +62 -0
- package/dist/cache/service-proxy.d.ts.map +1 -0
- package/dist/controller/controller.d.ts +8 -0
- package/dist/controller/controller.d.ts.map +1 -1
- package/dist/core/application.d.ts +5 -0
- package/dist/core/application.d.ts.map +1 -1
- package/dist/di/container.d.ts +18 -1
- package/dist/di/container.d.ts.map +1 -1
- package/dist/di/decorators.d.ts +37 -0
- package/dist/di/decorators.d.ts.map +1 -1
- package/dist/di/index.d.ts +2 -2
- 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/di/types.d.ts +22 -0
- package/dist/di/types.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 +6 -4
- package/dist/index.d.ts.map +1 -1
- package/dist/index.js +4641 -2840
- 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/symbol-interface-pattern.md +431 -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/symbol-interface-pattern.md +431 -0
- package/docs/zh/validation.md +407 -0
- package/package.json +1 -1
- package/src/cache/cache-module.ts +37 -0
- package/src/cache/index.ts +16 -1
- package/src/cache/interceptors.ts +295 -0
- package/src/cache/service-proxy.ts +219 -0
- package/src/controller/controller.ts +30 -6
- package/src/core/application.ts +25 -1
- package/src/di/container.ts +57 -7
- package/src/di/decorators.ts +46 -0
- package/src/di/index.ts +17 -2
- package/src/di/module-registry.ts +39 -0
- package/src/di/types.ts +29 -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 +140 -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/cache/cache-decorators.test.ts +284 -0
- package/tests/controller/path-combination.test.ts +353 -0
- 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,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
|
+
|
|
@@ -0,0 +1,144 @@
|
|
|
1
|
+
import type { Context } from '../../core/context';
|
|
2
|
+
import type { ResponseBuilder } from '../../request/response';
|
|
3
|
+
import type { Constructor } from '../../core/types';
|
|
4
|
+
import type { ServerWebSocket } from 'bun';
|
|
5
|
+
|
|
6
|
+
/**
|
|
7
|
+
* 守卫接口
|
|
8
|
+
* 守卫用于决定请求是否可以继续执行
|
|
9
|
+
*/
|
|
10
|
+
export interface CanActivate {
|
|
11
|
+
/**
|
|
12
|
+
* 判断是否允许访问
|
|
13
|
+
* @param context - 执行上下文
|
|
14
|
+
* @returns 是否允许访问,可以是同步或异步的布尔值
|
|
15
|
+
*/
|
|
16
|
+
canActivate(context: ExecutionContext): boolean | Promise<boolean>;
|
|
17
|
+
}
|
|
18
|
+
|
|
19
|
+
/**
|
|
20
|
+
* HTTP 参数主机接口
|
|
21
|
+
* 提供 HTTP 请求相关的上下文信息
|
|
22
|
+
*/
|
|
23
|
+
export interface HttpArgumentsHost {
|
|
24
|
+
/**
|
|
25
|
+
* 获取请求上下文
|
|
26
|
+
* @returns Context 对象
|
|
27
|
+
*/
|
|
28
|
+
getRequest(): Context;
|
|
29
|
+
|
|
30
|
+
/**
|
|
31
|
+
* 获取响应构建器
|
|
32
|
+
* @returns ResponseBuilder 对象(可能为 undefined)
|
|
33
|
+
*/
|
|
34
|
+
getResponse(): ResponseBuilder | undefined;
|
|
35
|
+
}
|
|
36
|
+
|
|
37
|
+
/**
|
|
38
|
+
* WebSocket 参数主机接口
|
|
39
|
+
* 提供 WebSocket 连接相关的上下文信息
|
|
40
|
+
*/
|
|
41
|
+
export interface WsArgumentsHost {
|
|
42
|
+
/**
|
|
43
|
+
* 获取 WebSocket 客户端
|
|
44
|
+
* @returns WebSocket 连接对象
|
|
45
|
+
*/
|
|
46
|
+
getClient(): ServerWebSocket<unknown>;
|
|
47
|
+
|
|
48
|
+
/**
|
|
49
|
+
* 获取消息数据
|
|
50
|
+
* @returns 消息数据
|
|
51
|
+
*/
|
|
52
|
+
getData(): unknown;
|
|
53
|
+
}
|
|
54
|
+
|
|
55
|
+
/**
|
|
56
|
+
* 执行上下文接口
|
|
57
|
+
* 提供请求处理过程中的上下文信息
|
|
58
|
+
*/
|
|
59
|
+
export interface ExecutionContext {
|
|
60
|
+
/**
|
|
61
|
+
* 获取 HTTP 上下文
|
|
62
|
+
* @returns HTTP 参数主机
|
|
63
|
+
*/
|
|
64
|
+
switchToHttp(): HttpArgumentsHost;
|
|
65
|
+
|
|
66
|
+
/**
|
|
67
|
+
* 获取 WebSocket 上下文
|
|
68
|
+
* @returns WebSocket 参数主机
|
|
69
|
+
*/
|
|
70
|
+
switchToWs(): WsArgumentsHost;
|
|
71
|
+
|
|
72
|
+
/**
|
|
73
|
+
* 获取当前处理的控制器类
|
|
74
|
+
* @returns 控制器类构造函数
|
|
75
|
+
*/
|
|
76
|
+
getClass(): Constructor<unknown>;
|
|
77
|
+
|
|
78
|
+
/**
|
|
79
|
+
* 获取当前处理的方法
|
|
80
|
+
* @returns 方法函数
|
|
81
|
+
*/
|
|
82
|
+
getHandler(): Function;
|
|
83
|
+
|
|
84
|
+
/**
|
|
85
|
+
* 获取方法名
|
|
86
|
+
* @returns 方法名字符串
|
|
87
|
+
*/
|
|
88
|
+
getMethodName(): string;
|
|
89
|
+
|
|
90
|
+
/**
|
|
91
|
+
* 获取方法或类的元数据
|
|
92
|
+
* @param key - 元数据键
|
|
93
|
+
* @returns 元数据值,如果不存在则返回 undefined
|
|
94
|
+
*/
|
|
95
|
+
getMetadata<T>(key: string | symbol): T | undefined;
|
|
96
|
+
|
|
97
|
+
/**
|
|
98
|
+
* 获取请求参数
|
|
99
|
+
* @returns 请求参数数组
|
|
100
|
+
*/
|
|
101
|
+
getArgs(): unknown[];
|
|
102
|
+
}
|
|
103
|
+
|
|
104
|
+
/**
|
|
105
|
+
* 守卫类型:可以是守卫类构造函数或守卫实例
|
|
106
|
+
*/
|
|
107
|
+
export type GuardType = Constructor<CanActivate> | CanActivate;
|
|
108
|
+
|
|
109
|
+
/**
|
|
110
|
+
* 守卫元数据
|
|
111
|
+
*/
|
|
112
|
+
export interface GuardMetadata {
|
|
113
|
+
/**
|
|
114
|
+
* 守卫列表
|
|
115
|
+
*/
|
|
116
|
+
guards: GuardType[];
|
|
117
|
+
}
|
|
118
|
+
|
|
119
|
+
/**
|
|
120
|
+
* 守卫配置选项
|
|
121
|
+
*/
|
|
122
|
+
export interface GuardOptions {
|
|
123
|
+
/**
|
|
124
|
+
* 是否跳过全局守卫
|
|
125
|
+
* @default false
|
|
126
|
+
*/
|
|
127
|
+
skipGlobalGuards?: boolean;
|
|
128
|
+
}
|
|
129
|
+
|
|
130
|
+
/**
|
|
131
|
+
* 守卫元数据键
|
|
132
|
+
*/
|
|
133
|
+
export const GUARDS_METADATA_KEY = Symbol('@dangao/bun-server:guards');
|
|
134
|
+
|
|
135
|
+
/**
|
|
136
|
+
* 守卫注册表 Token
|
|
137
|
+
*/
|
|
138
|
+
export const GUARD_REGISTRY_TOKEN = Symbol('@dangao/bun-server:guard-registry');
|
|
139
|
+
|
|
140
|
+
/**
|
|
141
|
+
* Roles 元数据键
|
|
142
|
+
*/
|
|
143
|
+
export const ROLES_METADATA_KEY = Symbol('@dangao/bun-server:roles');
|
|
144
|
+
|
package/src/security/index.ts
CHANGED
|
@@ -2,11 +2,15 @@ import { Module, MODULE_METADATA_KEY } from '../di/module';
|
|
|
2
2
|
import { AuthenticationManager } from './authentication-manager';
|
|
3
3
|
import { JwtAuthenticationProvider } from './providers/jwt-provider';
|
|
4
4
|
import { OAuth2AuthenticationProvider } from './providers/oauth2-provider';
|
|
5
|
-
import { createSecurityFilter } from './filter';
|
|
5
|
+
import { createSecurityFilter, getGuardRegistry, registerReflector } from './filter';
|
|
6
6
|
import { JWTUtil } from '../auth/jwt';
|
|
7
7
|
import { OAuth2Service } from '../auth/oauth2';
|
|
8
8
|
import { OAuth2Controller, OAUTH2_SERVICE_TOKEN, JWT_UTIL_TOKEN } from '../auth/controller';
|
|
9
9
|
import type { JWTConfig, OAuth2Client, UserInfo } from '../auth/types';
|
|
10
|
+
import type { GuardType } from './guards/types';
|
|
11
|
+
import { GUARD_REGISTRY_TOKEN } from './guards/types';
|
|
12
|
+
import { GuardRegistry } from './guards/guard-registry';
|
|
13
|
+
import { Reflector, REFLECTOR_TOKEN } from './guards/reflector';
|
|
10
14
|
|
|
11
15
|
/**
|
|
12
16
|
* 安全模块配置
|
|
@@ -45,8 +49,17 @@ export interface SecurityModuleConfig {
|
|
|
45
49
|
* @default false
|
|
46
50
|
*/
|
|
47
51
|
defaultAuthRequired?: boolean;
|
|
52
|
+
/**
|
|
53
|
+
* 全局守卫列表
|
|
54
|
+
*/
|
|
55
|
+
globalGuards?: GuardType[];
|
|
48
56
|
}
|
|
49
57
|
|
|
58
|
+
/**
|
|
59
|
+
* 内部存储:容器引用和守卫注册表
|
|
60
|
+
*/
|
|
61
|
+
let _guardRegistry: GuardRegistry | null = null;
|
|
62
|
+
|
|
50
63
|
/**
|
|
51
64
|
* 安全模块
|
|
52
65
|
*/
|
|
@@ -82,7 +95,23 @@ export class SecurityModule {
|
|
|
82
95
|
new OAuth2AuthenticationProvider(oauth2Service, jwtUtil),
|
|
83
96
|
);
|
|
84
97
|
|
|
85
|
-
//
|
|
98
|
+
// 创建守卫注册表(每次 forRoot 都创建新实例,避免测试间污染)
|
|
99
|
+
const guardRegistry = new GuardRegistry();
|
|
100
|
+
// 清理旧的 registry(如果存在)
|
|
101
|
+
if (_guardRegistry) {
|
|
102
|
+
_guardRegistry.clearGlobalGuards();
|
|
103
|
+
}
|
|
104
|
+
_guardRegistry = guardRegistry;
|
|
105
|
+
|
|
106
|
+
// 添加全局守卫
|
|
107
|
+
if (config.globalGuards && config.globalGuards.length > 0) {
|
|
108
|
+
guardRegistry.addGlobalGuards(...config.globalGuards);
|
|
109
|
+
}
|
|
110
|
+
|
|
111
|
+
// 创建 Reflector
|
|
112
|
+
const reflector = new Reflector();
|
|
113
|
+
|
|
114
|
+
// 创建安全过滤器(暂时不传 container,将在模块注册时更新)
|
|
86
115
|
const securityFilter = createSecurityFilter({
|
|
87
116
|
authenticationManager,
|
|
88
117
|
excludePaths: [
|
|
@@ -92,6 +121,7 @@ export class SecurityModule {
|
|
|
92
121
|
: []),
|
|
93
122
|
],
|
|
94
123
|
defaultAuthRequired: config.defaultAuthRequired ?? false,
|
|
124
|
+
guardRegistry,
|
|
95
125
|
});
|
|
96
126
|
|
|
97
127
|
const controllers: any[] = [];
|
|
@@ -117,6 +147,14 @@ export class SecurityModule {
|
|
|
117
147
|
provide: AuthenticationManager,
|
|
118
148
|
useValue: authenticationManager,
|
|
119
149
|
},
|
|
150
|
+
{
|
|
151
|
+
provide: GUARD_REGISTRY_TOKEN,
|
|
152
|
+
useValue: guardRegistry,
|
|
153
|
+
},
|
|
154
|
+
{
|
|
155
|
+
provide: REFLECTOR_TOKEN,
|
|
156
|
+
useValue: reflector,
|
|
157
|
+
},
|
|
120
158
|
);
|
|
121
159
|
|
|
122
160
|
// 添加安全过滤器中间件
|
|
@@ -135,11 +173,43 @@ export class SecurityModule {
|
|
|
135
173
|
JWT_UTIL_TOKEN,
|
|
136
174
|
OAUTH2_SERVICE_TOKEN,
|
|
137
175
|
AuthenticationManager,
|
|
176
|
+
GUARD_REGISTRY_TOKEN,
|
|
177
|
+
REFLECTOR_TOKEN,
|
|
138
178
|
],
|
|
139
179
|
};
|
|
140
180
|
Reflect.defineMetadata(MODULE_METADATA_KEY, metadata, SecurityModule);
|
|
141
181
|
|
|
142
182
|
return SecurityModule;
|
|
143
183
|
}
|
|
184
|
+
|
|
185
|
+
/**
|
|
186
|
+
* 获取守卫注册表
|
|
187
|
+
* @returns 守卫注册表实例
|
|
188
|
+
*/
|
|
189
|
+
public static getGuardRegistry(): GuardRegistry | null {
|
|
190
|
+
return _guardRegistry;
|
|
191
|
+
}
|
|
192
|
+
|
|
193
|
+
/**
|
|
194
|
+
* 添加全局守卫
|
|
195
|
+
* @param guards - 守卫类或实例
|
|
196
|
+
*/
|
|
197
|
+
public static addGlobalGuards(...guards: GuardType[]): void {
|
|
198
|
+
if (_guardRegistry) {
|
|
199
|
+
_guardRegistry.addGlobalGuards(...guards);
|
|
200
|
+
}
|
|
201
|
+
}
|
|
202
|
+
|
|
203
|
+
/**
|
|
204
|
+
* 重置模块状态(主要用于测试)
|
|
205
|
+
*/
|
|
206
|
+
public static reset(): void {
|
|
207
|
+
if (_guardRegistry) {
|
|
208
|
+
_guardRegistry.clearGlobalGuards();
|
|
209
|
+
}
|
|
210
|
+
_guardRegistry = null;
|
|
211
|
+
// 清除模块元数据
|
|
212
|
+
Reflect.deleteMetadata(MODULE_METADATA_KEY, SecurityModule);
|
|
213
|
+
}
|
|
144
214
|
}
|
|
145
215
|
|