chanjs 2.3.1 → 2.5.0

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
@@ -2,18 +2,18 @@ import express from "express";
2
2
  import fs from "fs";
3
3
  import path from "path";
4
4
 
5
- import AppContext from "./base/Context.js";
5
+ // 引入基础组件
6
6
  import Controller from "./base/Controller.js";
7
- import DatabaseManager from "./base/Database.js";
8
-
9
7
  import Service from "./base/Service.js";
8
+ import DatabaseManager from "./base/Database.js";
10
9
 
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 { db, loadConfig, loadController, loaderSort, prefixDbConfig, formatDateFields, request, delImg, getIp, setToken, getToken, verifyToken, generateToken, getFileTree, readFileContent, saveFileContent, isPathSafe, arrToObj, htmlDecode } from "./helper/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
14
  import { Cors, setBody, setCookie, setFavicon, setHeader, setStatic, setTemplate, waf } from "./middleware/index.js";
15
- import { errorResponse, notFoundResponse, parseDatabaseError } from "./utils/error-handler.js";
16
- import { error as responseError, fail, success } from "./utils/response.js";
15
+ import { errorResponse, notFoundResponse, parseDatabaseError, error as responseError, fail, success, checkKeywords } from "./utils/index.js";
16
+ import { importFile, importjs } from "./global/import.js";
17
17
 
18
18
  import "./global/index.js";
19
19
 
@@ -22,14 +22,14 @@ import "./global/index.js";
22
22
  * @class Chan
23
23
  * @description 管理应用程序的路由、中间件、数据库连接和服务
24
24
  * @example
25
- * const app = new Chan({ port: 3000, env: "production" });
25
+ * const app = new Chan();
26
26
  * await app.start();
27
27
  */
