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/App.js
CHANGED
|
@@ -2,18 +2,18 @@ import express from "express";
|
|
|
2
2
|
import fs from "fs";
|
|
3
3
|
import path from "path";
|
|
4
4
|
|
|
5
|
-
|
|
5
|
+
// 引入基础组件
|
|
6
6
|
import Controller from "./base/Controller.js";
|
|
7
|
-
import DatabaseManager from "./base/Database.js";
|
|
8
|
-
|
|
9
7
|
import Service from "./base/Service.js";
|
|
8
|
+
import DatabaseManager from "./base/Database.js";
|
|
10
9
|
|
|
10
|
+
// 引入配置和工具
|
|
11
11
|
import { Paths } from "./config/index.js";
|
|
12
12
|
import { getChildrenId, filterBody, filterImgFromStr, CODE as commonCode, sendMail, genRegEmailHtml, genResetPasswordEmail, pages, getHtmlFilesSync } from "./common/index.js";
|
|
13
|
-
import {
|
|
13
|
+
import { loadConfig, loadController, loaderSort, formatDateFields, request, delImg, getIp, setToken, getToken, verifyToken, generateToken, getFileTree, readFileContent, saveFileContent, isPathSafe, arrToObj, htmlDecode } from "./helper/index.js";
|
|
14
14
|
import { Cors, setBody, setCookie, setFavicon, setHeader, setStatic, setTemplate, waf } from "./middleware/index.js";
|
|
15
|
-
import { errorResponse, notFoundResponse, parseDatabaseError } from "./utils/
|
|
16
|
-
import {
|
|
15
|
+
import { errorResponse, notFoundResponse, parseDatabaseError, error as responseError, fail, success, checkKeywords } from "./utils/index.js";
|
|
16
|
+
import { importFile, importjs } from "./global/import.js";
|
|
17
17
|
|
|
18
18
|
import "./global/index.js";
|
|
19
19
|
|
|
@@ -22,14 +22,14 @@ import "./global/index.js";
|
|
|
22
22
|
* @class Chan
|
|
23
23
|
* @description 管理应用程序的路由、中间件、数据库连接和服务
|
|
24
24
|
* @example
|
|
25
|
-
* const app = new Chan(
|
|
25
|
+
* const app = new Chan();
|
|
26
26
|
* await app.start();
|
|
27
27
|
*/
|
|
28
28
|
class Chan {
|
|
29
|
+
|
|
30
|
+
// 静态属性:提供全局访问的辅助函数和工具
|
|
29
31
|
static helper = {
|
|
30
32
|
loadController,
|
|
31
|
-
db,
|
|
32
|
-
prefixDbConfig,
|
|
33
33
|
loaderSort,
|
|
34
34
|
loadConfig,
|
|
35
35
|
formatDateFields,
|
|
@@ -47,6 +47,8 @@ class Chan {
|
|
|
47
47
|
arrToObj,
|
|
48
48
|
htmlDecode,
|
|
49
49
|
};
|
|
50
|
+
|
|
51
|
+
// 静态属性:提供全局访问的常用业务函数和工具
|
|
50
52
|
static common = {
|
|
51
53
|
success,
|
|
52
54
|
fail,
|
|
@@ -61,31 +63,35 @@ class Chan {
|
|
|
61
63
|
filterBody,
|
|
62
64
|
filterImgFromStr,
|
|
63
65
|
};
|
|
66
|
+
|
|
67
|
+
// 静态属性:提供全局访问的配置
|
|
64
68
|
static config = {};
|
|
69
|
+
// 静态属性:提供全局访问的数据库
|
|
70
|
+
static db = {};
|
|
71
|
+
// 静态属性:提供全局访问的服务类
|
|
65
72
|
static Service = Service;
|
|
73
|
+
// 静态属性:提供全局访问的控制器类
|
|
66
74
|
static Controller = Controller;
|
|
75
|
+
// 静态属性:提供全局访问的路径配置
|
|
67
76
|
static paths = Paths;
|
|
77
|
+
// 静态属性:提供全局访问的导入函数
|
|
78
|
+
static importFile = importFile;
|
|
79
|
+
static importjs = importjs;
|
|
80
|
+
// 静态属性:提供全局访问的工具函数
|
|
81
|
+
static utils = {
|
|
82
|
+
checkKeywords,
|
|
83
|
+
};
|
|
68
84
|
|
|
69
85
|
/**
|
|
70
86
|
* 构造函数
|
|
71
87
|
* @constructor
|
|
72
|
-
* @param {Object} options - 应用配置选项
|
|
73
|
-
* @param {number} [options.port=3000] - 服务器端口
|
|
74
|
-
* @param {string} [options.env] - 运行环境
|
|
75
88
|
* @description 初始化 Express 应用和核心组件
|
|
76
89
|
*/
|
|
77
|
-
constructor(
|
|
90
|
+
constructor() {
|
|
78
91
|
this.app = express();
|
|
79
|
-
|
|
92
|
+
// 数据库连接管理
|
|
80
93
|
this.dbManager = new DatabaseManager();
|
|
81
|
-
|
|
82
|
-
this.options = {
|
|
83
|
-
port: options.port || 3000,
|
|
84
|
-
env: options.env || process.env.NODE_ENV || "development",
|
|
85
|
-
...options,
|
|
86
|
-
};
|
|
87
|
-
|
|
88
|
-
this._isStarted = false;
|
|
94
|
+
// 应用路由
|
|
89
95
|
this.router = express.Router();
|
|
90
96
|
}
|
|
91
97
|
|
|
@@ -97,63 +103,52 @@ class Chan {
|
|
|
97
103
|
* @returns {Promise<void>}
|
|
98
104
|
*/
|
|
99
105
|
async start() {
|
|
100
|
-
|
|
101
|
-
|
|
102
|
-
|
|
103
|
-
|
|
104
|
-
|
|
105
|
-
|
|
106
|
-
|
|
107
|
-
|
|
108
|
-
|
|
109
|
-
|
|
110
|
-
|
|
111
|
-
await this.initMiddleware();
|
|
112
|
-
|
|
113
|
-
this._applyConfig();
|
|
114
|
-
|
|
106
|
+
//加载配置
|
|
107
|
+
await this.config();
|
|
108
|
+
//加载数据库
|
|
109
|
+
await this.loadDB();
|
|
110
|
+
//加载扩展方法
|
|
111
|
+
await this.loadExtend();
|
|
112
|
+
//加载中间件
|
|
113
|
+
await this.loadAppMiddleware();
|
|
114
|
+
//设置app
|
|
115
|
+
this.setApp();
|
|
116
|
+
//加载路由
|
|
115
117
|
await this.loadRouter();
|
|
116
118
|
await this.loadCommonRouter();
|
|
117
|
-
|
|
118
|
-
|
|
119
|
-
this.
|
|
120
|
-
|
|
121
|
-
await this._listen();
|
|
122
|
-
|
|
123
|
-
this._isStarted = true;
|
|
119
|
+
this.useRouter();
|
|
120
|
+
//404 500 处理
|
|
121
|
+
this.setErrorHandler();
|
|
124
122
|
}
|
|
125
123
|
|
|
126
124
|
/**
|
|
127
|
-
*
|
|
125
|
+
* 加载配置
|
|
128
126
|
* @async
|
|
129
127
|
* @returns {Promise<void>}
|
|
130
|
-
* @description
|
|
128
|
+
* @description 加载应用配置并设置到全局变量
|
|
131
129
|
*/
|
|
132
|
-
async
|
|
130
|
+
async config() {
|
|
133
131
|
let config = await loadConfig();
|
|
134
132
|
Chan.config = config;
|
|
135
|
-
this.context.set("config", config);
|
|
136
133
|
}
|
|
137
134
|
|
|
138
135
|
/**
|
|
139
|
-
*
|
|
136
|
+
* 加载数据库
|
|
140
137
|
* @async
|
|
141
138
|
* @returns {Promise<void>}
|
|
142
139
|
* @description 根据配置初始化所有数据库连接
|
|
143
140
|
*/
|
|
144
|
-
async
|
|
141
|
+
async loadDB() {
|
|
145
142
|
const dbList = Chan.config?.db || [];
|
|
146
|
-
const connections = this.dbManager.getConnections();
|
|
147
143
|
|
|
148
144
|
for (const [index, item] of dbList.entries()) {
|
|
149
145
|
const key = item.key || String(index);
|
|
150
146
|
try {
|
|
151
|
-
const dbConfig =
|
|
147
|
+
const dbConfig = item;
|
|
152
148
|
if (!dbConfig) throw new Error("未找到配置");
|
|
153
149
|
|
|
154
150
|
const connection = this.dbManager.add(key, dbConfig, { isDefault: index === 0 });
|
|
155
|
-
|
|
156
|
-
|
|
151
|
+
|
|
157
152
|
if (index === 0) {
|
|
158
153
|
Chan.db = connection;
|
|
159
154
|
}
|
|
@@ -163,17 +158,17 @@ class Chan {
|
|
|
163
158
|
}
|
|
164
159
|
|
|
165
160
|
console.log(
|
|
166
|
-
`[DB] 初始化完成,已加载 ${dbList.length}
|
|
161
|
+
`[DB] 初始化完成,已加载 ${dbList.length} 个数据库`,
|
|
167
162
|
);
|
|
168
163
|
}
|
|
169
164
|
|
|
170
165
|
/**
|
|
171
|
-
*
|
|
166
|
+
* 加载扩展模块
|
|
172
167
|
* @async
|
|
173
168
|
* @returns {Promise<void>}
|
|
174
169
|
* @description 加载 common、helper、extend 目录下的扩展模块
|
|
175
170
|
*/
|
|
176
|
-
async
|
|
171
|
+
async loadExtend() {
|
|
177
172
|
const extensions = [
|
|
178
173
|
{ _path: Paths.commonPath, key: "common" },
|
|
179
174
|
{ _path: Paths.helperPath, key: "helper" },
|
|
@@ -198,7 +193,7 @@ class Chan {
|
|
|
198
193
|
const files = fs.readdirSync(_path).filter((file) => file.endsWith(".js"));
|
|
199
194
|
for (const file of files) {
|
|
200
195
|
const filePath = path.join(_path, file);
|
|
201
|
-
let helperModule = await importFile(filePath);
|
|
196
|
+
let helperModule = await Chan.importFile(filePath);
|
|
202
197
|
Object.assign(Chan[key], helperModule);
|
|
203
198
|
}
|
|
204
199
|
}
|
|
@@ -210,10 +205,21 @@ class Chan {
|
|
|
210
205
|
* @returns {Promise<void>}
|
|
211
206
|
* @description 配置并注册所有应用中间件
|
|
212
207
|
*/
|
|
213
|
-
async
|
|
208
|
+
async loadAppMiddleware() {
|
|
214
209
|
const config = Chan.config;
|
|
215
|
-
const {
|
|
216
|
-
|
|
210
|
+
const {
|
|
211
|
+
views = [],
|
|
212
|
+
env = "development",
|
|
213
|
+
APP_NAME = "ChanCMS",
|
|
214
|
+
APP_VERSION = "1.0.0",
|
|
215
|
+
cookieKey,
|
|
216
|
+
BODY_LIMIT = "10mb",
|
|
217
|
+
statics = [],
|
|
218
|
+
cors = {},
|
|
219
|
+
PROXY = "false",
|
|
220
|
+
waf: wafConfig = { enabled: false }
|
|
221
|
+
} = config;
|
|
222
|
+
|
|
217
223
|
await waf(this.app, wafConfig);
|
|
218
224
|
setFavicon(this.app);
|
|
219
225
|
setStatic(this.app, statics);
|
|
@@ -229,9 +235,8 @@ class Chan {
|
|
|
229
235
|
* @private
|
|
230
236
|
* @description 设置 Express 应用配置
|
|
231
237
|
*/
|
|
232
|
-
|
|
238
|
+
setApp() {
|
|
233
239
|
this.app.set("trust proxy", Chan.config.PROXY === "true");
|
|
234
|
-
this.app.set("env", this.options.env);
|
|
235
240
|
this.app.disable("x-powered-by");
|
|
236
241
|
}
|
|
237
242
|
|
|
@@ -240,20 +245,22 @@ class Chan {
|
|
|
240
245
|
* @private
|
|
241
246
|
* @description 将所有路由应用到 Express 应用
|
|
242
247
|
*/
|
|
243
|
-
|
|
248
|
+
useRouter() {
|
|
244
249
|
this.app.use(this.router);
|
|
245
250
|
}
|
|
246
251
|
|
|
247
252
|
/**
|
|
248
|
-
* 应用错误处理
|
|
253
|
+
* 应用错误处理404 500
|
|
249
254
|
* @private
|
|
250
255
|
* @description 配置全局错误处理中间件
|
|
251
256
|
*/
|
|
252
|
-
|
|
257
|
+
setErrorHandler() {
|
|
258
|
+
//404
|
|
253
259
|
this.app.use((req, res) => {
|
|
254
260
|
res.status(404).json(notFoundResponse(req));
|
|
255
261
|
});
|
|
256
262
|
|
|
263
|
+
// 500
|
|
257
264
|
this.app.use((err, req, res, next) => {
|
|
258
265
|
if (res.headersSent) return next(err);
|
|
259
266
|
|
|
@@ -326,23 +333,7 @@ class Chan {
|
|
|
326
333
|
return { message: stackLines[0] || '未知错误', file: errorLine };
|
|
327
334
|
}
|
|
328
335
|
|
|
329
|
-
|
|
330
|
-
* 启动服务器监听
|
|
331
|
-
* @async
|
|
332
|
-
* @private
|
|
333
|
-
* @returns {Promise<void>}
|
|
334
|
-
* @description 启动 HTTP 服务器监听指定端口
|
|
335
|
-
*/
|
|
336
|
-
async _listen() {
|
|
337
|
-
return new Promise((resolve, reject) => {
|
|
338
|
-
const server = this.app.listen(this.options.port, () => {
|
|
339
|
-
console.log(`Server running on port ${this.options.port}`);
|
|
340
|
-
resolve(server);
|
|
341
|
-
});
|
|
342
|
-
|
|
343
|
-
server.on("error", reject);
|
|
344
|
-
});
|
|
345
|
-
}
|
|
336
|
+
|
|
346
337
|
|
|
347
338
|
/**
|
|
348
339
|
* 加载模块路由
|
|
@@ -355,7 +346,7 @@ class Chan {
|
|
|
355
346
|
if (fs.existsSync(configPath)) {
|
|
356
347
|
const dirs = loaderSort(Chan.config.modules);
|
|
357
348
|
for (const item of dirs) {
|
|
358
|
-
let router = await importFile(`app/modules/${item}/router.js`);
|
|
349
|
+
let router = await Chan.importFile(`app/modules/${item}/router.js`);
|
|
359
350
|
router(this.app, this.router, Chan.config);
|
|
360
351
|
}
|
|
361
352
|
}
|
|
@@ -368,7 +359,7 @@ class Chan {
|
|
|
368
359
|
* @description 加载 app/router.js 中的公共路由
|
|
369
360
|
*/
|
|
370
361
|
async loadCommonRouter() {
|
|
371
|
-
let router = await importFile("app/router.js");
|
|
362
|
+
let router = await Chan.importFile("app/router.js");
|
|
372
363
|
if (router) {
|
|
373
364
|
router(this.app, this.router, Chan.config);
|
|
374
365
|
}
|
|
@@ -379,9 +370,14 @@ class Chan {
|
|
|
379
370
|
* @param {Function} [cb] - 回调函数
|
|
380
371
|
* @description 在启动前执行回调,传递端口号
|
|
381
372
|
*/
|
|
382
|
-
|
|
383
|
-
|
|
373
|
+
run(cb) {
|
|
374
|
+
this.app.listen(Chan.config.PORT, () => {
|
|
375
|
+
console.log(`Server running on port ${Chan.config.PORT}`);
|
|
376
|
+
cb?.(Chan.config.PORT);
|
|
377
|
+
});
|
|
384
378
|
}
|
|
385
379
|
}
|
|
386
380
|
|
|
381
|
+
global.Chan = Chan;
|
|
382
|
+
|
|
387
383
|
export default Chan;
|
package/base/Controller.js
CHANGED
|
@@ -1,92 +1,21 @@
|
|
|
1
|
-
import { success, fail
|
|
1
|
+
import { success, fail } from "../utils/response.js";
|
|
2
2
|
|
|
3
3
|
/**
|
|
4
4
|
* 控制器基类
|
|
5
5
|
* 提供统一的响应格式和常用方法
|
|
6
6
|
*/
|
|
7
7
|
export default class Controller {
|
|
8
|
-
|
|
9
|
-
* 构造函数
|
|
10
|
-
* @param {Object} context - 应用上下文实例
|
|
11
|
-
*/
|
|
12
|
-
constructor(context = null) {
|
|
13
|
-
if (context) {
|
|
14
|
-
this.context = context;
|
|
15
|
-
} else if (global.appContext) {
|
|
16
|
-
this.context = global.appContext;
|
|
17
|
-
} else {
|
|
18
|
-
this.context = { get: () => null, set: () => null };
|
|
19
|
-
}
|
|
20
|
-
}
|
|
21
|
-
|
|
22
|
-
/**
|
|
23
|
-
* 标准化返回结果
|
|
24
|
-
* @private
|
|
25
|
-
* @param {*} result - 原始结果
|
|
26
|
-
* @param {string} defaultMsg - 默认消息
|
|
27
|
-
* @returns {Object} 标准化的结果对象
|
|
28
|
-
*/
|
|
29
|
-
_normalizeResult(result, defaultMsg = "操作成功") {
|
|
30
|
-
if (result === null || result === undefined) {
|
|
31
|
-
return { success: false, code: 5001, msg: "Service 返回空值", data: {} };
|
|
32
|
-
}
|
|
33
|
-
|
|
34
|
-
if (typeof result === 'object' && 'success' in result) {
|
|
35
|
-
return result;
|
|
36
|
-
}
|
|
37
|
-
|
|
38
|
-
if (typeof result === 'object' || Array.isArray(result)) {
|
|
39
|
-
return { success: true, code: 200, msg: defaultMsg, data: result };
|
|
40
|
-
}
|
|
41
|
-
|
|
42
|
-
return { success: true, code: 200, msg: defaultMsg, data: result };
|
|
43
|
-
}
|
|
8
|
+
constructor() {}
|
|
44
9
|
|
|
45
10
|
/**
|
|
46
11
|
* 返回成功响应
|
|
47
|
-
* @param {
|
|
12
|
+
* @param {Object} options - 响应选项
|
|
48
13
|
* @param {*} options.data - 响应数据
|
|
49
14
|
* @param {string} options.msg - 响应消息,默认"操作成功"
|
|
50
15
|
* @returns {Object} 标准成功响应
|
|
51
16
|
*/
|
|
52
|
-
success(
|
|
53
|
-
|
|
54
|
-
|
|
55
|
-
if (options === null || options === undefined) {
|
|
56
|
-
data = undefined;
|
|
57
|
-
} else if (typeof options === 'object' && options !== null && !Array.isArray(options)) {
|
|
58
|
-
if ('data' in options) {
|
|
59
|
-
data = options.data;
|
|
60
|
-
message = options.msg || msg;
|
|
61
|
-
extra = { ...options };
|
|
62
|
-
delete extra.data;
|
|
63
|
-
delete extra.msg;
|
|
64
|
-
} else if ('success' in options && 'code' in options) {
|
|
65
|
-
return options;
|
|
66
|
-
} else {
|
|
67
|
-
data = options;
|
|
68
|
-
}
|
|
69
|
-
} else {
|
|
70
|
-
data = options;
|
|
71
|
-
}
|
|
72
|
-
|
|
73
|
-
if (data instanceof Error) {
|
|
74
|
-
return this.error({ err: data });
|
|
75
|
-
}
|
|
76
|
-
|
|
77
|
-
const normalized = this._normalizeResult(data, message);
|
|
78
|
-
|
|
79
|
-
if (!normalized.success) {
|
|
80
|
-
return this.fail({ msg: normalized.msg, data: normalized.data, code: normalized.code });
|
|
81
|
-
}
|
|
82
|
-
|
|
83
|
-
return {
|
|
84
|
-
success: true,
|
|
85
|
-
code: 200,
|
|
86
|
-
msg: normalized.msg,
|
|
87
|
-
data: normalized.data,
|
|
88
|
-
...extra
|
|
89
|
-
};
|
|
17
|
+
success({ data, msg = "操作成功" } = {}) {
|
|
18
|
+
return success({ data, msg });
|
|
90
19
|
}
|
|
91
20
|
|
|
92
21
|
/**
|
|
@@ -100,38 +29,4 @@ export default class Controller {
|
|
|
100
29
|
fail({ msg = "操作失败", data = {}, code = 201 } = {}) {
|
|
101
30
|
return fail({ msg, data, code });
|
|
102
31
|
}
|
|
103
|
-
|
|
104
|
-
/**
|
|
105
|
-
* 返回错误响应
|
|
106
|
-
* @param {Object} options - 响应选项
|
|
107
|
-
* @param {Error} options.err - 错误对象
|
|
108
|
-
* @param {*} options.data - 响应数据
|
|
109
|
-
* @param {number} options.code - 错误码,默认500
|
|
110
|
-
* @returns {Object} 标准错误响应
|
|
111
|
-
*/
|
|
112
|
-
error({ err, data = {}, code = 500 } = {}) {
|
|
113
|
-
return responseError({ err, data, code });
|
|
114
|
-
}
|
|
115
|
-
|
|
116
|
-
/**
|
|
117
|
-
* 返回分页响应
|
|
118
|
-
* @param {Array} list - 数据列表
|
|
119
|
-
* @param {number} total - 总记录数
|
|
120
|
-
* @param {number} current - 当前页码
|
|
121
|
-
* @param {number} pageSize - 每页大小
|
|
122
|
-
* @returns {Object} 标准分页响应
|
|
123
|
-
*/
|
|
124
|
-
paginate(list, total, current, pageSize) {
|
|
125
|
-
return success({
|
|
126
|
-
data: {
|
|
127
|
-
list,
|
|
128
|
-
pagination: {
|
|
129
|
-
total,
|
|
130
|
-
current,
|
|
131
|
-
pageSize,
|
|
132
|
-
totalPages: Math.ceil(total / pageSize),
|
|
133
|
-
},
|
|
134
|
-
},
|
|
135
|
-
});
|
|
136
|
-
}
|
|
137
32
|
}
|