befly 3.2.1 → 3.3.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 (73) hide show
  1. package/bin/index.ts +138 -0
  2. package/checks/conflict.ts +35 -25
  3. package/checks/table.ts +6 -6
  4. package/commands/addon.ts +57 -0
  5. package/commands/build.ts +74 -0
  6. package/commands/dev.ts +94 -0
  7. package/commands/index.ts +252 -0
  8. package/commands/script.ts +303 -0
  9. package/commands/start.ts +80 -0
  10. package/commands/syncApi.ts +327 -0
  11. package/{scripts → commands}/syncDb/apply.ts +2 -2
  12. package/{scripts → commands}/syncDb/constants.ts +13 -7
  13. package/{scripts → commands}/syncDb/ddl.ts +7 -5
  14. package/{scripts → commands}/syncDb/helpers.ts +18 -18
  15. package/{scripts → commands}/syncDb/index.ts +37 -23
  16. package/{scripts → commands}/syncDb/sqlite.ts +1 -1
  17. package/{scripts → commands}/syncDb/state.ts +10 -4
  18. package/{scripts → commands}/syncDb/table.ts +7 -7
  19. package/{scripts → commands}/syncDb/tableCreate.ts +7 -6
  20. package/{scripts → commands}/syncDb/types.ts +5 -5
  21. package/{scripts → commands}/syncDb/version.ts +1 -1
  22. package/commands/syncDb.ts +35 -0
  23. package/commands/syncDev.ts +174 -0
  24. package/commands/syncMenu.ts +368 -0
  25. package/config/env.ts +4 -4
  26. package/config/menu.json +67 -0
  27. package/{utils/crypto.ts → lib/cipher.ts} +16 -67
  28. package/lib/database.ts +296 -0
  29. package/{utils → lib}/dbHelper.ts +102 -56
  30. package/{utils → lib}/jwt.ts +124 -151
  31. package/{utils → lib}/logger.ts +47 -24
  32. package/lib/middleware.ts +271 -0
  33. package/{utils → lib}/redisHelper.ts +4 -4
  34. package/{utils/validate.ts → lib/validator.ts} +101 -78
  35. package/lifecycle/bootstrap.ts +63 -0
  36. package/lifecycle/checker.ts +165 -0
  37. package/lifecycle/cluster.ts +241 -0
  38. package/lifecycle/lifecycle.ts +139 -0
  39. package/lifecycle/loader.ts +513 -0
  40. package/main.ts +14 -12
  41. package/package.json +21 -9
  42. package/paths.ts +34 -0
  43. package/plugins/cache.ts +187 -0
  44. package/plugins/db.ts +4 -4
  45. package/plugins/logger.ts +1 -1
  46. package/plugins/redis.ts +4 -4
  47. package/router/api.ts +155 -0
  48. package/router/root.ts +53 -0
  49. package/router/static.ts +76 -0
  50. package/types/api.d.ts +0 -36
  51. package/types/befly.d.ts +8 -6
  52. package/types/common.d.ts +1 -1
  53. package/types/context.d.ts +3 -3
  54. package/types/util.d.ts +45 -0
  55. package/util.ts +299 -0
  56. package/config/fields.ts +0 -55
  57. package/config/regexAliases.ts +0 -51
  58. package/config/reserved.ts +0 -96
  59. package/scripts/syncDb/tests/constants.test.ts +0 -105
  60. package/scripts/syncDb/tests/ddl.test.ts +0 -134
  61. package/scripts/syncDb/tests/helpers.test.ts +0 -70
  62. package/scripts/syncDb.ts +0 -10
  63. package/types/index.d.ts +0 -450
  64. package/types/index.ts +0 -438
  65. package/types/validator.ts +0 -43
  66. package/utils/colors.ts +0 -221
  67. package/utils/database.ts +0 -348
  68. package/utils/helper.ts +0 -812
  69. package/utils/index.ts +0 -33
  70. package/utils/requestContext.ts +0 -167
  71. /package/{scripts → commands}/syncDb/schema.ts +0 -0
  72. /package/{utils → lib}/sqlBuilder.ts +0 -0
  73. /package/{utils → lib}/xml.ts +0 -0
