befly 3.8.29 → 3.8.30

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 (52) hide show
  1. package/README.md +8 -6
  2. package/checks/checkApi.ts +2 -1
  3. package/checks/checkTable.ts +3 -2
  4. package/hooks/parser.ts +5 -3
  5. package/hooks/permission.ts +12 -5
  6. package/lib/cacheHelper.ts +76 -62
  7. package/lib/connect.ts +8 -35
  8. package/lib/dbHelper.ts +14 -11
  9. package/lib/jwt.ts +58 -437
  10. package/lib/logger.ts +76 -197
  11. package/lib/redisHelper.ts +163 -1
  12. package/lib/sqlBuilder.ts +2 -1
  13. package/lib/validator.ts +9 -8
  14. package/loader/loadApis.ts +4 -7
  15. package/loader/loadHooks.ts +2 -2
  16. package/loader/loadPlugins.ts +4 -4
  17. package/main.ts +4 -17
  18. package/package.json +9 -8
  19. package/paths.ts +0 -6
  20. package/plugins/db.ts +2 -2
  21. package/plugins/jwt.ts +5 -5
  22. package/plugins/redis.ts +1 -1
  23. package/router/api.ts +2 -2
  24. package/router/static.ts +1 -2
  25. package/sync/syncAll.ts +2 -2
  26. package/sync/syncApi.ts +10 -7
  27. package/sync/syncDb/apply.ts +1 -2
  28. package/sync/syncDb.ts +6 -10
  29. package/sync/syncDev.ts +10 -48
  30. package/sync/syncMenu.ts +11 -8
  31. package/tests/cacheHelper.test.ts +327 -0
  32. package/tests/dbHelper-columns.test.ts +5 -20
  33. package/tests/dbHelper-execute.test.ts +14 -68
  34. package/tests/fields-redis-cache.test.ts +5 -3
  35. package/tests/integration.test.ts +15 -26
  36. package/tests/jwt.test.ts +36 -94
  37. package/tests/logger.test.ts +32 -34
  38. package/tests/redisHelper.test.ts +270 -0
  39. package/tests/redisKeys.test.ts +76 -0
  40. package/tests/sync-connection.test.ts +0 -6
  41. package/tests/syncDb-constants.test.ts +12 -12
  42. package/tests/util.test.ts +5 -1
  43. package/types/befly.d.ts +2 -15
  44. package/types/common.d.ts +11 -93
  45. package/types/database.d.ts +216 -5
  46. package/types/index.ts +1 -0
  47. package/types/logger.d.ts +11 -41
  48. package/types/table.d.ts +213 -0
  49. package/hooks/_rateLimit.ts +0 -64
  50. package/lib/regexAliases.ts +0 -59
  51. package/lib/xml.ts +0 -383
  52. package/tests/xml.test.ts +0 -101
package/main.ts CHANGED
@@ -4,12 +4,11 @@
4
4
  */
5
5
 
6
6
  // ========== 外部依赖 ==========
7
- import { calcPerfTime } from 'befly-util';
7
+ import { calcPerfTime } from 'befly-shared/calcPerfTime';
8
8
 
9
9
  // ========== 相对导入 ==========
10
10
  import { Logger } from './lib/logger.js';
11
11
  import { Cipher } from './lib/cipher.js';
12
- import { Jwt } from './lib/jwt.js';
13
12
  import { Connect } from './lib/connect.js';
14
13
  import { DbHelper } from './lib/dbHelper.js';
15
14
  import { RedisHelper } from './lib/redisHelper.js';
@@ -103,7 +102,7 @@ export class Befly {
103
102
  '/*': staticHandler(this.config)
104
103
  },
105
104
  error: (error: Error) => {
106
- Logger.error('服务启动时发生错误', error);
105
+ Logger.error({ err: error }, '服务启动时发生错误');
107
106
  return Response.json({ code: 1, msg: '内部服务器错误' });
108
107
  }
109
108
  });
