befly 3.8.29 → 3.8.30

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 (52) hide show
  1. package/README.md +8 -6
  2. package/checks/checkApi.ts +2 -1
  3. package/checks/checkTable.ts +3 -2
  4. package/hooks/parser.ts +5 -3
  5. package/hooks/permission.ts +12 -5
  6. package/lib/cacheHelper.ts +76 -62
  7. package/lib/connect.ts +8 -35
  8. package/lib/dbHelper.ts +14 -11
  9. package/lib/jwt.ts +58 -437
  10. package/lib/logger.ts +76 -197
  11. package/lib/redisHelper.ts +163 -1
  12. package/lib/sqlBuilder.ts +2 -1
  13. package/lib/validator.ts +9 -8
  14. package/loader/loadApis.ts +4 -7
  15. package/loader/loadHooks.ts +2 -2
  16. package/loader/loadPlugins.ts +4 -4
  17. package/main.ts +4 -17
  18. package/package.json +9 -8
  19. package/paths.ts +0 -6
  20. package/plugins/db.ts +2 -2
  21. package/plugins/jwt.ts +5 -5
  22. package/plugins/redis.ts +1 -1
  23. package/router/api.ts +2 -2
  24. package/router/static.ts +1 -2
  25. package/sync/syncAll.ts +2 -2
  26. package/sync/syncApi.ts +10 -7
  27. package/sync/syncDb/apply.ts +1 -2
  28. package/sync/syncDb.ts +6 -10
  29. package/sync/syncDev.ts +10 -48
  30. package/sync/syncMenu.ts +11 -8
  31. package/tests/cacheHelper.test.ts +327 -0
  32. package/tests/dbHelper-columns.test.ts +5 -20
  33. package/tests/dbHelper-execute.test.ts +14 -68
  34. package/tests/fields-redis-cache.test.ts +5 -3
  35. package/tests/integration.test.ts +15 -26
  36. package/tests/jwt.test.ts +36 -94
  37. package/tests/logger.test.ts +32 -34
  38. package/tests/redisHelper.test.ts +270 -0
  39. package/tests/redisKeys.test.ts +76 -0
  40. package/tests/sync-connection.test.ts +0 -6
  41. package/tests/syncDb-constants.test.ts +12 -12
  42. package/tests/util.test.ts +5 -1
  43. package/types/befly.d.ts +2 -15
  44. package/types/common.d.ts +11 -93
  45. package/types/database.d.ts +216 -5
  46. package/types/index.ts +1 -0
  47. package/types/logger.d.ts +11 -41
  48. package/types/table.d.ts +213 -0
  49. package/hooks/_rateLimit.ts +0 -64
  50. package/lib/regexAliases.ts +0 -59
  51. package/lib/xml.ts +0 -383
  52. package/tests/xml.test.ts +0 -101
package/lib/logger.ts CHANGED
@@ -1,216 +1,95 @@
1
1
  /**
2
- * 日志系统 - Befly 项目专用
3
- * 直接集成环境变量,提供开箱即用的日志功能
2
+ * 日志系统 - 基于 pino 实现
4
3
  */
5
4
 
5
+ import pino from 'pino';
6
6
  import { join } from 'pathe';
7
- import { appendFile, stat } from 'node:fs/promises';
8
- import type { LogLevel } from '../types/common.js';
9
- import type { LoggerConfig } from '../types/befly.js';
7
+
8
+ import type { LoggerConfig } from '../types/logger.js';
9
+
10
+ let instance: pino.Logger | null = null;
11
+ let mockInstance: pino.Logger | null = null;
12
+ let config: LoggerConfig = {
13
+ debug: 0,
14
+ dir: './logs',
15
+ console: 1,
16
+ maxSize: 10
17
+ };
10
18
 
11
19
  /**
12
- * 日志消息类型
20
+ * 配置日志
13
21
  */
14
- type LogMessage = string | number | boolean | null | undefined | Record<string, any> | any[];
22
+ export function configure(cfg: LoggerConfig): void {
23
+ config = { ...config, ...cfg };
24
+ instance = null;
25
+ }
15
26
 
