chanjs 2.1.1 → 2.3.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 +387 -0
- package/base/Context.js +78 -0
- package/base/Controller.js +137 -0
- package/base/Database.js +314 -0
- package/base/Service.js +539 -0
- package/common/api.js +25 -0
- package/common/category.js +22 -0
- package/common/code.js +42 -0
- package/common/email.js +110 -0
- package/common/index.js +7 -0
- package/common/pages.js +86 -0
- package/common/sms.js +104 -0
- package/common/utils.js +73 -0
- package/config/code.js +110 -52
- package/config/index.js +10 -0
- package/config/paths.js +60 -0
- package/extend/art-template.js +46 -28
- package/extend/index.js +6 -0
- package/global/env.js +11 -5
- package/global/global.js +63 -39
- package/global/import.js +43 -39
- package/global/index.js +8 -3
- package/helper/cache.js +182 -0
- package/helper/data-parse.js +121 -37
- package/helper/db.js +71 -83
- package/helper/file.js +158 -208
- package/helper/filter.js +34 -0
- package/helper/html.js +30 -47
- package/helper/index.js +29 -5
- package/helper/ip.js +48 -31
- package/helper/jwt.js +78 -11
- package/helper/loader.js +93 -50
- package/helper/request.js +41 -144
- package/helper/sign.js +96 -33
- package/helper/time.js +89 -74
- package/helper/tree.js +77 -0
- package/index.js +15 -181
- package/middleware/cookie.js +20 -4
- package/middleware/cors.js +20 -0
- package/middleware/favicon.js +21 -5
- package/middleware/header.js +26 -9
- package/middleware/index.js +14 -23
- package/middleware/preventRetry.js +30 -0
- package/middleware/setBody.js +24 -10
- package/middleware/static.js +31 -10
- package/middleware/template.js +34 -14
- package/middleware/validator.js +43 -23
- package/middleware/waf.js +147 -287
- package/package.json +1 -1
- package/utils/checker.js +68 -0
- package/utils/error-handler.js +115 -0
- package/utils/error.js +81 -0
- package/utils/index.js +6 -0
- package/utils/keywords.js +126 -0
- package/utils/rate-limit.js +116 -0
- package/utils/response.js +103 -64
- package/utils/xss-filter.js +42 -0
- package/core/controller.js +0 -33
- package/core/index.js +0 -3
- package/core/service.js +0 -307
- package/middleware/log.js +0 -21
package/helper/time.js
CHANGED
|
@@ -1,74 +1,89 @@
|
|
|
1
|
-
|
|
2
|
-
|
|
3
|
-
|
|
4
|
-
|
|
5
|
-
|
|
6
|
-
|
|
7
|
-
|
|
8
|
-
|
|
9
|
-
*
|
|
10
|
-
*
|
|
11
|
-
*
|
|
12
|
-
*
|
|
13
|
-
*
|
|
14
|
-
|
|
15
|
-
|
|
16
|
-
|
|
17
|
-
|
|
18
|
-
|
|
19
|
-
|
|
20
|
-
|
|
21
|
-
|
|
22
|
-
|
|
23
|
-
|
|
24
|
-
}
|
|
25
|
-
|
|
26
|
-
|
|
27
|
-
|
|
28
|
-
|
|
29
|
-
|
|
30
|
-
|
|
31
|
-
|
|
32
|
-
|
|
33
|
-
|
|
34
|
-
|
|
35
|
-
|
|
36
|
-
|
|
37
|
-
|
|
38
|
-
|
|
39
|
-
|
|
40
|
-
|
|
41
|
-
|
|
42
|
-
}
|
|
43
|
-
|
|
44
|
-
|
|
45
|
-
|
|
46
|
-
|
|
47
|
-
|
|
48
|
-
|
|
49
|
-
|
|
50
|
-
|
|
51
|
-
|
|
52
|
-
|
|
53
|
-
|
|
54
|
-
|
|
55
|
-
|
|
56
|
-
|
|
57
|
-
|
|
58
|
-
|
|
59
|
-
|
|
60
|
-
|
|
61
|
-
|
|
62
|
-
|
|
63
|
-
|
|
64
|
-
|
|
65
|
-
|
|
66
|
-
|
|
67
|
-
|
|
68
|
-
|
|
69
|
-
|
|
70
|
-
|
|
71
|
-
|
|
72
|
-
|
|
73
|
-
|
|
74
|
-
|
|
1
|
+
/**
|
|
2
|
+
* 格式化时间戳为指定格式的字符串
|
|
3
|
+
* @param {number|string|Date} timestamp - 时间戳、日期字符串或 Date 对象
|
|
4
|
+
* @param {string} [format='YYYY-MM-DD HH:mm:ss'] - 输出格式
|
|
5
|
+
* @returns {string} 格式化后的时间字符串
|
|
6
|
+
* @description
|
|
7
|
+
* 支持的格式占位符:
|
|
8
|
+
* - YYYY: 四位年份
|
|
9
|
+
* - MM: 两位月份(01-12)
|
|
10
|
+
* - DD: 两位日期(01-31)
|
|
11
|
+
* - HH: 两位小时(00-23)
|
|
12
|
+
* - mm: 两位分钟(00-59)
|
|
13
|
+
* - ss: 两位秒数(00-59)
|
|
14
|
+
* @example
|
|
15
|
+
* formatTime(1704067200000); // '2024-01-01 00:00:00'
|
|
16
|
+
* formatTime(1704067200000, 'YYYY/MM/DD'); // '2024/01/01'
|
|
17
|
+
* formatTime('2024-01-01', 'YYYY-MM-DD'); // '2024-01-01'
|
|
18
|
+
*/
|
|
19
|
+
export function formatTime(timestamp, format = 'YYYY-MM-DD HH:mm:ss') {
|
|
20
|
+
try {
|
|
21
|
+
const date = new Date(timestamp);
|
|
22
|
+
if (isNaN(date.getTime())) {
|
|
23
|
+
return timestamp;
|
|
24
|
+
}
|
|
25
|
+
const year = date.getFullYear();
|
|
26
|
+
const month = String(date.getMonth() + 1).padStart(2, '0');
|
|
27
|
+
const day = String(date.getDate()).padStart(2, '0');
|
|
28
|
+
const hours = String(date.getHours()).padStart(2, '0');
|
|
29
|
+
const minutes = String(date.getMinutes()).padStart(2, '0');
|
|
30
|
+
const seconds = String(date.getSeconds()).padStart(2, '0');
|
|
31
|
+
|
|
32
|
+
return format
|
|
33
|
+
.replace('YYYY', year)
|
|
34
|
+
.replace('MM', month)
|
|
35
|
+
.replace('DD', day)
|
|
36
|
+
.replace('HH', hours)
|
|
37
|
+
.replace('mm', minutes)
|
|
38
|
+
.replace('ss', seconds);
|
|
39
|
+
} catch (error) {
|
|
40
|
+
console.error('[formatTime] 格式化时间失败:', error, 'timestamp:', timestamp);
|
|
41
|
+
return timestamp;
|
|
42
|
+
}
|
|
43
|
+
}
|
|
44
|
+
|
|
45
|
+
/**
|
|
46
|
+
* 格式化对象或数组中的日期字段
|
|
47
|
+
* @param {Object|Array<Object>} data - 要处理的数据对象或数组
|
|
48
|
+
* @param {Array<string>} [fields] - 需要格式化的字段名数组(可选,默认处理常用日期字段)
|
|
49
|
+
* @returns {Object|Array<Object>} 处理后的数据
|
|
50
|
+
* @description
|
|
51
|
+
* 将对象或数组中指定的日期字段格式化为 'YYYY-MM-DD HH:mm:ss' 格式
|
|
52
|
+
* 支持递归处理数组中的每个对象
|
|
53
|
+
* 自动匹配下划线和驼峰格式的字段名(如 createdAt/created_at)
|
|
54
|
+
* @example
|
|
55
|
+
* const data = { id: 1, createdAt: 1704067200000, updatedAt: 1704067200000 };
|
|
56
|
+
* const result = formatDateFields(data);
|
|
57
|
+
* console.log(result);
|
|
58
|
+
* // { id: 1, createdAt: '2024-01-01 00:00:00', updatedAt: '2024-01-01 00:00:00' }
|
|
59
|
+
*
|
|
60
|
+
* const list = [
|
|
61
|
+
* { id: 1, createdAt: 1704067200000 },
|
|
62
|
+
* { id: 2, createdAt: 1704153600000 }
|
|
63
|
+
* ];
|
|
64
|
+
* const formatted = formatDateFields(list);
|
|
65
|
+
* console.log(formatted);
|
|
66
|
+
* // [
|
|
67
|
+
* // { id: 1, createdAt: '2024-01-01 00:00:00' },
|
|
68
|
+
* // { id: 2, createdAt: '2024-01-02 00:00:00' }
|
|
69
|
+
* // ]
|
|
70
|
+
*/
|
|
71
|
+
export function formatDateFields(data, fields) {
|
|
72
|
+
if (!data || typeof data !== 'object') return data;
|
|
73
|
+
|
|
74
|
+
if (Array.isArray(data)) {
|
|
75
|
+
return data.map(item => formatDateFields(item, fields));
|
|
76
|
+
}
|
|
77
|
+
|
|
78
|
+
const result = { ...data };
|
|
79
|
+
|
|
80
|
+
const defaultFields = ['createdAt', 'updatedAt', 'created_at', 'updated_at', 'createdAt', 'updatedAt'];
|
|
81
|
+
const targetFields = fields || defaultFields;
|
|
82
|
+
|
|
83
|
+
for (const field of targetFields) {
|
|
84
|
+
if (result[field]) {
|
|
85
|
+
result[field] = formatTime(result[field]);
|
|
86
|
+
}
|
|
87
|
+
}
|
|
88
|
+
return result;
|
|
89
|
+
}
|
package/helper/tree.js
ADDED
|
@@ -0,0 +1,77 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* 树形结构工具函数
|
|
3
|
+
* 提供数组转树形结构和路径查找功能
|
|
4
|
+
*/
|
|
5
|
+
|
|
6
|
+
/**
|
|
7
|
+
* 将扁平数组转换为树形结构
|
|
8
|
+
* @param {Array<Object>} arr - 扁平化的数据数组
|
|
9
|
+
* @param {number} [pid=0] - 父节点 ID,默认为 0
|
|
10
|
+
* @returns {Array<Object>} 树形结构数组
|
|
11
|
+
* @description
|
|
12
|
+
* 递归构建树形结构,为每个节点添加 level 属性
|
|
13
|
+
* 如果有子节点,则添加 children 属性
|
|
14
|
+
* @example
|
|
15
|
+
* const arr = [
|
|
16
|
+
* { id: 1, pid: 0, name: '根节点' },
|
|
17
|
+
* { id: 2, pid: 1, name: '子节点' },
|
|
18
|
+
* { id: 3, pid: 2, name: '孙节点' }
|
|
19
|
+
* ];
|
|
20
|
+
* const tree = tree(arr);
|
|
21
|
+
* // 返回带有 children 和 level 的树形结构
|
|
22
|
+
*/
|
|
23
|
+
export function tree(arr, pid = 0) {
|
|
24
|
+
if(arr.length === 0){
|
|
25
|
+
return []
|
|
26
|
+
}
|
|
27
|
+
let result = [];
|
|
28
|
+
arr.forEach((item) => {
|
|
29
|
+
if (item.pid === pid) {
|
|
30
|
+
let children = tree(arr, item.id);
|
|
31
|
+
if (children.length) {
|
|
32
|
+
item.children = children;
|
|
33
|
+
}
|
|
34
|
+
item.level = 1;
|
|
35
|
+
result.push(item);
|
|
36
|
+
}
|
|
37
|
+
});
|
|
38
|
+
return result;
|
|
39
|
+
}
|
|
40
|
+
|
|
41
|
+
/**
|
|
42
|
+
* 根据 ID 查找节点路径
|
|
43
|
+
* @param {number} id - 要查找的节点 ID
|
|
44
|
+
* @param {Array<Object>} source - 数据源数组
|
|
45
|
+
* @returns {Array<Object>} 从根节点到目标节点的路径数组
|
|
46
|
+
* @description
|
|
47
|
+
* 递归查找指定 ID 的节点及其所有父节点
|
|
48
|
+
* 为每个节点添加 path 属性,包含拼音路径
|
|
49
|
+
* @example
|
|
50
|
+
* const arr = [
|
|
51
|
+
* { id: 1, pid: 0, pinyin: 'root' },
|
|
52
|
+
* { id: 2, pid: 1, pinyin: 'child' }
|
|
53
|
+
* ];
|
|
54
|
+
* const path = treeById(2, arr);
|
|
55
|
+
* // 返回包含 root 和 child 节点的数组
|
|
56
|
+
*/
|
|
57
|
+
export function treeById(id, source) {
|
|
58
|
+
const arr = [];
|
|
59
|
+
const findId = (id, source) => {
|
|
60
|
+
for (let i = 0, item; i < source.length; i++) {
|
|
61
|
+
item = source[i];
|
|
62
|
+
if (item.id == id) {
|
|
63
|
+
arr.unshift(item);
|
|
64
|
+
if (item.pid != 0) {
|
|
65
|
+
findId(item.pid, source);
|
|
66
|
+
}
|
|
67
|
+
}
|
|
68
|
+
}
|
|
69
|
+
};
|
|
70
|
+
findId(id, source);
|
|
71
|
+
const _path = [];
|
|
72
|
+
arr.forEach((item) => {
|
|
73
|
+
_path.push("/" + item.pinyin);
|
|
74
|
+
item.path = _path.join("");
|
|
75
|
+
});
|
|
76
|
+
return arr;
|
|
77
|
+
}
|
package/index.js
CHANGED
|
@@ -1,181 +1,15 @@
|
|
|
1
|
-
|
|
2
|
-
|
|
3
|
-
|
|
4
|
-
|
|
5
|
-
|
|
6
|
-
|
|
7
|
-
|
|
8
|
-
|
|
9
|
-
|
|
10
|
-
|
|
11
|
-
|
|
12
|
-
|
|
13
|
-
|
|
14
|
-
|
|
15
|
-
|
|
16
|
-
validator,
|
|
17
|
-
waf,
|
|
18
|
-
} from "./middleware/index.js";
|
|
19
|
-
import { db, loaderSort, loadConfig } from "./helper/index.js";
|
|
20
|
-
import { dirname } from "./helper/file.js";
|
|
21
|
-
|
|
22
|
-
class Chan {
|
|
23
|
-
//版本号
|
|
24
|
-
#version = "1.0.0";
|
|
25
|
-
static helper = {};
|
|
26
|
-
static common = {}; //公共方法
|
|
27
|
-
static config = {}; //配置
|
|
28
|
-
static Service = Service; //服务
|
|
29
|
-
static Controller = Controller; //控制器
|
|
30
|
-
static extend = {}; //组件扩展
|
|
31
|
-
static middleware = {}; //中间件
|
|
32
|
-
static modules = {}; //模块
|
|
33
|
-
|
|
34
|
-
constructor() {
|
|
35
|
-
this.app = express();
|
|
36
|
-
this.router = express.Router();
|
|
37
|
-
}
|
|
38
|
-
|
|
39
|
-
beforeStart(cb) {
|
|
40
|
-
cb && cb();
|
|
41
|
-
}
|
|
42
|
-
|
|
43
|
-
async init() {
|
|
44
|
-
await this.config();
|
|
45
|
-
await this.loadExtend(); //utils
|
|
46
|
-
this.loadMiddleware();
|
|
47
|
-
this.loadDB();
|
|
48
|
-
}
|
|
49
|
-
|
|
50
|
-
//加载配置文件
|
|
51
|
-
async config() {
|
|
52
|
-
let config = await loadConfig();
|
|
53
|
-
//版本号,先从package.json中获取,如果没有,则使用默认版本号
|
|
54
|
-
config.APP_VERSION = APP_VERSION || config.APP_VERSION;
|
|
55
|
-
Chan.config = config;
|
|
56
|
-
}
|
|
57
|
-
|
|
58
|
-
//加载中间件
|
|
59
|
-
async loadMiddleware() {
|
|
60
|
-
const {
|
|
61
|
-
views,
|
|
62
|
-
env,
|
|
63
|
-
APP_NAME,
|
|
64
|
-
APP_VERSION,
|
|
65
|
-
cookieKey,
|
|
66
|
-
BODY_LIMIT,
|
|
67
|
-
statics,
|
|
68
|
-
logger,
|
|
69
|
-
cors,
|
|
70
|
-
PROXY,
|
|
71
|
-
} = Chan.config;
|
|
72
|
-
|
|
73
|
-
this.app.set("trust proxy", PROXY === "true" ? true : false);
|
|
74
|
-
log(this.app, logger);
|
|
75
|
-
setFavicon(this.app);
|
|
76
|
-
setCookie(this.app, cookieKey);
|
|
77
|
-
setBody(this.app, BODY_LIMIT);
|
|
78
|
-
waf(this.app);
|
|
79
|
-
setTemplate(this.app, { views, env });
|
|
80
|
-
setStatic(this.app, statics);
|
|
81
|
-
Cors(this.app, cors);
|
|
82
|
-
setHeader(this.app, { APP_NAME, APP_VERSION });
|
|
83
|
-
}
|
|
84
|
-
|
|
85
|
-
//数据库操作
|
|
86
|
-
loadDB() {
|
|
87
|
-
if (Chan.config?.db?.length > 0) {
|
|
88
|
-
Chan.config.db.map((item, index) => {
|
|
89
|
-
if (index == 0) {
|
|
90
|
-
Chan.knex = db(item);
|
|
91
|
-
} else {
|
|
92
|
-
Chan[`knex${index}`] = db(item);
|
|
93
|
-
}
|
|
94
|
-
});
|
|
95
|
-
}
|
|
96
|
-
}
|
|
97
|
-
|
|
98
|
-
async loadRouter() {
|
|
99
|
-
const configPath = path.join(APP_PATH, "modules");
|
|
100
|
-
if (fs.existsSync(configPath)) {
|
|
101
|
-
const dirs = loaderSort(Chan.config.modules);
|
|
102
|
-
for (const item of dirs) {
|
|
103
|
-
let router = await importFile(`app/modules/${item}/router.js`);
|
|
104
|
-
router(this.app, this.router, Chan.config);
|
|
105
|
-
}
|
|
106
|
-
}
|
|
107
|
-
}
|
|
108
|
-
|
|
109
|
-
//通用路由,加载错误处理和500路由和爬虫处理
|
|
110
|
-
async loadCommonRouter() {
|
|
111
|
-
try {
|
|
112
|
-
let router = await importFile("app/router.js");
|
|
113
|
-
router(this.app, this.router, Chan.config);
|
|
114
|
-
} catch (error) {
|
|
115
|
-
console.log(error);
|
|
116
|
-
}
|
|
117
|
-
}
|
|
118
|
-
|
|
119
|
-
async loadExtend() {
|
|
120
|
-
let arr = [
|
|
121
|
-
{
|
|
122
|
-
_path: COMMON_PATH,
|
|
123
|
-
key: "common",
|
|
124
|
-
},
|
|
125
|
-
{
|
|
126
|
-
_path: HELPER_PATH,
|
|
127
|
-
key: "helper",
|
|
128
|
-
},
|
|
129
|
-
{
|
|
130
|
-
_path: path.join(dirname(import.meta.url), "helper"),
|
|
131
|
-
key: "helper",
|
|
132
|
-
},
|
|
133
|
-
];
|
|
134
|
-
for (let item of arr) {
|
|
135
|
-
await this.loadFn(item._path, item.key);
|
|
136
|
-
}
|
|
137
|
-
}
|
|
138
|
-
|
|
139
|
-
async loadFn(_path, key) {
|
|
140
|
-
if (fs.existsSync(_path)) {
|
|
141
|
-
const files = fs
|
|
142
|
-
.readdirSync(_path)
|
|
143
|
-
.filter((file) => file.endsWith(".js"));
|
|
144
|
-
for (const file of files) {
|
|
145
|
-
const filePath = path.join(_path, file);
|
|
146
|
-
let helperModule = await importFile(filePath);
|
|
147
|
-
// Chan.common[file.replace(".js", "")] = helperModule;
|
|
148
|
-
// 将模块导出的所有方法/属性,直接混入到 Chan.common 上
|
|
149
|
-
Object.assign(Chan[key], helperModule);
|
|
150
|
-
}
|
|
151
|
-
}
|
|
152
|
-
}
|
|
153
|
-
|
|
154
|
-
async start(cb) {
|
|
155
|
-
await this.init();
|
|
156
|
-
await this.loadRouter();
|
|
157
|
-
await this.loadCommonRouter();
|
|
158
|
-
|
|
159
|
-
cb && cb();
|
|
160
|
-
}
|
|
161
|
-
|
|
162
|
-
getAllRouter() {
|
|
163
|
-
// 获取所有路径
|
|
164
|
-
const routes = this.router.stack
|
|
165
|
-
.filter((r) => r.route) // 过滤掉中间件
|
|
166
|
-
.map((r) => ({
|
|
167
|
-
path: r.route.path,
|
|
168
|
-
methods: Object.keys(r.route.methods),
|
|
169
|
-
}));
|
|
170
|
-
}
|
|
171
|
-
|
|
172
|
-
run(cb) {
|
|
173
|
-
const port = parseInt(Chan.config.PORT) || 3000;
|
|
174
|
-
this.app.listen(port, () => {
|
|
175
|
-
cb ? cb(port) : console.log(`Server is running on port ${port}`);
|
|
176
|
-
});
|
|
177
|
-
}
|
|
178
|
-
}
|
|
179
|
-
|
|
180
|
-
global.Chan = Chan;
|
|
181
|
-
export default Chan;
|
|
1
|
+
export { default as Chan } from "./App.js";
|
|
2
|
+
export { default as Controller } from "./base/Controller.js";
|
|
3
|
+
export { default as Service } from "./base/Service.js";
|
|
4
|
+
export { default as AppContext } from "./base/Context.js";
|
|
5
|
+
export { default as DatabaseManager } from "./base/Database.js";
|
|
6
|
+
|
|
7
|
+
export * as helper from "./helper/index.js";
|
|
8
|
+
export * as utils from "./utils/index.js";
|
|
9
|
+
export * as middleware from "./middleware/index.js";
|
|
10
|
+
export * as config from "./config/index.js";
|
|
11
|
+
export * as common from "./common/index.js";
|
|
12
|
+
export * as extend from "./extend/index.js";
|
|
13
|
+
|
|
14
|
+
import Chan from "./App.js";
|
|
15
|
+
export default Chan;
|
package/middleware/cookie.js
CHANGED
|
@@ -1,4 +1,20 @@
|
|
|
1
|
-
import cookieParser from "cookie-parser";
|
|
2
|
-
|
|
3
|
-
|
|
4
|
-
|
|
1
|
+
import cookieParser from "cookie-parser";
|
|
2
|
+
|
|
3
|
+
/**
|
|
4
|
+
* Cookie 中间件配置
|
|
5
|
+
* 配置 Express 应用的 cookie 解析功能
|
|
6
|
+
*/
|
|
7
|
+
|
|
8
|
+
/**
|
|
9
|
+
* 设置 Cookie 解析中间件
|
|
10
|
+
* @param {Object} app - Express 应用实例
|
|
11
|
+
* @param {string} cookieKey - Cookie 签名密钥
|
|
12
|
+
* @description
|
|
13
|
+
* 为 Express 应用配置 cookie-parser 中间件
|
|
14
|
+
* 使用密钥对 cookie 进行签名验证
|
|
15
|
+
* @example
|
|
16
|
+
* setCookie(app, 'my-secret-key');
|
|
17
|
+
*/
|
|
18
|
+
export const setCookie = (app, cookieKey) => {
|
|
19
|
+
app.use(cookieParser(cookieKey));
|
|
20
|
+
};
|
package/middleware/cors.js
CHANGED
|
@@ -1,4 +1,24 @@
|
|
|
1
1
|
import cors from "cors";
|
|
2
|
+
|
|
3
|
+
/**
|
|
4
|
+
* CORS 中间件配置
|
|
5
|
+
* 配置跨域资源共享
|
|
6
|
+
*/
|
|
7
|
+
|
|
8
|
+
/**
|
|
9
|
+
* 设置 CORS 中间件
|
|
10
|
+
* @param {Object} app - Express 应用实例
|
|
11
|
+
* @param {Object} _cors - CORS 配置选项
|
|
12
|
+
* @description
|
|
13
|
+
* 为 Express 应用配置 CORS 中间件
|
|
14
|
+
* 支持自定义 CORS 配置选项
|
|
15
|
+
* @example
|
|
16
|
+
* Cors(app, {
|
|
17
|
+
* origin: '*',
|
|
18
|
+
* methods: ['GET', 'POST'],
|
|
19
|
+
* allowedHeaders: ['Content-Type']
|
|
20
|
+
* });
|
|
21
|
+
*/
|
|
2
22
|
export const Cors = (app, _cors) => {
|
|
3
23
|
app.use(cors(_cors));
|
|
4
24
|
};
|
package/middleware/favicon.js
CHANGED
|
@@ -1,5 +1,21 @@
|
|
|
1
|
-
import path from "path";
|
|
2
|
-
import favicon from "serve-favicon";
|
|
3
|
-
|
|
4
|
-
|
|
5
|
-
|
|
1
|
+
import path from "path";
|
|
2
|
+
import favicon from "serve-favicon";
|
|
3
|
+
import { Paths } from "../config/paths.js";
|
|
4
|
+
|
|
5
|
+
/**
|
|
6
|
+
* Favicon 中间件配置
|
|
7
|
+
* 配置网站图标
|
|
8
|
+
*/
|
|
9
|
+
|
|
10
|
+
/**
|
|
11
|
+
* 设置 Favicon 中间件
|
|
12
|
+
* @param {Object} app - Express 应用实例
|
|
13
|
+
* @description
|
|
14
|
+
* 为 Express 应用配置 favicon 服务
|
|
15
|
+
* 从 public 目录加载 favicon.ico 文件
|
|
16
|
+
* @example
|
|
17
|
+
* setFavicon(app);
|
|
18
|
+
*/
|
|
19
|
+
export const setFavicon = (app) => {
|
|
20
|
+
app.use(favicon(path.join(Paths.publicPath, "favicon.ico")));
|
|
21
|
+
};
|
package/middleware/header.js
CHANGED
|
@@ -1,9 +1,26 @@
|
|
|
1
|
-
|
|
2
|
-
|
|
3
|
-
|
|
4
|
-
|
|
5
|
-
|
|
6
|
-
|
|
7
|
-
|
|
8
|
-
|
|
9
|
-
}
|
|
1
|
+
/**
|
|
2
|
+
* 响应头中间件配置
|
|
3
|
+
* 设置应用相关的响应头信息
|
|
4
|
+
*/
|
|
5
|
+
|
|
6
|
+
/**
|
|
7
|
+
* 设置响应头中间件
|
|
8
|
+
* @param {Object} app - Express 应用实例
|
|
9
|
+
* @param {Object} options - 配置选项
|
|
10
|
+
* @param {string} options.APP_NAME - 应用名称
|
|
11
|
+
* @param {string} options.APP_VERSION - 应用版本
|
|
12
|
+
* @description
|
|
13
|
+
* 为所有响应添加自定义响应头
|
|
14
|
+
* 包含 Create-By、X-Powered-By、ChanCMS、Server 等头
|
|
15
|
+
* @example
|
|
16
|
+
* setHeader(app, { APP_NAME: 'MyApp', APP_VERSION: '1.0.0' });
|
|
17
|
+
*/
|
|
18
|
+
export let setHeader = (app, { APP_NAME, APP_VERSION }) => {
|
|
19
|
+
app.use((req, res, next) => {
|
|
20
|
+
res.setHeader("Create-By", "Chanjs");
|
|
21
|
+
res.setHeader("X-Powered-By", "ChanCMS");
|
|
22
|
+
res.setHeader("ChanCMS", APP_VERSION);
|
|
23
|
+
res.setHeader("Server", APP_NAME);
|
|
24
|
+
next();
|
|
25
|
+
});
|
|
26
|
+
};
|
package/middleware/index.js
CHANGED
|
@@ -1,23 +1,14 @@
|
|
|
1
|
-
|
|
2
|
-
|
|
3
|
-
|
|
4
|
-
|
|
5
|
-
|
|
6
|
-
|
|
7
|
-
|
|
8
|
-
|
|
9
|
-
|
|
10
|
-
|
|
11
|
-
|
|
12
|
-
export {
|
|
13
|
-
|
|
14
|
-
|
|
15
|
-
setFavicon,
|
|
16
|
-
setBody,
|
|
17
|
-
setStatic,
|
|
18
|
-
setHeader,
|
|
19
|
-
setTemplate,
|
|
20
|
-
Cors,
|
|
21
|
-
validator,
|
|
22
|
-
waf,
|
|
23
|
-
};
|
|
1
|
+
/**
|
|
2
|
+
* 中间件模块入口
|
|
3
|
+
* 导出所有中间件配置函数
|
|
4
|
+
*/
|
|
5
|
+
|
|
6
|
+
export { setCookie } from "./cookie.js";
|
|
7
|
+
export { setFavicon } from "./favicon.js";
|
|
8
|
+
export { setBody } from "./setBody.js";
|
|
9
|
+
export { setStatic } from "./static.js";
|
|
10
|
+
export { setHeader } from "./header.js";
|
|
11
|
+
export { setTemplate } from "./template.js";
|
|
12
|
+
export { Cors } from "./cors.js";
|
|
13
|
+
export { validator } from "./validator.js";
|
|
14
|
+
export { waf } from "./waf.js";
|
|
@@ -0,0 +1,30 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* 防止快速重复请求中间件
|
|
3
|
+
* 使用 Cache 实现请求频率限制
|
|
4
|
+
*/
|
|
5
|
+
|
|
6
|
+
import Cache from "../helper/cache.js";
|
|
7
|
+
|
|
8
|
+
const sendResponse = (res, code, message, data = null) => {
|
|
9
|
+
res.json({ code, msg: message, data });
|
|
10
|
+
};
|
|
11
|
+
|
|
12
|
+
const requestCache = new Cache({ maxSize: 1000, defaultTTL: 500 });
|
|
13
|
+
|
|
14
|
+
export default () => {
|
|
15
|
+
return (req, res, next) => {
|
|
16
|
+
if (req.method !== 'GET') {
|
|
17
|
+
return next();
|
|
18
|
+
}
|
|
19
|
+
|
|
20
|
+
const key = `${req.method}:${req.originalUrl}`;
|
|
21
|
+
|
|
22
|
+
if (requestCache.has(key)) {
|
|
23
|
+
console.log(`[preventRetry] 阻止快速重复请求: ${key}`);
|
|
24
|
+
return sendResponse(res, 429, '请求过于频繁,请稍后再试');
|
|
25
|
+
}
|
|
26
|
+
|
|
27
|
+
requestCache.set(key, Date.now());
|
|
28
|
+
next();
|
|
29
|
+
};
|
|
30
|
+
};
|
package/middleware/setBody.js
CHANGED
|
@@ -1,10 +1,24 @@
|
|
|
1
|
-
import express from "express";
|
|
2
|
-
|
|
3
|
-
|
|
4
|
-
|
|
5
|
-
|
|
6
|
-
|
|
7
|
-
|
|
8
|
-
|
|
9
|
-
|
|
10
|
-
|
|
1
|
+
import express from "express";
|
|
2
|
+
|
|
3
|
+
/**
|
|
4
|
+
* 请求体解析中间件配置
|
|
5
|
+
* 配置请求体的解析方式
|
|
6
|
+
*/
|
|
7
|
+
|
|
8
|
+
/**
|
|
9
|
+
* 设置请求体解析中间件
|
|
10
|
+
* @param {Object} app - Express 应用实例
|
|
11
|
+
* @param {string} JSON_LIMIT - JSON 请求体大小限制
|
|
12
|
+
* @description
|
|
13
|
+
* 为 Express 应用配置请求体解析中间件
|
|
14
|
+
* 支持 JSON、URL 编码和 XML 格式的请求体
|
|
15
|
+
* @example
|
|
16
|
+
* setBody(app, '10mb');
|
|
17
|
+
*/
|
|
18
|
+
let setBody = function (app, JSON_LIMIT) {
|
|
19
|
+
app.use(express.raw({ type: "application/xml", limit: JSON_LIMIT }));
|
|
20
|
+
app.use(express.json({ limit: JSON_LIMIT }));
|
|
21
|
+
app.use(express.urlencoded({ extended: false }));
|
|
22
|
+
};
|
|
23
|
+
|
|
24
|
+
export { setBody };
|