befly 3.10.18 → 3.11.1

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.
Files changed (223) hide show
  1. package/README.md +83 -307
  2. package/dist/befly.config.d.ts +7 -0
  3. package/{befly.config.ts → dist/befly.config.js} +11 -36
  4. package/dist/befly.js +15621 -0
  5. package/dist/befly.min.js +21 -0
  6. package/dist/checks/checkApi.d.ts +1 -0
  7. package/{checks/checkApi.ts → dist/checks/checkApi.js} +12 -30
  8. package/dist/checks/checkHook.d.ts +1 -0
  9. package/dist/checks/checkHook.js +86 -0
  10. package/dist/checks/checkMenu.d.ts +7 -0
  11. package/{checks/checkMenu.ts → dist/checks/checkMenu.js} +18 -53
  12. package/dist/checks/checkPlugin.d.ts +1 -0
  13. package/dist/checks/checkPlugin.js +86 -0
  14. package/dist/checks/checkTable.d.ts +6 -0
  15. package/{checks/checkTable.ts → dist/checks/checkTable.js} +17 -41
  16. package/dist/configs/presetFields.d.ts +4 -0
  17. package/{configs/presetFields.ts → dist/configs/presetFields.js} +1 -1
  18. package/dist/configs/presetRegexp.d.ts +145 -0
  19. package/{utils/regex.ts → dist/configs/presetRegexp.js} +8 -31
  20. package/dist/hooks/auth.d.ts +7 -0
  21. package/{hooks/auth.ts → dist/hooks/auth.js} +8 -10
  22. package/dist/hooks/cors.d.ts +11 -0
  23. package/{hooks/cors.ts → dist/hooks/cors.js} +5 -13
  24. package/dist/hooks/parser.d.ts +14 -0
  25. package/{hooks/parser.ts → dist/hooks/parser.js} +31 -45
  26. package/dist/hooks/permission.d.ts +14 -0
  27. package/{hooks/permission.ts → dist/hooks/permission.js} +16 -25
  28. package/dist/hooks/validator.d.ts +11 -0
  29. package/{hooks/validator.ts → dist/hooks/validator.js} +9 -14
  30. package/dist/index.d.ts +26 -0
  31. package/{main.ts → dist/index.js} +61 -100
  32. package/dist/lib/asyncContext.d.ts +21 -0
  33. package/dist/lib/asyncContext.js +27 -0
  34. package/dist/lib/cacheHelper.d.ts +95 -0
  35. package/{lib/cacheHelper.ts → dist/lib/cacheHelper.js} +45 -105
  36. package/dist/lib/cacheKeys.d.ts +23 -0
  37. package/{lib/cacheKeys.ts → dist/lib/cacheKeys.js} +5 -10
  38. package/dist/lib/cipher.d.ts +153 -0
  39. package/{lib/cipher.ts → dist/lib/cipher.js} +23 -44
  40. package/dist/lib/connect.d.ts +91 -0
  41. package/{lib/connect.ts → dist/lib/connect.js} +47 -88
  42. package/dist/lib/dbDialect.d.ts +87 -0
  43. package/{lib/dbDialect.ts → dist/lib/dbDialect.js} +32 -112
  44. package/dist/lib/dbHelper.d.ts +204 -0
  45. package/{lib/dbHelper.ts → dist/lib/dbHelper.js} +82 -241
  46. package/dist/lib/dbUtils.d.ts +68 -0
  47. package/{lib/dbUtils.ts → dist/lib/dbUtils.js} +51 -126
  48. package/dist/lib/jwt.d.ts +13 -0
  49. package/{lib/jwt.ts → dist/lib/jwt.js} +11 -32
  50. package/dist/lib/logger.d.ts +42 -0
  51. package/dist/lib/logger.js +1144 -0
  52. package/dist/lib/redisHelper.d.ts +185 -0
  53. package/{lib/redisHelper.ts → dist/lib/redisHelper.js} +97 -141
  54. package/dist/lib/sqlBuilder.d.ts +160 -0
  55. package/{lib/sqlBuilder.ts → dist/lib/sqlBuilder.js} +132 -278
  56. package/dist/lib/sqlCheck.d.ts +23 -0
  57. package/{lib/sqlCheck.ts → dist/lib/sqlCheck.js} +24 -41
  58. package/dist/lib/validator.d.ts +45 -0
  59. package/{lib/validator.ts → dist/lib/validator.js} +44 -61
  60. package/dist/loader/loadApis.d.ts +12 -0
  61. package/{loader/loadApis.ts → dist/loader/loadApis.js} +10 -20
  62. package/dist/loader/loadHooks.d.ts +7 -0
  63. package/dist/loader/loadHooks.js +35 -0
  64. package/dist/loader/loadPlugins.d.ts +8 -0
  65. package/{loader/loadPlugins.ts → dist/loader/loadPlugins.js} +14 -26
  66. package/dist/paths.d.ts +93 -0
  67. package/{paths.ts → dist/paths.js} +6 -19
  68. package/dist/plugins/cache.d.ts +16 -0
  69. package/{plugins/cache.ts → dist/plugins/cache.js} +7 -12
  70. package/dist/plugins/cipher.d.ts +12 -0
  71. package/{plugins/cipher.ts → dist/plugins/cipher.js} +4 -6
  72. package/dist/plugins/config.d.ts +12 -0
  73. package/dist/plugins/config.js +8 -0
  74. package/dist/plugins/db.d.ts +16 -0
  75. package/{plugins/db.ts → dist/plugins/db.js} +11 -17
  76. package/dist/plugins/jwt.d.ts +12 -0
  77. package/dist/plugins/jwt.js +12 -0
  78. package/dist/plugins/logger.d.ts +32 -0
  79. package/{plugins/logger.ts → dist/plugins/logger.js} +5 -8
  80. package/dist/plugins/redis.d.ts +16 -0
  81. package/{plugins/redis.ts → dist/plugins/redis.js} +9 -12
  82. package/dist/plugins/tool.d.ts +81 -0
  83. package/{plugins/tool.ts → dist/plugins/tool.js} +9 -30
  84. package/dist/router/api.d.ts +14 -0
  85. package/dist/router/api.js +107 -0
  86. package/dist/router/static.d.ts +9 -0
  87. package/{router/static.ts → dist/router/static.js} +20 -34
  88. package/dist/scripts/ensureDist.d.ts +1 -0
  89. package/dist/scripts/ensureDist.js +296 -0
  90. package/dist/sync/syncApi.d.ts +3 -0
  91. package/{sync/syncApi.ts → dist/sync/syncApi.js} +35 -55
  92. package/dist/sync/syncCache.d.ts +2 -0
  93. package/{sync/syncCache.ts → dist/sync/syncCache.js} +1 -6
  94. package/dist/sync/syncDev.d.ts +6 -0
  95. package/{sync/syncDev.ts → dist/sync/syncDev.js} +29 -62
  96. package/dist/sync/syncMenu.d.ts +14 -0
  97. package/{sync/syncMenu.ts → dist/sync/syncMenu.js} +65 -125
  98. package/dist/sync/syncTable.d.ts +151 -0
  99. package/{sync/syncTable.ts → dist/sync/syncTable.js} +172 -379
  100. package/{types → dist/types}/api.d.ts +12 -51
  101. package/dist/types/api.js +4 -0
  102. package/{types → dist/types}/befly.d.ts +32 -227
  103. package/dist/types/befly.js +4 -0
  104. package/{types → dist/types}/cache.d.ts +7 -15
  105. package/dist/types/cache.js +4 -0
  106. package/dist/types/cipher.d.ts +27 -0
  107. package/dist/types/cipher.js +7 -0
  108. package/{types → dist/types}/common.d.ts +8 -33
  109. package/dist/types/common.js +5 -0
  110. package/{types → dist/types}/context.d.ts +3 -5
  111. package/dist/types/context.js +4 -0
  112. package/{types → dist/types}/crypto.d.ts +0 -3
  113. package/dist/types/crypto.js +4 -0
  114. package/dist/types/database.d.ts +138 -0
  115. package/dist/types/database.js +4 -0
  116. package/dist/types/hook.d.ts +17 -0
  117. package/dist/types/hook.js +6 -0
  118. package/dist/types/jwt.d.ts +75 -0
  119. package/dist/types/jwt.js +4 -0
  120. package/dist/types/logger.d.ts +59 -0
  121. package/dist/types/logger.js +6 -0
  122. package/dist/types/plugin.d.ts +16 -0
  123. package/dist/types/plugin.js +6 -0
  124. package/dist/types/redis.d.ts +71 -0
  125. package/dist/types/redis.js +4 -0
  126. package/{types/roleApisCache.ts → dist/types/roleApisCache.d.ts} +0 -2
  127. package/dist/types/roleApisCache.js +8 -0
  128. package/dist/types/sync.d.ts +92 -0
  129. package/dist/types/sync.js +4 -0
  130. package/dist/types/table.d.ts +34 -0
  131. package/dist/types/table.js +4 -0
  132. package/dist/types/validate.d.ts +67 -0
  133. package/dist/types/validate.js +4 -0
  134. package/dist/utils/calcPerfTime.d.ts +4 -0
  135. package/{utils/calcPerfTime.ts → dist/utils/calcPerfTime.js} +3 -3
  136. package/dist/utils/convertBigIntFields.d.ts +11 -0
  137. package/{utils/convertBigIntFields.ts → dist/utils/convertBigIntFields.js} +5 -9
  138. package/dist/utils/cors.d.ts +8 -0
  139. package/{utils/cors.ts → dist/utils/cors.js} +1 -3
  140. package/dist/utils/disableMenusGlob.d.ts +13 -0
  141. package/{utils/disableMenusGlob.ts → dist/utils/disableMenusGlob.js} +9 -29
  142. package/dist/utils/fieldClear.d.ts +11 -0
  143. package/{utils/fieldClear.ts → dist/utils/fieldClear.js} +15 -33
  144. package/dist/utils/getClientIp.d.ts +6 -0
  145. package/{utils/getClientIp.ts → dist/utils/getClientIp.js} +1 -7
  146. package/dist/utils/importDefault.d.ts +1 -0
  147. package/dist/utils/importDefault.js +29 -0
  148. package/dist/utils/isDirentDirectory.d.ts +2 -0
  149. package/{utils/isDirentDirectory.ts → dist/utils/isDirentDirectory.js} +3 -8
  150. package/dist/utils/loadMenuConfigs.d.ts +29 -0
  151. package/{utils/loadMenuConfigs.ts → dist/utils/loadMenuConfigs.js} +66 -52
  152. package/dist/utils/mergeAndConcat.d.ts +7 -0
  153. package/dist/utils/mergeAndConcat.js +72 -0
  154. package/dist/utils/processAtSymbol.d.ts +4 -0
  155. package/{utils/processFields.ts → dist/utils/processAtSymbol.js} +5 -9
  156. package/dist/utils/processInfo.d.ts +24 -0
  157. package/{utils/process.ts → dist/utils/processInfo.js} +2 -18
  158. package/dist/utils/response.d.ts +20 -0
  159. package/{utils/response.ts → dist/utils/response.js} +28 -49
  160. package/dist/utils/scanAddons.d.ts +17 -0
  161. package/{utils/scanAddons.ts → dist/utils/scanAddons.js} +7 -41
  162. package/dist/utils/scanConfig.d.ts +26 -0
  163. package/{utils/scanConfig.ts → dist/utils/scanConfig.js} +28 -66
  164. package/dist/utils/scanCoreBuiltins.d.ts +3 -0
  165. package/dist/utils/scanCoreBuiltins.js +65 -0
  166. package/dist/utils/scanFiles.d.ts +30 -0
  167. package/{utils/scanFiles.ts → dist/utils/scanFiles.js} +44 -71
  168. package/dist/utils/scanSources.d.ts +10 -0
  169. package/dist/utils/scanSources.js +46 -0
  170. package/dist/utils/sortModules.d.ts +28 -0
  171. package/{utils/sortModules.ts → dist/utils/sortModules.js} +26 -66
  172. package/dist/utils/util.d.ts +84 -0
  173. package/dist/utils/util.js +262 -0
  174. package/package.json +26 -34
  175. package/.gitignore +0 -0
  176. package/bunfig.toml +0 -3
  177. package/checks/checkHook.ts +0 -48
  178. package/checks/checkPlugin.ts +0 -48
  179. package/configs/presetRegexp.ts +0 -225
  180. package/docs/README.md +0 -98
  181. package/docs/api/api.md +0 -1921
  182. package/docs/guide/examples.md +0 -926
  183. package/docs/guide/quickstart.md +0 -354
  184. package/docs/hooks/auth.md +0 -38
  185. package/docs/hooks/cors.md +0 -28
  186. package/docs/hooks/hook.md +0 -838
  187. package/docs/hooks/parser.md +0 -19
  188. package/docs/hooks/rateLimit.md +0 -47
  189. package/docs/infra/redis.md +0 -628
  190. package/docs/plugins/cipher.md +0 -61
  191. package/docs/plugins/database.md +0 -189
  192. package/docs/plugins/plugin.md +0 -986
  193. package/docs/reference/addon.md +0 -510
  194. package/docs/reference/config.md +0 -573
  195. package/docs/reference/logger.md +0 -495
  196. package/docs/reference/sync.md +0 -478
  197. package/docs/reference/table.md +0 -763
  198. package/docs/reference/validator.md +0 -620
  199. package/lib/asyncContext.ts +0 -43
  200. package/lib/logger.ts +0 -811
  201. package/loader/loadHooks.ts +0 -51
  202. package/plugins/config.ts +0 -13
  203. package/plugins/jwt.ts +0 -15
  204. package/router/api.ts +0 -130
  205. package/tsconfig.json +0 -8
  206. package/types/database.d.ts +0 -541
  207. package/types/hook.d.ts +0 -25
  208. package/types/jwt.d.ts +0 -118
  209. package/types/logger.d.ts +0 -65
  210. package/types/plugin.d.ts +0 -19
  211. package/types/redis.d.ts +0 -83
  212. package/types/sync.d.ts +0 -398
  213. package/types/table.d.ts +0 -216
  214. package/types/validate.d.ts +0 -69
  215. package/utils/arrayKeysToCamel.ts +0 -18
  216. package/utils/configTypes.ts +0 -3
  217. package/utils/genShortId.ts +0 -12
  218. package/utils/importDefault.ts +0 -21
  219. package/utils/keysToCamel.ts +0 -22
  220. package/utils/keysToSnake.ts +0 -22
  221. package/utils/pickFields.ts +0 -19
  222. package/utils/scanSources.ts +0 -64
  223. package/utils/sqlLog.ts +0 -37
