befly 3.0.0 → 3.0.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.
Files changed (60) hide show
  1. package/checks/conflict.ts +35 -114
  2. package/checks/table.ts +31 -63
  3. package/config/env.ts +3 -3
  4. package/config/fields.ts +55 -0
  5. package/config/regexAliases.ts +51 -0
  6. package/config/reserved.ts +1 -1
  7. package/main.ts +17 -71
  8. package/package.json +7 -28
  9. package/plugins/db.ts +11 -10
  10. package/plugins/redis.ts +5 -9
  11. package/scripts/syncDb/apply.ts +3 -3
  12. package/scripts/syncDb/constants.ts +2 -1
  13. package/scripts/syncDb/ddl.ts +15 -8
  14. package/scripts/syncDb/helpers.ts +3 -2
  15. package/scripts/syncDb/index.ts +23 -35
  16. package/scripts/syncDb/state.ts +8 -6
  17. package/scripts/syncDb/table.ts +32 -22
  18. package/scripts/syncDb/tableCreate.ts +9 -3
  19. package/scripts/syncDb/tests/constants.test.ts +2 -1
  20. package/scripts/syncDb.ts +10 -9
  21. package/types/addon.d.ts +53 -0
  22. package/types/api.d.ts +17 -14
  23. package/types/befly.d.ts +2 -6
  24. package/types/context.d.ts +7 -0
  25. package/types/database.d.ts +9 -14
  26. package/types/index.d.ts +442 -8
  27. package/types/index.ts +35 -56
  28. package/types/redis.d.ts +2 -0
  29. package/types/validator.d.ts +0 -2
  30. package/types/validator.ts +43 -0
  31. package/utils/colors.ts +117 -37
  32. package/utils/database.ts +348 -0
  33. package/utils/dbHelper.ts +687 -116
  34. package/utils/helper.ts +812 -0
  35. package/utils/index.ts +10 -23
  36. package/utils/logger.ts +78 -171
  37. package/utils/redisHelper.ts +135 -152
  38. package/{types/context.ts → utils/requestContext.ts} +3 -3
  39. package/utils/sqlBuilder.ts +142 -165
  40. package/utils/validate.ts +51 -9
  41. package/apis/health/info.ts +0 -64
  42. package/apis/tool/tokenCheck.ts +0 -51
  43. package/bin/befly.ts +0 -202
  44. package/bunfig.toml +0 -3
  45. package/plugins/tool.ts +0 -34
  46. package/scripts/syncDev.ts +0 -112
  47. package/system.ts +0 -149
  48. package/tables/_common.json +0 -21
  49. package/tables/admin.json +0 -10
  50. package/utils/addonHelper.ts +0 -60
  51. package/utils/api.ts +0 -23
  52. package/utils/datetime.ts +0 -51
  53. package/utils/errorHandler.ts +0 -68
  54. package/utils/objectHelper.ts +0 -68
  55. package/utils/pluginHelper.ts +0 -62
  56. package/utils/response.ts +0 -38
  57. package/utils/sqlHelper.ts +0 -447
  58. package/utils/tableHelper.ts +0 -167
  59. package/utils/tool.ts +0 -230
  60. package/utils/typeHelper.ts +0 -101
@@ -1,10 +1,9 @@
1
1
  /**
2
- * SQL 构建器 - TypeScript 版本
3
- * 提供类型安全的 SQL 查询构建功能
2
+ * SQL 构造器 - TypeScript 版本
3
+ * 提供链式 API 构建 SQL 查询
4
4
  */
5
5
 
6
- import type { OrderDirection, SqlValue } from '../types/common.js';
7
- import type { SqlQuery, WhereOperator, WhereConditions, InsertData, UpdateData } from '../types/database';
6
+ import type { WhereConditions, SqlValue, OrderByField } from '../types/common.js';
8
7
 
9
8
  /**
10
9
  * SQL 构建器类
@@ -113,7 +112,120 @@ export class SqlBuilder {
113
112
  */