@@ -1,6 +1,6 @@
1
1
  /**
2
- * JWT 工具类 - TypeScript 版本
3
- * 提供JWT token的签名、验证和解码功能以及应用层的便捷接口
2
+ * JWT 工具类 - Befly 项目专用
3
+ * 直接集成环境变量,提供开箱即用的 JWT 功能
4
4
  */
5
5
 
6
6
  import { createHmac } from 'crypto';
@@ -96,36 +96,22 @@ export class Jwt {
96
96
  /**
97
97
  * 签名 JWT token
98
98
  * @param payload - JWT载荷数据
99
- * @param options - 签名选项
99
+ * @param options - 签名选项(自动使用 Env.JWT_SECRET)
100
100
  * @returns JWT token字符串
101
101
  */
102
- static sign(payload: JwtPayload, options: JwtSignOptions): string;
103
- static sign(payload: JwtPayload, secret: string, expiresIn: string | number): string;
104
- static sign(payload: JwtPayload, optionsOrSecret: JwtSignOptions | string = {}, expiresIn?: string | number): string {
102
+ static sign(payload: JwtPayload, options?: JwtSignOptions): string {
105
103
  if (!payload || typeof payload !== 'object') {
106
104
  throw new Error('载荷必须是非空对象');
107
105
  }
108
106
 
109
- // 支持两种调用方式
110
- let options: JwtSignOptions;
111
- if (typeof optionsOrSecret === 'string') {
112
- // sign(payload, secret, expiresIn) 形式
113
- options = {
114
- secret: optionsOrSecret,
115
- expiresIn: expiresIn
116
- };
117
- } else {
118
- // sign(payload, options) 形式
119
- options = optionsOrSecret;
120
- }
121
-
122
- const secret = options.secret || Env.JWT_SECRET;
123
- const algorithm = (options.algorithm || Env.JWT_ALGORITHM || 'HS256') as JwtAlgorithm;
124
-
107
+ const secret = options?.secret || Env.JWT_SECRET;
125
108
  if (!secret) {
126
- throw new Error('JWT密钥是必需的');
109
+ throw new Error('JWT密钥未配置');
127
110
  }
128
111
 
112
+ const opts = options || {};
113
+ const algorithm = (opts.algorithm || Env.JWT_ALGORITHM || 'HS256') as JwtAlgorithm;
114
+
129
115
  const now = Math.floor(Date.now() / 1000);
130
116
 
131
117
  // 创建 header
@@ -139,17 +125,22 @@ export class Jwt {
139
125
  // 创建 payload
140
126
  const jwtPayload: JwtPayload = { ...payload, iat: now };
141
127
 
142
- if (options.expiresIn || Env.JWT_EXPIRES_IN) {
143
- const expSeconds = Jwt.parseExpiration(options.expiresIn || Env.JWT_EXPIRES_IN);
128
+ if (opts.expiresIn) {
129
+ const expSeconds = Jwt.parseExpiration(opts.expiresIn);
130
+ jwtPayload.exp = now + expSeconds;
131
+ } else {
132
+ // 使用默认过期时间
133
+ const defaultExpiry = Env.JWT_EXPIRES_IN || '7d';
134
+ const expSeconds = Jwt.parseExpiration(defaultExpiry);
144
135
  jwtPayload.exp = now + expSeconds;
145
136
  }
146
- if (options.issuer) jwtPayload.iss = options.issuer;
147
- if (options.audience) jwtPayload.aud = options.audience;
148
- if (options.subject) jwtPayload.sub = options.subject;
149
- if (options.notBefore) {
150
- jwtPayload.nbf = typeof options.notBefore === 'number' ? options.notBefore : now + Jwt.parseExpiration(options.notBefore);
137
+ if (opts.issuer) jwtPayload.iss = opts.issuer;
138
+ if (opts.audience) jwtPayload.aud = opts.audience;
139
+ if (opts.subject) jwtPayload.sub = opts.subject;
140
+ if (opts.notBefore) {
141
+ jwtPayload.nbf = typeof opts.notBefore === 'number' ? opts.notBefore : now + Jwt.parseExpiration(opts.notBefore);
151
142
  }
152
- if (options.jwtId) jwtPayload.jti = options.jwtId;
143
+ if (opts.jwtId) jwtPayload.jti = opts.jwtId;
153
144
 
154
145
  const encodedPayload = Jwt.base64UrlEncode(JSON.stringify(jwtPayload));
155
146
 
@@ -163,31 +154,21 @@ export class Jwt {
163
154
  /**
164
155
  * 验证 JWT token
165
156
  * @param token - JWT token字符串
166
- * @param options - 验证选项
157
+ * @param options - 验证选项(自动使用 Env.JWT_SECRET)
167
158
  * @returns 解码后的载荷数据
168
159
  */
169
- static verify(token: string, options: JwtVerifyOptions): JwtPayload;
170
- static verify(token: string, secret: string): JwtPayload;
171
- static verify(token: string, optionsOrSecret: JwtVerifyOptions | string = {}): JwtPayload {
160
+ static verify(token: string, options?: JwtVerifyOptions): JwtPayload {
172
161
  if (!token || typeof token !== 'string') {
173
162
  throw new Error('Token必须是非空字符串');
174
163
  }
175
164
 
176
- // 支持两种调用方式
177
- let options: JwtVerifyOptions;
178
- if (typeof optionsOrSecret === 'string') {
179
- // verify(token, secret) 形式
180
- options = { secret: optionsOrSecret };
181
- } else {
182
- // verify(token, options) 形式
183
- options = optionsOrSecret;
184
- }
185
-
186
- const secret = options.secret || Env.JWT_SECRET;
165
+ const secret = options?.secret || Env.JWT_SECRET;
187
166
  if (!secret) {
188
- throw new Error('JWT密钥是必需的');
167
+ throw new Error('JWT密钥未配置');
189
168
  }
190
169
 
170
+ const opts = options || {};
171
+
191
172
  const parts = token.split('.');
192
173
  if (parts.length !== 3) {
193
174
  throw new Error('JWT格式无效');
@@ -215,24 +196,24 @@ export class Jwt {
215
196
  // 验证时间
216
197
  const now = Math.floor(Date.now() / 1000);
217
198
 
218
- if (!options.ignoreExpiration && payload.exp && payload.exp < now) {
199
+ if (!opts.ignoreExpiration && payload.exp && payload.exp < now) {
219
200
  throw new Error('Token已过期 (expired)');
220
201
  }
221
- if (!options.ignoreNotBefore && payload.nbf && payload.nbf > now) {
202
+ if (!opts.ignoreNotBefore && payload.nbf && payload.nbf > now) {
222
203
  throw new Error('Token尚未生效');
223
204
  }
224
205
 
225
206
  // 验证 issuer、audience、subject
226
- if (options.issuer && payload.iss !== options.issuer) {
207
+ if (opts.issuer && payload.iss !== opts.issuer) {
227
208
  throw new Error('Token发行者无效');
228
209
  }
229
- if (options.audience) {
210
+ if (opts.audience) {
230
211
  const audiences = Array.isArray(payload.aud) ? payload.aud : [payload.aud];
231
- if (!audiences.includes(options.audience)) {
212
+ if (!audiences.includes(opts.audience)) {
232
213
  throw new Error('Token受众无效');
233
214
  }
234
215
  }
235
- if (options.subject && payload.sub !== options.subject) {
216
+ if (opts.subject && payload.sub !== opts.subject) {
236
217
  throw new Error('Token主题无效');
237
218
  }
238
219
 
@@ -298,68 +279,146 @@ export class Jwt {
298
279
  return this.getTimeToExpiry(token) <= 0;
299
280
  }
300
281
 
301
- // ==================== 应用层便捷方法 ====================
282
+ /**
283
+ * 检查 token 是否即将过期
284
+ * @param token - JWT token字符串
285
+ * @param thresholdSeconds - 过期阈值(秒),默认300秒(5分钟)
286
+ * @returns 是否即将过期
287
+ */
288
+ static isNearExpiry(token: string, thresholdSeconds: number = 300): boolean {
289
+ const timeToExpiry = this.getTimeToExpiry(token);
290
+ return timeToExpiry > 0 && timeToExpiry <= thresholdSeconds;
291
+ }
292
+
293
+ /**
294
+ * 检查载荷是否包含特定角色
295
+ * @param payload - JWT载荷
296
+ * @param role - 要检查的角色
297
+ * @returns 是否包含该角色
298
+ */
299
+ static hasRole(payload: JwtPayload, role: string): boolean {
300
+ if (!payload) return false;
301
+ if (payload.role === role) return true;
302
+ if (Array.isArray(payload.roles) && payload.roles.includes(role)) return true;
303
+ return false;
304
+ }
305
+
306
+ /**
307
+ * 检查载荷是否包含任意一个角色
308
+ * @param payload - JWT载荷
309
+ * @param roles - 要检查的角色列表
310
+ * @returns 是否包含任意一个角色
311
+ */
312
+ static hasAnyRole(payload: JwtPayload, roles: string[]): boolean {
313
+ if (!payload) return false;
314
+ return roles.some((role) => this.hasRole(payload, role));
315
+ }
316
+
317
+ /**
318
+ * 检查载荷是否包含特定权限
319
+ * @param payload - JWT载荷
320
+ * @param permission - 要检查的权限
321
+ * @returns 是否包含该权限
322
+ */
323
+ static hasPermission(payload: JwtPayload, permission: string): boolean {
324
+ if (!payload || !payload.permissions) return false;
325
+ return Array.isArray(payload.permissions) && payload.permissions.includes(permission);
326
+ }
327
+
328
+ /**
329
+ * 检查载荷是否包含所有权限
330
+ * @param payload - JWT载荷
331
+ * @param permissions - 要检查的权限列表
332
+ * @returns 是否包含所有权限
333
+ */
334
+ static hasAllPermissions(payload: JwtPayload, permissions: string[]): boolean {
335
+ if (!payload || !payload.permissions) return false;
336
+ return permissions.every((permission) => this.hasPermission(payload, permission));
337
+ }
338
+
339
+ // ==================== 便捷方法(自动使用环境变量) ====================
340
+
341
+ /**
342
+ * 创建用户 token(快捷方法)
343
+ */
344
+ static create(payload: JwtPayload): string {
345
+ return this.sign(payload);
346
+ }
347
+
348
+ /**
349
+ * 检查 token(快捷方法)
350
+ */
351
+ static check(token: string): JwtPayload {
352
+ return this.verify(token);
353
+ }
354
+
355
+ /**
356
+ * 解析 token(快捷方法,不验证签名)
357
+ */
358
+ static parse(token: string): JwtPayload {
359
+ return this.decode(token);
360
+ }
302
361
 
303
362
  /**
304
363
  * 签名用户认证 token
305
364
  */
306
- static signUserToken(userInfo: JwtPayload, options: JwtSignOptions = {}): string {
365
+ static signUserToken(userInfo: JwtPayload, options?: JwtSignOptions): string {
307
366
  return this.sign(userInfo, options);
308
367
  }
309
368
 
310
369
  /**
311
370
  * 签名 API 访问 token
312
371
  */
313
- static signAPIToken(payload: JwtPayload, options: JwtSignOptions = {}): string {
372
+ static signAPIToken(payload: JwtPayload, options?: JwtSignOptions): string {
314
373
  return this.sign(payload, { audience: 'api', expiresIn: '1h', ...options });
315
374
  }
316
375
 
317
376
  /**
318
377
  * 签名刷新 token
319
378
  */
320
- static signRefreshToken(payload: JwtPayload, options: JwtSignOptions = {}): string {
379
+ static signRefreshToken(payload: JwtPayload, options?: JwtSignOptions): string {
321
380
  return this.sign(payload, { audience: 'refresh', expiresIn: '30d', ...options });
322
381
  }
323
382
 
324
383
  /**
325
384
  * 签名临时 token (用于重置密码等)
326
385
  */
327
- static signTempToken(payload: JwtPayload, options: JwtSignOptions = {}): string {
386
+ static signTempToken(payload: JwtPayload, options?: JwtSignOptions): string {
328
387
  return this.sign(payload, { audience: 'temporary', expiresIn: '15m', ...options });
329
388
  }
330
389
 
331
390
  /**
332
391
  * 验证用户认证 token
333
392
  */
334
- static verifyUserToken(token: string, options: JwtVerifyOptions = {}): JwtPayload {
393
+ static verifyUserToken(token: string, options?: JwtVerifyOptions): JwtPayload {
335
394
  return this.verify(token, options);
336
395
  }
337
396
 
338
397
  /**
339
398
  * 验证 API 访问 token
340
399
  */
341
- static verifyAPIToken(token: string, options: JwtVerifyOptions = {}): JwtPayload {
400
+ static verifyAPIToken(token: string, options?: JwtVerifyOptions): JwtPayload {
342
401
  return this.verify(token, { audience: 'api', ...options });
343
402
  }
344
403
 
345
404
  /**
346
405
  * 验证刷新 token
347
406
  */
348
- static verifyRefreshToken(token: string, options: JwtVerifyOptions = {}): JwtPayload {
407
+ static verifyRefreshToken(token: string, options?: JwtVerifyOptions): JwtPayload {
349
408
  return this.verify(token, { audience: 'refresh', ...options });
350
409
  }
351
410
 
352
411
  /**
353
412
  * 验证临时 token
354
413
  */
355
- static verifyTempToken(token: string, options: JwtVerifyOptions = {}): JwtPayload {
414
+ static verifyTempToken(token: string, options?: JwtVerifyOptions): JwtPayload {
356
415
  return this.verify(token, { audience: 'temporary', ...options });
357
416
  }
358
417
 
359
418
  /**
360
419
  * 验证 token 并检查权限
361
420
  */
362
- static verifyWithPermissions(token: string, requiredPermissions: string | string[], options: JwtVerifyOptions = {}): JwtPayload {
421
+ static verifyWithPermissions(token: string, requiredPermissions: string | string[], options?: JwtVerifyOptions): JwtPayload {
363
422
  const payload = this.verify(token, options);
364
423
 
365
424
  if (!payload.permissions) {
@@ -379,7 +438,7 @@ export class Jwt {
379
438
  /**
380
439
  * 验证 token 并检查角色
381
440
  */
382
- static verifyWithRoles(token: string, requiredRoles: string | string[], options: JwtVerifyOptions = {}): JwtPayload {
441
+ static verifyWithRoles(token: string, requiredRoles: string | string[], options?: JwtVerifyOptions): JwtPayload {
383
442
  const payload = this.verify(token, options);
384
443
 
385
444
  if (!payload.role && !payload.roles) {
@@ -401,93 +460,7 @@ export class Jwt {
401
460
  /**
402
461
  * 软验证 token (忽略过期时间)
403
462
  */
404
- static verifySoft(token: string, options: JwtVerifyOptions = {}): JwtPayload {
463
+ static verifySoft(token: string, options?: JwtVerifyOptions): JwtPayload {
405
464
  return this.verify(token, { ignoreExpiration: true, ...options });
406
465
  }
407
-
408
- /**
409
- * 检查 token 是否即将过期
410
- * @param token - JWT token字符串
411
- * @param thresholdSeconds - 过期阈值(秒),默认300秒(5分钟)
412
- * @returns 是否即将过期
413
- */
414
- static isNearExpiry(token: string, thresholdSeconds: number = 300): boolean {
415
- const timeToExpiry = this.getTimeToExpiry(token);
416
- return timeToExpiry > 0 && timeToExpiry <= thresholdSeconds;
417
- }
418
-
419
- // ==================== 便捷静态方法 ====================
420
-
421
- /**
422
- * 创建 token(使用默认配置)
423
- * @param payload - 载荷数据
424
- * @returns JWT token字符串
425
- */
426
- static create(payload: JwtPayload): string {
427
- return this.sign(payload, Env.JWT_SECRET, Env.JWT_EXPIRES_IN || '7d');
428
- }
429
-
430
- /**
431
- * 验证 token(使用默认配置)
432
- * @param token - JWT token字符串
433
- * @returns 解码后的载荷数据
434
- */
435
- static check(token: string): JwtPayload {
436
- return this.verify(token, { secret: Env.JWT_SECRET });
437
- }
438
-
439
- /**
440
- * 解析 token(不验证签名,使用 decode)
441
- * @param token - JWT token字符串
442
- * @returns 解码后的载荷数据
443
- */
444
- static parse(token: string): JwtPayload {
445
- return this.decode(token);
446
- }
447
-
448
- /**
449
- * 检查载荷是否包含特定角色
450
- * @param payload - JWT载荷
451
- * @param role - 要检查的角色
452
- * @returns 是否包含该角色
453
- */
454
- static hasRole(payload: JwtPayload, role: string): boolean {
455
- if (!payload) return false;
456
- if (payload.role === role) return true;
457
- if (Array.isArray(payload.roles) && payload.roles.includes(role)) return true;
458
- return false;
459
- }
460
-
461
- /**
462
- * 检查载荷是否包含任意一个角色
463
- * @param payload - JWT载荷
464
- * @param roles - 要检查的角色列表
465
- * @returns 是否包含任意一个角色
466
- */
467
- static hasAnyRole(payload: JwtPayload, roles: string[]): boolean {
468
- if (!payload) return false;
469
- return roles.some((role) => this.hasRole(payload, role));
470
- }
471
-
472
- /**
473
- * 检查载荷是否包含特定权限
474
- * @param payload - JWT载荷
475
- * @param permission - 要检查的权限
476
- * @returns 是否包含该权限
477
- */
478
- static hasPermission(payload: JwtPayload, permission: string): boolean {
479
- if (!payload || !payload.permissions) return false;
480
- return Array.isArray(payload.permissions) && payload.permissions.includes(permission);
481
- }
482
-
483
- /**
484
- * 检查载荷是否包含所有权限
485
- * @param payload - JWT载荷
486
- * @param permissions - 要检查的权限列表
487
- * @returns 是否包含所有权限
488
- */
489
- static hasAllPermissions(payload: JwtPayload, permissions: string[]): boolean {
490
- if (!payload || !payload.permissions) return false;
491
- return permissions.every((permission) => this.hasPermission(payload, permission));
492
- }
493
466
  }
@@ -1,12 +1,11 @@
1
1
  /**
2
- * 日志系统 - TypeScript 版本
3
- * 提供分级日志记录和文件管理功能
2
+ * 日志系统 - Befly 项目专用
3
+ * 直接集成环境变量,提供开箱即用的日志功能
4
4
  */
5
5
 
6
- import path from 'path';
6
+ import { join } from 'pathe';
7
7
  import { appendFile, stat } from 'node:fs/promises';
8
- import { formatDate } from './index.js';
9
- import { Colors } from './colors.js';
8
+ import chalk from 'chalk';
10
9
  import { Env } from '../config/env.js';
11
10
  import type { LogLevel } from '../types/common.js';
12
11
 
@@ -15,15 +14,31 @@ import type { LogLevel } from '../types/common.js';
15
14
  */
16
15
  type LogMessage = string | number | boolean | null | undefined | Record<string, any> | any[];
17
16
 
17
+ /**
18
+ * 格式化日期时间
19
+ */
20
+ function formatDate(): string {
21
+ const now = new Date();
22
+ const year = now.getFullYear();
23
+ const month = String(now.getMonth() + 1).padStart(2, '0');
24
+ const day = String(now.getDate()).padStart(2, '0');
25
+ const hours = String(now.getHours()).padStart(2, '0');
26
+ const minutes = String(now.getMinutes()).padStart(2, '0');
27
+ const seconds = String(now.getSeconds()).padStart(2, '0');
28
+ return `${year}-${month}-${day} ${hours}:${minutes}:${seconds}`;
29
+ }
30
+
18
31
  /**
19
32
  * 日志器类
20
33
  */
21
34
  export class Logger {
22
- /** 日志目录 */
23
- static logDir: string = Env.LOG_DIR || 'logs';
24
-
25
- /** 单个日志文件最大大小(字节) */
26
- static maxFileSize: number = Env.LOG_MAX_SIZE || 50 * 1024 * 1024; // 50MB
35
+ /** 日志配置(直接使用 Env) */
36
+ private static readonly config = {
37
+ logDir: Env.LOG_DIR || 'logs',
38
+ maxFileSize: Env.LOG_MAX_SIZE || 50 * 1024 * 1024,
39
+ enableDebug: Env.LOG_DEBUG === 1,
40
+ toConsole: Env.LOG_TO_CONSOLE === 1
41
+ };
27
42
 
28
43
  /** 当前使用的日志文件缓存 */
29
44
  private static currentFiles: Map<string, string> = new Map();
@@ -34,16 +49,16 @@ export class Logger {
34
49
  * @param message - 日志消息
35
50
  */
36
51
  static async log(level: LogLevel, message: LogMessage): Promise<void> {
37
- // debug 日志特殊处理:仅当 LOG_DEBUG=1 时才记录
38
- if (level === 'debug' && Env.LOG_DEBUG !== 1) return;
52
+ // debug 日志特殊处理:仅当 enableDebug 为 true 时才记录
53
+ if (level === 'debug' && !this.config.enableDebug) return;
39
54
 
40
55
  // 格式化消息
41
56
  const timestamp = formatDate();
42
57
  const colorMap = {
43
- info: Colors.greenBright,
44
- debug: Colors.cyanBright,
45
- warn: Colors.yellowBright,
46
- error: Colors.redBright
58
+ info: chalk.greenBright,
59
+ debug: chalk.cyanBright,
60
+ warn: chalk.yellowBright,
61
+ error: chalk.redBright
47
62
  };
48
63
 
49
64
  // 处理消息内容
@@ -59,7 +74,7 @@ export class Logger {
59
74
  const coloredMessage = `[${timestamp}] ${coloredLevelStr} - ${content}`;
60
75
 
61
76
  // 控制台输出
62
- if (Env.LOG_TO_CONSOLE === 1) {
77
+ if (this.config.toConsole) {
63
78
  console.log(coloredMessage);
64
79
  }
65
80
 
@@ -69,6 +84,14 @@ export class Logger {
69
84
  await this.writeToFile(plainMessage, level);
70
85
  }
71
86
 
87
+ /**
88
+ * 记录成功日志(使用 info 级别)
89
+ * @param message - 日志消息
90
+ */
91
+ static async success(message: LogMessage): Promise<void> {
92
+ await this.log('info', message);
93
+ }
94
+
72
95
  /**
73
96
  * 写入日志文件
74
97
  * @param message - 格式化后的消息
@@ -85,7 +108,7 @@ export class Logger {
85
108
  if (currentLogFile) {
86
109
  try {
87
110
  const stats = await stat(currentLogFile);
88
- if (stats.size >= this.maxFileSize) {
111
+ if (stats.size >= this.config.maxFileSize) {
89
112
  this.currentFiles.delete(prefix);
90
113
  currentLogFile = undefined;
91
114
  }
@@ -98,7 +121,7 @@ export class Logger {
98
121
  // 查找或创建新文件
99
122
  if (!currentLogFile) {
100
123
  const glob = new Bun.Glob(`${prefix}.*.log`);
101
- const files = await Array.fromAsync(glob.scan(this.logDir));
124
+ const files = await Array.fromAsync(glob.scan(this.config.logDir));
102
125
 
103
126
  // 按索引排序并查找可用文件
104
127
  const getIndex = (f: string) => parseInt(f.match(/\.(\d+)\.log$/)?.[1] || '0');
@@ -106,10 +129,10 @@ export class Logger {
106
129
 
107
130
  let foundFile = false;
108
131
  for (let i = files.length - 1; i >= 0; i--) {
109
- const filePath = path.join(this.logDir, files[i]);
132
+ const filePath = join(this.config.logDir, files[i]);
110
133
  try {
111
134
  const stats = await stat(filePath);
112
- if (stats.size < this.maxFileSize) {
135
+ if (stats.size < this.config.maxFileSize) {
113
136
  currentLogFile = filePath;
114
137
  foundFile = true;
115
138
  break;
@@ -119,10 +142,10 @@ export class Logger {
119
142
  }
120
143
  }
121
144
 
122
- // 没有可用文件,创建新文件
145
+ // 没有可用文件,创建新文件
123
146
  if (!foundFile) {
124
147
  const maxIndex = files.length > 0 ? Math.max(...files.map(getIndex)) : -1;
125
- currentLogFile = path.join(this.logDir, `${prefix}.${maxIndex + 1}.log`);
148
+ currentLogFile = join(this.config.logDir, `${prefix}.${maxIndex + 1}.log`);
126
149
  }
127
150
 
128
151
  this.currentFiles.set(prefix, currentLogFile);
@@ -175,7 +198,7 @@ export class Logger {
175
198
 
176
199
  /**
177
200
  * 记录调试日志
178
- * 受 LOG_DEBUG 环境变量控制,仅当 LOG_DEBUG=1 时才记录
201
+ * 受 enableDebug 配置控制,仅当 enableDebug=true 时才记录
179
202
  * @param message - 日志消息
180
203
  */
181
204
  static async debug(message: LogMessage): Promise<void> {