befly 3.9.38 → 3.9.40

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 (155) hide show
  1. package/README.md +37 -38
  2. package/befly.config.ts +62 -40
  3. package/checks/checkApi.ts +16 -16
  4. package/checks/checkApp.ts +19 -25
  5. package/checks/checkTable.ts +42 -42
  6. package/docs/README.md +42 -35
  7. package/docs/{api.md → api/api.md} +223 -231
  8. package/docs/cipher.md +71 -69
  9. package/docs/database.md +143 -141
  10. package/docs/{examples.md → guide/examples.md} +181 -181
  11. package/docs/guide/quickstart.md +331 -0
  12. package/docs/hooks/auth.md +38 -0
  13. package/docs/hooks/cors.md +28 -0
  14. package/docs/{hook.md → hooks/hook.md} +140 -57
  15. package/docs/hooks/parser.md +19 -0
  16. package/docs/hooks/rateLimit.md +47 -0
  17. package/docs/{redis.md → infra/redis.md} +84 -93
  18. package/docs/plugins/cipher.md +61 -0
  19. package/docs/plugins/database.md +128 -0
  20. package/docs/{plugin.md → plugins/plugin.md} +83 -81
  21. package/docs/quickstart.md +26 -26
  22. package/docs/{addon.md → reference/addon.md} +46 -46
  23. package/docs/{config.md → reference/config.md} +32 -80
  24. package/docs/{logger.md → reference/logger.md} +52 -52
  25. package/docs/{sync.md → reference/sync.md} +32 -35
  26. package/docs/{table.md → reference/table.md} +1 -1
  27. package/docs/{validator.md → reference/validator.md} +57 -57
  28. package/hooks/auth.ts +8 -4
  29. package/hooks/cors.ts +13 -13
  30. package/hooks/parser.ts +37 -17
  31. package/hooks/permission.ts +26 -14
  32. package/hooks/rateLimit.ts +276 -0
  33. package/hooks/validator.ts +8 -8
  34. package/lib/asyncContext.ts +43 -0
  35. package/lib/cacheHelper.ts +212 -77
  36. package/lib/cacheKeys.ts +38 -0
  37. package/lib/cipher.ts +30 -30
  38. package/lib/connect.ts +28 -28
  39. package/lib/dbHelper.ts +183 -102
  40. package/lib/jwt.ts +16 -16
  41. package/lib/logger.ts +610 -19
  42. package/lib/redisHelper.ts +185 -44
  43. package/lib/sqlBuilder.ts +90 -91
  44. package/lib/validator.ts +59 -39
  45. package/loader/loadApis.ts +48 -44
  46. package/loader/loadHooks.ts +40 -14
  47. package/loader/loadPlugins.ts +16 -17
  48. package/main.ts +57 -47
  49. package/package.json +47 -45
  50. package/paths.ts +15 -14
  51. package/plugins/cache.ts +5 -4
  52. package/plugins/cipher.ts +3 -3
  53. package/plugins/config.ts +2 -2
  54. package/plugins/db.ts +9 -9
  55. package/plugins/jwt.ts +3 -3
  56. package/plugins/logger.ts +8 -12
  57. package/plugins/redis.ts +8 -8
  58. package/plugins/tool.ts +6 -6
  59. package/router/api.ts +85 -56
  60. package/router/static.ts +12 -12
  61. package/sync/syncAll.ts +12 -12
  62. package/sync/syncApi.ts +55 -52
  63. package/sync/syncDb/apply.ts +20 -19
  64. package/sync/syncDb/constants.ts +25 -23
  65. package/sync/syncDb/ddl.ts +35 -36
  66. package/sync/syncDb/helpers.ts +6 -9
  67. package/sync/syncDb/schema.ts +10 -9
  68. package/sync/syncDb/sqlite.ts +7 -8
  69. package/sync/syncDb/table.ts +37 -35
  70. package/sync/syncDb/tableCreate.ts +21 -20
  71. package/sync/syncDb/types.ts +23 -20
  72. package/sync/syncDb/version.ts +10 -10
  73. package/sync/syncDb.ts +43 -36
  74. package/sync/syncDev.ts +74 -65
  75. package/sync/syncMenu.ts +190 -55
  76. package/tests/api-integration-array-number.test.ts +282 -0
  77. package/tests/befly-config-env.test.ts +78 -0
  78. package/tests/cacheHelper.test.ts +135 -104
  79. package/tests/cacheKeys.test.ts +41 -0
  80. package/tests/cipher.test.ts +90 -89
  81. package/tests/dbHelper-advanced.test.ts +140 -134
  82. package/tests/dbHelper-all-array-types.test.ts +316 -0
  83. package/tests/dbHelper-array-serialization.test.ts +258 -0
  84. package/tests/dbHelper-columns.test.ts +56 -55
  85. package/tests/dbHelper-execute.test.ts +45 -44
  86. package/tests/dbHelper-joins.test.ts +124 -119
  87. package/tests/fields-redis-cache.test.ts +29 -27
  88. package/tests/fields-validate.test.ts +38 -38
  89. package/tests/getClientIp.test.ts +54 -0
  90. package/tests/integration.test.ts +69 -67
  91. package/tests/jwt.test.ts +27 -26
  92. package/tests/logger.test.ts +267 -34
  93. package/tests/rateLimit-hook.test.ts +477 -0
  94. package/tests/redisHelper.test.ts +187 -188
  95. package/tests/redisKeys.test.ts +6 -73
  96. package/tests/scanConfig.test.ts +144 -0
  97. package/tests/sqlBuilder-advanced.test.ts +217 -215
  98. package/tests/sqlBuilder.test.ts +92 -91
  99. package/tests/sync-connection.test.ts +29 -29
  100. package/tests/syncDb-apply.test.ts +97 -96
  101. package/tests/syncDb-array-number.test.ts +160 -0
  102. package/tests/syncDb-constants.test.ts +48 -47
  103. package/tests/syncDb-ddl.test.ts +99 -98
  104. package/tests/syncDb-helpers.test.ts +29 -28
  105. package/tests/syncDb-schema.test.ts +61 -60
  106. package/tests/syncDb-types.test.ts +60 -59
  107. package/tests/syncMenu-paths.test.ts +68 -0
  108. package/tests/util.test.ts +42 -41
  109. package/tests/validator-array-number.test.ts +310 -0
  110. package/tests/validator-default.test.ts +373 -0
  111. package/tests/validator.test.ts +271 -266
  112. package/tsconfig.json +4 -5
  113. package/types/api.d.ts +7 -12
  114. package/types/befly.d.ts +60 -13
  115. package/types/cache.d.ts +8 -4
  116. package/types/common.d.ts +17 -9
  117. package/types/context.d.ts +2 -2
  118. package/types/crypto.d.ts +23 -0
  119. package/types/database.d.ts +19 -19
  120. package/types/hook.d.ts +2 -2
  121. package/types/jwt.d.ts +118 -0
  122. package/types/logger.d.ts +30 -0
  123. package/types/plugin.d.ts +4 -4
  124. package/types/redis.d.ts +7 -3
  125. package/types/roleApisCache.ts +23 -0
  126. package/types/sync.d.ts +10 -10
  127. package/types/table.d.ts +50 -9
  128. package/types/validate.d.ts +69 -0
  129. package/utils/addonHelper.ts +90 -0
  130. package/utils/arrayKeysToCamel.ts +18 -0
  131. package/utils/calcPerfTime.ts +13 -0
  132. package/utils/configTypes.ts +3 -0
  133. package/utils/cors.ts +19 -0
  134. package/utils/fieldClear.ts +75 -0
  135. package/utils/genShortId.ts +12 -0
  136. package/utils/getClientIp.ts +45 -0
  137. package/utils/keysToCamel.ts +22 -0
  138. package/utils/keysToSnake.ts +22 -0
  139. package/utils/modules.ts +98 -0
  140. package/utils/pickFields.ts +19 -0
  141. package/utils/process.ts +56 -0
  142. package/utils/regex.ts +225 -0
  143. package/utils/response.ts +115 -0
  144. package/utils/route.ts +23 -0
  145. package/utils/scanConfig.ts +142 -0
  146. package/utils/scanFiles.ts +48 -0
  147. package/.prettierignore +0 -2
  148. package/.prettierrc +0 -12
  149. package/docs/1-/345/237/272/346/234/254/344/273/213/347/273/215.md +0 -35
  150. package/docs/2-/345/210/235/346/255/245/344/275/223/351/252/214.md +0 -64
  151. package/docs/3-/347/254/254/344/270/200/344/270/252/346/216/245/345/217/243.md +0 -46
  152. package/docs/4-/346/223/215/344/275/234/346/225/260/346/215/256/345/272/223.md +0 -172
  153. package/hooks/requestLogger.ts +0 -84
  154. package/types/index.ts +0 -24
  155. package/util.ts +0 -283