114
113
  private _validateParam(value: any): void {
115
114
  if (value === undefined) {
116
- throw new Error('参数值不能为 undefined');
115
+ throw new Error(`参数值不能为 undefined`);
116
+ }
117
+ }
118
+
119
+ /**
120
+ * 处理单个操作符条件
121
+ */
122
+ private _applyOperator(fieldName: string, operator: WhereOperator, value: any): void {
123
+ const escapedField = this._escapeField(fieldName);
124
+
125
+ switch (operator) {
126
+ case '$ne':
127
+ case '$not':
128
+ this._validateParam(value);
129
+ this._where.push(`${escapedField} != ?`);
130
+ this._params.push(value);
131
+ break;
132
+
133
+ case '$in':
134
+ if (!Array.isArray(value)) {
135
+ throw new Error(`$in 操作符的值必须是数组 (operator: ${operator})`);
136
+ }
137
+ if (value.length === 0) {
138
+ throw new Error(`$in 操作符的数组不能为空。提示:空数组会导致查询永远不匹配任何记录,这通常不是预期行为。请检查查询条件或移除该字段。`);
139
+ }
140
+ const placeholders = value.map(() => '?').join(',');
141
+ this._where.push(`${escapedField} IN (${placeholders})`);
142
+ this._params.push(...value);
143
+ break;
144
+
145
+ case '$nin':
146
+ case '$notIn':
147
+ if (!Array.isArray(value)) {
148
+ throw new Error(`$nin/$notIn 操作符的值必须是数组 (operator: ${operator})`);
149
+ }
150
+ if (value.length === 0) {
151
+ throw new Error(`$nin/$notIn 操作符的数组不能为空。提示:空数组会导致查询匹配所有记录,这通常不是预期行为。请检查查询条件或移除该字段。`);
152
+ }
153
+ const placeholders2 = value.map(() => '?').join(',');
154
+ this._where.push(`${escapedField} NOT IN (${placeholders2})`);
155
+ this._params.push(...value);
156
+ break;
157
+
158
+ case '$like':
159
+ this._validateParam(value);
160
+ this._where.push(`${escapedField} LIKE ?`);
161
+ this._params.push(value);
162
+ break;
163
+
164
+ case '$notLike':
165
+ this._validateParam(value);
166
+ this._where.push(`${escapedField} NOT LIKE ?`);
167
+ this._params.push(value);
168
+ break;
169
+
170
+ case '$gt':
171
+ this._validateParam(value);
172
+ this._where.push(`${escapedField} > ?`);
173
+ this._params.push(value);
174
+ break;
175
+
176
+ case '$gte':
177
+ this._validateParam(value);
178
+ this._where.push(`${escapedField} >= ?`);
179
+ this._params.push(value);
180
+ break;
181
+
182
+ case '$lt':
183
+ this._validateParam(value);
184
+ this._where.push(`${escapedField} < ?`);
185
+ this._params.push(value);
186
+ break;
187
+
188
+ case '$lte':
189
+ this._validateParam(value);
190
+ this._where.push(`${escapedField} <= ?`);
191
+ this._params.push(value);
192
+ break;
193
+
194
+ case '$between':
195
+ if (Array.isArray(value) && value.length === 2) {
196
+ this._validateParam(value[0]);
197
+ this._validateParam(value[1]);
198
+ this._where.push(`${escapedField} BETWEEN ? AND ?`);
199
+ this._params.push(value[0], value[1]);
200
+ }
201
+ break;
202
+
203
+ case '$notBetween':
204
+ if (Array.isArray(value) && value.length === 2) {
205
+ this._validateParam(value[0]);
206
+ this._validateParam(value[1]);
207
+ this._where.push(`${escapedField} NOT BETWEEN ? AND ?`);
208
+ this._params.push(value[0], value[1]);
209
+ }
210
+ break;
211
+
212
+ case '$null':
213
+ if (value === true) {
214
+ this._where.push(`${escapedField} IS NULL`);
215
+ }
216
+ break;
217
+
218
+ case '$notNull':
219
+ if (value === true) {
220
+ this._where.push(`${escapedField} IS NOT NULL`);
221
+ }
222
+ break;
223
+
224
+ default:
225
+ // 等于条件
226
+ this._validateParam(value);
227
+ this._where.push(`${escapedField} = ?`);
228
+ this._params.push(value);
117
229
  }
118
230
  }