@@ -128,7 +127,7 @@ export class Befly {
128
127
  await Connect.disconnect();
129
128
  Logger.info('数据库连接已关闭');
130
129
  } catch (error: any) {
131
- Logger.error('关闭数据库连接时出错:', error);
130
+ Logger.error({ err: error }, '关闭数据库连接时出错');
132
131
  }
133
132
 
134
133
  Logger.info('服务器已优雅关闭');
@@ -140,20 +139,8 @@ export class Befly {
140
139
 
141
140
  return server;
142
141
  } catch (error: any) {
143
- Logger.error('项目启动失败', error);
142
+ Logger.error({ err: error }, '项目启动失败');
144
143
  process.exit(1);
145
144
  }
146
145
  }
147
146
  }
148
-
149
- // 核心类和工具导出
150
- export {
151
- // 配置
152
- Logger,
153
- Cipher,
154
- Jwt,
155
- Database,
156
- DbHelper,
157
- RedisHelper,
158
- coreDir
159
- };
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "befly",
3
- "version": "3.8.29",
3
+ "version": "3.8.30",
4
4
  "description": "Befly - 为 Bun 专属打造的 TypeScript API 接口框架核心引擎",
5
5
  "type": "module",
6
6
  "private": false,
@@ -15,10 +15,7 @@
15
15
  "types": "./main.ts",
16
16
  "default": "./main.ts"
17
17
  },
18
- "./types": {
19
- "types": "./types/index.ts",
20
- "default": "./types/index.ts"
21
- }
18
+ "./*": "./*"
22
19
  },
23
20
  "scripts": {
24
21
  "bundler": "bun build ./main.ts --outfile ./main.single.ts --minify --target bun"
@@ -66,12 +63,16 @@
66
63
  "bun": ">=1.3.0"
67
64
  },
68
65
  "dependencies": {
69
- "befly-util": "^1.0.7",
66
+ "befly-shared": "^1.1.1",
70
67
  "chalk": "^5.6.2",
71
68
  "es-toolkit": "^1.42.0",
72
- "pathe": "^2.0.3"
69
+ "fast-jwt": "^6.0.2",
70
+ "fast-xml-parser": "^5.3.2",
71
+ "pathe": "^2.0.3",
72
+ "pino": "^10.1.0",
73
+ "pino-roll": "^4.0.0"
73
74
  },
74
- "gitHead": "1f3cacbe4134ee60654b75338bcaeda535898a84",
75
+ "gitHead": "2031550167896390ac58c50aeb52b8c23426844e",
75
76
  "devDependencies": {
76
77
  "typescript": "^5.9.3"
77
78
  }
package/paths.ts CHANGED
@@ -46,9 +46,6 @@ export const corePluginDir = join(__dirname, 'plugins');
46
46
  */
47
47
  export const coreHookDir = join(__dirname, 'hooks');
48
48
 
49
- /**
50
- * Core 框架 API 目录
51
-
52
49
  /**
53
50
  * Core 框架 API 目录
54
51
  * @description packages/core/apis/
@@ -93,9 +90,6 @@ export const projectPluginDir = join(projectDir, 'plugins');
93
90
  */
94
91
  export const projectHookDir = join(projectDir, 'hooks');
95
92
 
96
- /**
97
- * 项目 API 目录
98
-
99
93
  /**
100
94
  * 项目 API 目录
101
95
  * @description {projectDir}/apis/
package/plugins/db.ts CHANGED
@@ -26,14 +26,14 @@ const dbPlugin: Plugin = {
26
26
 
27
27
  return dbManager;
28
28
  } catch (error: any) {
29
- Logger.error('数据库初始化失败', error);
29
+ Logger.error({ err: error }, '数据库初始化失败');
30
30
 
31
31
  // 清理资源
32
32
  if (sql) {
33
33
  try {
34
34
  await sql.close();
35
35
  } catch (cleanupError: any) {
36
- Logger.error('清理连接池失败:', cleanupError);
36
+ Logger.error({ err: cleanupError }, '清理连接池失败');
37
37
  }
38
38
  }
39
39
 
package/plugins/jwt.ts CHANGED
@@ -1,15 +1,15 @@
1
1
  /**
2
2
  * JWT 插件
3
- * 提供 JSON Web Token 签名和验证功能
4
3
  */
