befly 3.74.2 → 3.75.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/apis/dashboard/systemResources.js +3 -1
- package/apis/dict/_dict.js +1 -1
- package/apis/role/_role.js +2 -2
- package/apis/tongJi/dailyStats.js +2 -2
- package/apis/tongJi/dailyStatsDistribution.js +3 -3
- package/apis/tongJi/errorList.js +2 -2
- package/apis/tongJi/errorStats.js +6 -6
- package/checks/api.js +11 -6
- package/checks/items.js +56 -0
- package/checks/menu.js +1 -1
- package/checks/table.js +4 -5
- package/configs/beflyConfig.json +5 -10
- package/configs/constConfig.js +44 -3
- package/hooks/auth.js +3 -2
- package/hooks/parser.js +15 -4
- package/hooks/permission.js +3 -2
- package/hooks/rateLimit.js +16 -3
- package/hooks/validator.js +2 -1
- package/index.js +53 -24
- package/lib/cacheHelper.js +83 -229
- package/lib/connect.js +3 -13
- package/lib/dbHelper.js +57 -142
- package/lib/dbParse.js +65 -248
- package/lib/dbUtil.js +19 -63
- package/lib/logger.js +45 -22
- package/lib/redisHelper.js +186 -420
- package/lib/smtpText.js +7 -18
- package/lib/sqlBuilder.js +22 -51
- package/lib/xmlParse.js +15 -59
- package/package.json +1 -1
- package/plugins/cache.js +2 -3
- package/plugins/config.js +3 -1
- package/plugins/cron.js +2 -2
- package/plugins/email.js +3 -1
- package/plugins/logger.js +2 -2
- package/plugins/mysql.js +5 -10
- package/plugins/redis.js +4 -11
- package/plugins/tool.js +3 -3
- package/plugins/xmlParse.js +3 -1
- package/router/api.js +2 -6
- package/router/static.js +9 -10
- package/schemas/config.js +4 -4
- package/scripts/syncDb/context.js +4 -7
- package/scripts/syncDb/diff.js +2 -1
- package/scripts/syncDb/index.js +2 -6
- package/sync/api.js +7 -8
- package/sync/cache.js +3 -1
- package/sync/dev.js +28 -65
- package/sync/menu.js +5 -6
- package/sync/syncUtil.js +25 -0
- package/utils/calcPerfTime.js +2 -3
- package/utils/crypto.js +0 -40
- package/utils/deepMerge.js +2 -6
- package/utils/error.js +64 -0
- package/utils/fieldClear.js +13 -56
- package/utils/importDefault.js +11 -12
- package/utils/is.js +5 -182
- package/utils/loggerUtils.js +3 -1
- package/utils/response.js +12 -8
- package/utils/scanFiles.js +2 -53
- package/utils/scanSources.js +9 -0
- package/utils/util.js +25 -222
- package/checks/hook.js +0 -39
- package/checks/plugin.js +0 -39
- package/utils/processInfo.js +0 -39
package/lib/smtpText.js
CHANGED
|
@@ -1,5 +1,7 @@
|
|
|
1
1
|
import { Buffer } from "node:buffer";
|
|
2
2
|
|
|
3
|
+
import { createError } from "../utils/error.js";
|
|
4
|
+
|
|
3
5
|
function encodeBase64(value) {
|
|
4
6
|
return Buffer.from(String(value || ""), "utf8").toString("base64");
|
|
5
7
|
}
|
|
@@ -98,7 +100,7 @@ function createResponseReader() {
|
|
|
98
100
|
|
|
99
101
|
const responseLines = [];
|
|
100
102
|
let consumedCount = 0;
|
|
101
|
-
let code
|
|
103
|
+
let code;
|
|
102
104
|
for (const line of completeLines) {
|
|
103
105
|
consumedCount = consumedCount + 1;
|
|
104
106
|
responseLines.push(line);
|
|
@@ -143,8 +145,7 @@ function createResponseReader() {
|
|
|
143
145
|
close: function (error) {
|
|
144
146
|
closedError =
|
|
145
147
|
error ||
|
|
146
|
-
|
|
147
|
-
cause: null,
|
|
148
|
+
createError("SMTP 连接已关闭", {
|
|
148
149
|
code: "runtime",
|
|
149
150
|
subsystem: "smtp",
|
|
150
151
|
operation: "readResponse"
|
|
@@ -171,8 +172,7 @@ async function readResponseWithTimeout(reader, timeout) {
|
|
|
171
172
|
new Promise(function (_resolve, reject) {
|
|
172
173
|
timer = setTimeout(function () {
|
|
173
174
|
reject(
|
|
174
|
-
|
|
175
|
-
cause: null,
|
|
175
|
+
createError("SMTP 响应超时", {
|
|
176
176
|
code: "runtime",
|
|
177
177
|
subsystem: "smtp",
|
|
178
178
|
operation: "readResponse"
|
|
@@ -189,13 +189,7 @@ async function readResponseWithTimeout(reader, timeout) {
|
|
|
189
189
|
async function expectResponse(reader, expectedCodes, commandName, timeout) {
|
|
190
190
|
const response = await readResponseWithTimeout(reader, timeout);
|
|
191
191
|
if (!expectedCodes.includes(response.code)) {
|
|
192
|
-
throw
|
|
193
|
-
cause: null,
|
|
194
|
-
code: "runtime",
|
|
195
|
-
subsystem: "smtp",
|
|
196
|
-
operation: commandName,
|
|
197
|
-
response: response.text
|
|
198
|
-
});
|
|
192
|
+
throw createError(`SMTP ${commandName} 响应异常: ${response.text}`, { code: "runtime", subsystem: "smtp", operation: commandName, response: response.text });
|
|
199
193
|
}
|
|
200
194
|
return response;
|
|
201
195
|
}
|
|
@@ -207,12 +201,7 @@ function writeCommand(socket, command) {
|
|
|
207
201
|
export async function sendSmtpTextMail(config, options) {
|
|
208
202
|
const mail = createSmtpTextMessage(config, options);
|
|
209
203
|
if (mail.recipients.length === 0) {
|
|
210
|
-
throw
|
|
211
|
-
cause: null,
|
|
212
|
-
code: "validation",
|
|
213
|
-
subsystem: "smtp",
|
|
214
|
-
operation: "sendSmtpTextMail"
|
|
215
|
-
});
|
|
204
|
+
throw createError("邮件收件人不能为空", { code: "validation", subsystem: "smtp", operation: "sendSmtpTextMail" });
|
|
216
205
|
}
|
|
217
206
|
|
|
218
207
|
const reader = createResponseReader();
|
package/lib/sqlBuilder.js
CHANGED
|
@@ -1,6 +1,27 @@
|
|
|
1
1
|
/**
|
|
2
|
-
* SQL 构造器 - JavaScript 版本
|
|
3
2
|
* 提供链式 API 构建 SQL 查询
|
|
3
|
+
*
|
|
4
|
+
* ==================== where 节点树协议 ====================
|
|
5
|
+
*
|
|
6
|
+
* 本类消费由 lib/dbParse.js 产出的 where 节点树(唯一协议来源),
|
|
7
|
+
* 新增节点类型/操作符时必须同步修改 dbParse 的产出端与这里的编译端。
|
|
8
|
+
*
|
|
9
|
+
* 节点结构(三态):
|
|
10
|
+
* 1. 条件组:{ type: "group", join: "AND" | "OR", items: Node[] }
|
|
11
|
+
* - items 为空数组时编译为空串(不生成 WHERE 片段)
|
|
12
|
+
* - join 为 OR 时,组内每个子条件单独加括号
|
|
13
|
+
* 2. 操作节点:{ type: "op", field: string, operator: string, value: any }
|
|
14
|
+
* - operator 支持(缺省按等值处理):$not $in $notIn $like $leftLike $rightLike $notLike
|
|
15
|
+
* $gt $gte $lt $lte $between $notBetween $null $notNull
|
|
16
|
+
* - $between/$notBetween 的 value 为 [min, max]
|
|
17
|
+
* - 缺省操作符:value === null 编译为 IS NULL,否则 =
|
|
18
|
+
* - field 已由 dbParse 完成合法性校验,这里仅做标识符转义
|
|
19
|
+
* 3. 原始片段:{ type: "raw", sql: string, params?: any[] }
|
|
20
|
+
* - 由 whereRaw() 手动追加,或 dbParse 在特殊场景产出
|
|
21
|
+
*
|
|
22
|
+
* 边界约定:
|
|
23
|
+
* - 空字符串 like/leftLike/rightLike 由 dbParse 过滤,不进入节点树
|
|
24
|
+
* - 本类不校验节点合法性(信任上层),仅负责编译
|
|
4
25
|
*/
|
|
5
26
|
|
|
6
27
|
import { isNonEmptyString } from "../utils/is.js";
|
|
@@ -26,22 +47,6 @@ export class SqlBuilder {
|
|
|
26
47
|
};
|
|
27
48
|
}
|
|
28
49
|
|
|
29
|
-
/**
|
|
30
|
-
* 重置构建器状态
|
|
31
|
-
*/
|
|
32
|
-
reset() {
|
|
33
|
-
this.queryModel = {
|
|
34
|
-
select: [],
|
|
35
|
-
from: null,
|
|
36
|
-
where: { type: "group", join: "AND", items: [] },
|
|
37
|
-
joins: [],
|
|
38
|
-
orderBy: [],
|
|
39
|
-
limit: null,
|
|
40
|
-
offset: null
|
|
41
|
-
};
|
|
42
|
-
return this;
|
|
43
|
-
}
|
|
44
|
-
|
|
45
50
|
compileOperatorNode(node) {
|
|
46
51
|
const escapedField = escapeField(node.field, this.quoteIdentifier);
|
|
47
52
|
|
|
@@ -177,14 +182,6 @@ export class SqlBuilder {
|
|
|
177
182
|
return this;
|
|
178
183
|
}
|
|
179
184
|
|
|
180
|
-
/**
|
|
181
|
-
* FROM 原始表达式(不做转义)
|
|
182
|
-
*/
|
|
183
|
-
fromRaw(tableExpr) {
|
|
184
|
-
this.queryModel.from = { type: "raw", value: tableExpr.trim() };
|
|
185
|
-
return this;
|
|
186
|
-
}
|
|
187
|
-
|
|
188
185
|
/**
|
|
189
186
|
* WHERE 条件
|
|
190
187
|
*/
|
|
@@ -381,32 +378,6 @@ export class SqlBuilder {
|
|
|
381
378
|
};
|
|
382
379
|
}
|
|
383
380
|
|
|
384
|
-
/**
|
|
385
|
-
* 构建 COUNT 查询
|
|
386
|
-
*/
|
|
387
|
-
toCountSql() {
|
|
388
|
-
const params = [];
|
|
389
|
-
let fromSql = this.queryModel.from.value;
|
|
390
|
-
if (this.queryModel.from.type !== "raw") {
|
|
391
|
-
fromSql = escapeTable(this.queryModel.from.value, this.quoteIdentifier);
|
|
392
|
-
}
|
|
393
|
-
let sql = `SELECT COUNT(*) as total FROM ${fromSql}`;
|
|
394
|
-
|
|
395
|
-
if (this.queryModel.joins.length > 0) {
|
|
396
|
-
sql += ` ${this.queryModel.joins.map((join) => `${join.type.toUpperCase()} JOIN ${escapeTable(join.table, this.quoteIdentifier)} ON ${join.on}`).join(" ")}`;
|
|
397
|
-
}
|
|
398
|
-
|
|
399
|
-
const whereResult = this.compileWhereNode(this.queryModel.where);
|
|
400
|
-
if (whereResult.sql) {
|
|
401
|
-
sql += ` WHERE ${whereResult.sql}`;
|
|
402
|
-
for (const param of whereResult.params) {
|
|
403
|
-
params.push(param);
|
|
404
|
-
}
|
|
405
|
-
}
|
|
406
|
-
|
|
407
|
-
return { sql: sql, params: params };
|
|
408
|
-
}
|
|
409
|
-
|
|
410
381
|
static toDeleteInSql(options) {
|
|
411
382
|
if (options.ids.length === 0) {
|
|
412
383
|
return { sql: "", params: [] };
|
package/lib/xmlParse.js
CHANGED
|
@@ -1,3 +1,4 @@
|
|
|
1
|
+
import { createError } from "#befly/utils/error.js";
|
|
1
2
|
import { isString } from "#befly/utils/is.js";
|
|
2
3
|
|
|
3
4
|
function decodeXmlText(value) {
|
|
@@ -44,13 +45,8 @@ function skipIgnoredMarkup(rawXml, startIndex) {
|
|
|
44
45
|
|
|
45
46
|
if (rawXml.startsWith("<?", index)) {
|
|
46
47
|
const declarationEnd = rawXml.indexOf("?>", index);
|
|
47
|
-
if (declarationEnd
|
|
48
|
-
throw
|
|
49
|
-
cause: null,
|
|
50
|
-
code: "validation",
|
|
51
|
-
subsystem: "xml",
|
|
52
|
-
operation: "skipIgnoredMarkup"
|
|
53
|
-
});
|
|
48
|
+
if (declarationEnd === -1) {
|
|
49
|
+
throw createError("XML 声明未闭合", { code: "validation", subsystem: "xml", operation: "skipIgnoredMarkup" });
|
|
54
50
|
}
|
|
55
51
|
index = declarationEnd + 2;
|
|
56
52
|
continue;
|
|
@@ -58,13 +54,8 @@ function skipIgnoredMarkup(rawXml, startIndex) {
|
|
|
58
54
|
|
|
59
55
|
if (rawXml.startsWith("<!--", index)) {
|
|
60
56
|
const commentEnd = rawXml.indexOf("-->", index);
|
|
61
|
-
if (commentEnd
|
|
62
|
-
throw
|
|
63
|
-
cause: null,
|
|
64
|
-
code: "validation",
|
|
65
|
-
subsystem: "xml",
|
|
66
|
-
operation: "skipIgnoredMarkup"
|
|
67
|
-
});
|
|
57
|
+
if (commentEnd === -1) {
|
|
58
|
+
throw createError("XML 注释未闭合", { code: "validation", subsystem: "xml", operation: "skipIgnoredMarkup" });
|
|
68
59
|
}
|
|
69
60
|
index = commentEnd + 3;
|
|
70
61
|
continue;
|
|
@@ -78,25 +69,15 @@ function skipIgnoredMarkup(rawXml, startIndex) {
|
|
|
78
69
|
|
|
79
70
|
function parseNode(rawXml, startIndex) {
|
|
80
71
|
const openEnd = rawXml.indexOf(">", startIndex);
|
|
81
|
-
if (openEnd
|
|
82
|
-
throw
|
|
83
|
-
cause: null,
|
|
84
|
-
code: "validation",
|
|
85
|
-
subsystem: "xml",
|
|
86
|
-
operation: "parseNode"
|
|
87
|
-
});
|
|
72
|
+
if (openEnd === -1) {
|
|
73
|
+
throw createError("XML 开始标签不完整", { code: "validation", subsystem: "xml", operation: "parseNode" });
|
|
88
74
|
}
|
|
89
75
|
|
|
90
76
|
const openContent = rawXml.slice(startIndex + 1, openEnd).trim();
|
|
91
77
|
const isSelfClosing = openContent.endsWith("/");
|
|
92
78
|
const tagName = (isSelfClosing ? openContent.slice(0, -1).trim() : openContent).split(/\s+/)[0];
|
|
93
79
|
if (!tagName || tagName.startsWith("/")) {
|
|
94
|
-
throw
|
|
95
|
-
cause: null,
|
|
96
|
-
code: "validation",
|
|
97
|
-
subsystem: "xml",
|
|
98
|
-
operation: "parseNode"
|
|
99
|
-
});
|
|
80
|
+
throw createError("XML 标签格式不正确", { code: "validation", subsystem: "xml", operation: "parseNode" });
|
|
100
81
|
}
|
|
101
82
|
if (isSelfClosing) {
|
|
102
83
|
return {
|
|
@@ -116,13 +97,8 @@ function parseNode(rawXml, startIndex) {
|
|
|
116
97
|
|
|
117
98
|
if (rawXml.startsWith("<![CDATA[", index)) {
|
|
118
99
|
const cdataEnd = rawXml.indexOf("]]>", index);
|
|
119
|
-
if (cdataEnd
|
|
120
|
-
throw
|
|
121
|
-
cause: null,
|
|
122
|
-
code: "validation",
|
|
123
|
-
subsystem: "xml",
|
|
124
|
-
operation: "parseNode"
|
|
125
|
-
});
|
|
100
|
+
if (cdataEnd === -1) {
|
|
101
|
+
throw createError("XML CDATA 未闭合", { code: "validation", subsystem: "xml", operation: "parseNode" });
|
|
126
102
|
}
|
|
127
103
|
text += rawXml.slice(index + 9, cdataEnd);
|
|
128
104
|
index = cdataEnd + 3;
|
|
@@ -147,45 +123,25 @@ function parseNode(rawXml, startIndex) {
|
|
|
147
123
|
}
|
|
148
124
|
|
|
149
125
|
const nextTagIndex = rawXml.indexOf("<", index);
|
|
150
|
-
if (nextTagIndex
|
|
151
|
-
throw
|
|
152
|
-
cause: null,
|
|
153
|
-
code: "validation",
|
|
154
|
-
subsystem: "xml",
|
|
155
|
-
operation: "parseNode"
|
|
156
|
-
});
|
|
126
|
+
if (nextTagIndex === -1) {
|
|
127
|
+
throw createError("XML 结束标签缺失", { code: "validation", subsystem: "xml", operation: "parseNode" });
|
|
157
128
|
}
|
|
158
129
|
text += rawXml.slice(index, nextTagIndex);
|
|
159
130
|
index = nextTagIndex;
|
|
160
131
|
}
|
|
161
132
|
|
|
162
|
-
throw
|
|
163
|
-
cause: null,
|
|
164
|
-
code: "validation",
|
|
165
|
-
subsystem: "xml",
|
|
166
|
-
operation: "parseNode"
|
|
167
|
-
});
|
|
133
|
+
throw createError("XML 标签未闭合", { code: "validation", subsystem: "xml", operation: "parseNode" });
|
|
168
134
|
}
|
|
169
135
|
|
|
170
136
|
export function xmlParse(value) {
|
|
171
137
|
if (!isString(value) || value.length < 1) {
|
|
172
|
-
throw
|
|
173
|
-
cause: null,
|
|
174
|
-
code: "validation",
|
|
175
|
-
subsystem: "xml",
|
|
176
|
-
operation: "xmlParse"
|
|
177
|
-
});
|
|
138
|
+
throw createError("xmlParse 输入必须是非空字符串", { code: "validation", subsystem: "xml", operation: "xmlParse" });
|
|
178
139
|
}
|
|
179
140
|
|
|
180
141
|
const normalized = value.trim();
|
|
181
142
|
const startIndex = skipIgnoredMarkup(normalized, 0);
|
|
182
143
|
if (normalized[startIndex] !== "<") {
|
|
183
|
-
throw
|
|
184
|
-
cause: null,
|
|
185
|
-
code: "validation",
|
|
186
|
-
subsystem: "xml",
|
|
187
|
-
operation: "xmlParse"
|
|
188
|
-
});
|
|
144
|
+
throw createError("XML 根节点缺失", { code: "validation", subsystem: "xml", operation: "xmlParse" });
|
|
189
145
|
}
|
|
190
146
|
|
|
191
147
|
const node = parseNode(normalized, startIndex);
|
package/package.json
CHANGED
package/plugins/cache.js
CHANGED
|
@@ -1,15 +1,14 @@
|
|
|
1
|
+
import { PLUGIN_ORDER } from "../configs/constConfig.js";
|
|
1
2
|
/**
|
|
2
|
-
* 缓存插件 - JavaScript 版本
|
|
3
3
|
* 负责在服务器启动时缓存接口、菜单和角色权限到 Redis
|
|
4
4
|
*/
|
|
5
|
-
|
|
6
5
|
import { CacheHelper } from "../lib/cacheHelper.js";
|
|
7
6
|
|
|
8
7
|
/**
|
|
9
8
|
* 缓存插件
|
|
10
9
|
*/
|
|
11
10
|
export default {
|
|
12
|
-
order:
|
|
11
|
+
order: PLUGIN_ORDER.CACHE,
|
|
13
12
|
handler: async function (befly) {
|
|
14
13
|
return new CacheHelper({ mysql: befly.mysql, redis: befly.redis });
|
|
15
14
|
}
|
package/plugins/config.js
CHANGED
package/plugins/cron.js
CHANGED
|
@@ -1,8 +1,8 @@
|
|
|
1
|
+
import { PLUGIN_ORDER } from "../configs/constConfig.js";
|
|
1
2
|
/**
|
|
2
3
|
* 定时任务插件
|
|
3
4
|
* 基于 Bun.cron 提供进程内定时任务能力。
|
|
4
5
|
*/
|
|
5
|
-
|
|
6
6
|
import { Logger } from "../lib/logger.js";
|
|
7
7
|
import { isPrimaryProcess } from "../utils/is.js";
|
|
8
8
|
|
|
@@ -84,7 +84,7 @@ export function stopAll() {
|
|
|
84
84
|
}
|
|
85
85
|
|
|
86
86
|
export default {
|
|
87
|
-
order:
|
|
87
|
+
order: PLUGIN_ORDER.CRON,
|
|
88
88
|
handler: async function (befly) {
|
|
89
89
|
return {
|
|
90
90
|
register: (name, schedule, task) => register(befly, name, schedule, task),
|
package/plugins/email.js
CHANGED
|
@@ -2,8 +2,10 @@ import { Logger } from "#befly/lib/logger.js";
|
|
|
2
2
|
import { sendSmtpTextMail } from "#befly/lib/smtpText.js";
|
|
3
3
|
import { hasEmailConfig } from "#befly/utils/email.js";
|
|
4
4
|
|
|
5
|
+
import { PLUGIN_ORDER } from "../configs/constConfig.js";
|
|
6
|
+
|
|
5
7
|
export default {
|
|
6
|
-
order:
|
|
8
|
+
order: PLUGIN_ORDER.EMAIL,
|
|
7
9
|
handler: async function (befly) {
|
|
8
10
|
const config = befly?.config?.email || {};
|
|
9
11
|
const enabled = hasEmailConfig(config);
|
package/plugins/logger.js
CHANGED
|
@@ -1,15 +1,15 @@
|
|
|
1
|
+
import { PLUGIN_ORDER } from "../configs/constConfig.js";
|
|
1
2
|
/**
|
|
2
3
|
* 日志插件
|
|
3
4
|
* 提供全局日志功能
|
|
4
5
|
*/
|
|
5
|
-
|
|
6
6
|
import { Logger } from "../lib/logger.js";
|
|
7
7
|
|
|
8
8
|
/**
|
|
9
9
|
* 日志插件
|
|
10
10
|
*/
|
|
11
11
|
export default {
|
|
12
|
-
order:
|
|
12
|
+
order: PLUGIN_ORDER.LOGGER,
|
|
13
13
|
handler: async function () {
|
|
14
14
|
// 日志已在 createBefly 中按 runMode 初始化,这里只注入 Logger 实例
|
|
15
15
|
return Logger;
|
package/plugins/mysql.js
CHANGED
|
@@ -1,17 +1,17 @@
|
|
|
1
|
+
import { PLUGIN_ORDER } from "../configs/constConfig.js";
|
|
1
2
|
/**
|
|
2
3
|
* 数据库插件
|
|
3
4
|
* 初始化数据库连接和 SQL 管理器
|
|
4
5
|
*/
|
|
5
|
-
|
|
6
6
|
import { Connect } from "../lib/connect.js";
|
|
7
7
|
import { DbHelper } from "../lib/dbHelper.js";
|
|
8
|
-
import {
|
|
8
|
+
import { createError } from "../utils/error.js";
|
|
9
9
|
|
|
10
10
|
/**
|
|
11
11
|
* 数据库插件
|
|
12
12
|
*/
|
|
13
13
|
export default {
|
|
14
|
-
order:
|
|
14
|
+
order: PLUGIN_ORDER.MYSQL,
|
|
15
15
|
handler: async function (befly) {
|
|
16
16
|
try {
|
|
17
17
|
// 创建数据库管理器实例
|
|
@@ -20,18 +20,13 @@ export default {
|
|
|
20
20
|
dbName: befly.config?.mysql?.database,
|
|
21
21
|
sql: Connect.getMysql(),
|
|
22
22
|
beflyMode: befly.config?.mysql?.beflyMode,
|
|
23
|
+
queryTimeout: befly.config?.mysql?.queryTimeout,
|
|
23
24
|
tables: befly.tables
|
|
24
25
|
});
|
|
25
26
|
|
|
26
27
|
return dbManager;
|
|
27
28
|
} catch (error) {
|
|
28
|
-
|
|
29
|
-
throw new Error("数据库初始化失败", {
|
|
30
|
-
cause: error,
|
|
31
|
-
code: "runtime",
|
|
32
|
-
subsystem: "db",
|
|
33
|
-
operation: "init"
|
|
34
|
-
});
|
|
29
|
+
throw createError("数据库初始化失败", { cause: error, code: "runtime", subsystem: "db", operation: "init" });
|
|
35
30
|
}
|
|
36
31
|
}
|
|
37
32
|
};
|
package/plugins/redis.js
CHANGED
|
@@ -1,19 +1,18 @@
|
|
|
1
|
+
import { PLUGIN_ORDER } from "../configs/constConfig.js";
|
|
1
2
|
/**
|
|
2
3
|
* Redis 插件
|
|
3
4
|
* 初始化 Redis 连接和助手工具
|
|
4
5
|
*/
|
|
5
|
-
|
|
6
6
|
import { Connect } from "../lib/connect.js";
|
|
7
|
-
import { Logger } from "../lib/logger.js";
|
|
8
7
|
import { RedisHelper } from "../lib/redisHelper.js";
|
|
8
|
+
import { createError } from "../utils/error.js";
|
|
9
9
|
|
|
10
10
|
/**
|
|
11
11
|
* Redis 插件
|
|
12
12
|
*/
|
|
13
13
|
export default {
|
|
14
|
-
order:
|
|
14
|
+
order: PLUGIN_ORDER.REDIS,
|
|
15
15
|
handler: async function (befly) {
|
|
16
|
-
const env = befly.config?.runMode;
|
|
17
16
|
const redisPrefix = befly.config?.redis?.prefix;
|
|
18
17
|
try {
|
|
19
18
|
// 启动期已建立 Redis 连接;这里仅校验连接存在
|
|
@@ -22,13 +21,7 @@ export default {
|
|
|
22
21
|
// 返回 RedisHelper 实例
|
|
23
22
|
return new RedisHelper(redisPrefix);
|
|
24
23
|
} catch (error) {
|
|
25
|
-
|
|
26
|
-
throw new Error("Redis 初始化失败", {
|
|
27
|
-
cause: error,
|
|
28
|
-
code: "runtime",
|
|
29
|
-
subsystem: "redis",
|
|
30
|
-
operation: "init"
|
|
31
|
-
});
|
|
24
|
+
throw createError("Redis 初始化失败", { cause: error, code: "runtime", subsystem: "redis", operation: "init" });
|
|
32
25
|
}
|
|
33
26
|
}
|
|
34
27
|
};
|
package/plugins/tool.js
CHANGED
|
@@ -1,8 +1,8 @@
|
|
|
1
|
+
import { PLUGIN_ORDER } from "../configs/constConfig.js";
|
|
1
2
|
/**
|
|
2
3
|
* 工具插件
|
|
3
4
|
* 提供常用的工具函数
|
|
4
5
|
*/
|
|
5
|
-
|
|
6
6
|
import { isNumber, isString } from "../utils/is.js";
|
|
7
7
|
|
|
8
8
|
/**
|
|
@@ -94,7 +94,7 @@ export function Raw(ctx, data, options = {}) {
|
|
|
94
94
|
} else {
|
|
95
95
|
// JSON 序列化(对象/数组/原始值均可)
|
|
96
96
|
body = JSON.stringify(data);
|
|
97
|
-
finalContentType
|
|
97
|
+
finalContentType ||= "application/json";
|
|
98
98
|
}
|
|
99
99
|
|
|
100
100
|
// 合并响应头
|
|
@@ -119,7 +119,7 @@ export function Raw(ctx, data, options = {}) {
|
|
|
119
119
|
}
|
|
120
120
|
|
|
121
121
|
export default {
|
|
122
|
-
order:
|
|
122
|
+
order: PLUGIN_ORDER.TOOL,
|
|
123
123
|
handler: () => {
|
|
124
124
|
return {
|
|
125
125
|
Yes: Yes,
|
package/plugins/xmlParse.js
CHANGED
package/router/api.js
CHANGED
|
@@ -8,6 +8,7 @@ import picomatch from "picomatch";
|
|
|
8
8
|
import { Logger } from "../lib/logger.js";
|
|
9
9
|
// 相对导入
|
|
10
10
|
import { setCorsOptions } from "../utils/cors.js";
|
|
11
|
+
import { createError } from "../utils/error.js";
|
|
11
12
|
import { getClientIp } from "../utils/getClientIp.js";
|
|
12
13
|
import { ErrorResponse, FinalResponse } from "../utils/response.js";
|
|
13
14
|
import { genShortId } from "../utils/util.js";
|
|
@@ -17,12 +18,7 @@ function createExcludeApisLogMatchers(excludeApisLog) {
|
|
|
17
18
|
try {
|
|
18
19
|
return picomatch(pattern);
|
|
19
20
|
} catch (error) {
|
|
20
|
-
throw
|
|
21
|
-
cause: error,
|
|
22
|
-
code: "validation",
|
|
23
|
-
subsystem: "config",
|
|
24
|
-
operation: "excludeApisLog"
|
|
25
|
-
});
|
|
21
|
+
throw createError(`excludeApisLog glob 无效: ${pattern}`, { cause: error, code: "validation", subsystem: "config", operation: "excludeApisLog" });
|
|
26
22
|
}
|
|
27
23
|
});
|
|
28
24
|
}
|
package/router/static.js
CHANGED
|
@@ -76,17 +76,16 @@ export function staticHandler(corsConfig, uploadConfig) {
|
|
|
76
76
|
return new Response(file, {
|
|
77
77
|
headers: headers
|
|
78
78
|
});
|
|
79
|
-
} else {
|
|
80
|
-
return Response.json(
|
|
81
|
-
{
|
|
82
|
-
code: 1,
|
|
83
|
-
msg: "文件未找到"
|
|
84
|
-
},
|
|
85
|
-
{
|
|
86
|
-
headers: corsHeaders
|
|
87
|
-
}
|
|
88
|
-
);
|
|
89
79
|
}
|
|
80
|
+
return Response.json(
|
|
81
|
+
{
|
|
82
|
+
code: 1,
|
|
83
|
+
msg: "文件未找到"
|
|
84
|
+
},
|
|
85
|
+
{
|
|
86
|
+
headers: corsHeaders
|
|
87
|
+
}
|
|
88
|
+
);
|
|
90
89
|
} catch (error) {
|
|
91
90
|
Logger.error("静态文件处理失败", error);
|
|
92
91
|
|
package/schemas/config.js
CHANGED
|
@@ -1,12 +1,9 @@
|
|
|
1
|
-
import { RUN_MODE_VALUES } from "../configs/constConfig.js";
|
|
2
|
-
|
|
3
1
|
const noTrimString = { type: "string", noTrim: true };
|
|
4
2
|
const boolInt = { type: "boolInt" };
|
|
5
3
|
const port = { type: "number", integer: true, min: 1, max: 65535 };
|
|
6
4
|
|
|
7
5
|
export const configSchema = {
|
|
8
6
|
root: {
|
|
9
|
-
runMode: { type: "values", values: RUN_MODE_VALUES },
|
|
10
7
|
appName: noTrimString,
|
|
11
8
|
appPort: port,
|
|
12
9
|
appHost: noTrimString,
|
|
@@ -37,7 +34,8 @@ export const configSchema = {
|
|
|
37
34
|
beflyMode: { type: "values", values: ["manual", "auto"], optional: true },
|
|
38
35
|
idleTimeout: { type: "number", min: 1, optional: true },
|
|
39
36
|
maxLifetime: { type: "number", min: 0, optional: true },
|
|
40
|
-
connectionTimeout: { type: "number", min: 1, optional: true }
|
|
37
|
+
connectionTimeout: { type: "number", min: 1, optional: true },
|
|
38
|
+
queryTimeout: { type: "number", min: 1, optional: true }
|
|
41
39
|
},
|
|
42
40
|
redis: {
|
|
43
41
|
hostname: noTrimString,
|
|
@@ -58,6 +56,8 @@ export const configSchema = {
|
|
|
58
56
|
},
|
|
59
57
|
rateLimit: {
|
|
60
58
|
enable: boolInt,
|
|
59
|
+
// 限流服务不可用时是否放行(1 放行 / 0 拒绝),默认 1
|
|
60
|
+
failOpen: boolInt,
|
|
61
61
|
defaultLimit: { type: "number", integer: true, min: 1 },
|
|
62
62
|
defaultWindow: { type: "number", integer: true, min: 1 },
|
|
63
63
|
key: noTrimString,
|
|
@@ -1,9 +1,11 @@
|
|
|
1
1
|
import { mkdir } from "node:fs/promises";
|
|
2
2
|
import { basename, resolve, join } from "node:path";
|
|
3
3
|
|
|
4
|
+
import { BEFLY_TABLE_PREFIX } from "#befly/configs/constConfig.js";
|
|
4
5
|
import { Connect } from "#befly/lib/connect.js";
|
|
5
6
|
import { DbHelper } from "#befly/lib/dbHelper.js";
|
|
6
7
|
import { Logger } from "#befly/lib/logger.js";
|
|
8
|
+
import { createError } from "#befly/utils/error.js";
|
|
7
9
|
import { importDefault } from "#befly/utils/importDefault.js";
|
|
8
10
|
import { isNonEmptyString, isPlainObject } from "#befly/utils/is.js";
|
|
9
11
|
import { camelCase } from "#befly/utils/util.js";
|
|
@@ -35,12 +37,7 @@ export async function prepareSyncDbBaseContext(mysqlConfig) {
|
|
|
35
37
|
printSyncDbProcessLog(`表定义目录=${tablesDir}`);
|
|
36
38
|
|
|
37
39
|
if (!isPlainObject(mysqlConfig)) {
|
|
38
|
-
throw
|
|
39
|
-
cause: null,
|
|
40
|
-
code: "validation",
|
|
41
|
-
subsystem: "scripts",
|
|
42
|
-
operation: "syncDb"
|
|
43
|
-
});
|
|
40
|
+
throw createError("syncDb 缺少 mysql 配置", { code: "validation", subsystem: "scripts", operation: "syncDb" });
|
|
44
41
|
}
|
|
45
42
|
|
|
46
43
|
printSyncDbProcessLog(`同步模式=${mysqlConfig.beflyMode === "manual" ? "手动" : "自动"}`);
|
|
@@ -61,7 +58,7 @@ export async function prepareSyncDbBaseContext(mysqlConfig) {
|
|
|
61
58
|
const skippedBeflyTables = new Set();
|
|
62
59
|
for (const row of dbColumns) {
|
|
63
60
|
const tableName = String(row?.tableName || "");
|
|
64
|
-
if (isNonEmptyString(tableName) && tableName.toLowerCase().startsWith(
|
|
61
|
+
if (isNonEmptyString(tableName) && tableName.toLowerCase().startsWith(BEFLY_TABLE_PREFIX)) {
|
|
65
62
|
skippedBeflyTables.add(tableName);
|
|
66
63
|
}
|
|
67
64
|
}
|
package/scripts/syncDb/diff.js
CHANGED
|
@@ -1,5 +1,6 @@
|
|
|
1
1
|
import { join } from "node:path";
|
|
2
2
|
|
|
3
|
+
import { BEFLY_TABLE_PREFIX } from "#befly/configs/constConfig.js";
|
|
3
4
|
import { isNonEmptyString, isPlainObject } from "#befly/utils/is.js";
|
|
4
5
|
import { camelCase, snakeCase } from "#befly/utils/util.js";
|
|
5
6
|
|
|
@@ -67,7 +68,7 @@ export function groupSyncDbColumns(rows) {
|
|
|
67
68
|
if (!isNonEmptyString(tableName)) {
|
|
68
69
|
continue;
|
|
69
70
|
}
|
|
70
|
-
if (tableName.toLowerCase().startsWith(
|
|
71
|
+
if (tableName.toLowerCase().startsWith(BEFLY_TABLE_PREFIX)) {
|
|
71
72
|
continue;
|
|
72
73
|
}
|
|
73
74
|
|
package/scripts/syncDb/index.js
CHANGED
|
@@ -2,6 +2,7 @@ import { join, resolve } from "node:path";
|
|
|
2
2
|
|
|
3
3
|
import { Connect } from "#befly/lib/connect.js";
|
|
4
4
|
import { Logger } from "#befly/lib/logger.js";
|
|
5
|
+
import { createError } from "#befly/utils/error.js";
|
|
5
6
|
|
|
6
7
|
import { prepareSyncDbBaseContext } from "./context.js";
|
|
7
8
|
import { applySyncDbDiff } from "./diff.js";
|
|
@@ -34,12 +35,7 @@ export async function syncDb(mysqlConfig) {
|
|
|
34
35
|
reportPath: writtenReportPath
|
|
35
36
|
};
|
|
36
37
|
} catch (error) {
|
|
37
|
-
throw
|
|
38
|
-
cause: error,
|
|
39
|
-
code: "runtime",
|
|
40
|
-
subsystem: "scripts",
|
|
41
|
-
operation: "syncDb"
|
|
42
|
-
});
|
|
38
|
+
throw createError("执行表定义同步失败", { cause: error, code: "runtime", subsystem: "scripts", operation: "syncDb" });
|
|
43
39
|
} finally {
|
|
44
40
|
await Connect.disconnect();
|
|
45
41
|
await Logger.flush();
|