119
231
 
@@ -158,159 +270,14 @@ export class SqlBuilder {
158
270
  // 一级属性格式:age$gt, role$in 等
159
271
  const lastDollarIndex = key.lastIndexOf('$');
160
272
  const fieldName = key.substring(0, lastDollarIndex);
161
- const escapedFieldName = this._escapeField(fieldName);
162
273
  const operator = ('$' + key.substring(lastDollarIndex + 1)) as WhereOperator;
163
-
164
- switch (operator) {
165
- case '$ne':
166
- case '$not':
167
- this._validateParam(value);
168
- this._where.push(`${escapedFieldName} != ?`);
169
- this._params.push(value);
170
- break;
171
- case '$in':
172
- if (Array.isArray(value) && value.length > 0) {
173
- const placeholders = value.map(() => '?').join(',');
174
- this._where.push(`${escapedFieldName} IN (${placeholders})`);
175
- this._params.push(...value);
176
- }
177
- break;
178
- case '$nin':
179
- case '$notIn':
180
- if (Array.isArray(value) && value.length > 0) {
181
- const placeholders = value.map(() => '?').join(',');
182
- this._where.push(`${escapedFieldName} NOT IN (${placeholders})`);
183
- this._params.push(...value);
184
- }
185
- break;
186
- case '$like':
187
- this._validateParam(value);
188
- this._where.push(`${escapedFieldName} LIKE ?`);
189
- this._params.push(value);
190
- break;
191
- case '$notLike':
192
- this._validateParam(value);
193
- this._where.push(`${escapedFieldName} NOT LIKE ?`);
194
- this._params.push(value);
195
- break;
196
- case '$gt':
197
- this._validateParam(value);
198
- this._where.push(`${escapedFieldName} > ?`);
199
- this._params.push(value);
200
- break;
201
- case '$gte':
202
- this._validateParam(value);
203
- this._where.push(`${escapedFieldName} >= ?`);
204
- this._params.push(value);
205
- break;
206
- case '$lt':
207
- this._validateParam(value);
208
- this._where.push(`${escapedFieldName} < ?`);
209
- this._params.push(value);
210
- break;
211
- case '$lte':
212
- this._validateParam(value);
213
- this._where.push(`${escapedFieldName} <= ?`);
214
- this._params.push(value);
215
- break;
216
- case '$between':
217
- if (Array.isArray(value) && value.length === 2) {
218
- this._where.push(`${escapedFieldName} BETWEEN ? AND ?`);
219
- this._params.push(value[0], value[1]);
220
- }
221
- break;
222
- case '$notBetween':
223
- if (Array.isArray(value) && value.length === 2) {
224
- this._where.push(`${escapedFieldName} NOT BETWEEN ? AND ?`);
225
- this._params.push(value[0], value[1]);
226
- }
227
- break;
228
- case '$null':
229
- if (value === true) {
230
- this._where.push(`${escapedFieldName} IS NULL`);
231
- }
232
- break;
233
- case '$notNull':
234
- if (value === true) {
235
- this._where.push(`${escapedFieldName} IS NOT NULL`);
236
- }
237
- break;
238
- default:
239
- this._validateParam(value);
240
- this._where.push(`${escapedFieldName} = ?`);
241
- this._params.push(value);
242
- }
274
+ this._applyOperator(fieldName, operator, value);
243
275
  } else {
244
276
  // 检查值是否为对象(嵌套条件)
245
277
  if (typeof value === 'object' && value !== null && !Array.isArray(value)) {
246
278
  // 嵌套条件:如 { age: { $gt: 18 } }
247
- const escapedKey = this._escapeField(key);
248
279
  for (const [op, val] of Object.entries(value)) {
249
- switch (op as WhereOperator) {
250
- case '$ne':
251
- case '$not':
252
- this._validateParam(val);
253
- this._where.push(`${escapedKey} != ?`);
254
- this._params.push(val);
255
- break;
256
- case '$gt':
257
- this._validateParam(val);
258
- this._where.push(`${escapedKey} > ?`);
259
- this._params.push(val);
260
- break;
261
- case '$gte':
262
- this._validateParam(val);
263
- this._where.push(`${escapedKey} >= ?`);
264
- this._params.push(val);
265
- break;
266
- case '$lt':
267
- this._validateParam(val);
268
- this._where.push(`${escapedKey} < ?`);
269
- this._params.push(val);
270
- break;
271
- case '$lte':
272
- this._validateParam(val);
273
- this._where.push(`${escapedKey} <= ?`);
274
- this._params.push(val);
275
- break;
276
- case '$like':
277
- this._validateParam(val);
278
- this._where.push(`${escapedKey} LIKE ?`);
279
- this._params.push(val);
280
- break;
281
- case '$notLike':
282
- this._validateParam(val);
283
- this._where.push(`${escapedKey} NOT LIKE ?`);
284
- this._params.push(val);
285
- break;
286
- case '$in':
287
- if (Array.isArray(val) && val.length > 0) {
288
- const placeholders = val.map(() => '?').join(',');
289
- this._where.push(`${escapedKey} IN (${placeholders})`);
290
- this._params.push(...val);
291
- }
292
- break;
293
- case '$nin':
294
- case '$notIn':
295
- if (Array.isArray(val) && val.length > 0) {
296
- const placeholders = val.map(() => '?').join(',');
297
- this._where.push(`${escapedKey} NOT IN (${placeholders})`);
298
- this._params.push(...val);
299
- }
300
- break;
301
- case '$null':
302
- if (val) {
303
- this._where.push(`${escapedKey} IS NULL`);
304
- } else {
305
- this._where.push(`${escapedKey} IS NOT NULL`);
306
- }
307
- break;
308
- default:
309
- // 未知操作符,按等于处理
310
- this._validateParam(val);
311
- this._where.push(`${escapedKey} = ?`);
312
- this._params.push(val);
313
- }
280
+ this._applyOperator(key, op as WhereOperator, val);
314
281
  }
315
282
  } else {
316
283
  // 简单的等于条件
@@ -323,6 +290,16 @@ export class SqlBuilder {
323
290
  });
324
291
  }
