chanjs 2.8.11 → 2.8.13
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/README.md +1 -1
- package/core/Repository.js +1 -1
- package/core/errors.js +1 -1
- package/middleware/template.js +0 -33
- package/package.json +17 -18
- package/response/response.js +1 -1
- package/utils/pages.js +4 -3
package/README.md
CHANGED
package/core/Repository.js
CHANGED
|
@@ -92,7 +92,7 @@ function mapUrlFilters(query, fieldMap) {
|
|
|
92
92
|
const filter = {};
|
|
93
93
|
for (const [key, map] of Object.entries(fieldMap || {})) {
|
|
94
94
|
const raw = query?.[key];
|
|
95
|
-
if (raw
|
|
95
|
+
if (raw == null || String(raw).trim() === "") continue;
|
|
96
96
|
let piece;
|
|
97
97
|
try {
|
|
98
98
|
piece = map(String(raw).trim());
|
package/core/errors.js
CHANGED
|
@@ -50,7 +50,7 @@ export class AppError extends Error {
|
|
|
50
50
|
const json = { name: this.name, message: this.message, code: this.code, httpStatus: this.httpStatus };
|
|
51
51
|
for (const key of Object.getOwnPropertyNames(this)) {
|
|
52
52
|
const val = this[key];
|
|
53
|
-
if (val
|
|
53
|
+
if (val == null) continue;
|
|
54
54
|
if (typeof val === 'function' || typeof val === 'symbol') continue;
|
|
55
55
|
json[key] = val;
|
|
56
56
|
}
|
package/middleware/template.js
CHANGED
|
@@ -3,8 +3,6 @@ import dayjs from "dayjs";
|
|
|
3
3
|
import { createRequire } from "module";
|
|
4
4
|
import relativeTime from "dayjs/plugin/relativeTime.js";
|
|
5
5
|
import "dayjs/locale/zh-cn.js";
|
|
6
|
-
import { marked } from "marked";
|
|
7
|
-
import { filterXSS } from "../security/xss-filter.js";
|
|
8
6
|
import logger from "../utils/logger.js";
|
|
9
7
|
|
|
10
8
|
const require = createRequire(import.meta.url);
|
|
@@ -74,37 +72,6 @@ artTemplate.defaults.imports.safeStringify = (obj, keys) => {
|
|
|
74
72
|
artTemplate.defaults.imports.objKeys = (obj, separator = "\n") =>
|
|
75
73
|
typeof obj === "object" && obj ? Object.keys(obj).join(separator) : "";
|
|
76
74
|
|
|
77
|
-
/**
|
|
78
|
-
* Markdown渲染 + XSS过滤
|
|
79
|
-
* @param {string} content
|
|
80
|
-
* @param {"md"|"rich"} [editorType="rich"]
|
|
81
|
-
* @param {0|1} [allowScript=0] 1=信任内容不做XSS过滤
|
|
82
|
-
* @returns {string}
|
|
83
|
-
*/
|
|
84
|
-
artTemplate.defaults.imports.renderContent = (content, editorType = "rich", allowScript = 0) => {
|
|
85
|
-
if (typeof content !== "string") return content || "";
|
|
86
|
-
let html = content;
|
|
87
|
-
|
|
88
|
-
if (editorType === "md") {
|
|
89
|
-
try {
|
|
90
|
-
// marked v4+ 支持异步扩展,返回 string | Promise<string>
|
|
91
|
-
// 同步场景下直接取结果;若返回 Promise 则降级为空字符串
|
|
92
|
-
const result = marked.parse(content);
|
|
93
|
-
html = typeof result === "string" ? result : "";
|
|
94
|
-
} catch (e) {
|
|
95
|
-
logger.error(`[renderContent] MD解析失败: ${e.message}`);
|
|
96
|
-
}
|
|
97
|
-
}
|
|
98
|
-
|
|
99
|
-
if (Number(allowScript) !== 1) {
|
|
100
|
-
try { html = filterXSS(html); }
|
|
101
|
-
catch (e) {
|
|
102
|
-
logger.error(`[renderContent] XSS过滤失败: ${e.message}`);
|
|
103
|
-
}
|
|
104
|
-
}
|
|
105
|
-
return html;
|
|
106
|
-
};
|
|
107
|
-
|
|
108
75
|
/**
|
|
109
76
|
* art-template模板引擎中间件
|
|
110
77
|
* @param {express.Application} app
|
package/package.json
CHANGED
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
{
|
|
2
2
|
"type": "module",
|
|
3
3
|
"name": "chanjs",
|
|
4
|
-
"version": "2.8.
|
|
4
|
+
"version": "2.8.13",
|
|
5
5
|
"description": "chanjs基于 Node.js + Express 5 的标准 HMVC 框架(NHMVC),纯 JavaScript(ESM)开发。",
|
|
6
6
|
"main": "index.js",
|
|
7
7
|
"module": "index.js",
|
|
@@ -14,7 +14,7 @@
|
|
|
14
14
|
"nodejs mvc"
|
|
15
15
|
],
|
|
16
16
|
"engines": {
|
|
17
|
-
"node": ">=22.
|
|
17
|
+
"node": ">=24.22.1"
|
|
18
18
|
},
|
|
19
19
|
"scripts": {
|
|
20
20
|
"test": "node --test"
|
|
@@ -37,29 +37,28 @@
|
|
|
37
37
|
"art-template": "^4.13.4",
|
|
38
38
|
"cookie-parser": "^1.4.7",
|
|
39
39
|
"cors": "^2.8.6",
|
|
40
|
-
"dayjs": "^1.11.
|
|
41
|
-
"dotenv": "^
|
|
42
|
-
"express": "^5.
|
|
40
|
+
"dayjs": "^1.11.23",
|
|
41
|
+
"dotenv": "^18.0.9",
|
|
42
|
+
"express": "^5.3.0",
|
|
43
43
|
"express-art-template": "^1.0.1",
|
|
44
|
-
"i18next": "^
|
|
45
|
-
"ioredis": "^
|
|
44
|
+
"i18next": "^26.5.11",
|
|
45
|
+
"ioredis": "^6.0.0",
|
|
46
46
|
"jsonwebtoken": "^9.0.3",
|
|
47
|
-
"knex": "^3.
|
|
48
|
-
"
|
|
49
|
-
"
|
|
50
|
-
"
|
|
51
|
-
"
|
|
52
|
-
"pino": "^
|
|
53
|
-
"pino-
|
|
54
|
-
"pino-pretty": "^13.1.3",
|
|
47
|
+
"knex": "^3.3.0",
|
|
48
|
+
"mysql2": "^3.24.6",
|
|
49
|
+
"node-cron": "^4.6.0",
|
|
50
|
+
"pg": "^8.23.1",
|
|
51
|
+
"pino": "^10.4.0",
|
|
52
|
+
"pino-http": "^11.0.0",
|
|
53
|
+
"pino-pretty": "^13.2.0",
|
|
55
54
|
"serve-favicon": "^2.5.1",
|
|
56
|
-
"xss": "^1.0.
|
|
55
|
+
"xss": "^1.0.17"
|
|
57
56
|
},
|
|
58
57
|
"devDependencies": {
|
|
59
|
-
"zod": "^4.
|
|
58
|
+
"zod": "^4.6.7"
|
|
60
59
|
},
|
|
61
60
|
"peerDependencies": {
|
|
62
|
-
"zod": "^4.
|
|
61
|
+
"zod": "^4.6.7"
|
|
63
62
|
},
|
|
64
63
|
"peerDependenciesMeta": {
|
|
65
64
|
"zod": {
|
package/response/response.js
CHANGED
|
@@ -38,7 +38,7 @@ export function serializeError(err, exposeDetail = false) {
|
|
|
38
38
|
if (NEVER_EXPOSE.has(key)) continue;
|
|
39
39
|
if (SENSITIVE.has(key) && !exposeDetail) continue; // 生产环境不泄露堆栈/根因(SQL)
|
|
40
40
|
const val = err[key];
|
|
41
|
-
if (val
|
|
41
|
+
if (val == null) continue;
|
|
42
42
|
if (typeof val === "function" || typeof val === "symbol") continue;
|
|
43
43
|
try { extra[key] = val && typeof val === "object" && !Array.isArray(val) ? JSON.stringify(val) : val; }
|
|
44
44
|
catch { extra[key] = "[unserializable]"; }
|
package/utils/pages.js
CHANGED
|
@@ -48,7 +48,7 @@ export function pageHtml({ baseUrl, query, total, pageSize = 10, current = 1, un
|
|
|
48
48
|
|
|
49
49
|
const base = new URLSearchParams();
|
|
50
50
|
Object.entries(query || {}).forEach(([k, v]) => {
|
|
51
|
-
if (k === "current" || v
|
|
51
|
+
if (k === "current" || v == null || v === "") return;
|
|
52
52
|
base.set(k, v);
|
|
53
53
|
});
|
|
54
54
|
const link = (n) => {
|
|
@@ -116,8 +116,9 @@ export function pages(current, total, pageSize, href, query = '') {
|
|
|
116
116
|
items.push(ellipsis(), pageLink(totalPage));
|
|
117
117
|
}
|
|
118
118
|
|
|
119
|
-
//
|
|
120
|
-
|
|
119
|
+
// 下一页(最后一页不再给链接:current+1 恒为真值,不判断 totalPage 会渲染出越界页码,
|
|
120
|
+
// 点进去是空列表 —— 与 query 式 pageHtml() 的 current < totalPage 判断对齐)
|
|
121
|
+
items.push(navLink(current < totalPage ? current + 1 : 0, "下一页"));
|
|
121
122
|
|
|
122
123
|
return items.join("");
|
|
123
124
|
}
|