package/sync/syncDev.ts CHANGED
@@ -1,4 +1,4 @@
1
- /**
1
+ /**
2
2
  * SyncDev 命令 - 同步开发者管理员到数据库
3
3
  * - 邮箱: 通过 DEV_EMAIL 环境变量配置(默认 dev@qq.com)
4
4
  * - 姓名: 开发者
@@ -6,18 +6,19 @@
6
6
  * - 角色: 同步 dev, user, admin, guest 四个角色
7
7
  * - dev: 拥有所有菜单和接口权限
8
8
  * - user, admin, guest: 菜单和接口权限为空
9
+ * - 同步完成后:重建角色接口权限缓存到 Redis(极简方案:覆盖更新)
9
10
  * - 表名: addon_admin_admin
10
11
  */
11
12
 
12
- import { Logger } from '../lib/logger.js';
13
- import { Cipher } from '../lib/cipher.js';
14
- import { Connect } from '../lib/connect.js';
15
- import { DbHelper } from '../lib/dbHelper.js';
16
- import { RedisHelper } from '../lib/redisHelper.js';
17
- import { CacheHelper } from '../lib/cacheHelper.js';
18
- import { beflyConfig } from '../befly.config.js';
13
+ import type { SyncDevOptions } from "../types/sync.js";
19
14
 
