befly 3.8.29 → 3.8.31

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 (70) hide show
  1. package/README.md +91 -6
  2. package/checks/checkApi.ts +2 -1
  3. package/checks/checkApp.ts +31 -1
  4. package/checks/checkTable.ts +3 -2
  5. package/hooks/cors.ts +3 -3
  6. package/hooks/parser.ts +8 -6
  7. package/hooks/permission.ts +12 -5
  8. package/hooks/validator.ts +1 -1
  9. package/lib/cacheHelper.ts +73 -65
  10. package/lib/cipher.ts +2 -1
  11. package/lib/connect.ts +23 -52
  12. package/lib/dbHelper.ts +14 -11
  13. package/lib/jwt.ts +58 -437
  14. package/lib/logger.ts +76 -197
  15. package/lib/redisHelper.ts +163 -1
  16. package/lib/sqlBuilder.ts +2 -1
  17. package/lib/validator.ts +150 -384
  18. package/loader/loadApis.ts +4 -7
  19. package/loader/loadHooks.ts +6 -5
  20. package/loader/loadPlugins.ts +11 -13
  21. package/main.ts +26 -53
  22. package/package.json +10 -8
  23. package/paths.ts +0 -6
  24. package/plugins/cipher.ts +1 -1
  25. package/plugins/config.ts +3 -4
  26. package/plugins/db.ts +6 -7
  27. package/plugins/jwt.ts +7 -6
  28. package/plugins/logger.ts +6 -6
  29. package/plugins/redis.ts +9 -13
  30. package/router/api.ts +2 -2
  31. package/router/static.ts +4 -8
  32. package/sync/syncAll.ts +8 -13
  33. package/sync/syncApi.ts +14 -10
  34. package/sync/syncDb/apply.ts +1 -2
  35. package/sync/syncDb.ts +12 -15
  36. package/sync/syncDev.ts +19 -56
  37. package/sync/syncMenu.ts +182 -137
  38. package/tests/cacheHelper.test.ts +327 -0
  39. package/tests/dbHelper-columns.test.ts +5 -20
  40. package/tests/dbHelper-execute.test.ts +14 -68
  41. package/tests/fields-redis-cache.test.ts +5 -3
  42. package/tests/integration.test.ts +17 -32
  43. package/tests/jwt.test.ts +36 -94
  44. package/tests/logger.test.ts +32 -34
  45. package/tests/redisHelper.test.ts +271 -2
  46. package/tests/redisKeys.test.ts +76 -0
  47. package/tests/sync-connection.test.ts +0 -6
  48. package/tests/syncDb-constants.test.ts +12 -12
  49. package/tests/util.test.ts +5 -1
  50. package/tests/validator.test.ts +611 -85
  51. package/types/befly.d.ts +9 -15
  52. package/types/cache.d.ts +73 -0
  53. package/types/common.d.ts +10 -128
  54. package/types/database.d.ts +221 -5
  55. package/types/index.ts +6 -5
  56. package/types/plugin.d.ts +1 -4
  57. package/types/redis.d.ts +37 -2
  58. package/types/table.d.ts +175 -0
  59. package/config.ts +0 -70
  60. package/hooks/_rateLimit.ts +0 -64
  61. package/lib/regexAliases.ts +0 -59
  62. package/lib/xml.ts +0 -383
  63. package/tests/validator-advanced.test.ts +0 -653
  64. package/tests/xml.test.ts +0 -101
  65. package/types/addon.d.ts +0 -50
  66. package/types/crypto.d.ts +0 -23
  67. package/types/jwt.d.ts +0 -99
  68. package/types/logger.d.ts +0 -43
  69. package/types/tool.d.ts +0 -67
  70. package/types/validator.d.ts +0 -43
package/sync/syncApi.ts CHANGED
@@ -13,13 +13,17 @@
13
13
  import { readdirSync, statSync } from 'node:fs';
14
14
  import { join, dirname, relative, basename } from 'pathe';
15
15
  import { Connect } from '../lib/connect.js';
16
+ import { DbHelper } from '../lib/dbHelper.js';
16
17
  import { RedisHelper } from '../lib/redisHelper.js';
17
- import { scanFiles, scanAddons, addonDirExists, getAddonDir } from 'befly-util';
18
+ import { RedisKeys } from 'befly-shared/redisKeys';
19
+ import { scanFiles } from 'befly-shared/scanFiles';
20
+ import { scanAddons, addonDirExists, getAddonDir } from 'befly-shared/addonHelper';
18
21
 
19
22
  import { Logger } from '../lib/logger.js';
20
23
  import { projectDir } from '../paths.js';
24
+ import { beflyConfig } from '../befly.config.js';
21
25
 
