chanjs 2.1.1 → 2.3.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.
Files changed (61) hide show
  1. package/App.js +387 -0
  2. package/base/Context.js +78 -0
  3. package/base/Controller.js +137 -0
  4. package/base/Database.js +314 -0
  5. package/base/Service.js +539 -0
  6. package/common/api.js +25 -0
  7. package/common/category.js +22 -0
  8. package/common/code.js +42 -0
  9. package/common/email.js +110 -0
  10. package/common/index.js +7 -0
  11. package/common/pages.js +86 -0
  12. package/common/sms.js +104 -0
  13. package/common/utils.js +73 -0
  14. package/config/code.js +110 -52
  15. package/config/index.js +10 -0
  16. package/config/paths.js +60 -0
  17. package/extend/art-template.js +46 -28
  18. package/extend/index.js +6 -0
  19. package/global/env.js +11 -5
  20. package/global/global.js +63 -39
  21. package/global/import.js +43 -39
  22. package/global/index.js +8 -3
  23. package/helper/cache.js +182 -0
  24. package/helper/data-parse.js +121 -37
  25. package/helper/db.js +71 -83
  26. package/helper/file.js +158 -208
  27. package/helper/filter.js +34 -0
  28. package/helper/html.js +30 -47
  29. package/helper/index.js +29 -5
  30. package/helper/ip.js +48 -31
  31. package/helper/jwt.js +78 -11
  32. package/helper/loader.js +93 -50
  33. package/helper/request.js +41 -144
  34. package/helper/sign.js +96 -33
  35. package/helper/time.js +89 -74
  36. package/helper/tree.js +77 -0
  37. package/index.js +15 -181
  38. package/middleware/cookie.js +20 -4
  39. package/middleware/cors.js +20 -0
  40. package/middleware/favicon.js +21 -5
  41. package/middleware/header.js +26 -9
  42. package/middleware/index.js +14 -23
  43. package/middleware/preventRetry.js +30 -0
  44. package/middleware/setBody.js +24 -10
  45. package/middleware/static.js +31 -10
  46. package/middleware/template.js +34 -14
  47. package/middleware/validator.js +43 -23
  48. package/middleware/waf.js +147 -287
  49. package/package.json +1 -1
  50. package/utils/checker.js +68 -0
  51. package/utils/error-handler.js +115 -0
  52. package/utils/error.js +81 -0
  53. package/utils/index.js +6 -0
  54. package/utils/keywords.js +126 -0
  55. package/utils/rate-limit.js +116 -0
  56. package/utils/response.js +103 -64
  57. package/utils/xss-filter.js +42 -0
  58. package/core/controller.js +0 -33
  59. package/core/index.js +0 -3
  60. package/core/service.js +0 -307
  61. package/middleware/log.js +0 -21
