befly 2.3.2 → 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.ts +284 -0
  6. package/config/env.ts +218 -0
  7. package/config/reserved.ts +96 -0
  8. package/main.ts +101 -0
  9. package/package.json +45 -16
  10. package/plugins/{db.js → db.ts} +25 -12
  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.ts +112 -0
  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/checks/table.js +0 -221
  76. package/config/env.js +0 -62
  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 -603
  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 -387
  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 -228
@@ -0,0 +1,451 @@
1
+ /**
2
+ * 数据验证器 - TypeScript 版本
3
+ * 提供类型安全的字段验证功能
4
+ */
5
+
6
+ import { isType } from './typeHelper.js';
7
+ import { parseRule } from './tableHelper.js';
8
+ import type { TableDefinition, FieldRule, ParsedFieldRule } from '../types/common.js';
9
+ import type { ValidationResult, ValidationError } from '../types/validator';
10
+
11
+ /**
12
+ * 验证器类
13
+ */
14
+ export class Validator {
15
+ /**
16
+ * 验证数据
17
+ * @param data - 要验证的数据对象
18
+ * @param rules - 验证规则对象
19
+ * @param required - 必传字段数组
20
+ * @returns 验证结果 { code: 0|1, fields: {} }
21
+ */
22
+ validate(data: Record<string, any>, rules: TableDefinition, required: string[] = []): ValidationResult {
23
+ const result: ValidationResult = {
24
+ code: 0,
25
+ fields: {}
26
+ };
27
+
28
+ // 参数检查
29
+ if (!this.checkParams(data, rules, required, result)) {
30
+ return result;
31
+ }
32
+
33
+ // 检查必传字段
34
+ this.checkRequiredFields(data, rules, required, result);
35
+
36
+ // 验证所有在规则中定义的字段
37
+ this.validateFields(data, rules, required, result);
38
+
39
+ return result;
40
+ }
41
+
42
+ /**
43
+ * 检查参数有效性
44
+ */
45
+ private checkParams(data: any, rules: any, required: any, result: ValidationResult): boolean {
46
+ if (!data || typeof data !== 'object') {
47
+ result.code = 1;
48
+ result.fields.error = '数据必须是对象格式';
49
+ return false;
50
+ }
51
+
52
+ if (!rules || typeof rules !== 'object') {
53
+ result.code = 1;
54
+ result.fields.error = '验证规则必须是对象格式';
55
+ return false;
56
+ }
57
+
58
+ if (!Array.isArray(required)) {
59
+ result.code = 1;
60
+ result.fields.error = '必传字段必须是数组格式';
61
+ return false;
62
+ }
63
+
64
+ return true;
65
+ }
66
+
67
+ /**
68
+ * 检查必传字段
69
+ */
70
+ private checkRequiredFields(data: Record<string, any>, rules: TableDefinition, required: string[], result: ValidationResult): void {
71
+ for (const fieldName of required) {
72
+ const value = data[fieldName];
73
+ if (!(fieldName in data) || value === undefined || value === null || value === '') {
74
+ result.code = 1;
75
+ const ruleParts = parseRule(rules[fieldName] || '');
76
+ const fieldLabel = ruleParts.name || fieldName;
77
+ result.fields[fieldName] = `${fieldLabel}(${fieldName})为必填项`;
78
+ }
79
+ }
80
+ }
81
+
82
+ /**
83
+ * 验证所有字段
84
+ */
85
+ private validateFields(data: Record<string, any>, rules: TableDefinition, required: string[], result: ValidationResult): void {
86
+ for (const [fieldName, rule] of Object.entries(rules)) {
87
+ // 如果字段不存在且不是必传字段,跳过验证
88
+ if (!(fieldName in data) && !required.includes(fieldName)) {
89
+ continue;
90
+ }
91
+
92
+ // 如果必传验证已经失败,跳过后续验证
93
+ if (result.fields[fieldName]) {
94
+ continue;
95
+ }
96
+
97
+ const value = data[fieldName];
98
+ const error = this.validateFieldValue(value, rule, fieldName);
99
+
100
+ if (error) {
101
+ result.code = 1;
102
+ result.fields[fieldName] = error;
103
+ }
104
+ }
105
+ }
106
+
107
+ /**
108
+ * 验证单个字段的值
109
+ */
110
+ private validateFieldValue(value: any, rule: FieldRule, fieldName: string): ValidationError {
111
+ const parsed = parseRule(rule);
112
+ const { name, type, min, max, regex } = parsed;
113
+
114
+ switch (type.toLowerCase()) {
115
+ case 'number':
116
+ return this.validateNumber(value, name, min, max, regex, fieldName);
117
+ case 'string':
118
+ return this.validateString(value, name, min, max, regex, fieldName);
119
+ case 'text':
120
+ return this.validateString(value, name, min, max, regex, fieldName);
121
+ case 'array':
122
+ return this.validateArray(value, label, min, max, regex, fieldName);
123
+ default:
124
+ return `字段 ${fieldName} 的类型 ${type} 不支持`;
125
+ }
126
+ }
127
+
128
+ /**
129
+ * 验证数字类型
130
+ */
131
+ private validateNumber(value: any, name: string, min: number | null, max: number | null, spec: string | null, fieldName: string): ValidationError {
132
+ try {
133
+ if (isType(value, 'number') === false) {
134
+ return `${name}(${fieldName})必须是数字`;
135
+ }
136
+
137
+ if (min !== null && value < min) {
138
+ return `${name}(${fieldName})不能小于${min}`;
139
+ }
140
+
141
+ if (max !== null && max > 0 && value > max) {
142
+ return `${name}(${fieldName})不能大于${max}`;
143
+ }
144
+
145
+ if (spec && spec.trim() !== '') {
146
+ try {
147
+ const regExp = new RegExp(spec);
148
+ if (!regExp.test(String(value))) {
149
+ return `${name}(${fieldName})格式不正确`;
150
+ }
151
+ } catch (error: any) {
152
+ return `${name}(${fieldName})的正则表达式格式错误`;
153
+ }
154
+ }
155
+
156
+ return null;
157
+ } catch (error: any) {
158
+ return `${name}(${fieldName})验证出错: ${error.message}`;
159
+ }
160
+ }
161
+
162
+ /**
163
+ * 验证字符串类型
164
+ */
165
+ private validateString(value: any, name: string, min: number | null, max: number | null, spec: string | null, fieldName: string): ValidationError {
166
+ try {
167
+ if (isType(value, 'string') === false) {
168
+ return `${name}(${fieldName})必须是字符串`;
169
+ }
170
+
171
+ if (min !== null && value.length < min) {
172
+ return `${name}(${fieldName})长度不能少于${min}个字符`;
173
+ }
174
+
175
+ if (max !== null && max > 0 && value.length > max) {
176
+ return `${name}(${fieldName})长度不能超过${max}个字符`;
177
+ }
178
+
179
+ if (spec && spec.trim() !== '') {
180
+ try {
181
+ const regExp = new RegExp(spec);
182
+ if (!regExp.test(value)) {
183
+ return `${name}(${fieldName})格式不正确`;
184
+ }
185
+ } catch (error: any) {
186
+ return `${name}(${fieldName})的正则表达式格式错误`;
187
+ }
188
+ }
189
+
190
+ return null;
191
+ } catch (error: any) {
192
+ return `${name}(${fieldName})验证出错: ${error.message}`;
193
+ }
194
+ }
195
+
196
+ /**
197
+ * 验证数组类型
198
+ */
199
+ private validateArray(value: any, name: string, min: number | null, max: number | null, spec: string | null, fieldName: string): ValidationError {
200
+ try {
201
+ if (!Array.isArray(value)) {
202
+ return `${name}(${fieldName})必须是数组`;
203
+ }
204
+
205
+ if (min !== null && value.length < min) {
206
+ return `${name}(${fieldName})至少需要${min}个元素`;
207
+ }
208
+
209
+ if (max !== null && max > 0 && value.length > max) {
210
+ return `${name}(${fieldName})最多只能有${max}个元素`;
211
+ }
212
+
213
+ if (spec && spec.trim() !== '') {
214
+ try {
215
+ const regExp = new RegExp(spec);
216
+ for (const item of value) {
217
+ if (!regExp.test(String(item))) {
218
+ return `${name}(${fieldName})中的元素"${item}"格式不正确`;
219
+ }
220
+ }
221
+ } catch (error: any) {
222
+ return `${name}(${fieldName})的正则表达式格式错误`;
223
+ }
224
+ }
225
+
226
+ return null;
227
+ } catch (error: any) {
228
+ return `${name}(${fieldName})验证出错: ${error.message}`;
229
+ }
230
+ }
231
+
232
+ /**
233
+ * 快速验证(静态方法)
234
+ * @param data - 要验证的数据
235
+ * @param rules - 验证规则
236
+ * @param required - 必填字段
237
+ * @returns 验证结果
238
+ */
239
+ static validate(data: Record<string, any>, rules: TableDefinition, required?: string[]): ValidationResult;
240
+ static validate(value: any, rule: string): { valid: boolean; value: any; errors: string[] };
241
+ static validate(dataOrValue: any, rulesOrRule: any, required: string[] = []): any {
242
+ // 如果第二个参数是字符串,则是单值验证
243
+ if (typeof rulesOrRule === 'string') {
244
+ return Validator.validateSingleValue(dataOrValue, rulesOrRule);
245
+ }
246
+ // 否则是对象验证
247
+ return validator.validate(dataOrValue, rulesOrRule, required);
248
+ }
249
+
250
+ /**
251
+ * 验证单个值(静态方法)
252
+ * @param value - 要验证的值
253
+ * @param rule - 验证规则字符串
254
+ * @returns 验证结果 { valid: boolean, value: any, errors: string[] }
255
+ */
256
+ static validateSingleValue(value: any, rule: string): { valid: boolean; value: any; errors: string[] } {
257
+ const parsed = parseRule(rule);
258
+ const { name, type, min, max, regex, default: defaultValue } = parsed;
259
+
260
+ // 处理 undefined/null 值,使用默认值
261
+ if (value === undefined || value === null) {
262
+ if (defaultValue !== 'null' && defaultValue !== null) {
263
+ // 特殊处理数组类型的默认值字符串
264
+ if (type === 'array' && typeof defaultValue === 'string') {
265
+ if (defaultValue === '[]') {
266
+ return { valid: true, value: [], errors: [] };
267
+ }
268
+ // 尝试解析 JSON 格式的数组字符串
269
+ try {
270
+ const parsedArray = JSON.parse(defaultValue);
271
+ if (Array.isArray(parsedArray)) {
272
+ return { valid: true, value: parsedArray, errors: [] };
273
+ }
274
+ } catch {
275
+ // 解析失败,使用空数组
276
+ return { valid: true, value: [], errors: [] };
277
+ }
278
+ }
279
+ return { valid: true, value: defaultValue, errors: [] };
280
+ }
281
+ // 如果没有默认值,根据类型返回默认值
282
+ if (type === 'number') {
283
+ return { valid: true, value: 0, errors: [] };
284
+ } else if (type === 'array') {
285
+ return { valid: true, value: [], errors: [] };
286
+ } else if (type === 'string' || type === 'text') {
287
+ return { valid: true, value: '', errors: [] };
288
+ }
289
+ }
290
+
291
+ const errors: string[] = [];
292
+
293
+ // 类型转换
294
+ let convertedValue = value;
295
+ if (type === 'number' && typeof value === 'string') {
296
+ convertedValue = Number(value);
297
+ if (isNaN(convertedValue)) {
298
+ errors.push(`${name || '值'}必须是有效的数字`);
299
+ return { valid: false, value: null, errors };
300
+ }
301
+ }
302
+
303
+ // 类型验证
304
+ switch (type.toLowerCase()) {
305
+ case 'number':
306
+ if (!isType(convertedValue, 'number')) {
307
+ errors.push(`${name || '值'}必须是数字`);
308
+ }
309
+ if (min !== null && convertedValue < min) {
310
+ errors.push(`${name || '值'}不能小于${min}`);
311
+ }
312
+ if (max !== null && max > 0 && convertedValue > max) {
313
+ errors.push(`${name || '值'}不能大于${max}`);
314
+ }
315
+ if (regex && regex.trim() !== '' && regex !== 'null') {
316
+ try {
317
+ const regExp = new RegExp(regex);
318
+ if (!regExp.test(String(convertedValue))) {
319
+ errors.push(`${name || '值'}格式不正确`);
320
+ }
321
+ } catch {
322
+ errors.push(`${name || '值'}的正则表达式格式错误`);
323
+ }
324
+ }
325
+ break;
326
+
327
+ case 'string':
328
+ case 'text':
329
+ if (!isType(convertedValue, 'string')) {
330
+ errors.push(`${name || '值'}必须是字符串`);
331
+ }
332
+ if (min !== null && convertedValue.length < min) {
333
+ errors.push(`${name || '值'}长度不能少于${min}个字符`);
334
+ }
335
+ if (max !== null && max > 0 && convertedValue.length > max) {
336
+ errors.push(`${name || '值'}长度不能超过${max}个字符`);
337
+ }
338
+ if (regex && regex.trim() !== '' && regex !== 'null') {
339
+ try {
340
+ const regExp = new RegExp(regex);
341
+ if (!regExp.test(convertedValue)) {
342
+ errors.push(`${name || '值'}格式不正确`);
343
+ }
344
+ } catch {
345
+ errors.push(`${name || '值'}的正则表达式格式错误`);
346
+ }
347
+ }
348
+ break;
349
+
350
+ case 'array':
351
+ if (!Array.isArray(convertedValue)) {
352
+ errors.push(`${name || '值'}必须是数组`);
353
+ }
354
+ if (min !== null && convertedValue.length < min) {
355
+ errors.push(`${name || '值'}元素数量不能少于${min}个`);
356
+ }
357
+ if (max !== null && max > 0 && convertedValue.length > max) {
358
+ errors.push(`${name || '值'}元素数量不能超过${max}个`);
359
+ }
360
+ if (regex && regex.trim() !== '' && regex !== 'null') {
361
+ try {
362
+ const regExp = new RegExp(regex);
363
+ for (const item of convertedValue) {
364
+ if (!regExp.test(String(item))) {
365
+ errors.push(`${name || '值'}的元素格式不正确`);
366
+ break;
367
+ }
368
+ }
369
+ } catch {
370
+ errors.push(`${name || '值'}的正则表达式格式错误`);
371
+ }
372
+ }
373
+ break;
374
+ }
375
+
376
+ return {
377
+ valid: errors.length === 0,
378
+ value: errors.length === 0 ? convertedValue : null,
379
+ errors
380
+ };
381
+ }
382
+
383
+ /**
384
+ * 检查验证是否通过
385
+ * @param result - 验证结果
386
+ * @returns 是否通过
387
+ */
388
+ static isPassed(result: ValidationResult | { valid: boolean; value: any; errors: string[] }): boolean {
389
+ // 支持两种结果格式
390
+ if ('valid' in result) {
391
+ return result.valid === true;
392
+ }
393
+ return result.code === 0;
394
+ }
395
+
396
+ /**
397
+ * 检查验证是否失败
398
+ * @param result - 验证结果
399
+ * @returns 是否失败
400
+ */
401
+ static isFailed(result: ValidationResult): boolean {
402
+ return result.code === 1;
403
+ }
404
+
405
+ /**
406
+ * 获取第一个错误信息
407
+ * @param result - 验证结果
408
+ * @returns 错误信息
409
+ */
410
+ static getFirstError(result: ValidationResult | { valid: boolean; value: any; errors: string[] }): string | null {
411
+ // 单值验证结果
412
+ if ('valid' in result) {
413
+ return result.errors.length > 0 ? result.errors[0] : null;
414
+ }
415
+ // 对象验证结果
416
+ if (result.code === 0) return null;
417
+ if (!result.fields) return null;
418
+ const errors = Object.values(result.fields);
419
+ return errors.length > 0 ? errors[0] : null;
420
+ }
421
+
422
+ /**
423
+ * 获取所有错误信息
424
+ * @param result - 验证结果
425
+ * @returns 错误信息数组
426
+ */
427
+ static getAllErrors(result: ValidationResult | { valid: boolean; value: any; errors: string[] }): string[] {
428
+ // 单值验证结果
429
+ if ('valid' in result) {
430
+ return result.errors;
431
+ }
432
+ // 对象验证结果
433
+ if (result.code === 0) return [];
434
+ if (!result.fields) return [];
435
+ return Object.values(result.fields);
436
+ }
437
+
438
+ /**
439
+ * 获取错误字段列表
440
+ * @param result - 验证结果
441
+ * @returns 错误字段名数组
442
+ */
443
+ static getErrorFields(result: ValidationResult): string[] {
444
+ return result.code === 0 ? [] : Object.keys(result.fields);
445
+ }
446
+ }
447
+
448
+ /**
449
+ * 导出验证器实例
450
+ */
451
+ export const validator = new Validator();