chanjs 2.5.9 → 2.6.1

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
@@ -3,17 +3,14 @@ import fs from "fs";
3
3
  import path from "path";
4
4
 
5
5
  // 引入基础组件
6
- import Controller from "./base/Controller.js";
7
- import Service from "./base/Service.js";
8
6
  import DatabaseManager from "./base/Database.js";
9
7
 
10
8
  // 引入配置和工具
11
9
  import { Paths } from "./config/index.js";
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, getFolders, arrToObj, htmlDecode } from "./helper/index.js";
10
+ import { loadConfig, loaderSort, loadController } from "./helper/index.js";
14
11
  import { Cors, setBody, setCookie, setFavicon, setHeader, setStatic, setTemplate, waf, log } from "./middleware/index.js";
15
- import { errorResponse, notFoundResponse, parseDatabaseError, error as responseError, fail, success, checkKeywords } from "./utils/index.js";
16
- import { importFile, importjs } from "./global/import.js";
12
+ import { notFoundResponse, parseDatabaseError, error as responseError } from "./utils/index.js";
13
+ import { importFile } from "./global/import.js";
17
14
 
18
15
  import "./global/index.js";
19
16
 
@@ -27,62 +24,6 @@ import "./global/index.js";
27
24
  */
28
25
  class Chan {
29
26
 
30
- // 静态属性:提供全局访问的辅助函数和工具
31
- static helper = {
32
- loadController,
33
- loaderSort,
34
- loadConfig,
35
- formatDateFields,
36
- request,
37
- delImg,
38
- getIp,
39
- setToken,
40
- getToken,
41
- verifyToken,
42
- generateToken,
43
- getFileTree,
44
- readFileContent,
45
- saveFileContent,
46
- isPathSafe,
47
- getFolders,
48
- arrToObj,
49
- htmlDecode,
50
- };
51
-
52
- // 静态属性:提供全局访问的常用业务函数和工具
53
- static common = {
54
- success,
55
- fail,
56
- error: responseError,
57
- getChildrenId,
58
- CODE: commonCode,
59
- sendMail,
60
- genRegEmailHtml,
61
- genResetPasswordEmail,
62
- pages,
63
- getHtmlFilesSync,
64
- filterBody,
65
- filterImgFromStr,
66
- };
67
-
68
- // 静态属性:提供全局访问的配置
69
- static config = {};
70
- // 静态属性:提供全局访问的数据库
71
- static db = {};
72
- // 静态属性:提供全局访问的服务类
73
- static Service = Service;
74
- // 静态属性:提供全局访问的控制器类
75
- static Controller = Controller;
76
- // 静态属性:提供全局访问的路径配置
77
- static paths = Paths;
78
- // 静态属性:提供全局访问的导入函数
79
- static importFile = importFile;
80
- static importjs = importjs;
81
- // 静态属性:提供全局访问的工具函数
82
- static utils = {
83
- checkKeywords,
84
- };
85
-
86
27
  /**
87
28
  * 构造函数
88
29
  * @constructor
@@ -106,6 +47,7 @@ class Chan {
106
47
  async start() {
107
48
  //加载配置
108
49
  await this.config();
50
+
109
51
  //加载数据库
110
52
  await this.loadDB();
111
53
  //加载扩展方法
@@ -130,9 +72,7 @@ class Chan {
130
72
  */
131
73
  async config() {
132
74
  let config = await loadConfig();
133
- console.log('[App.config] 开始加载配置,config keys:', Object.keys(config));
134
75
  Chan.config = config;
135
- console.log('[App.config] 配置加载完成,Chan.config.modules:', Chan.config.modules);
136
76
  }
137
77
 
138
78
  /**
@@ -143,26 +83,24 @@ class Chan {
143
83
  */
144
84
  async loadDB() {
145
85
  const dbList = Chan.config?.db || [];
146
-
147
86
  for (const [index, item] of dbList.entries()) {
148
87
  const key = item.key || String(index);
149
88
  try {
150
89
  const dbConfig = item;
151
- if (!dbConfig) throw new Error("未找到配置");
152
-
90
+ if (!dbConfig) {
91
+ console.error(`[DB] 数据库 ${key} 未找到数据库配置`);
92
+ return;
93
+ };
153
94
  const connection = this.dbManager.add(key, dbConfig, { isDefault: index === 0 });
154
-
155
95
  if (index === 0) {
156
- Chan.db = connection;
96
+ Chan.db = this.dbManager.get();
157
97
  }
158
98
  } catch (error) {
159
99
  console.error(`[DB] 数据库 ${key} 初始化失败: ${error.message}`);
160
100
  }
161
101
  }
162
102
 
163
- console.log(
164
- `[DB] 初始化完成,已加载 ${dbList.length} 个数据库`,
165
- );
103
+ console.log(`[DB] 初始化完成,已加载 ${dbList.length} 个数据库`);
166
104
  }
167
105
 
168
106
  /**
@@ -181,6 +119,11 @@ class Chan {
181
119
  for (const { _path, key } of extensions) {
182
120
  await this.loadFn(_path, key);
183
121
  }
122
+
123
+ if (!Chan.helper) {
124
+ Chan.helper = {};
125
+ }
126
+ Chan.helper.loadController = loadController;
184
127
  }
185
128
 
186
129
  /**
@@ -196,7 +139,10 @@ class Chan {
196
139
  const files = fs.readdirSync(_path).filter((file) => file.endsWith(".js"));
197
140
  for (const file of files) {
198
141
  const filePath = path.join(_path, file);
199
- let helperModule = await Chan.importFile(filePath);
142
+ let helperModule = await importFile(filePath);
143
+ if (!Chan[key]) {
144
+ Chan[key] = {};
145
+ }
200
146
  Object.assign(Chan[key], helperModule);
201
147
  }
202
148
  }
@@ -292,7 +238,7 @@ class Chan {
292
238
 
293
239
  const parsedError = parseDatabaseError(err);
294
240
  console.error(`[Global Error Details] Database Error - ${parsedError.msg}`);
295
- res.status(parsedError.statusCode).json(errorResponse(err, req));
241
+ res.status(parsedError.statusCode).json(responseError(err, req));
296
242
  });
297
243
  }
298
244
 
@@ -350,8 +296,11 @@ class Chan {
350
296
  if (fs.existsSync(configPath)) {
351
297
  const dirs = loaderSort(Chan.config.modules);
352
298
  for (const item of dirs) {
353
- let router = await Chan.importFile(`app/modules/${item}/router.js`);
354
- router(this.app, this.router, Chan.config);
299
+ let routerFn = await importFile(path.join(Paths.modulesPath, item, "router.js"));
300
+ // 为每个模块创建子路由并添加前缀
301
+ const subRouter = express.Router();
302
+ routerFn(this.app, subRouter, Chan.config);
303
+ this.app.use(`/${item}`, subRouter);
355
304
  }
356
305
  }
357
306
  }
@@ -363,7 +312,7 @@ class Chan {
363
312
  * @description 加载 app/router.js 中的公共路由
364
313
  */
365
314
  async loadCommonRouter() {
366
- let router = await Chan.importFile("app/router.js");
315
+ let router = await importFile(path.join(Paths.appPath, "router.js"));
367
316
  if (router) {
368
317
  router(this.app, this.router, Chan.config);
369
318
  }
package/README.md CHANGED
@@ -1,104 +1,348 @@
1
-
2
1
  # Chanjs
3
2
 
4
- Chanjs 是一个基于 Express5+ 构建的轻量级 MVC 框架,完全使用 JavaScript 开发。它体现了函数式编程的概念,提供了卓越的性能、清晰的代码和易于遵循的过程,确保了高可维护性。
3
+ Chanjs 是一个基于 Express 5+ 构建的轻量级 MVC 框架,完全使用 JavaScript 开发。它体现了函数式编程的概念,提供了卓越的性能、清晰的代码和易于遵循的过程,确保了高可维护性。
5
4
 
6
5
  ## 特点
7
6
 
8
- - 基于 Express 构建
9
- - 支持 ES6 语法
10
- - 模块化设计
11
- - 多模块化路由
12
- - 模块化 views
13
- - 模块化 controllers
14
- - 模块化 services
15
- - 插件式架构
16
- - 轻量级(核心代码在300行以内)
17
- - 缓存支持
7
+ - **核心架构**
8
+ - 基于 Express 5+ 构建
9
+ - 支持 ES6+ 语法
10
+ - 轻量级设计(核心代码精简高效)
11
+
12
+ - **模块化设计**
13
+ - 多模块化路由(支持模块独立路由)
14
+ - 模块化 views(模板文件模块化)
15
+ - 模块化 controllers(控制器模块化)
16
+ - 模块化 services(服务层模块化)
17
+ - 模块化 middleware(模块级中间件)
18
+
19
+ - **插件式架构**
20
+ - 支持插件扩展
21
+ - 插件独立 MVC 结构
22
+ - 热插拔式插件管理
23
+
24
+ - **数据库支持**
25
+ - 多数据库支持(PostgreSQL、MySQL/MariaDB、SQLite3、Oracle、MSSQL)
26
+ - 数据库连接池管理
27
+ - Knex 查询构建器
28
+ - 事务支持
29
+ - 日期字段自动格式化
30
+
31
+ - **安全防护**
32
+ - WAF(Web应用防火墙)
33
+ - XSS 防护
34
+ - 关键词过滤
35
+ - 访问限流(Rate Limiting)
36
+ - 路径白名单
37
+ - Cookie 安全处理
38
+
39
+ - **中间件生态**
40
+ - CORS 跨域配置
41
+ - 请求体解析
42
+ - Cookie 处理
43
+ - 静态资源管理
44
+ - 日志记录
45
+ - 模板引擎(Art-template)
46
+
47
+ - **高级特性**
48
+ - 依赖注入(DI,基于 Map 实现)
49
+ - 面向切面编程(AOP,支持 before/after/error 切面)
50
+ - 事件系统(基于 EventEmitter)
51
+ - 自动控制器加载(loadController)
52
+
53
+ - **开发体验**
54
+ - 缓存支持(内存缓存)
55
+ - 统一响应格式
56
+ - 全局错误处理(404/500)
57
+ - 分页查询封装
58
+ - 配置文件管理
59
+ - 环境变量支持(.env.dev/.env.prd)
60
+
61
+ - **工具函数**
62
+ - 时间格式化
63
+ - 文件操作
64
+ - 路径处理
65
+ - 树形结构转换
66
+ - 代码生成
67
+ - 过滤器
18
68
 
19
69
  ## 约定优于配置
20
70
 
21
71
  ```code
22
72
  |- app
23
- |- module
73
+ |- common
74
+ |- helper
75
+ |- middleware
76
+ |- modules
24
77
  |- module1
25
78
  |- controller
26
79
  |- service
27
- |- view
80
+ |- middleware
28
81
  |- router.js
29
82
  |- module2
30
83
  |- controller
31
84
  |- service
32
- |- view
33
85
  |- router.js
34
- |- extend
35
- |- middleware
36
- |- plugin
37
- |- plus-module1
38
- |- controller
39
- |- service
40
- |- view
41
- |- router.js
42
- |- module2
43
- |- controller
44
- |- service
45
- |- view
86
+ |- plugins
87
+ |- plugin1
88
+ |- controller
46
89
  |- router.js
90
+ |- router.js
47
91
  |- config
92
+ |- data
93
+ |- doc
48
94
  |- public
49
- |- index.js
95
+ |- view
96
+ |- app.js
50
97
  |-.env.dev
51
98
  |-.env.prd
99
+ |- pm2.json
100
+ |- package.json
101
+ ```
102
+
103
+ ## 初始化过程
52
104
 
105
+ ```mermaid
106
+ graph TD
107
+ A[初始化] --> B[加载配置]
108
+ B --> C[加载数据库]
109
+ C --> D[加载扩展]
110
+ D --> E[加载中间件]
111
+ E --> F[设置应用配置]
112
+ F --> G[加载模块路由]
113
+ G --> H[加载公共路由]
114
+ H --> I[应用路由]
115
+ I --> J[设置错误处理]
116
+ J --> K[beforeStart钩子]
117
+ K --> L[run启动服务器]
118
+
119
+ G --> G1[加载服务]
120
+ G --> G2[加载控制器]
121
+ G --> G3[加载路由]
122
+
123
+ D --> D1[加载common]
124
+ D --> D2[加载helper]
125
+ D --> D3[加载extend]
53
126
  ```
54
127
 
55
- ### 初始化过程
56
-
57
- - 初始化
58
- - 加载配置
59
- - 加载模块
60
- - 加载服务
61
- - 加载控制器
62
- - 加载路由
63
- - 加载扩展
64
- - 加载插件
65
- - 加载服务
66
- - 加载控制器
67
- - 加载路由
68
- - `beforeStart()` 钩子用于将数据库中的配置合并到配置中
69
- - `run()` 启动服务器
70
-
71
- ### 官方网站
72
-
73
- 使用 Chanjs 开发的 CMS 系统
74
- 网站:[https://www.chancms.top](https://www.chancms.top)
128
+ ## 核心功能
129
+
130
+ ### 1. 依赖注入(DI)
131
+
132
+ Chanjs 提供了轻量级的依赖注入容器,支持 Service 和 Controller 的自动加载。
133
+
134
+ #### 使用方式
135
+
136
+ ```javascript
137
+ import { Service, Controller } from "chanjs";
138
+
139
+ class UserService extends Service {
140
+ constructor() {
141
+ super("user");
142
+ }
75
143
 
76
- ### 功能亮点
144
+ async getUserWithArticles(userId) {
145
+ const user = await this.findById(userId);
146
+ // 从依赖注入容器中获取 ArticleService 实例
147
+ const articleService = await this.get("article", "Article");
148
+ const articles = await articleService.findByUserId(userId);
149
+
150
+ return { user, articles };
151
+ }
152
+ }
153
+
154
+ class UserController extends Controller {
155
+ constructor() {
156
+ super();
157
+ }
158
+
159
+ async getUser(req, res, next) {
160
+ try {
161
+ const { id } = req.params;
162
+ // 从依赖注入容器中获取 UserService 实例
163
+ const userService = await this.get("user", "User");
164
+ const result = await userService.findById(id);
165
+ res.json(this.success({ data: result.data }));
166
+ } catch (err) {
167
+ next(err);
168
+ }
169
+ }
170
+ }
171
+ ```
172
+
173
+ ### 2. 面向切面编程(AOP)
174
+
175
+ Chanjs 提供了 AOP(面向切面编程)支持,可以在方法执行前后添加切面逻辑。
176
+
177
+ #### 支持的切面类型
178
+
179
+ - `before` - 方法执行前
180
+ - `after` - 方法执行后
181
+ - `error` - 方法执行异常时
182
+
183
+ #### 使用方式
184
+
185
+ ```javascript
186
+ import { aop } from "chanjs";
187
+
188
+ // 注册切面函数
189
+ aop.set("logBefore", async ({ ctx, methodName, args }) => {
190
+ console.log(`[Before] ${methodName} 被调用,参数:`, args);
191
+ });
192
+
193
+ aop.set("logAfter", async ({ ctx, methodName, result }) => {
194
+ console.log(`[After] ${methodName} 执行完成,结果:`, result);
195
+ });
196
+
197
+ aop.set("logError", async ({ ctx, methodName, error }) => {
198
+ console.error(`[Error] ${methodName} 执行失败:`, error);
199
+ });
200
+
201
+ // 绑定切面到实例方法
202
+ const controller = new UserController();
203
+ aop.wrap(controller, {
204
+ getUser: [
205
+ { type: "before", logBefore: true },
206
+ { type: "after", logAfter: true },
207
+ { type: "error", logError: true }
208
+ ]
209
+ });
210
+
211
+ // 调用方法时会自动执行切面
212
+ await controller.getUser(1);
213
+ ```
214
+
215
+ #### 切面配置选项
216
+
217
+ ```javascript
218
+ aop.wrap(instance, {
219
+ methodName: [
220
+ { type: "before", enabled: true, customParam: "value" },
221
+ { type: "after", enabled: true },
222
+ { type: "error", enabled: false }
223
+ ]
224
+ });
225
+ ```
226
+
227
+ - `type` - 切面类型(before/after/error)
228
+ - `enabled` - 是否启用该切面(默认 true)
229
+ - 其他自定义参数会传递给切面函数
230
+
231
+ ### 3. 事件系统
232
+
233
+ Chanjs 提供了基于 Node.js EventEmitter 的事件系统,支持应用内的事件发布订阅。
234
+
235
+ #### 使用方式
236
+
237
+ ```javascript
238
+ import { event } from "chanjs";
239
+
240
+ // 监听事件
241
+ event.on("user.login", (data) => {
242
+ console.log("用户登录:", data);
243
+ });
244
+
245
+ event.on("user.logout", (data) => {
246
+ console.log("用户退出:", data);
247
+ });
248
+
249
+ // 触发事件
250
+ event.emit("user.login", { userId: 1, username: "admin" });
251
+
252
+ // 移除监听器
253
+ event.off("user.login", listener);
254
+
255
+ // 移除所有监听器
256
+ event.removeAllListeners("user.login");
257
+ ```
258
+
259
+ ### 4. 自动加载控制器
260
+
261
+ Chanjs 提供了 `loadController` 辅助函数,可以自动加载指定模块下的所有控制器。
262
+
263
+ #### 使用方式
264
+
265
+ ```javascript
266
+ import { helper } from "chanjs";
267
+
268
+ // 加载 member 模块下的所有控制器
269
+ const controller = await helper.loadController("member");
270
+
271
+ // 使用控制器
272
+ controller.Member.getUser();
273
+ controller.Comment.getComments();
274
+ controller.Favorite.getFavorites();
275
+ ```
276
+
277
+ #### 目录结构
278
+
279
+ ```
280
+ member/
281
+ controller/
282
+ Member.js
283
+ Comment.js
284
+ Favorite.js
285
+ service/
286
+ Member.js
287
+ Comment.js
288
+ Favorite.js
289
+ router.js
290
+ ```
291
+
292
+ `loadController("member")` 会自动加载 `member/controller/` 目录下的所有文件,并返回一个对象:
77
293
 
78
- - 配置文件
79
- - 多模块 MVC 结构
80
- - 插件 MVC 支持
81
- - CORS 跨域配置支持
82
- - 多数据库支持 (PostgreSQL、MySQL / MariaDB、SQLite3、Oracle Database、MSSQL)
83
- - 路由控制
84
- - Art-template 模板引擎
85
- - 静态资源管理
86
- - Cookie 处理
87
- - 日志功能
294
+ ```javascript
295
+ {
296
+ Member: MemberController实例,
297
+ Comment: CommentController实例,
298
+ Favorite: FavoriteController实例
299
+ }
300
+ ```
88
301
 
89
- ### 运行
302
+ ## 运行
90
303
 
91
304
  ```javascript
92
305
  import Chanjs from "chanjs";
93
306
  const chan = new Chanjs();
307
+
94
308
  // 前置钩子
95
309
  chan.beforeStart(fn);
310
+
96
311
  // 开始加载
97
312
  await chan.start();
313
+
314
+ // 启动服务器
98
315
  chan.run((port) => {
99
316
  console.log(`ChanCMS is running on ${port}`);
100
317
  });
318
+ ```
101
319
 
320
+ ## 完整示例
321
+
322
+ ```javascript
323
+ import Chanjs from "chanjs";
324
+ import { aop, event } from "chanjs";
325
+
326
+ const app = new Chanjs();
327
+
328
+ // 注册切面
329
+ aop.set("logBefore", async ({ ctx, methodName, args }) => {
330
+ console.log(`[Before] ${methodName} 被调用`);
331
+ });
332
+
333
+ // 监听事件
334
+ event.on("app.start", () => {
335
+ console.log("应用启动");
336
+ });
337
+
338
+ // 启动应用
339
+ await app.start();
340
+
341
+ // 运行
342
+ app.run((port) => {
343
+ event.emit("app.start", { port });
344
+ console.log(`Server running on port ${port}`);
345
+ });
102
346
  ```
103
347
 
104
348
  该框架专为寻求简单与功能之间平衡的开发者设计,为构建 Web 应用程序提供了一个强大的基础。