5
4
 
6
5
  import { Jwt } from '../lib/jwt.js';
7
6
 
8
7
  import type { Plugin } from '../types/plugin.js';
9
8
 
10
- export default {
11
- name: 'jwt',
9
+ const jwtPlugin: Plugin = {
12
10
  handler: (context, config) => {
13
- return Jwt;
11
+ return new Jwt(config?.auth);
14
12
  }
15
- } as Plugin;
13
+ };
14
+
15
+ export default jwtPlugin;
package/plugins/redis.ts CHANGED
@@ -26,7 +26,7 @@ const redisPlugin: Plugin = {
26
26
  // 返回 RedisHelper 实例
27
27
  return new RedisHelper(config.prefix);
28
28
  } catch (error: any) {
29
- Logger.error('Redis 初始化失败', error);
29
+ Logger.error({ err: error }, 'Redis 初始化失败');
30
30
 
31
31
  // 插件内禁止直接退出进程,抛出异常交由主流程统一处理
32
32
  throw error;
package/router/api.ts CHANGED
@@ -4,7 +4,7 @@
4
4
  */
5
5
 
6
6
  // 外部依赖
7
- import { genShortId } from 'befly-util';
7
+ import { genShortId } from 'befly-shared/genShortId';
8
8
 
9
9
  // 相对导入
10
10
  import { FinalResponse } from '../util.js';
@@ -88,7 +88,7 @@ export function apiHandler(apis: Map<string, ApiRoute>, hooks: Hook[], context:
88
88
  } catch (err: any) {
89
89
  // 全局错误处理
90
90
  const errorPath = ctx.api ? apiPath : req.url;
91
- Logger.error(`请求错误: ${errorPath}`, err);
91
+ Logger.error({ err: err, path: errorPath }, '请求错误');
92
92
  ctx.result = {
93
93
  code: 1,
94
94
  msg: '内部服务错误'
package/router/static.ts CHANGED
@@ -55,8 +55,7 @@ export function staticHandler(config: BeflyOptions) {
55
55
  );
56
56
  }
57
57
  } catch (error: any) {
58
- // 记录详细的错误日志
59
- Logger.error('静态文件处理失败', error);
58
+ Logger.error({ err: error }, '静态文件处理失败');
60
59
 
61
60
  return Response.json(
62
61
  {
package/sync/syncAll.ts CHANGED
@@ -32,9 +32,9 @@ export async function syncAllCommand(config: BeflyOptions, options: SyncOptions
32
32
 
33
33
  // 输出总结
34
34
  const totalTimeSeconds = ((Date.now() - startTime) / 1000).toFixed(2);
35
- Logger.info(`同步完成 (耗时 ${totalTimeSeconds}s)`);
35
+ Logger.info({ duration: totalTimeSeconds }, '同步完成');
36
36
  } catch (error: any) {
37
- Logger.error('同步过程中发生错误', error);
37
+ Logger.error({ err: error }, '同步过程中发生错误');
38
38
  throw error;
39
39
  }
40
40
  }
package/sync/syncApi.ts CHANGED
@@ -13,8 +13,11 @@
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';
@@ -59,7 +62,7 @@ async function extractApiInfo(filePath: string, apiRoot: string, type: 'app' | '
59
62
  addonTitle: addonTitle || addonName
60
63
  };
61
64
  } catch (error: any) {
62
- Logger.error('同步 API 失败:', error);
65
+ Logger.error({ err: error }, '同步 API 失败');
63
66
  throw error;
64
67
  }
65
68
  }
@@ -136,7 +139,7 @@ async function scanAllApis(projectRoot: string): Promise<ApiInfo[]> {
136
139
 
137
140
  return apis;
138
141
  } catch (error: any) {
139
- Logger.error(`接口扫描失败:`, error);
142
+ Logger.error({ err: error }, '接口扫描失败');
140
143
  return apis;
141
144
  }
142
145
  }
