befly 3.74.3 → 3.75.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 (66) hide show
  1. package/apis/dashboard/systemResources.js +3 -1
  2. package/apis/dict/_dict.js +1 -1
  3. package/apis/role/_role.js +2 -2
  4. package/apis/tongJi/dailyStats.js +2 -2
  5. package/apis/tongJi/dailyStatsDistribution.js +3 -3
  6. package/apis/tongJi/errorList.js +2 -2
  7. package/apis/tongJi/errorStats.js +6 -6
  8. package/checks/api.js +14 -8
  9. package/checks/config.js +4 -3
  10. package/checks/items.js +65 -0
  11. package/checks/menu.js +4 -3
  12. package/checks/table.js +7 -7
  13. package/configs/beflyConfig.json +5 -10
  14. package/configs/constConfig.js +44 -3
  15. package/hooks/auth.js +3 -2
  16. package/hooks/parser.js +15 -4
  17. package/hooks/permission.js +3 -2
  18. package/hooks/rateLimit.js +16 -3
  19. package/hooks/validator.js +2 -1
  20. package/index.js +72 -28
  21. package/lib/cacheHelper.js +83 -229
  22. package/lib/connect.js +3 -13
  23. package/lib/dbHelper.js +57 -142
  24. package/lib/dbParse.js +65 -248
  25. package/lib/dbUtil.js +19 -63
  26. package/lib/logger.js +45 -22
  27. package/lib/redisHelper.js +186 -420
  28. package/lib/smtpText.js +7 -18
  29. package/lib/sqlBuilder.js +22 -51
  30. package/lib/xmlParse.js +15 -59
  31. package/package.json +1 -1
  32. package/plugins/cache.js +2 -3
  33. package/plugins/config.js +3 -1
  34. package/plugins/cron.js +2 -2
  35. package/plugins/email.js +3 -1
  36. package/plugins/logger.js +2 -2
  37. package/plugins/mysql.js +5 -10
  38. package/plugins/redis.js +4 -11
  39. package/plugins/tool.js +3 -3
  40. package/plugins/xmlParse.js +3 -1
  41. package/router/api.js +2 -6
  42. package/router/static.js +9 -10
  43. package/schemas/config.js +4 -4
  44. package/scripts/syncDb/context.js +4 -7
  45. package/scripts/syncDb/diff.js +2 -1
  46. package/scripts/syncDb/index.js +2 -6
  47. package/sync/api.js +7 -8
  48. package/sync/cache.js +3 -1
  49. package/sync/dev.js +28 -65
  50. package/sync/menu.js +5 -6
  51. package/sync/syncUtil.js +25 -0
  52. package/utils/calcPerfTime.js +2 -3
  53. package/utils/crypto.js +0 -40
  54. package/utils/deepMerge.js +2 -6
  55. package/utils/error.js +64 -0
  56. package/utils/fieldClear.js +13 -56
  57. package/utils/importDefault.js +11 -12
  58. package/utils/is.js +3 -180
  59. package/utils/loggerUtils.js +3 -1
  60. package/utils/response.js +12 -8
  61. package/utils/scanFiles.js +2 -53
  62. package/utils/scanSources.js +9 -0
  63. package/utils/util.js +25 -222
  64. package/checks/hook.js +0 -39
  65. package/checks/plugin.js +0 -39
  66. package/utils/processInfo.js +0 -39
package/index.js CHANGED
@@ -6,16 +6,17 @@
6
6
  // 检查
7
7
  import { checkApi } from "#befly/checks/api.js";
8
8
  import { checkConfig } from "#befly/checks/config.js";
9
- import { checkHook } from "#befly/checks/hook.js";
9
+ import { checkHook, checkPlugin } from "#befly/checks/items.js";
10
10
  import { checkMenu } from "#befly/checks/menu.js";
11
- import { checkPlugin } from "#befly/checks/plugin.js";
12
11
  import { checkTable } from "#befly/checks/table.js";
13
12
  // 配置
14
13
  import beflyConfig from "#befly/configs/beflyConfig.json";
