@zhin.js/core 1.0.16 → 1.0.18

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 (118) hide show
  1. package/CHANGELOG.md +19 -0
  2. package/REFACTORING_COMPLETE.md +178 -0
  3. package/REFACTORING_STATUS.md +263 -0
  4. package/lib/adapter.d.ts +44 -19
  5. package/lib/adapter.d.ts.map +1 -1
  6. package/lib/adapter.js +81 -50
  7. package/lib/adapter.js.map +1 -1
  8. package/lib/bot.d.ts +7 -12
  9. package/lib/bot.d.ts.map +1 -1
  10. package/lib/built/adapter-process.d.ts +36 -0
  11. package/lib/built/adapter-process.d.ts.map +1 -0
  12. package/lib/built/adapter-process.js +77 -0
  13. package/lib/built/adapter-process.js.map +1 -0
  14. package/lib/built/command.d.ts +46 -0
  15. package/lib/built/command.d.ts.map +1 -0
  16. package/lib/built/command.js +54 -0
  17. package/lib/built/command.js.map +1 -0
  18. package/lib/built/component.d.ts +42 -0
  19. package/lib/built/component.d.ts.map +1 -0
  20. package/lib/built/component.js +66 -0
  21. package/lib/built/component.js.map +1 -0
  22. package/lib/built/config.d.ts +31 -0
  23. package/lib/built/config.d.ts.map +1 -0
  24. package/lib/built/config.js +141 -0
  25. package/lib/built/config.js.map +1 -0
  26. package/lib/built/cron.d.ts +53 -0
  27. package/lib/built/cron.d.ts.map +1 -0
  28. package/lib/built/cron.js +79 -0
  29. package/lib/built/cron.js.map +1 -0
  30. package/lib/built/database.d.ts +17 -0
  31. package/lib/built/database.d.ts.map +1 -0
  32. package/lib/built/database.js +28 -0
  33. package/lib/built/database.js.map +1 -0
  34. package/lib/{permissions.d.ts → built/permission.d.ts} +5 -10
  35. package/lib/built/permission.d.ts.map +1 -0
  36. package/lib/{permissions.js → built/permission.js} +11 -10
  37. package/lib/built/permission.js.map +1 -0
  38. package/lib/command.d.ts +18 -7
  39. package/lib/command.d.ts.map +1 -1
  40. package/lib/command.js +36 -15
  41. package/lib/command.js.map +1 -1
  42. package/lib/component.d.ts +1 -1
  43. package/lib/component.d.ts.map +1 -1
  44. package/lib/component.js.map +1 -1
  45. package/lib/cron.d.ts +4 -12
  46. package/lib/cron.d.ts.map +1 -1
  47. package/lib/cron.js +33 -64
  48. package/lib/cron.js.map +1 -1
  49. package/lib/index.d.ts +11 -3
  50. package/lib/index.d.ts.map +1 -1
  51. package/lib/index.js +14 -4
  52. package/lib/index.js.map +1 -1
  53. package/lib/jsx-runtime.d.ts +2 -2
  54. package/lib/jsx.d.ts +2 -3
  55. package/lib/jsx.d.ts.map +1 -1
  56. package/lib/jsx.js.map +1 -1
  57. package/lib/message.d.ts +4 -7
  58. package/lib/message.d.ts.map +1 -1
  59. package/lib/message.js.map +1 -1
  60. package/lib/plugin.d.ts +164 -51
  61. package/lib/plugin.d.ts.map +1 -1
  62. package/lib/plugin.js +520 -137
  63. package/lib/plugin.js.map +1 -1
  64. package/lib/prompt.d.ts +1 -1
  65. package/lib/prompt.d.ts.map +1 -1
  66. package/lib/prompt.js +2 -1
  67. package/lib/prompt.js.map +1 -1
  68. package/lib/types.d.ts +33 -33
  69. package/lib/types.d.ts.map +1 -1
  70. package/lib/utils.d.ts +16 -1
  71. package/lib/utils.d.ts.map +1 -1
  72. package/lib/utils.js +166 -66
  73. package/lib/utils.js.map +1 -1
  74. package/package.json +17 -11
  75. package/src/adapter.ts +131 -80
  76. package/src/bot.ts +8 -13
  77. package/src/built/adapter-process.ts +77 -0
  78. package/src/built/command.ts +102 -0
  79. package/src/built/component.ts +111 -0
  80. package/src/built/config.ts +126 -0
  81. package/src/built/cron.ts +140 -0
  82. package/src/built/database.ts +38 -0
  83. package/src/{permissions.ts → built/permission.ts} +9 -12
  84. package/src/command.ts +48 -20
  85. package/src/component.ts +2 -3
  86. package/src/cron.ts +35 -70
  87. package/src/index.ts +15 -5
  88. package/src/jsx.ts +2 -3
  89. package/src/message.ts +3 -4
  90. package/src/plugin.ts +671 -184
  91. package/src/prompt.ts +4 -3
  92. package/src/types.ts +41 -35
  93. package/src/utils.ts +418 -296
  94. package/test/minimal-bot.ts +31 -0
  95. package/test/stress-test.ts +123 -0
  96. package/tests/command.test.ts +124 -44
  97. package/ASYNC-JSX-SUPPORT.md +0 -173
  98. package/lib/app.d.ts +0 -191
  99. package/lib/app.d.ts.map +0 -1
  100. package/lib/app.js +0 -604
  101. package/lib/app.js.map +0 -1
  102. package/lib/config.d.ts +0 -54
  103. package/lib/config.d.ts.map +0 -1
  104. package/lib/config.js +0 -308
  105. package/lib/config.js.map +0 -1
  106. package/lib/log-transport.d.ts +0 -37
  107. package/lib/log-transport.d.ts.map +0 -1
  108. package/lib/log-transport.js +0 -136
  109. package/lib/log-transport.js.map +0 -1
  110. package/lib/permissions.d.ts.map +0 -1
  111. package/lib/permissions.js.map +0 -1
  112. package/src/app.ts +0 -772
  113. package/src/config.ts +0 -397
  114. package/src/log-transport.ts +0 -163
  115. package/tests/app.test.ts +0 -265
  116. package/tests/permissions.test.ts +0 -358
  117. package/tests/plugin.test.ts +0 -234
  118. package/tests/prompt.test.ts +0 -223