@@ -184,7 +187,7 @@ async function syncApis(helper: any, apis: ApiInfo[]): Promise<void> {
184
187
  });
185
188
  }
186
189
  } catch (error: any) {
187
- Logger.error(`同步接口 "${api.name}" 失败:`, error);
190
+ Logger.error({ err: error, api: api.name }, '同步接口失败');
188
191
  }
189
192
  }
190
193
  }
@@ -222,7 +225,7 @@ export async function syncApiCommand(config: BeflyOptions, options: SyncApiOptio
222
225
  // 连接数据库(SQL + Redis)
223
226
  await Connect.connect(config);
224
227
 
225
- const helper = Connect.getDbHelper();
228
+ const helper = new DbHelper({ redis: new RedisHelper() } as any, Connect.getSql());
226
229
 
227
230
  // 1. 检查表是否存在(addon_admin_api 来自 addon-admin 组件)
228
231
  const exists = await helper.tableExists('addon_admin_api');
@@ -251,12 +254,12 @@ export async function syncApiCommand(config: BeflyOptions, options: SyncApiOptio
251
254
  });
252
255
 
253
256
  const redisHelper = new RedisHelper();
254
- await redisHelper.setObject('apis:all', apiList);
257
+ await redisHelper.setObject(RedisKeys.apisAll(), apiList);
255
258
  } catch (error: any) {
256
259
  // 忽略缓存错误
257
260
  }
