chanjs 2.5.2 → 2.5.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.
package/App.js CHANGED
@@ -10,8 +10,8 @@ import DatabaseManager from "./base/Database.js";
10
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 { loadConfig, loadController, loaderSort, formatDateFields, request, delImg, getIp, setToken, getToken, verifyToken, generateToken, getFileTree, readFileContent, saveFileContent, isPathSafe, arrToObj, htmlDecode } from "./helper/index.js";
14
- import { Cors, setBody, setCookie, setFavicon, setHeader, setStatic, setTemplate, waf } from "./middleware/index.js";
13
+ import { loadConfig, loadController, loaderSort, formatDateFields, request, delImg, getIp, setToken, getToken, verifyToken, generateToken, getFileTree, readFileContent, saveFileContent, isPathSafe, getFolders, arrToObj, htmlDecode } from "./helper/index.js";
14
+ import { Cors, setBody, setCookie, setFavicon, setHeader, setStatic, setTemplate, waf, log } from "./middleware/index.js";
15
15
  import { errorResponse, notFoundResponse, parseDatabaseError, error as responseError, fail, success, checkKeywords } from "./utils/index.js";
16
16
  import { importFile, importjs } from "./global/import.js";
17
17
 
@@ -44,6 +44,7 @@ class Chan {
44
44
  readFileContent,
45
45
  saveFileContent,
46
46
  isPathSafe,
47
+ getFolders,
47
48
  arrToObj,
48
49
  htmlDecode,
49
50
  };
