@dangao/bun-server 1.0.1 → 1.1.2
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/dist/controller/controller.d.ts +1 -1
- package/dist/controller/controller.d.ts.map +1 -1
- package/dist/core/application.d.ts.map +1 -1
- package/dist/database/database-extension.d.ts.map +1 -1
- package/dist/database/database-module.d.ts.map +1 -1
- package/dist/database/orm/transaction-decorator.d.ts +1 -0
- package/dist/database/orm/transaction-decorator.d.ts.map +1 -1
- package/dist/database/orm/transaction-interceptor.d.ts +12 -3
- package/dist/database/orm/transaction-interceptor.d.ts.map +1 -1
- package/dist/index.d.ts +1 -0
- package/dist/index.d.ts.map +1 -1
- package/dist/index.js +678 -310
- package/dist/interceptor/base-interceptor.d.ts +94 -0
- package/dist/interceptor/base-interceptor.d.ts.map +1 -0
- package/dist/interceptor/builtin/cache-interceptor.d.ts +69 -0
- package/dist/interceptor/builtin/cache-interceptor.d.ts.map +1 -0
- package/dist/interceptor/builtin/index.d.ts +4 -0
- package/dist/interceptor/builtin/index.d.ts.map +1 -0
- package/dist/interceptor/builtin/log-interceptor.d.ts +56 -0
- package/dist/interceptor/builtin/log-interceptor.d.ts.map +1 -0
- package/dist/interceptor/builtin/permission-interceptor.d.ts +70 -0
- package/dist/interceptor/builtin/permission-interceptor.d.ts.map +1 -0
- package/dist/interceptor/index.d.ts +7 -0
- package/dist/interceptor/index.d.ts.map +1 -0
- package/dist/interceptor/interceptor-chain.d.ts +22 -0
- package/dist/interceptor/interceptor-chain.d.ts.map +1 -0
- package/dist/interceptor/interceptor-registry.d.ts +59 -0
- package/dist/interceptor/interceptor-registry.d.ts.map +1 -0
- package/dist/interceptor/metadata.d.ts +12 -0
- package/dist/interceptor/metadata.d.ts.map +1 -0
- package/dist/interceptor/types.d.ts +42 -0
- package/dist/interceptor/types.d.ts.map +1 -0
- package/dist/middleware/decorators.d.ts +2 -1
- package/dist/middleware/decorators.d.ts.map +1 -1
- package/dist/router/decorators.d.ts.map +1 -1
- package/dist/router/registry.d.ts +2 -1
- package/dist/router/registry.d.ts.map +1 -1
- package/dist/router/route.d.ts +3 -2
- package/dist/router/route.d.ts.map +1 -1
- package/dist/router/router.d.ts +2 -1
- package/dist/router/router.d.ts.map +1 -1
- package/dist/websocket/decorators.d.ts +2 -1
- package/dist/websocket/decorators.d.ts.map +1 -1
- package/package.json +5 -3
- package/readme.md +163 -2
- package/src/auth/controller.ts +148 -0
- package/src/auth/decorators.ts +81 -0
- package/src/auth/index.ts +12 -0
- package/src/auth/jwt.ts +169 -0
- package/src/auth/oauth2.ts +244 -0
- package/src/auth/types.ts +248 -0
- package/src/cache/cache-module.ts +67 -0
- package/src/cache/decorators.ts +202 -0
- package/src/cache/index.ts +27 -0
- package/src/cache/service.ts +151 -0
- package/src/cache/types.ts +420 -0
- package/src/config/config-module.ts +76 -0
- package/src/config/index.ts +8 -0
- package/src/config/service.ts +93 -0
- package/src/config/types.ts +27 -0
- package/src/controller/controller.ts +278 -0
- package/src/controller/decorators.ts +84 -0
- package/src/controller/index.ts +7 -0
- package/src/controller/metadata.ts +27 -0
- package/src/controller/param-binder.ts +157 -0
- package/src/core/application.ts +239 -0
- package/src/core/context.ts +228 -0
- package/src/core/index.ts +4 -0
- package/src/core/server.ts +128 -0
- package/src/core/types.ts +2 -0
- package/src/database/connection-manager.ts +239 -0
- package/src/database/connection-pool.ts +322 -0
- package/src/database/database-extension.ts +83 -0
- package/src/database/database-module.ts +121 -0
- package/src/database/health-indicator.ts +51 -0
- package/src/database/index.ts +47 -0
- package/src/database/orm/decorators.ts +155 -0
- package/src/database/orm/drizzle-repository.ts +39 -0
- package/src/database/orm/index.ts +23 -0
- package/src/database/orm/repository-decorator.ts +39 -0
- package/src/database/orm/repository.ts +103 -0
- package/src/database/orm/service.ts +49 -0
- package/src/database/orm/transaction-decorator.ts +76 -0
- package/src/database/orm/transaction-interceptor.ts +263 -0
- package/src/database/orm/transaction-manager.ts +276 -0
- package/src/database/orm/transaction-types.ts +140 -0
- package/src/database/orm/types.ts +99 -0
- package/src/database/service.ts +221 -0
- package/src/database/types.ts +171 -0
- package/src/di/container.ts +398 -0
- package/src/di/decorators.ts +228 -0
- package/src/di/index.ts +4 -0
- package/src/di/module-registry.ts +188 -0
- package/src/di/module.ts +65 -0
- package/src/di/types.ts +67 -0
- package/src/error/error-codes.ts +222 -0
- package/src/error/filter.ts +43 -0
- package/src/error/handler.ts +66 -0
- package/src/error/http-exception.ts +115 -0
- package/src/error/i18n.ts +217 -0
- package/src/error/index.ts +16 -0
- package/src/extensions/index.ts +5 -0
- package/src/extensions/logger-extension.ts +31 -0
- package/src/extensions/logger-module.ts +69 -0
- package/src/extensions/types.ts +14 -0
- package/src/files/index.ts +5 -0
- package/src/files/static-middleware.ts +53 -0
- package/src/files/storage.ts +67 -0
- package/src/files/types.ts +33 -0
- package/src/files/upload-middleware.ts +45 -0
- package/src/health/controller.ts +76 -0
- package/src/health/health-module.ts +51 -0
- package/src/health/index.ts +12 -0
- package/src/health/types.ts +28 -0
- package/src/index.ts +292 -0
- package/src/interceptor/base-interceptor.ts +203 -0
- package/src/interceptor/builtin/cache-interceptor.ts +169 -0
- package/src/interceptor/builtin/index.ts +28 -0
- package/src/interceptor/builtin/log-interceptor.ts +178 -0
- package/src/interceptor/builtin/permission-interceptor.ts +173 -0
- package/src/interceptor/index.ts +26 -0
- package/src/interceptor/interceptor-chain.ts +79 -0
- package/src/interceptor/interceptor-registry.ts +132 -0
- package/src/interceptor/metadata.ts +40 -0
- package/src/interceptor/types.ts +52 -0
- package/src/metrics/collector.ts +209 -0
- package/src/metrics/controller.ts +40 -0
- package/src/metrics/index.ts +15 -0
- package/src/metrics/metrics-module.ts +58 -0
- package/src/metrics/middleware.ts +46 -0
- package/src/metrics/prometheus.ts +79 -0
- package/src/metrics/types.ts +103 -0
- package/src/middleware/builtin/cors.ts +60 -0
- package/src/middleware/builtin/error-handler.ts +90 -0
- package/src/middleware/builtin/file-upload.ts +42 -0
- package/src/middleware/builtin/index.ts +14 -0
- package/src/middleware/builtin/logger.ts +91 -0
- package/src/middleware/builtin/rate-limit.ts +252 -0
- package/src/middleware/builtin/static-file.ts +88 -0
- package/src/middleware/decorators.ts +92 -0
- package/src/middleware/index.ts +11 -0
- package/src/middleware/middleware.ts +13 -0
- package/src/middleware/pipeline.ts +93 -0
- package/src/queue/decorators.ts +110 -0
- package/src/queue/index.ts +26 -0
- package/src/queue/queue-module.ts +64 -0
- package/src/queue/service.ts +302 -0
- package/src/queue/types.ts +341 -0
- package/src/request/body-parser.ts +133 -0
- package/src/request/file-handler.ts +46 -0
- package/src/request/index.ts +5 -0
- package/src/request/request.ts +107 -0
- package/src/request/response.ts +150 -0
- package/src/router/decorators.ts +123 -0
- package/src/router/index.ts +6 -0
- package/src/router/registry.ts +99 -0
- package/src/router/route.ts +141 -0
- package/src/router/router.ts +242 -0
- package/src/router/types.ts +27 -0
- package/src/security/access-decision-manager.ts +34 -0
- package/src/security/authentication-manager.ts +47 -0
- package/src/security/context.ts +92 -0
- package/src/security/filter.ts +162 -0
- package/src/security/index.ts +8 -0
- package/src/security/providers/index.ts +3 -0
- package/src/security/providers/jwt-provider.ts +60 -0
- package/src/security/providers/oauth2-provider.ts +70 -0
- package/src/security/security-module.ts +145 -0
- package/src/security/types.ts +165 -0
- package/src/session/decorators.ts +45 -0
- package/src/session/index.ts +19 -0
- package/src/session/middleware.ts +143 -0
- package/src/session/service.ts +218 -0
- package/src/session/session-module.ts +69 -0
- package/src/session/types.ts +373 -0
- package/src/swagger/decorators.ts +133 -0
- package/src/swagger/generator.ts +234 -0
- package/src/swagger/index.ts +7 -0
- package/src/swagger/swagger-extension.ts +41 -0
- package/src/swagger/swagger-module.ts +83 -0
- package/src/swagger/types.ts +188 -0
- package/src/swagger/ui.ts +98 -0
- package/src/testing/harness.ts +96 -0
- package/src/validation/decorators.ts +95 -0
- package/src/validation/errors.ts +28 -0
- package/src/validation/index.ts +14 -0
- package/src/validation/types.ts +35 -0
- package/src/validation/validator.ts +63 -0
- package/src/websocket/decorators.ts +53 -0
- package/src/websocket/index.ts +12 -0
- package/src/websocket/registry.ts +133 -0
- package/tests/cache/cache-module.test.ts +212 -0
- package/tests/config/config-module.test.ts +151 -0
- package/tests/controller/controller.test.ts +189 -0
- package/tests/controller/path-combination.test.ts +207 -0
- package/tests/core/application.test.ts +57 -0
- package/tests/core/context-body.test.ts +44 -0
- package/tests/core/context.test.ts +86 -0
- package/tests/core/edge-cases.test.ts +432 -0
- package/tests/database/database-module.test.ts +385 -0
- package/tests/database/orm.test.ts +164 -0
- package/tests/database/postgres-mysql-integration.test.ts +395 -0
- package/tests/database/transaction.test.ts +238 -0
- package/tests/di/container.test.ts +264 -0
- package/tests/di/module.test.ts +128 -0
- package/tests/error/error-codes.test.ts +121 -0
- package/tests/error/error-handler.test.ts +68 -0
- package/tests/error/error-handling.test.ts +254 -0
- package/tests/error/http-exception.test.ts +37 -0
- package/tests/error/i18n-integration.test.ts +175 -0
- package/tests/extensions/logger-extension.test.ts +40 -0
- package/tests/files/static-middleware.test.ts +67 -0
- package/tests/files/upload-middleware.test.ts +43 -0
- package/tests/health/health-module.test.ts +116 -0
- package/tests/integration/application-router.test.ts +85 -0
- package/tests/integration/body-parsing.test.ts +88 -0
- package/tests/integration/cache-e2e.test.ts +114 -0
- package/tests/integration/oauth2-e2e.test.ts +615 -0
- package/tests/integration/session-e2e.test.ts +207 -0
- package/tests/interceptor/builtin/cache-interceptor.test.ts +137 -0
- package/tests/interceptor/builtin/permission-interceptor.test.ts +182 -0
- package/tests/interceptor/interceptor-advanced-integration.test.ts +592 -0
- package/tests/interceptor/interceptor-arg-modification.test.ts +76 -0
- package/tests/interceptor/interceptor-chain.test.ts +199 -0
- package/tests/interceptor/interceptor-integration.test.ts +230 -0
- package/tests/interceptor/interceptor-registry.test.ts +200 -0
- package/tests/interceptor/perf/interceptor-performance.test.ts +341 -0
- package/tests/metrics/metrics-module.test.ts +178 -0
- package/tests/middleware/builtin.test.ts +206 -0
- package/tests/middleware/file-upload.test.ts +41 -0
- package/tests/middleware/middleware.test.ts +120 -0
- package/tests/middleware/pipeline.test.ts +72 -0
- package/tests/middleware/rate-limit.test.ts +314 -0
- package/tests/middleware/static-file.test.ts +62 -0
- package/tests/perf/harness.test.ts +48 -0
- package/tests/perf/optimization.test.ts +183 -0
- package/tests/perf/regression.test.ts +120 -0
- package/tests/queue/queue-module.test.ts +217 -0
- package/tests/request/body-parser.test.ts +96 -0
- package/tests/request/response.test.ts +99 -0
- package/tests/router/decorators.test.ts +46 -0
- package/tests/router/registry.test.ts +51 -0
- package/tests/router/route.test.ts +71 -0
- package/tests/router/router-normalization.test.ts +106 -0
- package/tests/router/router.test.ts +133 -0
- package/tests/security/access-decision-manager.test.ts +84 -0
- package/tests/security/authentication-manager.test.ts +81 -0
- package/tests/security/context.test.ts +302 -0
- package/tests/security/filter.test.ts +225 -0
- package/tests/security/jwt-provider.test.ts +106 -0
- package/tests/security/oauth2-provider.test.ts +269 -0
- package/tests/security/security-module.test.ts +143 -0
- package/tests/session/session-module.test.ts +307 -0
- package/tests/stress/di-stress.test.ts +30 -0
- package/tests/swagger/decorators.test.ts +153 -0
- package/tests/swagger/generator.test.ts +202 -0
- package/tests/swagger/swagger-extension.test.ts +72 -0
- package/tests/swagger/swagger-module.test.ts +79 -0
- package/tests/utils/test-port.ts +10 -0
- package/tests/validation/controller-validation.test.ts +64 -0
- package/tests/validation/validation.test.ts +42 -0
- package/tests/websocket/gateway.test.ts +68 -0
|
@@ -0,0 +1,88 @@
|
|
|
1
|
+
import { normalize, resolve, sep } from 'path';
|
|
2
|
+
|
|
3
|
+
import type { Middleware } from '../middleware';
|
|
4
|
+
import type { HeadersInit } from 'bun'
|
|
5
|
+
|
|
6
|
+
export interface StaticFileOptions {
|
|
7
|
+
root: string;
|
|
8
|
+
prefix?: string;
|
|
9
|
+
indexFile?: string;
|
|
10
|
+
enableCache?: boolean;
|
|
11
|
+
headers?: HeadersInit | Headers;
|
|
12
|
+
}
|
|
13
|
+
|
|
14
|
+
function isSubPath(root: string, target: string): boolean {
|
|
15
|
+
const normalizedRoot = normalize(root);
|
|
16
|
+
const normalizedTarget = normalize(target);
|
|
17
|
+
if (normalizedRoot === normalizedTarget) {
|
|
18
|
+
return true;
|
|
19
|
+
}
|
|
20
|
+
const rootWithSep = normalizedRoot.endsWith(sep) ? normalizedRoot : normalizedRoot + sep;
|
|
21
|
+
return normalizedTarget.startsWith(rootWithSep);
|
|
22
|
+
}
|
|
23
|
+
|
|
24
|
+
/**
|
|
25
|
+
* 静态文件服务中间件
|
|
26
|
+
*/
|
|
27
|
+
export function createStaticFileMiddleware(options: StaticFileOptions): Middleware {
|
|
28
|
+
if (!options.root) {
|
|
29
|
+
throw new Error('Static file middleware requires a root directory');
|
|
30
|
+
}
|
|
31
|
+
|
|
32
|
+
const root = resolve(options.root);
|
|
33
|
+
const prefix = options.prefix ?? '/';
|
|
34
|
+
const indexFile = options.indexFile ?? 'index.html';
|
|
35
|
+
const enableCache = options.enableCache ?? true;
|
|
36
|
+
|
|
37
|
+
return async (context, next) => {
|
|
38
|
+
if (!context.path.startsWith(prefix)) {
|
|
39
|
+
return await next();
|
|
40
|
+
}
|
|
41
|
+
|
|
42
|
+
let relativePath = context.path.slice(prefix.length);
|
|
43
|
+
if (!relativePath || relativePath === '') {
|
|
44
|
+
relativePath = '/';
|
|
45
|
+
}
|
|
46
|
+
|
|
47
|
+
let cleanPath = relativePath.startsWith('/') ? relativePath.slice(1) : relativePath;
|
|
48
|
+
if (cleanPath === '') {
|
|
49
|
+
cleanPath = '.';
|
|
50
|
+
}
|
|
51
|
+
|
|
52
|
+
const segments = cleanPath.split('/').filter((segment) => segment.length > 0);
|
|
53
|
+
if (segments.some((segment) => segment === '..')) {
|
|
54
|
+
context.setStatus(403);
|
|
55
|
+
return context.createResponse({ error: 'Forbidden' });
|
|
56
|
+
}
|
|
57
|
+
|
|
58
|
+
let targetPath = resolve(root, cleanPath);
|
|
59
|
+
if (context.path.endsWith('/') || relativePath === '/') {
|
|
60
|
+
targetPath = resolve(root, cleanPath, indexFile);
|
|
61
|
+
}
|
|
62
|
+
|
|
63
|
+
if (!isSubPath(root, targetPath)) {
|
|
64
|
+
context.setStatus(403);
|
|
65
|
+
return context.createResponse({ error: 'Forbidden' });
|
|
66
|
+
}
|
|
67
|
+
|
|
68
|
+
const file = Bun.file(targetPath);
|
|
69
|
+
if (!(await file.exists())) {
|
|
70
|
+
return await next();
|
|
71
|
+
}
|
|
72
|
+
|
|
73
|
+
const headers = new Headers(options.headers);
|
|
74
|
+
if (enableCache) {
|
|
75
|
+
headers.set('Cache-Control', 'public, max-age=31536000, immutable');
|
|
76
|
+
}
|
|
77
|
+
if (!headers.has('Content-Type') && file.type) {
|
|
78
|
+
headers.set('Content-Type', file.type);
|
|
79
|
+
}
|
|
80
|
+
|
|
81
|
+
return new Response(file, {
|
|
82
|
+
status: 200,
|
|
83
|
+
headers,
|
|
84
|
+
});
|
|
85
|
+
};
|
|
86
|
+
}
|
|
87
|
+
|
|
88
|
+
|
|
@@ -0,0 +1,92 @@
|
|
|
1
|
+
import 'reflect-metadata';
|
|
2
|
+
import type { Constructor } from '../core/types';
|
|
3
|
+
import type { Middleware } from './middleware';
|
|
4
|
+
import { createRateLimitMiddleware, type RateLimitOptions } from './builtin/rate-limit';
|
|
5
|
+
|
|
6
|
+
const CLASS_MIDDLEWARE_METADATA_KEY = Symbol('middleware:class');
|
|
7
|
+
const METHOD_MIDDLEWARE_METADATA_KEY = Symbol('middleware:method');
|
|
8
|
+
|
|
9
|
+
/**
|
|
10
|
+
* 注册中间件到元数据
|
|
11
|
+
* @param target - 目标对象
|
|
12
|
+
* @param propertyKey - 属性键
|
|
13
|
+
* @param middlewares - 中间件列表
|
|
14
|
+
*/
|
|
15
|
+
function appendMiddlewareMetadata(
|
|
16
|
+
target: object,
|
|
17
|
+
propertyKey: string | symbol | undefined,
|
|
18
|
+
middlewares: Middleware[],
|
|
19
|
+
): void {
|
|
20
|
+
if (!middlewares.length) {
|
|
21
|
+
return;
|
|
22
|
+
}
|
|
23
|
+
|
|
24
|
+
if (propertyKey === undefined) {
|
|
25
|
+
const existing = (Reflect.getMetadata(CLASS_MIDDLEWARE_METADATA_KEY, target) as Middleware[]) || [];
|
|
26
|
+
Reflect.defineMetadata(CLASS_MIDDLEWARE_METADATA_KEY, existing.concat(middlewares), target);
|
|
27
|
+
return;
|
|
28
|
+
}
|
|
29
|
+
|
|
30
|
+
const existing =
|
|
31
|
+
(Reflect.getMetadata(METHOD_MIDDLEWARE_METADATA_KEY, target, propertyKey) as Middleware[]) || [];
|
|
32
|
+
Reflect.defineMetadata(
|
|
33
|
+
METHOD_MIDDLEWARE_METADATA_KEY,
|
|
34
|
+
existing.concat(middlewares),
|
|
35
|
+
target,
|
|
36
|
+
propertyKey,
|
|
37
|
+
);
|
|
38
|
+
}
|
|
39
|
+
|
|
40
|
+
/**
|
|
41
|
+
* UseMiddleware 装饰器
|
|
42
|
+
* 可用于控制器类或方法
|
|
43
|
+
* @param middlewares - 中间件列表
|
|
44
|
+
*/
|
|
45
|
+
export function UseMiddleware(...middlewares: Middleware[]): ClassDecorator & MethodDecorator {
|
|
46
|
+
return function (target: object, propertyKey?: string | symbol) {
|
|
47
|
+
appendMiddlewareMetadata(
|
|
48
|
+
propertyKey === undefined ? (target as object) : target,
|
|
49
|
+
propertyKey,
|
|
50
|
+
middlewares,
|
|
51
|
+
);
|
|
52
|
+
};
|
|
53
|
+
}
|
|
54
|
+
|
|
55
|
+
/**
|
|
56
|
+
* RateLimit 装饰器
|
|
57
|
+
* 用于在控制器方法上应用速率限制
|
|
58
|
+
* @param options - 速率限制选项
|
|
59
|
+
*/
|
|
60
|
+
export function RateLimit(options: RateLimitOptions): MethodDecorator {
|
|
61
|
+
return function (target: object, propertyKey: string | symbol) {
|
|
62
|
+
const middleware = createRateLimitMiddleware(options);
|
|
63
|
+
appendMiddlewareMetadata(target, propertyKey, [middleware]);
|
|
64
|
+
};
|
|
65
|
+
}
|
|
66
|
+
|
|
67
|
+
/**
|
|
68
|
+
* 获取类级中间件
|
|
69
|
+
* @param constructor - 控制器构造函数
|
|
70
|
+
* @returns 中间件列表
|
|
71
|
+
*/
|
|
72
|
+
export function getClassMiddlewares(constructor: Constructor<unknown>): Middleware[] {
|
|
73
|
+
return (
|
|
74
|
+
(Reflect.getMetadata(CLASS_MIDDLEWARE_METADATA_KEY, constructor) as Middleware[]) || []
|
|
75
|
+
);
|
|
76
|
+
}
|
|
77
|
+
|
|
78
|
+
/**
|
|
79
|
+
* 获取方法级中间件
|
|
80
|
+
* @param target - 控制器原型
|
|
81
|
+
* @param propertyKey - 方法名
|
|
82
|
+
* @returns 中间件列表
|
|
83
|
+
*/
|
|
84
|
+
export function getMethodMiddlewares(
|
|
85
|
+
target: object,
|
|
86
|
+
propertyKey: string | symbol,
|
|
87
|
+
): Middleware[] {
|
|
88
|
+
return (
|
|
89
|
+
(Reflect.getMetadata(METHOD_MIDDLEWARE_METADATA_KEY, target, propertyKey) as Middleware[]) ||
|
|
90
|
+
[]
|
|
91
|
+
);
|
|
92
|
+
}
|
|
@@ -0,0 +1,11 @@
|
|
|
1
|
+
export type { Middleware, NextFunction } from './middleware';
|
|
2
|
+
export { MiddlewarePipeline, runMiddlewares } from './pipeline';
|
|
3
|
+
export { UseMiddleware, RateLimit, getClassMiddlewares, getMethodMiddlewares } from './decorators';
|
|
4
|
+
export {
|
|
5
|
+
createLoggerMiddleware,
|
|
6
|
+
createRequestLoggingMiddleware,
|
|
7
|
+
createErrorHandlingMiddleware,
|
|
8
|
+
createCorsMiddleware,
|
|
9
|
+
} from './builtin';
|
|
10
|
+
|
|
11
|
+
|
|
@@ -0,0 +1,13 @@
|
|
|
1
|
+
import type { Context } from '../core/context';
|
|
2
|
+
|
|
3
|
+
/**
|
|
4
|
+
* 中间件 Next 函数
|
|
5
|
+
*/
|
|
6
|
+
export type NextFunction = () => Promise<Response>;
|
|
7
|
+
|
|
8
|
+
/**
|
|
9
|
+
* 中间件接口
|
|
10
|
+
*/
|
|
11
|
+
export type Middleware = (context: Context, next: NextFunction) => Promise<Response> | Response;
|
|
12
|
+
|
|
13
|
+
|
|
@@ -0,0 +1,93 @@
|
|
|
1
|
+
import type { Context } from '../core/context';
|
|
2
|
+
import type { Middleware, NextFunction } from './middleware';
|
|
3
|
+
|
|
4
|
+
/**
|
|
5
|
+
* 中间件执行管道
|
|
6
|
+
*/
|
|
7
|
+
export class MiddlewarePipeline {
|
|
8
|
+
private readonly middlewares: Middleware[] = [];
|
|
9
|
+
|
|
10
|
+
public constructor(initialMiddlewares: Middleware[] = []) {
|
|
11
|
+
this.middlewares.push(...initialMiddlewares);
|
|
12
|
+
}
|
|
13
|
+
|
|
14
|
+
/**
|
|
15
|
+
* 注册中间件
|
|
16
|
+
* @param middleware - 要注册的中间件
|
|
17
|
+
*/
|
|
18
|
+
public use(middleware: Middleware): void {
|
|
19
|
+
this.middlewares.push(middleware);
|
|
20
|
+
}
|
|
21
|
+
|
|
22
|
+
/**
|
|
23
|
+
* 清空所有中间件
|
|
24
|
+
*/
|
|
25
|
+
public clear(): void {
|
|
26
|
+
this.middlewares.length = 0;
|
|
27
|
+
}
|
|
28
|
+
|
|
29
|
+
/**
|
|
30
|
+
* 是否存在中间件
|
|
31
|
+
*/
|
|
32
|
+
public hasMiddlewares(): boolean {
|
|
33
|
+
return this.middlewares.length > 0;
|
|
34
|
+
}
|
|
35
|
+
|
|
36
|
+
/**
|
|
37
|
+
* 执行中间件管道
|
|
38
|
+
* @param context - 请求上下文
|
|
39
|
+
* @param finalHandler - 最终处理函数
|
|
40
|
+
* @returns 响应对象
|
|
41
|
+
*/
|
|
42
|
+
public async run(context: Context, finalHandler: NextFunction): Promise<Response> {
|
|
43
|
+
const length = this.middlewares.length;
|
|
44
|
+
if (length === 0) {
|
|
45
|
+
return await finalHandler();
|
|
46
|
+
}
|
|
47
|
+
|
|
48
|
+
// 优化:使用索引而不是数组来跟踪调用状态,减少内存分配
|
|
49
|
+
let currentIndex = 0;
|
|
50
|
+
const called = new Array<boolean>(length).fill(false);
|
|
51
|
+
|
|
52
|
+
// 创建链式调用函数(从后往前构建)
|
|
53
|
+
const createNext = (index: number): NextFunction => {
|
|
54
|
+
if (index >= length) {
|
|
55
|
+
return finalHandler;
|
|
56
|
+
}
|
|
57
|
+
|
|
58
|
+
return async () => {
|
|
59
|
+
if (called[index]) {
|
|
60
|
+
throw new Error('next() called multiple times');
|
|
61
|
+
}
|
|
62
|
+
called[index] = true;
|
|
63
|
+
currentIndex = index + 1;
|
|
64
|
+
const middleware = this.middlewares[index];
|
|
65
|
+
return await middleware(context, createNext(index + 1));
|
|
66
|
+
};
|
|
67
|
+
};
|
|
68
|
+
|
|
69
|
+
return await createNext(0)();
|
|
70
|
+
}
|
|
71
|
+
}
|
|
72
|
+
|
|
73
|
+
/**
|
|
74
|
+
* 使用指定的中间件队列执行一次性管道
|
|
75
|
+
* @param middlewares - 中间件数组
|
|
76
|
+
* @param context - 请求上下文
|
|
77
|
+
* @param finalHandler - 最终处理函数
|
|
78
|
+
* @returns 响应对象
|
|
79
|
+
*/
|
|
80
|
+
export async function runMiddlewares(
|
|
81
|
+
middlewares: Middleware[],
|
|
82
|
+
context: Context,
|
|
83
|
+
finalHandler: NextFunction,
|
|
84
|
+
): Promise<Response> {
|
|
85
|
+
if (middlewares.length === 0) {
|
|
86
|
+
return await finalHandler();
|
|
87
|
+
}
|
|
88
|
+
|
|
89
|
+
const pipeline = new MiddlewarePipeline(middlewares);
|
|
90
|
+
return await pipeline.run(context, finalHandler);
|
|
91
|
+
}
|
|
92
|
+
|
|
93
|
+
|
|
@@ -0,0 +1,110 @@
|
|
|
1
|
+
import 'reflect-metadata';
|
|
2
|
+
|
|
3
|
+
/**
|
|
4
|
+
* 队列装饰器元数据键
|
|
5
|
+
*/
|
|
6
|
+
const QUEUE_METADATA_KEY = Symbol('@dangao/bun-server:queue:queue');
|
|
7
|
+
const CRON_METADATA_KEY = Symbol('@dangao/bun-server:queue:cron');
|
|
8
|
+
|
|
9
|
+
/**
|
|
10
|
+
* 队列配置
|
|
11
|
+
*/
|
|
12
|
+
export interface QueueOptions {
|
|
13
|
+
/**
|
|
14
|
+
* 队列名称
|
|
15
|
+
* @default 'default'
|
|
16
|
+
*/
|
|
17
|
+
name?: string;
|
|
18
|
+
}
|
|
19
|
+
|
|
20
|
+
/**
|
|
21
|
+
* Cron 配置
|
|
22
|
+
*/
|
|
23
|
+
export interface CronDecoratorOptions {
|
|
24
|
+
/**
|
|
25
|
+
* Cron 表达式
|
|
26
|
+
*/
|
|
27
|
+
pattern: string;
|
|
28
|
+
|
|
29
|
+
/**
|
|
30
|
+
* 时区
|
|
31
|
+
* @default 'UTC'
|
|
32
|
+
*/
|
|
33
|
+
timezone?: string;
|
|
34
|
+
|
|
35
|
+
/**
|
|
36
|
+
* 是否立即执行一次
|
|
37
|
+
* @default false
|
|
38
|
+
*/
|
|
39
|
+
runOnInit?: boolean;
|
|
40
|
+
|
|
41
|
+
/**
|
|
42
|
+
* 队列名称
|
|
43
|
+
* @default 'default'
|
|
44
|
+
*/
|
|
45
|
+
queueName?: string;
|
|
46
|
+
}
|
|
47
|
+
|
|
48
|
+
/**
|
|
49
|
+
* 队列装饰器元数据
|
|
50
|
+
*/
|
|
51
|
+
export interface QueueMetadata {
|
|
52
|
+
name?: string;
|
|
53
|
+
}
|
|
54
|
+
|
|
55
|
+
export interface CronMetadata {
|
|
56
|
+
pattern: string;
|
|
57
|
+
timezone?: string;
|
|
58
|
+
runOnInit?: boolean;
|
|
59
|
+
queueName?: string;
|
|
60
|
+
}
|
|
61
|
+
|
|
62
|
+
/**
|
|
63
|
+
* 标记方法为队列任务处理器
|
|
64
|
+
* @param options - 队列配置
|
|
65
|
+
*/
|
|
66
|
+
export function Queue(options: QueueOptions = {}): MethodDecorator {
|
|
67
|
+
return function (
|
|
68
|
+
target: unknown,
|
|
69
|
+
propertyKey: string | symbol,
|
|
70
|
+
descriptor: PropertyDescriptor,
|
|
71
|
+
) {
|
|
72
|
+
const metadata: QueueMetadata = {
|
|
73
|
+
name: options.name,
|
|
74
|
+
};
|
|
75
|
+
|
|
76
|
+
Reflect.defineMetadata(QUEUE_METADATA_KEY, metadata, descriptor.value);
|
|
77
|
+
};
|
|
78
|
+
}
|
|
79
|
+
|
|
80
|
+
/**
|
|
81
|
+
* 标记方法为定时任务(Cron)
|
|
82
|
+
* @param options - Cron 配置
|
|
83
|
+
*/
|
|
84
|
+
export function Cron(options: CronDecoratorOptions): MethodDecorator {
|
|
85
|
+
return function (
|
|
86
|
+
target: unknown,
|
|
87
|
+
propertyKey: string | symbol,
|
|
88
|
+
descriptor: PropertyDescriptor,
|
|
89
|
+
) {
|
|
90
|
+
const metadata: CronMetadata = {
|
|
91
|
+
pattern: options.pattern,
|
|
92
|
+
timezone: options.timezone,
|
|
93
|
+
runOnInit: options.runOnInit ?? false,
|
|
94
|
+
queueName: options.queueName,
|
|
95
|
+
};
|
|
96
|
+
|
|
97
|
+
Reflect.defineMetadata(CRON_METADATA_KEY, metadata, descriptor.value);
|
|
98
|
+
};
|
|
99
|
+
}
|
|
100
|
+
|
|
101
|
+
/**
|
|
102
|
+
* 获取队列装饰器元数据
|
|
103
|
+
*/
|
|
104
|
+
export function getQueueMetadata(target: unknown): QueueMetadata | undefined {
|
|
105
|
+
return Reflect.getMetadata(QUEUE_METADATA_KEY, target as object);
|
|
106
|
+
}
|
|
107
|
+
|
|
108
|
+
export function getCronMetadata(target: unknown): CronMetadata | undefined {
|
|
109
|
+
return Reflect.getMetadata(CRON_METADATA_KEY, target as object);
|
|
110
|
+
}
|
|
@@ -0,0 +1,26 @@
|
|
|
1
|
+
export { QueueModule } from './queue-module';
|
|
2
|
+
export { QueueService } from './service';
|
|
3
|
+
export {
|
|
4
|
+
Queue,
|
|
5
|
+
Cron,
|
|
6
|
+
getQueueMetadata,
|
|
7
|
+
getCronMetadata,
|
|
8
|
+
type QueueOptions,
|
|
9
|
+
type CronDecoratorOptions,
|
|
10
|
+
type QueueMetadata,
|
|
11
|
+
type CronMetadata,
|
|
12
|
+
} from './decorators';
|
|
13
|
+
export {
|
|
14
|
+
MemoryQueueStore,
|
|
15
|
+
QUEUE_SERVICE_TOKEN,
|
|
16
|
+
QUEUE_OPTIONS_TOKEN,
|
|
17
|
+
} from './types';
|
|
18
|
+
export type {
|
|
19
|
+
QueueStore,
|
|
20
|
+
QueueModuleOptions,
|
|
21
|
+
Job,
|
|
22
|
+
JobData,
|
|
23
|
+
JobHandler,
|
|
24
|
+
JobOptions,
|
|
25
|
+
CronOptions,
|
|
26
|
+
} from './types';
|
|
@@ -0,0 +1,64 @@
|
|
|
1
|
+
import { Module, MODULE_METADATA_KEY, type ModuleProvider } from '../di/module';
|
|
2
|
+
|
|
3
|
+
import { QueueService } from './service';
|
|
4
|
+
import {
|
|
5
|
+
QUEUE_OPTIONS_TOKEN,
|
|
6
|
+
QUEUE_SERVICE_TOKEN,
|
|
7
|
+
MemoryQueueStore,
|
|
8
|
+
type QueueModuleOptions,
|
|
9
|
+
type QueueStore,
|
|
10
|
+
} from './types';
|
|
11
|
+
|
|
12
|
+
@Module({
|
|
13
|
+
providers: [],
|
|
14
|
+
})
|
|
15
|
+
export class QueueModule {
|
|
16
|
+
/**
|
|
17
|
+
* 创建队列模块
|
|
18
|
+
* @param options - 模块配置
|
|
19
|
+
*/
|
|
20
|
+
public static forRoot(
|
|
21
|
+
options: QueueModuleOptions = {},
|
|
22
|
+
): typeof QueueModule {
|
|
23
|
+
const providers: ModuleProvider[] = [];
|
|
24
|
+
|
|
25
|
+
// 如果没有提供 store,使用默认的内存存储
|
|
26
|
+
const store: QueueStore = options.store ?? new MemoryQueueStore();
|
|
27
|
+
|
|
28
|
+
const service = new QueueService({
|
|
29
|
+
store,
|
|
30
|
+
defaultQueue: options.defaultQueue,
|
|
31
|
+
enableWorker: options.enableWorker,
|
|
32
|
+
concurrency: options.concurrency,
|
|
33
|
+
});
|
|
34
|
+
|
|
35
|
+
providers.push(
|
|
36
|
+
{
|
|
37
|
+
provide: QUEUE_SERVICE_TOKEN,
|
|
38
|
+
useValue: service,
|
|
39
|
+
},
|
|
40
|
+
{
|
|
41
|
+
provide: QUEUE_OPTIONS_TOKEN,
|
|
42
|
+
useValue: options,
|
|
43
|
+
},
|
|
44
|
+
QueueService,
|
|
45
|
+
);
|
|
46
|
+
|
|
47
|
+
// 动态更新模块元数据
|
|
48
|
+
const existingMetadata =
|
|
49
|
+
Reflect.getMetadata(MODULE_METADATA_KEY, QueueModule) || {};
|
|
50
|
+
const metadata = {
|
|
51
|
+
...existingMetadata,
|
|
52
|
+
providers: [...(existingMetadata.providers || []), ...providers],
|
|
53
|
+
exports: [
|
|
54
|
+
...(existingMetadata.exports || []),
|
|
55
|
+
QUEUE_SERVICE_TOKEN,
|
|
56
|
+
QUEUE_OPTIONS_TOKEN,
|
|
57
|
+
QueueService,
|
|
58
|
+
],
|
|
59
|
+
};
|
|
60
|
+
Reflect.defineMetadata(MODULE_METADATA_KEY, metadata, QueueModule);
|
|
61
|
+
|
|
62
|
+
return QueueModule;
|
|
63
|
+
}
|
|
64
|
+
}
|