258
261
  } catch (error: any) {
259
- Logger.error('API 同步失败:', error);
262
+ Logger.error({ err: error }, 'API 同步失败');
260
263
  throw error;
261
264
  } finally {
262
265
  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
 
@@ -134,17 +136,11 @@ export async function syncDbCommand(config: BeflyOptions, options: SyncDbOptions
134
136
  // 清理 Redis 缓存(如果有表被处理)
135
137
  if (processedTables.length > 0) {
136
138
  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
- }
139
+ const cacheKeys = processedTables.map((tableName) => RedisKeys.tableColumns(tableName));
140
+ await redisHelper.delBatch(cacheKeys);
145
141
  }
146
142
  } catch (error: any) {
147
- Logger.error('数据库同步失败', error);
143
+ Logger.error({ err: error }, '数据库同步失败');
148
144
  throw error;
149
145
  } finally {
150
146
  if (sql) {
package/sync/syncDev.ts CHANGED
@@ -7,12 +7,15 @@
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 { RedisHelper } from '../lib/redisHelper.js';
17
+ import { CacheHelper } from '../lib/cacheHelper.js';
18
+
16
19
  import type { SyncDevOptions, SyncDevStats, BeflyOptions } from '../types/index.js';
17
20
 
18
21
  /**
@@ -33,7 +36,7 @@ export async function syncDevCommand(config: BeflyOptions, options: SyncDevOptio
33
36
  // 连接数据库(SQL + Redis)
34
37
  await Connect.connect(config);
35
38
 
36
- const helper = Connect.getDbHelper();
39
+ const helper = new DbHelper({ redis: new RedisHelper() } as any, Connect.getSql());
37
40
 
38
41
  // 检查 addon_admin_admin 表是否存在
39
42
  const existAdmin = await helper.tableExists('addon_admin_admin');
@@ -152,57 +155,16 @@ export async function syncDevCommand(config: BeflyOptions, options: SyncDevOptio
152
155
  });
153
156
  }
154
157
 
155
- // 缓存角色权限数据到 Redis
158
+ // 缓存角色权限数据到 Redis(复用 CacheHelper 逻辑)
156
159
  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
- }
160
+ const tempBefly = { db: helper, redis: new RedisHelper() } as any;
161
+ const cacheHelper = new CacheHelper(tempBefly);
162
+ await cacheHelper.cacheRolePermissions();
201
163
  } catch (error: any) {
202
164
  // 忽略缓存错误
203
165
  }
204
166
  } catch (error: any) {
205
- Logger.error('同步开发者管理员失败', error);
167
+ Logger.error({ err: error }, '同步开发者管理员失败');
206
168
  throw error;
207
169
  } finally {
208
170
  await Connect.disconnect();
package/sync/syncMenu.ts CHANGED
@@ -17,8 +17,11 @@
17
17
  import { join } from 'pathe';
18
18
  import { cloneDeep } from 'es-toolkit';
19
19
  import { Connect } from '../lib/connect.js';
20
+ import { DbHelper } from '../lib/dbHelper.js';
20
21
  import { RedisHelper } from '../lib/redisHelper.js';
21
- import { scanAddons, getAddonDir, scanConfig } from 'befly-util';
22
+ import { RedisKeys } from 'befly-shared/redisKeys';
23
+ import { scanAddons, getAddonDir } from 'befly-shared/addonHelper';
24
+ import { scanConfig } from 'befly-shared/scanConfig';
22
25
  import { Logger } from '../lib/logger.js';
23
26
  import { projectDir } from '../paths.js';
24
27
 
@@ -204,7 +207,7 @@ async function syncMenus(helper: any, menus: MenuConfig[]): Promise<void> {
204
207
  try {
205
208
  await syncMenuRecursive(helper, menu, 0, existingMenuMap, 1);
206
209
  } catch (error: any) {
207
- Logger.error(`同步菜单 "${menu.name}" 失败`, error.message || String(error));
210
+ Logger.error({ err: error, menu: menu.name }, '同步菜单失败');
208
211
  throw error;
209
212
  }
210
213
  }
@@ -257,7 +260,7 @@ async function loadMenuConfigs(): Promise<Array<{ menus: MenuConfig[]; addonName
257
260
  allMenus.push({ menus: menusWithPrefix, addonName: addonName });
258
261
  }
259
262
  } catch (error: any) {
260
- Logger.warn(`读取 addon 配置失败 ${addonName}: ${error.message}`);
263
+ Logger.warn({ err: error, addon: addonName }, '读取 addon 配置失败');
261
264
  }
262
265
  }
263
266
 
@@ -276,7 +279,7 @@ async function loadMenuConfigs(): Promise<Array<{ menus: MenuConfig[]; addonName
276
279
  allMenus.push({ menus: appMenus, addonName: 'app' });
277
280
  }
278
281
  } catch (error: any) {
279
- Logger.warn(`读取项目配置失败: ${error.message}`);
282
+ Logger.warn({ err: error }, '读取项目配置失败');
280
283
  }
281
284
 
282
285
  return allMenus;
@@ -301,7 +304,7 @@ export async function syncMenuCommand(config: BeflyOptions, options: SyncMenuOpt
301
304
  // 连接数据库(SQL + Redis)
302
305
  await Connect.connect(config);
303
306
 
304
- const helper = Connect.getDbHelper();
307
+ const helper = new DbHelper({ redis: new RedisHelper() } as any, Connect.getSql());
305
308
 
306
309
  // 3. 检查表是否存在(addon_admin_menu 来自 addon-admin 组件)
307
310
  const exists = await helper.tableExists('addon_admin_menu');
@@ -330,12 +333,12 @@ export async function syncMenuCommand(config: BeflyOptions, options: SyncMenuOpt
330
333
  // 8. 缓存菜单数据到 Redis
331
334
  try {
332
335
  const redisHelper = new RedisHelper();
333
- await redisHelper.setObject('menus:all', allMenusData);
336
+ await redisHelper.setObject(RedisKeys.menusAll(), allMenusData);
334
337
  } catch (error: any) {
335
- Logger.warn(`Redis 缓存菜单数据失败: ${error.message}`);
338
+ Logger.warn({ err: error }, 'Redis 缓存菜单数据失败');
336
339
  }
337
340
  } catch (error: any) {
338
- Logger.error('菜单同步失败', error);
341
+ Logger.error({ err: error }, '菜单同步失败');
339
342
  throw error;
340
343
  } finally {
341
344
  await Connect.disconnect();