befly 3.8.25 → 3.8.29

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 (62) hide show
  1. package/config.ts +8 -9
  2. package/hooks/{rateLimit.ts → _rateLimit.ts} +7 -13
  3. package/hooks/auth.ts +3 -11
  4. package/hooks/cors.ts +1 -4
  5. package/hooks/parser.ts +6 -8
  6. package/hooks/permission.ts +9 -12
  7. package/hooks/validator.ts +6 -9
  8. package/lib/cacheHelper.ts +0 -4
  9. package/lib/{database.ts → connect.ts} +65 -18
  10. package/lib/logger.ts +1 -17
  11. package/lib/redisHelper.ts +6 -5
  12. package/loader/loadApis.ts +3 -3
  13. package/loader/loadHooks.ts +15 -41
  14. package/loader/loadPlugins.ts +10 -16
  15. package/main.ts +25 -28
  16. package/package.json +4 -4
  17. package/plugins/cache.ts +2 -2
  18. package/plugins/cipher.ts +15 -0
  19. package/plugins/config.ts +16 -0
  20. package/plugins/db.ts +7 -17
  21. package/plugins/jwt.ts +15 -0
  22. package/plugins/logger.ts +1 -1
  23. package/plugins/redis.ts +4 -4
  24. package/plugins/tool.ts +50 -0
  25. package/router/api.ts +56 -42
  26. package/router/static.ts +12 -12
  27. package/sync/syncAll.ts +2 -20
  28. package/sync/syncApi.ts +7 -7
  29. package/sync/syncDb/apply.ts +10 -12
  30. package/sync/syncDb/constants.ts +64 -12
  31. package/sync/syncDb/ddl.ts +9 -8
  32. package/sync/syncDb/helpers.ts +7 -119
  33. package/sync/syncDb/schema.ts +16 -19
  34. package/sync/syncDb/sqlite.ts +1 -3
  35. package/sync/syncDb/table.ts +13 -146
  36. package/sync/syncDb/tableCreate.ts +28 -12
  37. package/sync/syncDb/types.ts +126 -0
  38. package/sync/syncDb/version.ts +4 -7
  39. package/sync/syncDb.ts +151 -6
  40. package/sync/syncDev.ts +19 -15
  41. package/sync/syncMenu.ts +87 -75
  42. package/tests/redisHelper.test.ts +15 -16
  43. package/tests/sync-connection.test.ts +189 -0
  44. package/tests/syncDb-apply.test.ts +288 -0
  45. package/tests/syncDb-constants.test.ts +151 -0
  46. package/tests/syncDb-ddl.test.ts +206 -0
  47. package/tests/syncDb-helpers.test.ts +113 -0
  48. package/tests/syncDb-schema.test.ts +178 -0
  49. package/tests/syncDb-types.test.ts +130 -0
  50. package/tsconfig.json +2 -2
  51. package/types/api.d.ts +1 -1
  52. package/types/befly.d.ts +23 -21
  53. package/types/common.d.ts +0 -29
  54. package/types/context.d.ts +8 -6
  55. package/types/hook.d.ts +3 -4
  56. package/types/plugin.d.ts +3 -0
  57. package/hooks/errorHandler.ts +0 -23
  58. package/hooks/requestId.ts +0 -24
  59. package/hooks/requestLogger.ts +0 -25
  60. package/hooks/responseFormatter.ts +0 -64
  61. package/router/root.ts +0 -56
  62. package/sync/syncDb/index.ts +0 -164