15
14
  import beflyMenus from "#befly/configs/beflyMenus.json";
15
+ import { BEFLY_ADMIN_TABLE, BEFLY_API_TABLE, BEFLY_MENU_TABLE, BEFLY_ROLE_TABLE } from "#befly/configs/constConfig.js";
16
16
  // ========== 相对导入(项目内部文件) ==========
17
17
  // 基础设施
18
18
  import { Connect } from "#befly/lib/connect.js";
19
+ // oxlint-disable-next-line unicorn/prefer-export-from -- Logger 同时被本文件大量使用,不能合并为 re-export
19
20
  import { Logger } from "#befly/lib/logger.js";
20
21
  // 路由处理
21
22
  import { apiHandler } from "#befly/router/api.js";
@@ -27,14 +28,20 @@ import { syncDev } from "#befly/sync/dev.js";
27
28
  import { syncMenu } from "#befly/sync/menu.js";
28
29
  // 工具
29
30
  import { calcPerfTime } from "#befly/utils/calcPerfTime.js";
31
+ import { formatYmdHms } from "#befly/utils/datetime.js";
30
32
  import { deepMerge } from "#befly/utils/deepMerge.js";
33
+ import { createError } from "#befly/utils/error.js";
31
34
  import { isPrimaryProcess } from "#befly/utils/is.js";
32
35
  import { scanSources } from "#befly/utils/scanSources.js";
36
+ import { getRunMode, waitFor } from "#befly/utils/util.js";
33
37
 
34
38
  export { syncDb } from "#befly/scripts/syncDb/index.js";
35
39
  export { xmlParse } from "#befly/lib/xmlParse.js";
36
40
  export { Logger };
37
41
 