@@ -1,16 +1,9 @@
1
- // 类型导入
2
- import type { Hook } from "../types/hook.ts";
3
-
4
1
  // 外部依赖
5
- import { isPlainObject, isEmpty } from "es-toolkit/compat";
6
2
  import { XMLParser } from "fast-xml-parser";
7
-
3
+ import { ErrorResponse } from "../utils/response";
8
4
  // 相对导入
9
- import { pickFields } from "../utils/pickFields.ts";
10
- import { ErrorResponse } from "../utils/response.ts";
11
-
5
+ import { isEmpty, isPlainObject, pickFields } from "../utils/util";
12
6
  const xmlParser = new XMLParser();
13
-
14
7
  /**
15
8
  * 请求参数解析钩子
16
9
  * - GET 请求:解析 URL 查询参数
@@ -19,48 +12,52 @@ const xmlParser = new XMLParser();
19
12
  * - rawBody: true 时跳过解析,由 handler 自行处理原始请求
20
13
  */
21
14
  export default {
15
+ name: "parser",
16
+ enable: true,
22
17
  deps: ["auth"],
23
18
  handler: async (befly, ctx) => {
24
- if (!ctx.api) return;
25
-
19
+ if (!ctx.api)
20
+ return;
26
21
  // rawBody 模式:跳过解析,保留原始请求供 handler 自行处理
27
22
  // 适用于:微信回调、支付回调、webhook 等需要手动解密/验签的场景
28
23
  if (ctx.api.rawBody) {
29
24
  ctx.body = {};
30
25
  return;
31
26
  }
32
-
33
27
  // GET 请求:解析查询参数
34
28
  if (ctx.req.method === "GET") {
35
29
  const url = new URL(ctx.req.url);
36
30
  const params = Object.fromEntries(url.searchParams);
37
31
  if (isPlainObject(ctx.api.fields) && !isEmpty(ctx.api.fields)) {
38
32
  ctx.body = pickFields(params, Object.keys(ctx.api.fields));
39
- } else {
33
+ }
34
+ else {
40
35
  ctx.body = params;
41
36
  }
42
- } else if (ctx.req.method === "POST") {
37
+ }
38
+ else if (ctx.req.method === "POST") {
43
39
  // POST 请求:解析请求体
44
40
  const contentType = ctx.req.headers.get("content-type") || "";
45
41
  // 获取 URL 查询参数(POST 请求也可能带参数)
46
42
  const url = new URL(ctx.req.url);
47
43
  const queryParams = Object.fromEntries(url.searchParams);
48
-
49
44
  try {
50
45
  // JSON 格式
51
46
  if (contentType.includes("application/json")) {
52
- const body = (await ctx.req.json()) as Record<string, any>;
47
+ const body = (await ctx.req.json());
53
48
  // 合并 URL 参数和请求体(请求体优先)
54
49
  const merged = { ...queryParams, ...body };
55
50
  if (isPlainObject(ctx.api.fields) && !isEmpty(ctx.api.fields)) {
56
51
  ctx.body = pickFields(merged, Object.keys(ctx.api.fields));
57
- } else {
52
+ }
53
+ else {
58
54
  ctx.body = merged;
59
55
  }
60
- } else if (contentType.includes("application/xml") || contentType.includes("text/xml")) {
56
+ }
57
+ else if (contentType.includes("application/xml") || contentType.includes("text/xml")) {
61
58
  // XML 格式
62
59
  const text = await ctx.req.text();
63
- const parsed = xmlParser.parse(text) as Record<string, any>;
60
+ const parsed = xmlParser.parse(text);
64
61
  // 提取根节点内容(如 xml),使 body 扁平化
65
62
  const rootKey = Object.keys(parsed)[0];
66
63
  const body = rootKey && typeof parsed[rootKey] === "object" ? parsed[rootKey] : parsed;
@@ -68,39 +65,28 @@ export default {
68
65
  const merged = { ...queryParams, ...body };
69
66
  if (isPlainObject(ctx.api.fields) && !isEmpty(ctx.api.fields)) {
70
67
  ctx.body = pickFields(merged, Object.keys(ctx.api.fields));
71
- } else {
68
+ }
69
+ else {
72
70
  ctx.body = merged;
73
71
  }
74
- } else {
72
+ }
73
+ else {
75
74
  // 不支持的 Content-Type
76
- ctx.response = ErrorResponse(
77
- ctx,
78
- "无效的请求参数格式",
79
- 1,
80
- null,
81
- {
82
- location: "content-type",
83
- value: contentType
84
- },
85
- "parser"
86
- );
75
+ ctx.response = ErrorResponse(ctx, "无效的请求参数格式", 1, null, {
76
+ location: "content-type",
77
+ value: contentType
78
+ }, "parser");
87
79
  return;
88
80
  }
89
- } catch {
81
+ }
82
+ catch {
90
83
  // 解析失败:属于客户端输入错误,返回安全 detail(不回传异常栈/原始 body)
91
- ctx.response = ErrorResponse(
92
- ctx,
93
- "无效的请求参数格式",
94
- 1,
95
- null,
96
- {
97
- location: "body",
98
- reason: contentType.includes("application/json") ? "invalid_json" : contentType.includes("xml") ? "invalid_xml" : "invalid_body"
99
- },
100
- "parser"
101
- );
84
+ ctx.response = ErrorResponse(ctx, "无效的请求参数格式", 1, null, {
85
+ location: "body",
86
+ reason: contentType.includes("application/json") ? "invalid_json" : contentType.includes("xml") ? "invalid_xml" : "invalid_body"
87
+ }, "parser");
102
88
  return;
103
89
  }
104
90
  }
105
91
  }
106
- } satisfies Hook;
92
+ };
@@ -0,0 +1,14 @@
1
+ /**
2
+ * 权限检查钩子
3
+ * - 接口无需权限(auth=false):直接通过
4
+ * - 用户未登录:返回 401
5
+ * - 开发者角色(dev):最高权限,直接通过
6
+ * - 其他角色:检查 Redis 中的角色权限集合
7
+ */
8
+ declare const _default: {
9
+ name: string;
10
+ enable: true;
11
+ deps: string[];
12
+ handler: (befly: import("../types/befly").BeflyContext, ctx: import("../types/context").RequestContext<Record<string, any>>) => Promise<void>;
13
+ };
14
+ export default _default;
@@ -1,11 +1,7 @@
1
- // 类型导入
2
- import type { Hook } from "../types/hook.ts";
3
-
4
- import { CacheKeys } from "../lib/cacheKeys.ts";
5
- import { Logger } from "../lib/logger.ts";
1
+ import { CacheKeys } from "../lib/cacheKeys";
2
+ import { Logger } from "../lib/logger";
6
3
  // 相对导入
