befly 2.0.14 → 2.1.1
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 +1 -0
- package/checks/table.js +106 -81
- package/config/env.js +1 -0
- package/main.js +43 -60
- package/package.json +10 -13
- package/plugins/db.js +137 -178
- package/scripts/syncDb.js +367 -0
- package/tables/common.json +14 -14
- package/tables/tool.json +4 -4
- package/utils/util.js +117 -19
- package/utils/validate.js +18 -119
- package/USEAGE.md +0 -5
- package/bin/befly.js +0 -176
- package/scripts/dbSync.js +0 -714
- package/scripts/release.js +0 -258
package/.gitignore
CHANGED
package/checks/table.js
CHANGED
|
@@ -1,13 +1,13 @@
|
|
|
1
1
|
import path from 'node:path';
|
|
2
|
-
import { Logger } from '../utils/
|
|
3
|
-
import { parseFieldRule } from '../utils/util.js';
|
|
2
|
+
import { Logger } from '../utils/logger.js';
|
|
3
|
+
import { parseFieldRule, validateFieldName, validateFieldType, validateMinMax, validateDefaultValue, validateIndex, validateRegex } from '../utils/util.js';
|
|
4
4
|
import { __dirtables, getProjectDir } from '../system.js';
|
|
5
5
|
|
|
6
|
+
// 所有校验函数均复用 utils/util.js 导出的实现
|
|
7
|
+
|
|
6
8
|
export default async () => {
|
|
7
9
|
try {
|
|
8
10
|
const tablesGlob = new Bun.Glob('*.json');
|
|
9
|
-
const coreTablesDir = __dirtables;
|
|
10
|
-
const userTablesDir = getProjectDir('tables');
|
|
11
11
|
|
|
12
12
|
// 统计信息
|
|
13
13
|
let totalFiles = 0;
|
|
@@ -15,18 +15,55 @@ export default async () => {
|
|
|
15
15
|
let validFiles = 0;
|
|
16
16
|
let invalidFiles = 0;
|
|
17
17
|
|
|
18
|
-
|
|
18
|
+
// 收集所有表文件
|
|
19
|
+
const allTableFiles = [];
|
|
20
|
+
const coreTableNames = new Set(); // 存储内核表文件名
|
|
21
|
+
|
|
22
|
+
// 收集内核表字段定义文件
|
|
23
|
+
for await (const file of tablesGlob.scan({
|
|
24
|
+
cwd: __dirtables,
|
|
25
|
+
absolute: true,
|
|
26
|
+
onlyFiles: true
|
|
27
|
+
})) {
|
|
28
|
+
const fileName = path.basename(file, '.json');
|
|
29
|
+
coreTableNames.add(fileName);
|
|
30
|
+
allTableFiles.push({ file, type: 'core' });
|
|
31
|
+
}
|
|
32
|
+
|
|
33
|
+
// 收集项目表字段定义文件,并检查是否与内核表同名
|
|
34
|
+
for await (const file of tablesGlob.scan({
|
|
35
|
+
cwd: getProjectDir('tables'),
|
|
36
|
+
absolute: true,
|
|
37
|
+
onlyFiles: true
|
|
38
|
+
})) {
|
|
39
|
+
const fileName = path.basename(file, '.json');
|
|
40
|
+
|
|
41
|
+
// 检查项目表是否与内核表同名
|
|
42
|
+
if (coreTableNames.has(fileName)) {
|
|
43
|
+
Logger.error(`项目表 ${fileName}.json 与内核表同名,项目表不能与内核表定义文件同名`);
|
|
44
|
+
invalidFiles++;
|
|
45
|
+
totalFiles++;
|
|
46
|
+
continue;
|
|
47
|
+
}
|
|
48
|
+
|
|
49
|
+
allTableFiles.push({ file, type: 'project' });
|
|
50
|
+
}
|
|
51
|
+
|
|
52
|
+
// 保留字段列表
|
|
53
|
+
const reservedFields = ['id', 'created_at', 'updated_at', 'deleted_at', 'state'];
|
|
54
|
+
|
|
55
|
+
// 合并进行验证逻辑
|
|
56
|
+
for (const { file, type } of allTableFiles) {
|
|
19
57
|
totalFiles++;
|
|
20
58
|
const fileName = path.basename(file);
|
|
59
|
+
const fileType = type === 'core' ? '内核' : '项目';
|
|
60
|
+
|
|
21
61
|
try {
|
|
22
62
|
// 读取并解析 JSON 文件
|
|
23
63
|
const table = await Bun.file(file).json();
|
|
24
64
|
let fileValid = true;
|
|
25
65
|
let fileRules = 0;
|
|
26
66
|
|
|
27
|
-
// 保留字段列表
|
|
28
|
-
const reservedFields = ['id', 'created_at', 'updated_at', 'deleted_at', 'state'];
|
|
29
|
-
|
|
30
67
|
// 检查 table 中的每个验证规则
|
|
31
68
|
for (const [fieldName, rule] of Object.entries(table)) {
|
|
32
69
|
fileRules++;
|
|
@@ -34,80 +71,84 @@ export default async () => {
|
|
|
34
71
|
|
|
35
72
|
// 检查是否使用了保留字段
|
|
36
73
|
if (reservedFields.includes(fieldName)) {
|
|
37
|
-
Logger.error(`${fileName} 文件包含保留字段 ${fieldName},不能在表定义中使用以下字段: ${reservedFields.join(', ')}`);
|
|
74
|
+
Logger.error(`${fileType}表 ${fileName} 文件包含保留字段 ${fieldName},不能在表定义中使用以下字段: ${reservedFields.join(', ')}`);
|
|
38
75
|
fileValid = false;
|
|
39
76
|
continue;
|
|
40
77
|
}
|
|
41
78
|
|
|
42
79
|
// 验证规则格式
|
|
43
|
-
|
|
44
|
-
|
|
45
|
-
if (ruleParts.length !== 7) {
|
|
46
|
-
Logger.warn(`${fileName} 文件 ${fieldName} 验证规则错误,应包含 7 个部分,但包含 ${ruleParts.length} 个部分`);
|
|
47
|
-
fileValid = false;
|
|
48
|
-
continue;
|
|
49
|
-
}
|
|
80
|
+
try {
|
|
81
|
+
const ruleParts = parseFieldRule(rule);
|
|
50
82
|
|
|
51
|
-
|
|
52
|
-
|
|
53
|
-
|
|
54
|
-
|
|
55
|
-
|
|
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
|
-
}
|
|
83
|
+
if (ruleParts.length !== 7) {
|
|
84
|
+
Logger.warn(`${fileType}表 ${fileName} 文件 ${fieldName} 验证规则错误,应包含 7 个部分,但包含 ${ruleParts.length} 个部分`);
|
|
85
|
+
fileValid = false;
|
|
86
|
+
continue;
|
|
87
|
+
}
|
|
67
88
|
|
|
68
|
-
|
|
69
|
-
Logger.warn(`${fileName} 文件 ${fieldName} 最大值 ${maxStr} 应为数字或 null`);
|
|
70
|
-
fileValid = false;
|
|
71
|
-
continue;
|
|
72
|
-
}
|
|
89
|
+
const [name, type, minStr, maxStr, defaultValue, isIndexStr, regexConstraint] = ruleParts;
|
|
73
90
|
|
|
74
|
-
|
|
75
|
-
|
|
76
|
-
|
|
77
|
-
|
|
78
|
-
|
|
79
|
-
|
|
91
|
+
// 使用新的验证函数进行严格验证
|
|
92
|
+
// 第1个值:名称必须为中文、数字、字母
|
|
93
|
+
if (!validateFieldName(name)) {
|
|
94
|
+
Logger.error(`${fileType}表 ${fileName} 文件 ${fieldName} 名称 "${name}" 格式错误,必须为中文、数字、字母`);
|
|
95
|
+
fileValid = false;
|
|
96
|
+
continue;
|
|
97
|
+
}
|
|
80
98
|
|
|
81
|
-
|
|
82
|
-
|
|
83
|
-
|
|
84
|
-
|
|
85
|
-
|
|
86
|
-
|
|
99
|
+
// 第2个值:字段类型必须为string,number,text,array之一
|
|
100
|
+
if (!validateFieldType(type)) {
|
|
101
|
+
Logger.error(`${fileType}表 ${fileName} 文件 ${fieldName} 类型 "${type}" 格式错误,必须为string、number、text、array之一`);
|
|
102
|
+
fileValid = false;
|
|
103
|
+
continue;
|
|
104
|
+
}
|
|
87
105
|
|
|
88
|
-
|
|
89
|
-
|
|
90
|
-
|
|
91
|
-
|
|
92
|
-
|
|
106
|
+
// 第3个值:最小值必须为null或数字
|
|
107
|
+
if (!validateMinMax(minStr)) {
|
|
108
|
+
Logger.error(`${fileType}表 ${fileName} 文件 ${fieldName} 最小值 "${minStr}" 格式错误,必须为null或数字`);
|
|
109
|
+
fileValid = false;
|
|
110
|
+
continue;
|
|
111
|
+
}
|
|
93
112
|
|
|
94
|
-
|
|
95
|
-
|
|
96
|
-
if (
|
|
97
|
-
Logger.error(`${fileName} 文件 ${fieldName}
|
|
113
|
+
// 第4个值:当类型为 string/array 时,最大长度必须为数字且不可为 null;其他类型允许为 null 或数字
|
|
114
|
+
if (type === 'string' || type === 'array') {
|
|
115
|
+
if (maxStr === 'null' || !validateMinMax(maxStr)) {
|
|
116
|
+
Logger.error(`${fileType}表 ${fileName} 文件 ${fieldName} 最大长度 "${maxStr}" 格式错误,string/array 类型必须为具体数字`);
|
|
98
117
|
fileValid = false;
|
|
99
118
|
continue;
|
|
100
119
|
}
|
|
101
|
-
} else
|
|
102
|
-
|
|
103
|
-
|
|
104
|
-
new RegExp(regexConstraint);
|
|
105
|
-
} catch (e) {
|
|
106
|
-
Logger.error(`${fileName} 文件 ${fieldName} 正则表达式 ${regexConstraint} 无效: ${e.message}`);
|
|
120
|
+
} else {
|
|
121
|
+
if (!validateMinMax(maxStr)) {
|
|
122
|
+
Logger.error(`${fileType}表 ${fileName} 文件 ${fieldName} 最大值 "${maxStr}" 格式错误,必须为null或数字`);
|
|
107
123
|
fileValid = false;
|
|
108
124
|
continue;
|
|
109
125
|
}
|
|
110
126
|
}
|
|
127
|
+
|
|
128
|
+
// 第5个值:默认值必须为null、字符串或数字
|
|
129
|
+
if (!validateDefaultValue(defaultValue)) {
|
|
130
|
+
Logger.error(`${fileType}表 ${fileName} 文件 ${fieldName} 默认值 "${defaultValue}" 格式错误,必须为null、字符串或数字`);
|
|
131
|
+
fileValid = false;
|
|
132
|
+
continue;
|
|
133
|
+
}
|
|
134
|
+
|
|
135
|
+
// 第6个值:是否创建索引必须为0或1
|
|
136
|
+
if (!validateIndex(isIndexStr)) {
|
|
137
|
+
Logger.error(`${fileType}表 ${fileName} 文件 ${fieldName} 索引标识 "${isIndexStr}" 格式错误,必须为0或1`);
|
|
138
|
+
fileValid = false;
|
|
139
|
+
continue;
|
|
140
|
+
}
|
|
141
|
+
|
|
142
|
+
// 第7个值:必须为null或正则表达式
|
|
143
|
+
if (!validateRegex(regexConstraint)) {
|
|
144
|
+
Logger.error(`${fileType}表 ${fileName} 文件 ${fieldName} 正则约束 "${regexConstraint}" 格式错误,必须为null或有效的正则表达式`);
|
|
145
|
+
fileValid = false;
|
|
146
|
+
continue;
|
|
147
|
+
}
|
|
148
|
+
} catch (error) {
|
|
149
|
+
Logger.error(`${fileType}表 ${fileName} 文件 ${fieldName} 验证规则解析失败: ${error.message}`);
|
|
150
|
+
fileValid = false;
|
|
151
|
+
continue;
|
|
111
152
|
}
|
|
112
153
|
}
|
|
113
154
|
|
|
@@ -117,25 +158,9 @@ export default async () => {
|
|
|
117
158
|
invalidFiles++;
|
|
118
159
|
}
|
|
119
160
|
} catch (error) {
|
|
120
|
-
Logger.error(
|
|
161
|
+
Logger.error(`${fileType}表 ${fileName} 解析失败: ${error.message}`);
|
|
121
162
|
invalidFiles++;
|
|
122
163
|
}
|
|
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
164
|
}
|
|
140
165
|
|
|
141
166
|
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 {
|
|
12
|
-
|
|
13
|
-
|
|
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:
|
|
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 = (
|
|
51
|
+
const singleCheckTime = calculateElapsedTime(singleCheckStart);
|
|
67
52
|
|
|
68
53
|
if (checkResult === true) {
|
|
69
54
|
passedChecks++;
|
|
70
|
-
Logger.info(`检查 ${fileName} 通过,耗时: ${singleCheckTime
|
|
55
|
+
Logger.info(`检查 ${fileName} 通过,耗时: ${singleCheckTime}`);
|
|
71
56
|
} else {
|
|
72
|
-
Logger.error(`检查未通过: ${fileName},耗时: ${singleCheckTime
|
|
57
|
+
Logger.error(`检查未通过: ${fileName},耗时: ${singleCheckTime}`);
|
|
73
58
|
failedChecks++;
|
|
74
59
|
}
|
|
75
60
|
} else {
|
|
76
|
-
const singleCheckTime = (
|
|
77
|
-
Logger.warn(`文件 ${fileName} 未导出默认函数,耗时: ${singleCheckTime
|
|
61
|
+
const singleCheckTime = calculateElapsedTime(singleCheckStart);
|
|
62
|
+
Logger.warn(`文件 ${fileName} 未导出默认函数,耗时: ${singleCheckTime}`);
|
|
78
63
|
failedChecks++;
|
|
79
64
|
}
|
|
80
65
|
} catch (error) {
|
|
81
|
-
const singleCheckTime = (
|
|
66
|
+
const singleCheckTime = calculateElapsedTime(singleCheckStart);
|
|
82
67
|
Logger.error({
|
|
83
|
-
msg: `检查失败 ${fileName},耗时: ${singleCheckTime
|
|
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 = (
|
|
76
|
+
const totalCheckTime = calculateElapsedTime(checkStartTime);
|
|
92
77
|
|
|
93
78
|
// 输出检查结果统计
|
|
94
|
-
Logger.info(`系统检查完成! 总耗时: ${totalCheckTime
|
|
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:
|
|
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 = (
|
|
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
|
|
126
|
+
Logger.info(`核心插件 ${fileName} 导入耗时: ${importTime}`);
|
|
142
127
|
}
|
|
143
|
-
const corePluginsScanTime = (
|
|
144
|
-
Logger.info(`核心插件扫描完成,耗时: ${corePluginsScanTime
|
|
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 = (
|
|
163
|
-
Logger.info(`核心插件初始化完成,耗时: ${corePluginsInitTime
|
|
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:
|
|
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 = (
|
|
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
|
|
174
|
+
Logger.info(`用户插件 ${fileName} 导入耗时: ${importTime}`);
|
|
190
175
|
}
|
|
191
|
-
const userPluginsScanTime = (
|
|
192
|
-
Logger.info(`用户插件扫描完成,耗时: ${userPluginsScanTime
|
|
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 = (
|
|
212
|
-
Logger.info(`用户插件初始化完成,耗时: ${userPluginsInitTime
|
|
196
|
+
const userPluginsInitTime = calculateElapsedTime(userPluginsInitStart);
|
|
197
|
+
Logger.info(`用户插件初始化完成,耗时: ${userPluginsInitTime}`);
|
|
213
198
|
}
|
|
214
199
|
|
|
215
|
-
const totalLoadTime = (
|
|
200
|
+
const totalLoadTime = calculateElapsedTime(loadStartTime);
|
|
216
201
|
const totalPluginCount = sortedCorePlugins.length + sortedUserPlugins.length;
|
|
217
|
-
Logger.info(`插件加载完成! 总耗时: ${totalLoadTime
|
|
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' ?
|
|
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 = (
|
|
260
|
+
const singleApiTime = calculateElapsedTime(singleApiStart);
|
|
278
261
|
loadedApis++;
|
|
279
|
-
// Logger.info(`${dirDisplayName}接口 ${apiPath} 加载成功,耗时: ${singleApiTime
|
|
262
|
+
// Logger.info(`${dirDisplayName}接口 ${apiPath} 加载成功,耗时: ${singleApiTime}`);
|
|
280
263
|
} catch (error) {
|
|
281
|
-
const singleApiTime = (
|
|
264
|
+
const singleApiTime = calculateElapsedTime(singleApiStart);
|
|
282
265
|
failedApis++;
|
|
283
266
|
Logger.error({
|
|
284
|
-
msg: `${dirDisplayName}接口 ${apiPath} 加载失败,耗时: ${singleApiTime
|
|
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 = (
|
|
292
|
-
Logger.info(`${dirDisplayName}接口加载完成! 总耗时: ${totalLoadTime
|
|
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 = (
|
|
315
|
-
Logger.info(`服务器启动准备完成,总耗时: ${totalStartupTime
|
|
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
|
|
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(
|
|
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 = (
|
|
549
|
-
Logger.info(`Befly 服务器启动成功! 完整启动耗时: ${finalStartupTime
|
|
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.
|
|
3
|
+
"version": "2.1.1",
|
|
4
4
|
"description": "Buma - 为 Bun 专属打造的 API 接口框架核心引擎",
|
|
5
5
|
"type": "module",
|
|
6
6
|
"private": false,
|
|
@@ -9,19 +9,19 @@
|
|
|
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
|
-
"
|
|
20
|
-
"
|
|
21
|
-
"
|
|
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:
|
|
20
|
+
"test:unit": "bun test tests",
|
|
21
|
+
"test:jwt": "bun test tests/jwt.test.js",
|
|
22
|
+
"dev": "bun run project/main.js",
|
|
23
|
+
"server": "bunx --bun pm2 start pm2.config.cjs -a",
|
|
24
|
+
"syncDb": "bun run scripts/syncDb.js"
|
|
25
25
|
},
|
|
26
26
|
"keywords": [
|
|
27
27
|
"bun",
|
|
@@ -38,7 +38,6 @@
|
|
|
38
38
|
"license": "Apache-2.0",
|
|
39
39
|
"files": [
|
|
40
40
|
"apis/",
|
|
41
|
-
"bin/",
|
|
42
41
|
"checks/",
|
|
43
42
|
"config/",
|
|
44
43
|
"libs/",
|
|
@@ -62,9 +61,7 @@
|
|
|
62
61
|
"vitest.config.js"
|
|
63
62
|
],
|
|
64
63
|
"gitHead": "1dc5f118a723969456559e758e2ba889f4601224",
|
|
65
|
-
"dependencies": {
|
|
66
|
-
"mariadb": "^3.4.5"
|
|
67
|
-
},
|
|
64
|
+
"dependencies": {},
|
|
68
65
|
"simple-git-hooks": {
|
|
69
66
|
"pre-commit": "bunx --bun lint-staged"
|
|
70
67
|
},
|