chanjs 2.7.2 → 2.7.4

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.
Files changed (67) hide show
  1. package/App.js +232 -16
  2. package/base/Aop.js +20 -3
  3. package/base/Container.js +80 -3
  4. package/base/Controller.js +38 -9
  5. package/base/Database.js +50 -0
  6. package/base/Event.js +12 -0
  7. package/base/{Service.js → Repository.js} +644 -539
  8. package/common/api.js +18 -8
  9. package/common/code.js +25 -15
  10. package/common/email.js +98 -17
  11. package/common/index.js +1 -1
  12. package/config/code.js +138 -82
  13. package/global/index.js +1 -1
  14. package/helper/index.js +43 -41
  15. package/index.js +19 -6
  16. package/loader/index.js +6 -0
  17. package/{helper → loader}/loader.js +41 -27
  18. package/middleware/compress.js +185 -0
  19. package/middleware/cors.js +36 -24
  20. package/middleware/header.js +5 -10
  21. package/middleware/index.js +1 -0
  22. package/middleware/log.js +27 -3
  23. package/middleware/setBody.js +9 -1
  24. package/middleware/static.js +2 -1
  25. package/middleware/template.js +139 -4
  26. package/middleware/waf.js +136 -76
  27. package/package.json +4 -4
  28. package/realtime/index.js +7 -0
  29. package/realtime/sse.js +424 -0
  30. package/realtime/websocket.js +540 -0
  31. package/response/index.js +12 -0
  32. package/response/response.js +258 -0
  33. package/schedule/index.js +6 -0
  34. package/schedule/schedule.js +491 -0
  35. package/{helper → security}/checker.js +23 -8
  36. package/security/index.js +14 -0
  37. package/{helper → security}/jwt.js +175 -107
  38. package/security/keywords.js +179 -0
  39. package/security/rate-limit.js +105 -0
  40. package/security/sign.js +210 -0
  41. package/security/xss-filter.js +63 -0
  42. package/storage/cache.js +258 -0
  43. package/storage/index.js +9 -0
  44. package/storage/redis.js +258 -0
  45. package/storage/store.js +266 -0
  46. package/{helper → utils}/file.js +106 -15
  47. package/{helper → utils}/filter.js +2 -1
  48. package/{helper → utils}/html.js +19 -1
  49. package/utils/index.js +34 -0
  50. package/{helper → utils}/ip.js +25 -16
  51. package/utils/request.js +172 -0
  52. package/{helper → utils}/time.js +1 -1
  53. package/utils/tree.js +121 -0
  54. package/common/category.js +0 -22
  55. package/common/sms.js +0 -104
  56. package/extend/art-template.js +0 -129
  57. package/extend/index.js +0 -6
  58. package/global/global.js +0 -63
  59. package/helper/cache.js +0 -187
  60. package/helper/keywords.js +0 -132
  61. package/helper/rate-limit.js +0 -116
  62. package/helper/request.js +0 -47
  63. package/helper/response.js +0 -180
  64. package/helper/sign.js +0 -96
  65. package/helper/tree.js +0 -77
  66. package/helper/xss-filter.js +0 -42
  67. /package/{helper → utils}/data-parse.js +0 -0