7
- import { ErrorResponse } from "../utils/response.ts";
8
-
4
+ import { ErrorResponse } from "../utils/response";
9
5
  /**
10
6
  * 权限检查钩子
11
7
  * - 接口无需权限(auth=false):直接通过
@@ -14,52 +10,47 @@ import { ErrorResponse } from "../utils/response.ts";
14
10
  * - 其他角色:检查 Redis 中的角色权限集合
15
11
  */
16
12
  export default {
13
+ name: "permission",
14
+ enable: true,
17
15
  deps: ["validator"],
18
16
  handler: async (befly, ctx) => {
19
- if (!ctx.api) return;
20
-
17
+ if (!ctx.api)
18
+ return;
21
19
  // 1. 接口无需权限
22
20
  if (ctx.api.auth === false) {
23
21
  return;
24
22
  }
25
-
26
23
  // 2. 用户未登录
27
24
  if (!ctx.user || !ctx.user.id) {
28
25
  ctx.response = ErrorResponse(ctx, "未登录", 1, null, null, "auth");
29
26
  return;
30
27
  }
31
-
32
28
  // 3. 开发者权限(最高权限)
33
29
  if (ctx.user.roleCode === "dev") {
34
30
  return;
35
31
  }
36
-
37
32
  // 4. 角色权限检查
38
33
  // apiPath 在 apiHandler 中已统一生成并写入 ctx.route
39
34
  const apiPath = ctx.route;
40
35
  const roleCode = ctx.user.roleCode;
41
-
42
36
  let hasPermission = false;
43
37
  if (roleCode && befly.redis) {
44
38
  try {
45
39
  // 极简方案:每个角色一个 Set,直接判断成员是否存在
46
40
  const roleApisKey = CacheKeys.roleApis(roleCode);
47
41
  hasPermission = await befly.redis.sismember(roleApisKey, apiPath);
48
- } catch (err: any) {
42
+ }
43
+ catch (err) {
49
44
  // Redis 异常:记录到 error 日志文件(不回传给客户端),并降级为拒绝访问
50
- Logger.error(
51
- {
52
- event: "hook_permission_redis_error",
53
- apiPath: apiPath,
54
- roleCode: roleCode,
55
- err: err
56
- },
57
- "hook permission redis error"
58
- );
45
+ Logger.error({
46
+ event: "hook_permission_redis_error",
47
+ apiPath: apiPath,
48
+ roleCode: roleCode,
49
+ err: err
50
+ }, "hook permission redis error");
59
51
  hasPermission = false;
60
52
  }
61
53
  }
62
-
63
54
  if (!hasPermission) {
64
55
  const apiNameLabel = typeof ctx.api.name === "string" && ctx.api.name.length > 0 ? ctx.api.name : null;
65
56
  const apiPathLabel = typeof apiPath === "string" && apiPath.length > 0 ? apiPath : null;
@@ -68,4 +59,4 @@ export default {
68
59
  return;
69
60
  }
70
61
  }
71
- } satisfies Hook;
62
+ };
@@ -0,0 +1,11 @@
1
+ /**
2
+ * 参数验证钩子
3
+ * 根据 API 定义的 fields 和 required 验证请求参数
4
+ */
5
+ declare const _default: {
6
+ name: string;
7
+ enable: true;
8
+ deps: string[];
9
+ handler: (befly: import("../types/befly").BeflyContext, ctx: import("../types/context").RequestContext<Record<string, any>>) => Promise<void>;
10
+ };
11
+ export default _default;
@@ -1,38 +1,33 @@
1
- // 类型导入
2
- import type { Hook } from "../types/hook.ts";
3
-
4
1
  // 相对导入
