befly 3.17.13 → 3.17.15

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.
@@ -2,7 +2,6 @@ import { UAParser } from "ua-parser-js";
2
2
 
3
3
  import adminTable from "../../tables/admin.json";
4
4
  import { toSessionTtlSeconds } from "../../utils/toSessionTtlSeconds.js";
5
- import { isString } from "../../utils/is.js";
6
5
 
7
6
  export default {
8
7
  name: "管理员登录",
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.req.headers.get("authorization");
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
- if (!ctx.apiPath) {
19
- return;
20
- }
21
-
22
- // body=raw 模式:跳过解析,保留原始请求供 handler 自行处理
18
+ // apiBody=raw 模式:跳过解析,保留原始请求供 handler 自行处理
23
19
  // 适用于:微信回调、支付回调、webhook 等需要手动解密/验签的场景
24
- if (ctx.body === "raw") {
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.req.method === "GET") {
31
- const url = new URL(ctx.req.url);
32
- const params = Object.fromEntries(url.searchParams);
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
- const merged = Object.assign({}, queryParams, body);
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
- const merged = Object.assign({}, queryParams, body);
57
- ctx.body = merged;
45
+ ctx.body = Object.assign({}, queryParams, body);
58
46
  } else {
59
47
  // 不支持的 Content-Type
60
48
  ctx.response = ErrorResponse(
@@ -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 { isNonEmptyString, isString, isValidPositiveInt } from "../utils/is.js";
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.auth === false) {
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 auth 为角色类型白名单时,仅做 ctx.roleType 校验
35
- if (Array.isArray(ctx.auth) && ctx.auth.includes(ctx.roleType) === false) {
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} 接口`,
@@ -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.fields)) {
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.fields, ctx.required || []);
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
@@ -87,7 +87,7 @@ export class Connect {
87
87
  };
88
88
 
89
89
  // Called when disconnected from Redis server
90
- this.redisClient.onclose = (error) => {
90
+ this.redisClient.onclose = () => {
91
91
  Logger.warn("Redis 断开连接");
92
92
  };
93
93
  } catch (error) {
@@ -1,4 +1,4 @@
1
- import { isNullable, isNumber } from "../../utils/is.js";
1
+ import { isNumber } from "../../utils/is.js";
2
2
  import { snakeCase } from "../../utils/util.js";
3
3
  import { Logger } from "../logger.js";
4
4
  import { SqlBuilder } from "../sqlBuilder/index.js";
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 { isAbsolute as nodePathIsAbsolute, join as nodePathJoin, resolve as nodePathResolve } from "node:path";
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";
@@ -132,7 +132,7 @@ class LogFileSink {
132
132
  this.scheduledTimer = setTimeout(() => {
133
133
  // timer 触发时先清空句柄,避免 flush 内再次 schedule 时被认为“已安排”。
134
134
  this.scheduledTimer = null;
135
- void this.flush();
135
+ this.flush();
136
136
  }, this.flushDelayMs);
137
137
  }
138
138
 
@@ -220,7 +220,7 @@ class LogFileSink {
220
220
  this.stream.on("error", (error) => {
221
221
  safeWriteStderr(`[Logger] file sink error (${this.prefix}): ${error?.message || error}`);
222
222
  this.disabled = true;
223
- void this.closeStream();
223
+ this.closeStream();
224
224
  });
225
225
  } catch (error) {
226
226
  safeWriteStderr(`[Logger] createWriteStream failed (${this.prefix}): ${error?.message || error}`);
@@ -349,7 +349,7 @@ function ensureLogDirExists() {
349
349
  */
350
350
  export function configure(cfg) {
351
351
  // 旧实例可能仍持有文件句柄;这里异步关闭(不阻塞主流程)
352
- void shutdown();
352
+ shutdown();
353
353
 
354
354
  // 方案B:每次 configure 都从默认配置重新构建(避免继承上一次配置造成测试/运行时污染)
355
355
  config = Object.assign(
@@ -5,7 +5,6 @@
5
5
 
6
6
  import { Connect } from "./connect.js";
7
7
  import { Logger } from "./logger.js";
8
- import { isFiniteNumber, isNonEmptyString, isString } from "../utils/is.js";
9
8
 
10
9
  /**
11
10
  * Redis 助手类
package/package.json CHANGED
@@ -1,7 +1,7 @@
1
1
  {
2
2
  "name": "befly",
3
- "version": "3.17.13",
4
- "gitHead": "1259303ee55a5aa686be914e961dc42767c0136b",
3
+ "version": "3.17.15",
4
+ "gitHead": "aa84ccde6c76bc45727464c30d4680c379815110",
5
5
  "private": false,
6
6
  "description": "Befly - 为 Bun 专属打造的 JavaScript API 接口框架核心引擎",
7
7
  "keywords": [
package/router/api.js CHANGED
@@ -56,6 +56,7 @@ export function apiHandler(apis, hooks, context) {
56
56
  const ctx = {
57
57
  // 请求的参数
58
58
  method: req.method,
59
+ url: req.url,
59
60
  body: {},
60
61
  req: req,
61
62
  now: now,
@@ -66,13 +67,13 @@ export function apiHandler(apis, hooks, context) {
66
67
  // 接口的参数
67
68
  apiPath: apiData.apiPath,
68
69
  apiName: apiData.name,
69
- filePath: apiData.filePath,
70
- handler: apiData.handler,
71
- method: apiData.method,
72
- body: apiData.body,
73
- auth: apiData.auth,
74
- fields: apiData.fields,
75
- required: apiData.required
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
76
77
  };
77
78
 
78
79
  try {
@@ -108,7 +109,7 @@ export function apiHandler(apis, hooks, context) {
108
109
  Logger.info("请求", logData);
109
110
 
110
111
  // 5. 执行 API handler
111
- const result = await ctx.handler(context, ctx);
112
+ const result = await ctx.apiHandler(context, ctx);
112
113
 
113
114
  if (result instanceof Response) {
114
115
  ctx.response = result;
@@ -120,9 +121,8 @@ export function apiHandler(apis, hooks, context) {
120
121
  return FinalResponse(ctx);
121
122
  } catch (err) {
122
123
  // 全局错误处理
123
- const errorPath = ctx.apiPath ? ctx.apiPath : req.url;
124
124
  Logger.error("请求错误", err, {
125
- path: errorPath,
125
+ path: ctx.apiPath,
126
126
  requestId: requestId,
127
127
  method: req.method,
128
128
  apiPath: ctx.apiPath,
@@ -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.prototype.hasOwnProperty.call(existingFields, fieldInfo.fieldName)) {
60
+ if (Object.hasOwn(existingFields, fieldInfo.fieldName)) {
61
61
  continue;
62
62
  }
63
63
 
package/sql/befly.sql CHANGED
@@ -23,6 +23,7 @@ CREATE TABLE IF NOT EXISTS `befly_api` (
23
23
  `auth` VARCHAR(200) NOT NULL DEFAULT '',
24
24
  `path` VARCHAR(200) NOT NULL DEFAULT '',
25
25
  `parent_path` VARCHAR(200) NOT NULL DEFAULT '',
26
+ `method` VARCHAR(20) NOT NULL DEFAULT '',
26
27
  `state` TINYINT NOT NULL DEFAULT 1,
27
28
  `created_at` BIGINT NOT NULL DEFAULT 0,
28
29
  `updated_at` BIGINT NOT NULL DEFAULT 0,
package/sync/api.js CHANGED
@@ -57,6 +57,7 @@ export async function syncApi(ctx, apis) {
57
57
  data: {
58
58
  name: api.name,
59
59
  path: api.apiPath,
60
+ method: api.method,
60
61
  parentPath: parentPath,
61
62
  auth: auth
62
63
  }
@@ -67,6 +68,7 @@ export async function syncApi(ctx, apis) {
67
68
  insList.push({
68
69
  name: api.name,
69
70
  path: api.apiPath,
71
+ method: api.method,
70
72
  parentPath: parentPath,
71
73
  auth: auth
72
74
  });
package/tables/api.json CHANGED
@@ -11,6 +11,12 @@
11
11
  "min": 1,
12
12
  "max": 200
13
13
  },
14
+ "method": {
15
+ "name": "请求方式",
16
+ "input": "string",
17
+ "min": 1,
18
+ "max": 20
19
+ },
14
20
  "path": {
15
21
  "name": "接口路径",
16
22
  "input": "string",
@@ -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";
@@ -1,5 +1,3 @@
1
- import { join } from "pathe";
2
-
3
1
  import {
4
2
  //
5
3
  coreTableDir,