42
+ // 主进程同步完成的 Redis 就绪标记(非主进程等待此标记后再对外服务)
43
+ const SYNC_READY_KEY = "befly:syncReady";
44
+
38
45
  function prefixMenuPaths(menus, prefix) {
39
46
  return menus.map((menu) => {
40
47
  const nextMenu = {
@@ -58,8 +65,7 @@ function prefixMenuPaths(menus, prefix) {
58
65
  async function syncReady(ctx) {
59
66
  const missingCtxKeys = ["ctx.redis", "ctx.mysql", "ctx.cache"].filter((key) => !ctx[key.slice(4)]);
60
67
  if (missingCtxKeys.length > 0) {
61
- throw new Error(`启动失败:${missingCtxKeys.join("、")} 未初始化`, {
62
- cause: null,
68
+ throw createError(`启动失败:${missingCtxKeys.join("、")} 未初始化`, {
63
69
  code: "runtime",
64
70
  subsystem: "sync",
65
71
  operation: "ctxCheck",
@@ -67,7 +73,7 @@ async function syncReady(ctx) {
67
73
  });
68
74
  }
69
75
 
70
- const requiredTables = ["beflyApi", "beflyMenu", "beflyAdmin", "beflyRole"];
76
+ const requiredTables = [BEFLY_API_TABLE, BEFLY_MENU_TABLE, BEFLY_ADMIN_TABLE, BEFLY_ROLE_TABLE];
71
77
  const missingTables = [];
72
78
 
73
79
  for (const table of requiredTables) {
@@ -77,8 +83,7 @@ async function syncReady(ctx) {
77
83
  }
78
84
 
79
85
  if (missingTables.length > 0) {
80
- throw new Error(`同步依赖表缺失: ${missingTables.join("、")}`, {
81
- cause: null,
86
+ throw createError(`同步依赖表缺失: ${missingTables.join("、")}`, {
82
87
  code: "runtime",
83
88
  subsystem: "sync",
84
89
  operation: "tableExists",
@@ -93,13 +98,13 @@ export async function createBefly(config = {}, menus = []) {
93
98
  const mergedMenus = deepMerge(prefixMenuPaths(beflyMenus, "core"), menus);
94
99
 
95
100
  Logger.configure({
96
- runtimeEnv: mergedConfig.runMode,
101
+ runtimeEnv: getRunMode(),
97
102
  ...mergedConfig.logger
98
103
  });
99
104
  Logger.info(`启动 合并配置 耗时 ${calcPerfTime(mergeStartTime)}`);
100
105
 
101
106
  let stageStartTime = Bun.nanoseconds();
102
- const configHasError = await checkConfig(mergedConfig);
107
+ const configErrors = await checkConfig(mergedConfig);
103
108
  Logger.info(`启动 检查配置 耗时 ${calcPerfTime(stageStartTime)}`);
104
109
 
105
110
  stageStartTime = Bun.nanoseconds();
@@ -107,34 +112,48 @@ export async function createBefly(config = {}, menus = []) {
107
112
  Logger.info(`启动 扫描源码 耗时 ${calcPerfTime(stageStartTime)}`);
108
113
 
109
114
  stageStartTime = Bun.nanoseconds();
110
- const apiHasError = await checkApi(apis);
115
+ const apiErrors = await checkApi(apis);
111
116
  Logger.info(`启动 检查接口 耗时 ${calcPerfTime(stageStartTime)}`);
112
117
 
113
118
  stageStartTime = Bun.nanoseconds();
114
- const tableHasError = await checkTable(tables);
119
+ const tableErrors = await checkTable(tables);
115
120
  Logger.info(`启动 检查表 耗时 ${calcPerfTime(stageStartTime)}`);
116
121
 
117
122
  stageStartTime = Bun.nanoseconds();
118
- const pluginHasError = await checkPlugin(plugins);
123
+ const pluginErrors = await checkPlugin(plugins);
119
124
  Logger.info(`启动 检查插件 耗时 ${calcPerfTime(stageStartTime)}`);
120
125
 
121
126
  stageStartTime = Bun.nanoseconds();
122
- const hookHasError = await checkHook(hooks);
127
+ const hookErrors = await checkHook(hooks);
123
128
  Logger.info(`启动 检查钩子 耗时 ${calcPerfTime(stageStartTime)}`);
124
129
 
125
130
  stageStartTime = Bun.nanoseconds();
126
- const menuHasError = await checkMenu(mergedMenus);
131
+ const menuErrors = await checkMenu(mergedMenus);
127
132
  Logger.info(`启动 检查菜单 耗时 ${calcPerfTime(stageStartTime)}`);
128
133
 
129
- if (configHasError || apiHasError || tableHasError || pluginHasError || hookHasError || menuHasError) {
130
- throw new Error("检查失败:存在配置/结构问题", {
131
- cause: null,
134
+ // 汇总各检查明细,报错可直接定位到具体文件/路径/配置/字段
135
+ const failedChecks = [
136
+ { check: "配置", errors: configErrors },
137
+ { check: "接口", errors: apiErrors },
138
+ { check: "表结构", errors: tableErrors },
139
+ { check: "插件", errors: pluginErrors },
140
+ { check: "钩子", errors: hookErrors },
141
+ { check: "菜单", errors: menuErrors }
142
+ ].filter((entry) => entry.errors.length > 0);
143
+
144
+ if (failedChecks.length > 0) {
145
+ throw createError("检查失败:存在配置/结构问题", {
132
146
  code: "policy",
133
147
  subsystem: "checks",
134
- operation: "checkAll"
148
+ operation: "checkAll",
149
+ errors: failedChecks
135
150
  });
136
151
  }
137
152
 
153
+ // 运行模式以环境为准(NODE_ENV,线上部署注入 production;本地开发默认 development),
154
+ // 覆盖配置值,保证 befly.config.runMode 与运行时判断一致
155
+ mergedConfig.runMode = getRunMode();
156
+
138
157
  return new Befly({
139
158
  config: mergedConfig,
140
159
  tables: tables,
@@ -168,12 +187,7 @@ export class Befly {
168
187
  return;
169
188
  }
170
189
 
171
- throw new Error("请使用 createBefly(...) 创建实例后再调用 start", {
172
- cause: null,
173
- code: "policy",
174
- subsystem: "start",
175
- operation: "factoryGuard"
176
- });
190
+ throw createError("请使用 createBefly(...) 创建实例后再调用 start", { code: "policy", subsystem: "start", operation: "factoryGuard" });
177
191
  }
178
192
 
179
193
  /**
@@ -207,7 +221,7 @@ export class Befly {
207
221
  await syncReady(this.context);
208
222
  Logger.info(`启动 同步就绪 耗时 ${calcPerfTime(stageStartTime)}`);
209
223
 
210
- // 自动同步(PM2 cluster:主进程执行,其它进程等待同步完成)
224
+ // 自动同步(bm2 多实例:主进程执行同步并写就绪标记,其它进程等待同步完成后再对外服务)
211
225
  if (isPrimaryProcess()) {
212
226
  stageStartTime = Bun.nanoseconds();
213
227
  await syncApi(this.context, this.apis);
@@ -224,6 +238,20 @@ export class Befly {
224
238
  stageStartTime = Bun.nanoseconds();
225
239
  await syncCache(this.context);
226
240
  Logger.info(`启动 同步缓存 耗时 ${calcPerfTime(stageStartTime)}`);
241
+
242
+ // 同步完成:写入就绪标记(TTL 防崩溃残留),非主进程据此放行
243
+ await this.context.redis.setString(SYNC_READY_KEY, String(Date.now()), 300);
244
+ } else {
245
+ // 非主进程:在监听端口前等待主进程本次同步完成(时间戳判断,同机时钟一致)
246
+ const startAt = Date.now();
247
+ await waitFor({
248
+ label: "主进程同步完成",
249
+ check: async () => {
250
+ const ready = await this.context.redis.getString(SYNC_READY_KEY);
251
+ return ready !== null && Number(ready) > startAt;
252
+ }
253
+ });
254
+ Logger.info("启动 等待主进程同步完成 完成");
227
255
  }
228
256
 
229
257
  // 加载钩子
@@ -241,7 +269,7 @@ export class Befly {
241
269
  stageStartTime = Bun.nanoseconds();
242
270
  const server = Bun.serve({
243
271
  port: this.context.config.appPort || 3000,
244
- hostname: this.context.config.appHost || "0.0.0.0",
272
+ hostname: this.context.config.appHost || "127.0.0.1",
245
273
  // 开发模式下启用详细错误信息
246
274
  development: this.context.config.runMode === "development",
247
275
  // 空闲连接超时时间(秒),防止恶意连接占用资源
@@ -256,6 +284,22 @@ export class Befly {
256
284
  return staticFetch(req);
257
285
  }
258
286
 
287
+ // 根路径返回服务基本信息
288
+ if (url.pathname === "/") {
289
+ return Response.json(
290
+ {
291
+ code: 0,
292
+ msg: "success",
293
+ data: {
294
+ name: this.context.config.appName,
295
+ mode: this.context.config.runMode,
296
+ time: formatYmdHms(Date.now())
297
+ }
298
+ },
299
+ { status: 200 }
300
+ );
301
+ }
302
+
259
303
  return Response.json({ code: 1, msg: "路径不存在" }, { status: 200 });
260
304
  },
261
305
  error: (error) => {
@@ -302,7 +346,7 @@ export class Befly {
302
346
  // - 作为库代码,也不应该强行终止宿主进程
303
347
 
304
348
  // 注意:测试要求 start() 失败时必须 Logger.error 一次,所以这里始终调用 error。
305
- Logger.error("项目启动失败", error, null, false);
349
+ Logger.error("项目启动失败", error);
306
350
 
307
351
  // 尽力把错误日志落盘/输出后再把异常抛给上层。
308
352
  try {
@@ -312,7 +356,7 @@ export class Befly {
312
356
  // ignore
313
357
  }
314
358
 
315
- throw new Error("运行时错误", {
359
+ throw createError("运行时错误", {
316
360
  cause: error,
317
361
  code: error?.code || "runtime",
318
362
  subsystem: "start",