22
- import type { SyncApiOptions, ApiInfo, BeflyOptions } from '../types/index.js';
26
+ import type { SyncApiOptions, ApiInfo } from '../types/index.js';
23
27
 
24
28
  /**
25
29
  * 从 API 文件中提取接口信息
@@ -59,7 +63,7 @@ async function extractApiInfo(filePath: string, apiRoot: string, type: 'app' | '
59
63
  addonTitle: addonTitle || addonName
60
64
  };
61
65
  } catch (error: any) {
62
- Logger.error('同步 API 失败:', error);
66
+ Logger.error({ err: error }, '同步 API 失败');
63
67
  throw error;
64
68
  }
65
69
  }
@@ -136,7 +140,7 @@ async function scanAllApis(projectRoot: string): Promise<ApiInfo[]> {
136
140
 
137
141
  return apis;
138
142
  } catch (error: any) {
139
- Logger.error(`接口扫描失败:`, error);
143
+ Logger.error({ err: error }, '接口扫描失败');
140
144
  return apis;
141
145
  }
142
146
  }
@@ -184,7 +188,7 @@ async function syncApis(helper: any, apis: ApiInfo[]): Promise<void> {
184
188
  });
185
189
  }
186
190
  } catch (error: any) {
187
- Logger.error(`同步接口 "${api.name}" 失败:`, error);
191
+ Logger.error({ err: error, api: api.name }, '同步接口失败');
188
192
  }
189
193
  }
190
194
  }
@@ -212,7 +216,7 @@ async function deleteObsoleteRecords(helper: any, apiPaths: Set<string>): Promis
212
216
  /**
213
217
  * SyncApi 命令主函数
214
218
  */
215
- export async function syncApiCommand(config: BeflyOptions, options: SyncApiOptions = {}): Promise<void> {
219
+ export async function syncApiCommand(options: SyncApiOptions = {}): Promise<void> {
216
220
  try {
217
221
  if (options.plan) {
218
222
  Logger.debug('[计划] 同步 API 接口到数据库(plan 模式不执行)');
@@ -220,9 +224,9 @@ export async function syncApiCommand(config: BeflyOptions, options: SyncApiOptio
220
224
  }
221
225
 
222
226
  // 连接数据库(SQL + Redis)
223
- await Connect.connect(config);
227
+ await Connect.connect();
224
228
 
225
- const helper = Connect.getDbHelper();
229
+ const helper = new DbHelper({ redis: new RedisHelper() } as any, Connect.getSql());
226
230
 
227
231
  // 1. 检查表是否存在(addon_admin_api 来自 addon-admin 组件)
228
232
  const exists = await helper.tableExists('addon_admin_api');
@@ -251,12 +255,12 @@ export async function syncApiCommand(config: BeflyOptions, options: SyncApiOptio
251
255
  });
252
256
 
253
257
  const redisHelper = new RedisHelper();
254
- await redisHelper.setObject('apis:all', apiList);
258
+ await redisHelper.setObject(RedisKeys.apisAll(), apiList);
255
259
  } catch (error: any) {
256
260
  // 忽略缓存错误
257
261
  }
258
262
  } catch (error: any) {
259
- Logger.error('API 同步失败:', error);
263
+ Logger.error({ err: error }, 'API 同步失败');
260
264
  throw error;
261
265
  } finally {
262
266
  await Connect.disconnect();
@@ -175,8 +175,7 @@ export async function applyTablePlan(sql: SQL, tableName: string, fields: Record
175
175
  Logger.debug(`[索引变化] 删除索引 ${tableName}.${act.indexName} (${act.fieldName})`);
176
176
  }
177
177
  } catch (error: any) {
178
- Logger.error(`${act.action === 'create' ? '创建' : '删除'}索引失败`, error);
179
- Logger.warn(`表名: ${tableName}, 索引名: ${act.indexName}, 字段: ${act.fieldName}`);
178
+ Logger.error({ err: error, table: tableName, index: act.indexName, field: act.fieldName }, `${act.action === 'create' ? '创建' : '删除'}索引失败`);
180
179
  throw error;
181
180
  }
182
181
  }
package/sync/syncDb.ts CHANGED
@@ -13,7 +13,9 @@ import { snakeCase } from 'es-toolkit/string';
13
13
  import { Connect } from '../lib/connect.js';
14
14
  import { RedisHelper } from '../lib/redisHelper.js';
15
15
  import { checkTable } from '../checks/checkTable.js';
16
- import { scanFiles, scanAddons, addonDirExists, getAddonDir } from 'befly-util';
16
+ import { scanFiles } from 'befly-shared/scanFiles';
17
+ import { scanAddons, addonDirExists, getAddonDir } from 'befly-shared/addonHelper';
18
+ import { RedisKeys } from 'befly-shared/redisKeys';
17
19
  import { Logger } from '../lib/logger.js';
