befly 2.3.3 → 3.0.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 (93) hide show
  1. package/apis/health/info.ts +64 -0
  2. package/apis/tool/tokenCheck.ts +51 -0
  3. package/bin/befly.ts +202 -0
  4. package/checks/conflict.ts +408 -0
  5. package/checks/{table.js → table.ts} +139 -61
  6. package/config/env.ts +218 -0
  7. package/config/reserved.ts +96 -0
  8. package/main.ts +101 -0
  9. package/package.json +44 -8
  10. package/plugins/{db.js → db.ts} +24 -11
  11. package/plugins/logger.ts +28 -0
  12. package/plugins/redis.ts +51 -0
  13. package/plugins/tool.ts +34 -0
  14. package/scripts/syncDb/apply.ts +171 -0
  15. package/scripts/syncDb/constants.ts +70 -0
  16. package/scripts/syncDb/ddl.ts +182 -0
  17. package/scripts/syncDb/helpers.ts +172 -0
  18. package/scripts/syncDb/index.ts +215 -0
  19. package/scripts/syncDb/schema.ts +199 -0
  20. package/scripts/syncDb/sqlite.ts +50 -0
  21. package/scripts/syncDb/state.ts +104 -0
  22. package/scripts/syncDb/table.ts +204 -0
  23. package/scripts/syncDb/tableCreate.ts +142 -0
  24. package/scripts/syncDb/tests/constants.test.ts +104 -0
  25. package/scripts/syncDb/tests/ddl.test.ts +134 -0
  26. package/scripts/syncDb/tests/helpers.test.ts +70 -0
  27. package/scripts/syncDb/types.ts +92 -0
  28. package/scripts/syncDb/version.ts +73 -0
  29. package/scripts/syncDb.ts +9 -0
  30. package/scripts/{syncDev.js → syncDev.ts} +41 -25
  31. package/system.ts +149 -0
  32. package/tables/_common.json +21 -0
  33. package/tables/admin.json +10 -0
  34. package/tsconfig.json +58 -0
  35. package/types/api.d.ts +246 -0
  36. package/types/befly.d.ts +234 -0
  37. package/types/common.d.ts +215 -0
  38. package/types/context.ts +167 -0
  39. package/types/crypto.d.ts +23 -0
  40. package/types/database.d.ts +278 -0
  41. package/types/index.d.ts +16 -0
  42. package/types/index.ts +459 -0
  43. package/types/jwt.d.ts +99 -0
  44. package/types/logger.d.ts +43 -0
  45. package/types/plugin.d.ts +109 -0
  46. package/types/redis.d.ts +44 -0
  47. package/types/tool.d.ts +67 -0
  48. package/types/validator.d.ts +45 -0
  49. package/utils/addonHelper.ts +60 -0
  50. package/utils/api.ts +23 -0
  51. package/utils/{colors.js → colors.ts} +79 -21
  52. package/utils/crypto.ts +308 -0
  53. package/utils/datetime.ts +51 -0
  54. package/utils/dbHelper.ts +142 -0
  55. package/utils/errorHandler.ts +68 -0
  56. package/utils/index.ts +46 -0
  57. package/utils/jwt.ts +493 -0
  58. package/utils/logger.ts +284 -0
  59. package/utils/objectHelper.ts +68 -0
  60. package/utils/pluginHelper.ts +62 -0
  61. package/utils/redisHelper.ts +338 -0
  62. package/utils/response.ts +38 -0
  63. package/utils/{sqlBuilder.js → sqlBuilder.ts} +233 -97
  64. package/utils/sqlHelper.ts +447 -0
  65. package/utils/tableHelper.ts +167 -0
  66. package/utils/tool.ts +230 -0
  67. package/utils/typeHelper.ts +101 -0
  68. package/utils/validate.ts +451 -0
  69. package/utils/{xml.js → xml.ts} +100 -74
  70. package/.npmrc +0 -3
  71. package/.prettierignore +0 -2
  72. package/.prettierrc +0 -11
  73. package/apis/health/info.js +0 -49
  74. package/apis/tool/tokenCheck.js +0 -29
  75. package/bin/befly.js +0 -109
  76. package/config/env.js +0 -64
  77. package/main.js +0 -579
  78. package/plugins/logger.js +0 -14
  79. package/plugins/redis.js +0 -32
  80. package/plugins/tool.js +0 -8
  81. package/scripts/syncDb.js +0 -752
  82. package/system.js +0 -118
  83. package/tables/common.json +0 -16
  84. package/tables/tool.json +0 -6
  85. package/utils/api.js +0 -27
  86. package/utils/crypto.js +0 -260
  87. package/utils/index.js +0 -334
  88. package/utils/jwt.js +0 -387
  89. package/utils/logger.js +0 -143
  90. package/utils/redisHelper.js +0 -74
  91. package/utils/sqlManager.js +0 -471
  92. package/utils/tool.js +0 -31
  93. package/utils/validate.js +0 -226