5
- import { Validator } from "../lib/validator.ts";
6
- import { ErrorResponse } from "../utils/response.ts";
7
-
2
+ import { Validator } from "../lib/validator";
3
+ import { ErrorResponse } from "../utils/response";
8
4
  /**
9
5
  * 参数验证钩子
10
6
  * 根据 API 定义的 fields 和 required 验证请求参数
11
7
  */
12
8
  export default {
9
+ name: "validator",
10
+ enable: true,
13
11
  deps: ["parser"],
14
12
  handler: async (befly, ctx) => {
15
- if (!ctx.api) return;
16
-
13
+ if (!ctx.api)
14
+ return;
17
15
  // 无需验证
18
16
  if (!ctx.api.fields) {
19
17
  return;
20
18
  }
21
-
22
19
  // 应用字段默认值
23
20
  for (const [field, fieldDef] of Object.entries(ctx.api.fields)) {
24
21
  // 字段未传值且定义了默认值时,应用默认值
25
- if (ctx.body[field] === undefined && (fieldDef as any)?.default !== undefined && (fieldDef as any)?.default !== null) {
26
- ctx.body[field] = (fieldDef as any).default;
22
+ if (ctx.body[field] === undefined && fieldDef?.default !== undefined && fieldDef?.default !== null) {
23
+ ctx.body[field] = fieldDef.default;
27
24
  }
28
25
  }
29
-
30
26
  // 验证参数
31
27
  const result = Validator.validate(ctx.body, ctx.api.fields, ctx.api.required || []);
32
-
33
28
  if (result.code !== 0) {
34
29
  ctx.response = ErrorResponse(ctx, result.firstError || "参数验证失败", 1, null, result.fieldErrors, "validator");
35
30
  return;
36
31
  }
37
32
  }
38
- } satisfies Hook;
33
+ };
@@ -0,0 +1,26 @@
1
+ /**
2
+ * Befly 框架主入口文件
3
+ * 提供简洁的框架接口,核心逻辑已提取到 loader 层
4
+ */
5
+ import type { BeflyContext } from "./types/befly";
6
+ /**
7
+ * Befly 框架核心类
8
+ * 职责:管理应用上下文和生命周期
9
+ */
10
+ export declare class Befly {
11
+ /** API 路由映射表 */
12
+ private apis;
13
+ /** 插件列表 */
14
+ private plugins;
15
+ /** 钩子列表 */
16
+ private hooks;
17
+ /** 应用上下文 */
18
+ context: Partial<BeflyContext>;
19
+ /** 配置引用(延迟加载) */
20
+ private config;
21
+ /**
22
+ * 启动完整的生命周期流程
23
+ * @returns HTTP 服务器实例
24
+ */
25
+ start(): Promise<ReturnType<typeof Bun.serve>>;
26
+ }
@@ -2,204 +2,165 @@
2
2
  * Befly 框架主入口文件
