chanjs 2.6.0 → 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 +5 -14
- package/README.md +304 -49
- package/helper/loader.js +0 -3
- package/package.json +1 -1
package/App.js
CHANGED
|
@@ -72,9 +72,7 @@ class Chan {
|
|
|
72
72
|
*/
|
|
73
73
|
async config() {
|
|
74
74
|
let config = await loadConfig();
|
|
75
|
-
console.log('[App.config] 开始加载配置,config keys:', Object.keys(config));
|
|
76
75
|
Chan.config = config;
|
|
77
|
-
console.log('[App.config] 配置加载完成,Chan.config.modules:', Chan.config.modules);
|
|
78
76
|
}
|
|
79
77
|
|
|
80
78
|
/**
|
|
@@ -85,15 +83,15 @@ class Chan {
|
|
|
85
83
|
*/
|
|
86
84
|
async loadDB() {
|
|
87
85
|
const dbList = Chan.config?.db || [];
|
|
88
|
-
|
|
89
86
|
for (const [index, item] of dbList.entries()) {
|
|
90
87
|
const key = item.key || String(index);
|
|
91
88
|
try {
|
|
92
89
|
const dbConfig = item;
|
|
93
|
-
if (!dbConfig)
|
|
94
|
-
|
|
90
|
+
if (!dbConfig) {
|
|
91
|
+
console.error(`[DB] 数据库 ${key} 未找到数据库配置`);
|
|
92
|
+
return;
|
|
93
|
+
};
|
|
95
94
|
const connection = this.dbManager.add(key, dbConfig, { isDefault: index === 0 });
|
|
96
|
-
|
|
97
95
|
if (index === 0) {
|
|
98
96
|
Chan.db = this.dbManager.get();
|
|
99
97
|
}
|
|
@@ -102,9 +100,7 @@ class Chan {
|
|
|
102
100
|
}
|
|
103
101
|
}
|
|
104
102
|
|
|
105
|
-
console.log(
|
|
106
|
-
`[DB] 初始化完成,已加载 ${dbList.length} 个数据库`,
|
|
107
|
-
);
|
|
103
|
+
console.log(`[DB] 初始化完成,已加载 ${dbList.length} 个数据库`);
|
|
108
104
|
}
|
|
109
105
|
|
|
110
106
|
/**
|
|
@@ -299,13 +295,8 @@ class Chan {
|
|
|
299
295
|
const configPath = path.join(Paths.appPath, "modules");
|
|
300
296
|
if (fs.existsSync(configPath)) {
|
|
301
297
|
const dirs = loaderSort(Chan.config.modules);
|
|
302
|
-
console.log('路由加载顺序:', dirs);
|
|
303
|
-
console.log('Chan.helper keys:', Object.keys(Chan.helper));
|
|
304
|
-
console.log('Chan.helper.loadController type:', typeof Chan.helper.loadController);
|
|
305
298
|
for (const item of dirs) {
|
|
306
299
|
let routerFn = await importFile(path.join(Paths.modulesPath, item, "router.js"));
|
|
307
|
-
console.log('路由函数:', routerFn);
|
|
308
|
-
|
|
309
300
|
// 为每个模块创建子路由并添加前缀
|
|
310
301
|
const subRouter = express.Router();
|
|
311
302
|
routerFn(this.app, subRouter, Chan.config);
|
package/README.md
CHANGED
|
@@ -1,93 +1,348 @@
|
|
|
1
|
-
|
|
2
1
|
# Chanjs
|
|
3
2
|
|
|
4
|
-
Chanjs 是一个基于
|
|
3
|
+
Chanjs 是一个基于 Express 5+ 构建的轻量级 MVC 框架,完全使用 JavaScript 开发。它体现了函数式编程的概念,提供了卓越的性能、清晰的代码和易于遵循的过程,确保了高可维护性。
|
|
5
4
|
|
|
6
5
|
## 特点
|
|
7
6
|
|
|
8
|
-
-
|
|
9
|
-
-
|
|
10
|
-
-
|
|
11
|
-
-
|
|
12
|
-
|
|
13
|
-
|
|
14
|
-
-
|
|
15
|
-
-
|
|
16
|
-
-
|
|
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
|
|
73
|
+
|- common
|
|
74
|
+
|- helper
|
|
75
|
+
|- middleware
|
|
23
76
|
|- modules
|
|
24
77
|
|- module1
|
|
25
78
|
|- controller
|
|
26
79
|
|- service
|
|
80
|
+
|- middleware
|
|
27
81
|
|- router.js
|
|
28
82
|
|- module2
|
|
29
83
|
|- controller
|
|
30
84
|
|- service
|
|
31
85
|
|- router.js
|
|
32
|
-
|-
|
|
33
|
-
|
|
34
|
-
|
|
35
|
-
|
|
86
|
+
|- plugins
|
|
87
|
+
|- plugin1
|
|
88
|
+
|- controller
|
|
89
|
+
|- router.js
|
|
90
|
+
|- router.js
|
|
36
91
|
|- config
|
|
92
|
+
|- data
|
|
93
|
+
|- doc
|
|
37
94
|
|- public
|
|
38
|
-
|-
|
|
95
|
+
|- view
|
|
96
|
+
|- app.js
|
|
39
97
|
|-.env.dev
|
|
40
98
|
|-.env.prd
|
|
99
|
+
|- pm2.json
|
|
100
|
+
|- package.json
|
|
101
|
+
```
|
|
41
102
|
|
|
103
|
+
## 初始化过程
|
|
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]
|
|
42
126
|
```
|
|
43
127
|
|
|
44
|
-
|
|
45
|
-
|
|
46
|
-
|
|
47
|
-
|
|
48
|
-
|
|
49
|
-
|
|
50
|
-
|
|
51
|
-
|
|
52
|
-
|
|
53
|
-
|
|
54
|
-
|
|
55
|
-
|
|
56
|
-
|
|
57
|
-
|
|
58
|
-
|
|
59
|
-
|
|
60
|
-
|
|
61
|
-
|
|
62
|
-
|
|
63
|
-
|
|
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
|
+
}
|
|
143
|
+
|
|
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
|
+
}
|
|
64
158
|
|
|
65
|
-
|
|
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)
|
|
66
174
|
|
|
67
|
-
|
|
68
|
-
- 多模块 MVC 结构
|
|
69
|
-
- 插件 MVC 支持
|
|
70
|
-
- CORS 跨域配置支持
|
|
71
|
-
- 多数据库支持 (PostgreSQL、MySQL / MariaDB、SQLite3、Oracle Database、MSSQL)
|
|
72
|
-
- 路由控制
|
|
73
|
-
- Art-template 模板引擎
|
|
74
|
-
- 静态资源管理
|
|
75
|
-
- Cookie 处理
|
|
76
|
-
- 日志功能
|
|
175
|
+
Chanjs 提供了 AOP(面向切面编程)支持,可以在方法执行前后添加切面逻辑。
|
|
77
176
|
|
|
78
|
-
|
|
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/` 目录下的所有文件,并返回一个对象:
|
|
293
|
+
|
|
294
|
+
```javascript
|
|
295
|
+
{
|
|
296
|
+
Member: MemberController实例,
|
|
297
|
+
Comment: CommentController实例,
|
|
298
|
+
Favorite: FavoriteController实例
|
|
299
|
+
}
|
|
300
|
+
```
|
|
301
|
+
|
|
302
|
+
## 运行
|
|
79
303
|
|
|
80
304
|
```javascript
|
|
81
305
|
import Chanjs from "chanjs";
|
|
82
306
|
const chan = new Chanjs();
|
|
307
|
+
|
|
83
308
|
// 前置钩子
|
|
84
309
|
chan.beforeStart(fn);
|
|
310
|
+
|
|
85
311
|
// 开始加载
|
|
86
312
|
await chan.start();
|
|
313
|
+
|
|
314
|
+
// 启动服务器
|
|
87
315
|
chan.run((port) => {
|
|
88
316
|
console.log(`ChanCMS is running on ${port}`);
|
|
89
317
|
});
|
|
318
|
+
```
|
|
319
|
+
|
|
320
|
+
## 完整示例
|
|
90
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
|
+
});
|
|
91
346
|
```
|
|
92
347
|
|
|
93
348
|
该框架专为寻求简单与功能之间平衡的开发者设计,为构建 Web 应用程序提供了一个强大的基础。
|
package/helper/loader.js
CHANGED
|
@@ -29,9 +29,6 @@ export function loaderSort(modules = []) {
|
|
|
29
29
|
* @description
|
|
30
30
|
* 从 config/index.js 加载配置,支持缓存机制
|
|
31
31
|
* 如果存在缓存且未过期,则直接返回缓存值
|
|
32
|
-
* @example
|
|
33
|
-
* const config = await loadConfig();
|
|
34
|
-
* console.log(config.APP_NAME);
|
|
35
32
|
*/
|
|
36
33
|
export async function loadConfig() {
|
|
37
34
|
const cacheKey = 'chanjs:config';
|