@@ -1,64 +0,0 @@
1
- // 相对导入
2
- import { JsonResponse } from '../util.js';
3
-
4
- // 类型导入
5
- import type { Hook } from '../types/hook.js';
6
-
7
- const hook: Hook = {
8
- after: ['requestId'],
9
- order: 100,
10
- handler: async (befly, ctx, next) => {
11
- await next();
12
-
13
- // 如果已经有 response,直接返回
14
- if (ctx.response) {
15
- return;
16
- }
17
-
18
- // 如果有 result,格式化为 JSON 响应
19
- if (ctx.result !== undefined) {
20
- let result = ctx.result;
21
-
22
- // 1. 如果是字符串,自动包裹为成功响应
23
- if (typeof result === 'string') {
24
- result = {
25
- code: 0,
26
- msg: result,
27
- data: {}
28
- };
29
- }
30
- // 2. 如果是对象,自动补充 code: 0
31
- else if (result && typeof result === 'object') {
32
- if (!('code' in result)) {
33
- result = {
34
- code: 0,
35
- ...result
36
- };
37
- }
38
- }
39
-
40
- // 处理 BigInt 序列化问题
41
- if (result && typeof result === 'object') {
42
- const jsonString = JSON.stringify(result, (key, value) => (typeof value === 'bigint' ? value.toString() : value));
43
- ctx.response = new Response(jsonString, {
44
- headers: {
45
- ...ctx.corsHeaders,
46
- 'Content-Type': 'application/json'
47
- }
48
- });
49
- } else {
50
- // 简单类型直接返回
51
- ctx.response = Response.json(result, {
52
- headers: ctx.corsHeaders
53
- });
54
- }
55
- return;
56
- }
57
-
58
- // 如果还没有响应,且不是 OPTIONS 请求,则设置默认 JSON 响应
59
- if (ctx.req.method !== 'OPTIONS' && !ctx.response) {
60
- ctx.response = JsonResponse(ctx, 'No response generated');
61
- }
62
- }
63
- };
64
- export default hook;
package/router/root.ts DELETED
@@ -1,56 +0,0 @@
1
- /**
2
- * 根路径路由处理器
3
- * 处理 / 路径的请求
4
- */
5
-
6
- // 相对导入
7
- import { Logger } from '../lib/logger.js';
8
- import { setCorsOptions } from '../util.js';
9
-
10
- /**
11
- * 根路径处理器
12
- */
13
- export async function rootHandler(req: Request): Promise<Response> {
14
- // 设置 CORS 响应头
15
- const corsHeaders = setCorsOptions(req);
16
-
17
- try {
18
- return Response.json(
19
- {
20
- code: 0,
21
- msg: `Befly 接口服务已启动`,
22
- data: {
23
- mode: process.env.NODE_ENV || 'development',
24
- timestamp: Date.now()
25
- }
26
- },
27
- {
28
- headers: corsHeaders
29
- }
30
- );
31
- } catch (error: any) {
32
- // 记录详细的错误日志
33
- Logger.error('根路径处理失败', error);
34
-
35
- // 根据错误类型返回不同的错误信息
36
- let errorMessage = '服务异常';
37
- let errorDetail = {};
38
-
39
- // 开发环境返回详细错误信息
40
- if (process.env.NODE_ENV === 'development') {
41
- errorDetail = {
42
- type: error.constructor?.name || 'Error',
43
- message: error.message,
44
- stack: error.stack
45
- };
46
- }
47
-
48
- return Response.json(
49
- { code: 1, msg: errorMessage, data: errorDetail },
50
- {
51
- status: 500,
52
- headers: corsHeaders
53
- }
54
- );
55
- }
56
- }
@@ -1,164 +0,0 @@
1
- /**
2
- * syncDb 主入口文件
3
- *
4
- * 功能:
5
- * - 协调所有模块,执行数据库表结构同步
6
- * - 处理核心表、项目表、addon 表
7
- * - 提供统计信息和错误处理
8
- */
9
-
10
- import { basename, resolve } from 'pathe';
11
- import { existsSync } from 'node:fs';
12
- import { snakeCase } from 'es-toolkit/string';
13
- import { Database } from '../../lib/database.js';
14
- import { RedisHelper } from '../../lib/redisHelper.js';
15
- import { checkTable } from '../../checks/checkTable.js';
16
- import { scanFiles, scanAddons, addonDirExists, getAddonDir } from 'befly-util';
17
- import { Logger } from '../../lib/logger.js';
18
- import { projectDir } from '../../paths.js';
19
-
20
- // 导入模块化的功能
21
- import { ensureDbVersion } from './version.js';
22
- import { tableExists } from './schema.js';
23
- import { modifyTable } from './table.js';
24
- import { createTable } from './tableCreate.js';
25
- import { applyFieldDefaults } from './helpers.js';
26
- import type { SQL } from 'bun';
27
- import type { BeflyOptions, SyncDbOptions } from '../../types/index.js';
28
-
29
- // 全局 SQL 客户端实例
30
- let sql: SQL | null = null;
31
-
32
- // 记录处理过的表名(用于清理缓存)
33
- const processedTables: string[] = [];
34
-
35
- /**
36
- * 主同步函数
37
- *
38
- * 流程:
39
- * 1. 验证表定义文件
40
- * 2. 建立数据库连接并检查版本
41
- * 3. 扫描表定义文件(核心表、项目表、addon表)
42
- * 4. 对比并应用表结构变更
43
- */
44
- export const SyncDb = async (config: BeflyOptions, options: SyncDbOptions = {}): Promise<void> => {
45
- try {
46
- // 清空处理记录
47
- processedTables.length = 0;
48
-
49
- // 验证表定义文件
50
- await checkTable();
51
-
52
- // 建立数据库连接并检查版本
53
- sql = await Database.connectSql({ max: 1 });
54
- await ensureDbVersion(sql);
55
-
56
- // 初始化 Redis 连接(用于清理缓存)
57
- await Database.connectRedis();
58
-
59
- // 扫描表定义文件
60
- const directories: Array<{ path: string; type: 'app' | 'addon'; addonName?: string; addonNameSnake?: string }> = [];
61
-
62
- // 1. 项目表(无前缀)- 如果 tables 目录存在
63
- const projectTablesDir = resolve(projectDir, 'tables');
64
- if (existsSync(projectTablesDir)) {
65
- directories.push({ path: projectTablesDir, type: 'app' });
66
- }
67
-
68
- // 添加所有 addon 的 tables 目录(addon_{name}_ 前缀)
69
- const addons = scanAddons();
70
- for (const addon of addons) {
71
- if (addonDirExists(addon, 'tables')) {
72
- directories.push({
73
- path: getAddonDir(addon, 'tables'),
74
- type: 'addon',
75
- addonName: addon,
76
- addonNameSnake: snakeCase(addon) // 提前转换,避免每个文件都转换
77
- });
78
- }
79
- }
80
-
81
- // 处理表文件
82
- for (const dirConfig of directories) {
83
- const { path: dir, type, addonName } = dirConfig;
84
- const dirType = type === 'addon' ? `组件${addonName}` : '项目';
85
-
86
- const files = await scanFiles(dir, '*.json');
87
-
88
- for (const { filePath: file, fileName } of files) {
89
- // 确定表名:
90
- // - addon 表:{addonName}_{表名}
91
- // 例如:admin addon 的 user.json → admin_user
92
- // - 项目表:{表名}
93
- // 例如:user.json → user
94
- let tableName = snakeCase(fileName);
95
- if (type === 'addon' && dirConfig.addonNameSnake) {
96
- // addon 表,使用提前转换好的名称
97
- tableName = `addon_${dirConfig.addonNameSnake}_${tableName}`;
98
- }
99
-
100
- // 如果指定了表名,则只同步该表
101
- if (options.table && options.table !== tableName) {
102
- continue;
103
- }
104
-
105
- const tableDefinitionModule = await import(file, { with: { type: 'json' } });
106
- const tableDefinition = tableDefinitionModule.default;
107
-
108
- // 为字段属性设置默认值
109
- for (const fieldDef of Object.values(tableDefinition)) {
110
- applyFieldDefaults(fieldDef);
111
- }
112
-
113
- const dbName = config.plugins?.db?.database;
114
- const existsTable = await tableExists(sql!, tableName, dbName);
115
-
116
- // 读取 force 参数
117
- const force = options.force || false;
118
-
119
- if (existsTable) {
120
- await modifyTable(sql!, tableName, tableDefinition, force, dbName);
121
- } else {
122
- await createTable(sql!, tableName, tableDefinition);
123
- }
124
-
125
- // 记录处理过的表名(用于清理缓存)
126
- processedTables.push(tableName);
127
- }
128
- }
129
-
130
- // 清理 Redis 缓存(如果有表被处理)
131
- if (processedTables.length > 0) {
132
- Logger.debug(`🧹 清理 ${processedTables.length} 个表的字段缓存...`);
133
-
134
- const redisHelper = new RedisHelper();
135
- for (const tableName of processedTables) {
136
- const cacheKey = `table:columns:${tableName}`;
137
- try {
138
- await redisHelper.del(cacheKey);
139
- } catch (error: any) {
140
- Logger.warn(`清理表 ${tableName} 的缓存失败: ${error.message}`);
141
- }
142
- }
143
-
144
- Logger.debug(`✓ 已清理表字段缓存`);
145
- }
146
- } catch (error: any) {
147
- Logger.error(`数据库同步失败`, error);
148
- throw error;
149
- } finally {
150
- if (sql) {
151
- try {
152
- await Database.disconnectSql();
153
- } catch (error: any) {
154
- Logger.warn(`关闭数据库连接时出错: ${error.message}`);
155
- }
156
- }
157
-
158
- try {
159
- await Database.disconnectRedis();
160
- } catch (error: any) {
161
- Logger.warn(`关闭 Redis 连接时出错: ${error.message}`);
162
- }
163
- }
164
- };