3
3
  * 提供简洁的框架接口,核心逻辑已提取到 loader 层
4
4
  */
5
-
6
- // ========== 类型导入 ==========
7
- import type { ApiRoute } from "./types/api.ts";
8
- import type { BeflyContext, BeflyOptions } from "./types/befly.ts";
9
- import type { Hook } from "./types/hook.ts";
10
- import type { Plugin } from "./types/plugin.ts";
11
-
12
- import { checkApi } from "./checks/checkApi.ts";
13
- import { checkHook } from "./checks/checkHook.ts";
14
- import { checkMenu } from "./checks/checkMenu.ts";
15
- import { checkPlugin } from "./checks/checkPlugin.ts";
16
- import { checkTable } from "./checks/checkTable.ts";
5
+ import { loadBeflyConfig } from "./befly.config";
6
+ import { checkApi } from "./checks/checkApi";
7
+ import { checkHook } from "./checks/checkHook";
8
+ import { checkMenu } from "./checks/checkMenu";
9
+ import { checkPlugin } from "./checks/checkPlugin";
10
+ import { checkTable } from "./checks/checkTable";
17
11
  // ========== 相对导入(项目内部文件) ==========
18
12
  // 基础设施
19
- import { Connect } from "./lib/connect.ts";
20
- import { Logger } from "./lib/logger.ts";
13
+ import { Connect } from "./lib/connect";
14
+ import { Logger } from "./lib/logger";
21
15
  // 加载器
