befly 2.0.14 → 2.1.0

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.
package/.gitignore CHANGED
@@ -91,3 +91,4 @@ profile-*
91
91
  profile*
92
92
  *clinic*
93
93
  *flamegraph*
94
+ logs
package/checks/table.js CHANGED
@@ -1,13 +1,53 @@
1
1
  import path from 'node:path';
2
- import { Logger } from '../utils/Logger.js';
2
+ import { Logger } from '../utils/logger.js';
3
3
  import { parseFieldRule } from '../utils/util.js';
4
4
  import { __dirtables, getProjectDir } from '../system.js';
5
5
 
6
+ // 验证字段名称是否为中文、数字、字母
7
+ const validateFieldName = (name) => {
8
+ const nameRegex = /^[\u4e00-\u9fa5a-zA-Z0-9]+$/;
9
+ return nameRegex.test(name);
10
+ };
11
+
12
+ // 验证字段类型是否为指定的四种类型之一
13
+ const validateFieldType = (type) => {
14
+ const validTypes = ['string', 'number', 'text', 'array'];
15
+ return validTypes.includes(type);
16
+ };
17
+
18
+ // 验证最小值/最大值是否为null或数字
19
+ const validateMinMax = (value) => {
20
+ return value === 'null' || (!isNaN(parseFloat(value)) && isFinite(parseFloat(value)));
21
+ };
22
+
23
+ // 验证默认值是否为null、字符串或数字
24
+ const validateDefaultValue = (value) => {
25
+ if (value === 'null') return true;
26
+ // 检查是否为数字
27
+ if (!isNaN(parseFloat(value)) && isFinite(parseFloat(value))) return true;
28
+ // 其他情况视为字符串,都是有效的
29
+ return true;
30
+ };
31
+
32
+ // 验证索引标识是否为0或1
33
+ const validateIndex = (value) => {
34
+ return value === '0' || value === '1';
35
+ };
36
+
37
+ // 验证正则表达式是否有效
38
+ const validateRegex = (value) => {
39
+ if (value === 'null') return true;
40
+ try {
41
+ new RegExp(value);
42
+ return true;
43
+ } catch (e) {
44
+ return false;
45
+ }
46
+ };
47
+
6
48
  export default async () => {
7
49
  try {
8
50
  const tablesGlob = new Bun.Glob('*.json');
9
- const coreTablesDir = __dirtables;
10
- const userTablesDir = getProjectDir('tables');
11
51
 
12
52
  // 统计信息
13
53
  let totalFiles = 0;
@@ -15,18 +55,55 @@ export default async () => {
15
55
  let validFiles = 0;
16
56
  let invalidFiles = 0;
17
57
 
18
- const validateFile = async (file) => {
58
+ // 收集所有表文件
59
+ const allTableFiles = [];
60
+ const coreTableNames = new Set(); // 存储内核表文件名
61
+
62
+ // 收集内核表字段定义文件
63
+ for await (const file of tablesGlob.scan({
64
+ cwd: __dirtables,
65
+ absolute: true,
66
+ onlyFiles: true
67
+ })) {
68
+ const fileName = path.basename(file, '.json');
69
+ coreTableNames.add(fileName);
70
+ allTableFiles.push({ file, type: 'core' });
71
+ }
72
+
73
+ // 收集项目表字段定义文件,并检查是否与内核表同名
74
+ for await (const file of tablesGlob.scan({
75
+ cwd: getProjectDir('tables'),
76
+ absolute: true,
77
+ onlyFiles: true
78
+ })) {
79
+ const fileName = path.basename(file, '.json');
80
+
81
+ // 检查项目表是否与内核表同名
82
+ if (coreTableNames.has(fileName)) {
83
+ Logger.error(`项目表 ${fileName}.json 与内核表同名,项目表不能与内核表定义文件同名`);
84
+ invalidFiles++;
85
+ totalFiles++;
86
+ continue;
87
+ }
88
+
89
+ allTableFiles.push({ file, type: 'project' });
90
+ }
91
+
92
+ // 保留字段列表
93
+ const reservedFields = ['id', 'created_at', 'updated_at', 'deleted_at', 'state'];
94
+
95
+ // 合并进行验证逻辑
96
+ for (const { file, type } of allTableFiles) {
19
97
  totalFiles++;
20
98
  const fileName = path.basename(file);
99
+ const fileType = type === 'core' ? '内核' : '项目';
100
+
21
101
  try {
22
102
  // 读取并解析 JSON 文件
23
103
  const table = await Bun.file(file).json();
24
104
  let fileValid = true;
25
105
  let fileRules = 0;
26
106
 
27
- // 保留字段列表
28
- const reservedFields = ['id', 'created_at', 'updated_at', 'deleted_at', 'state'];
29
-
30
107
  // 检查 table 中的每个验证规则
31
108
  for (const [fieldName, rule] of Object.entries(table)) {
32
109
  fileRules++;
@@ -34,80 +111,84 @@ export default async () => {
34
111
 
35
112
  // 检查是否使用了保留字段
36
113
  if (reservedFields.includes(fieldName)) {
37
- Logger.error(`${fileName} 文件包含保留字段 ${fieldName},不能在表定义中使用以下字段: ${reservedFields.join(', ')}`);
114
+ Logger.error(`${fileType}表 ${fileName} 文件包含保留字段 ${fieldName},不能在表定义中使用以下字段: ${reservedFields.join(', ')}`);
38
115
  fileValid = false;
39
116
  continue;
40
117
  }
41
118
 
42
119
  // 验证规则格式
43
- const ruleParts = parseFieldRule(rule);
44
-
45
- if (ruleParts.length !== 7) {
46
- Logger.warn(`${fileName} 文件 ${fieldName} 验证规则错误,应包含 7 个部分,但包含 ${ruleParts.length} 个部分`);
47
- fileValid = false;
48
- continue;
49
- }
50
-
51
- const [name, type, minStr, maxStr, defaultValue, isIndexStr, regexConstraint] = ruleParts;
120
+ try {
121
+ const ruleParts = parseFieldRule(rule);
52
122
 
53
- // 验证类型(必须严格使用小写类型名称)
54
- const validTypes = ['number', 'string', 'text', 'array'];
55
- if (!validTypes.includes(type)) {
56
- Logger.warn(`${fileName} 文件 ${fieldName} 类型 ${type} 不支持,应为小写的 number、string、text 或 array`);
57
- fileValid = false;
58
- continue;
59
- }
60
-
61
- // 验证最小值/最大值
62
- if (minStr !== 'null' && isNaN(parseInt(minStr))) {
63
- Logger.warn(`${fileName} 文件 ${fieldName} 最小值 ${minStr} 应为数字或 null`);
64
- fileValid = false;
65
- continue;
66
- }
123
+ if (ruleParts.length !== 7) {
124
+ Logger.warn(`${fileType}表 ${fileName} 文件 ${fieldName} 验证规则错误,应包含 7 个部分,但包含 ${ruleParts.length} 个部分`);
125
+ fileValid = false;
126
+ continue;
127
+ }
67
128
 
68
- if (maxStr !== 'null' && isNaN(parseInt(maxStr))) {
69
- Logger.warn(`${fileName} 文件 ${fieldName} 最大值 ${maxStr} 应为数字或 null`);
70
- fileValid = false;
71
- continue;
72
- }
129
+ const [name, type, minStr, maxStr, defaultValue, isIndexStr, regexConstraint] = ruleParts;
73
130
 
74
- // 验证索引字段
75
- if (isIndexStr !== 'null' && !['0', '1'].includes(isIndexStr)) {
76
- Logger.warn(`${fileName} 文件 ${fieldName} 索引标识 ${isIndexStr} 应为 0、1 或 null`);
77
- fileValid = false;
78
- continue;
79
- }
131
+ // 使用新的验证函数进行严格验证
132
+ // 1个值:名称必须为中文、数字、字母
133
+ if (!validateFieldName(name)) {
134
+ Logger.error(`${fileType}表 ${fileName} 文件 ${fieldName} 名称 "${name}" 格式错误,必须为中文、数字、字母`);
135
+ fileValid = false;
136
+ continue;
137
+ }
80
138
 
81
- // 验证正则约束
82
- if (regexConstraint !== 'null') {
83
- if (type === 'number' && regexConstraint.includes('=')) {
84
- // 数字计算表达式应包含安全字符
85
- const safePattern = /^[x\d\+\-\*\/\(\)\.\s\%]+$/;
86
- const expressionPart = regexConstraint.split('=')[0].trim();
139
+ // 第2个值:字段类型必须为string,number,text,array之一
140
+ if (!validateFieldType(type)) {
141
+ Logger.error(`${fileType}表 ${fileName} 文件 ${fieldName} 类型 "${type}" 格式错误,必须为string、number、text、array之一`);
142
+ fileValid = false;
143
+ continue;
144
+ }
87
145
 
88
- if (!safePattern.test(expressionPart)) {
89
- Logger.warn(`${fileName} 文件 ${fieldName} 表达式 ${expressionPart} 包含不安全的字符`);
90
- fileValid = false;
91
- continue;
92
- }
146
+ // 第3个值:最小值必须为null或数字
147
+ if (!validateMinMax(minStr)) {
148
+ Logger.error(`${fileType}表 ${fileName} 文件 ${fieldName} 最小值 "${minStr}" 格式错误,必须为null或数字`);
149
+ fileValid = false;
150
+ continue;
151
+ }
93
152
 
94
- // 验证等号右侧是否为数字
95
- const rightPart = regexConstraint.split('=')[1].trim();
96
- if (isNaN(parseFloat(rightPart))) {
97
- Logger.error(`${fileName} 文件 ${fieldName} 计算规则右边必须是数字,而不是 ${rightPart}`);
153
+ // 第4个值:当类型为 string/array 时,最大长度必须为数字且不可为 null;其他类型允许为 null 或数字
154
+ if (type === 'string' || type === 'array') {
155
+ if (maxStr === 'null' || !validateMinMax(maxStr)) {
156
+ Logger.error(`${fileType}表 ${fileName} 文件 ${fieldName} 最大长度 "${maxStr}" 格式错误,string/array 类型必须为具体数字`);
98
157
  fileValid = false;
99
158
  continue;
100
159
  }
101
- } else if (type === 'string' || type === 'array' || type === 'text') {
102
- // 尝试编译正则表达式以检查是否有效
103
- try {
104
- new RegExp(regexConstraint);
105
- } catch (e) {
106
- Logger.error(`${fileName} 文件 ${fieldName} 正则表达式 ${regexConstraint} 无效: ${e.message}`);
160
+ } else {
161
+ if (!validateMinMax(maxStr)) {
162
+ Logger.error(`${fileType}表 ${fileName} 文件 ${fieldName} 最大值 "${maxStr}" 格式错误,必须为null或数字`);
107
163
  fileValid = false;
108
164
  continue;
109
165
  }
110
166
  }
167
+
168
+ // 第5个值:默认值必须为null、字符串或数字
169
+ if (!validateDefaultValue(defaultValue)) {
170
+ Logger.error(`${fileType}表 ${fileName} 文件 ${fieldName} 默认值 "${defaultValue}" 格式错误,必须为null、字符串或数字`);
171
+ fileValid = false;
172
+ continue;
173
+ }
174
+
175
+ // 第6个值:是否创建索引必须为0或1
176
+ if (!validateIndex(isIndexStr)) {
177
+ Logger.error(`${fileType}表 ${fileName} 文件 ${fieldName} 索引标识 "${isIndexStr}" 格式错误,必须为0或1`);
178
+ fileValid = false;
179
+ continue;
180
+ }
181
+
182
+ // 第7个值:必须为null或正则表达式
183
+ if (!validateRegex(regexConstraint)) {
184
+ Logger.error(`${fileType}表 ${fileName} 文件 ${fieldName} 正则约束 "${regexConstraint}" 格式错误,必须为null或有效的正则表达式`);
185
+ fileValid = false;
186
+ continue;
187
+ }
188
+ } catch (error) {
189
+ Logger.error(`${fileType}表 ${fileName} 文件 ${fieldName} 验证规则解析失败: ${error.message}`);
190
+ fileValid = false;
191
+ continue;
111
192
  }
112
193
  }
113
194
 
@@ -117,25 +198,9 @@ export default async () => {
117
198
  invalidFiles++;
118
199
  }
119
200
  } catch (error) {
120
- Logger.error(`Table ${fileName} 解析失败: ${error.message}`);
201
+ Logger.error(`${fileType}表 ${fileName} 解析失败: ${error.message}`);
121
202
  invalidFiles++;
122
203
  }
123
- };
124
-
125
- for await (const file of tablesGlob.scan({
126
- cwd: coreTablesDir,
127
- absolute: true,
128
- onlyFiles: true
129
- })) {
130
- await validateFile(file);
131
- }
132
-
133
- for await (const file of tablesGlob.scan({
134
- cwd: userTablesDir,
135
- absolute: true,
136
- onlyFiles: true
137
- })) {
138
- await validateFile(file);
139
204
  }
140
205
 
141
206
  if (invalidFiles > 0) {
package/config/env.js CHANGED
@@ -25,6 +25,7 @@ export const Env = {
25
25
  TZ: process.env.TZ,
26
26
  // 数据库配置
27
27
  MYSQL_ENABLE: Number(process.env.MYSQL_ENABLE),
28
+ MYSQL_URL: process.env.MYSQL_URL,
28
29
  MYSQL_HOST: process.env.MYSQL_HOST,
29
30
  MYSQL_PORT: Number(process.env.MYSQL_PORT),
30
31
  MYSQL_DB: process.env.MYSQL_DB,
package/main.js CHANGED
@@ -1,27 +1,14 @@
1
1
  import path from 'node:path';
2
2
  import { Env } from './config/env.js';
3
-
4
- // 工具函数
5
3
  import { Api } from './utils/api.js';
6
4
  import { Logger } from './utils/logger.js';
7
5
  import { Jwt } from './utils/jwt.js';
8
6
  import { validator } from './utils/validate.js';
9
7
  import { Crypto2 } from './utils/crypto.js';
10
8
  import { Xml } from './libs/xml.js';
11
- import { isEmptyObject, isType, pickFields, sortPlugins, RYes, RNo, filename2, dirname2, filterLogFields } from './utils/util.js';
12
-
13
- const setCorsOptions = (req) => {
14
- return {
15
- headers: {
16
- 'Access-Control-Allow-Origin': Env.ALLOWED_ORIGIN || req.headers.get('origin') || '*',
17
- 'Access-Control-Allow-Methods': Env.ALLOWED_METHODS || 'GET, POST, PUT, DELETE, OPTIONS',
18
- 'Access-Control-Allow-Headers': Env.ALLOWED_HEADERS || 'Content-Type, Authorization, authorization, token',
19
- 'Access-Control-Expose-Headers': Env.EXPOSE_HEADERS || 'Content-Range, X-Content-Range, Authorization, authorization, token',
20
- 'Access-Control-Max-Age': Env.MAX_AGE || 86400,
21
- 'Access-Control-Allow-Credentials': Env.ALLOW_CREDENTIALS || 'true'
22
- }
23
- };
24
- };
9
+ import { SyncDb } from './scripts/syncDb.js';
10
+ import { __dirchecks, __dirplugins, __dirapis, getProjectDir } from './system.js';
11
+ import { isEmptyObject, isType, pickFields, sortPlugins, RYes, RNo, filterLogFields, setCorsOptions, calculateElapsedTime } from './utils/util.js';
25
12
 
26
13
  class Befly {
27
14
  constructor(options = {}) {
@@ -34,9 +21,7 @@ class Befly {
34
21
  async initCheck() {
35
22
  try {
36
23
  const checkStartTime = Bun.nanoseconds();
37
- Logger.info('开始执行系统检查...');
38
24
 
39
- const checksDir = path.join(dirname2(import.meta.url), 'checks');
40
25
  const glob = new Bun.Glob('*.js');
41
26
 
42
27
  // 统计信息
@@ -46,7 +31,7 @@ class Befly {
46
31
 
47
32
  // 扫描并执行检查函数
48
33
  for await (const file of glob.scan({
49
- cwd: checksDir,
34
+ cwd: __dirchecks,
50
35
  onlyFiles: true,
51
36
  absolute: true
52
37
  })) {
@@ -63,24 +48,24 @@ class Befly {
63
48
  // 执行默认导出的函数
64
49
  if (typeof check.default === 'function') {
65
50
  const checkResult = await check.default(this.appContext);
66
- const singleCheckTime = (Bun.nanoseconds() - singleCheckStart) / 1_000_000;
51
+ const singleCheckTime = calculateElapsedTime(singleCheckStart);
67
52
 
68
53
  if (checkResult === true) {
69
54
  passedChecks++;
70
- Logger.info(`检查 ${fileName} 通过,耗时: ${singleCheckTime.toFixed(2)}ms`);
55
+ Logger.info(`检查 ${fileName} 通过,耗时: ${singleCheckTime}`);
71
56
  } else {
72
- Logger.error(`检查未通过: ${fileName},耗时: ${singleCheckTime.toFixed(2)}ms`);
57
+ Logger.error(`检查未通过: ${fileName},耗时: ${singleCheckTime}`);
73
58
  failedChecks++;
74
59
  }
75
60
  } else {
76
- const singleCheckTime = (Bun.nanoseconds() - singleCheckStart) / 1_000_000;
77
- Logger.warn(`文件 ${fileName} 未导出默认函数,耗时: ${singleCheckTime.toFixed(2)}ms`);
61
+ const singleCheckTime = calculateElapsedTime(singleCheckStart);
62
+ Logger.warn(`文件 ${fileName} 未导出默认函数,耗时: ${singleCheckTime}`);
78
63
  failedChecks++;
79
64
  }
80
65
  } catch (error) {
81
- const singleCheckTime = (Bun.nanoseconds() - singleCheckStart) / 1_000_000;
66
+ const singleCheckTime = calculateElapsedTime(singleCheckStart);
82
67
  Logger.error({
83
- msg: `检查失败 ${fileName},耗时: ${singleCheckTime.toFixed(2)}ms`,
68
+ msg: `检查失败 ${fileName},耗时: ${singleCheckTime}`,
84
69
  error: error.message,
85
70
  stack: error.stack
86
71
  });
@@ -88,10 +73,10 @@ class Befly {
88
73
  }
89
74
  }
90
75
 
91
- const totalCheckTime = (Bun.nanoseconds() - checkStartTime) / 1_000_000;
76
+ const totalCheckTime = calculateElapsedTime(checkStartTime);
92
77
 
93
78
  // 输出检查结果统计
94
- Logger.info(`系统检查完成! 总耗时: ${totalCheckTime.toFixed(2)}ms,总检查数: ${totalChecks}, 通过: ${passedChecks}, 失败: ${failedChecks}`);
79
+ Logger.info(`系统检查完成! 总耗时: ${totalCheckTime},总检查数: ${totalChecks}, 通过: ${passedChecks}, 失败: ${failedChecks}`);
95
80
 
96
81
  if (failedChecks > 0) {
97
82
  process.exit();
@@ -122,7 +107,7 @@ class Befly {
122
107
  // 扫描核心插件目录
123
108
  const corePluginsScanStart = Bun.nanoseconds();
124
109
  for await (const file of glob.scan({
125
- cwd: path.join(dirname2(import.meta.url), 'plugins'),
110
+ cwd: __dirplugins,
126
111
  onlyFiles: true,
127
112
  absolute: true
128
113
  })) {
@@ -131,17 +116,17 @@ class Befly {
131
116
 
132
117
  const importStart = Bun.nanoseconds();
133
118
  const plugin = await import(file);
134
- const importTime = (Bun.nanoseconds() - importStart) / 1_000_000; // 转换为毫秒
119
+ const importTime = calculateElapsedTime(importStart);
135
120
 
136
121
  const pluginInstance = plugin.default;
137
122
  pluginInstance.pluginName = fileName;
138
123
  corePlugins.push(pluginInstance);
139
124
  loadedPluginNames.add(fileName); // 记录已加载的核心插件名称
140
125
 
141
- Logger.info(`核心插件 ${fileName} 导入耗时: ${importTime.toFixed(2)}ms`);
126
+ Logger.info(`核心插件 ${fileName} 导入耗时: ${importTime}`);
142
127
  }
143
- const corePluginsScanTime = (Bun.nanoseconds() - corePluginsScanStart) / 1_000_000;
144
- Logger.info(`核心插件扫描完成,耗时: ${corePluginsScanTime.toFixed(2)}ms,共找到 ${corePlugins.length} 个插件`);
128
+ const corePluginsScanTime = calculateElapsedTime(corePluginsScanStart);
129
+ Logger.info(`核心插件扫描完成,耗时: ${corePluginsScanTime},共找到 ${corePlugins.length} 个插件`);
145
130
 
146
131
  const sortedCorePlugins = sortPlugins(corePlugins);
147
132
  if (sortedCorePlugins === false) {
@@ -159,13 +144,13 @@ class Befly {
159
144
  Logger.warn(`插件 ${plugin.pluginName} 初始化失败:`, error.message);
160
145
  }
161
146
  }
162
- const corePluginsInitTime = (Bun.nanoseconds() - corePluginsInitStart) / 1_000_000;
163
- Logger.info(`核心插件初始化完成,耗时: ${corePluginsInitTime.toFixed(2)}ms`);
147
+ const corePluginsInitTime = calculateElapsedTime(corePluginsInitStart);
148
+ Logger.info(`核心插件初始化完成,耗时: ${corePluginsInitTime}`);
164
149
 
165
150
  // 扫描用户插件目录
166
151
  const userPluginsScanStart = Bun.nanoseconds();
167
152
  for await (const file of glob.scan({
168
- cwd: path.join(process.cwd(), 'plugins'),
153
+ cwd: getProjectDir('plugins'),
169
154
  onlyFiles: true,
170
155
  absolute: true
171
156
  })) {
@@ -180,16 +165,16 @@ class Befly {
180
165
 
181
166
  const importStart = Bun.nanoseconds();
182
167
  const plugin = await import(file);
183
- const importTime = (Bun.nanoseconds() - importStart) / 1_000_000; // 转换为毫秒
168
+ const importTime = calculateElapsedTime(importStart);
184
169
 
185
170
  const pluginInstance = plugin.default;
186
171
  pluginInstance.pluginName = fileName;
187
172
  userPlugins.push(pluginInstance);
188
173
 
189
- Logger.info(`用户插件 ${fileName} 导入耗时: ${importTime.toFixed(2)}ms`);
174
+ Logger.info(`用户插件 ${fileName} 导入耗时: ${importTime}`);
190
175
  }
191
- const userPluginsScanTime = (Bun.nanoseconds() - userPluginsScanStart) / 1_000_000;
192
- Logger.info(`用户插件扫描完成,耗时: ${userPluginsScanTime.toFixed(2)}ms,共找到 ${userPlugins.length} 个插件`);
176
+ const userPluginsScanTime = calculateElapsedTime(userPluginsScanStart);
177
+ Logger.info(`用户插件扫描完成,耗时: ${userPluginsScanTime},共找到 ${userPlugins.length} 个插件`);
193
178
 
194
179
  const sortedUserPlugins = sortPlugins(userPlugins);
195
180
  if (sortedUserPlugins === false) {
@@ -208,13 +193,13 @@ class Befly {
208
193
  Logger.warn(`插件 ${plugin.pluginName} 初始化失败:`, error.message);
209
194
  }
210
195
  }
211
- const userPluginsInitTime = (Bun.nanoseconds() - userPluginsInitStart) / 1_000_000;
212
- Logger.info(`用户插件初始化完成,耗时: ${userPluginsInitTime.toFixed(2)}ms`);
196
+ const userPluginsInitTime = calculateElapsedTime(userPluginsInitStart);
197
+ Logger.info(`用户插件初始化完成,耗时: ${userPluginsInitTime}`);
213
198
  }
214
199
 
215
- const totalLoadTime = (Bun.nanoseconds() - loadStartTime) / 1_000_000;
200
+ const totalLoadTime = calculateElapsedTime(loadStartTime);
216
201
  const totalPluginCount = sortedCorePlugins.length + sortedUserPlugins.length;
217
- Logger.info(`插件加载完成! 总耗时: ${totalLoadTime.toFixed(2)}ms,共加载 ${totalPluginCount} 个插件`);
202
+ Logger.info(`插件加载完成! 总耗时: ${totalLoadTime},共加载 ${totalPluginCount} 个插件`);
218
203
  } catch (error) {
219
204
  Logger.error({
220
205
  msg: '加载插件时发生错误',
@@ -228,10 +213,8 @@ class Befly {
228
213
  const loadStartTime = Bun.nanoseconds();
229
214
  const dirDisplayName = dirName === 'core' ? '核心' : '用户';
230
215
 
231
- const coreApisDir = path.join(dirname2(import.meta.url), 'apis');
232
- const userApisDir = path.join(process.cwd(), 'apis');
233
216
  const glob = new Bun.Glob('**/*.js');
234
- const apiDir = dirName === 'core' ? coreApisDir : userApisDir;
217
+ const apiDir = dirName === 'core' ? __dirapis : getProjectDir('apis');
235
218
 
236
219
  let totalApis = 0;
237
220
  let loadedApis = 0;
@@ -274,22 +257,22 @@ class Befly {
274
257
  api.route = `${api.method.toUpperCase()}/api/${dirName}/${apiPath}`;
275
258
  this.apiRoutes.set(api.route, api);
276
259
 
277
- const singleApiTime = (Bun.nanoseconds() - singleApiStart) / 1_000_000;
260
+ const singleApiTime = calculateElapsedTime(singleApiStart);
278
261
  loadedApis++;
279
- // Logger.info(`${dirDisplayName}接口 ${apiPath} 加载成功,耗时: ${singleApiTime.toFixed(2)}ms`);
262
+ // Logger.info(`${dirDisplayName}接口 ${apiPath} 加载成功,耗时: ${singleApiTime}`);
280
263
  } catch (error) {
281
- const singleApiTime = (Bun.nanoseconds() - singleApiStart) / 1_000_000;
264
+ const singleApiTime = calculateElapsedTime(singleApiStart);
282
265
  failedApis++;
283
266
  Logger.error({
284
- msg: `${dirDisplayName}接口 ${apiPath} 加载失败,耗时: ${singleApiTime.toFixed(2)}ms`,
267
+ msg: `${dirDisplayName}接口 ${apiPath} 加载失败,耗时: ${singleApiTime}`,
285
268
  error: error.message,
286
269
  stack: error.stack
287
270
  });
288
271
  }
289
272
  }
290
273
 
291
- const totalLoadTime = (Bun.nanoseconds() - loadStartTime) / 1_000_000;
292
- Logger.info(`${dirDisplayName}接口加载完成! 总耗时: ${totalLoadTime.toFixed(2)}ms,总数: ${totalApis}, 成功: ${loadedApis}, 失败: ${failedApis}`);
274
+ const totalLoadTime = calculateElapsedTime(loadStartTime);
275
+ Logger.info(`${dirDisplayName}接口加载完成! 总耗时: ${totalLoadTime},总数: ${totalApis}, 成功: ${loadedApis}, 失败: ${failedApis}`);
293
276
  } catch (error) {
294
277
  Logger.error({
295
278
  msg: '加载接口时发生错误',
@@ -311,8 +294,8 @@ class Befly {
311
294
  await this.loadApis('core');
312
295
  await this.loadApis('app');
313
296
 
314
- const totalStartupTime = (Bun.nanoseconds() - serverStartTime) / 1_000_000;
315
- Logger.info(`服务器启动准备完成,总耗时: ${totalStartupTime.toFixed(2)}ms`);
297
+ const totalStartupTime = calculateElapsedTime(serverStartTime);
298
+ Logger.info(`服务器启动准备完成,总耗时: ${totalStartupTime}`);
316
299
 
317
300
  const server = Bun.serve({
318
301
  port: Env.APP_PORT,
@@ -399,7 +382,7 @@ class Befly {
399
382
  } else if (contentType.indexOf('form-data') !== -1) {
400
383
  ctx.body = await req.formData();
401
384
  } else if (contentType.indexOf('x-www-form-urlencoded') !== -1) {
402
- const text = await clonedReq.text();
385
+ const text = await req.text();
403
386
  const formData = new URLSearchParams(text);
404
387
  ctx.body = Object.fromEntries(formData);
405
388
  } else {
@@ -511,7 +494,7 @@ class Befly {
511
494
  }
512
495
 
513
496
  const url = new URL(req.url);
514
- const filePath = path.join(process.cwd(), 'public', url.pathname);
497
+ const filePath = path.join(getProjectDir('public'), url.pathname);
515
498
 
516
499
  try {
517
500
  const file = await Bun.file(filePath);
@@ -545,8 +528,8 @@ class Befly {
545
528
  }
546
529
  });
547
530
 
548
- const finalStartupTime = (Bun.nanoseconds() - serverStartTime) / 1_000_000;
549
- Logger.info(`Befly 服务器启动成功! 完整启动耗时: ${finalStartupTime.toFixed(2)}ms`);
531
+ const finalStartupTime = calculateElapsedTime(serverStartTime);
532
+ Logger.info(`Befly 服务器启动成功! 完整启动耗时: ${finalStartupTime}`);
550
533
  Logger.info(`服务器监听地址: http://${Env.APP_HOST}:${Env.APP_PORT}`);
551
534
 
552
535
  if (callback && typeof callback === 'function') {
@@ -555,4 +538,4 @@ class Befly {
555
538
  }
556
539
  }
557
540
 
558
- export { Befly, Env, Api, Jwt, Crypto2, Logger, RYes, RNo };
541
+ export { Befly, Env, Api, Jwt, Crypto2, Logger, RYes, RNo, SyncDb };
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "befly",
3
- "version": "2.0.14",
3
+ "version": "2.1.0",
4
4
  "description": "Buma - 为 Bun 专属打造的 API 接口框架核心引擎",
5
5
  "type": "module",
6
6
  "private": false,
@@ -9,19 +9,18 @@
9
9
  "registry": "https://registry.npmjs.org"
10
10
  },
11
11
  "main": "main.js",
12
- "bin": {
13
- "befly": "./bin/befly.js"
14
- },
15
12
  "exports": {
16
13
  ".": "./main.js"
17
14
  },
18
15
  "scripts": {
19
- "r": "bun publish --registry=https://registry.npmjs.org --access=public",
20
- "ra": "bun run scripts/release.js -a",
21
- "rb": "bun run scripts/release.js -b",
22
- "rc": "bun run scripts/release.js -c",
16
+ "ra": "bun run release.js -a",
17
+ "rb": "bun run release.js -b",
18
+ "rc": "bun run release.js -c",
23
19
  "test": "bun test",
24
- "test:jwt": "bun test tests/jwt.test.js"
20
+ "test:jwt": "bun test tests/jwt.test.js",
21
+ "dev": "bun run project/main.js",
22
+ "server": "bunx --bun pm2 start pm2.config.cjs -a",
23
+ "syncDb": "bun run scripts/syncDb.js"
25
24
  },
26
25
  "keywords": [
27
26
  "bun",
@@ -38,7 +37,6 @@
38
37
  "license": "Apache-2.0",
39
38
  "files": [
40
39
  "apis/",
41
- "bin/",
42
40
  "checks/",
43
41
  "config/",
44
42
  "libs/",
@@ -62,9 +60,7 @@
62
60
  "vitest.config.js"
63
61
  ],
64
62
  "gitHead": "1dc5f118a723969456559e758e2ba889f4601224",
65
- "dependencies": {
66
- "mariadb": "^3.4.5"
67
- },
63
+ "dependencies": {},
68
64
  "simple-git-hooks": {
69
65
  "pre-commit": "bunx --bun lint-staged"
70
66
  },