vafast 0.1.10 → 0.1.12
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/auth/token.d.ts +40 -0
- package/dist/auth/token.js +124 -0
- package/dist/defineRoute.d.ts +2 -0
- package/dist/defineRoute.js +3 -0
- package/dist/index.d.ts +14 -459
- package/dist/index.js +14 -1400
- package/dist/index.js.map +1 -0
- package/dist/middleware/auth.d.ts +14 -0
- package/dist/middleware/auth.js +106 -0
- package/dist/middleware/authMiddleware.d.ts +2 -0
- package/dist/middleware/authMiddleware.js +13 -0
- package/dist/middleware/component-renderer.d.ts +6 -0
- package/dist/middleware/component-renderer.js +136 -0
- package/dist/middleware/component-router.d.ts +10 -0
- package/dist/middleware/component-router.js +39 -0
- package/dist/middleware/cors.d.ts +9 -0
- package/dist/middleware/cors.js +30 -0
- package/dist/middleware/rateLimit.d.ts +8 -0
- package/dist/middleware/rateLimit.js +33 -0
- package/dist/middleware.d.ts +18 -0
- package/dist/middleware.js +51 -0
- package/dist/monitoring/index.d.ts +29 -0
- package/dist/monitoring/index.js +24 -0
- package/dist/monitoring/native-monitor.d.ts +38 -0
- package/dist/monitoring/native-monitor.js +176 -0
- package/dist/monitoring/types.d.ts +146 -0
- package/dist/monitoring/types.js +8 -0
- package/dist/router.d.ts +17 -0
- package/dist/router.js +74 -0
- package/dist/server/base-server.d.ts +38 -0
- package/dist/server/base-server.js +167 -0
- package/dist/server/component-server.d.ts +32 -0
- package/dist/server/component-server.js +146 -0
- package/dist/server/index.d.ts +7 -0
- package/dist/server/index.js +9 -0
- package/dist/server/server-factory.d.ts +42 -0
- package/dist/server/server-factory.js +70 -0
- package/dist/server/server.d.ts +7 -0
- package/dist/server/server.js +73 -0
- package/dist/types/component-route.d.ts +25 -0
- package/dist/types/component-route.js +1 -0
- package/dist/types/route.d.ts +37 -0
- package/dist/types/route.js +1 -0
- package/dist/types.d.ts +18 -0
- package/dist/types.js +1 -0
- package/dist/utils/base64url.d.ts +2 -0
- package/dist/utils/base64url.js +11 -0
- package/dist/utils/dependency-manager.d.ts +23 -0
- package/dist/utils/dependency-manager.js +67 -0
- package/dist/utils/go-await.d.ts +26 -0
- package/dist/utils/go-await.js +33 -0
- package/dist/utils/handle.d.ts +10 -0
- package/dist/utils/handle.js +24 -0
- package/dist/utils/html-renderer.d.ts +18 -0
- package/dist/utils/html-renderer.js +64 -0
- package/dist/utils/parsers.d.ts +36 -0
- package/dist/utils/parsers.js +126 -0
- package/dist/utils/path-matcher.d.ts +23 -0
- package/dist/utils/path-matcher.js +82 -0
- package/dist/utils/request-validator.d.ts +63 -0
- package/dist/utils/request-validator.js +94 -0
- package/dist/utils/response.d.ts +12 -0
- package/dist/utils/response.js +69 -0
- package/dist/utils/route-handler-factory.d.ts +50 -0
- package/dist/utils/route-handler-factory.js +181 -0
- package/dist/utils/validators/schema-validator.d.ts +66 -0
- package/dist/utils/validators/schema-validator.js +222 -0
- package/dist/utils/validators/schema-validators-ultra.d.ts +51 -0
- package/dist/utils/validators/schema-validators-ultra.js +289 -0
- package/dist/utils/validators/validators.d.ts +30 -0
- package/dist/utils/validators/validators.js +54 -0
- package/package.json +3 -4
package/dist/index.js
CHANGED
|
@@ -1,1400 +1,14 @@
|
|
|
1
|
-
|
|
2
|
-
|
|
3
|
-
|
|
4
|
-
|
|
5
|
-
|
|
6
|
-
|
|
7
|
-
*
|
|
8
|
-
|
|
9
|
-
|
|
10
|
-
|
|
11
|
-
|
|
12
|
-
|
|
13
|
-
|
|
14
|
-
|
|
15
|
-
...route,
|
|
16
|
-
fullPath: currentPath,
|
|
17
|
-
middlewareChain: currentMiddleware
|
|
18
|
-
});
|
|
19
|
-
else if ("children" in route && route.children) for (const child of route.children) processRoute(child, currentPath, currentMiddleware);
|
|
20
|
-
}
|
|
21
|
-
for (const route of routes) processRoute(route);
|
|
22
|
-
return flattened;
|
|
23
|
-
}
|
|
24
|
-
/**
|
|
25
|
-
* 标准化路径:去重斜杠、解码URL、处理结尾斜杠
|
|
26
|
-
*/
|
|
27
|
-
function normalizePath(path) {
|
|
28
|
-
let normalized = decodeURIComponent(path);
|
|
29
|
-
normalized = normalized.replace(/\/+/g, "/");
|
|
30
|
-
if (normalized === "") normalized = "/";
|
|
31
|
-
if (normalized !== "/" && normalized.endsWith("/")) normalized = normalized.slice(0, -1);
|
|
32
|
-
return normalized;
|
|
33
|
-
}
|
|
34
|
-
/**
|
|
35
|
-
* 匹配函数:支持动态路由和路径标准化
|
|
36
|
-
*/
|
|
37
|
-
function matchPath(pattern, path) {
|
|
38
|
-
const normalizedPath = normalizePath(path);
|
|
39
|
-
const patternParts = pattern.split("/").filter(Boolean);
|
|
40
|
-
const pathParts = normalizedPath.split("/").filter(Boolean);
|
|
41
|
-
const params = {};
|
|
42
|
-
for (let i = 0; i < patternParts.length; i++) {
|
|
43
|
-
const pat = patternParts[i];
|
|
44
|
-
const part = pathParts[i];
|
|
45
|
-
if (pat === "*") {
|
|
46
|
-
params["*"] = pathParts.slice(i).join("/");
|
|
47
|
-
return {
|
|
48
|
-
matched: true,
|
|
49
|
-
params
|
|
50
|
-
};
|
|
51
|
-
}
|
|
52
|
-
if (pat.startsWith(":")) {
|
|
53
|
-
if (!part) return {
|
|
54
|
-
matched: false,
|
|
55
|
-
params: {}
|
|
56
|
-
};
|
|
57
|
-
params[pat.slice(1)] = part;
|
|
58
|
-
continue;
|
|
59
|
-
}
|
|
60
|
-
if (pat !== part) return {
|
|
61
|
-
matched: false,
|
|
62
|
-
params: {}
|
|
63
|
-
};
|
|
64
|
-
}
|
|
65
|
-
if (patternParts.length !== pathParts.length) return {
|
|
66
|
-
matched: false,
|
|
67
|
-
params: {}
|
|
68
|
-
};
|
|
69
|
-
return {
|
|
70
|
-
matched: true,
|
|
71
|
-
params
|
|
72
|
-
};
|
|
73
|
-
}
|
|
74
|
-
|
|
75
|
-
//#endregion
|
|
76
|
-
//#region src/utils/response.ts
|
|
77
|
-
/** 生成 JSON 响应 */
|
|
78
|
-
function json(data, status = 200, headers = {}) {
|
|
79
|
-
if (Object.keys(headers).length === 0) return new Response(JSON.stringify(data), {
|
|
80
|
-
status,
|
|
81
|
-
headers: { "Content-Type": "application/json" }
|
|
82
|
-
});
|
|
83
|
-
const h = new Headers({
|
|
84
|
-
"Content-Type": "application/json",
|
|
85
|
-
...headers
|
|
86
|
-
});
|
|
87
|
-
return new Response(JSON.stringify(data), {
|
|
88
|
-
status,
|
|
89
|
-
headers: h
|
|
90
|
-
});
|
|
91
|
-
}
|
|
92
|
-
/** 生成重定向响应 */
|
|
93
|
-
function redirect(location, status = 302) {
|
|
94
|
-
return new Response(null, {
|
|
95
|
-
status,
|
|
96
|
-
headers: { Location: location }
|
|
97
|
-
});
|
|
98
|
-
}
|
|
99
|
-
/** 生成纯文本响应 */
|
|
100
|
-
function text(content, status = 200, headers = {}) {
|
|
101
|
-
const h = new Headers({
|
|
102
|
-
"Content-Type": "text/plain; charset=utf-8",
|
|
103
|
-
...headers
|
|
104
|
-
});
|
|
105
|
-
return new Response(content, {
|
|
106
|
-
status,
|
|
107
|
-
headers: h
|
|
108
|
-
});
|
|
109
|
-
}
|
|
110
|
-
/** 生成HTML响应 */
|
|
111
|
-
function html(content, status = 200, headers = {}) {
|
|
112
|
-
const h = new Headers({
|
|
113
|
-
"Content-Type": "text/html; charset=utf-8",
|
|
114
|
-
...headers
|
|
115
|
-
});
|
|
116
|
-
return new Response(content, {
|
|
117
|
-
status,
|
|
118
|
-
headers: h
|
|
119
|
-
});
|
|
120
|
-
}
|
|
121
|
-
/** 生成空响应(204 No Content) */
|
|
122
|
-
function empty(status = 204, headers = {}) {
|
|
123
|
-
return new Response(null, {
|
|
124
|
-
status,
|
|
125
|
-
headers
|
|
126
|
-
});
|
|
127
|
-
}
|
|
128
|
-
/** 生成流式响应 */
|
|
129
|
-
function stream(stream$1, status = 200, headers = {}) {
|
|
130
|
-
const h = new Headers({
|
|
131
|
-
"Content-Type": "application/octet-stream",
|
|
132
|
-
...headers
|
|
133
|
-
});
|
|
134
|
-
return new Response(stream$1, {
|
|
135
|
-
status,
|
|
136
|
-
headers: h
|
|
137
|
-
});
|
|
138
|
-
}
|
|
139
|
-
|
|
140
|
-
//#endregion
|
|
141
|
-
//#region src/middleware.ts
|
|
142
|
-
/** 中间件类型:使用 next() 传递给下一个处理 */
|
|
143
|
-
/** Vafast 自定义错误类型 */
|
|
144
|
-
var VafastError = class extends Error {
|
|
145
|
-
status;
|
|
146
|
-
type;
|
|
147
|
-
expose;
|
|
148
|
-
constructor(message, options = {}) {
|
|
149
|
-
super(message);
|
|
150
|
-
this.name = "VafastError";
|
|
151
|
-
this.status = options.status ?? 500;
|
|
152
|
-
this.type = options.type ?? "internal_error";
|
|
153
|
-
this.expose = options.expose ?? false;
|
|
154
|
-
if (options.cause) this.cause = options.cause;
|
|
155
|
-
}
|
|
156
|
-
};
|
|
157
|
-
/**
|
|
158
|
-
* 组合类型: 自动注入错误处理器进行中间件组合
|
|
159
|
-
*/
|
|
160
|
-
function composeMiddleware(middleware, finalHandler) {
|
|
161
|
-
const all = [errorHandler, ...middleware];
|
|
162
|
-
return function composedHandler(req) {
|
|
163
|
-
let i = -1;
|
|
164
|
-
const dispatch = (index) => {
|
|
165
|
-
if (index <= i) return Promise.reject(/* @__PURE__ */ new Error("next() called multiple times"));
|
|
166
|
-
i = index;
|
|
167
|
-
const fn = index < all.length ? all[index] : finalHandler;
|
|
168
|
-
return Promise.resolve(fn(req, (() => dispatch(index + 1))));
|
|
169
|
-
};
|
|
170
|
-
return dispatch(0);
|
|
171
|
-
};
|
|
172
|
-
}
|
|
173
|
-
/** 默认包含的全局错误处理器 */
|
|
174
|
-
const errorHandler = async (req, next) => {
|
|
175
|
-
try {
|
|
176
|
-
return await next();
|
|
177
|
-
} catch (err) {
|
|
178
|
-
console.error("未处理的错误:", err);
|
|
179
|
-
if (err instanceof VafastError) return json({
|
|
180
|
-
error: err.type,
|
|
181
|
-
message: err.expose ? err.message : "发生了一个错误"
|
|
182
|
-
}, err.status);
|
|
183
|
-
return json({
|
|
184
|
-
error: "internal_error",
|
|
185
|
-
message: "出现了一些问题"
|
|
186
|
-
}, 500);
|
|
187
|
-
}
|
|
188
|
-
};
|
|
189
|
-
|
|
190
|
-
//#endregion
|
|
191
|
-
//#region src/server/base-server.ts
|
|
192
|
-
/**
|
|
193
|
-
* 服务器基类
|
|
194
|
-
* 包含所有服务器类型的公共逻辑
|
|
195
|
-
*/
|
|
196
|
-
var BaseServer = class {
|
|
197
|
-
globalMiddleware = [];
|
|
198
|
-
use(mw) {
|
|
199
|
-
this.globalMiddleware.push(mw);
|
|
200
|
-
}
|
|
201
|
-
/**
|
|
202
|
-
* 打印扁平化后的路由信息,用于调试
|
|
203
|
-
*/
|
|
204
|
-
logFlattenedRoutes(routes, type = "路由") {
|
|
205
|
-
console.log(`🚀 扁平化后的${type}:`);
|
|
206
|
-
for (const route of routes) {
|
|
207
|
-
const method = route.method || "GET";
|
|
208
|
-
const path = route.fullPath || route.path;
|
|
209
|
-
console.log(` ${method} ${path}`);
|
|
210
|
-
if (route.middlewareChain && route.middlewareChain.length > 0) console.log(` 中间件链: ${route.middlewareChain.length} 个`);
|
|
211
|
-
}
|
|
212
|
-
console.log("");
|
|
213
|
-
}
|
|
214
|
-
/**
|
|
215
|
-
* 检测路由冲突
|
|
216
|
-
* 检查是否有路径相同但方法不同的路由,以及潜在的路径冲突
|
|
217
|
-
*/
|
|
218
|
-
detectRouteConflicts(routes) {
|
|
219
|
-
const pathGroups = /* @__PURE__ */ new Map();
|
|
220
|
-
for (const route of routes) {
|
|
221
|
-
const path = route.fullPath || route.path;
|
|
222
|
-
const method = route.method || "GET";
|
|
223
|
-
if (!pathGroups.has(path)) pathGroups.set(path, []);
|
|
224
|
-
pathGroups.get(path).push({
|
|
225
|
-
...route,
|
|
226
|
-
method
|
|
227
|
-
});
|
|
228
|
-
}
|
|
229
|
-
for (const [path, routeList] of pathGroups) if (routeList.length > 1) {
|
|
230
|
-
const methods = routeList.map((r) => r.method);
|
|
231
|
-
const uniqueMethods = [...new Set(methods)];
|
|
232
|
-
if (uniqueMethods.length === 1) {
|
|
233
|
-
console.warn(`⚠️ 路由冲突: ${uniqueMethods[0]} ${path} 定义了 ${routeList.length} 次`);
|
|
234
|
-
routeList.forEach((route, index) => {
|
|
235
|
-
console.warn(` ${index + 1}. ${route.method} ${path}`);
|
|
236
|
-
});
|
|
237
|
-
} else console.log(`ℹ️ 路径 ${path} 支持方法: ${uniqueMethods.join(", ")}`);
|
|
238
|
-
}
|
|
239
|
-
this.detectDynamicRouteConflicts(routes);
|
|
240
|
-
}
|
|
241
|
-
/**
|
|
242
|
-
* 检测动态路由的潜在冲突
|
|
243
|
-
*/
|
|
244
|
-
detectDynamicRouteConflicts(routes) {
|
|
245
|
-
const dynamicRoutes = routes.filter((r) => {
|
|
246
|
-
const path = r.fullPath || r.path;
|
|
247
|
-
return path.includes(":") || path.includes("*");
|
|
248
|
-
});
|
|
249
|
-
for (let i = 0; i < dynamicRoutes.length; i++) for (let j = i + 1; j < dynamicRoutes.length; j++) {
|
|
250
|
-
const route1 = dynamicRoutes[i];
|
|
251
|
-
const route2 = dynamicRoutes[j];
|
|
252
|
-
const method1 = route1.method || "GET";
|
|
253
|
-
const method2 = route2.method || "GET";
|
|
254
|
-
if (method1 === method2) {
|
|
255
|
-
const path1 = route1.fullPath || route1.path;
|
|
256
|
-
const path2 = route2.fullPath || route2.path;
|
|
257
|
-
if (this.pathsMayConflict(path1, path2)) console.warn(`⚠️ 潜在路由冲突: ${method1} ${path1} 可能与 ${path2} 冲突`);
|
|
258
|
-
}
|
|
259
|
-
}
|
|
260
|
-
}
|
|
261
|
-
/**
|
|
262
|
-
* 判断两个路径是否可能冲突
|
|
263
|
-
*/
|
|
264
|
-
pathsMayConflict(path1, path2) {
|
|
265
|
-
const parts1 = path1.split("/").filter(Boolean);
|
|
266
|
-
const parts2 = path2.split("/").filter(Boolean);
|
|
267
|
-
if (parts1.length !== parts2.length) return false;
|
|
268
|
-
for (let i = 0; i < parts1.length; i++) {
|
|
269
|
-
const p1 = parts1[i];
|
|
270
|
-
const p2 = parts2[i];
|
|
271
|
-
if (!p1.startsWith(":") && !p1.startsWith("*") && !p2.startsWith(":") && !p2.startsWith("*") && p1 !== p2) return false;
|
|
272
|
-
if (p1 === "*" && p2.startsWith(":") || p2 === "*" && p1.startsWith(":")) return true;
|
|
273
|
-
}
|
|
274
|
-
return false;
|
|
275
|
-
}
|
|
276
|
-
/**
|
|
277
|
-
* 路径匹配
|
|
278
|
-
*/
|
|
279
|
-
matchPath(pattern, path) {
|
|
280
|
-
const patternParts = pattern.split("/").filter(Boolean);
|
|
281
|
-
const pathParts = path.split("/").filter(Boolean);
|
|
282
|
-
if (patternParts.length !== pathParts.length) return false;
|
|
283
|
-
for (let i = 0; i < patternParts.length; i++) if (patternParts[i] !== pathParts[i] && !patternParts[i].startsWith(":")) return false;
|
|
284
|
-
return true;
|
|
285
|
-
}
|
|
286
|
-
/**
|
|
287
|
-
* 提取路径参数
|
|
288
|
-
*/
|
|
289
|
-
extractParams(pattern, path) {
|
|
290
|
-
const params = {};
|
|
291
|
-
const patternParts = pattern.split("/").filter(Boolean);
|
|
292
|
-
const pathParts = path.split("/").filter(Boolean);
|
|
293
|
-
for (let i = 0; i < patternParts.length; i++) if (patternParts[i].startsWith(":")) {
|
|
294
|
-
const paramName = patternParts[i].slice(1);
|
|
295
|
-
params[paramName] = pathParts[i];
|
|
296
|
-
}
|
|
297
|
-
return params;
|
|
298
|
-
}
|
|
299
|
-
/**
|
|
300
|
-
* 处理 OPTIONS 请求
|
|
301
|
-
*/
|
|
302
|
-
handleOptions(pathname, routes) {
|
|
303
|
-
const availableMethods = [];
|
|
304
|
-
for (const route of routes) {
|
|
305
|
-
const path = route.fullPath || route.path;
|
|
306
|
-
const method = route.method || "GET";
|
|
307
|
-
if (this.matchPath(path, pathname)) availableMethods.push(method);
|
|
308
|
-
}
|
|
309
|
-
const uniqueMethods = [...new Set(availableMethods)].sort();
|
|
310
|
-
return new Response(null, {
|
|
311
|
-
status: 204,
|
|
312
|
-
headers: {
|
|
313
|
-
Allow: uniqueMethods.join(", "),
|
|
314
|
-
"Access-Control-Allow-Methods": uniqueMethods.join(", "),
|
|
315
|
-
"Access-Control-Allow-Headers": "Content-Type, Authorization"
|
|
316
|
-
}
|
|
317
|
-
});
|
|
318
|
-
}
|
|
319
|
-
};
|
|
320
|
-
|
|
321
|
-
//#endregion
|
|
322
|
-
//#region src/utils/path-matcher.ts
|
|
323
|
-
/**
|
|
324
|
-
* 路径匹配工具类
|
|
325
|
-
* 提供统一的路径匹配和参数提取功能
|
|
326
|
-
*/
|
|
327
|
-
var PathMatcher = class {
|
|
328
|
-
/**
|
|
329
|
-
* 路径匹配
|
|
330
|
-
*/
|
|
331
|
-
static matchPath(pattern, path) {
|
|
332
|
-
const patternParts = pattern.split("/").filter(Boolean);
|
|
333
|
-
const pathParts = path.split("/").filter(Boolean);
|
|
334
|
-
if (patternParts.length !== pathParts.length) return false;
|
|
335
|
-
for (let i = 0; i < patternParts.length; i++) if (patternParts[i] !== pathParts[i] && !patternParts[i].startsWith(":")) return false;
|
|
336
|
-
return true;
|
|
337
|
-
}
|
|
338
|
-
/**
|
|
339
|
-
* 提取路径参数
|
|
340
|
-
*/
|
|
341
|
-
static extractParams(pattern, path) {
|
|
342
|
-
const params = {};
|
|
343
|
-
const patternParts = pattern.split("/").filter(Boolean);
|
|
344
|
-
const pathParts = path.split("/").filter(Boolean);
|
|
345
|
-
for (let i = 0; i < patternParts.length; i++) if (patternParts[i].startsWith(":")) {
|
|
346
|
-
const paramName = patternParts[i].slice(1);
|
|
347
|
-
params[paramName] = pathParts[i];
|
|
348
|
-
}
|
|
349
|
-
return params;
|
|
350
|
-
}
|
|
351
|
-
/**
|
|
352
|
-
* 计算路径特异性分数
|
|
353
|
-
* 用于路由排序:静态 > 动态(:param) > 通配符(*)
|
|
354
|
-
*/
|
|
355
|
-
static calculatePathScore(path) {
|
|
356
|
-
const parts = path.split("/").filter(Boolean);
|
|
357
|
-
let score = 0;
|
|
358
|
-
for (const p of parts) if (p === "*") score += 1;
|
|
359
|
-
else if (p.startsWith(":")) score += 2;
|
|
360
|
-
else score += 3;
|
|
361
|
-
return score * 10 + parts.length;
|
|
362
|
-
}
|
|
363
|
-
/**
|
|
364
|
-
* 判断两个路径是否可能冲突
|
|
365
|
-
*/
|
|
366
|
-
static pathsMayConflict(path1, path2) {
|
|
367
|
-
const parts1 = path1.split("/").filter(Boolean);
|
|
368
|
-
const parts2 = path2.split("/").filter(Boolean);
|
|
369
|
-
if (parts1.length !== parts2.length) return false;
|
|
370
|
-
for (let i = 0; i < parts1.length; i++) {
|
|
371
|
-
const p1 = parts1[i];
|
|
372
|
-
const p2 = parts2[i];
|
|
373
|
-
if (!p1.startsWith(":") && !p1.startsWith("*") && !p2.startsWith(":") && !p2.startsWith("*") && p1 !== p2) return false;
|
|
374
|
-
if (p1 === "*" && p2.startsWith(":") || p2 === "*" && p1.startsWith(":")) return true;
|
|
375
|
-
}
|
|
376
|
-
return false;
|
|
377
|
-
}
|
|
378
|
-
};
|
|
379
|
-
|
|
380
|
-
//#endregion
|
|
381
|
-
//#region src/server/server.ts
|
|
382
|
-
var Server = class extends BaseServer {
|
|
383
|
-
routes;
|
|
384
|
-
constructor(routes) {
|
|
385
|
-
super();
|
|
386
|
-
this.routes = flattenNestedRoutes(routes);
|
|
387
|
-
this.routes = this.routes.sort((a, b) => PathMatcher.calculatePathScore(b.fullPath) - PathMatcher.calculatePathScore(a.fullPath));
|
|
388
|
-
this.detectRouteConflicts(this.routes);
|
|
389
|
-
this.logFlattenedRoutes(this.routes);
|
|
390
|
-
}
|
|
391
|
-
fetch = async (req) => {
|
|
392
|
-
const { pathname } = new URL(req.url);
|
|
393
|
-
const method = req.method;
|
|
394
|
-
if (method === "OPTIONS") return this.handleOptions(pathname, this.routes);
|
|
395
|
-
let matched;
|
|
396
|
-
let params = {};
|
|
397
|
-
let availableMethods = [];
|
|
398
|
-
for (const route of this.routes) {
|
|
399
|
-
const result = matchPath(route.fullPath, pathname);
|
|
400
|
-
if (result.matched) if (route.method === method) {
|
|
401
|
-
matched = route;
|
|
402
|
-
params = result.params;
|
|
403
|
-
break;
|
|
404
|
-
} else availableMethods.push(route.method);
|
|
405
|
-
}
|
|
406
|
-
const handler = async (req$1) => {
|
|
407
|
-
if (matched) {
|
|
408
|
-
req$1.params = params;
|
|
409
|
-
return await matched.handler(req$1, params);
|
|
410
|
-
} else if (availableMethods.length > 0) return json({
|
|
411
|
-
success: false,
|
|
412
|
-
error: "Method Not Allowed",
|
|
413
|
-
message: `Method ${method} not allowed for this endpoint`,
|
|
414
|
-
allowedMethods: availableMethods
|
|
415
|
-
}, 405, { Allow: availableMethods.join(", ") });
|
|
416
|
-
else return json({
|
|
417
|
-
success: false,
|
|
418
|
-
error: "Not Found"
|
|
419
|
-
}, 404);
|
|
420
|
-
};
|
|
421
|
-
const middlewareChain = matched?.middlewareChain ? [...this.globalMiddleware, ...matched.middlewareChain] : this.globalMiddleware;
|
|
422
|
-
const composedHandler = composeMiddleware(middlewareChain, handler);
|
|
423
|
-
return await composedHandler(req);
|
|
424
|
-
};
|
|
425
|
-
};
|
|
426
|
-
|
|
427
|
-
//#endregion
|
|
428
|
-
//#region src/middleware/component-router.ts
|
|
429
|
-
/**
|
|
430
|
-
* 扁平化嵌套组件路由
|
|
431
|
-
*/
|
|
432
|
-
function flattenComponentRoutes(routes) {
|
|
433
|
-
const flattened = [];
|
|
434
|
-
function processRoute(route, parentPath = "", parentMiddleware = []) {
|
|
435
|
-
const currentPath = parentPath + route.path;
|
|
436
|
-
const currentMiddleware = [...parentMiddleware, ...route.middleware || []];
|
|
437
|
-
if ("component" in route) flattened.push({
|
|
438
|
-
...route,
|
|
439
|
-
fullPath: currentPath,
|
|
440
|
-
middlewareChain: currentMiddleware
|
|
441
|
-
});
|
|
442
|
-
else if ("children" in route && route.children) for (const child of route.children) processRoute(child, currentPath, currentMiddleware);
|
|
443
|
-
}
|
|
444
|
-
for (const route of routes) processRoute(route);
|
|
445
|
-
return flattened;
|
|
446
|
-
}
|
|
447
|
-
|
|
448
|
-
//#endregion
|
|
449
|
-
//#region src/utils/html-renderer.ts
|
|
450
|
-
/**
|
|
451
|
-
* HTML渲染工具类
|
|
452
|
-
* 提供统一的HTML模板生成功能
|
|
453
|
-
*/
|
|
454
|
-
var HtmlRenderer = class {
|
|
455
|
-
/**
|
|
456
|
-
* 生成基础HTML模板
|
|
457
|
-
*/
|
|
458
|
-
static generateBaseHtml(content, context, clientScriptPath = "/client.js") {
|
|
459
|
-
return `
|
|
460
|
-
<!doctype html>
|
|
461
|
-
<html>
|
|
462
|
-
<head>
|
|
463
|
-
<meta charset="utf-8">
|
|
464
|
-
<title>Vafast SSR App</title>
|
|
465
|
-
<meta name="viewport" content="width=device-width, initial-scale=1">
|
|
466
|
-
</head>
|
|
467
|
-
<body>
|
|
468
|
-
<div id="app">${content}</div>
|
|
469
|
-
<script>
|
|
470
|
-
window.__ROUTE_INFO__ = {
|
|
471
|
-
params: ${JSON.stringify(context.params || {})},
|
|
472
|
-
query: ${JSON.stringify(context.query || {})},
|
|
473
|
-
pathname: '${context.pathname}'
|
|
474
|
-
};
|
|
475
|
-
<\/script>
|
|
476
|
-
<script type="module" src="${clientScriptPath}"><\/script>
|
|
477
|
-
</body>
|
|
478
|
-
</html>
|
|
479
|
-
`;
|
|
480
|
-
}
|
|
481
|
-
/**
|
|
482
|
-
* 生成Vue组件HTML
|
|
483
|
-
*/
|
|
484
|
-
static generateVueHtml(content, context, clientScriptPath = "/client.js") {
|
|
485
|
-
return this.generateBaseHtml(content, context, clientScriptPath);
|
|
486
|
-
}
|
|
487
|
-
/**
|
|
488
|
-
* 生成React组件HTML
|
|
489
|
-
*/
|
|
490
|
-
static generateReactHtml(content, context, clientScriptPath = "/client.js") {
|
|
491
|
-
return `
|
|
492
|
-
<!doctype html>
|
|
493
|
-
<html>
|
|
494
|
-
<head>
|
|
495
|
-
<meta charset="utf-8">
|
|
496
|
-
<title>Vafast SSR App</title>
|
|
497
|
-
<meta name="viewport" content="width=device-width, initial-scale=1">
|
|
498
|
-
</head>
|
|
499
|
-
<body>
|
|
500
|
-
<div id="root">${content}</div>
|
|
501
|
-
<script>
|
|
502
|
-
window.__ROUTE_INFO__ = {
|
|
503
|
-
params: ${JSON.stringify(context.params || {})},
|
|
504
|
-
query: ${JSON.stringify(context.query || {})},
|
|
505
|
-
pathname: '${context.pathname}'
|
|
506
|
-
};
|
|
507
|
-
<\/script>
|
|
508
|
-
<script type="module" src="${clientScriptPath}"><\/script>
|
|
509
|
-
</body>
|
|
510
|
-
</html>
|
|
511
|
-
`;
|
|
512
|
-
}
|
|
513
|
-
};
|
|
514
|
-
|
|
515
|
-
//#endregion
|
|
516
|
-
//#region src/utils/dependency-manager.ts
|
|
517
|
-
/**
|
|
518
|
-
* 依赖管理器
|
|
519
|
-
* 负责按需加载和管理框架依赖
|
|
520
|
-
*/
|
|
521
|
-
var DependencyManager = class {
|
|
522
|
-
dependencyCache = /* @__PURE__ */ new Map();
|
|
523
|
-
/**
|
|
524
|
-
* 按需获取框架依赖
|
|
525
|
-
*/
|
|
526
|
-
async getFrameworkDeps(framework) {
|
|
527
|
-
if (this.dependencyCache.has(framework)) return this.dependencyCache.get(framework);
|
|
528
|
-
console.log(`📦 按需加载 ${framework} 依赖...`);
|
|
529
|
-
try {
|
|
530
|
-
let deps;
|
|
531
|
-
switch (framework) {
|
|
532
|
-
case "vue":
|
|
533
|
-
deps = await Promise.all([import("vue"), import("@vue/server-renderer")]);
|
|
534
|
-
break;
|
|
535
|
-
case "react":
|
|
536
|
-
deps = await Promise.all([import("react"), import("react-dom/server")]);
|
|
537
|
-
break;
|
|
538
|
-
default: throw new Error(`不支持的框架: ${framework}`);
|
|
539
|
-
}
|
|
540
|
-
this.dependencyCache.set(framework, deps);
|
|
541
|
-
console.log(`✅ ${framework} 依赖加载完成`);
|
|
542
|
-
return deps;
|
|
543
|
-
} catch (error) {
|
|
544
|
-
console.error(`❌ ${framework} 依赖加载失败:`, error);
|
|
545
|
-
throw error;
|
|
546
|
-
}
|
|
547
|
-
}
|
|
548
|
-
/**
|
|
549
|
-
* 检测组件类型
|
|
550
|
-
*/
|
|
551
|
-
detectComponentType(component) {
|
|
552
|
-
if (component.render && typeof component.render === "function") return "vue";
|
|
553
|
-
if (component.$$typeof) return "react";
|
|
554
|
-
return "vue";
|
|
555
|
-
}
|
|
556
|
-
/**
|
|
557
|
-
* 清除缓存
|
|
558
|
-
*/
|
|
559
|
-
clearCache() {
|
|
560
|
-
this.dependencyCache.clear();
|
|
561
|
-
console.log("🧹 依赖缓存已清除");
|
|
562
|
-
}
|
|
563
|
-
/**
|
|
564
|
-
* 获取缓存状态
|
|
565
|
-
*/
|
|
566
|
-
getCacheStatus() {
|
|
567
|
-
const status = {};
|
|
568
|
-
for (const [framework] of this.dependencyCache) status[framework] = true;
|
|
569
|
-
return status;
|
|
570
|
-
}
|
|
571
|
-
};
|
|
572
|
-
|
|
573
|
-
//#endregion
|
|
574
|
-
//#region src/server/component-server.ts
|
|
575
|
-
/**
|
|
576
|
-
* 组件路由服务器
|
|
577
|
-
* 专门处理声明式组件路由
|
|
578
|
-
*/
|
|
579
|
-
var ComponentServer = class extends BaseServer {
|
|
580
|
-
routes;
|
|
581
|
-
dependencyManager;
|
|
582
|
-
constructor(routes) {
|
|
583
|
-
super();
|
|
584
|
-
this.routes = flattenComponentRoutes(routes);
|
|
585
|
-
this.dependencyManager = new DependencyManager();
|
|
586
|
-
this.detectRouteConflicts(this.routes);
|
|
587
|
-
this.logFlattenedRoutes(this.routes, "组件路由");
|
|
588
|
-
console.log("🚀 依赖按需加载,服务器启动完成");
|
|
589
|
-
}
|
|
590
|
-
/**
|
|
591
|
-
* 处理请求
|
|
592
|
-
*/
|
|
593
|
-
async fetch(req) {
|
|
594
|
-
const url = new URL(req.url);
|
|
595
|
-
const pathname = url.pathname;
|
|
596
|
-
const method = req.method;
|
|
597
|
-
if (method !== "GET") return new Response("Method Not Allowed", { status: 405 });
|
|
598
|
-
let matchedRoute = null;
|
|
599
|
-
for (const route of this.routes) if (PathMatcher.matchPath(route.fullPath, pathname)) {
|
|
600
|
-
matchedRoute = route;
|
|
601
|
-
break;
|
|
602
|
-
}
|
|
603
|
-
if (!matchedRoute) return new Response("Not Found", { status: 404 });
|
|
604
|
-
try {
|
|
605
|
-
const context = {
|
|
606
|
-
req,
|
|
607
|
-
params: PathMatcher.extractParams(matchedRoute.fullPath, pathname),
|
|
608
|
-
query: Object.fromEntries(url.searchParams),
|
|
609
|
-
pathname
|
|
610
|
-
};
|
|
611
|
-
return await this.executeMiddlewareChain(matchedRoute.middlewareChain, context, matchedRoute.component);
|
|
612
|
-
} catch (error) {
|
|
613
|
-
console.error("组件渲染失败:", error);
|
|
614
|
-
return new Response("Internal Server Error", { status: 500 });
|
|
615
|
-
}
|
|
616
|
-
}
|
|
617
|
-
/**
|
|
618
|
-
* 执行中间件链
|
|
619
|
-
*/
|
|
620
|
-
async executeMiddlewareChain(middlewareChain, context, componentImport) {
|
|
621
|
-
const renderComponent = async () => {
|
|
622
|
-
const componentModule = await componentImport();
|
|
623
|
-
const component = componentModule.default || componentModule;
|
|
624
|
-
const componentType = this.dependencyManager.detectComponentType(component);
|
|
625
|
-
const deps = await this.dependencyManager.getFrameworkDeps(componentType);
|
|
626
|
-
if (componentType === "vue") return await this.renderVueComponent(component, context, deps);
|
|
627
|
-
else if (componentType === "react") return await this.renderReactComponent(component, context, deps);
|
|
628
|
-
else throw new Error(`不支持的组件类型: ${componentType}`);
|
|
629
|
-
};
|
|
630
|
-
let index = 0;
|
|
631
|
-
const next = async () => {
|
|
632
|
-
if (index >= middlewareChain.length) return await renderComponent();
|
|
633
|
-
const middleware = middlewareChain[index++];
|
|
634
|
-
return await middleware(context.req, next);
|
|
635
|
-
};
|
|
636
|
-
return await next();
|
|
637
|
-
}
|
|
638
|
-
/**
|
|
639
|
-
* 渲染 Vue 组件
|
|
640
|
-
*/
|
|
641
|
-
async renderVueComponent(component, context, deps) {
|
|
642
|
-
try {
|
|
643
|
-
const [vue, renderer] = deps;
|
|
644
|
-
const app = vue.createSSRApp(component);
|
|
645
|
-
app.provide("routeInfo", {
|
|
646
|
-
params: context.params || {},
|
|
647
|
-
query: context.query || {},
|
|
648
|
-
pathname: context.pathname
|
|
649
|
-
});
|
|
650
|
-
const html$1 = await renderer.renderToString(app);
|
|
651
|
-
const fullHtml = HtmlRenderer.generateVueHtml(html$1, context);
|
|
652
|
-
return new Response(fullHtml, { headers: { "Content-Type": "text/html; charset=utf-8" } });
|
|
653
|
-
} catch (error) {
|
|
654
|
-
console.error("Vue 组件渲染失败:", error);
|
|
655
|
-
return new Response("Vue Component Render Error", { status: 500 });
|
|
656
|
-
}
|
|
657
|
-
}
|
|
658
|
-
/**
|
|
659
|
-
* 渲染 React 组件
|
|
660
|
-
*/
|
|
661
|
-
async renderReactComponent(component, context, deps) {
|
|
662
|
-
try {
|
|
663
|
-
const [react, renderer] = deps;
|
|
664
|
-
const content = react.createElement(component, {
|
|
665
|
-
req: context.req,
|
|
666
|
-
params: context.params || {},
|
|
667
|
-
query: context.query || {}
|
|
668
|
-
});
|
|
669
|
-
const html$1 = renderer.renderToString(content);
|
|
670
|
-
const fullHtml = HtmlRenderer.generateReactHtml(html$1, context);
|
|
671
|
-
return new Response(fullHtml, { headers: { "Content-Type": "text/html; charset=utf-8" } });
|
|
672
|
-
} catch (error) {
|
|
673
|
-
console.error("React 组件渲染失败:", error);
|
|
674
|
-
return new Response("React Component Render Error", { status: 500 });
|
|
675
|
-
}
|
|
676
|
-
}
|
|
677
|
-
/**
|
|
678
|
-
* 获取依赖管理器(用于外部访问)
|
|
679
|
-
*/
|
|
680
|
-
getDependencyManager() {
|
|
681
|
-
return this.dependencyManager;
|
|
682
|
-
}
|
|
683
|
-
};
|
|
684
|
-
|
|
685
|
-
//#endregion
|
|
686
|
-
//#region src/server/server-factory.ts
|
|
687
|
-
/**
|
|
688
|
-
* 服务器工厂类
|
|
689
|
-
* 用于创建和管理不同类型的服务器
|
|
690
|
-
*/
|
|
691
|
-
var ServerFactory = class {
|
|
692
|
-
servers = /* @__PURE__ */ new Map();
|
|
693
|
-
/**
|
|
694
|
-
* 创建标准REST API服务器
|
|
695
|
-
*/
|
|
696
|
-
createRestServer(routes) {
|
|
697
|
-
const server = new Server(routes);
|
|
698
|
-
this.servers.set("rest", server);
|
|
699
|
-
return server;
|
|
700
|
-
}
|
|
701
|
-
/**
|
|
702
|
-
* 创建组件服务器
|
|
703
|
-
*/
|
|
704
|
-
createComponentServer(routes) {
|
|
705
|
-
const server = new ComponentServer(routes);
|
|
706
|
-
this.servers.set("component", server);
|
|
707
|
-
return server;
|
|
708
|
-
}
|
|
709
|
-
/**
|
|
710
|
-
* 获取指定类型的服务器
|
|
711
|
-
*/
|
|
712
|
-
getServer(type) {
|
|
713
|
-
return this.servers.get(type);
|
|
714
|
-
}
|
|
715
|
-
/**
|
|
716
|
-
* 获取所有服务器
|
|
717
|
-
*/
|
|
718
|
-
getAllServers() {
|
|
719
|
-
return this.servers;
|
|
720
|
-
}
|
|
721
|
-
/**
|
|
722
|
-
* 移除指定类型的服务器
|
|
723
|
-
*/
|
|
724
|
-
removeServer(type) {
|
|
725
|
-
return this.servers.delete(type);
|
|
726
|
-
}
|
|
727
|
-
/**
|
|
728
|
-
* 清除所有服务器
|
|
729
|
-
*/
|
|
730
|
-
clearServers() {
|
|
731
|
-
this.servers.clear();
|
|
732
|
-
}
|
|
733
|
-
/**
|
|
734
|
-
* 获取服务器状态信息
|
|
735
|
-
*/
|
|
736
|
-
getServerStatus() {
|
|
737
|
-
const status = {};
|
|
738
|
-
for (const [name, server] of this.servers) if (server instanceof Server) status[name] = {
|
|
739
|
-
type: "REST API",
|
|
740
|
-
routes: server.routes?.length || 0
|
|
741
|
-
};
|
|
742
|
-
else if (server instanceof ComponentServer) status[name] = {
|
|
743
|
-
type: "Component",
|
|
744
|
-
routes: server.routes?.length || 0
|
|
745
|
-
};
|
|
746
|
-
return status;
|
|
747
|
-
}
|
|
748
|
-
};
|
|
749
|
-
|
|
750
|
-
//#endregion
|
|
751
|
-
//#region src/utils/parsers.ts
|
|
752
|
-
/**
|
|
753
|
-
* 简化的请求体解析函数
|
|
754
|
-
* 优先简洁性,处理最常见的场景
|
|
755
|
-
*/
|
|
756
|
-
async function parseBody(req) {
|
|
757
|
-
const contentType = req.headers.get("content-type") || "";
|
|
758
|
-
if (contentType.includes("application/json")) return await req.json();
|
|
759
|
-
if (contentType.includes("application/x-www-form-urlencoded")) {
|
|
760
|
-
const text$1 = await req.text();
|
|
761
|
-
return Object.fromEntries(new URLSearchParams(text$1));
|
|
762
|
-
}
|
|
763
|
-
return await req.text();
|
|
764
|
-
}
|
|
765
|
-
/**
|
|
766
|
-
* 解析 multipart/form-data 格式
|
|
767
|
-
* 支持文件上传和普通表单字段
|
|
768
|
-
*/
|
|
769
|
-
async function parseMultipartFormData(req) {
|
|
770
|
-
const formData = await req.formData();
|
|
771
|
-
const result = {
|
|
772
|
-
fields: {},
|
|
773
|
-
files: {}
|
|
774
|
-
};
|
|
775
|
-
for (const [key, value] of formData.entries()) if (typeof value === "object" && value !== null && "name" in value && "type" in value && "size" in value) {
|
|
776
|
-
const file = value;
|
|
777
|
-
const arrayBuffer = await file.arrayBuffer();
|
|
778
|
-
result.files[key] = {
|
|
779
|
-
name: file.name,
|
|
780
|
-
type: file.type,
|
|
781
|
-
size: file.size,
|
|
782
|
-
data: arrayBuffer
|
|
783
|
-
};
|
|
784
|
-
} else result.fields[key] = value;
|
|
785
|
-
return result;
|
|
786
|
-
}
|
|
787
|
-
/**
|
|
788
|
-
* 解析请求体为特定类型
|
|
789
|
-
* 提供类型安全的解析方法
|
|
790
|
-
*/
|
|
791
|
-
async function parseBodyAs(req) {
|
|
792
|
-
const body = await parseBody(req);
|
|
793
|
-
return body;
|
|
794
|
-
}
|
|
795
|
-
/**
|
|
796
|
-
* 解析请求体为表单数据
|
|
797
|
-
* 专门用于处理 multipart/form-data
|
|
798
|
-
*/
|
|
799
|
-
async function parseFormData(req) {
|
|
800
|
-
const contentType = req.headers.get("content-type") || "";
|
|
801
|
-
if (!contentType.includes("multipart/form-data")) throw new Error("请求不是 multipart/form-data 格式");
|
|
802
|
-
return await parseMultipartFormData(req);
|
|
803
|
-
}
|
|
804
|
-
/**
|
|
805
|
-
* 解析请求体为文件
|
|
806
|
-
* 专门用于处理文件上传
|
|
807
|
-
*/
|
|
808
|
-
async function parseFile(req) {
|
|
809
|
-
const contentType = req.headers.get("content-type") || "";
|
|
810
|
-
if (!contentType.includes("multipart/form-data")) throw new Error("请求不是 multipart/form-data 格式");
|
|
811
|
-
const formData = await parseMultipartFormData(req);
|
|
812
|
-
const fileKeys = Object.keys(formData.files);
|
|
813
|
-
if (fileKeys.length === 0) throw new Error("请求中没有文件");
|
|
814
|
-
if (fileKeys.length > 1) throw new Error("请求中包含多个文件,请使用 parseFormData");
|
|
815
|
-
return formData.files[fileKeys[0]];
|
|
816
|
-
}
|
|
817
|
-
/** 获取查询字符串,直接返回对象 */
|
|
818
|
-
function parseQuery(req) {
|
|
819
|
-
const url = new URL(req.url);
|
|
820
|
-
return qs.parse(url.search.slice(1));
|
|
821
|
-
}
|
|
822
|
-
/** 解析请求头,返回对象 */
|
|
823
|
-
function parseHeaders(req) {
|
|
824
|
-
const headers = {};
|
|
825
|
-
req.headers.forEach((value, key) => {
|
|
826
|
-
if (value !== void 0 && value !== null) headers[key] = value;
|
|
827
|
-
});
|
|
828
|
-
return headers;
|
|
829
|
-
}
|
|
830
|
-
/** 使用cookie库解析Cookie,保证可靠性 */
|
|
831
|
-
function parseCookies(req) {
|
|
832
|
-
const cookieHeader = req.headers.get("cookie");
|
|
833
|
-
if (!cookieHeader) return {};
|
|
834
|
-
try {
|
|
835
|
-
const parsed = cookie.parse(cookieHeader);
|
|
836
|
-
const result = {};
|
|
837
|
-
for (const [key, value] of Object.entries(parsed)) if (value !== void 0 && value !== null) result[key] = value;
|
|
838
|
-
return result;
|
|
839
|
-
} catch (error) {
|
|
840
|
-
console.error("Cookie解析失败:", error);
|
|
841
|
-
console.error("原始Cookie字符串:", cookieHeader);
|
|
842
|
-
return {};
|
|
843
|
-
}
|
|
844
|
-
}
|
|
845
|
-
|
|
846
|
-
//#endregion
|
|
847
|
-
//#region src/utils/handle.ts
|
|
848
|
-
/** 获取单个 Cookie 值 */
|
|
849
|
-
function getCookie(req, key) {
|
|
850
|
-
const cookies = parseCookies(req);
|
|
851
|
-
return cookies[key] || null;
|
|
852
|
-
}
|
|
853
|
-
|
|
854
|
-
//#endregion
|
|
855
|
-
//#region src/middleware/authMiddleware.ts
|
|
856
|
-
const requireAuth = async (req, next) => {
|
|
857
|
-
const token = getCookie(req, "auth");
|
|
858
|
-
if (!token || token !== "valid-token") throw new VafastError("Unauthorized", {
|
|
859
|
-
status: 401,
|
|
860
|
-
type: "unauthorized",
|
|
861
|
-
expose: true
|
|
862
|
-
});
|
|
863
|
-
return next();
|
|
864
|
-
};
|
|
865
|
-
|
|
866
|
-
//#endregion
|
|
867
|
-
//#region src/middleware/rateLimit.ts
|
|
868
|
-
const store = /* @__PURE__ */ new Map();
|
|
869
|
-
function rateLimit(options = {}) {
|
|
870
|
-
const windowMs = options.windowMs ?? 6e4;
|
|
871
|
-
const max = options.max ?? 30;
|
|
872
|
-
const keyFn = options.keyFn ?? getIP;
|
|
873
|
-
return async (req, next) => {
|
|
874
|
-
const key = keyFn(req);
|
|
875
|
-
const now = Date.now();
|
|
876
|
-
const entry = store.get(key);
|
|
877
|
-
if (entry && entry.expires > now) {
|
|
878
|
-
if (entry.count >= max) throw new VafastError("Too many requests", {
|
|
879
|
-
status: 429,
|
|
880
|
-
type: "rate_limit",
|
|
881
|
-
expose: true
|
|
882
|
-
});
|
|
883
|
-
entry.count += 1;
|
|
884
|
-
} else store.set(key, {
|
|
885
|
-
count: 1,
|
|
886
|
-
expires: now + windowMs
|
|
887
|
-
});
|
|
888
|
-
return next();
|
|
889
|
-
};
|
|
890
|
-
}
|
|
891
|
-
function getIP(req) {
|
|
892
|
-
return req.headers.get("cf-connecting-ip") || req.headers.get("x-forwarded-for")?.split(",")[0]?.trim() || "unknown";
|
|
893
|
-
}
|
|
894
|
-
|
|
895
|
-
//#endregion
|
|
896
|
-
//#region src/middleware/cors.ts
|
|
897
|
-
function createCORS(options = {}) {
|
|
898
|
-
const { origin = [], methods = [
|
|
899
|
-
"GET",
|
|
900
|
-
"POST",
|
|
901
|
-
"PUT",
|
|
902
|
-
"DELETE",
|
|
903
|
-
"OPTIONS"
|
|
904
|
-
], headers = [], credentials = false, maxAge } = options;
|
|
905
|
-
return async (req, next) => {
|
|
906
|
-
const reqOrigin = req.headers.get("Origin") || "";
|
|
907
|
-
const isAllowedOrigin = origin === "*" || origin.includes(reqOrigin);
|
|
908
|
-
if (req.method === "OPTIONS") {
|
|
909
|
-
const resHeaders = new Headers();
|
|
910
|
-
if (isAllowedOrigin) {
|
|
911
|
-
resHeaders.set("Access-Control-Allow-Origin", origin === "*" ? "*" : reqOrigin);
|
|
912
|
-
resHeaders.set("Access-Control-Allow-Methods", methods.join(","));
|
|
913
|
-
resHeaders.set("Access-Control-Allow-Headers", headers.join(","));
|
|
914
|
-
if (credentials) resHeaders.set("Access-Control-Allow-Credentials", "true");
|
|
915
|
-
if (maxAge) resHeaders.set("Access-Control-Max-Age", maxAge.toString());
|
|
916
|
-
}
|
|
917
|
-
return new Response(null, {
|
|
918
|
-
status: 204,
|
|
919
|
-
headers: resHeaders
|
|
920
|
-
});
|
|
921
|
-
}
|
|
922
|
-
const res = await next();
|
|
923
|
-
if (isAllowedOrigin) {
|
|
924
|
-
res.headers.set("Access-Control-Allow-Origin", origin === "*" ? "*" : reqOrigin);
|
|
925
|
-
if (credentials) res.headers.set("Access-Control-Allow-Credentials", "true");
|
|
926
|
-
}
|
|
927
|
-
return res;
|
|
928
|
-
};
|
|
929
|
-
}
|
|
930
|
-
|
|
931
|
-
//#endregion
|
|
932
|
-
//#region src/utils/base64url.ts
|
|
933
|
-
function base64urlEncode(str) {
|
|
934
|
-
return btoa(str).replace(/=/g, "").replace(/\+/g, "-").replace(/\//g, "_");
|
|
935
|
-
}
|
|
936
|
-
function base64urlDecode(str) {
|
|
937
|
-
const pad = str.length % 4 === 0 ? "" : "=".repeat(4 - str.length % 4);
|
|
938
|
-
const base64 = str.replace(/-/g, "+").replace(/_/g, "/") + pad;
|
|
939
|
-
return atob(base64);
|
|
940
|
-
}
|
|
941
|
-
|
|
942
|
-
//#endregion
|
|
943
|
-
//#region src/auth/token.ts
|
|
944
|
-
var TokenError = class extends Error {
|
|
945
|
-
constructor(message, code) {
|
|
946
|
-
super(message);
|
|
947
|
-
this.code = code;
|
|
948
|
-
this.name = "TokenError";
|
|
949
|
-
}
|
|
950
|
-
};
|
|
951
|
-
const encoder = new TextEncoder();
|
|
952
|
-
/** 使用 HMAC-SHA256 进行签名 */
|
|
953
|
-
async function sign(data, secret) {
|
|
954
|
-
const key = await crypto.subtle.importKey("raw", encoder.encode(secret), {
|
|
955
|
-
name: "HMAC",
|
|
956
|
-
hash: "SHA-256"
|
|
957
|
-
}, false, ["sign"]);
|
|
958
|
-
const signature = await crypto.subtle.sign("HMAC", key, encoder.encode(data));
|
|
959
|
-
return btoa(String.fromCharCode.apply(null, Array.from(new Uint8Array(signature))));
|
|
960
|
-
}
|
|
961
|
-
/** 生成令牌 */
|
|
962
|
-
async function generateToken(payload, secret, options = {}) {
|
|
963
|
-
const { expiresIn = 3600, issuer, audience, subject } = options;
|
|
964
|
-
const now = Math.floor(Date.now() / 1e3);
|
|
965
|
-
const tokenPayload = {
|
|
966
|
-
...payload,
|
|
967
|
-
iat: now,
|
|
968
|
-
exp: now + expiresIn
|
|
969
|
-
};
|
|
970
|
-
if (issuer) tokenPayload.iss = issuer;
|
|
971
|
-
if (audience) tokenPayload.aud = audience;
|
|
972
|
-
if (subject) tokenPayload.sub = subject;
|
|
973
|
-
const data = base64urlEncode(JSON.stringify(tokenPayload));
|
|
974
|
-
const sig = await sign(data, secret);
|
|
975
|
-
const token = `${data}.${base64urlEncode(sig)}`;
|
|
976
|
-
return {
|
|
977
|
-
payload: tokenPayload,
|
|
978
|
-
token,
|
|
979
|
-
expiresAt: tokenPayload.exp * 1e3
|
|
980
|
-
};
|
|
981
|
-
}
|
|
982
|
-
/** 验证令牌 */
|
|
983
|
-
async function verifyToken(token, secret) {
|
|
984
|
-
try {
|
|
985
|
-
const [data, sig] = token.split(".");
|
|
986
|
-
if (!data || !sig) throw new TokenError("令牌格式无效", "MALFORMED_TOKEN");
|
|
987
|
-
const expectedSig = await sign(data, secret);
|
|
988
|
-
const expected = base64urlEncode(expectedSig);
|
|
989
|
-
if (sig !== expected) throw new TokenError("令牌签名无效", "INVALID_SIGNATURE");
|
|
990
|
-
const payload = JSON.parse(base64urlDecode(data));
|
|
991
|
-
if (payload.exp && Date.now() / 1e3 > payload.exp) throw new TokenError("令牌已过期", "EXPIRED_TOKEN");
|
|
992
|
-
return payload;
|
|
993
|
-
} catch (error) {
|
|
994
|
-
if (error instanceof TokenError) throw error;
|
|
995
|
-
throw new TokenError("令牌验证失败", "INVALID_TOKEN");
|
|
996
|
-
}
|
|
997
|
-
}
|
|
998
|
-
/** 解析令牌(不验证签名) */
|
|
999
|
-
function parseToken(token) {
|
|
1000
|
-
try {
|
|
1001
|
-
const [data] = token.split(".");
|
|
1002
|
-
if (!data) return null;
|
|
1003
|
-
return JSON.parse(base64urlDecode(data));
|
|
1004
|
-
} catch {
|
|
1005
|
-
return null;
|
|
1006
|
-
}
|
|
1007
|
-
}
|
|
1008
|
-
/** 检查令牌是否过期 */
|
|
1009
|
-
function isTokenExpired(token) {
|
|
1010
|
-
const payload = parseToken(token);
|
|
1011
|
-
if (!payload || !payload.exp) return true;
|
|
1012
|
-
return Date.now() / 1e3 > payload.exp;
|
|
1013
|
-
}
|
|
1014
|
-
/** 获取令牌剩余有效时间(秒) */
|
|
1015
|
-
function getTokenTimeRemaining(token) {
|
|
1016
|
-
const payload = parseToken(token);
|
|
1017
|
-
if (!payload || !payload.exp) return 0;
|
|
1018
|
-
const remaining = payload.exp - Date.now() / 1e3;
|
|
1019
|
-
return Math.max(0, Math.floor(remaining));
|
|
1020
|
-
}
|
|
1021
|
-
/** 刷新令牌 */
|
|
1022
|
-
async function refreshToken(token, secret, options = {}) {
|
|
1023
|
-
try {
|
|
1024
|
-
const payload = await verifyToken(token, secret);
|
|
1025
|
-
if (!payload) return null;
|
|
1026
|
-
const { exp, iat,...cleanPayload } = payload;
|
|
1027
|
-
await new Promise((resolve) => setTimeout(resolve, 10));
|
|
1028
|
-
return await generateToken(cleanPayload, secret, options);
|
|
1029
|
-
} catch {
|
|
1030
|
-
return null;
|
|
1031
|
-
}
|
|
1032
|
-
}
|
|
1033
|
-
/** 创建访问令牌和刷新令牌对 */
|
|
1034
|
-
async function createTokenPair(payload, secret, options = {}) {
|
|
1035
|
-
const accessToken = await generateToken(payload, secret, {
|
|
1036
|
-
...options,
|
|
1037
|
-
expiresIn: options.expiresIn || 3600
|
|
1038
|
-
});
|
|
1039
|
-
const refreshToken$1 = await generateToken(payload, secret, {
|
|
1040
|
-
...options,
|
|
1041
|
-
expiresIn: 168 * 3600
|
|
1042
|
-
});
|
|
1043
|
-
return {
|
|
1044
|
-
accessToken,
|
|
1045
|
-
refreshToken: refreshToken$1
|
|
1046
|
-
};
|
|
1047
|
-
}
|
|
1048
|
-
|
|
1049
|
-
//#endregion
|
|
1050
|
-
//#region src/middleware/auth.ts
|
|
1051
|
-
function createAuth(options) {
|
|
1052
|
-
const { secret, cookieName = "auth", headerName = "authorization", required = true, roles = [], permissions = [] } = options;
|
|
1053
|
-
return async (req, next) => {
|
|
1054
|
-
const token = getCookie(req, cookieName) || req.headers.get(headerName)?.replace("Bearer ", "") || "";
|
|
1055
|
-
if (!token && required) throw new VafastError("缺少认证令牌", {
|
|
1056
|
-
status: 401,
|
|
1057
|
-
type: "unauthorized",
|
|
1058
|
-
expose: true
|
|
1059
|
-
});
|
|
1060
|
-
if (!token && !required) return next();
|
|
1061
|
-
try {
|
|
1062
|
-
const user = await verifyToken(token, secret);
|
|
1063
|
-
if (!user) throw new VafastError("令牌验证失败", {
|
|
1064
|
-
status: 401,
|
|
1065
|
-
type: "unauthorized",
|
|
1066
|
-
expose: true
|
|
1067
|
-
});
|
|
1068
|
-
if (roles.length > 0 && user.role && !roles.includes(user.role)) throw new VafastError("权限不足", {
|
|
1069
|
-
status: 403,
|
|
1070
|
-
type: "forbidden",
|
|
1071
|
-
expose: true
|
|
1072
|
-
});
|
|
1073
|
-
if (permissions.length > 0 && user.permissions) {
|
|
1074
|
-
const userPermissions = Array.isArray(user.permissions) ? user.permissions : [user.permissions];
|
|
1075
|
-
const hasPermission = permissions.some((permission) => userPermissions.includes(permission));
|
|
1076
|
-
if (!hasPermission) throw new VafastError("权限不足", {
|
|
1077
|
-
status: 403,
|
|
1078
|
-
type: "forbidden",
|
|
1079
|
-
expose: true
|
|
1080
|
-
});
|
|
1081
|
-
}
|
|
1082
|
-
req.user = user;
|
|
1083
|
-
req.token = token;
|
|
1084
|
-
return next();
|
|
1085
|
-
} catch (error) {
|
|
1086
|
-
if (error instanceof TokenError) {
|
|
1087
|
-
let status = 401;
|
|
1088
|
-
let message = "认证失败";
|
|
1089
|
-
switch (error.code) {
|
|
1090
|
-
case "EXPIRED_TOKEN":
|
|
1091
|
-
status = 401;
|
|
1092
|
-
message = "令牌已过期";
|
|
1093
|
-
break;
|
|
1094
|
-
case "INVALID_SIGNATURE":
|
|
1095
|
-
status = 401;
|
|
1096
|
-
message = "令牌签名无效";
|
|
1097
|
-
break;
|
|
1098
|
-
case "MALFORMED_TOKEN":
|
|
1099
|
-
status = 400;
|
|
1100
|
-
message = "令牌格式错误";
|
|
1101
|
-
break;
|
|
1102
|
-
default:
|
|
1103
|
-
status = 401;
|
|
1104
|
-
message = "令牌验证失败";
|
|
1105
|
-
}
|
|
1106
|
-
throw new VafastError(message, {
|
|
1107
|
-
status,
|
|
1108
|
-
type: "unauthorized",
|
|
1109
|
-
expose: true
|
|
1110
|
-
});
|
|
1111
|
-
}
|
|
1112
|
-
if (error instanceof VafastError) throw error;
|
|
1113
|
-
throw new VafastError("认证过程中发生错误", {
|
|
1114
|
-
status: 500,
|
|
1115
|
-
type: "internal_error",
|
|
1116
|
-
expose: false
|
|
1117
|
-
});
|
|
1118
|
-
}
|
|
1119
|
-
};
|
|
1120
|
-
}
|
|
1121
|
-
function createOptionalAuth(options) {
|
|
1122
|
-
return createAuth({
|
|
1123
|
-
...options,
|
|
1124
|
-
required: false
|
|
1125
|
-
});
|
|
1126
|
-
}
|
|
1127
|
-
function createRoleAuth(roles, options) {
|
|
1128
|
-
return createAuth({
|
|
1129
|
-
...options,
|
|
1130
|
-
roles
|
|
1131
|
-
});
|
|
1132
|
-
}
|
|
1133
|
-
function createPermissionAuth(permissions, options) {
|
|
1134
|
-
return createAuth({
|
|
1135
|
-
...options,
|
|
1136
|
-
permissions
|
|
1137
|
-
});
|
|
1138
|
-
}
|
|
1139
|
-
|
|
1140
|
-
//#endregion
|
|
1141
|
-
//#region src/defineRoute.ts
|
|
1142
|
-
function defineRoutes(routes) {
|
|
1143
|
-
return routes;
|
|
1144
|
-
}
|
|
1145
|
-
|
|
1146
|
-
//#endregion
|
|
1147
|
-
//#region src/utils/go-await.ts
|
|
1148
|
-
/**
|
|
1149
|
-
* Go 风格的错误处理工具
|
|
1150
|
-
* 将 Promise 转换为 [Error | null, T | undefined] 格式
|
|
1151
|
-
*
|
|
1152
|
-
* @author Framework Team
|
|
1153
|
-
* @version 1.0.0
|
|
1154
|
-
* @license MIT
|
|
1155
|
-
*/
|
|
1156
|
-
/**
|
|
1157
|
-
* Go 风格的错误处理工具
|
|
1158
|
-
* 将 Promise 转换为 [Error | null, T | undefined] 格式
|
|
1159
|
-
*
|
|
1160
|
-
* @param promise 要处理的 Promise
|
|
1161
|
-
* @returns [Error | null, T | undefined] 元组,第一个元素是错误,第二个是结果
|
|
1162
|
-
*
|
|
1163
|
-
* @example
|
|
1164
|
-
* ```typescript
|
|
1165
|
-
* const [error, result] = await goAwait(someAsyncFunction());
|
|
1166
|
-
* if (error) {
|
|
1167
|
-
* console.error("操作失败:", error);
|
|
1168
|
-
* } else {
|
|
1169
|
-
* console.log("操作成功:", result);
|
|
1170
|
-
* }
|
|
1171
|
-
* ```
|
|
1172
|
-
*/
|
|
1173
|
-
function goAwait(promise) {
|
|
1174
|
-
return promise.then((data) => [null, data]).catch((err) => [err instanceof Error ? err : new Error(String(err)), void 0]);
|
|
1175
|
-
}
|
|
1176
|
-
|
|
1177
|
-
//#endregion
|
|
1178
|
-
//#region src/utils/validators/schema-validators-ultra.ts
|
|
1179
|
-
const SCHEMA_FLAGS = {
|
|
1180
|
-
BODY: 1,
|
|
1181
|
-
QUERY: 2,
|
|
1182
|
-
PARAMS: 4,
|
|
1183
|
-
HEADERS: 8,
|
|
1184
|
-
COOKIES: 16
|
|
1185
|
-
};
|
|
1186
|
-
const ultraSchemaCache = /* @__PURE__ */ new Map();
|
|
1187
|
-
const schemaCacheHits = /* @__PURE__ */ new Map();
|
|
1188
|
-
const errorPool = [];
|
|
1189
|
-
const ERROR_POOL_SIZE = 200;
|
|
1190
|
-
const errorMessagePool = /* @__PURE__ */ new Map();
|
|
1191
|
-
const commonMessages = [
|
|
1192
|
-
"请求体验证失败",
|
|
1193
|
-
"Query参数验证失败",
|
|
1194
|
-
"路径参数验证失败",
|
|
1195
|
-
"请求头验证失败",
|
|
1196
|
-
"Cookie验证失败",
|
|
1197
|
-
"类型验证失败",
|
|
1198
|
-
"Schema编译失败",
|
|
1199
|
-
"未知错误"
|
|
1200
|
-
];
|
|
1201
|
-
commonMessages.forEach((msg) => errorMessagePool.set(msg, msg));
|
|
1202
|
-
for (let i = 0; i < ERROR_POOL_SIZE; i++) errorPool.push(/* @__PURE__ */ new Error());
|
|
1203
|
-
let errorPoolIndex = 0;
|
|
1204
|
-
function getErrorFromPool(message) {
|
|
1205
|
-
const error = errorPool[errorPoolIndex];
|
|
1206
|
-
error.message = message;
|
|
1207
|
-
errorPoolIndex = (errorPoolIndex + 1) % ERROR_POOL_SIZE;
|
|
1208
|
-
return error;
|
|
1209
|
-
}
|
|
1210
|
-
function getPooledString(key) {
|
|
1211
|
-
let pooled = errorMessagePool.get(key);
|
|
1212
|
-
if (!pooled) {
|
|
1213
|
-
pooled = key;
|
|
1214
|
-
errorMessagePool.set(key, key);
|
|
1215
|
-
}
|
|
1216
|
-
return pooled;
|
|
1217
|
-
}
|
|
1218
|
-
function getUltraSchemaCompiler(schema) {
|
|
1219
|
-
let compiler = ultraSchemaCache.get(schema);
|
|
1220
|
-
if (compiler) {
|
|
1221
|
-
schemaCacheHits.set(schema, (schemaCacheHits.get(schema) || 0) + 1);
|
|
1222
|
-
return compiler;
|
|
1223
|
-
}
|
|
1224
|
-
try {
|
|
1225
|
-
compiler = TypeCompiler.Compile(schema);
|
|
1226
|
-
ultraSchemaCache.set(schema, compiler);
|
|
1227
|
-
return compiler;
|
|
1228
|
-
} catch (error) {
|
|
1229
|
-
return null;
|
|
1230
|
-
}
|
|
1231
|
-
}
|
|
1232
|
-
function getSchemaFlags(config) {
|
|
1233
|
-
let flags = 0;
|
|
1234
|
-
if (config.body) flags |= SCHEMA_FLAGS.BODY;
|
|
1235
|
-
if (config.query) flags |= SCHEMA_FLAGS.QUERY;
|
|
1236
|
-
if (config.params) flags |= SCHEMA_FLAGS.PARAMS;
|
|
1237
|
-
if (config.headers) flags |= SCHEMA_FLAGS.HEADERS;
|
|
1238
|
-
if (config.cookies) flags |= SCHEMA_FLAGS.COOKIES;
|
|
1239
|
-
return flags;
|
|
1240
|
-
}
|
|
1241
|
-
function validateSchemaUltra(schema, data, context) {
|
|
1242
|
-
if (!schema) return data;
|
|
1243
|
-
try {
|
|
1244
|
-
let compiler = ultraSchemaCache.get(schema);
|
|
1245
|
-
if (!compiler) try {
|
|
1246
|
-
compiler = TypeCompiler.Compile(schema);
|
|
1247
|
-
ultraSchemaCache.set(schema, compiler);
|
|
1248
|
-
} catch (error) {
|
|
1249
|
-
const message = getPooledString(`${context}验证失败: Schema编译失败`);
|
|
1250
|
-
throw getErrorFromPool(message);
|
|
1251
|
-
}
|
|
1252
|
-
const result = compiler.Check(data);
|
|
1253
|
-
if (!result) {
|
|
1254
|
-
const message = getPooledString(`${context}验证失败`);
|
|
1255
|
-
throw getErrorFromPool(message);
|
|
1256
|
-
}
|
|
1257
|
-
return data;
|
|
1258
|
-
} catch (error) {
|
|
1259
|
-
if (error instanceof Error && error.message.includes("验证失败")) throw error;
|
|
1260
|
-
const message = getPooledString(`${context}验证失败: ${error instanceof Error ? error.message : "未知错误"}`);
|
|
1261
|
-
throw getErrorFromPool(message);
|
|
1262
|
-
}
|
|
1263
|
-
}
|
|
1264
|
-
function validateAllSchemasUltra(config, data) {
|
|
1265
|
-
const flags = getSchemaFlags(config);
|
|
1266
|
-
if (flags & SCHEMA_FLAGS.BODY) validateSchemaUltra(config.body, data.body, "请求体");
|
|
1267
|
-
if (flags & SCHEMA_FLAGS.QUERY) validateSchemaUltra(config.query, data.query, "Query参数");
|
|
1268
|
-
if (flags & SCHEMA_FLAGS.PARAMS) validateSchemaUltra(config.params, data.params, "路径参数");
|
|
1269
|
-
if (flags & SCHEMA_FLAGS.HEADERS) validateSchemaUltra(config.headers, data.headers, "请求头");
|
|
1270
|
-
if (flags & SCHEMA_FLAGS.COOKIES) validateSchemaUltra(config.cookies, data.cookies, "Cookie");
|
|
1271
|
-
return data;
|
|
1272
|
-
}
|
|
1273
|
-
function precompileSchemasUltra(config) {
|
|
1274
|
-
const flags = getSchemaFlags(config);
|
|
1275
|
-
if (flags & SCHEMA_FLAGS.BODY && config.body) getUltraSchemaCompiler(config.body);
|
|
1276
|
-
if (flags & SCHEMA_FLAGS.QUERY && config.query) getUltraSchemaCompiler(config.query);
|
|
1277
|
-
if (flags & SCHEMA_FLAGS.PARAMS && config.params) getUltraSchemaCompiler(config.params);
|
|
1278
|
-
if (flags & SCHEMA_FLAGS.HEADERS && config.headers) getUltraSchemaCompiler(config.headers);
|
|
1279
|
-
if (flags & SCHEMA_FLAGS.COOKIES && config.cookies) getUltraSchemaCompiler(config.cookies);
|
|
1280
|
-
}
|
|
1281
|
-
|
|
1282
|
-
//#endregion
|
|
1283
|
-
//#region src/utils/route-handler-factory.ts
|
|
1284
|
-
const defaultValidationErrorHandler = (error, field, value, schema) => {
|
|
1285
|
-
return json({
|
|
1286
|
-
success: false,
|
|
1287
|
-
error: "Validation Error",
|
|
1288
|
-
message: error instanceof Error ? error.message : "验证失败",
|
|
1289
|
-
field,
|
|
1290
|
-
receivedValue: value,
|
|
1291
|
-
timestamp: (/* @__PURE__ */ new Date()).toISOString()
|
|
1292
|
-
}, 400);
|
|
1293
|
-
};
|
|
1294
|
-
const TEXT_HEADERS = { "Content-Type": "text/plain; charset=utf-8" };
|
|
1295
|
-
const EMPTY_RESPONSE_204 = new Response(null, { status: 204 });
|
|
1296
|
-
function autoResponseUltra(result) {
|
|
1297
|
-
if (result instanceof Response) return result;
|
|
1298
|
-
if (result === null || result === void 0) return EMPTY_RESPONSE_204;
|
|
1299
|
-
switch (typeof result) {
|
|
1300
|
-
case "string": return new Response(result, { headers: TEXT_HEADERS });
|
|
1301
|
-
case "number":
|
|
1302
|
-
case "boolean": return new Response(result.toString(), { headers: TEXT_HEADERS });
|
|
1303
|
-
case "object":
|
|
1304
|
-
if ("data" in result) {
|
|
1305
|
-
const { data, status = 200, headers = {} } = result;
|
|
1306
|
-
if (data === null || data === void 0) return new Response("", {
|
|
1307
|
-
status: status === 200 ? 204 : status,
|
|
1308
|
-
headers
|
|
1309
|
-
});
|
|
1310
|
-
if (typeof data === "string" || typeof data === "number" || typeof data === "boolean") {
|
|
1311
|
-
const finalHeaders = {
|
|
1312
|
-
"Content-Type": "text/plain; charset=utf-8",
|
|
1313
|
-
...headers
|
|
1314
|
-
};
|
|
1315
|
-
return new Response(data.toString(), {
|
|
1316
|
-
status,
|
|
1317
|
-
headers: finalHeaders
|
|
1318
|
-
});
|
|
1319
|
-
}
|
|
1320
|
-
return json(data, status, headers);
|
|
1321
|
-
}
|
|
1322
|
-
return json(result);
|
|
1323
|
-
default: return EMPTY_RESPONSE_204;
|
|
1324
|
-
}
|
|
1325
|
-
}
|
|
1326
|
-
function createRouteHandler(handler, config = {}) {
|
|
1327
|
-
const hasBodySchema = config.body !== void 0;
|
|
1328
|
-
const hasQuerySchema = config.query !== void 0;
|
|
1329
|
-
const hasParamsSchema = config.params !== void 0;
|
|
1330
|
-
const hasHeadersSchema = config.headers !== void 0;
|
|
1331
|
-
const hasCookiesSchema = config.cookies !== void 0;
|
|
1332
|
-
if (hasBodySchema || hasQuerySchema || hasParamsSchema || hasHeadersSchema || hasCookiesSchema) precompileSchemasUltra(config);
|
|
1333
|
-
const errorHandler$1 = config.validationErrorHandler || defaultValidationErrorHandler;
|
|
1334
|
-
return async (req) => {
|
|
1335
|
-
try {
|
|
1336
|
-
let queryObj = {};
|
|
1337
|
-
let headers = {};
|
|
1338
|
-
let cookies = {};
|
|
1339
|
-
let body = void 0;
|
|
1340
|
-
let params = {};
|
|
1341
|
-
queryObj = parseQuery(req);
|
|
1342
|
-
headers = parseHeaders(req);
|
|
1343
|
-
cookies = parseCookies(req);
|
|
1344
|
-
const [, parsedBody] = await goAwait(parseBody(req));
|
|
1345
|
-
body = parsedBody;
|
|
1346
|
-
params = req.pathParams || req.params || {};
|
|
1347
|
-
if (hasBodySchema || hasQuerySchema || hasParamsSchema || hasHeadersSchema || hasCookiesSchema) {
|
|
1348
|
-
const data = {
|
|
1349
|
-
body,
|
|
1350
|
-
query: queryObj,
|
|
1351
|
-
params,
|
|
1352
|
-
headers,
|
|
1353
|
-
cookies
|
|
1354
|
-
};
|
|
1355
|
-
validateAllSchemasUltra(config, data);
|
|
1356
|
-
}
|
|
1357
|
-
const extras = req.__locals ?? {};
|
|
1358
|
-
const result = await handler({
|
|
1359
|
-
req,
|
|
1360
|
-
body,
|
|
1361
|
-
query: queryObj,
|
|
1362
|
-
params,
|
|
1363
|
-
headers,
|
|
1364
|
-
cookies,
|
|
1365
|
-
...extras
|
|
1366
|
-
});
|
|
1367
|
-
return autoResponseUltra(result);
|
|
1368
|
-
} catch (error) {
|
|
1369
|
-
if (error instanceof Error && error.message.includes("验证失败")) {
|
|
1370
|
-
const field = extractFieldFromError(error);
|
|
1371
|
-
const value = extractValueFromError(error);
|
|
1372
|
-
const schema = extractSchemaFromError(error);
|
|
1373
|
-
return await errorHandler$1(error, field, value, schema);
|
|
1374
|
-
}
|
|
1375
|
-
return json({
|
|
1376
|
-
success: false,
|
|
1377
|
-
error: "Internal Error",
|
|
1378
|
-
message: error instanceof Error ? error.message : "未知错误"
|
|
1379
|
-
}, 500);
|
|
1380
|
-
}
|
|
1381
|
-
};
|
|
1382
|
-
}
|
|
1383
|
-
function extractFieldFromError(error) {
|
|
1384
|
-
const fieldMatch = error.message.match(/字段\s*(\w+)/);
|
|
1385
|
-
return fieldMatch ? fieldMatch[1] : "unknown";
|
|
1386
|
-
}
|
|
1387
|
-
function extractValueFromError(error) {
|
|
1388
|
-
return void 0;
|
|
1389
|
-
}
|
|
1390
|
-
function extractSchemaFromError(error) {
|
|
1391
|
-
return void 0;
|
|
1392
|
-
}
|
|
1393
|
-
function withExtra() {
|
|
1394
|
-
return function withExtraHandler(config, handler) {
|
|
1395
|
-
return createRouteHandler(handler, config);
|
|
1396
|
-
};
|
|
1397
|
-
}
|
|
1398
|
-
|
|
1399
|
-
//#endregion
|
|
1400
|
-
export { BaseServer, ComponentServer, DependencyManager, HtmlRenderer, PathMatcher, Server, ServerFactory, TokenError, VafastError, base64urlDecode, base64urlEncode, composeMiddleware, createAuth, createCORS, createOptionalAuth, createPermissionAuth, createRoleAuth, createRouteHandler, createTokenPair, defineRoutes, empty, flattenNestedRoutes, generateToken, getTokenTimeRemaining, html, isTokenExpired, json, matchPath, normalizePath, parseBody, parseBodyAs, parseCookies, parseFile, parseFormData, parseHeaders, parseQuery, parseToken, rateLimit, redirect, refreshToken, requireAuth, stream, text, verifyToken, withExtra };
|
|
1
|
+
export * from "./server";
|
|
2
|
+
export * from "./middleware";
|
|
3
|
+
export * from "./utils/parsers";
|
|
4
|
+
export * from "./utils/response";
|
|
5
|
+
export * from "./router";
|
|
6
|
+
export * from "./middleware/authMiddleware";
|
|
7
|
+
export * from "./middleware/rateLimit";
|
|
8
|
+
export * from "./middleware/cors";
|
|
9
|
+
export * from "./auth/token";
|
|
10
|
+
export * from "./middleware/auth";
|
|
11
|
+
export * from "./utils/base64url";
|
|
12
|
+
export * from "./defineRoute";
|
|
13
|
+
export * from "./types";
|
|
14
|
+
export * from "./utils/route-handler-factory";
|