22
- import { loadApis } from "./loader/loadApis.ts";
23
- import { loadHooks } from "./loader/loadHooks.ts";
24
- import { loadPlugins } from "./loader/loadPlugins.ts";
16
+ import { loadApis } from "./loader/loadApis";
17
+ import { loadHooks } from "./loader/loadHooks";
18
+ import { loadPlugins } from "./loader/loadPlugins";
25
19
  // 路由处理
26
- import { apiHandler } from "./router/api.ts";
27
- import { staticHandler } from "./router/static.ts";
20
+ import { apiHandler } from "./router/api";
21
+ import { staticHandler } from "./router/static";
28
22
  // 同步
29
- import { syncApi } from "./sync/syncApi.ts";
30
- import { syncCache } from "./sync/syncCache.ts";
31
- import { syncDev } from "./sync/syncDev.ts";
32
- import { syncMenu } from "./sync/syncMenu.ts";
33
- import { syncTable } from "./sync/syncTable.ts";
23
+ import { syncApi } from "./sync/syncApi";
24
+ import { syncCache } from "./sync/syncCache";
25
+ import { syncDev } from "./sync/syncDev";
26
+ import { syncMenu } from "./sync/syncMenu";
27
+ import { syncTable } from "./sync/syncTable";
34
28
  // 工具
35
- import { calcPerfTime } from "./utils/calcPerfTime.ts";
36
- import { getProcessRole } from "./utils/process.ts";
37
- import { scanSources } from "./utils/scanSources.ts";
38
-
29
+ import { calcPerfTime } from "./utils/calcPerfTime";
30
+ import { getProcessRole } from "./utils/processInfo";
31
+ import { scanSources } from "./utils/scanSources";
39
32
  /**
40
33
  * Befly 框架核心类
41
34
  * 职责:管理应用上下文和生命周期
42
35
  */