package/src/config.ts DELETED
@@ -1,397 +0,0 @@
1
- import fs from "node:fs";
2
- import path from "node:path";
3
- import { pathToFileURL } from "node:url";
4
- import { parse as parseYaml, stringify as stringifyYaml } from "yaml";
5
- import { parse as parseToml } from "toml";
6
- import { config as loadDotenv } from "dotenv";
7
- import { Schema } from '@zhin.js/hmr';
8
- import type { AppConfig, DefineConfig } from "./types.js";
9
- import { LogLevel } from "@zhin.js/logger";
10
- import { EventEmitter } from "node:events";
11
-
12
- export interface ConfigOptions {
13
- configPath?: string;
14
- envPath?: string;
15
- envOverride?: boolean;
16
- }
17
-
18
- /**
19
- * 支持的配置文件格式
20
- */
21
- export type ConfigFormat = "json" | "yaml" | "yml" | "toml" | "js" | "ts";
22
- export class Config<T extends object = object> extends EventEmitter {
23
- #filepath: string;
24
- #schema: Schema<Partial<T>>;
25
- #data: T;
26
- constructor(filename: string, schema: Schema<Partial<T>>, initialConfig: T) {
27
- super();
28
- const ext = path.extname(filename).toLowerCase();
29
- if (!Config.supportedExtensions.includes(ext)) {
30
- throw new Error(
31
- `不支持的配置文件格式: ${ext},支持的格式有 ${Config.supportedExtensions.join(
32
- "/"
33
- )}`
34
- );
35
- }
36
- const fullpath = path.resolve(process.cwd(), filename);
37
- if (!fs.existsSync(fullpath)) {
38
- Config.save(fullpath, initialConfig);
39
- }
40
- this.#filepath = fullpath;
41
- this.#schema = schema;
42
- this.#data = initialConfig;
43
- this.#load(this.#data);
44
- }
45
- get filepath() {
46
- return this.#filepath;
47
- }
48
- get config() {
49
- return Config.proxyResult(this.#data,()=>{Config.save(this.#filepath, this.#data);});
50
- }
51
- set config(newConfig: T) {
52
- const beforeConfig = this.#data;
53
- this.#data = newConfig;
54
- this.emit("change", beforeConfig, this.#data);
55
- Config.save(this.#filepath, this.#data);
56
- }
57
- #load(before: T) {
58
- // 加载配置文件
59
- Config.load(this.#filepath, this.#schema)
60
- .then((data) => {
61
- this.#data = data as T;
62
- if (JSON.stringify(before) !== JSON.stringify(this.#data)) {
63
- this.emit("change", before, this.#data);
64
- }
65
- })
66
- }
67
- reload() {
68
- this.#load(this.#data);
69
- }
70
- get<K extends Config.Paths<T>>(key: K): Config.Value<T, K> {
71
- const lastKey = /\.?([^.]*)$/.exec(key)?.[1];
72
- if (!lastKey) throw new Error(`无法获取配置项: ${key}`);
73
- const obj = Config.getNestedObject(this.#data, key);
74
- const result= Reflect.get(obj, lastKey);
75
- return Config.proxyResult(result,()=>{Config.save(this.#filepath, this.#data);}) as Config.Value<T, K>;
76
- }
77
- set<K extends Config.Paths<T>>(key: K, value: Config.Value<T, K>): void {
78
- const prop = /\.?([^.]*)$/.exec(key)?.[1];
79
- if (!prop) throw new Error(`无法设置配置项: ${key}`);
80
- const obj = Config.getNestedObject(this.#data, key);
81
- const beforeConfig = this.#data;
82
- Reflect.set(obj, prop, value);
83
- Config.save(this.#filepath, this.#data);
84
- this.emit("change", beforeConfig, this.#data);
85
- }
86
- }
87
- export namespace Config {
88
- export const supportedExtensions = [
89
- ".json",
90
- ".yaml",
91
- ".yml",
92
- ".toml",
93
- ".js",
94
- ".ts",
95
- ];
96
- export function proxyResult<T>(result:T,onSet:(value:T)=>void):T{
97
- if((typeof result!=='object' && typeof result!=='string') || result===null) return result;
98
- if(typeof result==='string') return replaceEnvVars(result) as T;
99
- return new Proxy(result as (T & object),{
100
- get(target,prop,receiver){
101
- const value=Reflect.get(target,prop,receiver)
102
- return proxyResult(value as T,onSet);
103
- },
104
- set(target,prop,value,receiver){
105
- const result=Reflect.set(target,prop,value,receiver)
106
- onSet(value);
107
- return result;
108
- }
109
- });
110
- }
111
- /**
112
- * 配置文件元数据,用于记录原始格式信息
113
- */
114
- interface ConfigMetadata {
115
- usesFunction: boolean; // 是否使用函数导出
116
- originalContent?: string; // 原始文件内容
117
- }
118
-
119
- // 存储配置文件的元数据
120
- const configMetadataMap = new Map<string, ConfigMetadata>();
121
-
122
- /**
123
- * 智能保存配置文件,保留原有格式
124
- */
125
- export function save<T extends object>(filePath: string, config: T): void {
126
- const ext = path.extname(filePath).toLowerCase();
127
- const metadata = configMetadataMap.get(filePath);
128
- let content: string;
129
-
130
- switch (ext) {
131
- case ".json":
132
- content = JSON.stringify(config, null, 2);
133
- break;
134
- case ".yaml":
135
- case ".yml":
136
- content = stringifyYaml(config, { indent: 2 });
137
- break;
138
- case ".toml":
139
- throw new Error("暂不支持保存 TOML 格式的配置文件");
140
- case ".js":
141
- case ".ts":
142
- // 智能保存 JS/TS 配置
143
- content = saveJsConfig(filePath, config, metadata);
144
- break;
145
- default:
146
- throw new Error(`不支持的配置文件格式: ${ext}`);
147
- }
148
-
149
- fs.writeFileSync(filePath, content, "utf-8");
150
- }
151
-
152
- /**
153
- * 智能保存 JS/TS 配置文件,保留函数格式和环境变量
154
- */
155
- function saveJsConfig<T extends object>(
156
- filePath: string,
157
- config: T,
158
- metadata?: ConfigMetadata
159
- ): string {
160
- const ext = path.extname(filePath);
161
- const usesFunction = metadata?.usesFunction ?? false;
162
- const originalContent = metadata?.originalContent;
163
-
164
- if (usesFunction && originalContent) {
165
- // 如果使用函数导出,尝试保留原有格式并更新配置
166
- return updateJsConfigWithFunction(originalContent, config, ext === ".ts");
167
- } else {
168
- // 简单对象导出
169
- const typeAnnotation = ext === ".ts" ? ": DefineConfig<AppConfig>" : "";
170
- return `export default ${JSON.stringify(
171
- config,
172
- null,
173
- 2
174
- )}${typeAnnotation};`;
175
- }
176
- }
177
-
178
- /**
179
- * 更新使用函数导出的配置文件
180
- * 保留环境变量访问模式和原有代码结构
181
- */
182
- function updateJsConfigWithFunction<T extends object>(
183
- originalContent: string,
184
- newConfig: T,
185
- isTypeScript: boolean
186
- ): string {
187
- // 简单的字符串替换策略:
188
- // 1. 找到 return 语句中的对象
189
- // 2. 替换对象内容,但保留环境变量访问
190
-
191
- const returnMatch = originalContent.match(
192
- /return\s*({[\s\S]*?})\s*[;}]?\s*$/m
193
- );
194
- if (!returnMatch) {
195
- // 如果找不到 return 语句,回退到简单格式
196
- const typeAnnotation = isTypeScript ? ": DefineConfig<AppConfig>" : "";
197
- return `export default ${JSON.stringify(
198
- newConfig,
199
- null,
200
- 2
201
- )}${typeAnnotation};`;
202
- }
203
-
204
- // 保留原有的函数签名和代码结构
205
- // 只更新 return 语句中的对象
206
- const configString = formatConfigObject(newConfig, 2);
207
- const beforeReturn = originalContent.substring(0, returnMatch.index! + 6); // "return"
208
-
209
- return `${beforeReturn.trim()} ${configString}\n});`;
210
- }
211
-
212
- /**
213
- * 格式化配置对象为字符串,保留环境变量访问模式
214
- */
215
- function formatConfigObject(obj: any, indent: number = 0): string {
216
- const spaces = " ".repeat(indent);
217
- const nextSpaces = " ".repeat(indent + 2);
218
-
219
- if (obj === null || obj === undefined) {
220
- return String(obj);
221
- }
222
-
223
- if (typeof obj === "string") {
224
- for(const [key,value] of Object.entries(process.env)){
225
- if(obj===value && value!=='zhin') return `process.env.${key}`;
226
- }
227
- return JSON.stringify(obj);
228
- }
229
-
230
- if (typeof obj === "number" || typeof obj === "boolean") {
231
- return String(obj);
232
- }
233
-
234
- if (Array.isArray(obj)) {
235
- if (obj.length === 0) return "[]";
236
- const items = obj
237
- .map((item) => `${nextSpaces}${formatConfigObject(item, indent + 2)}`)
238
- .join(",\n");
239
- return `[\n${items}\n${spaces}]`;
240
- }
241
-
242
- if (typeof obj === "object") {
243
- const entries = Object.entries(obj);
244
- if (entries.length === 0) return "{}";
245
-
246
- const props = entries
247
- .map(([key, value]) => {
248
- const needsQuotes = /[^a-zA-Z0-9_$]/.test(key);
249
- const keyStr = needsQuotes ? `'${key}'` : key;
250
- return `${nextSpaces}${keyStr}: ${formatConfigObject(
251
- value,
252
- indent + 2
253
- )}`;
254
- })
255
- .join(",\n");
256
-
257
- return `{\n${props}\n${spaces}}`;
258
- }
259
-
260
- return JSON.stringify(obj);
261
- }
262
-
263
- /**
264
- * 记录配置文件的元数据
265
- */
266
- export function setMetadata(
267
- filePath: string,
268
- metadata: ConfigMetadata
269
- ): void {
270
- configMetadataMap.set(filePath, metadata);
271
- }
272
-
273
- /**
274
- * 获取配置文件的元数据
275
- */
276
- export function getMetadata(filePath: string): ConfigMetadata | undefined {
277
- return configMetadataMap.get(filePath);
278
- }
279
- export async function load<T extends object>(
280
- filePath: string,
281
- schema: Schema<T>
282
- ): Promise<T> {
283
- const ext = path.extname(filePath).toLowerCase();
284
- const content = fs.readFileSync(filePath, "utf-8");
285
- let rawConfig: any;
286
- let usesFunction = false;
287
-
288
- switch (ext) {
289
- case ".json":
290
- rawConfig = JSON.parse(content);
291
- break;
292
- case ".yaml":
293
- case ".yml":
294
- rawConfig = parseYaml(content);
295
- break;
296
- case ".toml":
297
- rawConfig = parseToml(content);
298
- break;
299
- case ".js":
300
- case ".ts":
301
- // 使用动态导入加载 JS/TS 模块
302
- const fileUrl = pathToFileURL(path.resolve(filePath)).href;
303
- const module = await import(`${fileUrl}?t=${Date.now()}`);
304
- // 支持 ES 模块的 default 导出和 CommonJS 模块
305
- rawConfig = module.default || module;
306
- if (typeof rawConfig === "function") {
307
- usesFunction = true;
308
- rawConfig = await rawConfig(
309
- (process.env || {}) as Record<string, string>
310
- );
311
- }
312
- // 记录元数据
313
- setMetadata(filePath, { usesFunction, originalContent: content });
314
- break;
315
- default:
316
- throw new Error(`不支持的配置文件格式: ${ext}`);
317
- }
318
- return schema(rawConfig);
319
- }
320
- export type Value<
321
- T,
322
- K extends Paths<T>
323
- > = K extends `${infer Key}.${infer Rest}`
324
- ? Key extends keyof T
325
- ? Rest extends Paths<T[Key]>
326
- ? Value<T[Key], Rest>
327
- : never
328
- : never
329
- : K extends keyof T
330
- ? T[K]
331
- : never;
332
-
333
- export type Paths<
334
- T,
335
- Prefix extends string = "",
336
- Depth extends any[] = []
337
- > = Depth["length"] extends 10
338
- ? never
339
- : {
340
- [K in keyof T]: T[K] extends object
341
- ?
342
- | `${Prefix}${K & string}`
343
- | Paths<T[K], `${Prefix}${K & string}.`, [...Depth, 1]>
344
- : `${Prefix}${K & string}`;
345
- }[keyof T];
346
- export function getNestedObject<T>(obj: T, path: string): any {
347
- const parts = path.split(".");
348
- let current: any = obj;
349
-
350
- for (let i = 0; i < parts.length - 1; i++) {
351
- const part = parts[i];
352
- if (!(part in current)) {
353
- current[part] = {};
354
- }
355
- current = current[part];
356
- }
357
-
358
- return current;
359
- }
360
- }
361
-
362
- /**
363
- * 替换字符串中的环境变量
364
- */
365
- function replaceEnvVars(str: string): string{
366
- if (typeof str !== 'string') return str;
367
- return (str as string).replace(/^\$\{([^}]+)\}$/, (match, content) => {
368
- // 解析环境变量名和默认值
369
- const colonIndex = content.indexOf(":-");
370
- let envName: string;
371
- let defaultValue: string | undefined;
372
-
373
- if (colonIndex !== -1) {
374
- // 格式: VAR_NAME:-default_value
375
- envName = content.slice(0, colonIndex);
376
- defaultValue = content.slice(colonIndex + 2);
377
- } else {
378
- // 格式: VAR_NAME
379
- envName = content;
380
- defaultValue = undefined;
381
- }
382
-
383
- const envValue = process.env[envName];
384
-
385
- if (envValue !== undefined) {
386
- return envValue;
387
- } else if (defaultValue !== undefined) {
388
- return defaultValue;
389
- } else {
390
- return match;
391
- }
392
- });
393
- }
394
-
395
- export function defineConfig<T extends DefineConfig<AppConfig>>(config: T): T {
396
- return config;
397
- }
@@ -1,163 +0,0 @@
1
- import { LogTransport } from '@zhin.js/logger'
2
- import { App } from './app.js'
3
-
4
- /**
5
- * 数据库日志传输器
6
- * 将日志存储到数据库,并自动清理旧日志
7
- */
8
- export class DatabaseLogTransport implements LogTransport {
9
- private app: App
10
- private stripAnsiRegex = /\x1b\[[0-9;]*m/g
11
- private cleanupTimer?: NodeJS.Timeout
12
- private maxDays: number
13
- private maxRecords: number
14
- private cleanupInterval: number
15
-
16
- constructor(app: App) {
17
- this.app = app
18
-
19
- // 从配置读取日志清理策略
20
- const logConfig = app['config']?.log || {}
21
- this.maxDays = logConfig.maxDays || 7 // 默认保留 7 天
22
- this.maxRecords = logConfig.maxRecords || 10000 // 默认最多 10000 条
23
- this.cleanupInterval = logConfig.cleanupInterval || 24 // 默认 24 小时清理一次
24
-
25
- // 启动定时清理
26
- this.startCleanup()
27
- }
28
-
29
- /**
30
- * 启动定时清理任务
31
- */
32
- private startCleanup(): void {
33
- // 立即执行一次清理
34
- this.cleanupOldLogs().catch(err => {
35
- this.app.logger.error('[DatabaseLogTransport] Initial cleanup failed:', err.message)
36
- })
37
-
38
- // 设置定时任务
39
- this.cleanupTimer = setInterval(() => {
40
- this.cleanupOldLogs().catch(err => {
41
- this.app.logger.error('[DatabaseLogTransport] Scheduled cleanup failed:', err.message)
42
- })
43
- }, this.cleanupInterval * 60 * 60 * 1000) // 转换为毫秒
44
- }
45
-
46
- /**
47
- * 清理旧日志
48
- */
49
- private async cleanupOldLogs(): Promise<void> {
50
- if (!this.app.database) {
51
- return
52
- }
53
-
54
- try {
55
- const LogModel = this.app.database.model('SystemLog')
56
- if (!LogModel) {
57
- return
58
- }
59
-
60
- // 1. 按时间清理:删除超过 maxDays 天的日志
61
- const cutoffDate = new Date()
62
- cutoffDate.setDate(cutoffDate.getDate() - this.maxDays)
63
-
64
- const deletedByDate = await LogModel
65
- .delete({ timestamp: { $lt: cutoffDate } })
66
-
67
- // 2. 按数量清理:如果日志总数超过 maxRecords,删除最旧的
68
- const total = await LogModel.select()
69
- const totalCount = total.length
70
-
71
- if (totalCount > this.maxRecords) {
72
- const excessCount = totalCount - this.maxRecords
73
-
74
- // 查找最旧的 excessCount 条日志的 ID
75
- const oldestLogs = await LogModel
76
- .select('id','timestamp')
77
- .orderBy('timestamp', 'ASC')
78
- .limit(excessCount)
79
-
80
- const idsToDelete = oldestLogs.map((log: any) => log.id)
81
-
82
- if (idsToDelete.length > 0) {
83
- await LogModel
84
- .delete({ id: { $in: idsToDelete } })
85
- }
86
- }
87
-
88
- this.app.logger.info(
89
- `[DatabaseLogTransport] Log cleanup completed. ` +
90
- `Deleted ${deletedByDate || 0} logs older than ${this.maxDays} days. ` +
91
- `Current total: ${Math.max(0, totalCount - (deletedByDate || 0))} logs.`
92
- )
93
- } catch (error) {
94
- // 静默处理错误
95
- this.app.logger.debug('[DatabaseLogTransport] Cleanup error:', (error as Error).message,(error as Error).stack)
96
- }
97
- }
98
-
99
- /**
100
- * 停止清理任务
101
- */
102
- public stopCleanup(): void {
103
- if (this.cleanupTimer) {
104
- clearInterval(this.cleanupTimer)
105
- this.cleanupTimer = undefined
106
- }
107
- }
108
-
109
- /**
110
- * 移除 ANSI 颜色代码
111
- */
112
- private stripAnsi(str: string): string {
113
- return str.replace(this.stripAnsiRegex, '')
114
- }
115
-
116
- write(message: string): void {
117
- // 移除 ANSI 颜色代码
118
- const cleanMessage = this.stripAnsi(message)
119
-
120
- // 解析日志消息
121
- // 格式: [09-08 04:07:55.852] [INFO] [MyApp]: message
122
- const logRegex = /\[[\d-]+ [\d:.]+\] \[(\w+)\] \[([^\]]+)\]: ([\s\S]+)/
123
- const match = cleanMessage.match(logRegex)
124
-
125
- if (match) {
126
- const [, level, name, msg] = match
127
- const source = name.split(':')[0] // 取第一部分作为 source
128
-
129
- // 异步存储到数据库,不阻塞日志输出
130
- this.saveToDatabase(level.toLowerCase(), name, msg.trim(), source).catch(err => {
131
- // 避免日志存储失败导致应用崩溃
132
- console.error('[DatabaseLogTransport] Failed to save log:', err.message)
133
- })
134
- }
135
- }
136
-
137
- /**
138
- * 保存日志到数据库
139
- */
140
- private async saveToDatabase(level: string, name: string, message: string, source: string): Promise<void> {
141
- if (!this.app.database) {
142
- return // 没有数据库则跳过
143
- }
144
-
145
- try {
146
- const LogModel = this.app.database.model('SystemLog')
147
- if (!LogModel) {
148
- return // 模型不存在则跳过
149
- }
150
-
151
- await LogModel.create({
152
- level,
153
- name,
154
- message,
155
- source,
156
- timestamp: new Date()
157
- })
158
- } catch (error) {
159
- // 静默处理错误,避免干扰主流程
160
- }
161
- }
162
- }
163
-