325
292
 
293
+ /**
294
+ * 获取 WHERE 条件(供 DbHelper 使用)
295
+ */
296
+ getWhereConditions(): { sql: string; params: SqlValue[] } {
297
+ return {
298
+ sql: this._where.length > 0 ? this._where.join(' AND ') : '',
299
+ params: [...this._params]
300
+ };
301
+ }
302
+
326
303
  /**
327
304
  * SELECT 字段
328
305
  */
@@ -342,7 +319,7 @@ export class SqlBuilder {
342
319
  */
343
320
  from(table: string): this {
344
321
  if (typeof table !== 'string' || !table.trim()) {
345
- throw new Error('FROM 表名必须是非空字符串');
322
+ throw new Error(`FROM 表名必须是非空字符串 (table: ${table})`);
346
323
  }
347
324
  this._from = this._escapeTable(table.trim());
348
325
  return this;
@@ -370,7 +347,7 @@ export class SqlBuilder {
370
347
  */
371
348
  leftJoin(table: string, on: string): this {
372
349
  if (typeof table !== 'string' || typeof on !== 'string') {
373
- throw new Error('JOIN 表名和条件必须是字符串');
350
+ throw new Error(`JOIN 表名和条件必须是字符串 (table: ${table}, on: ${on})`);
374
351
  }
375
352
  const escapedTable = this._escapeTable(table);
376
353
  this._joins.push(`LEFT JOIN ${escapedTable} ON ${on}`);
@@ -388,7 +365,7 @@ export class SqlBuilder {
388
365
 
389
366
  fields.forEach((item) => {
390
367
  if (typeof item !== 'string' || !item.includes('#')) {
391
- throw new Error('orderBy 字段必须是 "字段#方向" 格式的字符串(例如:"name#ASC", "id#DESC")');
368
+ throw new Error(`orderBy 字段必须是 "字段#方向" 格式的字符串(例如:"name#ASC", "id#DESC") (item: ${item})`);
392
369
  }
393
370
 
394
371
  const [fieldName, direction] = item.split('#');
@@ -396,11 +373,11 @@ export class SqlBuilder {
396
373
  const cleanDir = direction.trim().toUpperCase() as OrderDirection;
397
374
 
398
375
  if (!cleanField) {
399
- throw new Error('orderBy 中字段名不能为空');
376
+ throw new Error(`orderBy 中字段名不能为空 (item: ${item})`);
400
377
  }
401
378
 
402
379
  if (!['ASC', 'DESC'].includes(cleanDir)) {
403
- throw new Error('ORDER BY 方向必须是 ASC 或 DESC');
380
+ throw new Error(`ORDER BY 方向必须是 ASC 或 DESC (direction: ${cleanDir})`);
404
381
  }
405
382
 
406
383
  const escapedField = this._escapeField(cleanField);
@@ -438,12 +415,12 @@ export class SqlBuilder {
438
415
  */
439
416
  limit(count: number, offset?: number): this {
440
417
  if (typeof count !== 'number' || count < 0) {
441
- throw new Error('LIMIT 数量必须是非负数');
418
+ throw new Error(`LIMIT 数量必须是非负数 (count: ${count})`);
442
419
  }
443
420
  this._limit = Math.floor(count);
444
421
  if (offset !== undefined && offset !== null) {
445
422
  if (typeof offset !== 'number' || offset < 0) {
446
- throw new Error('OFFSET 必须是非负数');
423
+ throw new Error(`OFFSET 必须是非负数 (offset: ${offset})`);
447
424
  }
448
425
  this._offset = Math.floor(offset);
449
426
  }
@@ -455,7 +432,7 @@ export class SqlBuilder {
455
432
  */
456
433
  offset(count: number): this {
457
434
  if (typeof count !== 'number' || count < 0) {
458
- throw new Error('OFFSET 必须是非负数');
435
+ throw new Error(`OFFSET 必须是非负数 (count: ${count})`);
459
436
  }
460
437
  this._offset = Math.floor(count);
461
438
  return this;
@@ -509,23 +486,23 @@ export class SqlBuilder {
509
486
  */
510
487
  toInsertSql(table: string, data: InsertData): SqlQuery {
511
488
  if (!table || typeof table !== 'string') {
512
- throw new Error('INSERT 需要表名');
489
+ throw new Error(`INSERT 需要表名 (table: ${table})`);
513
490
  }
514
491
 
515
492
  if (!data || typeof data !== 'object') {
516
- throw new Error('INSERT 需要数据');
493
+ throw new Error(`INSERT 需要数据 (table: ${table}, data: ${JSON.stringify(data)})`);
517
494
  }
518
495
 
519
496
  const escapedTable = this._escapeTable(table);
520
497
 
521
498
  if (Array.isArray(data)) {
522
499
  if (data.length === 0) {
523
- throw new Error('插入数据不能为空');
500
+ throw new Error(`插入数据不能为空 (table: ${table})`);
524
501
  }
525
502
 
526
503
  const fields = Object.keys(data[0]);
527
504
  if (fields.length === 0) {
528
- throw new Error('插入数据必须至少有一个字段');
505
+ throw new Error(`插入数据必须至少有一个字段 (table: ${table})`);
529
506
  }
530
507
 
531
508
  const escapedFields = fields.map((field) => this._escapeField(field));
@@ -539,7 +516,7 @@ export class SqlBuilder {
539
516
  } else {
540
517
  const fields = Object.keys(data);
541
518
  if (fields.length === 0) {
542
- throw new Error('插入数据必须至少有一个字段');
519
+ throw new Error(`插入数据必须至少有一个字段 (table: ${table})`);
543
520
  }
544
521
 
545
522
  const escapedFields = fields.map((field) => this._escapeField(field));
package/utils/validate.ts CHANGED
@@ -3,8 +3,9 @@
3
3
  * 提供类型安全的字段验证功能
4
4
  */
5
5
 
6
- import { isType } from './typeHelper.js';
7
- import { parseRule } from './tableHelper.js';
6
+ import { isType } from './helper.js';
7
+ import { parseRule } from './helper.js';
8
+ import { RegexAliases } from '../config/regexAliases.js';
8
9
  import type { TableDefinition, FieldRule, ParsedFieldRule } from '../types/common.js';
9
10
  import type { ValidationResult, ValidationError } from '../types/validator';
10
11
 
@@ -104,12 +105,42 @@ export class Validator {
104
105
  }
105
106
  }
106
107
 
108
+ /**
109
+ * 解析 regex 别名
110
+ * 如果 regex 以 @ 开头,则从别名表中获取实际正则表达式
111
+ * @param regex - 原始 regex 或别名(如 @number)
112
+ * @returns 实际的正则表达式字符串
113
+ */
114
+ private resolveRegexAlias(regex: string | null): string | null {
115
+ if (!regex || typeof regex !== 'string') {
116
+ return regex;
117
+ }
118
+
119
+ // 检查是否是别名(以 @ 开头)
120
+ if (regex.startsWith('@')) {
121
+ const aliasName = regex.substring(1);
122
+ const resolvedRegex = RegexAliases[aliasName as keyof typeof RegexAliases];
123
+
124
+ if (resolvedRegex) {
125
+ return resolvedRegex;
126
+ }
127
+
128
+ // 别名不存在,返回原值(让后续验证报错)
129
+ return regex;
130
+ }
131
+
132
+ return regex;
133
+ }
134
+
107
135
  /**
108
136
  * 验证单个字段的值
109
137
  */
110
138
  private validateFieldValue(value: any, rule: FieldRule, fieldName: string): ValidationError {
111
139
  const parsed = parseRule(rule);
112
- const { name, type, min, max, regex } = parsed;
140
+ let { name, type, min, max, regex } = parsed;
141
+
142
+ // 解析 regex 别名
143
+ regex = this.resolveRegexAlias(regex);
113
144
 
114
145
  switch (type.toLowerCase()) {
115
146
  case 'number':
@@ -118,8 +149,9 @@ export class Validator {
118
149
  return this.validateString(value, name, min, max, regex, fieldName);
119
150
  case 'text':
120
151
  return this.validateString(value, name, min, max, regex, fieldName);
121
- case 'array':
122
- return this.validateArray(value, label, min, max, regex, fieldName);
152
+ case 'array_string':
153
+ case 'array_text':
154
+ return this.validateArray(value, name, min, max, regex, fieldName);
123
155
  default:
124
156
  return `字段 ${fieldName} 的类型 ${type} 不支持`;
125
157
  }
@@ -255,13 +287,22 @@ export class Validator {
255
287
  */
256
288
  static validateSingleValue(value: any, rule: string): { valid: boolean; value: any; errors: string[] } {
257
289
  const parsed = parseRule(rule);
258
- const { name, type, min, max, regex, default: defaultValue } = parsed;
290
+ let { name, type, min, max, regex, default: defaultValue } = parsed;
291
+
292
+ // 解析 regex 别名
293
+ if (regex && typeof regex === 'string' && regex.startsWith('@')) {
294
+ const aliasName = regex.substring(1);
295
+ const resolvedRegex = RegexAliases[aliasName as keyof typeof RegexAliases];
296
+ if (resolvedRegex) {
297
+ regex = resolvedRegex;
298
+ }
299
+ }
259
300
 
260
301
  // 处理 undefined/null 值,使用默认值
261
302
  if (value === undefined || value === null) {
262
303
  if (defaultValue !== 'null' && defaultValue !== null) {
263
304
  // 特殊处理数组类型的默认值字符串
264
- if (type === 'array' && typeof defaultValue === 'string') {
305
+ if ((type === 'array_string' || type === 'array_text') && typeof defaultValue === 'string') {
265
306
  if (defaultValue === '[]') {
266
307
  return { valid: true, value: [], errors: [] };
267
308
  }
@@ -281,7 +322,7 @@ export class Validator {
281
322
  // 如果没有默认值,根据类型返回默认值
282
323
  if (type === 'number') {
283
324
  return { valid: true, value: 0, errors: [] };
284
- } else if (type === 'array') {
325
+ } else if (type === 'array_string' || type === 'array_text') {
285
326
  return { valid: true, value: [], errors: [] };
286
327
  } else if (type === 'string' || type === 'text') {
287
328
  return { valid: true, value: '', errors: [] };
@@ -347,7 +388,8 @@ export class Validator {
347
388
  }
348
389
  break;
349
390
 
350
- case 'array':
391
+ case 'array_string':
392
+ case 'array_text':
351
393
  if (!Array.isArray(convertedValue)) {
352
394
  errors.push(`${name || '值'}必须是数组`);
353
395
  }
@@ -1,64 +0,0 @@
1
- /**
2
- * 健康检查接口 - TypeScript 版本
3
- * 检查服务器、Redis、数据库等状态
4
- */
5
-
6
- import { Env } from '../../config/env.js';
7
- import { Api } from '../../utils/api.js';
8
- import { Yes } from '../../utils/index.js';
9
- import type { BeflyContext } from '../../types/befly.js';
10
- import type { HealthInfo } from '../../types/api.js';
11
-
12
- export default Api('健康检查', {
13
- fields: {},
14
- required: [],
15
- handler: async (befly: BeflyContext, ctx: any) => {
16
- const info: HealthInfo = {
17
- status: 'ok',
18
- timestamp: new Date().toISOString(),
19
- uptime: process.uptime(),
20
- memory: process.memoryUsage(),
21
- runtime: 'Bun',
22
- version: Bun.version,
23
- platform: process.platform,
24
- arch: process.arch
25
- };
26
-
27
- // 检查 Redis 连接状态
28
- if (Env.REDIS_ENABLE === 1) {
29
- if (befly.redis) {
30
- try {
31
- await befly.redis.getRedisClient().ping();
32
- info.redis = '已连接';
33
- } catch (error: any) {
34
- info.redis = '未连接';
35
- info.redisError = error.message;
36
- }
37
- } else {
38
- info.redis = '未开启';
39
- }
40
- } else {
41
- info.redis = '禁用';
42
- }
43
-
44
- // 检查数据库连接状态
45
- if (Env.DB_ENABLE === 1) {
46
- if (befly.db) {
47
- try {
48
- // 执行简单查询测试连接
49
- await befly.db.query('SELECT 1');
50
- info.database = '已连接';
51
- } catch (error: any) {
52
- info.database = '未连接';
53
- info.databaseError = error.message;
54
- }
55
- } else {
56
- info.database = '未开启';
57
- }
58
- } else {
59
- info.database = '禁用';
60
- }
61
-
62
- return Yes('健康检查成功', info);
63
- }
64
- });
@@ -1,51 +0,0 @@
1
- /**
2
- * 令牌检测接口 - TypeScript 版本
3
- * 验证 JWT 令牌是否有效
4
- */
5
-
6
- import { Env } from '../../config/env.js';
7
- import { Api } from '../../utils/api.js';
8
- import { Yes, No } from '../../utils/index.js';
9
- import { Jwt } from '../../utils/jwt.js';
10
- import type { BeflyContext } from '../../types/befly.js';
11
- import type { JwtPayload } from '../../utils/jwt.js';
12
- import type { TokenCheckData } from '../../types/api.js';
13
-
14
- export default Api('令牌检测', {
15
- fields: {},
16
- required: [],
17
- handler: async (befly: BeflyContext, ctx: any) => {
18
- // 从 Authorization 头获取 token
19
- const authHeader = ctx.req?.headers?.get('authorization') || '';
20
- const token = authHeader.split(' ')[1] || '';
21
-
22
- if (!token) {
23
- return No('令牌不能为空');
24
- }
25
-
26
- try {
27
- // 验证令牌
28
- const jwtData = await Jwt.verify(token);
29
-
30
- // 计算剩余有效期
31
- const expiresIn = jwtData.exp ? jwtData.exp - Math.floor(Date.now() / 1000) : undefined;
32
-
33
- const data: TokenCheckData = {
34
- valid: true,
35
- payload: jwtData,
36
- expiresIn
37
- };
38
-
39
- return Yes('令牌有效', data);
40
- } catch (error: any) {
41
- // 针对预期的令牌错误,返回友好提示(非致命错误)
42
- if (error.message.includes('expired')) {
43
- return No('令牌已过期', { expired: true });
44
- } else if (error.message.includes('invalid')) {
45
- return No('令牌无效', { invalid: true });
46
- }
47
- // 其他未知错误向上抛出,由路由层统一处理
48
- throw error;
49
- }
50
- }
51
- });