43
36
  export class Befly {
44
37
  /** API 路由映射表 */
45
- private apis: Map<string, ApiRoute> = new Map();
46
-
38
+ apis = new Map();
47
39
  /** 插件列表 */
48
- private plugins: Plugin[] = [];
49
-
40
+ plugins = [];
50
41
  /** 钩子列表 */
51
- private hooks: Hook[] = [];
52
-
42
+ hooks = [];
53
43
  /** 应用上下文 */
54
- public context: Partial<BeflyContext> = {};
55
-
44
+ context = {};
56
45
  /** 配置引用(延迟加载) */
57
- private config: BeflyOptions | null = null;
58
-
46
+ config = null;
59
47
  /**
60
48
  * 启动完整的生命周期流程
61
49
  * @returns HTTP 服务器实例
62
50
  */
63
- public async start(): Promise<ReturnType<typeof Bun.serve>> {
51
+ async start() {
64
52
  try {
65
53
  const serverStartTime = Bun.nanoseconds();
66
-
67
- // 0. 延迟加载配置(避免循环依赖)
68
- const { loadBeflyConfig } = await import("./befly.config.ts");
54
+ // 0. 加载配置
69
55
  this.config = await loadBeflyConfig();
70
-
71
56
  // 将配置注入到 ctx,供插件/Hook/sync 等按需读取
72
57
  this.context.config = this.config;
73
-
74
58
  const { apis, tables, plugins, hooks, addons } = await scanSources();
75
-
76
59
  // 让后续 syncMenu 能拿到 addon 的 views 路径等信息
77
60
  this.context.addons = addons;
78
-
79
61
  await checkApi(apis);
80
62
  await checkTable(tables);
81
63
  await checkPlugin(plugins);
82
64
  await checkHook(hooks);
83
65
  const checkedMenus = await checkMenu(addons, { disableMenus: this.config.disableMenus || [] });
84
-
85
66
  // 1. 启动期建立基础连接(SQL + Redis)
86
67
  // 说明:连接职责收敛到启动期单点;插件只消费已连接实例(Connect.getSql/getRedis)。
87
68
  await Connect.connect({
88
69
  db: this.config.db || {},
89
70
  redis: this.config.redis || {}
90
71
  });
91
-
92
72
  // 2. 加载插件
93
- this.plugins = await loadPlugins(plugins as any, this.context as BeflyContext, this.config!.disablePlugins || []);
94
-
73
+ this.plugins = await loadPlugins(plugins, this.context);
95
74
  // 启动期依赖完整性检查:避免 sync 阶段出现 undefined 调用
96
75
  // 注意:这里不做兼容别名(例如 dbHelper=db),要求上下文必须注入标准字段。
97
- if (!(this.context as any).redis) {
76
+ if (!this.context.redis) {
98
77
  throw new Error("启动失败:ctx.redis 未初始化(Redis 插件未加载或注入失败)");
99
78
  }
100
- if (!(this.context as any).db) {
79
+ if (!this.context.db) {
101
80
  throw new Error("启动失败:ctx.db 未初始化(Db 插件未加载或注入失败)");
102
81
  }
103
- if (!(this.context as any).cache) {
82
+ if (!this.context.cache) {
104
83
  throw new Error("启动失败:ctx.cache 未初始化(cache 插件未加载或注入失败)");
105
84
  }
106
-
107
85
  // 5. 自动同步 (仅主进程执行,避免集群模式下重复执行)
108
- await syncTable(this.context as BeflyContext, tables);
109
- await syncApi(this.context as BeflyContext, apis as any);
110
-
111
- await syncMenu(this.context as BeflyContext, checkedMenus);
112
- await syncDev(this.context as BeflyContext, { devEmail: this.config.devEmail, devPassword: this.config.devPassword });
113
-
86
+ await syncTable(this.context, tables);
87
+ await syncApi(this.context, apis);
88
+ await syncMenu(this.context, checkedMenus);
89
+ await syncDev(this.context, { devEmail: this.config.devEmail, devPassword: this.config.devPassword });
114
90
  // 缓存同步:统一在所有同步完成后执行(cacheApis + cacheMenus + rebuildRoleApiPermissions)
115
- await syncCache(this.context as BeflyContext);
116
-
91
+ await syncCache(this.context);
117
92
  // 3. 加载钩子
118
- this.hooks = await loadHooks(hooks as any, this.config!.disableHooks || []);
119
-
93
+ this.hooks = await loadHooks(hooks);
120
94
  // 4. 加载所有 API
121
- this.apis = await loadApis(apis as any);
122
-
95
+ this.apis = await loadApis(apis);
123
96
  // 6. 启动 HTTP服务器
124
- const apiFetch = apiHandler(this.apis, this.hooks, this.context as BeflyContext);
125
- const staticFetch = staticHandler(this.config!.cors);
126
-
97
+ const apiFetch = apiHandler(this.apis, this.hooks, this.context);
98
+ const staticFetch = staticHandler(this.config.cors);
127
99
  const server = Bun.serve({
128
- port: this.config!.appPort,
129
- hostname: this.config!.appHost,
100
+ port: this.config.appPort,
101
+ hostname: this.config.appHost,
130
102
  // 开发模式下启用详细错误信息
131
- development: this.config!.nodeEnv === "development",
103
+ development: this.config.nodeEnv === "development",
132
104
  // 空闲连接超时时间(秒),防止恶意连接占用资源
133
105
  idleTimeout: 30,
134
106
  fetch: async (req, bunServer) => {
135
107
  const url = new URL(req.url);
136
-
137
108
  if (url.pathname === "/") {
138
- return Response.json({ code: 0, msg: `${this.config!.appName} 接口服务已启动` });
109
+ return Response.json({ code: 0, msg: `${this.config.appName} 接口服务已启动` });
139
110
  }
140
-
141
111
  if (url.pathname.startsWith("/api/")) {
142
112
  return apiFetch(req, bunServer);
143
113
  }
144
-
145
114
  return staticFetch(req);
146
115
  },
147
- error: (error: Error) => {
116
+ error: (error) => {
148
117
  Logger.error({ err: error }, "服务启动时发生错误");
149
118
  // 开发模式下返回详细错误信息
150
- if (this.config!.nodeEnv === "development") {
151
- return Response.json(
152
- {
153
- code: 1,
154
- msg: "内部服务器错误",
155
- error: error.message,
156
- stack: error.stack
157
- },
158
- { status: 200 }
159
- );
119
+ if (this.config.nodeEnv === "development") {
120
+ return Response.json({
121
+ code: 1,
122
+ msg: "内部服务器错误",
123
+ error: error.message,
124
+ stack: error.stack
125
+ }, { status: 200 });
160
126
  }
161
127
  return Response.json({ code: 1, msg: "内部服务器错误" }, { status: 200 });
162
128
  }
163
129
  });
164
-
165
130
  const finalStartupTime = calcPerfTime(serverStartTime);
166
131
  const processRole = getProcessRole();
167
132
  const roleLabel = processRole.role === "primary" ? "主进程" : `工作进程 #${processRole.instanceId}`;
168
133
  const envLabel = processRole.env === "standalone" ? "" : ` [${processRole.env}]`;
169
-
170
- Logger.info(`${this.config!.appName} 启动成功! (${roleLabel}${envLabel})`);
134
+ Logger.info(`${this.config.appName} 启动成功! (${roleLabel}${envLabel})`);
171
135
  Logger.info(`服务器启动耗时: ${finalStartupTime}`);
172
136
  Logger.info(`服务器监听地址: ${server.url}`);
173
-
174
137
  // 7. 注册优雅关闭处理
175
- const gracefulShutdown = async (signal: string) => {
138
+ const gracefulShutdown = async (signal) => {
176
139
  Logger.info(`收到 ${signal} 信号,开始优雅关闭...`);
177
-
178
140
  // 优雅停止(等待进行中的请求完成)
179
141
  try {
180
142
  await server.stop();
181
143
  Logger.info("HTTP 服务器已停止");
182
- } catch (error: any) {
144
+ }
145
+ catch (error) {
183
146
  Logger.error({ err: error }, "停止 HTTP 服务器时出错");
184
147
  }
185
-
186
148
  // 关闭数据库连接
187
149
  try {
188
150
  await Connect.disconnect();
189
151
  Logger.info("数据库连接已关闭");
190
- } catch (error: any) {
152
+ }
153
+ catch (error) {
191
154
  Logger.error({ err: error }, "关闭数据库连接时出错");
192
155
  }
193
-
194
156
  Logger.info("服务器已优雅关闭");
195
157
  process.exit(0);
196
158
  };
197
-
198
159
  process.on("SIGTERM", () => gracefulShutdown("SIGTERM"));
199
160
  process.on("SIGINT", () => gracefulShutdown("SIGINT"));
200
-
201
161
  return server;
202
- } catch (error: any) {
162
+ }
163
+ catch (error) {
203
164
  Logger.error({ err: error }, "项目启动失败");
204
165
  process.exit(1);
205
166
  }
@@ -0,0 +1,21 @@
1
+ /**
2
+ * AsyncLocalStorage 请求级上下文
3
+ *
4
+ * 目标:
5
+ * - 给 Logger/慢查询日志提供 requestId/route/userId 等元信息
6
+ * - 避免在深层工具函数中层层传参
7
+ */
8
+ export type RequestMeta = {
9
+ requestId: string;
10
+ method: string;
11
+ route: string;
12
+ ip: string;
13
+ userId?: string | number | null;
14
+ roleCode?: string | null;
15
+ nickname?: string | null;
16
+ roleType?: string | null;
17
+ now: number;
18
+ };
19
+ export declare function withCtx<T>(meta: RequestMeta, fn: () => T): T;
20
+ export declare function getCtx(): RequestMeta | null;
21
+ export declare function setCtxUser(userId: string | number | null | undefined, roleCode?: string | null | undefined, nickname?: string | null | undefined, roleType?: string | null | undefined): void;
@@ -0,0 +1,27 @@
1
+ /**
2
+ * AsyncLocalStorage 请求级上下文
3
+ *
4
+ * 目标:
5
+ * - 给 Logger/慢查询日志提供 requestId/route/userId 等元信息
6
+ * - 避免在深层工具函数中层层传参
7
+ */
8
+ import { AsyncLocalStorage } from "node:async_hooks";
9
+ const storage = new AsyncLocalStorage();
10
+ export function withCtx(meta, fn) {
11
+ return storage.run(meta, fn);
12
+ }
13
+ export function getCtx() {
14
+ const store = storage.getStore();
15
+ if (!store)
16
+ return null;
17
+ return store;
18
+ }
19
+ export function setCtxUser(userId, roleCode, nickname, roleType) {
20
+ const store = storage.getStore();
21
+ if (!store)
22
+ return;
23
+ store.userId = userId;
24
+ store.roleCode = roleCode;
25
+ store.nickname = nickname;
26
+ store.roleType = roleType;
27
+ }