@@ -1,116 +0,0 @@
1
- import { isIgnored } from "./checker.js";
2
- import { getIp } from "../helper/ip.js";
3
-
4
- /**
5
- * 访问频率限制工具
6
- * 提供基于 IP 和 Cookie 的访问限流功能
7
- */
8
-
9
- const COOKIE_NAME = `${process.env.APP_NAME || 'app'}_ratelimit`;
10
-
11
- /**
12
- * 解析时间窗口参数
13
- * @private
14
- * @param {number|string} time - 时间值
15
- * @returns {number} 毫秒数
16
- * @description
17
- * 支持以下格式:
18
- * - 数字:直接作为毫秒数
19
- * - 数字+s:秒
20
- * - 数字+m:分钟
21
- * - 数字+h:小时
22
- * - 数字+d:天
23
- */
24
- function parseWindowMs(time) {
25
- if (typeof time === 'number') return time;
26
- if (typeof time !== 'string') return 60 * 60 * 1000;
27
-
28
- const match = time.match(/^(\d+)([smhd])$/);
29
- if (!match) return 60 * 60 * 1000;
30
-
31
- const value = parseInt(match[1], 10);
32
- const unit = match[2];
33
-
34
- const multipliers = {
35
- 's': 1000,
36
- 'm': 60 * 1000,
37
- 'h': 60 * 60 * 1000,
38
- 'd': 24 * 60 * 60 * 1000,
39
- };
40
-
41
- return value * (multipliers[unit] || 1000);
42
- }
43
-
44
- /**
45
- * 创建限流中间件
46
- * @param {Object} rateLimitConfig - 限流配置
47
- * @param {number|string} rateLimitConfig.windowMs - 时间窗口
48
- * @param {number} rateLimitConfig.max - 最大请求次数
49
- * @param {Array<string>} [rateLimitConfig.ignorePaths] - 忽略的路径数组
50
- * @returns {Function} Express 中间件函数
51
- * @description
52
- * 基于客户端 IP 实现滑动窗口限流算法
53
- * 使用 Cookie 存储限流状态,避免内存泄漏
54
- * @example
55
- * const rateLimiter = createRateLimitMiddleware({
56
- * windowMs: '1m',
57
- * max: 60,
58
- * ignorePaths: ['/health']
59
- * });
60
- */
61
- export function createRateLimitMiddleware(rateLimitConfig) {
62
- const config = {
63
- ...rateLimitConfig,
64
- windowMs: parseWindowMs(rateLimitConfig?.windowMs),
65
- };
66
-
67
- return (req, res, next) => {
68
- try {
69
- if (isIgnored(req.path, config.ignorePaths)) {
70
- return next();
71
- }
72
-
73
- const now = Date.now();
74
- const ip = getIp(req);
75
- const cookieValue = req.cookies && req.cookies[COOKIE_NAME];
76
- let currentData = null;
77
-
78
- if (cookieValue) {
79
- try {
80
- currentData = JSON.parse(cookieValue);
81
- if (currentData && currentData.ip !== ip) {
82
- currentData = null;
83
- }
84
- } catch (e) {
85
- currentData = null;
86
- }
87
- }
88
-
89
- let newData;
90
- if (!currentData || now > currentData.resetTime) {
91
- newData = { count: 1, resetTime: now + config.windowMs, ip };
92
- } else if (currentData.count >= config.max) {
93
- console.error(`[WAF 限流拦截] 路径:${req.path} 计数:${currentData.count}/${config.max} IP:${ip}`);
94
- return res.status(429).json({
95
- code: 429,
96
- success: false,
97
- msg: '请求过于频繁,请稍后重试',
98
- retryAfter: Math.ceil((currentData.resetTime - now) / 1000),
99
- });
100
- } else {
101
- newData = { count: currentData.count + 1, resetTime: currentData.resetTime, ip };
102
- }
103
-
104
- res.cookie(COOKIE_NAME, JSON.stringify(newData), {
105
- httpOnly: true,
106
- maxAge: config.windowMs,
107
- sameSite: 'strict',
108
- });
109
-
110
- next();
111
- } catch (error) {
112
- console.error(`[WAF 限流异常] 路径:${req.path} 错误:${error.message}`);
113
- next();
114
- }
115
- };
116
- }
package/helper/request.js DELETED
@@ -1,47 +0,0 @@
1
- /**
2
- * HTTP 请求工具函数
3
- * 提供统一的请求发送方法
4
- */
5
-
6
- /**
7
- * 发送 HTTP 请求
8
- * @async
9
- * @param {string} url - 请求 URL
10
- * @param {Object} [options={}] - 请求选项
11
- * @param {string} [options.method='GET'] - HTTP 方法
12
- * @param {Object} [options.headers] - 请求头
13
- * @param {Object|string} [options.body] - 请求体
14
- * @returns {Promise<Object|null>} 响应数据,失败时返回 null
15
- * @description
16
- * 发送 HTTP 请求并返回 JSON 格式的响应数据
17
- * 自动将对象类型的 body 转换为 JSON 字符串
18
- * 默认 Content-Type 为 application/json
19
- * @example
20
- * const data = await request('https://api.example.com/users', {
21
- * method: 'POST',
22
- * body: { name: '张三' }
23
- * });
24
- */
25
- export async function request(url, options = {}) {
26
- const defaultOptions = {
27
- method: 'GET',
28
- headers: {
29
- 'Content-Type': 'application/json',
30
- },
31
- };
32
-
33
- const finalOptions = { ...defaultOptions, ...options };
34
-
35
- if (finalOptions.body && typeof finalOptions.body !== 'string') {
36
- finalOptions.body = JSON.stringify(finalOptions.body);
37
- }
38
-
39
- try {
40
- const response = await fetch(url, finalOptions);
41
- const data = await response.json();
42
- return data;
43
- } catch (error) {
44
- console.error('[Request] 请求失败:', error.message);
45
- return null;
46
- }
47
- }
@@ -1,180 +0,0 @@
1
- import { CODE, DB_ERROR } from "../config/code.js";
2
-
3
- /**
4
- * 响应工具函数
5
- * 提供统一的响应格式和错误处理
6
- */
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
-
22
- /**
23
- * 获取默认错误代码
24
- * @private
25
- * @param {Error} error - 错误对象
26
- * @returns {number} 错误代码
27
- */
28
- const getDefaultErrorCode = (error) => {
29
- if (!error?.message) return 5001;
30
- if (error.message.includes("syntax") || error.message.includes("SQL")) {
31
- return 6008;
32
- } else if (error.message.includes("Connection closed")) {
33
- return 6009;
34
- } else if (error.message.includes("permission")) {
35
- return 3003;
36
- }
37
- return 5001;
38
- };
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
-
66
- /**
67
- * 生成错误响应
68
- * @param {Object} options - 响应选项
69
- * @param {Error} options.err - 错误对象
70
- * @param {Object} [options.data={}] - 响应数据
71
- * @param {number} [options.code=500] - 错误代码
72
- * @returns {Object} 错误响应对象
73
- * @description
74
- * 根据错误类型生成标准错误响应
75
- * 开发环境下包含数据库错误详情
76
- */
77
- export const error = ({ err, data = {}, code = 500 } = {}) => {
78
- if (err) {
79
- console.error("[DB Error]", err?.message || err);
80
- const errorCode = err?.code && DB_ERROR[err.code] ? DB_ERROR[err.code] : getDefaultErrorCode(err);
81
- const msg = ERROR_MESSAGES[errorCode] || "操作失败";
82
-
83
- return {
84
- success: false,
85
- msg,
86
- code: errorCode,
87
- data: process.env.NODE_ENV === 'development' ? {
88
- sql: err?.sql,
89
- sqlMessage: err?.sqlMessage,
90
- message: err?.message,
91
- } : {},
92
- };
93
- }
94
-
95
- const msg = CODE[code] || "操作失败";
96
- return {
97
- success: false,
98
- msg,
99
- code,
100
- data,
101
- };
102
- };
103
-
104
- /**
105
- * 生成失败响应
106
- * @param {Object} options - 响应选项
107
- * @param {string} [options.msg="操作失败"] - 错误消息
108
- * @param {Object} [options.data={}] - 响应数据
109
- * @param {number} [options.code=201] - 错误代码
110
- * @returns {Object} 失败响应对象
111
- */
112
- export const fail = ({ msg = "操作失败", data = {}, code = 201 } = {}) => {
113
- return {
114
- success: false,
115
- msg,
116
- code,
117
- data,
118
- };
119
- };
120
-
121
- /**
122
- * 生成成功响应
123
- * @param {Object} options - 响应选项
124
- * @param {Object} [options.data={}] - 响应数据
125
- * @param {string} [options.msg="操作成功"] - 成功消息
126
- * @returns {Object} 成功响应对象
127
- */
128
- export const success = ({ data = {}, msg = "操作成功" } = {}) => ({
129
- success: true,
130
- msg,
131
- code: 200,
132
- data,
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/helper/sign.js DELETED
@@ -1,96 +0,0 @@
1
- import CryptoJS from "crypto-js";
2
-
3
- /**
4
- * 加密和签名工具函数
5
- * 提供 AES 加密解密和数据签名功能
6
- */
7
-
8
- /**
9
- * AES 加密
10
- * @param {Object} obj - 要加密的对象
11
- * @param {string} key - 加密密钥
12
- * @returns {string|null} 加密后的字符串,失败时返回 null
13
- * @description
14
- * 使用 AES 算法加密数据
15
- * 需要配置 AES_SALT 环境变量作为密钥
16
- * @example
17
- * const encrypted = aesEncrypt({ id: 1, name: '张三' }, 'my-secret-key');
18
- */
19
- export const aesEncrypt = (obj, key) => {
20
- if (!key) {
21
- console.error('[安全错误] AES_SALT 必须配置');
22
- return null;
23
- }
24
- try {
25
- const encrypted = CryptoJS.AES.encrypt(JSON.stringify(obj), key);
26
- return encrypted.toString();
27
- } catch (error) {
28
- console.error("AES加密失败:", error);
29
- return "";
30
- }
31
- };
32
-
33
- /**
34
- * AES 解密
35
- * @param {string} str - 要解密的字符串
36
- * @param {string} key - 解密密钥
37
- * @returns {string|null} 解密后的字符串,失败时返回 null
38
- * @description
39
- * 使用 AES 算法解密数据
40
- * 需要配置 AES_SALT 环境变量作为密钥
41
- * @example
42
- * const decrypted = aesDecrypt(encryptedStr, 'my-secret-key');
43
- */
44
- export const aesDecrypt = (str, key) => {
45
- if (!key) {
46
- console.error('[安全错误] AES_SALT 必须配置');
47
- return null;
48
- }
49
- try {
50
- const decrypted = CryptoJS.AES.decrypt(str, key);
51
- return decrypted.toString(CryptoJS.enc.Utf8);
52
- } catch (error) {
53
- console.error("AES解密失败:", error);
54
- return "";
55
- }
56
- };
57
-
58
- /**
59
- * 数据签名
60
- * @param {Object} data - 要签名的数据对象
61
- * @param {string} secret - 签名密钥
62
- * @returns {string|null} 签名字符串,失败时返回 null
63
- * @description
64
- * 使用 HMAC-SHA256 算法对数据进行签名
65
- * @example
66
- * const signature = signData({ userId: 123 }, 'my-sign-key');
67
- */
68
- export function signData(data, secret) {
69
- if (!secret) {
70
- console.error('[安全错误] 签名密钥必须配置');
71
- return null;
72
- }
73
- try {
74
- const sign = CryptoJS.HmacSHA256(JSON.stringify(data), secret).toString();
75
- return sign;
76
- } catch (error) {
77
- console.error("数据签名失败:", error.message);
78
- return null;
79
- }
80
- }
81
-
82
- /**
83
- * 验证数据签名
84
- * @param {Object} data - 原始数据对象
85
- * @param {string} signature - 待验证的签名
86
- * @param {string} secret - 签名密钥
87
- * @returns {boolean} 签名是否有效
88
- * @description
89
- * 重新计算签名并与传入的签名进行比对
90
- * @example
91
- * const isValid = verifySign({ userId: 123 }, signature, 'my-sign-key');
92
- */
93
- export function verifySign(data, signature, secret) {
94
- const computedSign = signData(data, secret);
95
- return computedSign === signature;
96
- }
package/helper/tree.js DELETED
@@ -1,77 +0,0 @@
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
- }
@@ -1,42 +0,0 @@
1
- import xss from 'xss';
2
-
3
- /**
4
- * XSS 过滤工具
5
- * 提供跨站脚本攻击过滤功能
6
- */
7
-
8
- /**
9
- * 过滤 XSS 攻击代码
10
- * @param {*} data - 要过滤的数据,可以是字符串、数组或对象
11
- * @returns {*} 过滤后的数据
12
- * @description
13
- * 递归过滤数据中的所有字符串值
14
- * 使用 xss 库清除危险的 HTML 和 JavaScript 代码
15
- * 支持字符串、数组和对象的递归处理
16
- * @example
17
- * const clean = filterXSS({
18
- * name: '<script>alert(1)</script>',
19
- * items: ['<img src=x onerror=alert(1)>']
20
- * });
21
- */
22
- export function filterXSS(data) {
23
- if (typeof data === 'string') {
24
- return xss(data);
25
- }
26
-
27
- if (Array.isArray(data)) {
28
- return data.map(item => filterXSS(item));
29
- }
30
-
31
- if (data && typeof data === 'object') {
32
- const result = {};
33
- for (const key in data) {
34
- if (Object.prototype.hasOwnProperty.call(data, key)) {
35
- result[key] = filterXSS(data[key]);
36
- }
37
- }
38
- return result;
39
- }
40
-
41
- return data;
42
- }
File without changes