package/core/service.js DELETED
@@ -1,307 +0,0 @@
1
- import { success, fail, error } from "../utils/response.js";
2
- import { CODE } from "../config/code.js";
3
-
4
- /**
5
- * 数据库服务类
6
- */
7
- class Service {
8
- /**
9
- * 构造函数
10
- * @param {Object} knex - Knex实例
11
- * @param {string} tableName - 表名
12
- */
13
- constructor(knex, tableName) {
14
- if (!knex || !tableName) {
15
- throw new Error("Service: knex instance and tableName are required");
16
- }
17
-
18
- this.db = knex;
19
- this.tableName = tableName;
20
- this.pageSize = Chan.config?.PAGE_SIZE || 20;
21
- this.limit = Chan.config?.LIMIT_MAX || 300;
22
- }
23
-
24
-
25
-
26
- /**
27
- * 查询表所有记录(慎用)
28
- * @param {Object} query - 查询条件
29
- * @returns {Promise} 查询结果
30
- */
31
- async all(query = {}) {
32
- try {
33
- let _query = this.db(this.tableName);
34
- if (Object.keys(query).length) _query = _query.where(query);
35
- const res = await _query.select();
36
- return success(res);
37
- } catch (err) {
38
- return error(err);
39
- }
40
- }
41
-
42
- /**
43
- * 查询所有文档(仅支持对象模式排序)
44
- * @param {Object} query - 查询条件,可包含 sort: { field: 'asc|desc' }
45
- * @returns {Promise} 查询结果
46
- */
47
- async find(query = {}) {
48
- try {
49
- let _query = this.db(this.tableName);
50
-
51
- // 解构出排序参数,剩余作为 where 条件
52
- const { sort, ...whereConditions } = query;
53
-
54
- // 应用 where 条件
55
- if (Object.keys(whereConditions).length > 0) {
56
- _query = _query.where(whereConditions);
57
- }
58
-
59
- // 应用排序(仅支持对象模式)
60
- if (sort && typeof sort === 'object') {
61
- for (const [field, dir] of Object.entries(sort)) {
62
- const direction = ['asc', 'desc'].includes(dir.toLowerCase())
63
- ? dir.toLowerCase()
64
- : 'asc'; // 默认升序
65
- _query = _query.orderBy(field, direction);
66
- }
67
- }
68
-
69
- const res = await _query.select();
70
- return success(res);
71
- } catch (err) {
72
- return error(err);
73
- }
74
- }
75
-
76
- /**
77
- * 获取单个记录
78
- * @param {Object} query - 查询条件
79
- * @returns {Promise} 查询结果
80
- */
81
- async findOne(query = {}) {
82
- try {
83
- let dbQuery = this.db(this.tableName);
84
- if (Object.keys(query).length) dbQuery = dbQuery.where(query);
85
- const res = await dbQuery.first();
86
- return success(res);
87
- } catch (err) {
88
- return error(err);
89
- }
90
- }
91
-
92
- /**
93
- * 根据ID查询记录
94
- * @param {Object} options - 查询选项
95
- * @param {Object} options.query - 查询条件
96
- * @param {Array} options.field - 返回字段
97
- */
98
- async findById({ query, field = [] }) {
99
- try {
100
- let _query = this.db(this.tableName).where(query);
101
- if (field.length) _query = _query.select(field);
102
- const res = await _query.first();
103
- return success(res);
104
- } catch (err) {
105
- return error(err);
106
- }
107
- }
108
-
109
-
110
- /**
111
- * 创建新记录
112
- * @param {Object} data - 要插入的数据
113
- * @returns {Promise} 操作结果
114
- */
115
- async insert(data = {}) {
116
- try {
117
- if (!Object.keys(data).length) return fail(CODE[2002], { code: 2002 });
118
- const result = await this.db(this.tableName).insert(data);
119
- return success(result);
120
- } catch (err) {
121
- return error(err);
122
- }
123
- }
124
-
125
- /**
126
- * 插入多条记录
127
- * @param {Array} records - 数据数组
128
- * @returns {Promise} 操作结果
129
- */
130
- async insertMany(records = []) {
131
- try {
132
- if (!records.length) return fail(CODE[2002], { code: 2002 });
133
- const result = await this.db(this.tableName).insert(records);
134
- return success(result);
135
- } catch (err) {
136
- return error(err);
137
- }
138
- }
139
-
140
- /**
141
- * 根据条件删除记录
142
- * @param {Object} query - 查询条件
143
- * @returns {Promise} 操作结果
144
- */
145
- async delete(query = {}) {
146
- try {
147
- if (!Object.keys(query).length) return fail(CODE[2002], { code: 2002 });
148
- const affectedRows = await this.db(this.tableName).where(query).del();
149
- return success(affectedRows > 0);
150
- } catch (err) {
151
- return error(err);
152
- }
153
- }
154
-
155
- /**
156
- * 根据条件更新记录
157
- * @param {Object} options - 更新选项
158
- * @param {Object} options.query - 查询条件
159
- * @param {Object} options.params - 更新数据
160
- * @returns {Promise} 操作结果
161
- */
162
- async update({ query, params } = {}) {
163
- try {
164
- if (!query || !params || !Object.keys(query).length) {
165
- return fail(CODE[2001], { code: 2001 });
166
- }
167
- const result = await this.db(this.tableName).where(query).update(params);
168
- return success(!!result);
169
- } catch (err) {
170
- return error(err);
171
- }
172
- }
173
-
174
- /**
175
- * 批量更新多条记录(事务)
176
- * @param {Array} updates - [{ query, params }, ...]
177
- * @returns {Promise} 操作结果
178
- */
179
- async updateMany(updates = []) {
180
- if (!Array.isArray(updates) || !updates.length) {
181
- return fail(CODE[2002], { code: 2002 });
182
- }
183
-
184
- const trx = await this.db.transaction();
185
- try {
186
- for (const { query, params } of updates) {
187
- // FIX-4 防止空条件全表更新
188
- if (!query || !Object.keys(query).length) {
189
- await trx.rollback();
190
- return fail('批量更新不允许空条件');
191
- }
192
- const result = await trx(this.tableName).where(query).update(params);
193
- if (result === 0) {
194
- await trx.rollback();
195
- console.warn('[updateMany] 未匹配到行, query=%j', query); // FIX-3 日志
196
- return fail(`更新失败: ${JSON.stringify(query)}`);
197
- }
198
- }
199
- await trx.commit();
200
- return success(true);
201
- } catch (err) {
202
- await trx.rollback();
203
- return error(err);
204
- }
205
- }
206
-
207
-
208
- /**
209
- * 分页查询
210
- * @param {Object} options - 查询选项
211
- * @param {number} options.current - 当前页
212
- * @param {number} options.pageSize - 每页条数
213
- * @param {Object} options.query - 查询条件
214
- * @param {Array} options.field - 返回字段
215
- * @param {Object} options.sort - 排序条件 { field: 'asc|desc' }
216
- * @returns {Promise} 查询结果
217
- */
218
- async query({ current = 1, pageSize = 10, query = {}, field = [], sort = {} }) {
219
- try {
220
- const size = pageSize; // 如需要可再做非空校验
221
- const offset = (current - 1) * size;
222
-
223
- let countQuery = this.db(this.tableName).count("* as total");
224
- let dataQuery = this.db(this.tableName);
225
-
226
- if (Object.keys(query).length) {
227
- Object.entries(query).forEach(([k, v]) => {
228
- countQuery = countQuery.where(k, v);
229
- dataQuery = dataQuery.where(k, v);
230
- });
231
- }
232
-
233
- if (field && field.length) dataQuery = dataQuery.select(field);
234
-
235
- // 应用排序(仅支持对象模式)
236
- if (sort && typeof sort === 'object' && Object.keys(sort).length) {
237
- for (const [field, dir] of Object.entries(sort)) {
238
- const direction = ['asc', 'desc'].includes(dir.toLowerCase())
239
- ? dir.toLowerCase()
240
- : 'asc';
241
- dataQuery = dataQuery.orderBy(field, direction);
242
- }
243
- }
244
-
245
- const [totalResult, list] = await Promise.all([
246
- countQuery.first(),
247
- dataQuery.offset(offset).limit(size),
248
- ]);
249
-
250
- // FIX-1 显式转数字,防止字符串 "0"
251
- const total = Number(totalResult?.total ?? 0);
252
- return success({ list, total, current, pageSize: size });
253
- } catch (err) {
254
- return error(err);
255
- }
256
- }
257
-
258
- /**
259
- * 计数查询
260
- * @param {Object} query - 查询条件
261
- * @returns {Promise} 查询结果
262
- */
263
- async count(query = {}) {
264
- try {
265
- let dataQuery = this.db(this.tableName);
266
- if (Object.keys(query).length) dataQuery = dataQuery.where(query);
267
- const result = await dataQuery.count("* as total").first();
268
- return success(Number(result?.total ?? 0));
269
- } catch (err) {
270
- return error(err);
271
- }
272
- }
273
-
274
- /**
275
- * 多表关联查询
276
- * @param {Object} query - 关联配置
277
- * @returns {Promise} 查询结果
278
- */
279
- async join(query) {
280
- try {
281
- const {
282
- joinTable,
283
- localField,
284
- foreignField,
285
- select = "*",
286
- where = {},
287
- } = query;
288
-
289
- let _query = this.db(this.tableName)
290
- .join(
291
- joinTable,
292
- `${this.tableName}.${localField}`,
293
- "=",
294
- `${joinTable}.${foreignField}`
295
- )
296
- .select(select);
297
-
298
- if (Object.keys(where).length) _query = _query.where(where);
299
- const res = await _query;
300
- return success(res);
301
- } catch (err) {
302
- return error(err);
303
- }
304
- }
305
- }
306
-
307
- export default Service;
package/middleware/log.js DELETED
@@ -1,21 +0,0 @@
1
- import morgan from "morgan";
2
- import { getIp } from "../helper/ip.js";
3
- morgan.token("ip", (req, res) => {
4
- return getIp(req);
5
- });
6
- // 自定义 morgan 格式:IP Method URL Status Length - Response-Time ms
7
- morgan.format("chancms", (tokens, req, res) => {
8
- return [
9
- tokens.ip(req, res), // 客户端 IP
10
- tokens.method(req, res), // GET/POST
11
- tokens.url(req, res), // 完整 URL(含 query)
12
- tokens.status(req, res), // 状态码(如 403)
13
- tokens.res(req, res, "content-length") || "-", // 响应体大小
14
- "-", // 占位符(原日志中的 '-')
15
- tokens["response-time"](req, res),
16
- "ms", // 响应时间
17
- ].join(" ");
18
- });
19
- export const log = (app, logger) => {
20
- app.use(morgan(logger.level));
21
- };