18
20
  import { projectDir } from '../paths.js';
19
21
 
@@ -24,8 +26,9 @@ import { modifyTable } from './syncDb/table.js';
24
26
  import { createTable } from './syncDb/tableCreate.js';
25
27
  import { applyFieldDefaults } from './syncDb/helpers.js';
26
28
  import { setDbType } from './syncDb/constants.js';
29
+ import { beflyConfig } from '../befly.config.js';
27
30
  import type { SQL } from 'bun';
28
- import type { BeflyOptions, SyncDbOptions } from '../types/index.js';
31
+ import type { SyncDbOptions } from '../types/index.js';
29
32
 
30
33
  // 全局 SQL 客户端实例
31
34
  let sql: SQL | null = null;
@@ -42,20 +45,20 @@ const processedTables: string[] = [];
42
45
  * 3. 扫描表定义文件(核心表、项目表、addon表)
43
46
  * 4. 对比并应用表结构变更
44
47
  */
45
- export async function syncDbCommand(config: BeflyOptions, options: SyncDbOptions = {}): Promise<void> {
48
+ export async function syncDbCommand(options: SyncDbOptions = {}): Promise<void> {
46
49
  try {
47
50
  // 清空处理记录
48
51
  processedTables.length = 0;
49
52
 
50
53
  // 设置数据库类型(从配置获取)
51
- const dbType = config.db?.type || 'mysql';
54
+ const dbType = beflyConfig.db?.type || 'mysql';
52
55
  setDbType(dbType);
53
56
 
54
57
  // 验证表定义文件
55
58
  await checkTable();
56
59
 
57
60
  // 建立数据库连接并检查版本
58
- sql = await Connect.connectSql({ max: 1 });
61
+ sql = await Connect.connectSql();
59
62
  await ensureDbVersion(sql);
60
63
 
61
64
  // 初始化 Redis 连接(用于清理缓存)
@@ -114,7 +117,7 @@ export async function syncDbCommand(config: BeflyOptions, options: SyncDbOptions
114
117
  applyFieldDefaults(fieldDef);
115
118
  }
116
119
 
117
- const dbName = config.db?.database;
120
+ const dbName = beflyConfig.db?.database;
118
121
  const existsTable = await tableExists(sql!, tableName, dbName);
119
122
 
120
123
  // 读取 force 参数
@@ -134,17 +137,11 @@ export async function syncDbCommand(config: BeflyOptions, options: SyncDbOptions
134
137
  // 清理 Redis 缓存(如果有表被处理)
135
138
  if (processedTables.length > 0) {
136
139
  const redisHelper = new RedisHelper();
137
- for (const tableName of processedTables) {
138
- const cacheKey = `table:columns:${tableName}`;
139
- try {
140
- await redisHelper.del(cacheKey);
141
- } catch (error: any) {
142
- Logger.warn(`清理表 ${tableName} 的缓存失败: ${error.message}`);
143
- }
144
- }
140
+ const cacheKeys = processedTables.map((tableName) => RedisKeys.tableColumns(tableName));
141
+ await redisHelper.delBatch(cacheKeys);
145
142
  }
146
143
  } catch (error: any) {
147
- Logger.error('数据库同步失败', error);
144
+ Logger.error({ err: error }, '数据库同步失败');
148
145
  throw error;
149
146
  } finally {
150
147
  if (sql) {
package/sync/syncDev.ts CHANGED
@@ -7,33 +7,37 @@
7
7
  * - 表名: addon_admin_admin
8
8
  */
9
9
 
10
- import { scanAddons, getAddonDir, normalizeModuleForSync } from 'befly-util';
10
+ import { scanAddons, getAddonDir, normalizeModuleForSync } from 'befly-shared/addonHelper';
11
11
 
12
12
  import { Logger } from '../lib/logger.js';
13
13
  import { Cipher } from '../lib/cipher.js';
14
14
  import { Connect } from '../lib/connect.js';
15
15
  import { DbHelper } from '../lib/dbHelper.js';
16
- import type { SyncDevOptions, SyncDevStats, BeflyOptions } from '../types/index.js';
16
+ import { RedisHelper } from '../lib/redisHelper.js';
17
+ import { CacheHelper } from '../lib/cacheHelper.js';
18
+ import { beflyConfig } from '../befly.config.js';
19
+
20
+ import type { SyncDevOptions, SyncDevStats } from '../types/index.js';
17
21
 
18
22
  /**
19
23
  * SyncDev 命令主函数
20
24
  */
21
- export async function syncDevCommand(config: BeflyOptions, options: SyncDevOptions = {}): Promise<void> {
25
+ export async function syncDevCommand(options: SyncDevOptions = {}): Promise<void> {
22
26
  try {
23
27
  if (options.plan) {
24
28
  Logger.debug('[计划] 同步完成后将初始化/更新开发管理员账号(plan 模式不执行)');
25
29
  return;
26
30
  }
27
31
 
28
- if (!config.devPassword) {
32
+ if (!beflyConfig.devPassword) {
29
33
  // 未配置开发者密码,跳过同步
30
34
  return;
31
35
  }
32
36
 
33
37
  // 连接数据库(SQL + Redis)
34
- await Connect.connect(config);
38
+ await Connect.connect();
35
39
 
36
- const helper = Connect.getDbHelper();
40
+ const helper = new DbHelper({ redis: new RedisHelper() } as any, Connect.getSql());
37
41
 
38
42
  // 检查 addon_admin_admin 表是否存在
39
43
  const existAdmin = await helper.tableExists('addon_admin_admin');
@@ -118,12 +122,12 @@ export async function syncDevCommand(config: BeflyOptions, options: SyncDevOptio
118
122
  }
119
123
 
120
124
  // 使用 bcrypt 加密密码
121
- const hashed = await Cipher.hashPassword(config.devPassword);
125
+ const hashed = await Cipher.hashPassword(beflyConfig.devPassword);
122
126
 
123
127
  // 准备开发管理员数据
124
128
  const devData = {
125
129
  nickname: '开发者',
126
- email: config.devEmail,
130
+ email: beflyConfig.devEmail,
127
131
  username: 'dev',
128
132
  password: hashed,
129
133
  roleId: devRole.id,
@@ -134,14 +138,14 @@ export async function syncDevCommand(config: BeflyOptions, options: SyncDevOptio
134
138
  // 查询现有账号
135
139
  const existing = await helper.getOne({
136
140
  table: 'addon_admin_admin',
137
- where: { email: config.devEmail }
141
+ where: { email: beflyConfig.devEmail }
138
142
  });
139
143
 
140
144
  if (existing) {
141
145
  // 更新现有账号
142
146
  await helper.updData({
143
147
  table: 'addon_admin_admin',
144
- where: { email: config.devEmail },
148
+ where: { email: beflyConfig.devEmail },
145
149
  data: devData
146
150
  });
147
151
  } else {
@@ -152,57 +156,16 @@ export async function syncDevCommand(config: BeflyOptions, options: SyncDevOptio
152
156
  });
153
157
  }
154
158
 
155
- // 缓存角色权限数据到 Redis
159
+ // 缓存角色权限数据到 Redis(复用 CacheHelper 逻辑)
156
160
  try {
157
- // 检查必要的表是否存在
158
- const apiTableExists = await helper.tableExists('addon_admin_api');
159
- const roleTableExists = await helper.tableExists('addon_admin_role');
160
-
161
- if (apiTableExists && roleTableExists) {
162
- // 查询所有角色
163
- const roles = await helper.getAll({
164
- table: 'addon_admin_role',
165
- fields: ['id', 'code', 'apis']
166
- });
167
-
168
- // 查询所有接口
169
- const allApis = await helper.getAll({
170
- table: 'addon_admin_api',
171
- fields: ['id', 'name', 'path', 'method', 'description', 'addonName']
172
- });
173
-
174
- const redis = Connect.getRedis();
175
-
176
- // 为每个角色缓存接口权限
177
- for (const role of roles) {
178
- if (!role.apis) continue;
179
-
180
- // 解析角色的接口 ID 列表
181
- const apiIds = role.apis
182
- .split(',')
183
- .map((id: string) => parseInt(id.trim()))
184
- .filter((id: number) => !isNaN(id));
185
-
186
- // 根据 ID 过滤出接口路径
187
- const roleApiPaths = allApis.filter((api: any) => apiIds.includes(api.id)).map((api: any) => `${api.method}${api.path}`);
188
-
189
- if (roleApiPaths.length === 0) continue;
190
-
191
- // 使用 Redis Set 缓存角色权限
192
- const redisKey = `role:apis:${role.code}`;
193
-
194
- // 先删除旧数据
195
- await redis.del(redisKey);
196
-
197
- // 批量添加到 Set(使用扩展运算符展开数组)
198
- await redis.sadd(redisKey, ...roleApiPaths);
199
- }
200
- }
161
+ const tempBefly = { db: helper, redis: new RedisHelper() } as any;
162
+ const cacheHelper = new CacheHelper(tempBefly);
163
+ await cacheHelper.cacheRolePermissions();
201
164
  } catch (error: any) {
202
165
  // 忽略缓存错误
203
166
  }
204
167
  } catch (error: any) {
205
- Logger.error('同步开发者管理员失败', error);
168
+ Logger.error({ err: error }, '同步开发者管理员失败');
206
169
  throw error;
207
170
  } finally {
208
171
  await Connect.disconnect();