befly 3.17.12 → 3.17.14
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/apis/auth/login.js +0 -1
- package/checks/table.js +0 -2
- package/hooks/auth.js +1 -1
- package/hooks/parser.js +9 -21
- package/hooks/permission.js +4 -5
- package/hooks/validator.js +2 -2
- package/index.js +0 -1
- package/lib/connect.js +1 -1
- package/lib/dbHelper/dataOps.js +1 -1
- package/lib/logger.js +1 -1
- package/lib/redisHelper.js +0 -1
- package/package.json +2 -2
- package/router/api.js +11 -10
- package/scripts/syncDb/diff.js +1 -1
- package/utils/formatZodIssues.js +0 -11
- package/utils/scanSources.js +0 -2
package/apis/auth/login.js
CHANGED
package/checks/table.js
CHANGED
|
@@ -8,10 +8,8 @@ z.config(z.locales.zhCN());
|
|
|
8
8
|
|
|
9
9
|
const lowerCamelRegex = /^_?[a-z][a-z0-9]*(?:[A-Z][a-z0-9]*)*$/;
|
|
10
10
|
const noTrimString = z.string().refine(isNoTrimStringAllowEmpty, "不允许首尾空格");
|
|
11
|
-
const FIELD_NAME_REGEX_SOURCE = "^[\\u4e00-\\u9fa5a-zA-Z0-9_]+$";
|
|
12
11
|
const INPUT_TYPES = ["number", "integer", "string", "char", "array", "array_number", "array_integer", "json", "json_number", "json_integer"];
|
|
13
12
|
|
|
14
|
-
const fieldNameRegex = new RegExp(FIELD_NAME_REGEX_SOURCE);
|
|
15
13
|
const inputRegexLiteral = /^\/.*?\/[gimsuy]*$/;
|
|
16
14
|
const inputEnumRegex = /^[^/].*\|.*$/;
|
|
17
15
|
const inputAliasRegex = /^@.+$/;
|
package/hooks/auth.js
CHANGED
|
@@ -3,7 +3,7 @@ import { toSessionTtlSeconds } from "../utils/toSessionTtlSeconds.js";
|
|
|
3
3
|
export default {
|
|
4
4
|
deps: [],
|
|
5
5
|
handler: async (befly, ctx) => {
|
|
6
|
-
const authHeader = ctx.
|
|
6
|
+
const authHeader = ctx.headers.get("authorization");
|
|
7
7
|
const ttlSeconds = toSessionTtlSeconds(befly.config?.session?.expireDays);
|
|
8
8
|
|
|
9
9
|
if (authHeader && authHeader.startsWith("Bearer ")) {
|
package/hooks/parser.js
CHANGED
|
@@ -15,36 +15,25 @@ const xmlParser = new XMLParser();
|
|
|
15
15
|
export default {
|
|
16
16
|
deps: ["auth"],
|
|
17
17
|
handler: async (befly, ctx) => {
|
|
18
|
-
|
|
19
|
-
return;
|
|
20
|
-
}
|
|
21
|
-
|
|
22
|
-
// body=raw 模式:跳过解析,保留原始请求供 handler 自行处理
|
|
18
|
+
// apiBody=raw 模式:跳过解析,保留原始请求供 handler 自行处理
|
|
23
19
|
// 适用于:微信回调、支付回调、webhook 等需要手动解密/验签的场景
|
|
24
|
-
if (ctx.
|
|
20
|
+
if (ctx.apiBody === "raw") {
|
|
25
21
|
ctx.body = {};
|
|
26
22
|
return;
|
|
27
23
|
}
|
|
28
|
-
|
|
24
|
+
const queryParams = Object.fromEntries(new URL(ctx.url).searchParams);
|
|
25
|
+
const contentType = ctx.headers.get("content-type") || "";
|
|
29
26
|
// GET 请求:解析查询参数
|
|
30
|
-
if (ctx.
|
|
31
|
-
|
|
32
|
-
|
|
33
|
-
ctx.body = params;
|
|
34
|
-
} else if (ctx.req.method === "POST") {
|
|
27
|
+
if (ctx.method === "GET") {
|
|
28
|
+
ctx.body = queryParams;
|
|
29
|
+
} else if (ctx.method === "POST") {
|
|
35
30
|
// POST 请求:解析请求体
|
|
36
|
-
const contentType = ctx.req.headers.get("content-type") || "";
|
|
37
|
-
// 获取 URL 查询参数(POST 请求也可能带参数)
|
|
38
|
-
const url = new URL(ctx.req.url);
|
|
39
|
-
const queryParams = Object.fromEntries(url.searchParams);
|
|
40
|
-
|
|
41
31
|
try {
|
|
42
32
|
// JSON 格式
|
|
43
33
|
if (contentType.includes("application/json")) {
|
|
44
34
|
const body = await ctx.req.json();
|
|
45
35
|
// 合并 URL 参数和请求体(请求体优先)
|
|
46
|
-
|
|
47
|
-
ctx.body = merged;
|
|
36
|
+
ctx.body = Object.assign({}, queryParams, body);
|
|
48
37
|
} else if (contentType.includes("application/xml") || contentType.includes("text/xml")) {
|
|
49
38
|
// XML 格式
|
|
50
39
|
const text = await ctx.req.text();
|
|
@@ -53,8 +42,7 @@ export default {
|
|
|
53
42
|
const rootKey = Object.keys(parsed)[0];
|
|
54
43
|
const body = rootKey && typeof parsed[rootKey] === "object" ? parsed[rootKey] : parsed;
|
|
55
44
|
// 合并 URL 参数和请求体(请求体优先)
|
|
56
|
-
|
|
57
|
-
ctx.body = merged;
|
|
45
|
+
ctx.body = Object.assign({}, queryParams, body);
|
|
58
46
|
} else {
|
|
59
47
|
// 不支持的 Content-Type
|
|
60
48
|
ctx.response = ErrorResponse(
|
package/hooks/permission.js
CHANGED
|
@@ -1,8 +1,7 @@
|
|
|
1
1
|
import { CacheKeys } from "../lib/cacheKeys.js";
|
|
2
|
-
import { Logger } from "../lib/logger.js";
|
|
3
2
|
// 相对导入
|
|
4
3
|
import { ErrorResponse } from "../utils/response.js";
|
|
5
|
-
import {
|
|
4
|
+
import { isValidPositiveInt } from "../utils/is.js";
|
|
6
5
|
|
|
7
6
|
/**
|
|
8
7
|
* 权限检查钩子
|
|
@@ -16,7 +15,7 @@ export default {
|
|
|
16
15
|
deps: ["validator"],
|
|
17
16
|
handler: async (befly, ctx) => {
|
|
18
17
|
// 1. 接口无需权限
|
|
19
|
-
if (ctx.
|
|
18
|
+
if (ctx.apiAuth === false) {
|
|
20
19
|
return;
|
|
21
20
|
}
|
|
22
21
|
|
|
@@ -31,8 +30,8 @@ export default {
|
|
|
31
30
|
return;
|
|
32
31
|
}
|
|
33
32
|
|
|
34
|
-
// 3.5
|
|
35
|
-
if (Array.isArray(ctx.
|
|
33
|
+
// 3.5 apiAuth 为角色类型白名单时,仅做 ctx.roleType 校验
|
|
34
|
+
if (Array.isArray(ctx.apiAuth) && ctx.apiAuth.includes(ctx.roleType) === false) {
|
|
36
35
|
ctx.response = ErrorResponse(
|
|
37
36
|
ctx,
|
|
38
37
|
`无权访问 ${ctx.apiName} 接口`,
|
package/hooks/validator.js
CHANGED
|
@@ -15,7 +15,7 @@ export default {
|
|
|
15
15
|
const rawBody = isPlainObject(ctx.body) ? ctx.body : {};
|
|
16
16
|
const nextBody = {};
|
|
17
17
|
|
|
18
|
-
for (const [field] of Object.entries(ctx.
|
|
18
|
+
for (const [field] of Object.entries(ctx.apiFields)) {
|
|
19
19
|
let value = rawBody[field];
|
|
20
20
|
|
|
21
21
|
if (value === undefined) {
|
|
@@ -33,7 +33,7 @@ export default {
|
|
|
33
33
|
ctx.body = nextBody;
|
|
34
34
|
|
|
35
35
|
// 验证参数
|
|
36
|
-
const result = Validator.validate(ctx.body, ctx.
|
|
36
|
+
const result = Validator.validate(ctx.body, ctx.apiFields, ctx.apiRequired);
|
|
37
37
|
|
|
38
38
|
if (result.code !== 0) {
|
|
39
39
|
ctx.response = ErrorResponse(ctx, result.firstError || "参数验证失败", 1, null, result.fieldErrors, "validator");
|
package/index.js
CHANGED
|
@@ -32,7 +32,6 @@ import { calcPerfTime } from "./utils/calcPerfTime.js";
|
|
|
32
32
|
import { scanSources } from "./utils/scanSources.js";
|
|
33
33
|
import { isPrimaryProcess } from "./utils/is.js";
|
|
34
34
|
import { deepMerge } from "./utils/deepMerge.js";
|
|
35
|
-
import { omit } from "./utils/util.js";
|
|
36
35
|
import { sortModules } from "./utils/sortModules.js";
|
|
37
36
|
|
|
38
37
|
function prefixMenuPaths(menus, prefix) {
|
package/lib/connect.js
CHANGED
package/lib/dbHelper/dataOps.js
CHANGED
package/lib/logger.js
CHANGED
|
@@ -4,7 +4,7 @@
|
|
|
4
4
|
|
|
5
5
|
import { createWriteStream, existsSync, mkdirSync } from "node:fs";
|
|
6
6
|
import { stat } from "node:fs/promises";
|
|
7
|
-
import {
|
|
7
|
+
import { join as nodePathJoin, resolve as nodePathResolve } from "node:path";
|
|
8
8
|
|
|
9
9
|
import { formatYmdHms } from "../utils/formatYmdHms.js";
|
|
10
10
|
import { buildSensitiveKeyMatcher, sanitizeLogObject } from "../utils/loggerUtils.js";
|
package/lib/redisHelper.js
CHANGED
package/package.json
CHANGED
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "befly",
|
|
3
|
-
"version": "3.17.
|
|
4
|
-
"gitHead": "
|
|
3
|
+
"version": "3.17.14",
|
|
4
|
+
"gitHead": "c71ecec269032dcc0aa8ca995da4918f99f5ee0e",
|
|
5
5
|
"private": false,
|
|
6
6
|
"description": "Befly - 为 Bun 专属打造的 JavaScript API 接口框架核心引擎",
|
|
7
7
|
"keywords": [
|
package/router/api.js
CHANGED
|
@@ -20,6 +20,7 @@ export function apiHandler(apis, hooks, context) {
|
|
|
20
20
|
return async (req, server) => {
|
|
21
21
|
// 1. 生成请求 ID
|
|
22
22
|
const requestId = genShortId();
|
|
23
|
+
const now = Date.now();
|
|
23
24
|
|
|
24
25
|
const corsHeaders = setCorsOptions(req, context.config?.cors || {});
|
|
25
26
|
corsHeaders["X-Request-ID"] = requestId;
|
|
@@ -55,6 +56,7 @@ export function apiHandler(apis, hooks, context) {
|
|
|
55
56
|
const ctx = {
|
|
56
57
|
// 请求的参数
|
|
57
58
|
method: req.method,
|
|
59
|
+
url: req.url,
|
|
58
60
|
body: {},
|
|
59
61
|
req: req,
|
|
60
62
|
now: now,
|
|
@@ -65,13 +67,13 @@ export function apiHandler(apis, hooks, context) {
|
|
|
65
67
|
// 接口的参数
|
|
66
68
|
apiPath: apiData.apiPath,
|
|
67
69
|
apiName: apiData.name,
|
|
68
|
-
|
|
69
|
-
|
|
70
|
-
|
|
71
|
-
|
|
72
|
-
|
|
73
|
-
|
|
74
|
-
|
|
70
|
+
apiMethod: apiData.method,
|
|
71
|
+
apiBody: apiData.body,
|
|
72
|
+
apiHandler: apiData.handler,
|
|
73
|
+
apiAuth: apiData.auth,
|
|
74
|
+
apiFields: apiData.fields,
|
|
75
|
+
apiRequired: apiData.required,
|
|
76
|
+
apiFile: apiData.filePath
|
|
75
77
|
};
|
|
76
78
|
|
|
77
79
|
try {
|
|
@@ -107,7 +109,7 @@ export function apiHandler(apis, hooks, context) {
|
|
|
107
109
|
Logger.info("请求", logData);
|
|
108
110
|
|
|
109
111
|
// 5. 执行 API handler
|
|
110
|
-
const result = await ctx.
|
|
112
|
+
const result = await ctx.apiHandler(context, ctx);
|
|
111
113
|
|
|
112
114
|
if (result instanceof Response) {
|
|
113
115
|
ctx.response = result;
|
|
@@ -119,9 +121,8 @@ export function apiHandler(apis, hooks, context) {
|
|
|
119
121
|
return FinalResponse(ctx);
|
|
120
122
|
} catch (err) {
|
|
121
123
|
// 全局错误处理
|
|
122
|
-
const errorPath = ctx.apiPath ? ctx.apiPath : req.url;
|
|
123
124
|
Logger.error("请求错误", err, {
|
|
124
|
-
path:
|
|
125
|
+
path: ctx.apiPath,
|
|
125
126
|
requestId: requestId,
|
|
126
127
|
method: req.method,
|
|
127
128
|
apiPath: ctx.apiPath,
|
package/scripts/syncDb/diff.js
CHANGED
|
@@ -57,7 +57,7 @@ export function buildSyncDbDiff(groupedDbColumns, existingTableMap) {
|
|
|
57
57
|
const missingFields = [];
|
|
58
58
|
for (const columnMeta of columns) {
|
|
59
59
|
const fieldInfo = toSyncDbFieldDef(columnMeta);
|
|
60
|
-
if (Object.
|
|
60
|
+
if (Object.hasOwn(existingFields, fieldInfo.fieldName)) {
|
|
61
61
|
continue;
|
|
62
62
|
}
|
|
63
63
|
|
package/utils/formatZodIssues.js
CHANGED
|
@@ -1,14 +1,3 @@
|
|
|
1
|
-
function getValueByPath(source, path) {
|
|
2
|
-
if (!Array.isArray(path)) return undefined;
|
|
3
|
-
|
|
4
|
-
let current = source;
|
|
5
|
-
for (const segment of path) {
|
|
6
|
-
if (current === null || current === undefined) return undefined;
|
|
7
|
-
current = current[segment];
|
|
8
|
-
}
|
|
9
|
-
return current;
|
|
10
|
-
}
|
|
11
|
-
|
|
12
1
|
function formatValue(value) {
|
|
13
2
|
if (value === undefined) return "undefined";
|
|
14
3
|
if (value === null) return "null";
|