16
27
  /**
17
- * 格式化日期时间
28
+ * 设置 Mock Logger(用于测试)
29
+ * @param mock - Mock pino 实例,传 null 清除 mock
18
30
  */
19
- function formatDate(): string {
20
- const now = new Date();
21
- const year = now.getFullYear();
22
- const month = String(now.getMonth() + 1).padStart(2, '0');
23
- const day = String(now.getDate()).padStart(2, '0');
24
- const hours = String(now.getHours()).padStart(2, '0');
25
- const minutes = String(now.getMinutes()).padStart(2, '0');
26
- const seconds = String(now.getSeconds()).padStart(2, '0');
27
- return `${year}-${month}-${day} ${hours}:${minutes}:${seconds}`;
31
+ export function setMockLogger(mock: pino.Logger | null): void {
32
+ mockInstance = mock;
28
33
  }
29
34
 
30
35
  /**
31
- * 日志器类
36
+ * 获取 pino 日志实例
32
37
  */
33
- export class Logger {
34
- /** 当前使用的日志文件缓存 */
35
- private static currentFiles: Map<string, string> = new Map();
36
-
37
- /** 日志配置 */
38
- private static config: LoggerConfig = {
39
- debug: 1,
40
- excludeFields: 'password,token,secret',
41
- dir: './logs',
42
- console: 1,
43
- maxSize: 10 * 1024 * 1024
44
- };
45
-
46
- /**
47
- * 配置日志器
48
- * @param config - 日志配置
49
- */
50
- static configure(config: LoggerConfig) {
51
- this.config = { ...this.config, ...config };
52
- }
53
-
54
- /**
55
- * 记录日志
56
- * @param level - 日志级别
57
- * @param message - 日志消息
58
- */
59
- static async log(level: LogLevel, message: LogMessage): Promise<void> {
60
- // debug 日志特殊处理:仅当 LOG_DEBUG=1 时才记录
61
- if (level === 'debug' && this.config.debug !== 1) return;
62
-
63
- // 格式化消息
64
- const timestamp = formatDate();
65
-
66
- // 处理消息内容
67
- let content = '';
68
- if (typeof message === 'object' && message !== null && Object.keys(message).length > 0) {
69
- content = JSON.stringify(message, null, 0).replace(/\\"/g, '"');
70
- } else {
71
- content = String(message);
38
+ export function getLogger(): pino.Logger {
39
+ // 优先返回 mock 实例(用于测试)
40
+ if (mockInstance) return mockInstance;
41
+
42
+ if (instance) return instance;
43
+
44
+ const level = config.debug === 1 ? 'debug' : 'info';
45
+ const targets: pino.TransportTargetOptions[] = [];
46
+
47
+ // 文件输出
48
+ targets.push({
49
+ target: 'pino-roll',
50
+ level: level,
51
+ options: {
52
+ file: join(config.dir || './logs', 'app'),
53
+ frequency: 'daily',
54
+ size: `${config.maxSize || 10}m`,
55
+ mkdir: true,
56
+ dateFormat: 'yyyy-MM-dd'
72
57
  }
73
-
74
- // 格式化日志消息
75
- const levelStr = level.toUpperCase().padStart(5);
76
- const logMessage = `[${timestamp}] ${levelStr} - ${content}`;
77
-
78
- // 控制台输出
79
- if (this.config.console === 1) {
80
- console.log(logMessage);
81
- }
82
-
83
- // 文件输出
84
- await this.writeToFile(logMessage, level);
58
+ });
59
+
60
+ // 控制台输出
61
+ if (config.console === 1) {
62
+ targets.push({
63
+ target: 'pino/file',
64
+ level: level,
65
+ options: { destination: 1 }
66
+ });
85
67
  }
86
68
 
87
- /**
88
- * 记录成功日志(使用 info 级别)
89
- * @param message - 日志消息
90
- */
91
- static async success(message: LogMessage): Promise<void> {
92
- await this.log('info', message);
93
- }
94
-
95
- /**
96
- * 写入日志文件
97
- * @param message - 格式化后的消息
98
- * @param level - 日志级别
99
- */
100
- static async writeToFile(message: string, level: LogLevel = 'info'): Promise<void> {
101
- try {
102
- const logDir = this.config.dir || './logs';
103
- // 确定文件前缀
104
- const prefix = level === 'debug' ? 'debug' : new Date().toISOString().split('T')[0];
105
-
106
- // 检查缓存的当前文件是否仍然可用
107
- let currentLogFile = this.currentFiles.get(prefix);
108
-
109
- if (currentLogFile) {
110
- try {
111
- const stats = await stat(currentLogFile);
112
- if (stats.size >= (this.config.maxSize || 10 * 1024 * 1024)) {
113
- this.currentFiles.delete(prefix);
114
- currentLogFile = undefined;
115
- }
116
- } catch {
117
- this.currentFiles.delete(prefix);
118
- currentLogFile = undefined;
119
- }
120
- }
121
-
122
- // 查找或创建新文件
123
- if (!currentLogFile) {
124
- const glob = new Bun.Glob(`${prefix}.*.log`);
125
- const files = await Array.fromAsync(glob.scan(this.config.dir || 'logs'));
126
-
127
- // 按索引排序并查找可用文件
128
- const getIndex = (f: string) => parseInt(f.match(/\.(\d+)\.log$/)?.[1] || '0');
129
- files.sort((a, b) => getIndex(a) - getIndex(b));
130
-
131
- let foundFile = false;
132
- for (let i = files.length - 1; i >= 0; i--) {
133
- const filePath = join(this.config.dir || 'logs', files[i]);
134
- try {
135
- const stats = await stat(filePath);
136
- // 检查文件大小
137
- if (stats.size < (this.config.maxSize || 10 * 1024 * 1024)) {
138
- currentLogFile = filePath;
139
- foundFile = true;
140
- break;
141
- }
142
- } catch {
143
- continue;
144
- }
145
- }
146
-
147
- // 没有可用文件,创建新文件
148
- if (!foundFile) {
149
- const maxIndex = files.length > 0 ? Math.max(...files.map(getIndex)) : -1;
150
- currentLogFile = join(this.config.dir || 'logs', `${prefix}.${maxIndex + 1}.log`);
151
- }
69
+ instance = pino({
70
+ level: level,
71
+ transport: { targets: targets }
72
+ });
152
73
 
153
- this.currentFiles.set(prefix, currentLogFile);
154
- }
155
-
156
- await appendFile(currentLogFile, message + '\n', 'utf8');
157
- } catch (error: any) {
158
- console.error('写入日志文件失败:', error?.message || error);
159
- }
160
- }
161
-
162
- /**
163
- * 记录错误日志
164
- * @param name - 错误名称/位置
165
- * @param error - 错误对象或消息
166
- */
167
- static async error(name: string, error?: any): Promise<void> {
168
- if (!error) {
169
- return this.log('error', name);
170
- }
171
-
172
- // 构建错误消息
173
- const parts = [name];
174
- if (error?.message || error?.stack) {
175
- if (error.message) parts.push(error.message);
176
- if (error.stack) parts.push('\n' + error.stack);
177
- } else {
178
- const errorStr = typeof error === 'object' ? JSON.stringify(error) : String(error);
179
- parts.push(errorStr);
180
- }
181
-
182
- await this.log('error', parts.join(' - '));
183
- }
184
-
185
- /**
186
- * 记录警告日志
187
- * @param message - 日志消息
188
- */
189
- static async warn(message: LogMessage): Promise<void> {
190
- await this.log('warn', message);
191
- }
192
-
193
- /**
194
- * 记录信息日志
195
- * @param message - 日志消息
196
- */
197
- static async info(message: LogMessage): Promise<void> {
198
- await this.log('info', message);
199
- }
200
-
201
- /**
202
- * 记录调试日志
203
- * 受 enableDebug 配置控制,仅当 enableDebug=true 时才记录
204
- * @param message - 日志消息
205
- */
206
- static async debug(message: LogMessage): Promise<void> {
207
- await this.log('debug', message);
208
- }
209
-
210
- /**
211
- * 清除文件缓存
212
- */
213
- static clearCache(): void {
214
- this.currentFiles.clear();
215
- }
74
+ return instance;
216
75
  }
76
+
77
+ /**
78
+ * 日志实例(延迟初始化)
79
+ */
80
+ export const Logger = {
81
+ get info() {
82
+ return getLogger().info.bind(getLogger());
83
+ },
84
+ get warn() {
85
+ return getLogger().warn.bind(getLogger());
86
+ },
87
+ get error() {
88
+ return getLogger().error.bind(getLogger());
89
+ },
90
+ get debug() {
91
+ return getLogger().debug.bind(getLogger());
92
+ },
93
+ configure: configure,
94
+ setMock: setMockLogger
95
+ };
@@ -6,7 +6,7 @@
6
6
  import { SQL, RedisClient } from 'bun';
7
7
  import { Logger } from './logger.js';
8
8
  import { Connect } from './connect.js';
9
- import type { KeyValue } from '../types/common.js';
9
+ import type { KeyValue } from 'befly-shared/types';
10
10
 
11
11
  /**
12
12
  * Redis 助手类
@@ -79,6 +79,11 @@ export class RedisHelper {
79
79
  }
80
80
  }
81
81
 
82
+ // ==================== ID 生成 ====================
83
+ // 注意:ID 生成功能强依赖 Redis 原子操作(INCR/INCRBY)保证分布式唯一性
84
+ // 主要被 DbHelper.insData/insBatch 使用
85
+ // 如未来有其他 ID 生成需求,可考虑提取到独立模块
86
+
82
87
  /**
83
88
  * 生成基于时间的唯一 ID
84
89
  * 格式: 秒级时间戳(10位) + 4位自增 = 14位纯数字
@@ -209,6 +214,25 @@ export class RedisHelper {
209
214
  }
210
215
  }
211
216
 
217
+ /**
218
+ * 批量获取剩余过期时间(利用 Bun Redis 自动管道优化)
219
+ * @param keys - 键名数组
220
+ * @returns TTL 数组(-2 表示键不存在,-1 表示无过期时间)
221
+ */
222
+ async ttlBatch(keys: string[]): Promise<number[]> {
223
+ if (keys.length === 0) {
224
+ return [];
225
+ }
226
+
227
+ try {
228
+ const results = await Promise.all(keys.map((key) => this.ttl(key)));
229
+ return results;
230
+ } catch (error: any) {
231
+ Logger.error('Redis ttlBatch 错误', error);
232
+ return keys.map(() => -1);
233
+ }
234
+ }
235
+
212
236
  /**
213
237
  * 向 Set 中添加一个或多个成员
214
238
  * @param key - 键名
@@ -273,6 +297,44 @@ export class RedisHelper {
273
297
  }
274
298
  }
275
299
 
300
+ /**
301
+ * 批量向多个 Set 添加成员(利用 Bun Redis 自动管道优化)
302
+ * @param items - [{ key, members }] 数组
303
+ * @returns 成功添加的总成员数量
304
+ */
305
+ async saddBatch(items: Array<{ key: string; members: string[] }>): Promise<number> {
306
+ if (items.length === 0) {
307
+ return 0;
308
+ }
309
+
310
+ try {
311
+ const results = await Promise.all(items.map((item) => this.sadd(item.key, item.members)));
312
+ return results.reduce((sum, count) => sum + count, 0);
313
+ } catch (error: any) {
314
+ Logger.error('Redis saddBatch 错误', error);
315
+ return 0;
316
+ }
317
+ }
318
+
319
+ /**
320
+ * 批量检查成员是否在 Set 中(利用 Bun Redis 自动管道优化)
321
+ * @param items - [{ key, member }] 数组
322
+ * @returns 布尔数组(true 表示存在,false 表示不存在)
323
+ */
324
+ async sismemberBatch(items: Array<{ key: string; member: string }>): Promise<boolean[]> {
325
+ if (items.length === 0) {
326
+ return [];
327
+ }
328
+
329
+ try {
330
+ const results = await Promise.all(items.map((item) => this.sismember(item.key, item.member)));
331
+ return results.map((r) => r > 0);
332
+ } catch (error: any) {
333
+ Logger.error('Redis sismemberBatch 错误', error);
334
+ return items.map(() => false);
335
+ }
336
+ }
337
+
276
338
  /**
277
339
  * 删除键
278
340
  * @param key - 键名
@@ -288,6 +350,106 @@ export class RedisHelper {
288
350
  }
289
351
  }
290
352
 
353
+ /**
354
+ * 批量删除键(利用 Bun Redis 自动管道优化)
355
+ * @param keys - 键名数组
356
+ * @returns 成功删除的键数量
357
+ */
358
+ async delBatch(keys: string[]): Promise<number> {
359
+ if (keys.length === 0) {
360
+ return 0;
361
+ }
362
+
363
+ try {
364
+ const results = await Promise.all(
365
+ keys.map((key) => {
366
+ const pkey = `${this.prefix}${key}`;
367
+ return this.client.del(pkey);
368
+ })
369
+ );
370
+ return results.reduce((sum, count) => sum + count, 0);
371
+ } catch (error: any) {
372
+ Logger.error('Redis delBatch 错误', error);
373
+ return 0;
374
+ }
375
+ }
376
+
377
+ /**
378
+ * 批量设置对象(利用 Bun Redis 自动管道优化)
379
+ * @param items - 键值对数组 [{ key, value, ttl? }]
380
+ * @returns 成功设置的数量
381
+ */
382
+ async setBatch<T = any>(items: Array<{ key: string; value: T; ttl?: number | null }>): Promise<number> {
383
+ if (items.length === 0) {
384
+ return 0;
385
+ }
386
+
387
+ try {
388
+ const results = await Promise.all(items.map((item) => this.setObject(item.key, item.value, item.ttl ?? null)));
389
+ return results.filter((r) => r !== null).length;
390
+ } catch (error: any) {
391
+ Logger.error('Redis setBatch 错误', error);
392
+ return 0;
393
+ }
394
+ }
395
+
396
+ /**
397
+ * 批量获取对象(利用 Bun Redis 自动管道优化)
398
+ * @param keys - 键名数组
399
+ * @returns 对象数组(不存在的键返回 null)
400
+ */
401
+ async getBatch<T = any>(keys: string[]): Promise<Array<T | null>> {
402
+ if (keys.length === 0) {
403
+ return [];
404
+ }
405
+
406
+ try {
407
+ const results = await Promise.all(keys.map((key) => this.getObject<T>(key)));
408
+ return results;
409
+ } catch (error: any) {
410
+ Logger.error('Redis getBatch 错误', error);
411
+ return keys.map(() => null);
412
+ }
413
+ }
414
+
415
+ /**
416
+ * 批量检查键是否存在(利用 Bun Redis 自动管道优化)
417
+ * @param keys - 键名数组
418
+ * @returns 布尔数组(true 表示存在,false 表示不存在)
419
+ */
420
+ async existsBatch(keys: string[]): Promise<boolean[]> {
421
+ if (keys.length === 0) {
422
+ return [];
423
+ }
424
+
425
+ try {
426
+ const results = await Promise.all(keys.map((key) => this.exists(key)));
427
+ return results.map((r) => r > 0);
428
+ } catch (error: any) {
429
+ Logger.error('Redis existsBatch 错误', error);
430
+ return keys.map(() => false);
431
+ }
432
+ }
433
+
434
+ /**
435
+ * 批量设置过期时间(利用 Bun Redis 自动管道优化)
436
+ * @param items - 键名和过期时间数组 [{ key, seconds }]
437
+ * @returns 成功设置的数量
438
+ */
439
+ async expireBatch(items: Array<{ key: string; seconds: number }>): Promise<number> {
440
+ if (items.length === 0) {
441
+ return 0;
442
+ }
443
+
444
+ try {
445
+ const results = await Promise.all(items.map((item) => this.expire(item.key, item.seconds)));
446
+ return results.filter((r) => r > 0).length;
447
+ } catch (error: any) {
448
+ Logger.error('Redis expireBatch 错误', error);
449
+ return 0;
450
+ }
451
+ }
452
+
291
453
  /**
292
454
  * 测试 Redis 连接
293
455
  * @returns ping 响应结果
package/lib/sqlBuilder.ts CHANGED
@@ -3,7 +3,8 @@
3
3
  * 提供链式 API 构建 SQL 查询
4
4
  */
5
5
 
6
- import type { WhereConditions, SqlValue, OrderByField } from '../types/common.js';
6
+ import type { WhereConditions, OrderByField } from '../types/common.js';
7
+ import type { SqlValue } from 'befly-shared/types';
7
8
 
8
9
  /**
9
10
  * SQL 构建器类
package/lib/validator.ts CHANGED
@@ -1,11 +1,12 @@
1
1
  /**
2
2
  * 数据验证器 - Befly 项目专用
3
3
  * 内置 RegexAliases,支持对象格式的字段定义
4
+ * 使用正则缓存优化性能
4
5
  */
5
6
 
6
- import type { TableDefinition, FieldDefinition } from '../types/common.js';
7
7
  import type { ValidationResult, ValidationError } from '../types/validator';
8
- import { RegexAliases } from './regexAliases.js';
8
+ import { RegexAliases, getCompiledRegex } from 'befly-shared/regex';
9
+ import type { TableDefinition, FieldDefinition } from 'befly-shared/types';
9
10
 
10
11
  /**
11
12
  * 验证器类(Befly 项目专用)
@@ -175,7 +176,7 @@ export class Validator {
175
176
 
176
177
  if (spec && spec.trim() !== '') {
177
178
  try {
178
- const regExp = new RegExp(spec);
179
+ const regExp = getCompiledRegex(spec);
179
180
  if (!regExp.test(String(numValue))) {
180
181
  return `${name}(${fieldName})格式不正确`;
181
182
  }
@@ -209,7 +210,7 @@ export class Validator {
209
210
 
210
211
  if (spec && spec.trim() !== '') {
211
212
  try {
212
- const regExp = new RegExp(spec);
213
+ const regExp = getCompiledRegex(spec);
213
214
  if (!regExp.test(value)) {
214
215
  return `${name}(${fieldName})格式不正确`;
215
216
  }
@@ -243,7 +244,7 @@ export class Validator {
243
244
 
244
245
  if (spec && spec.trim() !== '') {
245
246
  try {
246
- const regExp = new RegExp(spec);
247
+ const regExp = getCompiledRegex(spec);
247
248
  for (const item of value) {
248
249
  if (!regExp.test(String(item))) {
249
250
  return `${name}(${fieldName})中的元素"${item}"格式不正确`;
@@ -328,7 +329,7 @@ export class Validator {
328
329
  }
329
330
  if (regexp && regexp.trim() !== '') {
330
331
  try {
331
- const regExp = new RegExp(regexp);
332
+ const regExp = getCompiledRegex(regexp);
332
333
  if (!regExp.test(String(convertedValue))) {
333
334
  errors.push(`${name || '值'}格式不正确`);
334
335
  }
@@ -351,7 +352,7 @@ export class Validator {
351
352
  }
352
353
  if (regexp && regexp.trim() !== '') {
353
354
  try {
354
- const regExp = new RegExp(regexp);
355
+ const regExp = getCompiledRegex(regexp);
355
356
  if (!regExp.test(convertedValue)) {
356
357
  errors.push(`${name || '值'}格式不正确`);
357
358
  }
@@ -374,7 +375,7 @@ export class Validator {
374
375
  }
375
376
  if (regexp && regexp.trim() !== '') {
376
377
  try {
377
- const regExp = new RegExp(regexp);
378
+ const regExp = getCompiledRegex(regexp);
378
379
  for (const item of convertedValue) {
379
380
  if (!regExp.test(String(item))) {
380
381
  errors.push(`${name || '值'}的元素格式不正确`);
@@ -9,7 +9,8 @@ import { existsSync } from 'node:fs';
9
9
  // 外部依赖
10
10
  import { relative, basename, join } from 'pathe';
11
11
  import { isPlainObject } from 'es-toolkit/compat';
12
- import { calcPerfTime, scanFiles, scanAddons, getAddonDir, addonDirExists } from 'befly-util';
12
+ import { scanFiles } from 'befly-shared/scanFiles';
13
+ import { scanAddons, getAddonDir, addonDirExists } from 'befly-shared/addonHelper';
13
14
 
14
15
  // 相对导入
15
16
  import { Logger } from '../lib/logger.js';
@@ -62,8 +63,6 @@ const DEFAULT_API_FIELDS = {
62
63
  */
63
64
  export async function loadApis(apis: Map<string, ApiRoute>): Promise<void> {
64
65
  try {
65
- const loadStartTime = Bun.nanoseconds();
66
-
67
66
  // 1. 扫描项目 API
68
67
  const projectApiFiles = await scanFiles(projectApiDir);
69
68
  const projectApiList = projectApiFiles.map((file) => ({
@@ -122,14 +121,12 @@ export async function loadApis(apis: Map<string, ApiRoute>): Promise<void> {
122
121
  api.route = `${api.method.toUpperCase()}/api/${apiFile.routePrefix ? apiFile.routePrefix + '/' : ''}${apiFile.relativePath}`;
123
122
  apis.set(api.route, api);
124
123
  } catch (error: any) {
125
- Logger.error(`[${apiFile.typeName}] 接口 ${apiFile.relativePath} 加载失败`, error);
124
+ Logger.error({ err: error, api: apiFile.relativePath, type: apiFile.typeName }, '接口加载失败');
126
125
  process.exit(1);
127
126
  }
128
127
  }
129
-
130
- const totalLoadTime = calcPerfTime(loadStartTime);
131
128
  } catch (error: any) {
132
- Logger.error('加载 API 时发生错误', error);
129
+ Logger.error({ err: error }, '加载 API 时发生错误');
133
130
  process.exit(1);
134
131
  }
135
132
  }
@@ -21,7 +21,7 @@ export async function loadHooks(pluginsConfig: Record<string, any> | undefined,
21
21
  const enabledHooks = coreHooks.filter((hook) => !disableHooks.includes(hook.name));
22
22
 
23
23
  if (disableHooks.length > 0) {
24
- Logger.info(`禁用钩子: ${disableHooks.join(', ')}`);
24
+ Logger.info({ hooks: disableHooks }, '禁用钩子');
25
25
  }
26
26
 
27
27
  // 3. 按 order 排序
@@ -33,7 +33,7 @@ export async function loadHooks(pluginsConfig: Record<string, any> | undefined,
33
33
 
34
34
  hooks.push(...sortedHooks);
35
35
  } catch (error: any) {
36
- Logger.error('加载钩子时发生错误', error);
36
+ Logger.error({ err: error }, '加载钩子时发生错误');
37
37
  process.exit(1);
38
38
  }
39
39
  }
@@ -3,7 +3,7 @@
3
3
  * 负责扫描和初始化所有插件(核心、组件、项目)
4
4
  */
5
5
 
6
- import { scanAddons, getAddonDir } from 'befly-util';
6
+ import { scanAddons, getAddonDir } from 'befly-shared/addonHelper';
7
7
 
8
8
  import { Logger } from '../lib/logger.js';
9
9
  import { corePluginDir, projectPluginDir } from '../paths.js';
@@ -41,7 +41,7 @@ export async function loadPlugins(config: Record<string, any> | undefined, plugi
41
41
  const enabledPlugins = allPlugins.filter((plugin) => !disablePlugins.includes(plugin.name));
42
42
 
43
43
  if (disablePlugins.length > 0) {
44
- Logger.info(`禁用插件: ${disablePlugins.join(', ')}`);
44
+ Logger.info({ plugins: disablePlugins }, '禁用插件');
45
45
  }
46
46
 
47
47
  // 6. 排序与初始化
@@ -60,12 +60,12 @@ export async function loadPlugins(config: Record<string, any> | undefined, plugi
60
60
  // 直接挂载到 befly 下
61
61
  (context as any)[plugin.name!] = pluginInstance;
62
62
  } catch (error: any) {
63
- Logger.error(`插件 ${plugin.name} 初始化失败`, error);
63
+ Logger.error({ err: error, plugin: plugin.name }, '插件初始化失败');
64
64
  process.exit(1);
65
65
  }
66
66
  }
67
67
  } catch (error: any) {
68
- Logger.error('加载插件时发生错误', error);
68
+ Logger.error({ err: error }, '加载插件时发生错误');
69
69
  process.exit(1);
70
70
  }
71
71
  }