28
28
  class Chan {
29
+
30
+ // 静态属性:提供全局访问的辅助函数和工具
29
31
  static helper = {
30
32
  loadController,
31
- db,
32
- prefixDbConfig,
33
33
  loaderSort,
34
34
  loadConfig,
35
35
  formatDateFields,
@@ -47,6 +47,8 @@ class Chan {
47
47
  arrToObj,
48
48
  htmlDecode,
49
49
  };
50
+
51
+ // 静态属性:提供全局访问的常用业务函数和工具
50
52
  static common = {
51
53
  success,
52
54
  fail,
@@ -61,31 +63,35 @@ class Chan {
61
63
  filterBody,
62
64
  filterImgFromStr,
63
65
  };
66
+
67
+ // 静态属性:提供全局访问的配置
64
68
  static config = {};
69
+ // 静态属性:提供全局访问的数据库
70
+ static db = {};
71
+ // 静态属性:提供全局访问的服务类
65
72
  static Service = Service;
73
+ // 静态属性:提供全局访问的控制器类
66
74
  static Controller = Controller;
75
+ // 静态属性:提供全局访问的路径配置
67
76
  static paths = Paths;
77
+ // 静态属性:提供全局访问的导入函数
78
+ static importFile = importFile;
79
+ static importjs = importjs;
80
+ // 静态属性:提供全局访问的工具函数
81
+ static utils = {
82
+ checkKeywords,
83
+ };
68
84
 
69
85
  /**
70
86
  * 构造函数
71
87
  * @constructor
72
- * @param {Object} options - 应用配置选项
73
- * @param {number} [options.port=3000] - 服务器端口
74
- * @param {string} [options.env] - 运行环境
75
88
  * @description 初始化 Express 应用和核心组件
76
89
  */
77
- constructor(options = {}) {
90
+ constructor() {
78
91
  this.app = express();
79
- this.context = new AppContext();
92
+ // 数据库连接管理
80
93
  this.dbManager = new DatabaseManager();
81
-
82
- this.options = {
83
- port: options.port || 3000,
84
- env: options.env || process.env.NODE_ENV || "development",
85
- ...options,
86
- };
87
-
88
-
94
+ // 应用路由
89
95
  this.router = express.Router();
90
96
  }
91
97
 
@@ -97,10 +103,6 @@ class Chan {
97
103
  * @returns {Promise<void>}
98
104
  */
99
105
  async start() {
100
-
101
- global.appContext = this.context;
102
- global.Chan = Chan;
103
-
104
106
  //加载配置
105
107
  await this.config();
106
108
  //加载数据库
@@ -117,19 +119,17 @@ class Chan {
117
119
  this.useRouter();
118
120
  //404 500 处理
119
121
  this.setErrorHandler();
120
-
121
122
  }
122
123
 
123
124
  /**
124
125
  * 加载配置
125
126
  * @async
126
127
  * @returns {Promise<void>}
127
- * @description 加载应用配置并设置到上下文
128
+ * @description 加载应用配置并设置到全局变量
128
129
  */
129
130
  async config() {
130
131
  let config = await loadConfig();
131
132
  Chan.config = config;
132
- this.context.set("config", config);
133
133
  }
134
134
 
135
135
  /**
@@ -140,17 +140,15 @@ class Chan {
140
140
  */
141
141
  async loadDB() {
142
142
  const dbList = Chan.config?.db || [];
143
- const connections = this.dbManager.getConnections();
144
143
 
145
144
  for (const [index, item] of dbList.entries()) {
146
145
  const key = item.key || String(index);
147
146
  try {
148
- const dbConfig = prefixDbConfig(key) || item;
147
+ const dbConfig = item;
149
148
  if (!dbConfig) throw new Error("未找到配置");
150
149
 
151
150
  const connection = this.dbManager.add(key, dbConfig, { isDefault: index === 0 });
152
- this.context.set(`db:${key}`, connection);
153
-
151
+
154
152
  if (index === 0) {
155
153
  Chan.db = connection;
156
154
  }
@@ -160,7 +158,7 @@ class Chan {
160
158
  }
161
159
 
162
160
  console.log(
163
- `[DB] 初始化完成,已加载 ${dbList.length} 个数据库,成功 ${Object.keys(connections).length} 个`,
161
+ `[DB] 初始化完成,已加载 ${dbList.length} 个数据库`,
164
162
  );
165
163
  }
166
164
 
@@ -195,7 +193,7 @@ class Chan {
195
193
  const files = fs.readdirSync(_path).filter((file) => file.endsWith(".js"));
196
194
  for (const file of files) {
197
195
  const filePath = path.join(_path, file);
198
- let helperModule = await importFile(filePath);
196
+ let helperModule = await Chan.importFile(filePath);
199
197
  Object.assign(Chan[key], helperModule);
200
198
  }
201
199
  }
@@ -221,7 +219,7 @@ class Chan {
221
219
  PROXY = "false",
222
220
  waf: wafConfig = { enabled: false }
223
221
  } = config;
224
-
222
+
225
223
  await waf(this.app, wafConfig);
226
224
  setFavicon(this.app);
227
225
  setStatic(this.app, statics);
@@ -239,7 +237,6 @@ class Chan {
239
237
  */
240
238
  setApp() {
241
239
  this.app.set("trust proxy", Chan.config.PROXY === "true");
242
- this.app.set("env", this.options.env);
243
240
  this.app.disable("x-powered-by");
244
241
  }
245
242
 
@@ -349,7 +346,7 @@ class Chan {
349
346
  if (fs.existsSync(configPath)) {
350
347
  const dirs = loaderSort(Chan.config.modules);
351
348
  for (const item of dirs) {
352
- let router = await importFile(`app/modules/${item}/router.js`);
349
+ let router = await Chan.importFile(`app/modules/${item}/router.js`);
353
350
  router(this.app, this.router, Chan.config);
354
351
  }
355
352
  }
@@ -362,7 +359,7 @@ class Chan {
362
359
  * @description 加载 app/router.js 中的公共路由
363
360
  */
364
361
  async loadCommonRouter() {
365
- let router = await importFile("app/router.js");
362
+ let router = await Chan.importFile("app/router.js");
366
363
  if (router) {
367
364
  router(this.app, this.router, Chan.config);
368
365
  }
@@ -374,11 +371,13 @@ class Chan {
374
371
  * @description 在启动前执行回调,传递端口号
375
372
  */
376
373
  run(cb) {
377
- this.app.listen(this.options.port, () => {
378
- console.log(`Server running on port ${this.options.port}`);
379
- cb?.(this.options.port);
374
+ this.app.listen(Chan.config.PORT, () => {
375
+ console.log(`Server running on port ${Chan.config.PORT}`);
376
+ cb?.(Chan.config.PORT);
380
377
  });
381
378
  }
382
379
  }
383
380
 
381
+ global.Chan = Chan;
382
+
384
383
  export default Chan;
@@ -1,92 +1,21 @@
1
- import { success, fail, error as responseError } from "../utils/response.js";
1
+ import { success, fail } from "../utils/response.js";
2
2
 
3
3
  /**
4
4
  * 控制器基类
5
5
  * 提供统一的响应格式和常用方法
6
6
  */
7
7
  export default class Controller {
8
- /**
9
- * 构造函数
10
- * @param {Object} context - 应用上下文实例
11
- */
12
- constructor(context = null) {
13
- if (context) {
14
- this.context = context;
15
- } else if (global.appContext) {
16
- this.context = global.appContext;
17
- } else {
18
- this.context = { get: () => null, set: () => null };
19
- }
20
- }
21
-
22
- /**
23
- * 标准化返回结果
24
- * @private
25
- * @param {*} result - 原始结果
26
- * @param {string} defaultMsg - 默认消息
27
- * @returns {Object} 标准化的结果对象
28
- */
29
- _normalizeResult(result, defaultMsg = "操作成功") {
30
- if (result === null || result === undefined) {
31
- return { success: false, code: 5001, msg: "Service 返回空值", data: {} };
32
- }
33
-
34
- if (typeof result === 'object' && 'success' in result) {
35
- return result;
36
- }
37
-
38
- if (typeof result === 'object' || Array.isArray(result)) {
39
- return { success: true, code: 200, msg: defaultMsg, data: result };
40
- }
41
-
42
- return { success: true, code: 200, msg: defaultMsg, data: result };
43
- }
8
+ constructor() {}
44
9
 
45
10
  /**
46
11
  * 返回成功响应
47
- * @param {*} options - 响应选项或数据
12
+ * @param {Object} options - 响应选项
48
13
  * @param {*} options.data - 响应数据
49
14
  * @param {string} options.msg - 响应消息,默认"操作成功"
50
15
  * @returns {Object} 标准成功响应
51
16
  */
52
- success(options, msg = "操作成功") {
53
- let data, message = msg, extra = {};
54
-
55
- if (options === null || options === undefined) {
56
- data = undefined;
57
- } else if (typeof options === 'object' && options !== null && !Array.isArray(options)) {
58
- if ('data' in options) {
59
- data = options.data;
60
- message = options.msg || msg;
61
- extra = { ...options };
62
- delete extra.data;
63
- delete extra.msg;
64
- } else if ('success' in options && 'code' in options) {
65
- return options;
66
- } else {
67
- data = options;
68
- }
69
- } else {
70
- data = options;
71
- }
72
-
73
- if (data instanceof Error) {
74
- return this.error({ err: data });
75
- }
76
-
77
- const normalized = this._normalizeResult(data, message);
78
-
79
- if (!normalized.success) {
80
- return this.fail({ msg: normalized.msg, data: normalized.data, code: normalized.code });
81
- }
82
-
83
- return {
84
- success: true,
85
- code: 200,
86
- msg: normalized.msg,
87
- data: normalized.data,
88
- ...extra
89
- };
17
+ success({ data, msg = "操作成功" } = {}) {
18
+ return success({ data, msg });
90
19
  }
91
20
 
92
21
  /**
@@ -100,38 +29,4 @@ export default class Controller {
100
29
  fail({ msg = "操作失败", data = {}, code = 201 } = {}) {
101
30
  return fail({ msg, data, code });
102
31
  }
103
-
104
- /**
105
- * 返回错误响应
106
- * @param {Object} options - 响应选项
107
- * @param {Error} options.err - 错误对象
108
- * @param {*} options.data - 响应数据
109
- * @param {number} options.code - 错误码,默认500
110
- * @returns {Object} 标准错误响应
111
- */
112
- error({ err, data = {}, code = 500 } = {}) {
113
- return responseError({ err, data, code });
114
- }
115
-
116
- /**
117
- * 返回分页响应
118
- * @param {Array} list - 数据列表
119
- * @param {number} total - 总记录数
120
- * @param {number} current - 当前页码
121
- * @param {number} pageSize - 每页大小
122
- * @returns {Object} 标准分页响应
123
- */
124
- paginate(list, total, current, pageSize) {
125
- return success({
126
- data: {
127
- list,
128
- pagination: {
129
- total,
130
- current,
131
- pageSize,
132
- totalPages: Math.ceil(total / pageSize),
133
- },
134
- },
135
- });
136
- }
137
32
  }
package/base/Database.js CHANGED
@@ -2,7 +2,7 @@ import knex from "knex";
2
2
 
3
3
  /**
4
4
  * 数据库管理器类
5
- * 用于管理多个数据库连接和创建数据模型
5
+ * 用于管理多个数据库连接
6
6
  */
7
7
  class DatabaseManager {
8
8
  /**
@@ -78,237 +78,6 @@ class DatabaseManager {
78
78
  }
79
79
  }
80
80
 
81
- /**
82
- * 关闭所有数据库连接
83
- * @returns {Promise<void>}
84
- */
85
- async closeAll() {
86
- const closePromises = [];
87
- for (const [name, connection] of this._connections) {
88
- closePromises.push(
89
- connection.destroy().catch((err) => {
90
- console.error(`Error closing database "${name}":`, err);
91
- })
92
- );
93
- }
94
- await Promise.all(closePromises);
95
- this._connections.clear();
96
- }
97
-
98
- /**
99
- * 获取所有连接名称
100
- * @returns {Array<string>} 连接名称数组
101
- */
102
- getConnections() {
103
- return Array.from(this._connections.keys());
104
- }
105
-
106
- /**
107
- * 创建数据模型
108
- * @param {string} name - 模型名称
109
- * @param {string} tableName - 表名
110
- * @param {string} connectionName - 连接名称
111
- * @returns {Object} 模型对象
112
- */
113
- createModel(name, tableName, connectionName) {
114
- const db = this.get(connectionName);
115
- return {
116
- db,
117
- tableName,
118
-
119
- /**
120
- * 查询字段
121
- * @param {string} fields - 字段列表,默认"*"
122
- * @returns {Object} Knex查询构建器
123
- */
124
- select(fields = "*") {
125
- return db(this.tableName).select(fields);
126
- },
127
-
128
- /**
129
- * 条件查询
130
- * @param {Object} conditions - 查询条件
131
- * @returns {Object} Knex查询构建器
132
- */
133
- where(conditions) {
134
- return db(this.tableName).where(conditions);
135
- },
136
-
137
- /**
138
- * 查找记录
139
- * @param {Object} query - 查询参数
140
- * @param {Object} query.sort - 排序条件
141
- * @param {Object} query.where - 查询条件
142
- * @returns {Promise<Array>} 查询结果数组
143
- */
144
- find(query = {}) {
145
- let q = db(this.tableName);
146
- const { sort, ...where } = query;
147
-
148
- if (Object.keys(where).length > 0) {
149
- q = q.where(where);
150
- }
151
-
152
- if (sort && typeof sort === "object") {
153
- for (const [field, dir] of Object.entries(sort)) {
154
- q = q.orderBy(field, dir);
155
- }
156
- }
157
-
158
- return q.select();
159
- },
160
-
161
- /**
162
- * 查找单条记录
163
- * @param {Object} query - 查询条件
164
- * @returns {Promise<Object|null>} 查询结果或null
165
- */
166
- findOne(query = {}) {
167
- let q = db(this.tableName);
168
- if (Object.keys(query).length > 0) {
169
- q = q.where(query);
170
- }
171
- return q.first();
172
- },
173
-
174
- /**
175
- * 根据ID查找记录
176
- * @param {number|string} id - 记录ID
177
- * @returns {Promise<Object|null>} 查询结果或null
178
- */
179
- findById(id) {
180
- return db(this.tableName).where({ id }).first();
181
- },
182
-
183
- /**
184
- * 创建记录
185
- * @param {Object} data - 要插入的数据
186
- * @returns {Promise<Array>} 插入结果
187
- */
188
- create(data) {
189
- return db(this.tableName).insert(data);
190
- },
191
-
192
- /**
193
- * 批量创建记录
194
- * @param {Array} records - 要插入的记录数组
195
- * @returns {Promise<Array>} 插入结果
196
- */
197
- createMany(records) {
198
- return db(this.tableName).insert(records);
199
- },
200
-
201
- /**
202
- * 更新记录
203
- * @param {Object} conditions - 更新条件
204
- * @param {Object} data - 更新数据
205
- * @returns {Promise<number>} 影响的行数
206
- */
207
- update(conditions, data) {
208
- return db(this.tableName).where(conditions).update(data);
209
- },
210
-
211
- /**
212
- * 删除记录
213
- * @param {Object} conditions - 删除条件
214
- * @returns {Promise<number>} 影响的行数
215
- */
216
- delete(conditions) {
217
- return db(this.tableName).where(conditions).del();
218
- },
219
-
220
- /**
221
- * 统计记录数
222
- * @param {Object} query - 查询条件
223
- * @returns {Promise<number>} 记录总数
224
- */
225
- count(query = {}) {
226
- let q = db(this.tableName).count("* as total");
227
- if (Object.keys(query).length > 0) {
228
- q = q.where(query);
229
- }
230
- return q.first().then((result) => result?.total ?? 0);
231
- },
232
-
233
- /**
234
- * 分页查询
235
- * @param {Object} options - 查询选项
236
- * @param {number} options.current - 当前页码,默认1
237
- * @param {number} options.pageSize - 每页大小,默认10
238
- * @param {Object} options.where - 查询条件
239
- * @param {Array} options.select - 查询字段
240
- * @returns {Promise<Object>} 分页结果
241
- */
242
- query(options = {}) {
243
- const { current = 1, pageSize = 10, where = {}, select } = options;
244
- const offset = (current - 1) * pageSize;
245
-
246
- const countQuery = db(this.tableName).count("* as total");
247
- const dataQuery = db(this.tableName);
248
-
249
- if (Object.keys(where).length > 0) {
250
- countQuery.where(where);
251
- dataQuery.where(where);
252
- }
253
-
254
- if (select) {
255
- dataQuery.select(select);
256
- }
257
-
258
- return Promise.all([
259
- countQuery.first(),
260
- dataQuery.offset(offset).limit(pageSize),
261
- ]).then(([totalResult, list]) => ({
262
- list,
263
- total: totalResult?.total ?? 0,
264
- current,
265
- pageSize,
266
- }));
267
- },
268
-
269
- /**
270
- * 关联查询
271
- * @param {Object} options - 查询选项
272
- * @param {string} options.joinTable - 关联表名
273
- * @param {string} options.localField - 本地表字段
274
- * @param {string} options.foreignField - 关联表字段
275
- * @param {string} options.select - 查询字段,默认"*"
276
- * @param {Object} options.where - 查询条件
277
- * @returns {Object} Knex查询构建器
278
- */
279
- join(options) {
280
- const { joinTable, localField, foreignField, select = "*", where = {} } = options;
281
- let q = db(this.tableName)
282
- .join(joinTable, `${this.tableName}.${localField}`, "=", `${joinTable}.${foreignField}`)
283
- .select(select);
284
-
285
- if (Object.keys(where).length > 0) {
286
- q = q.where(where);
287
- }
288
-
289
- return q;
290
- },
291
-
292
- /**
293
- * 执行事务
294
- * @param {Function} callback - 事务回调函数
295
- * @returns {Promise} 事务执行结果
296
- */
297
- transaction(callback) {
298
- return db.transaction(callback);
299
- },
300
-
301
- /**
302
- * 执行原生SQL
303
- * @param {string} sql - SQL语句
304
- * @param {Array} bindings - 绑定参数
305
- * @returns {Promise} 查询结果
306
- */
307
- raw(sql, bindings) {
308
- return db.raw(sql, bindings);
309
- },
310
- };
311
- }
312
81
  }
313
82
 
314
83
  export default DatabaseManager;
package/base/Service.js CHANGED
@@ -14,11 +14,6 @@ class Service {
14
14
  if (knex && tableName) {
15
15
  this.db = knex;
16
16
  this.tableName = tableName;
17
- this._config = Chan.config || {};
18
- } else if (global.appContext) {
19
- this.context = global.appContext;
20
- } else {
21
- this.context = { get: () => null, set: () => null };
22
17
  }
23
18
 
24
19
  this._dateFields = ['created_at', 'updated_at', 'deleted_at', 'publish_time', 'start_time', 'end_time', 'login_time', 'created_date', 'updated_date', 'createdAt', 'updatedAt', 'deletedAt', 'publishTime', 'startTime', 'endTime', 'loginTime', 'createdDate', 'updatedDate'];
@@ -29,7 +24,7 @@ class Service {
29
24
  * @returns {number} 每页记录数,默认20
30
25
  */
31
26
  get pageSize() {
32
- return this._config.PAGE_SIZE || 20;
27
+ return Chan.config.PAGE_SIZE || 20;
33
28
  }
34
29
 
35
30
  /**
@@ -37,35 +32,7 @@ class Service {
37
32
  * @returns {number} 最大记录数,默认300
38
33
  */
39
34
  get limit() {
40
- return this._config.LIMIT_MAX || 300;
41
- }
42
-
43
- /**
44
- * 获取模型实例
45
- * @param {string} name - 模型名称
46
- * @returns {Object} 模型实例
47
- */
48
- getModel(name) {
49
- return this.context.getModel(name);
50
- }
51
-
52
- /**
53
- * 获取数据库连接
54
- * @param {string} name - 数据库名称
55
- * @returns {Object} 数据库实例
56
- */
57
- getDB(name) {
58
- return this.context.get(`db:${name}`);
59
- }
60
-
61
- /**
62
- * 执行事务
63
- * @param {Function} callback - 事务回调函数
64
- * @returns {Promise} 事务执行结果
65
- */
66
- async withTransaction(callback) {
67
- const db = this.getDB("default") || Chan.db;
68
- return db.transaction(callback);
35
+ return Chan.config.LIMIT_MAX || 300;
69
36
  }
70
37
 
71
38
  /**
package/global/import.js CHANGED
@@ -36,8 +36,4 @@ const importFile = async (filepath) => {
36
36
  */
37
37
  export const importjs = createRequire(import.meta.url);
38
38
 
39
- /**
40
- * 将导入函数挂载到全局
41
- */
42
- global.requirejs = importjs;
43
- global.importFile = importFile;
39
+ export { importFile };
package/helper/index.js CHANGED
@@ -1,5 +1,3 @@
1
- export { db } from "./db.js";
2
- export { prefixDbConfig } from "./db.js";
3
1
  export { loaderSort, loadConfig, clearConfigCache, bindInstance, loadController } from "./loader.js";
4
2
  export { formatTime } from "./time.js";
5
3
  export { formatDateFields } from "./time.js";
package/index.js CHANGED
@@ -1,7 +1,6 @@
1
1
  export { default as Chan } from "./App.js";
2
2
  export { default as Controller } from "./base/Controller.js";
3
3
  export { default as Service } from "./base/Service.js";
4
- export { default as AppContext } from "./base/Context.js";
5
4
  export { default as DatabaseManager } from "./base/Database.js";
6
5
 
7
6
  export * as helper from "./helper/index.js";
@@ -10,5 +10,4 @@ export { setStatic } from "./static.js";
10
10
  export { setHeader } from "./header.js";
11
11
  export { setTemplate } from "./template.js";
12
12
  export { Cors } from "./cors.js";
13
- export { validator } from "./validator.js";
14
13
  export { waf } from "./waf.js";
@@ -1,4 +1,5 @@
1
1
  import "../extend/index.js";
2
+ import { importjs } from "../global/import.js";
2
3
 
3
4
  /**
4
5
  * 模板引擎中间件配置
@@ -30,5 +31,5 @@ export let setTemplate = (app, config) => {
30
31
  });
31
32
  app.set("view engine", "html");
32
33
  app.set("views", all);
33
- app.engine(".html", requirejs("express-art-template"));
34
+ app.engine(".html", importjs("express-art-template"));
34
35
  };