@@ -129,7 +130,9 @@ class Chan {
129
130
  */
130
131
  async config() {
131
132
  let config = await loadConfig();
133
+ console.log('[App.config] 开始加载配置,config keys:', Object.keys(config));
132
134
  Chan.config = config;
135
+ console.log('[App.config] 配置加载完成,Chan.config.modules:', Chan.config.modules);
133
136
  }
134
137
 
135
138
  /**
@@ -209,7 +212,7 @@ class Chan {
209
212
  const config = Chan.config;
210
213
  const {
211
214
  views = [],
212
- env = "development",
215
+ NODE_ENV = "dev",
213
216
  APP_NAME = "ChanCMS",
214
217
  APP_VERSION = "1.0.0",
215
218
  cookieKey,
@@ -217,6 +220,7 @@ class Chan {
217
220
  statics = [],
218
221
  cors = {},
219
222
  PROXY = "false",
223
+ logger = {},
220
224
  waf: wafConfig = { enabled: false }
221
225
  } = config;
222
226
 
@@ -226,7 +230,8 @@ class Chan {
226
230
  setCookie(this.app, cookieKey);
227
231
  setBody(this.app, BODY_LIMIT);
228
232
  Cors(this.app, cors);
229
- setTemplate(this.app, { views, NODE_ENV: env });
233
+ log(this.app, logger);
234
+ setTemplate(this.app, { views, NODE_ENV });
230
235
  setHeader(this.app, { APP_NAME, APP_VERSION });
231
236
  }
232
237
 
@@ -1,8 +1,12 @@
1
1
  import template from "art-template";
2
2
  import dayjs from "dayjs";
3
+ import { createRequire } from 'module';
4
+ const require = createRequire(import.meta.url);
5
+ const { marked } = require('marked');
6
+
7
+ //template.defaults.native = false; // 禁用原生模板引擎 防止模板直接调用nodejs语法
8
+ //template.defaults.debug = false; // 禁用调试模式
3
9
 
4
- template.defaults.native = false; // 禁用原生模板引擎 防止模板直接调用nodejs语法
5
- template.defaults.debug = false; // 禁用调试模式
6
10
 
7
11
  /**
8
12
  * 日期格式化过滤器
@@ -44,3 +48,22 @@ template.defaults.imports.truncate = (str, length = 10) => {
44
48
  template.defaults.imports.safeStringify = (obj) => {
45
49
  return JSON.stringify(obj, null, 2);
46
50
  };
51
+
52
+ /**
53
+ * Markdown 渲染过滤器
54
+ * 自动检测内容是否为 Markdown 格式,如果是则渲染为 HTML
55
+ * @param {string} content - 文章内容
56
+ * @returns {string} 渲染后的 HTML
57
+ */
58
+ template.defaults.imports.renderMarkdown = (content) => {
59
+ if (!content || typeof content !== 'string') {
60
+ return content || '';
61
+ }
62
+
63
+ try {
64
+ return marked.parse(content);
65
+ } catch (err) {
66
+ console.error('[renderMarkdown] Markdown 渲染失败:', err.message);
67
+ return content;
68
+ }
69
+ };
package/helper/file.js CHANGED
@@ -156,3 +156,38 @@ export function isPathSafe(targetPath, basePath) {
156
156
  const relativePath = path.relative(normalizedBase, normalizedTarget);
157
157
  return !relativePath.startsWith('..');
158
158
  }
159
+
160
+ /**
161
+ * 获取指定文件夹下的所有文件夹
162
+ * @param {string} folderPath - 文件夹路径
163
+ * @returns {Array<string>} 文件夹名称数组
164
+ * @description
165
+ * 同步获取指定文件夹下的所有文件夹(不包含隐藏文件)
166
+ * @example
167
+ * const folders = getFolders('/path/to/directory');
168
+ * // 返回: ['default', 'test', ...]
169
+ */
170
+ export function getFolders(folderPath) {
171
+ if (!fs.existsSync(folderPath)) {
172
+ return [];
173
+ }
174
+
175
+ const items = fs.readdirSync(folderPath);
176
+ const folders = [];
177
+
178
+ for (const item of items) {
179
+ if (item.startsWith('.')) continue;
180
+
181
+ const itemPath = path.join(folderPath, item);
182
+ try {
183
+ const stat = fs.statSync(itemPath);
184
+ if (stat.isDirectory()) {
185
+ folders.push(item);
186
+ }
187
+ } catch (err) {
188
+ continue;
189
+ }
190
+ }
191
+
192
+ return folders;
193
+ }
package/helper/index.js CHANGED
@@ -8,6 +8,7 @@ export { getFileTree } from "./file.js";
8
8
  export { readFileContent } from "./file.js";
9
9
  export { saveFileContent } from "./file.js";
10
10
  export { isPathSafe } from "./file.js";
11
+ export { getFolders } from "./file.js";
11
12
  export { htmlDecode } from "./html.js";
12
13
  export { getIp } from "./ip.js";
13
14
  export { verifyToken } from "./jwt.js";
package/helper/ip.js CHANGED
@@ -37,14 +37,11 @@ export function getIp(req) {
37
37
  ip = ip.substring(7);
38
38
  }
39
39
 
40
- const isLocalAddress = ip === '::1' ||
41
- ip === '127.0.0.1' ||
42
- ip === '0.0.0.0' ||
43
- ip === 'localhost';
44
-
45
- if (!isLocalAddress) {
46
- return ip;
40
+ if (ip === '::1') {
41
+ ip = '127.0.0.1';
47
42
  }
43
+
44
+ return ip;
48
45
  }
49
46
 
50
47
  const headers = req.headers || {};
@@ -11,3 +11,4 @@ export { setHeader } from "./header.js";
11
11
  export { setTemplate } from "./template.js";
12
12
  export { Cors } from "./cors.js";
13
13
  export { waf } from "./waf.js";
14
+ export { log } from "./log.js";
@@ -0,0 +1,41 @@
1
+ import morgan from "morgan";
2
+ import { getIp } from "../helper/ip.js";
3
+
4
+ // 自定义 IP 令牌
5
+ morgan.token("ip", (req, res) => {
6
+ return getIp(req);
7
+ });
8
+
9
+ // 自定义用户信息令牌
10
+ morgan.token("user", (req, res) => {
11
+ if (req.user) {
12
+ return `${req.user.uid}:${req.user.username}`;
13
+ }
14
+ return "-";
15
+ });
16
+
17
+ // 自定义时间令牌(带日期)
18
+ morgan.token("datetime", (req, res) => {
19
+ return new Date().toISOString();
20
+ });
21
+
22
+ // 自定义 morgan 格式:时间 IP 用户 Method URL Status Length - Response-Time ms
23
+ morgan.format("chancms", (tokens, req, res) => {
24
+ return [
25
+ tokens.datetime(req, res),
26
+ tokens.ip(req, res),
27
+ tokens.user(req, res),
28
+ tokens.method(req, res),
29
+ tokens.url(req, res),
30
+ tokens.status(req, res),
31
+ tokens.res(req, res, "content-length") || "-",
32
+ "-",
33
+ tokens["response-time"](req, res),
34
+ "ms",
35
+ ].join(" ");
36
+ });
37
+
38
+ export const log = (app, logger) => {
39
+ const level = logger?.level || "chancms";
40
+ app.use(morgan(level === "chancms" ? "chancms" : level));
41
+ };
@@ -23,10 +23,10 @@ import { importjs } from "../global/import.js";
23
23
  */
24
24
  export let setTemplate = (app, config) => {
25
25
  const { views, NODE_ENV } = config;
26
- const all = [...views, "app/modules/web/view"];
26
+ const all = [...views];
27
27
  app.set("view options", {
28
28
  debug: NODE_ENV === "dev",
29
- cache: NODE_ENV === "prd",
29
+ cache: true,
30
30
  minimize: true,
31
31
  });
32
32
  app.set("view engine", "html");
package/middleware/waf.js CHANGED
@@ -104,7 +104,7 @@ function createWafMiddleware(wafConfig) {
104
104
 
105
105
  if (matched) {
106
106
  const { category, keyword } = matched;
107
- console.error(`[WAF 拦截] IP:${clientIp} 路径:${requestPath} 关键词:${keyword} 类别:${category}`);
107
+ console.warn(`[WAF 拦截] IP:${clientIp} 路径:${requestPath} 关键词:${keyword} 类别:${category}`);
108
108
 
109
109
  const htmlResponse = `
110
110
  <!DOCTYPE html>
package/package.json CHANGED
@@ -1,7 +1,7 @@
1
1
  {
2
2
  "type": "module",
3
3
  "name": "chanjs",
4
- "version": "2.5.2",
4
+ "version": "2.5.4",
5
5
  "description": "chanjs基于express5 纯js研发的轻量级mvc框架。",
6
6
  "main": "index.js",
7
7
  "module": "index.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",".sql"
19
+ ".py", ".pl", ".cfm", ".jhtml", ".shtml",".sql",".env",".git"
20
20
  ],
21
21
  /**
22
22
  * 敏感目录名称