chanjs 2.3.0 → 2.5.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/App.js +83 -87
- package/base/Controller.js +5 -110
- package/base/Database.js +1 -232
- package/base/Service.js +2 -35
- package/global/import.js +1 -5
- package/helper/index.js +0 -2
- package/index.js +0 -1
- package/middleware/index.js +0 -1
- package/middleware/template.js +2 -1
- package/middleware/waf.js +34 -6
- package/package.json +1 -1
- package/utils/index.js +1 -3
- package/utils/keywords.js +8 -2
- package/utils/response.js +87 -10
- package/base/Context.js +0 -78
- package/helper/db.js +0 -79
- package/middleware/preventRetry.js +0 -30
- package/middleware/validator.js +0 -43
- package/utils/error-handler.js +0 -115
- package/utils/error.js +0 -81
package/base/Database.js
CHANGED
|
@@ -2,7 +2,7 @@ import knex from "knex";
|
|
|
2
2
|
|
|
3
3
|
/**
|
|
4
4
|
* 数据库管理器类
|
|
5
|
-
*
|
|
5
|
+
* 用于管理多个数据库连接
|
|
6
6
|
*/
|
|
7
7
|
class DatabaseManager {
|
|
8
8
|
/**
|
|
@@ -78,237 +78,6 @@ class DatabaseManager {
|
|
|
78
78
|
}
|
|
79
79
|
}
|
|
80
80
|
|
|
81
|
-
/**
|
|
82
|
-
* 关闭所有数据库连接
|
|
83
|
-
* @returns {Promise<void>}
|
|
84
|
-
*/
|
|
85
|
-
async closeAll() {
|
|
86
|
-
const closePromises = [];
|
|
87
|
-
for (const [name, connection] of this._connections) {
|
|
88
|
-
closePromises.push(
|
|
89
|
-
connection.destroy().catch((err) => {
|
|
90
|
-
console.error(`Error closing database "${name}":`, err);
|
|
91
|
-
})
|
|
92
|
-
);
|
|
93
|
-
}
|
|
94
|
-
await Promise.all(closePromises);
|
|
95
|
-
this._connections.clear();
|
|
96
|
-
}
|
|
97
|
-
|
|
98
|
-
/**
|
|
99
|
-
* 获取所有连接名称
|
|
100
|
-
* @returns {Array<string>} 连接名称数组
|
|
101
|
-
*/
|
|
102
|
-
getConnections() {
|
|
103
|
-
return Array.from(this._connections.keys());
|
|
104
|
-
}
|
|
105
|
-
|
|
106
|
-
/**
|
|
107
|
-
* 创建数据模型
|
|
108
|
-
* @param {string} name - 模型名称
|
|
109
|
-
* @param {string} tableName - 表名
|
|
110
|
-
* @param {string} connectionName - 连接名称
|
|
111
|
-
* @returns {Object} 模型对象
|
|
112
|
-
*/
|
|
113
|
-
createModel(name, tableName, connectionName) {
|
|
114
|
-
const db = this.get(connectionName);
|
|
115
|
-
return {
|
|
116
|
-
db,
|
|
117
|
-
tableName,
|
|
118
|
-
|
|
119
|
-
/**
|
|
120
|
-
* 查询字段
|
|
121
|
-
* @param {string} fields - 字段列表,默认"*"
|
|
122
|
-
* @returns {Object} Knex查询构建器
|
|
123
|
-
*/
|
|
124
|
-
select(fields = "*") {
|
|
125
|
-
return db(this.tableName).select(fields);
|
|
126
|
-
},
|
|
127
|
-
|
|
128
|
-
/**
|
|
129
|
-
* 条件查询
|
|
130
|
-
* @param {Object} conditions - 查询条件
|
|
131
|
-
* @returns {Object} Knex查询构建器
|
|
132
|
-
*/
|
|
133
|
-
where(conditions) {
|
|
134
|
-
return db(this.tableName).where(conditions);
|
|
135
|
-
},
|
|
136
|
-
|
|
137
|
-
/**
|
|
138
|
-
* 查找记录
|
|
139
|
-
* @param {Object} query - 查询参数
|
|
140
|
-
* @param {Object} query.sort - 排序条件
|
|
141
|
-
* @param {Object} query.where - 查询条件
|
|
142
|
-
* @returns {Promise<Array>} 查询结果数组
|
|
143
|
-
*/
|
|
144
|
-
find(query = {}) {
|
|
145
|
-
let q = db(this.tableName);
|
|
146
|
-
const { sort, ...where } = query;
|
|
147
|
-
|
|
148
|
-
if (Object.keys(where).length > 0) {
|
|
149
|
-
q = q.where(where);
|
|
150
|
-
}
|
|
151
|
-
|
|
152
|
-
if (sort && typeof sort === "object") {
|
|
153
|
-
for (const [field, dir] of Object.entries(sort)) {
|
|
154
|
-
q = q.orderBy(field, dir);
|
|
155
|
-
}
|
|
156
|
-
}
|
|
157
|
-
|
|
158
|
-
return q.select();
|
|
159
|
-
},
|
|
160
|
-
|
|
161
|
-
/**
|
|
162
|
-
* 查找单条记录
|
|
163
|
-
* @param {Object} query - 查询条件
|
|
164
|
-
* @returns {Promise<Object|null>} 查询结果或null
|
|
165
|
-
*/
|
|
166
|
-
findOne(query = {}) {
|
|
167
|
-
let q = db(this.tableName);
|
|
168
|
-
if (Object.keys(query).length > 0) {
|
|
169
|
-
q = q.where(query);
|
|
170
|
-
}
|
|
171
|
-
return q.first();
|
|
172
|
-
},
|
|
173
|
-
|
|
174
|
-
/**
|
|
175
|
-
* 根据ID查找记录
|
|
176
|
-
* @param {number|string} id - 记录ID
|
|
177
|
-
* @returns {Promise<Object|null>} 查询结果或null
|
|
178
|
-
*/
|
|
179
|
-
findById(id) {
|
|
180
|
-
return db(this.tableName).where({ id }).first();
|
|
181
|
-
},
|
|
182
|
-
|
|
183
|
-
/**
|
|
184
|
-
* 创建记录
|
|
185
|
-
* @param {Object} data - 要插入的数据
|
|
186
|
-
* @returns {Promise<Array>} 插入结果
|
|
187
|
-
*/
|
|
188
|
-
create(data) {
|
|
189
|
-
return db(this.tableName).insert(data);
|
|
190
|
-
},
|
|
191
|
-
|
|
192
|
-
/**
|
|
193
|
-
* 批量创建记录
|
|
194
|
-
* @param {Array} records - 要插入的记录数组
|
|
195
|
-
* @returns {Promise<Array>} 插入结果
|
|
196
|
-
*/
|
|
197
|
-
createMany(records) {
|
|
198
|
-
return db(this.tableName).insert(records);
|
|
199
|
-
},
|
|
200
|
-
|
|
201
|
-
/**
|
|
202
|
-
* 更新记录
|
|
203
|
-
* @param {Object} conditions - 更新条件
|
|
204
|
-
* @param {Object} data - 更新数据
|
|
205
|
-
* @returns {Promise<number>} 影响的行数
|
|
206
|
-
*/
|
|
207
|
-
update(conditions, data) {
|
|
208
|
-
return db(this.tableName).where(conditions).update(data);
|
|
209
|
-
},
|
|
210
|
-
|
|
211
|
-
/**
|
|
212
|
-
* 删除记录
|
|
213
|
-
* @param {Object} conditions - 删除条件
|
|
214
|
-
* @returns {Promise<number>} 影响的行数
|
|
215
|
-
*/
|
|
216
|
-
delete(conditions) {
|
|
217
|
-
return db(this.tableName).where(conditions).del();
|
|
218
|
-
},
|
|
219
|
-
|
|
220
|
-
/**
|
|
221
|
-
* 统计记录数
|
|
222
|
-
* @param {Object} query - 查询条件
|
|
223
|
-
* @returns {Promise<number>} 记录总数
|
|
224
|
-
*/
|
|
225
|
-
count(query = {}) {
|
|
226
|
-
let q = db(this.tableName).count("* as total");
|
|
227
|
-
if (Object.keys(query).length > 0) {
|
|
228
|
-
q = q.where(query);
|
|
229
|
-
}
|
|
230
|
-
return q.first().then((result) => result?.total ?? 0);
|
|
231
|
-
},
|
|
232
|
-
|
|
233
|
-
/**
|
|
234
|
-
* 分页查询
|
|
235
|
-
* @param {Object} options - 查询选项
|
|
236
|
-
* @param {number} options.current - 当前页码,默认1
|
|
237
|
-
* @param {number} options.pageSize - 每页大小,默认10
|
|
238
|
-
* @param {Object} options.where - 查询条件
|
|
239
|
-
* @param {Array} options.select - 查询字段
|
|
240
|
-
* @returns {Promise<Object>} 分页结果
|
|
241
|
-
*/
|
|
242
|
-
query(options = {}) {
|
|
243
|
-
const { current = 1, pageSize = 10, where = {}, select } = options;
|
|
244
|
-
const offset = (current - 1) * pageSize;
|
|
245
|
-
|
|
246
|
-
const countQuery = db(this.tableName).count("* as total");
|
|
247
|
-
const dataQuery = db(this.tableName);
|
|
248
|
-
|
|
249
|
-
if (Object.keys(where).length > 0) {
|
|
250
|
-
countQuery.where(where);
|
|
251
|
-
dataQuery.where(where);
|
|
252
|
-
}
|
|
253
|
-
|
|
254
|
-
if (select) {
|
|
255
|
-
dataQuery.select(select);
|
|
256
|
-
}
|
|
257
|
-
|
|
258
|
-
return Promise.all([
|
|
259
|
-
countQuery.first(),
|
|
260
|
-
dataQuery.offset(offset).limit(pageSize),
|
|
261
|
-
]).then(([totalResult, list]) => ({
|
|
262
|
-
list,
|
|
263
|
-
total: totalResult?.total ?? 0,
|
|
264
|
-
current,
|
|
265
|
-
pageSize,
|
|
266
|
-
}));
|
|
267
|
-
},
|
|
268
|
-
|
|
269
|
-
/**
|
|
270
|
-
* 关联查询
|
|
271
|
-
* @param {Object} options - 查询选项
|
|
272
|
-
* @param {string} options.joinTable - 关联表名
|
|
273
|
-
* @param {string} options.localField - 本地表字段
|
|
274
|
-
* @param {string} options.foreignField - 关联表字段
|
|
275
|
-
* @param {string} options.select - 查询字段,默认"*"
|
|
276
|
-
* @param {Object} options.where - 查询条件
|
|
277
|
-
* @returns {Object} Knex查询构建器
|
|
278
|
-
*/
|
|
279
|
-
join(options) {
|
|
280
|
-
const { joinTable, localField, foreignField, select = "*", where = {} } = options;
|
|
281
|
-
let q = db(this.tableName)
|
|
282
|
-
.join(joinTable, `${this.tableName}.${localField}`, "=", `${joinTable}.${foreignField}`)
|
|
283
|
-
.select(select);
|
|
284
|
-
|
|
285
|
-
if (Object.keys(where).length > 0) {
|
|
286
|
-
q = q.where(where);
|
|
287
|
-
}
|
|
288
|
-
|
|
289
|
-
return q;
|
|
290
|
-
},
|
|
291
|
-
|
|
292
|
-
/**
|
|
293
|
-
* 执行事务
|
|
294
|
-
* @param {Function} callback - 事务回调函数
|
|
295
|
-
* @returns {Promise} 事务执行结果
|
|
296
|
-
*/
|
|
297
|
-
transaction(callback) {
|
|
298
|
-
return db.transaction(callback);
|
|
299
|
-
},
|
|
300
|
-
|
|
301
|
-
/**
|
|
302
|
-
* 执行原生SQL
|
|
303
|
-
* @param {string} sql - SQL语句
|
|
304
|
-
* @param {Array} bindings - 绑定参数
|
|
305
|
-
* @returns {Promise} 查询结果
|
|
306
|
-
*/
|
|
307
|
-
raw(sql, bindings) {
|
|
308
|
-
return db.raw(sql, bindings);
|
|
309
|
-
},
|
|
310
|
-
};
|
|
311
|
-
}
|
|
312
81
|
}
|
|
313
82
|
|
|
314
83
|
export default DatabaseManager;
|
package/base/Service.js
CHANGED
|
@@ -14,11 +14,6 @@ class Service {
|
|
|
14
14
|
if (knex && tableName) {
|
|
15
15
|
this.db = knex;
|
|
16
16
|
this.tableName = tableName;
|
|
17
|
-
this._config = Chan.config || {};
|
|
18
|
-
} else if (global.appContext) {
|
|
19
|
-
this.context = global.appContext;
|
|
20
|
-
} else {
|
|
21
|
-
this.context = { get: () => null, set: () => null };
|
|
22
17
|
}
|
|
23
18
|
|
|
24
19
|
this._dateFields = ['created_at', 'updated_at', 'deleted_at', 'publish_time', 'start_time', 'end_time', 'login_time', 'created_date', 'updated_date', 'createdAt', 'updatedAt', 'deletedAt', 'publishTime', 'startTime', 'endTime', 'loginTime', 'createdDate', 'updatedDate'];
|
|
@@ -29,7 +24,7 @@ class Service {
|
|
|
29
24
|
* @returns {number} 每页记录数,默认20
|
|
30
25
|
*/
|
|
31
26
|
get pageSize() {
|
|
32
|
-
return
|
|
27
|
+
return Chan.config.PAGE_SIZE || 20;
|
|
33
28
|
}
|
|
34
29
|
|
|
35
30
|
/**
|
|
@@ -37,35 +32,7 @@ class Service {
|
|
|
37
32
|
* @returns {number} 最大记录数,默认300
|
|
38
33
|
*/
|
|
39
34
|
get limit() {
|
|
40
|
-
return
|
|
41
|
-
}
|
|
42
|
-
|
|
43
|
-
/**
|
|
44
|
-
* 获取模型实例
|
|
45
|
-
* @param {string} name - 模型名称
|
|
46
|
-
* @returns {Object} 模型实例
|
|
47
|
-
*/
|
|
48
|
-
getModel(name) {
|
|
49
|
-
return this.context.getModel(name);
|
|
50
|
-
}
|
|
51
|
-
|
|
52
|
-
/**
|
|
53
|
-
* 获取数据库连接
|
|
54
|
-
* @param {string} name - 数据库名称
|
|
55
|
-
* @returns {Object} 数据库实例
|
|
56
|
-
*/
|
|
57
|
-
getDB(name) {
|
|
58
|
-
return this.context.get(`db:${name}`);
|
|
59
|
-
}
|
|
60
|
-
|
|
61
|
-
/**
|
|
62
|
-
* 执行事务
|
|
63
|
-
* @param {Function} callback - 事务回调函数
|
|
64
|
-
* @returns {Promise} 事务执行结果
|
|
65
|
-
*/
|
|
66
|
-
async withTransaction(callback) {
|
|
67
|
-
const db = this.getDB("default") || Chan.db;
|
|
68
|
-
return db.transaction(callback);
|
|
35
|
+
return Chan.config.LIMIT_MAX || 300;
|
|
69
36
|
}
|
|
70
37
|
|
|
71
38
|
/**
|
package/global/import.js
CHANGED
package/helper/index.js
CHANGED
package/index.js
CHANGED
|
@@ -1,7 +1,6 @@
|
|
|
1
1
|
export { default as Chan } from "./App.js";
|
|
2
2
|
export { default as Controller } from "./base/Controller.js";
|
|
3
3
|
export { default as Service } from "./base/Service.js";
|
|
4
|
-
export { default as AppContext } from "./base/Context.js";
|
|
5
4
|
export { default as DatabaseManager } from "./base/Database.js";
|
|
6
5
|
|
|
7
6
|
export * as helper from "./helper/index.js";
|
package/middleware/index.js
CHANGED
package/middleware/template.js
CHANGED
|
@@ -1,4 +1,5 @@
|
|
|
1
1
|
import "../extend/index.js";
|
|
2
|
+
import { importjs } from "../global/import.js";
|
|
2
3
|
|
|
3
4
|
/**
|
|
4
5
|
* 模板引擎中间件配置
|
|
@@ -30,5 +31,5 @@ export let setTemplate = (app, config) => {
|
|
|
30
31
|
});
|
|
31
32
|
app.set("view engine", "html");
|
|
32
33
|
app.set("views", all);
|
|
33
|
-
app.engine(".html",
|
|
34
|
+
app.engine(".html", importjs("express-art-template"));
|
|
34
35
|
};
|
package/middleware/waf.js
CHANGED
|
@@ -2,13 +2,15 @@ import { getIp } from "../helper/ip.js";
|
|
|
2
2
|
import { checkKeywords } from "../utils/checker.js";
|
|
3
3
|
import { filterXSS } from "../utils/xss-filter.js";
|
|
4
4
|
import { createRateLimitMiddleware } from "../utils/rate-limit.js";
|
|
5
|
-
import { configError } from "../utils/error.js";
|
|
6
5
|
|
|
7
6
|
/**
|
|
8
7
|
* Web应用防火墙(WAF)中间件
|
|
9
8
|
* 提供访问限流、关键词过滤、XSS防护等功能
|
|
10
9
|
*/
|
|
11
10
|
|
|
11
|
+
const WAF_BLOCKED_KEY = `${process.env.APP_NAME || 'app'}_waf_blocked`;
|
|
12
|
+
const WAF_BLOCKED_HEADER = 'x-waf-blocked';
|
|
13
|
+
|
|
12
14
|
/**
|
|
13
15
|
* 路径白名单 - 这些路径不会被 WAF 检查
|
|
14
16
|
* @type {Array<string>}
|
|
@@ -56,6 +58,18 @@ function createWafMiddleware(wafConfig) {
|
|
|
56
58
|
res.setHeader("X-XSS-Protection", "1; mode=block");
|
|
57
59
|
res.setHeader("X-Frame-Options", "DENY");
|
|
58
60
|
|
|
61
|
+
if (!isWhitelistedPath(requestPath)) {
|
|
62
|
+
const isBlocked = req.headers[WAF_BLOCKED_HEADER];
|
|
63
|
+
if (isBlocked === 'true') {
|
|
64
|
+
console.error(`[WAF 永久封禁] IP:${clientIp} 路径:${requestPath} 原因:localStorage标记`);
|
|
65
|
+
return res.status(403).json({
|
|
66
|
+
code: 403,
|
|
67
|
+
success: false,
|
|
68
|
+
msg: '您的访问已被限制,如需恢复请联系管理员'
|
|
69
|
+
});
|
|
70
|
+
}
|
|
71
|
+
}
|
|
72
|
+
|
|
59
73
|
await new Promise((resolve) => {
|
|
60
74
|
rateLimitMiddleware(req, res, resolve);
|
|
61
75
|
});
|
|
@@ -92,11 +106,25 @@ function createWafMiddleware(wafConfig) {
|
|
|
92
106
|
const { category, keyword } = matched;
|
|
93
107
|
console.error(`[WAF 拦截] IP:${clientIp} 路径:${requestPath} 关键词:${keyword} 类别:${category}`);
|
|
94
108
|
|
|
95
|
-
|
|
96
|
-
|
|
97
|
-
|
|
98
|
-
|
|
99
|
-
|
|
109
|
+
const htmlResponse = `
|
|
110
|
+
<!DOCTYPE html>
|
|
111
|
+
<html>
|
|
112
|
+
<head>
|
|
113
|
+
<meta charset="UTF-8">
|
|
114
|
+
<title>访问受限</title>
|
|
115
|
+
</head>
|
|
116
|
+
<body>
|
|
117
|
+
<h1>非法请求</h1>
|
|
118
|
+
<p>检测到恶意内容,您的访问已被限制。</p>
|
|
119
|
+
<p>如需恢复请联系管理员。</p>
|
|
120
|
+
<script>
|
|
121
|
+
localStorage.setItem('${WAF_BLOCKED_KEY}', 'true');
|
|
122
|
+
</script>
|
|
123
|
+
</body>
|
|
124
|
+
</html>
|
|
125
|
+
`;
|
|
126
|
+
|
|
127
|
+
return res.status(403).setHeader('Content-Type', 'text/html').send(htmlResponse);
|
|
100
128
|
}
|
|
101
129
|
}
|
|
102
130
|
|
package/package.json
CHANGED
package/utils/index.js
CHANGED
|
@@ -1,6 +1,4 @@
|
|
|
1
|
-
export { success, fail, error } from "./response.js";
|
|
2
|
-
export { parseDatabaseError, notFoundResponse, errorResponse } from "./error-handler.js";
|
|
3
|
-
export { configError, authError } from "./error.js";
|
|
1
|
+
export { success, fail, error, parseDatabaseError, notFoundResponse, errorResponse } from "./response.js";
|
|
4
2
|
export { checkKeywords, isIgnored } from "./checker.js";
|
|
5
3
|
export { filterXSS } from "./xss-filter.js";
|
|
6
4
|
export { createRateLimitMiddleware } from "./rate-limit.js";
|
package/utils/keywords.js
CHANGED
|
@@ -16,7 +16,7 @@ export const KEYWORD_RULES = {
|
|
|
16
16
|
*/
|
|
17
17
|
extensions: [
|
|
18
18
|
".php", ".asp", ".aspx", ".jsp", ".jspx", ".do", ".action", ".cgi",
|
|
19
|
-
".py", ".pl", ".cfm", ".jhtml", ".shtml"
|
|
19
|
+
".py", ".pl", ".cfm", ".jhtml", ".shtml",".sql"
|
|
20
20
|
],
|
|
21
21
|
/**
|
|
22
22
|
* 敏感目录名称
|
|
@@ -39,7 +39,13 @@ export const KEYWORD_RULES = {
|
|
|
39
39
|
commandInjection: [
|
|
40
40
|
"cmd=", "system(", "exec(", "shell_exec(", "passthru(",
|
|
41
41
|
"eval(", "assert(", "preg_replace", "bash -i", "rm -rf",
|
|
42
|
-
"wget ", "curl ", "chmod ", "base64_decode", "phpinfo()"
|
|
42
|
+
"wget ", "curl ", "chmod ", "base64_decode", "phpinfo()",
|
|
43
|
+
"kill ", "killall", "shutdown", "reboot", "halt", "fdisk",
|
|
44
|
+
"mkfs", "dd ", "ssh ", "scp ", "rsync", "nc ",
|
|
45
|
+
"netcat", "nmap", "iptables", "systemctl", "service",
|
|
46
|
+
"init", "crontab", "at ", "su ", "sudo", "useradd",
|
|
47
|
+
"userdel", "usermod", "groupadd", "groupdel", "passwd",
|
|
48
|
+
"chpasswd", "mount ", "umount", "ln -s"
|
|
43
49
|
],
|
|
44
50
|
/**
|
|
45
51
|
* 路径遍历关键词
|
package/utils/response.js
CHANGED
|
@@ -5,6 +5,20 @@ import { CODE, DB_ERROR } from "../config/code.js";
|
|
|
5
5
|
* 提供统一的响应格式和错误处理
|
|
6
6
|
*/
|
|
7
7
|
|
|
8
|
+
const ERROR_MESSAGES = {
|
|
9
|
+
6001: "数据库连接失败",
|
|
10
|
+
6002: "数据库访问被拒绝",
|
|
11
|
+
6003: "存在关联数据,操作失败",
|
|
12
|
+
6004: "数据库字段错误",
|
|
13
|
+
6005: "数据重复,违反唯一性约束",
|
|
14
|
+
6006: "目标表不存在",
|
|
15
|
+
6007: "数据库操作超时",
|
|
16
|
+
6008: "数据库语法错误,请检查查询语句",
|
|
17
|
+
6009: "数据库连接已关闭,请重试",
|
|
18
|
+
4003: "资源已存在",
|
|
19
|
+
5001: "系统内部错误",
|
|
20
|
+
};
|
|
21
|
+
|
|
8
22
|
/**
|
|
9
23
|
* 获取默认错误代码
|
|
10
24
|
* @private
|
|
@@ -23,6 +37,32 @@ const getDefaultErrorCode = (error) => {
|
|
|
23
37
|
return 5001;
|
|
24
38
|
};
|
|
25
39
|
|
|
40
|
+
/**
|
|
41
|
+
* 解析数据库错误
|
|
42
|
+
* @param {Error} error - 数据库错误对象
|
|
43
|
+
* @returns {Object} 包含 code、msg 和 statusCode 的对象
|
|
44
|
+
* @description
|
|
45
|
+
* 根据数据库错误代码映射为业务状态码
|
|
46
|
+
* 返回对应的错误消息和 HTTP 状态码
|
|
47
|
+
*/
|
|
48
|
+
export function parseDatabaseError(error) {
|
|
49
|
+
const errorCode = error?.code && DB_ERROR[error.code]
|
|
50
|
+
? DB_ERROR[error.code]
|
|
51
|
+
: error?.message?.includes("syntax") || error?.message?.includes("SQL")
|
|
52
|
+
? 6008
|
|
53
|
+
: error?.message?.includes("Connection closed")
|
|
54
|
+
? 6009
|
|
55
|
+
: error?.message?.includes("permission")
|
|
56
|
+
? 3003
|
|
57
|
+
: 5001;
|
|
58
|
+
|
|
59
|
+
return {
|
|
60
|
+
code: errorCode,
|
|
61
|
+
msg: ERROR_MESSAGES[errorCode] || error?.message || "服务器内部错误",
|
|
62
|
+
statusCode: errorCode >= 6000 ? 500 : errorCode >= 4000 ? 400 : 500,
|
|
63
|
+
};
|
|
64
|
+
}
|
|
65
|
+
|
|
26
66
|
/**
|
|
27
67
|
* 生成错误响应
|
|
28
68
|
* @param {Object} options - 响应选项
|
|
@@ -33,8 +73,6 @@ const getDefaultErrorCode = (error) => {
|
|
|
33
73
|
* @description
|
|
34
74
|
* 根据错误类型生成标准错误响应
|
|
35
75
|
* 开发环境下包含数据库错误详情
|
|
36
|
-
* @example
|
|
37
|
-
* const response = error({ err: databaseError });
|
|
38
76
|
*/
|
|
39
77
|
export const error = ({ err, data = {}, code = 500 } = {}) => {
|
|
40
78
|
if (err) {
|
|
@@ -70,10 +108,6 @@ export const error = ({ err, data = {}, code = 500 } = {}) => {
|
|
|
70
108
|
* @param {Object} [options.data={}] - 响应数据
|
|
71
109
|
* @param {number} [options.code=201] - 错误代码
|
|
72
110
|
* @returns {Object} 失败响应对象
|
|
73
|
-
* @description
|
|
74
|
-
* 生成标准失败响应
|
|
75
|
-
* @example
|
|
76
|
-
* const response = fail({ msg: '用户不存在', code: 404 });
|
|
77
111
|
*/
|
|
78
112
|
export const fail = ({ msg = "操作失败", data = {}, code = 201 } = {}) => {
|
|
79
113
|
return {
|
|
@@ -90,10 +124,6 @@ export const fail = ({ msg = "操作失败", data = {}, code = 201 } = {}) => {
|
|
|
90
124
|
* @param {Object} [options.data={}] - 响应数据
|
|
91
125
|
* @param {string} [options.msg="操作成功"] - 成功消息
|
|
92
126
|
* @returns {Object} 成功响应对象
|
|
93
|
-
* @description
|
|
94
|
-
* 生成标准成功响应
|
|
95
|
-
* @example
|
|
96
|
-
* const response = success({ data: { id: 1, name: '张三' } });
|
|
97
127
|
*/
|
|
98
128
|
export const success = ({ data = {}, msg = "操作成功" } = {}) => ({
|
|
99
129
|
success: true,
|
|
@@ -101,3 +131,50 @@ export const success = ({ data = {}, msg = "操作成功" } = {}) => ({
|
|
|
101
131
|
code: 200,
|
|
102
132
|
data,
|
|
103
133
|
});
|
|
134
|
+
|
|
135
|
+
/**
|
|
136
|
+
* 生成 404 响应
|
|
137
|
+
* @param {Object} req - Express 请求对象
|
|
138
|
+
* @returns {Object} 404 响应对象
|
|
139
|
+
*/
|
|
140
|
+
export function notFoundResponse(req) {
|
|
141
|
+
return {
|
|
142
|
+
success: false,
|
|
143
|
+
msg: "接口不存在",
|
|
144
|
+
code: 404,
|
|
145
|
+
data: { path: req.path, method: req.method },
|
|
146
|
+
};
|
|
147
|
+
}
|
|
148
|
+
|
|
149
|
+
/**
|
|
150
|
+
* 生成错误响应(Express 错误处理中间件用)
|
|
151
|
+
* @param {Error} err - 错误对象
|
|
152
|
+
* @param {Object} req - Express 请求对象
|
|
153
|
+
* @returns {Object} 错误响应对象
|
|
154
|
+
*/
|
|
155
|
+
export function errorResponse(err, req) {
|
|
156
|
+
const errorInfo = parseDatabaseError(err);
|
|
157
|
+
|
|
158
|
+
console.error(`[Error Handler] ${errorInfo.msg} - ${err?.message}`, {
|
|
159
|
+
code: errorInfo.code,
|
|
160
|
+
path: req?.path,
|
|
161
|
+
method: req?.method,
|
|
162
|
+
sql: err?.sql,
|
|
163
|
+
sqlMessage: err?.sqlMessage,
|
|
164
|
+
stack: err?.stack,
|
|
165
|
+
});
|
|
166
|
+
|
|
167
|
+
return {
|
|
168
|
+
success: false,
|
|
169
|
+
msg: errorInfo.msg,
|
|
170
|
+
code: errorInfo.code,
|
|
171
|
+
data: process.env.NODE_ENV === "development"
|
|
172
|
+
? {
|
|
173
|
+
message: err?.message,
|
|
174
|
+
sql: err?.sql,
|
|
175
|
+
sqlMessage: err?.sqlMessage,
|
|
176
|
+
stack: err?.stack,
|
|
177
|
+
}
|
|
178
|
+
: {},
|
|
179
|
+
};
|
|
180
|
+
}
|
package/base/Context.js
DELETED
|
@@ -1,78 +0,0 @@
|
|
|
1
|
-
/**
|
|
2
|
-
* 应用上下文类
|
|
3
|
-
* 用于存储和管理应用的配置和模型实例
|
|
4
|
-
*/
|
|
5
|
-
class AppContext {
|
|
6
|
-
/**
|
|
7
|
-
* 构造函数
|
|
8
|
-
* 初始化配置和模型的Map存储
|
|
9
|
-
*/
|
|
10
|
-
constructor() {
|
|
11
|
-
this._config = new Map();
|
|
12
|
-
this._models = new Map();
|
|
13
|
-
}
|
|
14
|
-
|
|
15
|
-
/**
|
|
16
|
-
* 设置配置项
|
|
17
|
-
* @param {string} key - 配置键名
|
|
18
|
-
* @param {*} value - 配置值
|
|
19
|
-
*/
|
|
20
|
-
set(key, value) {
|
|
21
|
-
this._config.set(key, value);
|
|
22
|
-
}
|
|
23
|
-
|
|
24
|
-
/**
|
|
25
|
-
* 获取配置项
|
|
26
|
-
* @param {string} key - 配置键名
|
|
27
|
-
* @param {*} defaultValue - 默认值
|
|
28
|
-
* @returns {*} 配置值或默认值
|
|
29
|
-
*/
|
|
30
|
-
get(key, defaultValue) {
|
|
31
|
-
return this._config.get(key) ?? defaultValue;
|
|
32
|
-
}
|
|
33
|
-
|
|
34
|
-
/**
|
|
35
|
-
* 检查配置项是否存在
|
|
36
|
-
* @param {string} key - 配置键名
|
|
37
|
-
* @returns {boolean} 是否存在
|
|
38
|
-
*/
|
|
39
|
-
has(key) {
|
|
40
|
-
return this._config.has(key);
|
|
41
|
-
}
|
|
42
|
-
|
|
43
|
-
/**
|
|
44
|
-
* 设置模型实例
|
|
45
|
-
* @param {string} name - 模型名称
|
|
46
|
-
* @param {Object} instance - 模型实例
|
|
47
|
-
*/
|
|
48
|
-
setModel(name, instance) {
|
|
49
|
-
this._models.set(name, instance);
|
|
50
|
-
}
|
|
51
|
-
|
|
52
|
-
/**
|
|
53
|
-
* 获取模型实例
|
|
54
|
-
* @param {string} name - 模型名称
|
|
55
|
-
* @returns {Object|undefined} 模型实例
|
|
56
|
-
*/
|
|
57
|
-
getModel(name) {
|
|
58
|
-
return this._models.get(name);
|
|
59
|
-
}
|
|
60
|
-
|
|
61
|
-
/**
|
|
62
|
-
* 获取所有模型实例
|
|
63
|
-
* @returns {Object} 包含所有模型的对象
|
|
64
|
-
*/
|
|
65
|
-
getAllModels() {
|
|
66
|
-
return Object.fromEntries(this._models.entries());
|
|
67
|
-
}
|
|
68
|
-
|
|
69
|
-
/**
|
|
70
|
-
* 清空所有配置和模型
|
|
71
|
-
*/
|
|
72
|
-
clear() {
|
|
73
|
-
this._config.clear();
|
|
74
|
-
this._models.clear();
|
|
75
|
-
}
|
|
76
|
-
}
|
|
77
|
-
|
|
78
|
-
export default AppContext;
|