@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,373 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Session 数据
|
|
3
|
+
*/
|
|
4
|
+
export interface SessionData {
|
|
5
|
+
[key: string]: unknown;
|
|
6
|
+
}
|
|
7
|
+
|
|
8
|
+
/**
|
|
9
|
+
* Session 信息
|
|
10
|
+
*/
|
|
11
|
+
export interface Session {
|
|
12
|
+
/**
|
|
13
|
+
* Session ID
|
|
14
|
+
*/
|
|
15
|
+
id: string;
|
|
16
|
+
|
|
17
|
+
/**
|
|
18
|
+
* Session 数据
|
|
19
|
+
*/
|
|
20
|
+
data: SessionData;
|
|
21
|
+
|
|
22
|
+
/**
|
|
23
|
+
* 创建时间
|
|
24
|
+
*/
|
|
25
|
+
createdAt: number;
|
|
26
|
+
|
|
27
|
+
/**
|
|
28
|
+
* 最后访问时间
|
|
29
|
+
*/
|
|
30
|
+
lastAccessedAt: number;
|
|
31
|
+
|
|
32
|
+
/**
|
|
33
|
+
* 过期时间(毫秒时间戳)
|
|
34
|
+
*/
|
|
35
|
+
expiresAt: number;
|
|
36
|
+
}
|
|
37
|
+
|
|
38
|
+
/**
|
|
39
|
+
* Session 存储接口
|
|
40
|
+
*/
|
|
41
|
+
export interface SessionStore {
|
|
42
|
+
/**
|
|
43
|
+
* 获取 Session
|
|
44
|
+
* @param sessionId - Session ID
|
|
45
|
+
* @returns Session,如果不存在或已过期则返回 undefined
|
|
46
|
+
*/
|
|
47
|
+
get(sessionId: string): Promise<Session | undefined>;
|
|
48
|
+
|
|
49
|
+
/**
|
|
50
|
+
* 设置 Session
|
|
51
|
+
* @param session - Session 信息
|
|
52
|
+
* @param maxAge - 最大存活时间(毫秒)
|
|
53
|
+
* @returns 是否设置成功
|
|
54
|
+
*/
|
|
55
|
+
set(session: Session, maxAge: number): Promise<boolean>;
|
|
56
|
+
|
|
57
|
+
/**
|
|
58
|
+
* 删除 Session
|
|
59
|
+
* @param sessionId - Session ID
|
|
60
|
+
* @returns 是否删除成功
|
|
61
|
+
*/
|
|
62
|
+
delete(sessionId: string): Promise<boolean>;
|
|
63
|
+
|
|
64
|
+
/**
|
|
65
|
+
* 检查 Session 是否存在
|
|
66
|
+
* @param sessionId - Session ID
|
|
67
|
+
* @returns 是否存在
|
|
68
|
+
*/
|
|
69
|
+
has(sessionId: string): Promise<boolean>;
|
|
70
|
+
|
|
71
|
+
/**
|
|
72
|
+
* 更新 Session 的最后访问时间
|
|
73
|
+
* @param sessionId - Session ID
|
|
74
|
+
* @returns 是否更新成功
|
|
75
|
+
*/
|
|
76
|
+
touch(sessionId: string): Promise<boolean>;
|
|
77
|
+
|
|
78
|
+
/**
|
|
79
|
+
* 清空所有 Session
|
|
80
|
+
* @returns 是否清空成功
|
|
81
|
+
*/
|
|
82
|
+
clear(): Promise<boolean>;
|
|
83
|
+
}
|
|
84
|
+
|
|
85
|
+
/**
|
|
86
|
+
* 内存 Session 存储实现
|
|
87
|
+
*/
|
|
88
|
+
export class MemorySessionStore implements SessionStore {
|
|
89
|
+
private store: Map<string, Session> = new Map();
|
|
90
|
+
private cleanupInterval?: ReturnType<typeof setInterval>;
|
|
91
|
+
|
|
92
|
+
public constructor(options?: { cleanupInterval?: number }) {
|
|
93
|
+
// 定期清理过期 Session
|
|
94
|
+
if (options?.cleanupInterval !== undefined) {
|
|
95
|
+
this.cleanupInterval = setInterval(() => {
|
|
96
|
+
this.cleanup();
|
|
97
|
+
}, options.cleanupInterval);
|
|
98
|
+
}
|
|
99
|
+
}
|
|
100
|
+
|
|
101
|
+
public async get(sessionId: string): Promise<Session | undefined> {
|
|
102
|
+
const session = this.store.get(sessionId);
|
|
103
|
+
if (!session) {
|
|
104
|
+
return undefined;
|
|
105
|
+
}
|
|
106
|
+
|
|
107
|
+
// 检查是否过期
|
|
108
|
+
if (Date.now() > session.expiresAt) {
|
|
109
|
+
this.store.delete(sessionId);
|
|
110
|
+
return undefined;
|
|
111
|
+
}
|
|
112
|
+
|
|
113
|
+
return session;
|
|
114
|
+
}
|
|
115
|
+
|
|
116
|
+
public async set(session: Session, maxAge: number): Promise<boolean> {
|
|
117
|
+
const now = Date.now();
|
|
118
|
+
session.expiresAt = now + maxAge;
|
|
119
|
+
session.lastAccessedAt = now;
|
|
120
|
+
this.store.set(session.id, session);
|
|
121
|
+
return true;
|
|
122
|
+
}
|
|
123
|
+
|
|
124
|
+
public async delete(sessionId: string): Promise<boolean> {
|
|
125
|
+
return this.store.delete(sessionId);
|
|
126
|
+
}
|
|
127
|
+
|
|
128
|
+
public async has(sessionId: string): Promise<boolean> {
|
|
129
|
+
const session = this.store.get(sessionId);
|
|
130
|
+
if (!session) {
|
|
131
|
+
return false;
|
|
132
|
+
}
|
|
133
|
+
|
|
134
|
+
// 检查是否过期
|
|
135
|
+
if (Date.now() > session.expiresAt) {
|
|
136
|
+
this.store.delete(sessionId);
|
|
137
|
+
return false;
|
|
138
|
+
}
|
|
139
|
+
|
|
140
|
+
return true;
|
|
141
|
+
}
|
|
142
|
+
|
|
143
|
+
public async touch(sessionId: string): Promise<boolean> {
|
|
144
|
+
const session = this.store.get(sessionId);
|
|
145
|
+
if (!session) {
|
|
146
|
+
return false;
|
|
147
|
+
}
|
|
148
|
+
|
|
149
|
+
// 检查是否过期
|
|
150
|
+
if (Date.now() > session.expiresAt) {
|
|
151
|
+
this.store.delete(sessionId);
|
|
152
|
+
return false;
|
|
153
|
+
}
|
|
154
|
+
|
|
155
|
+
// 更新最后访问时间
|
|
156
|
+
session.lastAccessedAt = Date.now();
|
|
157
|
+
return true;
|
|
158
|
+
}
|
|
159
|
+
|
|
160
|
+
public async clear(): Promise<boolean> {
|
|
161
|
+
this.store.clear();
|
|
162
|
+
return true;
|
|
163
|
+
}
|
|
164
|
+
|
|
165
|
+
/**
|
|
166
|
+
* 清理过期 Session
|
|
167
|
+
*/
|
|
168
|
+
private cleanup(): void {
|
|
169
|
+
const now = Date.now();
|
|
170
|
+
for (const [sessionId, session] of this.store.entries()) {
|
|
171
|
+
if (now > session.expiresAt) {
|
|
172
|
+
this.store.delete(sessionId);
|
|
173
|
+
}
|
|
174
|
+
}
|
|
175
|
+
}
|
|
176
|
+
|
|
177
|
+
/**
|
|
178
|
+
* 销毁存储,清理定时器
|
|
179
|
+
*/
|
|
180
|
+
public destroy(): void {
|
|
181
|
+
if (this.cleanupInterval) {
|
|
182
|
+
clearInterval(this.cleanupInterval);
|
|
183
|
+
this.cleanupInterval = undefined;
|
|
184
|
+
}
|
|
185
|
+
this.store.clear();
|
|
186
|
+
}
|
|
187
|
+
}
|
|
188
|
+
|
|
189
|
+
/**
|
|
190
|
+
* Redis Session 存储实现(需要 redis 包)
|
|
191
|
+
*/
|
|
192
|
+
export interface RedisSessionStoreOptions {
|
|
193
|
+
/**
|
|
194
|
+
* Redis 客户端(需要用户提供)
|
|
195
|
+
*/
|
|
196
|
+
client: {
|
|
197
|
+
get(key: string): Promise<string | null>;
|
|
198
|
+
set(
|
|
199
|
+
key: string,
|
|
200
|
+
value: string,
|
|
201
|
+
options?: { PX?: number },
|
|
202
|
+
): Promise<void>;
|
|
203
|
+
del(key: string): Promise<void>;
|
|
204
|
+
exists(key: string): Promise<number>;
|
|
205
|
+
expire(key: string, seconds: number): Promise<void>;
|
|
206
|
+
};
|
|
207
|
+
/**
|
|
208
|
+
* 键前缀
|
|
209
|
+
* @default 'session:'
|
|
210
|
+
*/
|
|
211
|
+
keyPrefix?: string;
|
|
212
|
+
}
|
|
213
|
+
|
|
214
|
+
export class RedisSessionStore implements SessionStore {
|
|
215
|
+
private client: RedisSessionStoreOptions['client'];
|
|
216
|
+
private keyPrefix: string;
|
|
217
|
+
|
|
218
|
+
public constructor(options: RedisSessionStoreOptions) {
|
|
219
|
+
this.client = options.client;
|
|
220
|
+
this.keyPrefix = options.keyPrefix ?? 'session:';
|
|
221
|
+
}
|
|
222
|
+
|
|
223
|
+
private getKey(sessionId: string): string {
|
|
224
|
+
return `${this.keyPrefix}${sessionId}`;
|
|
225
|
+
}
|
|
226
|
+
|
|
227
|
+
public async get(sessionId: string): Promise<Session | undefined> {
|
|
228
|
+
try {
|
|
229
|
+
const value = await this.client.get(this.getKey(sessionId));
|
|
230
|
+
if (value === null) {
|
|
231
|
+
return undefined;
|
|
232
|
+
}
|
|
233
|
+
return JSON.parse(value) as Session;
|
|
234
|
+
} catch {
|
|
235
|
+
return undefined;
|
|
236
|
+
}
|
|
237
|
+
}
|
|
238
|
+
|
|
239
|
+
public async set(session: Session, maxAge: number): Promise<boolean> {
|
|
240
|
+
try {
|
|
241
|
+
const serialized = JSON.stringify(session);
|
|
242
|
+
await this.client.set(this.getKey(session.id), serialized, {
|
|
243
|
+
PX: maxAge,
|
|
244
|
+
});
|
|
245
|
+
return true;
|
|
246
|
+
} catch {
|
|
247
|
+
return false;
|
|
248
|
+
}
|
|
249
|
+
}
|
|
250
|
+
|
|
251
|
+
public async delete(sessionId: string): Promise<boolean> {
|
|
252
|
+
try {
|
|
253
|
+
await this.client.del(this.getKey(sessionId));
|
|
254
|
+
return true;
|
|
255
|
+
} catch {
|
|
256
|
+
return false;
|
|
257
|
+
}
|
|
258
|
+
}
|
|
259
|
+
|
|
260
|
+
public async has(sessionId: string): Promise<boolean> {
|
|
261
|
+
try {
|
|
262
|
+
const result = await this.client.exists(this.getKey(sessionId));
|
|
263
|
+
return result === 1;
|
|
264
|
+
} catch {
|
|
265
|
+
return false;
|
|
266
|
+
}
|
|
267
|
+
}
|
|
268
|
+
|
|
269
|
+
public async touch(sessionId: string): Promise<boolean> {
|
|
270
|
+
try {
|
|
271
|
+
const session = await this.get(sessionId);
|
|
272
|
+
if (!session) {
|
|
273
|
+
return false;
|
|
274
|
+
}
|
|
275
|
+
|
|
276
|
+
// 更新最后访问时间
|
|
277
|
+
session.lastAccessedAt = Date.now();
|
|
278
|
+
const remainingTime = session.expiresAt - Date.now();
|
|
279
|
+
if (remainingTime > 0) {
|
|
280
|
+
await this.client.expire(this.getKey(sessionId), Math.floor(remainingTime / 1000));
|
|
281
|
+
return true;
|
|
282
|
+
}
|
|
283
|
+
|
|
284
|
+
return false;
|
|
285
|
+
} catch {
|
|
286
|
+
return false;
|
|
287
|
+
}
|
|
288
|
+
}
|
|
289
|
+
|
|
290
|
+
public async clear(): Promise<boolean> {
|
|
291
|
+
// Redis 没有直接清空所有 session 的方法
|
|
292
|
+
// 实际实现中可能需要使用 SCAN 命令遍历所有键
|
|
293
|
+
// 这里简化处理,返回 false
|
|
294
|
+
return false;
|
|
295
|
+
}
|
|
296
|
+
}
|
|
297
|
+
|
|
298
|
+
/**
|
|
299
|
+
* SessionModule 配置选项
|
|
300
|
+
*/
|
|
301
|
+
export interface SessionModuleOptions {
|
|
302
|
+
/**
|
|
303
|
+
* Session 存储实现
|
|
304
|
+
* @default MemorySessionStore
|
|
305
|
+
*/
|
|
306
|
+
store?: SessionStore;
|
|
307
|
+
|
|
308
|
+
/**
|
|
309
|
+
* Session 名称(Cookie 名称)
|
|
310
|
+
* @default 'sessionId'
|
|
311
|
+
*/
|
|
312
|
+
name?: string;
|
|
313
|
+
|
|
314
|
+
/**
|
|
315
|
+
* Session 最大存活时间(毫秒)
|
|
316
|
+
* @default 86400000 (24 小时)
|
|
317
|
+
*/
|
|
318
|
+
maxAge?: number;
|
|
319
|
+
|
|
320
|
+
/**
|
|
321
|
+
* 是否在每次访问时更新过期时间
|
|
322
|
+
* @default true
|
|
323
|
+
*/
|
|
324
|
+
rolling?: boolean;
|
|
325
|
+
|
|
326
|
+
/**
|
|
327
|
+
* Cookie 选项
|
|
328
|
+
*/
|
|
329
|
+
cookie?: {
|
|
330
|
+
/**
|
|
331
|
+
* 是否只在 HTTPS 下发送
|
|
332
|
+
* @default false
|
|
333
|
+
*/
|
|
334
|
+
secure?: boolean;
|
|
335
|
+
|
|
336
|
+
/**
|
|
337
|
+
* 是否只能通过 HTTP 访问(不能通过 JavaScript)
|
|
338
|
+
* @default true
|
|
339
|
+
*/
|
|
340
|
+
httpOnly?: boolean;
|
|
341
|
+
|
|
342
|
+
/**
|
|
343
|
+
* Cookie 路径
|
|
344
|
+
* @default '/'
|
|
345
|
+
*/
|
|
346
|
+
path?: string;
|
|
347
|
+
|
|
348
|
+
/**
|
|
349
|
+
* Cookie 域名
|
|
350
|
+
*/
|
|
351
|
+
domain?: string;
|
|
352
|
+
|
|
353
|
+
/**
|
|
354
|
+
* SameSite 属性
|
|
355
|
+
* @default 'lax'
|
|
356
|
+
*/
|
|
357
|
+
sameSite?: 'strict' | 'lax' | 'none';
|
|
358
|
+
};
|
|
359
|
+
}
|
|
360
|
+
|
|
361
|
+
/**
|
|
362
|
+
* SessionService Token
|
|
363
|
+
*/
|
|
364
|
+
export const SESSION_SERVICE_TOKEN = Symbol(
|
|
365
|
+
'@dangao/bun-server:session:service',
|
|
366
|
+
);
|
|
367
|
+
|
|
368
|
+
/**
|
|
369
|
+
* SessionModule Options Token
|
|
370
|
+
*/
|
|
371
|
+
export const SESSION_OPTIONS_TOKEN = Symbol(
|
|
372
|
+
'@dangao/bun-server:session:options',
|
|
373
|
+
);
|
|
@@ -0,0 +1,133 @@
|
|
|
1
|
+
import 'reflect-metadata';
|
|
2
|
+
import type {
|
|
3
|
+
ApiBodyMetadata,
|
|
4
|
+
ApiOperationMetadata,
|
|
5
|
+
ApiParamMetadata,
|
|
6
|
+
ApiResponseMetadata,
|
|
7
|
+
} from './types';
|
|
8
|
+
|
|
9
|
+
/**
|
|
10
|
+
* Swagger 元数据键
|
|
11
|
+
*/
|
|
12
|
+
const API_TAG_METADATA_KEY = Symbol('swagger:api-tag');
|
|
13
|
+
const API_OPERATION_METADATA_KEY = Symbol('swagger:api-operation');
|
|
14
|
+
const API_PARAM_METADATA_KEY = Symbol('swagger:api-param');
|
|
15
|
+
const API_BODY_METADATA_KEY = Symbol('swagger:api-body');
|
|
16
|
+
const API_RESPONSE_METADATA_KEY = Symbol('swagger:api-response');
|
|
17
|
+
|
|
18
|
+
/**
|
|
19
|
+
* API 标签装饰器
|
|
20
|
+
* 用于标记控制器或操作的标签
|
|
21
|
+
* @param metadata - 标签元数据
|
|
22
|
+
*/
|
|
23
|
+
export function ApiTags(...tags: string[]): ClassDecorator & MethodDecorator {
|
|
24
|
+
return function (target: unknown, propertyKey?: string | symbol) {
|
|
25
|
+
if (propertyKey === undefined) {
|
|
26
|
+
// 类装饰器
|
|
27
|
+
Reflect.defineMetadata(API_TAG_METADATA_KEY, tags, target as Object);
|
|
28
|
+
} else {
|
|
29
|
+
// 方法装饰器
|
|
30
|
+
const existingTags: string[] =
|
|
31
|
+
Reflect.getMetadata(API_TAG_METADATA_KEY, target as Object, propertyKey) || [];
|
|
32
|
+
Reflect.defineMetadata(API_TAG_METADATA_KEY, [...existingTags, ...tags], target as Object, propertyKey);
|
|
33
|
+
}
|
|
34
|
+
};
|
|
35
|
+
}
|
|
36
|
+
|
|
37
|
+
/**
|
|
38
|
+
* API 操作装饰器
|
|
39
|
+
* 用于描述 API 操作
|
|
40
|
+
* @param metadata - 操作元数据
|
|
41
|
+
*/
|
|
42
|
+
export function ApiOperation(metadata: ApiOperationMetadata): MethodDecorator {
|
|
43
|
+
return function (target: unknown, propertyKey: string | symbol) {
|
|
44
|
+
Reflect.defineMetadata(API_OPERATION_METADATA_KEY, metadata, target as Object, propertyKey);
|
|
45
|
+
};
|
|
46
|
+
}
|
|
47
|
+
|
|
48
|
+
/**
|
|
49
|
+
* API 参数装饰器
|
|
50
|
+
* 用于描述 API 参数
|
|
51
|
+
* @param metadata - 参数元数据
|
|
52
|
+
*/
|
|
53
|
+
export function ApiParam(metadata: ApiParamMetadata): ParameterDecorator {
|
|
54
|
+
return function (target: unknown, propertyKey: string | symbol | undefined, parameterIndex: number) {
|
|
55
|
+
const existingParams: Array<{ index: number; metadata: ApiParamMetadata }> =
|
|
56
|
+
Reflect.getMetadata(API_PARAM_METADATA_KEY, target as Object, propertyKey!) || [];
|
|
57
|
+
existingParams.push({ index: parameterIndex, metadata });
|
|
58
|
+
Reflect.defineMetadata(API_PARAM_METADATA_KEY, existingParams, target as Object, propertyKey!);
|
|
59
|
+
};
|
|
60
|
+
}
|
|
61
|
+
|
|
62
|
+
/**
|
|
63
|
+
* API 请求体装饰器
|
|
64
|
+
* 用于描述请求体
|
|
65
|
+
* @param metadata - 请求体元数据
|
|
66
|
+
*/
|
|
67
|
+
export function ApiBody(metadata: ApiBodyMetadata): MethodDecorator {
|
|
68
|
+
return function (target: unknown, propertyKey: string | symbol) {
|
|
69
|
+
Reflect.defineMetadata(API_BODY_METADATA_KEY, metadata, target as Object, propertyKey);
|
|
70
|
+
};
|
|
71
|
+
}
|
|
72
|
+
|
|
73
|
+
/**
|
|
74
|
+
* API 响应装饰器
|
|
75
|
+
* 用于描述 API 响应
|
|
76
|
+
* @param metadata - 响应元数据
|
|
77
|
+
*/
|
|
78
|
+
export function ApiResponse(metadata: ApiResponseMetadata): MethodDecorator {
|
|
79
|
+
return function (target: unknown, propertyKey: string | symbol) {
|
|
80
|
+
const existingResponses: ApiResponseMetadata[] =
|
|
81
|
+
Reflect.getMetadata(API_RESPONSE_METADATA_KEY, target as Object, propertyKey) || [];
|
|
82
|
+
existingResponses.push(metadata);
|
|
83
|
+
Reflect.defineMetadata(API_RESPONSE_METADATA_KEY, existingResponses, target as Object, propertyKey);
|
|
84
|
+
};
|
|
85
|
+
}
|
|
86
|
+
|
|
87
|
+
/**
|
|
88
|
+
* 获取 API 标签
|
|
89
|
+
*/
|
|
90
|
+
export function getApiTags(target: unknown, propertyKey?: string | symbol): string[] {
|
|
91
|
+
if (propertyKey === undefined) {
|
|
92
|
+
return Reflect.getMetadata(API_TAG_METADATA_KEY, target as Object) || [];
|
|
93
|
+
}
|
|
94
|
+
return Reflect.getMetadata(API_TAG_METADATA_KEY, target as Object, propertyKey) || [];
|
|
95
|
+
}
|
|
96
|
+
|
|
97
|
+
/**
|
|
98
|
+
* 获取 API 操作元数据
|
|
99
|
+
*/
|
|
100
|
+
export function getApiOperation(
|
|
101
|
+
target: unknown,
|
|
102
|
+
propertyKey: string | symbol,
|
|
103
|
+
): ApiOperationMetadata | undefined {
|
|
104
|
+
return Reflect.getMetadata(API_OPERATION_METADATA_KEY, target as Object, propertyKey);
|
|
105
|
+
}
|
|
106
|
+
|
|
107
|
+
/**
|
|
108
|
+
* 获取 API 参数元数据
|
|
109
|
+
*/
|
|
110
|
+
export function getApiParams(
|
|
111
|
+
target: unknown,
|
|
112
|
+
propertyKey: string | symbol,
|
|
113
|
+
): Array<{ index: number; metadata: ApiParamMetadata }> {
|
|
114
|
+
return Reflect.getMetadata(API_PARAM_METADATA_KEY, target as Object, propertyKey) || [];
|
|
115
|
+
}
|
|
116
|
+
|
|
117
|
+
/**
|
|
118
|
+
* 获取 API 请求体元数据
|
|
119
|
+
*/
|
|
120
|
+
export function getApiBody(target: unknown, propertyKey: string | symbol): ApiBodyMetadata | undefined {
|
|
121
|
+
return Reflect.getMetadata(API_BODY_METADATA_KEY, target as Object, propertyKey);
|
|
122
|
+
}
|
|
123
|
+
|
|
124
|
+
/**
|
|
125
|
+
* 获取 API 响应元数据
|
|
126
|
+
*/
|
|
127
|
+
export function getApiResponses(
|
|
128
|
+
target: unknown,
|
|
129
|
+
propertyKey: string | symbol,
|
|
130
|
+
): ApiResponseMetadata[] {
|
|
131
|
+
return Reflect.getMetadata(API_RESPONSE_METADATA_KEY, target as Object, propertyKey) || [];
|
|
132
|
+
}
|
|
133
|
+
|