20
- import type { SyncDevOptions } from '../types/index.js';
15
+ import { beflyConfig } from "../befly.config.js";
16
+ import { CacheHelper } from "../lib/cacheHelper.js";
17
+ import { Cipher } from "../lib/cipher.js";
18
+ import { Connect } from "../lib/connect.js";
19
+ import { DbHelper } from "../lib/dbHelper.js";
20
+ import { Logger } from "../lib/logger.js";
21
+ import { RedisHelper } from "../lib/redisHelper.js";
21
22
 
22
23
  /**
23
24
  * SyncDev 命令主函数
@@ -25,7 +26,7 @@ import type { SyncDevOptions } from '../types/index.js';
25
26
  export async function syncDevCommand(options: SyncDevOptions = {}): Promise<void> {
26
27
  try {
27
28
  if (options.plan) {
28
- Logger.debug('[计划] 同步完成后将初始化/更新开发管理员账号(plan 模式不执行)');
29
+ Logger.debug("[计划] 同步完成后将初始化/更新开发管理员账号(plan 模式不执行)");
29
30
  return;
30
31
  }
31
32
 
@@ -37,88 +38,91 @@ export async function syncDevCommand(options: SyncDevOptions = {}): Promise<void
37
38
  // 连接数据库(SQL + Redis)
38
39
  await Connect.connect();
39
40
 
40
- const helper = new DbHelper({ redis: new RedisHelper() } as any, Connect.getSql());
41
+ const redisHelper = new RedisHelper();
42
+ const helper = new DbHelper({ redis: redisHelper } as any, Connect.getSql());
41
43
 
42
44
  // 检查 addon_admin_admin 表是否存在
43
- const existAdmin = await helper.tableExists('addon_admin_admin');
45
+ const existAdmin = await helper.tableExists("addon_admin_admin");
44
46
  if (!existAdmin) {
45
- Logger.debug('[SyncDev] 表 addon_admin_admin 不存在,跳过开发者账号同步');
47
+ Logger.debug("[SyncDev] 表 addon_admin_admin 不存在,跳过开发者账号同步");
46
48
  return;
47
49
  }
48
50
 
49
51
  // 检查 addon_admin_role 表是否存在
50
- const existRole = await helper.tableExists('addon_admin_role');
52
+ const existRole = await helper.tableExists("addon_admin_role");
51
53
  if (!existRole) {
52
- Logger.debug('[SyncDev] 表 addon_admin_role 不存在,跳过开发者账号同步');
54
+ Logger.debug("[SyncDev] 表 addon_admin_role 不存在,跳过开发者账号同步");
53
55
  return;
54
56
  }
55
57
 
56
58
  // 检查 addon_admin_menu 表是否存在
57
- const existMenu = await helper.tableExists('addon_admin_menu');
59
+ const existMenu = await helper.tableExists("addon_admin_menu");
58
60
  if (!existMenu) {
59
- Logger.debug('[SyncDev] 表 addon_admin_menu 不存在,跳过开发者账号同步');
61
+ Logger.debug("[SyncDev] 表 addon_admin_menu 不存在,跳过开发者账号同步");
60
62
  return;
61
63
  }
62
64
 
63
65
  // 查询所有菜单 ID
64
66
  const allMenus = await helper.getAll({
65
- table: 'addon_admin_menu',
66
- fields: ['id']
67
+ table: "addon_admin_menu",
68
+ fields: ["id"],
69
+ orderBy: ["id#ASC"]
67
70
  });
68
71
 
69
72
  if (!allMenus || !Array.isArray(allMenus.lists)) {
70
- Logger.debug('[SyncDev] 菜单数据为空,跳过开发者账号同步');
73
+ Logger.debug("[SyncDev] 菜单数据为空,跳过开发者账号同步");
71
74
  return;
72
75
  }
73
76
 
74
- const menuIds = allMenus.lists.length > 0 ? allMenus.lists.map((m: any) => m.id).join(',') : '';
77
+ const menuIds = allMenus.lists.length > 0 ? allMenus.lists.map((m: any) => m.id) : [];
75
78
 
76
79
  // 查询所有接口 ID
77
- const existApi = await helper.tableExists('addon_admin_api');
78
- let apiIds = '';
80
+ const existApi = await helper.tableExists("addon_admin_api");
81
+ let apiIds: number[] = [];
79
82
  if (existApi) {
80
83
  const allApis = await helper.getAll({
81
- table: 'addon_admin_api',
82
- fields: ['id']
84
+ table: "addon_admin_api",
85
+ fields: ["id"],
86
+ orderBy: ["id#ASC"]
83
87
  });
84
88
 
85
89
  if (allApis && Array.isArray(allApis.lists) && allApis.lists.length > 0) {
86
- apiIds = allApis.lists.map((a: any) => a.id).join(',');
90
+ apiIds = allApis.lists.map((a: any) => a.id);
87
91
  }
88
92
  }
89
93
 
90
94
  // 定义四个角色的配置
91
95
  const roles = [
92
96
  {
93
- code: 'dev',
94
- name: '开发者角色',
95
- description: '拥有所有菜单和接口权限的开发者角色',
97
+ code: "dev",
98
+ name: "开发者角色",
99
+ description: "拥有所有菜单和接口权限的开发者角色",
96
100
  menus: menuIds,
97
101
  apis: apiIds,
98
102
  sort: 0
99
103
  },
100
104
  {
101
- code: 'user',
102
- name: '用户角色',
103
- description: '普通用户角色',
104
- menus: '',
105
- apis: '',
105
+ code: "user",
106
+ name: "用户角色",
107
+ description: "普通用户角色",
108
+ menus: [],
109
+ apis: [],
106
110
  sort: 1
107
111
  },
108
112
  {
109
- code: 'admin',
110
- name: '管理员角色',
111
- description: '管理员角色',
112
- menus: '',
113
- apis: '',
113
+ code: "admin",
114
+ name: "管理员角色",
115
+ description: "管理员角色",
116
+ menus: [],
117
+ apis: [],
114
118
  sort: 2
115
119
  },
116
120
  {
117
- code: 'guest',
118
- name: '访客角色',
119
- description: '访客角色',
120
- menus: '',
121
- apis: '',
121
+ code: "guest",
122
+ name: "访客角色",
123
+ description: "访客角色",
124
+ menus: [],
125
+ apis: [],
122
126
  sort: 3
123
127
  }
124
128
  ];
@@ -127,18 +131,23 @@ export async function syncDevCommand(options: SyncDevOptions = {}): Promise<void
127
131
  let devRole = null;
128
132
  for (const roleConfig of roles) {
129
133
  const existingRole = await helper.getOne({
130
- table: 'addon_admin_role',
134
+ table: "addon_admin_role",
131
135
  where: { code: roleConfig.code }
132
136
  });
133
137
 
134
138
  if (existingRole) {
135
139
  // 检查字段是否有变化
136
- const hasChanges = existingRole.name !== roleConfig.name || existingRole.description !== roleConfig.description || existingRole.menus !== roleConfig.menus || existingRole.apis !== roleConfig.apis || existingRole.sort !== roleConfig.sort;
140
+ const existingMenusJson = JSON.stringify(existingRole.menus || []);
141
+ const existingApisJson = JSON.stringify(existingRole.apis || []);
142
+ const nextMenusJson = JSON.stringify(roleConfig.menus);
143
+ const nextApisJson = JSON.stringify(roleConfig.apis);
144
+
145
+ const hasChanges = existingRole.name !== roleConfig.name || existingRole.description !== roleConfig.description || existingMenusJson !== nextMenusJson || existingApisJson !== nextApisJson || existingRole.sort !== roleConfig.sort;
137
146
 
138
147
  if (hasChanges) {
139
148
  // 更新现有角色
140
149
  await helper.updData({
141
- table: 'addon_admin_role',
150
+ table: "addon_admin_role",
142
151
  where: { code: roleConfig.code },
143
152
  data: {
144
153
  name: roleConfig.name,
@@ -149,71 +158,71 @@ export async function syncDevCommand(options: SyncDevOptions = {}): Promise<void
149
158
  }
150
159
  });
151
160
  }
152
- if (roleConfig.code === 'dev') {
161
+ if (roleConfig.code === "dev") {
153
162
  devRole = existingRole;
154
163
  }
155
164
  } else {
156
165
  // 创建新角色
157
166
  const roleId = await helper.insData({
158
- table: 'addon_admin_role',
167
+ table: "addon_admin_role",
159
168
  data: roleConfig
160
169
  });
161
- if (roleConfig.code === 'dev') {
170
+ if (roleConfig.code === "dev") {
162
171
  devRole = { id: roleId };
163
172
  }
164
173
  }
165
174
  }
166
175
 
167
176
  if (!devRole) {
168
- Logger.error('dev 角色不存在,无法创建开发者账号');
177
+ Logger.error("dev 角色不存在,无法创建开发者账号");
169
178
  return;
170
179
  }
171
180
 
172
181
  // 先对密码进行 SHA-256 + 盐值 哈希(模拟前端加密),再用 bcrypt 存储
173
- const sha256Hashed = Cipher.sha256(beflyConfig.devPassword + 'befly');
182
+ const sha256Hashed = Cipher.sha256(beflyConfig.devPassword + "befly");
174
183
  const hashed = await Cipher.hashPassword(sha256Hashed);
175
184
 
176
185
  // 准备开发管理员数据
177
186
  const devData = {
178
- nickname: '开发者',
187
+ nickname: "开发者",
179
188
  email: beflyConfig.devEmail,
180
- username: 'dev',
189
+ username: "dev",
181
190
  password: hashed,
182
- roleCode: 'dev',
183
- roleType: 'admin'
191
+ roleCode: "dev",
192
+ roleType: "admin"
184
193
  };
185
194
 
186
195
  // 查询现有账号
187
196
  const existing = await helper.getOne({
188
- table: 'addon_admin_admin',
197
+ table: "addon_admin_admin",
189
198
  where: { email: beflyConfig.devEmail }
190
199
  });
191
200
 
192
201
  if (existing) {
193
202
  // 更新现有账号
194
203
  await helper.updData({
195
- table: 'addon_admin_admin',
204
+ table: "addon_admin_admin",
196
205
  where: { email: beflyConfig.devEmail },
197
206
  data: devData
198
207
  });
199
208
  } else {
200
209
  // 插入新账号
201
210
  await helper.insData({
202
- table: 'addon_admin_admin',
211
+ table: "addon_admin_admin",
203
212
  data: devData
204
213
  });
205
214
  }
206
215
 
207
- // 缓存角色权限数据到 Redis(复用 CacheHelper 逻辑)
216
+ // 重建角色接口权限缓存到 Redis(极简方案:覆盖更新)
217
+ // 说明:syncDev 会修改角色 apis,需同步刷新对应角色权限缓存
208
218
  try {
209
- const tempBefly = { db: helper, redis: new RedisHelper() } as any;
210
- const cacheHelper = new CacheHelper(tempBefly);
211
- await cacheHelper.cacheRolePermissions();
219
+ const cacheHelper = new CacheHelper({ db: helper, redis: redisHelper } as any);
220
+ await cacheHelper.rebuildRoleApiPermissions();
212
221
  } catch (error: any) {
213
- // 忽略缓存错误
222
+ Logger.warn({ err: error }, "[SyncDev] 重建角色接口权限缓存失败");
214
223
  }
215
224
  } catch (error: any) {
216
- Logger.error({ err: error }, '同步开发者管理员失败');
225
+ Logger.error({ err: error }, "同步开发者管理员失败");
217
226
  throw error;
218
227
  } finally {
219
228
  await Connect.disconnect();