@dangao/bun-server 1.0.0 → 1.0.3
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/package.json +4 -2
- 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 +251 -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 +233 -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 +62 -0
- package/src/database/database-module.ts +115 -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 +45 -0
- package/src/database/orm/transaction-interceptor.ts +243 -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 +270 -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 +91 -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 +122 -0
- package/src/router/index.ts +6 -0
- package/src/router/registry.ts +98 -0
- package/src/router/route.ts +140 -0
- package/src/router/router.ts +241 -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 +51 -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/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/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 +48 -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,143 @@
|
|
|
1
|
+
import type { Context } from '../core/context';
|
|
2
|
+
import type { Middleware, NextFunction } from '../middleware/middleware';
|
|
3
|
+
import { SessionService } from './service';
|
|
4
|
+
import { SESSION_SERVICE_TOKEN } from './types';
|
|
5
|
+
import type { Container } from '../di/container';
|
|
6
|
+
|
|
7
|
+
/**
|
|
8
|
+
* Session 中间件
|
|
9
|
+
* 从 Cookie 中读取 Session ID,并将 Session 对象附加到 Context
|
|
10
|
+
*/
|
|
11
|
+
export function createSessionMiddleware(
|
|
12
|
+
container: Container,
|
|
13
|
+
): Middleware {
|
|
14
|
+
return async (context: Context, next: NextFunction): Promise<Response> => {
|
|
15
|
+
let sessionService: SessionService | undefined;
|
|
16
|
+
try {
|
|
17
|
+
sessionService = container.resolve<SessionService>(
|
|
18
|
+
SESSION_SERVICE_TOKEN,
|
|
19
|
+
);
|
|
20
|
+
} catch {
|
|
21
|
+
// 如果 SessionService 未注册,跳过 Session 处理
|
|
22
|
+
return await next();
|
|
23
|
+
}
|
|
24
|
+
|
|
25
|
+
if (!sessionService) {
|
|
26
|
+
return await next();
|
|
27
|
+
}
|
|
28
|
+
|
|
29
|
+
// 从 Cookie 中读取 Session ID
|
|
30
|
+
const cookieName = sessionService.getCookieName();
|
|
31
|
+
const cookieHeader = context.request.headers.get('cookie');
|
|
32
|
+
let sessionId: string | undefined;
|
|
33
|
+
|
|
34
|
+
if (cookieHeader) {
|
|
35
|
+
// Cookie 格式: "name=value; name2=value2" 或 "name=value"
|
|
36
|
+
// 需要正确处理多个 Cookie 的情况
|
|
37
|
+
const cookies = cookieHeader.split(';').map((c) => c.trim());
|
|
38
|
+
for (const cookie of cookies) {
|
|
39
|
+
if (cookie.startsWith(`${cookieName}=`)) {
|
|
40
|
+
sessionId = cookie.substring(cookieName.length + 1).trim();
|
|
41
|
+
break;
|
|
42
|
+
}
|
|
43
|
+
}
|
|
44
|
+
}
|
|
45
|
+
|
|
46
|
+
// 如果有 Session ID,获取 Session
|
|
47
|
+
if (sessionId) {
|
|
48
|
+
const session = await sessionService.get(sessionId);
|
|
49
|
+
if (session) {
|
|
50
|
+
// 将 Session 附加到 Context
|
|
51
|
+
(context as unknown as { session: typeof session }).session = session;
|
|
52
|
+
(context as unknown as { sessionId: string }).sessionId = sessionId;
|
|
53
|
+
} else {
|
|
54
|
+
// Session 已过期或不存在,清除 sessionId
|
|
55
|
+
sessionId = undefined;
|
|
56
|
+
}
|
|
57
|
+
}
|
|
58
|
+
|
|
59
|
+
// 如果没有 Session,创建一个新 Session
|
|
60
|
+
if (!sessionId) {
|
|
61
|
+
const newSession = await sessionService.create();
|
|
62
|
+
(context as unknown as { session: typeof newSession }).session =
|
|
63
|
+
newSession;
|
|
64
|
+
(context as unknown as { sessionId: string }).sessionId = newSession.id;
|
|
65
|
+
sessionId = newSession.id;
|
|
66
|
+
}
|
|
67
|
+
|
|
68
|
+
// 调用下一个中间件
|
|
69
|
+
const response = await next();
|
|
70
|
+
|
|
71
|
+
// 设置或更新 Cookie
|
|
72
|
+
const currentSessionId = (context as unknown as { sessionId?: string })
|
|
73
|
+
.sessionId;
|
|
74
|
+
if (currentSessionId) {
|
|
75
|
+
const cookieOptions = sessionService.getCookieOptions();
|
|
76
|
+
const maxAge = sessionService.getMaxAge();
|
|
77
|
+
const cookieValue = buildCookie(
|
|
78
|
+
cookieName,
|
|
79
|
+
currentSessionId,
|
|
80
|
+
{
|
|
81
|
+
...cookieOptions,
|
|
82
|
+
maxAge,
|
|
83
|
+
},
|
|
84
|
+
);
|
|
85
|
+
|
|
86
|
+
// 创建新的 Response,添加 Set-Cookie 头
|
|
87
|
+
const newHeaders = new Headers(response.headers);
|
|
88
|
+
newHeaders.set('Set-Cookie', cookieValue);
|
|
89
|
+
|
|
90
|
+
return new Response(response.body, {
|
|
91
|
+
status: response.status,
|
|
92
|
+
statusText: response.statusText,
|
|
93
|
+
headers: newHeaders,
|
|
94
|
+
});
|
|
95
|
+
}
|
|
96
|
+
|
|
97
|
+
return response;
|
|
98
|
+
};
|
|
99
|
+
}
|
|
100
|
+
|
|
101
|
+
/**
|
|
102
|
+
* 构建 Cookie 字符串
|
|
103
|
+
*/
|
|
104
|
+
function buildCookie(
|
|
105
|
+
name: string,
|
|
106
|
+
value: string,
|
|
107
|
+
options: {
|
|
108
|
+
secure?: boolean;
|
|
109
|
+
httpOnly?: boolean;
|
|
110
|
+
path?: string;
|
|
111
|
+
domain?: string;
|
|
112
|
+
sameSite?: 'strict' | 'lax' | 'none';
|
|
113
|
+
maxAge?: number;
|
|
114
|
+
},
|
|
115
|
+
): string {
|
|
116
|
+
let cookie = `${name}=${value}`;
|
|
117
|
+
|
|
118
|
+
if (options.path) {
|
|
119
|
+
cookie += `; Path=${options.path}`;
|
|
120
|
+
}
|
|
121
|
+
|
|
122
|
+
if (options.domain) {
|
|
123
|
+
cookie += `; Domain=${options.domain}`;
|
|
124
|
+
}
|
|
125
|
+
|
|
126
|
+
if (options.maxAge) {
|
|
127
|
+
cookie += `; Max-Age=${Math.floor(options.maxAge / 1000)}`;
|
|
128
|
+
}
|
|
129
|
+
|
|
130
|
+
if (options.secure) {
|
|
131
|
+
cookie += '; Secure';
|
|
132
|
+
}
|
|
133
|
+
|
|
134
|
+
if (options.httpOnly) {
|
|
135
|
+
cookie += '; HttpOnly';
|
|
136
|
+
}
|
|
137
|
+
|
|
138
|
+
if (options.sameSite) {
|
|
139
|
+
cookie += `; SameSite=${options.sameSite}`;
|
|
140
|
+
}
|
|
141
|
+
|
|
142
|
+
return cookie;
|
|
143
|
+
}
|
|
@@ -0,0 +1,218 @@
|
|
|
1
|
+
import { Injectable } from '../di/decorators';
|
|
2
|
+
import { Inject } from '../di/decorators';
|
|
3
|
+
import type {
|
|
4
|
+
SessionStore,
|
|
5
|
+
SessionModuleOptions,
|
|
6
|
+
Session,
|
|
7
|
+
SessionData,
|
|
8
|
+
} from './types';
|
|
9
|
+
import { SESSION_OPTIONS_TOKEN } from './types';
|
|
10
|
+
import { randomBytes } from 'crypto';
|
|
11
|
+
|
|
12
|
+
/**
|
|
13
|
+
* Session 服务
|
|
14
|
+
*/
|
|
15
|
+
@Injectable()
|
|
16
|
+
export class SessionService {
|
|
17
|
+
private store: SessionStore;
|
|
18
|
+
private name: string;
|
|
19
|
+
private maxAge: number;
|
|
20
|
+
private rolling: boolean;
|
|
21
|
+
private cookieOptions: Required<SessionModuleOptions>['cookie'];
|
|
22
|
+
|
|
23
|
+
public constructor(
|
|
24
|
+
@Inject(SESSION_OPTIONS_TOKEN) options: SessionModuleOptions,
|
|
25
|
+
) {
|
|
26
|
+
this.store = options.store!;
|
|
27
|
+
this.name = options.name ?? 'sessionId';
|
|
28
|
+
this.maxAge = options.maxAge ?? 86400000; // 24 小时
|
|
29
|
+
this.rolling = options.rolling ?? true;
|
|
30
|
+
this.cookieOptions = {
|
|
31
|
+
secure: options.cookie?.secure ?? false,
|
|
32
|
+
httpOnly: options.cookie?.httpOnly ?? true,
|
|
33
|
+
path: options.cookie?.path ?? '/',
|
|
34
|
+
domain: options.cookie?.domain,
|
|
35
|
+
sameSite: options.cookie?.sameSite ?? 'lax',
|
|
36
|
+
};
|
|
37
|
+
}
|
|
38
|
+
|
|
39
|
+
/**
|
|
40
|
+
* 生成 Session ID
|
|
41
|
+
* @returns Session ID
|
|
42
|
+
*/
|
|
43
|
+
private generateSessionId(): string {
|
|
44
|
+
return randomBytes(32).toString('hex');
|
|
45
|
+
}
|
|
46
|
+
|
|
47
|
+
/**
|
|
48
|
+
* 创建新 Session
|
|
49
|
+
* @param initialData - 初始数据
|
|
50
|
+
* @returns Session
|
|
51
|
+
*/
|
|
52
|
+
public async create(initialData: SessionData = {}): Promise<Session> {
|
|
53
|
+
const now = Date.now();
|
|
54
|
+
const session: Session = {
|
|
55
|
+
id: this.generateSessionId(),
|
|
56
|
+
data: initialData,
|
|
57
|
+
createdAt: now,
|
|
58
|
+
lastAccessedAt: now,
|
|
59
|
+
expiresAt: now + this.maxAge,
|
|
60
|
+
};
|
|
61
|
+
|
|
62
|
+
await this.store.set(session, this.maxAge);
|
|
63
|
+
return session;
|
|
64
|
+
}
|
|
65
|
+
|
|
66
|
+
/**
|
|
67
|
+
* 获取 Session
|
|
68
|
+
* @param sessionId - Session ID
|
|
69
|
+
* @returns Session,如果不存在或已过期则返回 undefined
|
|
70
|
+
*/
|
|
71
|
+
public async get(sessionId: string): Promise<Session | undefined> {
|
|
72
|
+
const session = await this.store.get(sessionId);
|
|
73
|
+
if (!session) {
|
|
74
|
+
return undefined;
|
|
75
|
+
}
|
|
76
|
+
|
|
77
|
+
// 如果启用了 rolling,更新最后访问时间和过期时间
|
|
78
|
+
if (this.rolling) {
|
|
79
|
+
await this.touch(sessionId);
|
|
80
|
+
}
|
|
81
|
+
|
|
82
|
+
return session;
|
|
83
|
+
}
|
|
84
|
+
|
|
85
|
+
/**
|
|
86
|
+
* 更新 Session 数据
|
|
87
|
+
* @param sessionId - Session ID
|
|
88
|
+
* @param data - 新数据
|
|
89
|
+
* @returns 是否更新成功
|
|
90
|
+
*/
|
|
91
|
+
public async set(
|
|
92
|
+
sessionId: string,
|
|
93
|
+
data: SessionData,
|
|
94
|
+
): Promise<boolean> {
|
|
95
|
+
const session = await this.store.get(sessionId);
|
|
96
|
+
if (!session) {
|
|
97
|
+
return false;
|
|
98
|
+
}
|
|
99
|
+
|
|
100
|
+
session.data = { ...session.data, ...data };
|
|
101
|
+
session.lastAccessedAt = Date.now();
|
|
102
|
+
|
|
103
|
+
if (this.rolling) {
|
|
104
|
+
session.expiresAt = Date.now() + this.maxAge;
|
|
105
|
+
}
|
|
106
|
+
|
|
107
|
+
return this.store.set(session, this.maxAge);
|
|
108
|
+
}
|
|
109
|
+
|
|
110
|
+
/**
|
|
111
|
+
* 获取 Session 数据中的某个值
|
|
112
|
+
* @param sessionId - Session ID
|
|
113
|
+
* @param key - 数据键
|
|
114
|
+
* @returns 数据值,如果不存在则返回 undefined
|
|
115
|
+
*/
|
|
116
|
+
public async getValue<T = unknown>(
|
|
117
|
+
sessionId: string,
|
|
118
|
+
key: string,
|
|
119
|
+
): Promise<T | undefined> {
|
|
120
|
+
const session = await this.store.get(sessionId);
|
|
121
|
+
if (!session) {
|
|
122
|
+
return undefined;
|
|
123
|
+
}
|
|
124
|
+
|
|
125
|
+
return session.data[key] as T | undefined;
|
|
126
|
+
}
|
|
127
|
+
|
|
128
|
+
/**
|
|
129
|
+
* 设置 Session 数据中的某个值
|
|
130
|
+
* @param sessionId - Session ID
|
|
131
|
+
* @param key - 数据键
|
|
132
|
+
* @param value - 数据值
|
|
133
|
+
* @returns 是否设置成功
|
|
134
|
+
*/
|
|
135
|
+
public async setValue<T = unknown>(
|
|
136
|
+
sessionId: string,
|
|
137
|
+
key: string,
|
|
138
|
+
value: T,
|
|
139
|
+
): Promise<boolean> {
|
|
140
|
+
const session = await this.store.get(sessionId);
|
|
141
|
+
if (!session) {
|
|
142
|
+
return false;
|
|
143
|
+
}
|
|
144
|
+
|
|
145
|
+
session.data[key] = value;
|
|
146
|
+
session.lastAccessedAt = Date.now();
|
|
147
|
+
|
|
148
|
+
if (this.rolling) {
|
|
149
|
+
session.expiresAt = Date.now() + this.maxAge;
|
|
150
|
+
}
|
|
151
|
+
|
|
152
|
+
return this.store.set(session, this.maxAge);
|
|
153
|
+
}
|
|
154
|
+
|
|
155
|
+
/**
|
|
156
|
+
* 删除 Session 数据中的某个值
|
|
157
|
+
* @param sessionId - Session ID
|
|
158
|
+
* @param key - 数据键
|
|
159
|
+
* @returns 是否删除成功
|
|
160
|
+
*/
|
|
161
|
+
public async deleteValue(sessionId: string, key: string): Promise<boolean> {
|
|
162
|
+
const session = await this.store.get(sessionId);
|
|
163
|
+
if (!session) {
|
|
164
|
+
return false;
|
|
165
|
+
}
|
|
166
|
+
|
|
167
|
+
delete session.data[key];
|
|
168
|
+
session.lastAccessedAt = Date.now();
|
|
169
|
+
|
|
170
|
+
if (this.rolling) {
|
|
171
|
+
session.expiresAt = Date.now() + this.maxAge;
|
|
172
|
+
}
|
|
173
|
+
|
|
174
|
+
return this.store.set(session, this.maxAge);
|
|
175
|
+
}
|
|
176
|
+
|
|
177
|
+
/**
|
|
178
|
+
* 删除 Session
|
|
179
|
+
* @param sessionId - Session ID
|
|
180
|
+
* @returns 是否删除成功
|
|
181
|
+
*/
|
|
182
|
+
public async delete(sessionId: string): Promise<boolean> {
|
|
183
|
+
return this.store.delete(sessionId);
|
|
184
|
+
}
|
|
185
|
+
|
|
186
|
+
/**
|
|
187
|
+
* 更新 Session 的最后访问时间
|
|
188
|
+
* @param sessionId - Session ID
|
|
189
|
+
* @returns 是否更新成功
|
|
190
|
+
*/
|
|
191
|
+
public async touch(sessionId: string): Promise<boolean> {
|
|
192
|
+
return this.store.touch(sessionId);
|
|
193
|
+
}
|
|
194
|
+
|
|
195
|
+
/**
|
|
196
|
+
* 获取 Cookie 名称
|
|
197
|
+
* @returns Cookie 名称
|
|
198
|
+
*/
|
|
199
|
+
public getCookieName(): string {
|
|
200
|
+
return this.name;
|
|
201
|
+
}
|
|
202
|
+
|
|
203
|
+
/**
|
|
204
|
+
* 获取 Cookie 选项
|
|
205
|
+
* @returns Cookie 选项
|
|
206
|
+
*/
|
|
207
|
+
public getCookieOptions(): Required<SessionModuleOptions>['cookie'] {
|
|
208
|
+
return this.cookieOptions;
|
|
209
|
+
}
|
|
210
|
+
|
|
211
|
+
/**
|
|
212
|
+
* 获取最大存活时间
|
|
213
|
+
* @returns 最大存活时间(毫秒)
|
|
214
|
+
*/
|
|
215
|
+
public getMaxAge(): number {
|
|
216
|
+
return this.maxAge;
|
|
217
|
+
}
|
|
218
|
+
}
|
|
@@ -0,0 +1,69 @@
|
|
|
1
|
+
import { Module, MODULE_METADATA_KEY, type ModuleProvider } from '../di/module';
|
|
2
|
+
|
|
3
|
+
import { SessionService } from './service';
|
|
4
|
+
import {
|
|
5
|
+
SESSION_OPTIONS_TOKEN,
|
|
6
|
+
SESSION_SERVICE_TOKEN,
|
|
7
|
+
MemorySessionStore,
|
|
8
|
+
type SessionModuleOptions,
|
|
9
|
+
type SessionStore,
|
|
10
|
+
} from './types';
|
|
11
|
+
|
|
12
|
+
@Module({
|
|
13
|
+
providers: [],
|
|
14
|
+
})
|
|
15
|
+
export class SessionModule {
|
|
16
|
+
/**
|
|
17
|
+
* 创建 Session 模块
|
|
18
|
+
* @param options - 模块配置
|
|
19
|
+
*/
|
|
20
|
+
public static forRoot(
|
|
21
|
+
options: SessionModuleOptions = {},
|
|
22
|
+
): typeof SessionModule {
|
|
23
|
+
const providers: ModuleProvider[] = [];
|
|
24
|
+
|
|
25
|
+
// 如果没有提供 store,使用默认的内存存储
|
|
26
|
+
const store: SessionStore =
|
|
27
|
+
options.store ??
|
|
28
|
+
new MemorySessionStore({
|
|
29
|
+
cleanupInterval: 60000, // 每分钟清理一次
|
|
30
|
+
});
|
|
31
|
+
|
|
32
|
+
const service = new SessionService({
|
|
33
|
+
store,
|
|
34
|
+
name: options.name,
|
|
35
|
+
maxAge: options.maxAge,
|
|
36
|
+
rolling: options.rolling,
|
|
37
|
+
cookie: options.cookie,
|
|
38
|
+
});
|
|
39
|
+
|
|
40
|
+
providers.push(
|
|
41
|
+
{
|
|
42
|
+
provide: SESSION_SERVICE_TOKEN,
|
|
43
|
+
useValue: service,
|
|
44
|
+
},
|
|
45
|
+
{
|
|
46
|
+
provide: SESSION_OPTIONS_TOKEN,
|
|
47
|
+
useValue: options,
|
|
48
|
+
},
|
|
49
|
+
SessionService,
|
|
50
|
+
);
|
|
51
|
+
|
|
52
|
+
// 动态更新模块元数据
|
|
53
|
+
const existingMetadata =
|
|
54
|
+
Reflect.getMetadata(MODULE_METADATA_KEY, SessionModule) || {};
|
|
55
|
+
const metadata = {
|
|
56
|
+
...existingMetadata,
|
|
57
|
+
providers: [...(existingMetadata.providers || []), ...providers],
|
|
58
|
+
exports: [
|
|
59
|
+
...(existingMetadata.exports || []),
|
|
60
|
+
SESSION_SERVICE_TOKEN,
|
|
61
|
+
SESSION_OPTIONS_TOKEN,
|
|
62
|
+
SessionService,
|
|
63
|
+
],
|
|
64
|
+
};
|
|
65
|
+
Reflect.defineMetadata(MODULE_METADATA_KEY, metadata, SessionModule);
|
|
66
|
+
|
|
67
|
+
return SessionModule;
|
|
68
|
+
}
|
|
69
|
+
}
|