@@ -1,471 +0,0 @@
1
- import { Env } from '../config/env.js';
2
- import { Logger } from './logger.js';
3
- import { createQueryBuilder } from './sqlBuilder.js';
4
-
5
- // 数据库管理器(从 plugins/db.js 提取)
6
- export class SqlManager {
7
- // 私有属性
8
- #sql;
9
- #befly;
10
-
11
- constructor(client, befly) {
12
- this.#sql = client;
13
- this.#befly = befly;
14
- }
15
-
16
- // 原始连接池访问(兼容占位)
17
- get pool() {
18
- return {
19
- activeConnections: () => 0,
20
- totalConnections: () => 0,
21
- idleConnections: () => 0,
22
- taskQueueSize: () => 0
23
- };
24
- }
25
-
26
- // 创建查询构造器
27
- query() {
28
- return createQueryBuilder();
29
- }
30
-
31
- // 私有方法:通用数据处理函数 - 自动添加ID、时间戳和状态
32
- async #processDataForInsert(data) {
33
- const now = Date.now();
34
- const genId = async () => (this.#befly?.redis?.genTimeID ? await this.#befly.redis.genTimeID() : now);
35
-
36
- if (Array.isArray(data)) {
37
- return await Promise.all(
38
- data.map(async (item) => ({
39
- ...item,
40
- id: await genId(),
41
- state: 0,
42
- created_at: now,
43
- updated_at: now
44
- }))
45
- );
46
- } else {
47
- return {
48
- ...data,
49
- id: await genId(),
50
- state: 0,
51
- created_at: now,
52
- updated_at: now
53
- };
54
- }
55
- }
56
-
57
- // 私有方法:添加默认的state过滤条件
58
- #addDefaultStateFilter(where = {}) {
59
- const hasStateCondition = Object.keys(where).some((key) => key === 'state' || key.startsWith('state$'));
60
- if (!hasStateCondition) return { ...where, state$ne: 2 };
61
- return where;
62
- }
63
-
64
- // 私有方法:执行 SQL(支持传入事务连接对象)
65
- async #executeWithConn(query, params = [], conn = null) {
66
- if (!query || typeof query !== 'string') {
67
- throw new Error('SQL 语句是必需的');
68
- }
69
-
70
- const isSelectLike = /^\s*(with|select|show|desc|explain)\b/i.test(query);
71
- const isWriteLike = /^\s*(insert|update|delete|replace)\b/i.test(query);
72
- const client = conn || this.#sql;
73
- try {
74
- if (Env.DB_DEBUG === 1) {
75
- Logger.debug('执行SQL:', { sql: query, params });
76
- }
77
- // 读查询
78
- if (isSelectLike) {
79
- if (params && params.length > 0) {
80
- return await client.unsafe(query, params);
81
- } else {
82
- return await client.unsafe(query);
83
- }
84
- }
85
-
86
- // 写查询
87
- if (isWriteLike) {
88
- const runOn = conn ? client : await this.#sql.reserve();
89
- try {
90
- if (params && params.length > 0) {
91
- await runOn.unsafe(query, params);
92
- } else {
93
- await runOn.unsafe(query);
94
- }
95
- const [{ affectedRows }] = await runOn`SELECT ROW_COUNT() AS affectedRows`;
96
- const [{ insertId }] = await runOn`SELECT LAST_INSERT_ID() AS insertId`;
97
- return { affectedRows: Number(affectedRows) || 0, insertId: Number(insertId) || 0 };
98
- } finally {
99
- if (!conn && runOn && typeof runOn.release === 'function') runOn.release();
100
- }
101
- }
102
-
103
- // 其他(DDL等)
104
- if (params && params.length > 0) {
105
- return await client.unsafe(query, params);
106
- } else {
107
- return await client.unsafe(query);
108
- }
109
- } catch (error) {
110
- Logger.error('SQL 执行失败:', { sql: query, params, error: error.message });
111
- throw error;
112
- }
113
- }
114
-
115
- // 私有方法:获取单条记录详情(支持传入连接对象)
116
- async #getDetailWithConn(table, options = {}, conn = null) {
117
- if (!table || typeof table !== 'string') {
118
- throw new Error('表名是必需的');
119
- }
120
-
121
- const { where = {}, fields = '*', leftJoins = [] } = typeof options === 'object' && !Array.isArray(options) ? options : { where: options };
122
-
123
- try {
124
- const filteredWhere = this.#addDefaultStateFilter(where);
125
- const builder = createQueryBuilder().select(fields).from(table).where(filteredWhere).limit(1);
126
-
127
- leftJoins.forEach((join) => {
128
- if (typeof join === 'string') {
129
- const parts = join.split(' ON ');
130
- if (parts.length === 2) builder.leftJoin(parts[0].trim(), parts[1].trim());
131
- } else if (join && typeof join === 'object' && join.table && join.on) {
132
- builder.leftJoin(join.table, join.on);
133
- }
134
- });
135
-
136
- const { sql: q, params } = builder.toSelectSql();
137
- const result = await this.#executeWithConn(q, params, conn);
138
- return result[0] || null;
139
- } catch (error) {
140
- Logger.error('getDetail 执行失败:', error);
141
- throw error;
142
- }
143
- }
144
-
145
- // 私有方法:获取列表(支持传入连接对象)
146
- async #getListWithConn(table, options = {}, conn = null) {
147
- if (!table || typeof table !== 'string') {
148
- throw new Error('表名是必需的');
149
- }
150
-
151
- const { where = {}, fields = '*', leftJoins = [], orderBy = [], groupBy = [], having = [], page = 1, pageSize = 10 } = options;
152
-
153
- try {
154
- const filteredWhere = this.#addDefaultStateFilter(where);
155
- const builder = createQueryBuilder().select(fields).from(table).where(filteredWhere);
156
-
157
- leftJoins.forEach((join) => {
158
- if (typeof join === 'string') {
159
- const parts = join.split(' ON ');
160
- if (parts.length === 2) builder.leftJoin(parts[0].trim(), parts[1].trim());
161
- } else if (join && typeof join === 'object' && join.table && join.on) {
162
- builder.leftJoin(join.table, join.on);
163
- }
164
- });
165
-
166
- if (Array.isArray(groupBy) && groupBy.length > 0) builder.groupBy(groupBy);
167
- if (Array.isArray(having) && having.length > 0) having.forEach((h) => builder.having(h));
168
- if (Array.isArray(orderBy) && orderBy.length > 0) builder.orderBy(orderBy);
169
-
170
- const numPage = parseInt(page) || 1;
171
- const numPageSize = parseInt(pageSize) || 10;
172
- if (numPage > 0 && numPageSize > 0) {
173
- const offset = (numPage - 1) * numPageSize;
174
- builder.limit(numPageSize, offset);
175
- }
176
-
177
- const { sql: q, params } = builder.toSelectSql();
178
- const rows = await this.#executeWithConn(q, params, conn);
179
-
180
- let total = 0;
181
- if (numPage > 0 && numPageSize > 0) {
182
- const countBuilder = createQueryBuilder().from(table).where(filteredWhere);
183
- leftJoins.forEach((join) => {
184
- if (typeof join === 'string') {
185
- const parts = join.split(' ON ');
186
- if (parts.length === 2) countBuilder.leftJoin(parts[0].trim(), parts[1].trim());
187
- } else if (join && typeof join === 'object' && join.table && join.on) {
188
- countBuilder.leftJoin(join.table, join.on);
189
- }
190
- });
191
-
192
- const { sql: countSql, params: countParams } = countBuilder.toCountSql();
193
- const countResult = await this.#executeWithConn(countSql, countParams, conn);
194
- total = countResult[0]?.total || 0;
195
- }
196
-
197
- return {
198
- rows: Array.isArray(rows) ? rows : [],
199
- total,
200
- page: numPage,
201
- pageSize: numPageSize
202
- };
203
- } catch (error) {
204
- Logger.error('getList 执行失败:', error);
205
- throw error;
206
- }
207
- }
208
-
209
- // 私有方法:获取所有记录(支持传入连接对象)
210
- async #getAllWithConn(table, options = {}, conn = null) {
211
- if (!table || typeof table !== 'string') {
212
- throw new Error('表名是必需的');
213
- }
214
-
215
- const { where = {}, fields = '*', leftJoins = [], orderBy = [] } = typeof options === 'object' && !Array.isArray(options) ? options : { where: options };
216
-
217
- try {
218
- const filteredWhere = this.#addDefaultStateFilter(where);
219
- const builder = createQueryBuilder().select(fields).from(table).where(filteredWhere);
220
-
221
- leftJoins.forEach((join) => {
222
- if (typeof join === 'string') {
223
- const parts = join.split(' ON ');
224
- if (parts.length === 2) builder.leftJoin(parts[0].trim(), parts[1].trim());
225
- } else if (join && typeof join === 'object' && join.table && join.on) {
226
- builder.leftJoin(join.table, join.on);
227
- }
228
- });
229
-
230
- if (Array.isArray(orderBy) && orderBy.length > 0) builder.orderBy(orderBy);
231
-
232
- const { sql: q, params } = builder.toSelectSql();
233
- const result = await this.#executeWithConn(q, params, conn);
234
- return Array.isArray(result) ? result : [];
235
- } catch (error) {
236
- Logger.error('getAll 执行失败:', error);
237
- throw error;
238
- }
239
- }
240
-
241
- // 私有方法:插入数据(支持传入连接对象)
242
- async #insDataWithConn(table, data, conn = null) {
243
- if (!table || typeof table !== 'string') {
244
- throw new Error('表名是必需的');
245
- }
246
- if (!data) {
247
- throw new Error('插入数据是必需的');
248
- }
249
- try {
250
- const processedData = await this.#processDataForInsert(data);
251
- const builder = createQueryBuilder();
252
- const { sql: q, params } = builder.toInsertSql(table, processedData);
253
- return await this.#executeWithConn(q, params, conn);
254
- } catch (error) {
255
- Logger.error('insData 执行失败:', error);
256
- throw error;
257
- }
258
- }
259
-
260
- // 私有方法:更新数据(支持传入连接对象)
261
- async #updDataWithConn(table, data, where, conn = null) {
262
- if (!table || typeof table !== 'string') {
263
- throw new Error('表名是必需的');
264
- }
265
- if (!data || typeof data !== 'object') {
266
- throw new Error('更新数据是必需的');
267
- }
268
- if (!where) {
269
- throw new Error('更新操作需要 WHERE 条件');
270
- }
271
- try {
272
- const filteredData = Object.fromEntries(Object.entries(data).filter(([key, value]) => value !== undefined && !['id', 'created_at', 'deleted_at'].includes(key)));
273
- const updateData = { ...filteredData, updated_at: Date.now() };
274
- const builder = createQueryBuilder().where(where);
275
- const { sql: q, params } = builder.toUpdateSql(table, updateData);
276
- return await this.#executeWithConn(q, params, conn);
277
- } catch (error) {
278
- Logger.error('updData 执行失败:', error);
279
- throw error;
280
- }
281
- }
282
-
283
- // 私有方法:删除数据(支持传入连接对象)
284
- async #delDataWithConn(table, where, conn = null) {
285
- if (!table || typeof table !== 'string') {
286
- throw new Error('表名是必需的');
287
- }
288
- if (!where) {
289
- throw new Error('删除操作需要 WHERE 条件');
290
- }
291
- try {
292
- const builder = createQueryBuilder().where(where);
293
- const { sql: q, params } = builder.toDeleteSql(table);
294
- return await this.#executeWithConn(q, params, conn);
295
- } catch (error) {
296
- Logger.error('delData 执行失败:', error);
297
- throw error;
298
- }
299
- }
300
-
301
- // 私有方法:软删除数据(支持传入连接对象)
302
- async #delData2WithConn(table, where, conn = null) {
303
- if (!table || typeof table !== 'string') {
304
- throw new Error('表名是必需的');
305
- }
306
- if (!where) {
307
- throw new Error('软删除操作需要 WHERE 条件');
308
- }
309
- try {
310
- const updateData = { state: 2, updated_at: Date.now() };
311
- const builder = createQueryBuilder().where(where);
312
- const { sql: q, params } = builder.toUpdateSql(table, updateData);
313
- return await this.#executeWithConn(q, params, conn);
314
- } catch (error) {
315
- Logger.error('delData2 执行失败:', error);
316
- throw error;
317
- }
318
- }
319
-
320
- // 私有方法:批量插入(支持传入连接对象)
321
- async #insBatchWithConn(table, dataArray, conn = null) {
322
- if (!table || typeof table !== 'string') {
323
- throw new Error('表名是必需的');
324
- }
325
- if (!Array.isArray(dataArray) || dataArray.length === 0) {
326
- throw new Error('批量插入数据不能为空');
327
- }
328
- try {
329
- const processedDataArray = await this.#processDataForInsert(dataArray);
330
- const builder = createQueryBuilder();
331
- const { sql: q, params } = builder.toInsertSql(table, processedDataArray);
332
- return await this.#executeWithConn(q, params, conn);
333
- } catch (error) {
334
- Logger.error('insBatch 执行失败:', error);
335
- throw error;
336
- }
337
- }
338
-
339
- // 私有方法:获取记录总数(支持传入连接对象)
340
- async #getCountWithConn(table, options = {}, conn = null) {
341
- if (!table || typeof table !== 'string') {
342
- throw new Error('表名是必需的');
343
- }
344
- const { where = {}, leftJoins = [] } = typeof options === 'object' && !Array.isArray(options) ? options : { where: options };
345
- try {
346
- const filteredWhere = this.#addDefaultStateFilter(where);
347
- const builder = createQueryBuilder().from(table).where(filteredWhere);
348
-
349
- leftJoins.forEach((join) => {
350
- if (typeof join === 'string') {
351
- const parts = join.split(' ON ');
352
- if (parts.length === 2) builder.leftJoin(parts[0].trim(), parts[1].trim());
353
- } else if (join && typeof join === 'object' && join.table && join.on) {
354
- builder.leftJoin(join.table, join.on);
355
- }
356
- });
357
-
358
- const { sql: q, params } = builder.toCountSql();
359
- const result = await this.#executeWithConn(q, params, conn);
360
- return result[0]?.total || 0;
361
- } catch (error) {
362
- Logger.error('getCount 执行失败:', error);
363
- throw error;
364
- }
365
- }
366
-
367
- // 执行原始 SQL - 核心方法
368
- async execute(sql, params = []) {
369
- return await this.#executeWithConn(sql, params);
370
- }
371
-
372
- // 获取单条记录详情
373
- async getDetail(table, options = {}) {
374
- return await this.#getDetailWithConn(table, options);
375
- }
376
-
377
- // 获取列表(支持分页)
378
- async getList(table, options = {}) {
379
- return await this.#getListWithConn(table, options);
380
- }
381
-
382
- // 获取所有记录
383
- async getAll(table, options = {}) {
384
- return await this.#getAllWithConn(table, options);
385
- }
386
-
387
- // 插入数据 - 增强版
388
- async insData(table, data) {
389
- return await this.#insDataWithConn(table, data);
390
- }
391
-
392
- // 更新数据 - 增强版
393
- async updData(table, data, where) {
394
- return await this.#updDataWithConn(table, data, where);
395
- }
396
-
397
- // 删除数据
398
- async delData(table, where) {
399
- return await this.#delDataWithConn(table, where);
400
- }
401
-
402
- // 软删除数据
403
- async delData2(table, where) {
404
- return await this.#delData2WithConn(table, where);
405
- }
406
-
407
- // 批量插入 - 增强版
408
- async insBatch(table, dataArray) {
409
- return await this.#insBatchWithConn(table, dataArray);
410
- }
411
-
412
- // 获取记录总数
413
- async getCount(table, options = {}) {
414
- return await this.#getCountWithConn(table, options);
415
- }
416
-
417
- // 事务处理
418
- async trans(callback) {
419
- if (typeof callback !== 'function') {
420
- throw new Error('事务回调函数是必需的');
421
- }
422
- try {
423
- const result = await this.#sql.begin(async (tx) => {
424
- const txMethods = {
425
- query: async (query, params = []) => this.#executeWithConn(query, params, tx),
426
- execute: async (query, params = []) => this.#executeWithConn(query, params, tx),
427
-
428
- getDetail: async (table, options = {}) => this.#getDetailWithConn(table, options, tx),
429
- getList: async (table, options = {}) => this.#getListWithConn(table, options, tx),
430
- getAll: async (table, options = {}) => this.#getAllWithConn(table, options, tx),
431
- insData: async (table, data) => this.#insDataWithConn(table, data, tx),
432
- updData: async (table, data, where) => this.#updDataWithConn(table, data, where, tx),
433
- delData: async (table, where) => this.#delDataWithConn(table, where, tx),
434
- delData2: async (table, where) => this.#delData2WithConn(table, where, tx),
435
- getCount: async (table, options = {}) => this.#getCountWithConn(table, options, tx),
436
- insBatch: async (table, dataArray) => this.#insBatchWithConn(table, dataArray, tx)
437
- };
438
- return await callback(txMethods);
439
- });
440
- return result;
441
- } catch (error) {
442
- Logger.info('事务已回滚');
443
- throw error;
444
- }
445
- }
446
-
447
- // 获取连接池状态
448
- getPoolStatus() {
449
- return {
450
- activeConnections: 0,
451
- totalConnections: 0,
452
- idleConnections: 0,
453
- taskQueueSize: 0
454
- };
455
- }
456
-
457
- // 关闭连接池
458
- async close() {
459
- if (this.#sql) {
460
- try {
461
- await this.#sql.close();
462
- Logger.info('数据库连接已关闭');
463
- } catch (error) {
464
- Logger.error('关闭数据库连接失败:', error);
465
- throw error;
466
- }
467
- }
468
- }
469
- }
470
-
471
- export default SqlManager;
package/utils/tool.js DELETED
@@ -1,31 +0,0 @@
1
- import { omitFields } from './index.js';
2
-
3
- // 工具类:通过构造函数注入 befly
4
- export class Tool {
5
- constructor(befly) {
6
- this.befly = befly;
7
- }
8
-
9
- async updData(data, now = Date.now()) {
10
- const cleaned = omitFields(data ?? {}, ['id', 'created_at', 'deleted_at'], [undefined]);
11
- return { ...cleaned, updated_at: now };
12
- }
13
-
14
- async insData(data, now = Date.now()) {
15
- const genId = async () => await this.befly.redis.genTimeID();
16
-
17
- if (Array.isArray(data)) {
18
- return await Promise.all(
19
- data.map(async (item) => ({
20
- ...omitFields(item ?? {}, [], [undefined]),
21
- id: await genId(),
22
- created_at: now,
23
- updated_at: now
24
- }))
25
- );
26
- } else {
27
- const cleaned = omitFields(data ?? {}, [], [undefined]);
28
- return { ...cleaned, id: await genId(), created_at: now, updated_at: now };
29
- }
30
- }
31
- }