zhin.js 1.0.17 → 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.
- package/CHANGELOG.md +10 -0
- package/lib/index.d.ts +18 -3
- package/lib/index.d.ts.map +1 -1
- package/lib/index.js +0 -17
- package/lib/index.js.map +1 -1
- package/lib/log-transport.d.ts +37 -0
- package/lib/log-transport.d.ts.map +1 -0
- package/lib/log-transport.js +140 -0
- package/lib/log-transport.js.map +1 -0
- package/lib/setup.d.ts +2 -0
- package/lib/setup.d.ts.map +1 -0
- package/lib/setup.js +116 -0
- package/lib/setup.js.map +1 -0
- package/lib/types.d.ts +33 -0
- package/lib/types.d.ts.map +1 -0
- package/lib/types.js +2 -0
- package/lib/types.js.map +1 -0
- package/package.json +22 -4
- package/src/index.ts +24 -19
- package/src/log-transport.ts +167 -0
- package/src/setup.ts +145 -0
- package/src/types.ts +34 -0
package/CHANGELOG.md
CHANGED
package/lib/index.d.ts
CHANGED
|
@@ -1,6 +1,21 @@
|
|
|
1
1
|
export * from '@zhin.js/core';
|
|
2
|
-
import { AppConfig, App } from '@zhin.js/core';
|
|
3
2
|
export { default as logger } from '@zhin.js/logger';
|
|
4
|
-
|
|
5
|
-
|
|
3
|
+
declare module "zhin.js" {
|
|
4
|
+
namespace Plugin {
|
|
5
|
+
interface Contexts {
|
|
6
|
+
}
|
|
7
|
+
interface Extensions {
|
|
8
|
+
/**
|
|
9
|
+
* 定义数据库模型
|
|
10
|
+
* @param name 模型名称
|
|
11
|
+
* @param definition 模型定义
|
|
12
|
+
*/
|
|
13
|
+
defineModel<K extends keyof Models>(name: K, definition: import('@zhin.js/core').Definition<Models[K]>): void;
|
|
14
|
+
}
|
|
15
|
+
}
|
|
16
|
+
interface RegisteredAdapters {
|
|
17
|
+
}
|
|
18
|
+
interface Models {
|
|
19
|
+
}
|
|
20
|
+
}
|
|
6
21
|
//# sourceMappingURL=index.d.ts.map
|
package/lib/index.d.ts.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../src/index.ts"],"names":[],"mappings":"AAKA,cAAc,eAAe,CAAA;AAE7B,OAAO,EAAE,
|
|
1
|
+
{"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../src/index.ts"],"names":[],"mappings":"AAKA,cAAc,eAAe,CAAA;AAE7B,OAAO,EAAE,OAAO,IAAI,MAAM,EAAE,MAAM,iBAAiB,CAAA;AAOnD,OAAO,QAAQ,SAAS,CAAC;IAEvB,UAAU,MAAM,CAAC;QAEf,UAAU,QAAQ;SAAG;QAErB,UAAU,UAAU;YAClB;;;;eAIG;YACH,WAAW,CAAC,CAAC,SAAS,MAAM,MAAM,EAAE,IAAI,EAAE,CAAC,EAAE,UAAU,EAAE,OAAO,eAAe,EAAE,UAAU,CAAC,MAAM,CAAC,CAAC,CAAC,CAAC,GAAG,IAAI,CAAC;SAC/G;KACF;IAED,UAAU,kBAAkB;KAAG;IAE/B,UAAU,MAAM;KAAG;CACpB"}
|
package/lib/index.js
CHANGED
|
@@ -3,23 +3,6 @@
|
|
|
3
3
|
// ================================================================================================
|
|
4
4
|
// 导出核心框架
|
|
5
5
|
export * from '@zhin.js/core';
|
|
6
|
-
import { App, Config } from '@zhin.js/core';
|
|
7
|
-
import path from 'path';
|
|
8
|
-
import fs from 'fs';
|
|
9
6
|
// 重新导出 logger(作为独立的工具)
|
|
10
7
|
export { default as logger } from '@zhin.js/logger';
|
|
11
|
-
export async function createApp(config_param) {
|
|
12
|
-
const envFiles = ['.env', `.env.${process.env.NODE_ENV}`].filter(f => fs.existsSync(path.join(process.cwd(), f)));
|
|
13
|
-
if (config_param === undefined) {
|
|
14
|
-
config_param = Config.supportedExtensions.map(ext => `zhin.config${ext}`).find(f => fs.existsSync(path.join(process.cwd(), f)));
|
|
15
|
-
}
|
|
16
|
-
if (config_param === undefined)
|
|
17
|
-
throw new Error('No configuration file found and no configuration provided.');
|
|
18
|
-
const app = new App(config_param);
|
|
19
|
-
app.watching(envFiles, () => {
|
|
20
|
-
console.log('Environment file changed, exiting to reload...');
|
|
21
|
-
process.exit(51);
|
|
22
|
-
});
|
|
23
|
-
return app;
|
|
24
|
-
}
|
|
25
8
|
//# sourceMappingURL=index.js.map
|
package/lib/index.js.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"index.js","sourceRoot":"","sources":["../src/index.ts"],"names":[],"mappings":"AAAA,mGAAmG;AACnG,uBAAuB;AACvB,mGAAmG;AAEnG,SAAS;AACT,cAAc,eAAe,CAAA;
|
|
1
|
+
{"version":3,"file":"index.js","sourceRoot":"","sources":["../src/index.ts"],"names":[],"mappings":"AAAA,mGAAmG;AACnG,uBAAuB;AACvB,mGAAmG;AAEnG,SAAS;AACT,cAAc,eAAe,CAAA;AAC7B,uBAAuB;AACvB,OAAO,EAAE,OAAO,IAAI,MAAM,EAAE,MAAM,iBAAiB,CAAA"}
|
|
@@ -0,0 +1,37 @@
|
|
|
1
|
+
import { LogTransport } from '@zhin.js/logger';
|
|
2
|
+
import { Plugin } from '@zhin.js/core';
|
|
3
|
+
/**
|
|
4
|
+
* 数据库日志传输器
|
|
5
|
+
* 将日志存储到数据库,并自动清理旧日志
|
|
6
|
+
*/
|
|
7
|
+
export declare class DatabaseLogTransport implements LogTransport {
|
|
8
|
+
private plugin;
|
|
9
|
+
private stripAnsiRegex;
|
|
10
|
+
private cleanupTimer?;
|
|
11
|
+
private maxDays;
|
|
12
|
+
private maxRecords;
|
|
13
|
+
private cleanupInterval;
|
|
14
|
+
constructor(plugin: Plugin);
|
|
15
|
+
/**
|
|
16
|
+
* 启动定时清理任务
|
|
17
|
+
*/
|
|
18
|
+
private startCleanup;
|
|
19
|
+
/**
|
|
20
|
+
* 清理旧日志
|
|
21
|
+
*/
|
|
22
|
+
private cleanupOldLogs;
|
|
23
|
+
/**
|
|
24
|
+
* 停止清理任务
|
|
25
|
+
*/
|
|
26
|
+
stopCleanup(): void;
|
|
27
|
+
/**
|
|
28
|
+
* 移除 ANSI 颜色代码
|
|
29
|
+
*/
|
|
30
|
+
private stripAnsi;
|
|
31
|
+
write(message: string): void;
|
|
32
|
+
/**
|
|
33
|
+
* 保存日志到数据库
|
|
34
|
+
*/
|
|
35
|
+
private saveToDatabase;
|
|
36
|
+
}
|
|
37
|
+
//# sourceMappingURL=log-transport.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"log-transport.d.ts","sourceRoot":"","sources":["../src/log-transport.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,YAAY,EAAE,MAAM,iBAAiB,CAAA;AAC9C,OAAO,EAAE,MAAM,EAAE,MAAM,eAAe,CAAA;AAGtC;;;GAGG;AACH,qBAAa,oBAAqB,YAAW,YAAY;IACvD,OAAO,CAAC,MAAM,CAAQ;IACtB,OAAO,CAAC,cAAc,CAAoB;IAC1C,OAAO,CAAC,YAAY,CAAC,CAAgB;IACrC,OAAO,CAAC,OAAO,CAAQ;IACvB,OAAO,CAAC,UAAU,CAAQ;IAC1B,OAAO,CAAC,eAAe,CAAQ;gBAEnB,MAAM,EAAE,MAAM;IAiB1B;;OAEG;IACH,OAAO,CAAC,YAAY;IAcpB;;OAEG;YACW,cAAc;IAgD5B;;OAEG;IACI,WAAW,IAAI,IAAI;IAO1B;;OAEG;IACH,OAAO,CAAC,SAAS;IAIjB,KAAK,CAAC,OAAO,EAAE,MAAM,GAAG,IAAI;IAqB5B;;OAEG;YACW,cAAc;CAuB7B"}
|
|
@@ -0,0 +1,140 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* 数据库日志传输器
|
|
3
|
+
* 将日志存储到数据库,并自动清理旧日志
|
|
4
|
+
*/
|
|
5
|
+
export class DatabaseLogTransport {
|
|
6
|
+
plugin;
|
|
7
|
+
stripAnsiRegex = /\x1b\[[0-9;]*m/g;
|
|
8
|
+
cleanupTimer;
|
|
9
|
+
maxDays;
|
|
10
|
+
maxRecords;
|
|
11
|
+
cleanupInterval;
|
|
12
|
+
constructor(plugin) {
|
|
13
|
+
this.plugin = plugin;
|
|
14
|
+
const configService = plugin.inject('config');
|
|
15
|
+
const appConfig = configService.get('zhin.config.yml');
|
|
16
|
+
// 从配置读取日志清理策略
|
|
17
|
+
const logConfig = appConfig.log || {};
|
|
18
|
+
this.maxDays = logConfig.maxDays || 7; // 默认保留 7 天
|
|
19
|
+
this.maxRecords = logConfig.maxRecords || 10000; // 默认最多 10000 条
|
|
20
|
+
this.cleanupInterval = logConfig.cleanupInterval || 24; // 默认 24 小时清理一次
|
|
21
|
+
// 启动定时清理
|
|
22
|
+
this.startCleanup();
|
|
23
|
+
// 插件销毁时停止清理任务
|
|
24
|
+
plugin.onDispose(() => this.stopCleanup());
|
|
25
|
+
}
|
|
26
|
+
/**
|
|
27
|
+
* 启动定时清理任务
|
|
28
|
+
*/
|
|
29
|
+
startCleanup() {
|
|
30
|
+
// 立即执行一次清理
|
|
31
|
+
this.cleanupOldLogs().catch(err => {
|
|
32
|
+
this.plugin.logger.error('[DatabaseLogTransport] Initial cleanup failed:', err.message);
|
|
33
|
+
});
|
|
34
|
+
// 设置定时任务
|
|
35
|
+
this.cleanupTimer = setInterval(() => {
|
|
36
|
+
this.cleanupOldLogs().catch(err => {
|
|
37
|
+
this.plugin.logger.error('[DatabaseLogTransport] Scheduled cleanup failed:', err.message);
|
|
38
|
+
});
|
|
39
|
+
}, this.cleanupInterval * 60 * 60 * 1000); // 转换为毫秒
|
|
40
|
+
}
|
|
41
|
+
/**
|
|
42
|
+
* 清理旧日志
|
|
43
|
+
*/
|
|
44
|
+
async cleanupOldLogs() {
|
|
45
|
+
const db = this.plugin.inject('database');
|
|
46
|
+
if (!db) {
|
|
47
|
+
return;
|
|
48
|
+
}
|
|
49
|
+
try {
|
|
50
|
+
const LogModel = db.models.get('SystemLog');
|
|
51
|
+
if (!LogModel) {
|
|
52
|
+
return;
|
|
53
|
+
}
|
|
54
|
+
// 1. 按时间清理:删除超过 maxDays 天的日志
|
|
55
|
+
const cutoffDate = new Date();
|
|
56
|
+
cutoffDate.setDate(cutoffDate.getDate() - this.maxDays);
|
|
57
|
+
const deletedData = await LogModel
|
|
58
|
+
.delete({ timestamp: { $lt: cutoffDate } });
|
|
59
|
+
const deletedCount = typeof deletedData === 'number' ? deletedData : deletedData.length;
|
|
60
|
+
// 2. 按数量清理:如果日志总数超过 maxRecords,删除最旧的
|
|
61
|
+
const total = await LogModel.select();
|
|
62
|
+
const totalCount = total.length;
|
|
63
|
+
if (totalCount > this.maxRecords) {
|
|
64
|
+
const excessCount = totalCount - this.maxRecords;
|
|
65
|
+
// 查找最旧的 excessCount 条日志的 ID
|
|
66
|
+
const oldestLogs = await LogModel.select('id', 'timestamp').orderBy('timestamp', 'ASC').limit(excessCount);
|
|
67
|
+
const idsToDelete = oldestLogs.map((log) => log.id);
|
|
68
|
+
if (idsToDelete.length > 0) {
|
|
69
|
+
await LogModel
|
|
70
|
+
.delete({ id: { $in: idsToDelete } });
|
|
71
|
+
}
|
|
72
|
+
}
|
|
73
|
+
this.plugin.logger.info(`[DatabaseLogTransport] Log cleanup completed. ` +
|
|
74
|
+
`Deleted ${deletedData || 0} logs older than ${this.maxDays} days. ` +
|
|
75
|
+
`Current total: ${Math.max(0, totalCount - (deletedCount || 0))} logs.`);
|
|
76
|
+
}
|
|
77
|
+
catch (error) {
|
|
78
|
+
// 静默处理错误
|
|
79
|
+
this.plugin.logger.debug('[DatabaseLogTransport] Cleanup error:', error.message, error.stack);
|
|
80
|
+
}
|
|
81
|
+
}
|
|
82
|
+
/**
|
|
83
|
+
* 停止清理任务
|
|
84
|
+
*/
|
|
85
|
+
stopCleanup() {
|
|
86
|
+
if (this.cleanupTimer) {
|
|
87
|
+
clearInterval(this.cleanupTimer);
|
|
88
|
+
this.cleanupTimer = undefined;
|
|
89
|
+
}
|
|
90
|
+
}
|
|
91
|
+
/**
|
|
92
|
+
* 移除 ANSI 颜色代码
|
|
93
|
+
*/
|
|
94
|
+
stripAnsi(str) {
|
|
95
|
+
return str.replace(this.stripAnsiRegex, '');
|
|
96
|
+
}
|
|
97
|
+
write(message) {
|
|
98
|
+
// 移除 ANSI 颜色代码
|
|
99
|
+
const cleanMessage = this.stripAnsi(message);
|
|
100
|
+
// 解析日志消息
|
|
101
|
+
// 格式: [09-08 04:07:55.852] [INFO] [MyApp]: message
|
|
102
|
+
const logRegex = /\[[\d-]+ [\d:.]+\] \[(\w+)\] \[([^\]]+)\]: ([\s\S]+)/;
|
|
103
|
+
const match = cleanMessage.match(logRegex);
|
|
104
|
+
if (match) {
|
|
105
|
+
const [, level, name, msg] = match;
|
|
106
|
+
const source = name.split(':')[0]; // 取第一部分作为 source
|
|
107
|
+
// 异步存储到数据库,不阻塞日志输出
|
|
108
|
+
this.saveToDatabase(level.toLowerCase(), name, msg.trim(), source).catch(err => {
|
|
109
|
+
// 避免日志存储失败导致应用崩溃
|
|
110
|
+
console.error('[DatabaseLogTransport] Failed to save log:', err.message);
|
|
111
|
+
});
|
|
112
|
+
}
|
|
113
|
+
}
|
|
114
|
+
/**
|
|
115
|
+
* 保存日志到数据库
|
|
116
|
+
*/
|
|
117
|
+
async saveToDatabase(level, name, message, source) {
|
|
118
|
+
const databaseService = this.plugin.inject('database');
|
|
119
|
+
if (!databaseService) {
|
|
120
|
+
return; // 没有数据库则跳过
|
|
121
|
+
}
|
|
122
|
+
try {
|
|
123
|
+
const LogModel = databaseService.models.get('SystemLog');
|
|
124
|
+
if (!LogModel) {
|
|
125
|
+
return; // 模型不存在则跳过
|
|
126
|
+
}
|
|
127
|
+
await LogModel.insert({
|
|
128
|
+
level,
|
|
129
|
+
name,
|
|
130
|
+
message,
|
|
131
|
+
source,
|
|
132
|
+
timestamp: new Date()
|
|
133
|
+
});
|
|
134
|
+
}
|
|
135
|
+
catch (error) {
|
|
136
|
+
// 静默处理错误,避免干扰主流程
|
|
137
|
+
}
|
|
138
|
+
}
|
|
139
|
+
}
|
|
140
|
+
//# sourceMappingURL=log-transport.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"log-transport.js","sourceRoot":"","sources":["../src/log-transport.ts"],"names":[],"mappings":"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"}
|
package/lib/setup.d.ts
ADDED
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"setup.d.ts","sourceRoot":"","sources":["../src/setup.ts"],"names":[],"mappings":""}
|
package/lib/setup.js
ADDED
|
@@ -0,0 +1,116 @@
|
|
|
1
|
+
import { setLevel, LogLevel } from '@zhin.js/logger';
|
|
2
|
+
import { usePlugin, resolveEntry, ConfigService, PermissionService, createCommandService, createComponentService, createCronService, defineDatabaseService, ProcessAdapter, } from '@zhin.js/core';
|
|
3
|
+
import { DatabaseLogTransport } from './log-transport.js';
|
|
4
|
+
import * as path from 'path';
|
|
5
|
+
const plugin = usePlugin();
|
|
6
|
+
const { provide, start, stop, logger, children, import: importPlugin } = plugin;
|
|
7
|
+
// 1. 先加载配置,用户在 zhin.config 中决定启用哪些服务
|
|
8
|
+
const configService = new ConfigService();
|
|
9
|
+
await configService.load('zhin.config.yml', {
|
|
10
|
+
log_level: LogLevel.INFO,
|
|
11
|
+
database: {
|
|
12
|
+
dialect: "sqlite",
|
|
13
|
+
filename: "./data/test.db"
|
|
14
|
+
},
|
|
15
|
+
plugin_dirs: [path.relative(process.cwd(), import.meta.dirname), 'node_modules/@zhin.js', './plugins',],
|
|
16
|
+
plugins: [],
|
|
17
|
+
services: ['process', 'config', 'command', 'component', 'permission', 'cron'],
|
|
18
|
+
});
|
|
19
|
+
const appConfig = configService.get('zhin.config.yml');
|
|
20
|
+
const enabledServices = new Set(appConfig.services || ['process', 'config', 'command', 'component', 'permission', 'cron']);
|
|
21
|
+
// 注册配置服务(必须)
|
|
22
|
+
provide({
|
|
23
|
+
name: 'config',
|
|
24
|
+
description: '配置服务',
|
|
25
|
+
value: configService,
|
|
26
|
+
});
|
|
27
|
+
// 2. 可选服务按配置注册
|
|
28
|
+
if (enabledServices.has('process')) {
|
|
29
|
+
provide({
|
|
30
|
+
name: 'process',
|
|
31
|
+
description: '命令行适配器',
|
|
32
|
+
mounted: async (plugin) => {
|
|
33
|
+
const adapter = new ProcessAdapter(plugin);
|
|
34
|
+
await adapter.start();
|
|
35
|
+
return adapter;
|
|
36
|
+
},
|
|
37
|
+
dispose: async (adapter) => {
|
|
38
|
+
await adapter.stop();
|
|
39
|
+
}
|
|
40
|
+
});
|
|
41
|
+
}
|
|
42
|
+
if (enabledServices.has('command')) {
|
|
43
|
+
provide(createCommandService());
|
|
44
|
+
}
|
|
45
|
+
if (enabledServices.has('component')) {
|
|
46
|
+
provide(createComponentService());
|
|
47
|
+
}
|
|
48
|
+
if (enabledServices.has('permission')) {
|
|
49
|
+
provide({
|
|
50
|
+
name: 'permission',
|
|
51
|
+
description: '权限管理',
|
|
52
|
+
value: new PermissionService(),
|
|
53
|
+
});
|
|
54
|
+
}
|
|
55
|
+
if (enabledServices.has('cron')) {
|
|
56
|
+
provide(createCronService());
|
|
57
|
+
}
|
|
58
|
+
// 3. 配置生效:日志、数据库、插件加载
|
|
59
|
+
setLevel(appConfig.log_level || LogLevel.INFO);
|
|
60
|
+
if (appConfig.database) {
|
|
61
|
+
provide(defineDatabaseService(appConfig.database));
|
|
62
|
+
const logTransport = new DatabaseLogTransport(plugin);
|
|
63
|
+
// 直接访问 logger 实例的 transports 数组,或使用非递归方式
|
|
64
|
+
// 检查是否已存在 DatabaseLogTransport(避免重复添加)
|
|
65
|
+
if (!logger['transports'].some((t) => t instanceof DatabaseLogTransport)) {
|
|
66
|
+
logger['transports'].push(logTransport);
|
|
67
|
+
}
|
|
68
|
+
}
|
|
69
|
+
// 4. 启动核心上下文(确保扩展方法可用,比如 addCommand)
|
|
70
|
+
await start();
|
|
71
|
+
// 5. 加载插件(父插件已启动,会自动 start 子插件)
|
|
72
|
+
// 先去重插件列表,避免重复加载
|
|
73
|
+
const pluginNames = new Set(appConfig.plugins || []);
|
|
74
|
+
logger.debug(`Plugin list: ${Array.from(pluginNames).join(', ')}`);
|
|
75
|
+
for (const pluginName of pluginNames) {
|
|
76
|
+
const dir = appConfig.plugin_dirs?.find((dir) => resolveEntry(path.join(dir, pluginName)));
|
|
77
|
+
if (dir) {
|
|
78
|
+
const pluginPath = path.join(process.cwd(), dir, pluginName);
|
|
79
|
+
logger.debug(`Importing plugin: ${pluginName} from ${pluginPath}`);
|
|
80
|
+
await importPlugin(pluginPath);
|
|
81
|
+
}
|
|
82
|
+
}
|
|
83
|
+
logger.debug(`${children.length} plugins loaded`);
|
|
84
|
+
// 6. 优雅关闭(使用 once 防止重复注册)
|
|
85
|
+
const handleSIGTERM = () => {
|
|
86
|
+
logger.info('Received SIGTERM, shutting down gracefully...');
|
|
87
|
+
stop();
|
|
88
|
+
process.exit(0);
|
|
89
|
+
};
|
|
90
|
+
const handleSIGINT = () => {
|
|
91
|
+
logger.info('Received SIGINT, shutting down gracefully...');
|
|
92
|
+
stop();
|
|
93
|
+
process.exit(0);
|
|
94
|
+
};
|
|
95
|
+
// 7. 异常处理
|
|
96
|
+
const handleUncaughtException = (error) => {
|
|
97
|
+
logger.error('Uncaught exception:', error);
|
|
98
|
+
stop();
|
|
99
|
+
process.exit(1);
|
|
100
|
+
};
|
|
101
|
+
const handleUnhandledRejection = (reason) => {
|
|
102
|
+
logger.error('Unhandled rejection:', reason);
|
|
103
|
+
stop();
|
|
104
|
+
process.exit(1);
|
|
105
|
+
};
|
|
106
|
+
// 移除可能存在的旧监听器(防止热重载时累积)
|
|
107
|
+
process.removeListener('SIGTERM', handleSIGTERM);
|
|
108
|
+
process.removeListener('SIGINT', handleSIGINT);
|
|
109
|
+
process.removeListener('uncaughtException', handleUncaughtException);
|
|
110
|
+
process.removeListener('unhandledRejection', handleUnhandledRejection);
|
|
111
|
+
// 注册新监听器
|
|
112
|
+
process.once('SIGTERM', handleSIGTERM);
|
|
113
|
+
process.once('SIGINT', handleSIGINT);
|
|
114
|
+
process.on('uncaughtException', handleUncaughtException);
|
|
115
|
+
process.on('unhandledRejection', handleUnhandledRejection);
|
|
116
|
+
//# sourceMappingURL=setup.js.map
|
package/lib/setup.js.map
ADDED
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"setup.js","sourceRoot":"","sources":["../src/setup.ts"],"names":[],"mappings":"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"}
|
package/lib/types.d.ts
ADDED
|
@@ -0,0 +1,33 @@
|
|
|
1
|
+
import { Bot, DatabaseConfig, Databases } from "@zhin.js/core";
|
|
2
|
+
import { LogLevel } from "@zhin.js/logger";
|
|
3
|
+
/**
|
|
4
|
+
* App配置类型,涵盖机器人、数据库、插件、调试等
|
|
5
|
+
*/
|
|
6
|
+
export interface AppConfig<T extends keyof Databases = keyof Databases> {
|
|
7
|
+
bots?: Bot.Config[];
|
|
8
|
+
log_level: LogLevel;
|
|
9
|
+
/** 数据库配置列表 */
|
|
10
|
+
database?: DatabaseConfig<T>;
|
|
11
|
+
/** 插件目录列表,默认为 ['./plugins', 'node_modules'] */
|
|
12
|
+
plugin_dirs?: string[];
|
|
13
|
+
/** 需要加载的插件列表 */
|
|
14
|
+
plugins?: string[];
|
|
15
|
+
/** 启用的内置服务列表,例如 ['process','config','command','component','permission','cron'] */
|
|
16
|
+
services?: ('process' | 'config' | 'command' | 'component' | 'permission' | 'cron')[];
|
|
17
|
+
/** 禁用的依赖列表 */
|
|
18
|
+
disable_dependencies?: string[];
|
|
19
|
+
/** 是否启用调试模式 */
|
|
20
|
+
debug?: boolean;
|
|
21
|
+
/** 日志配置 */
|
|
22
|
+
log?: {
|
|
23
|
+
/** 最大日志保留天数,默认 7 天 */
|
|
24
|
+
maxDays?: number;
|
|
25
|
+
/** 最大日志条数,默认 10000 条 */
|
|
26
|
+
maxRecords?: number;
|
|
27
|
+
/** 自动清理间隔(小时),默认 24 小时 */
|
|
28
|
+
cleanupInterval?: number;
|
|
29
|
+
};
|
|
30
|
+
/** 插件配置(键为插件名,值为配置对象) */
|
|
31
|
+
[key: string]: any;
|
|
32
|
+
}
|
|
33
|
+
//# sourceMappingURL=types.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"types.d.ts","sourceRoot":"","sources":["../src/types.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,GAAG,EAAE,cAAc,EAAE,SAAS,EAAE,MAAM,eAAe,CAAC;AAC/D,OAAO,EAAE,QAAQ,EAAE,MAAM,iBAAiB,CAAC;AAE3C;;GAEG;AACH,MAAM,WAAW,SAAS,CAAC,CAAC,SAAS,MAAM,SAAS,GAAG,MAAM,SAAS;IACpE,IAAI,CAAC,EAAE,GAAG,CAAC,MAAM,EAAE,CAAC;IACpB,SAAS,EAAE,QAAQ,CAAC;IACpB,cAAc;IACd,QAAQ,CAAC,EAAE,cAAc,CAAC,CAAC,CAAC,CAAC;IAC7B,+CAA+C;IAC/C,WAAW,CAAC,EAAE,MAAM,EAAE,CAAC;IACvB,gBAAgB;IAChB,OAAO,CAAC,EAAE,MAAM,EAAE,CAAC;IACnB,kFAAkF;IAClF,QAAQ,CAAC,EAAE,CAAC,SAAS,GAAG,QAAQ,GAAG,SAAS,GAAG,WAAW,GAAG,YAAY,GAAG,MAAM,CAAC,EAAE,CAAC;IACtF,cAAc;IACd,oBAAoB,CAAC,EAAE,MAAM,EAAE,CAAC;IAChC,eAAe;IACf,KAAK,CAAC,EAAE,OAAO,CAAC;IAChB,WAAW;IACX,GAAG,CAAC,EAAE;QACJ,sBAAsB;QACtB,OAAO,CAAC,EAAE,MAAM,CAAC;QACjB,wBAAwB;QACxB,UAAU,CAAC,EAAE,MAAM,CAAC;QACpB,0BAA0B;QAC1B,eAAe,CAAC,EAAE,MAAM,CAAC;KAC1B,CAAC;IACF,yBAAyB;IACzB,CAAC,GAAG,EAAE,MAAM,GAAG,GAAG,CAAC;CACpB"}
|
package/lib/types.js
ADDED
package/lib/types.js.map
ADDED
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"types.js","sourceRoot":"","sources":["../src/types.ts"],"names":[],"mappings":""}
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "zhin.js",
|
|
3
|
-
"version": "1.0.
|
|
3
|
+
"version": "1.0.18",
|
|
4
4
|
"description": "开箱即用的机器人框架 - 包含进程适配器、HTTP服务、Web控制台和SQLite数据库",
|
|
5
5
|
"type": "module",
|
|
6
6
|
"main": "./lib/index.js",
|
|
@@ -8,27 +8,45 @@
|
|
|
8
8
|
"exports": {
|
|
9
9
|
".": {
|
|
10
10
|
"types": "./lib/index.d.ts",
|
|
11
|
+
"development": "./src/index.ts",
|
|
11
12
|
"import": "./lib/index.js"
|
|
12
13
|
},
|
|
13
14
|
"./jsx": {
|
|
14
15
|
"types": "./lib/jsx.d.ts",
|
|
16
|
+
"development": "./src/jsx.ts",
|
|
15
17
|
"import": "./lib/jsx.js"
|
|
16
18
|
},
|
|
17
19
|
"./jsx-runtime": {
|
|
18
20
|
"types": "./lib/jsx-runtime.d.ts",
|
|
21
|
+
"development": "./src/jsx-runtime.ts",
|
|
19
22
|
"import": "./lib/jsx-runtime.js"
|
|
20
23
|
},
|
|
24
|
+
"./setup": {
|
|
25
|
+
"types": "./lib/setup.d.ts",
|
|
26
|
+
"development": "./src/setup.ts",
|
|
27
|
+
"import": "./lib/setup.js"
|
|
28
|
+
},
|
|
21
29
|
"./jsx-dev-runtime": {
|
|
22
30
|
"types": "./lib/jsx-dev-runtime.d.ts",
|
|
31
|
+
"development": "./src/jsx-dev-runtime.ts",
|
|
23
32
|
"import": "./lib/jsx-dev-runtime.js"
|
|
24
33
|
}
|
|
25
34
|
},
|
|
26
35
|
"dependencies": {
|
|
27
|
-
"
|
|
28
|
-
"
|
|
36
|
+
"yaml": "^2.3.4",
|
|
37
|
+
"dotenv": "^16.3.1",
|
|
38
|
+
"@zhin.js/core": "1.0.18",
|
|
39
|
+
"@zhin.js/schema": "1.0.2",
|
|
40
|
+
"@zhin.js/logger": "0.1.2"
|
|
29
41
|
},
|
|
30
42
|
"devDependencies": {
|
|
31
|
-
"typescript": "^5.3.0"
|
|
43
|
+
"typescript": "^5.3.0",
|
|
44
|
+
"@types/node": "^24.3.0"
|
|
45
|
+
},
|
|
46
|
+
"repository": {
|
|
47
|
+
"type": "git",
|
|
48
|
+
"url": "git+https://github.com/zhinjs/zhin.git",
|
|
49
|
+
"directory": "packages/zhin"
|
|
32
50
|
},
|
|
33
51
|
"scripts": {
|
|
34
52
|
"build": "tsc",
|
package/src/index.ts
CHANGED
|
@@ -4,26 +4,31 @@
|
|
|
4
4
|
|
|
5
5
|
// 导出核心框架
|
|
6
6
|
export * from '@zhin.js/core'
|
|
7
|
-
import logger from '@zhin.js/logger'
|
|
8
|
-
import { AppConfig, App, Config } from '@zhin.js/core'
|
|
9
|
-
import path from 'path'
|
|
10
|
-
import fs from 'fs'
|
|
11
|
-
|
|
12
7
|
// 重新导出 logger(作为独立的工具)
|
|
13
8
|
export { default as logger } from '@zhin.js/logger'
|
|
14
9
|
|
|
15
|
-
|
|
16
|
-
|
|
17
|
-
|
|
18
|
-
|
|
19
|
-
|
|
20
|
-
|
|
10
|
+
// ================================================================================================
|
|
11
|
+
// 模块声明 - 允许插件通过 declare module "zhin.js" 扩展类型
|
|
12
|
+
// ================================================================================================
|
|
13
|
+
// 重新声明可扩展接口,使得 declare module "zhin.js" 可以正确合并类型
|
|
14
|
+
// 这些接口会与 @zhin.js/core 中的原始定义合并
|
|
15
|
+
declare module "zhin.js" {
|
|
16
|
+
// 重新导出 Plugin 命名空间中的可扩展接口
|
|
17
|
+
namespace Plugin {
|
|
18
|
+
// 可扩展的 Context 注册表
|
|
19
|
+
interface Contexts {}
|
|
20
|
+
// 扩展方法
|
|
21
|
+
interface Extensions {
|
|
22
|
+
/**
|
|
23
|
+
* 定义数据库模型
|
|
24
|
+
* @param name 模型名称
|
|
25
|
+
* @param definition 模型定义
|
|
26
|
+
*/
|
|
27
|
+
defineModel<K extends keyof Models>(name: K, definition: import('@zhin.js/core').Definition<Models[K]>): void;
|
|
21
28
|
}
|
|
22
|
-
|
|
23
|
-
|
|
24
|
-
|
|
25
|
-
|
|
26
|
-
|
|
27
|
-
|
|
28
|
-
return app
|
|
29
|
-
}
|
|
29
|
+
}
|
|
30
|
+
// 可扩展的适配器注册表
|
|
31
|
+
interface RegisteredAdapters {}
|
|
32
|
+
// 可扩展的数据模型注册表
|
|
33
|
+
interface Models {}
|
|
34
|
+
}
|
|
@@ -0,0 +1,167 @@
|
|
|
1
|
+
import { LogTransport } from '@zhin.js/logger'
|
|
2
|
+
import { Plugin } from '@zhin.js/core'
|
|
3
|
+
import { AppConfig } from './types.js'
|
|
4
|
+
|
|
5
|
+
/**
|
|
6
|
+
* 数据库日志传输器
|
|
7
|
+
* 将日志存储到数据库,并自动清理旧日志
|
|
8
|
+
*/
|
|
9
|
+
export class DatabaseLogTransport implements LogTransport {
|
|
10
|
+
private plugin: Plugin
|
|
11
|
+
private stripAnsiRegex = /\x1b\[[0-9;]*m/g
|
|
12
|
+
private cleanupTimer?: NodeJS.Timeout
|
|
13
|
+
private maxDays: number
|
|
14
|
+
private maxRecords: number
|
|
15
|
+
private cleanupInterval: number
|
|
16
|
+
|
|
17
|
+
constructor(plugin: Plugin) {
|
|
18
|
+
this.plugin = plugin
|
|
19
|
+
const configService = plugin.inject('config')!
|
|
20
|
+
const appConfig = configService.get<AppConfig>('zhin.config.yml')
|
|
21
|
+
// 从配置读取日志清理策略
|
|
22
|
+
const logConfig = appConfig.log || {}
|
|
23
|
+
this.maxDays = logConfig.maxDays || 7 // 默认保留 7 天
|
|
24
|
+
this.maxRecords = logConfig.maxRecords || 10000 // 默认最多 10000 条
|
|
25
|
+
this.cleanupInterval = logConfig.cleanupInterval || 24 // 默认 24 小时清理一次
|
|
26
|
+
|
|
27
|
+
// 启动定时清理
|
|
28
|
+
this.startCleanup()
|
|
29
|
+
|
|
30
|
+
// 插件销毁时停止清理任务
|
|
31
|
+
plugin.onDispose(() => this.stopCleanup())
|
|
32
|
+
}
|
|
33
|
+
|
|
34
|
+
/**
|
|
35
|
+
* 启动定时清理任务
|
|
36
|
+
*/
|
|
37
|
+
private startCleanup(): void {
|
|
38
|
+
// 立即执行一次清理
|
|
39
|
+
this.cleanupOldLogs().catch(err => {
|
|
40
|
+
this.plugin.logger.error('[DatabaseLogTransport] Initial cleanup failed:', err.message)
|
|
41
|
+
})
|
|
42
|
+
|
|
43
|
+
// 设置定时任务
|
|
44
|
+
this.cleanupTimer = setInterval(() => {
|
|
45
|
+
this.cleanupOldLogs().catch(err => {
|
|
46
|
+
this.plugin.logger.error('[DatabaseLogTransport] Scheduled cleanup failed:', err.message)
|
|
47
|
+
})
|
|
48
|
+
}, this.cleanupInterval * 60 * 60 * 1000) // 转换为毫秒
|
|
49
|
+
}
|
|
50
|
+
|
|
51
|
+
/**
|
|
52
|
+
* 清理旧日志
|
|
53
|
+
*/
|
|
54
|
+
private async cleanupOldLogs(): Promise<void> {
|
|
55
|
+
const db=this.plugin.inject('database')
|
|
56
|
+
if (!db) {
|
|
57
|
+
return
|
|
58
|
+
}
|
|
59
|
+
|
|
60
|
+
try {
|
|
61
|
+
const LogModel = db.models.get('SystemLog')
|
|
62
|
+
if (!LogModel) {
|
|
63
|
+
return
|
|
64
|
+
}
|
|
65
|
+
|
|
66
|
+
// 1. 按时间清理:删除超过 maxDays 天的日志
|
|
67
|
+
const cutoffDate = new Date()
|
|
68
|
+
cutoffDate.setDate(cutoffDate.getDate() - this.maxDays)
|
|
69
|
+
|
|
70
|
+
const deletedData = await LogModel
|
|
71
|
+
.delete({ timestamp: { $lt: cutoffDate } })
|
|
72
|
+
const deletedCount = typeof deletedData === 'number' ? deletedData : deletedData.length
|
|
73
|
+
// 2. 按数量清理:如果日志总数超过 maxRecords,删除最旧的
|
|
74
|
+
const total = await LogModel.select()
|
|
75
|
+
const totalCount = total.length
|
|
76
|
+
|
|
77
|
+
if (totalCount > this.maxRecords) {
|
|
78
|
+
const excessCount = totalCount - this.maxRecords
|
|
79
|
+
|
|
80
|
+
// 查找最旧的 excessCount 条日志的 ID
|
|
81
|
+
const oldestLogs: any[] = await LogModel.select('id','timestamp').orderBy('timestamp', 'ASC').limit(excessCount)
|
|
82
|
+
|
|
83
|
+
const idsToDelete = oldestLogs.map((log: any) => log.id)
|
|
84
|
+
|
|
85
|
+
if (idsToDelete.length > 0) {
|
|
86
|
+
await LogModel
|
|
87
|
+
.delete({ id: { $in: idsToDelete } })
|
|
88
|
+
}
|
|
89
|
+
}
|
|
90
|
+
|
|
91
|
+
this.plugin.logger.info(
|
|
92
|
+
`[DatabaseLogTransport] Log cleanup completed. ` +
|
|
93
|
+
`Deleted ${deletedData || 0} logs older than ${this.maxDays} days. ` +
|
|
94
|
+
`Current total: ${Math.max(0, totalCount - (deletedCount || 0))} logs.`
|
|
95
|
+
)
|
|
96
|
+
} catch (error) {
|
|
97
|
+
// 静默处理错误
|
|
98
|
+
this.plugin.logger.debug('[DatabaseLogTransport] Cleanup error:', (error as Error).message,(error as Error).stack)
|
|
99
|
+
}
|
|
100
|
+
}
|
|
101
|
+
|
|
102
|
+
/**
|
|
103
|
+
* 停止清理任务
|
|
104
|
+
*/
|
|
105
|
+
public stopCleanup(): void {
|
|
106
|
+
if (this.cleanupTimer) {
|
|
107
|
+
clearInterval(this.cleanupTimer)
|
|
108
|
+
this.cleanupTimer = undefined
|
|
109
|
+
}
|
|
110
|
+
}
|
|
111
|
+
|
|
112
|
+
/**
|
|
113
|
+
* 移除 ANSI 颜色代码
|
|
114
|
+
*/
|
|
115
|
+
private stripAnsi(str: string): string {
|
|
116
|
+
return str.replace(this.stripAnsiRegex, '')
|
|
117
|
+
}
|
|
118
|
+
|
|
119
|
+
write(message: string): void {
|
|
120
|
+
// 移除 ANSI 颜色代码
|
|
121
|
+
const cleanMessage = this.stripAnsi(message)
|
|
122
|
+
|
|
123
|
+
// 解析日志消息
|
|
124
|
+
// 格式: [09-08 04:07:55.852] [INFO] [MyApp]: message
|
|
125
|
+
const logRegex = /\[[\d-]+ [\d:.]+\] \[(\w+)\] \[([^\]]+)\]: ([\s\S]+)/
|
|
126
|
+
const match = cleanMessage.match(logRegex)
|
|
127
|
+
|
|
128
|
+
if (match) {
|
|
129
|
+
const [, level, name, msg] = match
|
|
130
|
+
const source = name.split(':')[0] // 取第一部分作为 source
|
|
131
|
+
|
|
132
|
+
// 异步存储到数据库,不阻塞日志输出
|
|
133
|
+
this.saveToDatabase(level.toLowerCase(), name, msg.trim(), source).catch(err => {
|
|
134
|
+
// 避免日志存储失败导致应用崩溃
|
|
135
|
+
console.error('[DatabaseLogTransport] Failed to save log:', err.message)
|
|
136
|
+
})
|
|
137
|
+
}
|
|
138
|
+
}
|
|
139
|
+
|
|
140
|
+
/**
|
|
141
|
+
* 保存日志到数据库
|
|
142
|
+
*/
|
|
143
|
+
private async saveToDatabase(level: string, name: string, message: string, source: string): Promise<void> {
|
|
144
|
+
const databaseService=this.plugin.inject('database')
|
|
145
|
+
if (!databaseService) {
|
|
146
|
+
return // 没有数据库则跳过
|
|
147
|
+
}
|
|
148
|
+
|
|
149
|
+
try {
|
|
150
|
+
const LogModel = databaseService.models.get('SystemLog')
|
|
151
|
+
if (!LogModel) {
|
|
152
|
+
return // 模型不存在则跳过
|
|
153
|
+
}
|
|
154
|
+
|
|
155
|
+
await LogModel.insert({
|
|
156
|
+
level,
|
|
157
|
+
name,
|
|
158
|
+
message,
|
|
159
|
+
source,
|
|
160
|
+
timestamp: new Date()
|
|
161
|
+
})
|
|
162
|
+
} catch (error) {
|
|
163
|
+
// 静默处理错误,避免干扰主流程
|
|
164
|
+
}
|
|
165
|
+
}
|
|
166
|
+
}
|
|
167
|
+
|
package/src/setup.ts
ADDED
|
@@ -0,0 +1,145 @@
|
|
|
1
|
+
|
|
2
|
+
import { setLevel, LogLevel } from '@zhin.js/logger';
|
|
3
|
+
import {
|
|
4
|
+
usePlugin, resolveEntry,
|
|
5
|
+
ConfigService, PermissionService,
|
|
6
|
+
createCommandService, createComponentService, createCronService,
|
|
7
|
+
defineDatabaseService,
|
|
8
|
+
ProcessAdapter,
|
|
9
|
+
} from '@zhin.js/core';
|
|
10
|
+
import { AppConfig } from './types.js';
|
|
11
|
+
import { DatabaseLogTransport } from './log-transport.js';
|
|
12
|
+
import * as path from 'path';
|
|
13
|
+
|
|
14
|
+
const plugin = usePlugin();
|
|
15
|
+
const {
|
|
16
|
+
provide,
|
|
17
|
+
start, stop,
|
|
18
|
+
logger, children,
|
|
19
|
+
import: importPlugin } = plugin;
|
|
20
|
+
|
|
21
|
+
// 1. 先加载配置,用户在 zhin.config 中决定启用哪些服务
|
|
22
|
+
const configService = new ConfigService();
|
|
23
|
+
await configService.load('zhin.config.yml', {
|
|
24
|
+
log_level: LogLevel.INFO,
|
|
25
|
+
database: {
|
|
26
|
+
dialect: "sqlite",
|
|
27
|
+
filename: "./data/test.db"
|
|
28
|
+
},
|
|
29
|
+
plugin_dirs: [path.relative(process.cwd(), import.meta.dirname), 'node_modules/@zhin.js', './plugins',],
|
|
30
|
+
plugins: [],
|
|
31
|
+
services: ['process', 'config', 'command', 'component', 'permission', 'cron'],
|
|
32
|
+
});
|
|
33
|
+
const appConfig = configService.get<AppConfig>('zhin.config.yml');
|
|
34
|
+
const enabledServices = new Set(appConfig.services || ['process', 'config', 'command', 'component', 'permission', 'cron']);
|
|
35
|
+
|
|
36
|
+
// 注册配置服务(必须)
|
|
37
|
+
provide({
|
|
38
|
+
name: 'config',
|
|
39
|
+
description: '配置服务',
|
|
40
|
+
value: configService,
|
|
41
|
+
});
|
|
42
|
+
|
|
43
|
+
// 2. 可选服务按配置注册
|
|
44
|
+
if (enabledServices.has('process')) {
|
|
45
|
+
provide({
|
|
46
|
+
name: 'process',
|
|
47
|
+
description: '命令行适配器',
|
|
48
|
+
mounted: async (plugin) => {
|
|
49
|
+
const adapter = new ProcessAdapter(plugin);
|
|
50
|
+
await adapter.start();
|
|
51
|
+
return adapter;
|
|
52
|
+
},
|
|
53
|
+
dispose: async (adapter) => {
|
|
54
|
+
await adapter.stop();
|
|
55
|
+
}
|
|
56
|
+
});
|
|
57
|
+
}
|
|
58
|
+
|
|
59
|
+
if (enabledServices.has('command')) {
|
|
60
|
+
provide(createCommandService());
|
|
61
|
+
}
|
|
62
|
+
|
|
63
|
+
if (enabledServices.has('component')) {
|
|
64
|
+
provide(createComponentService());
|
|
65
|
+
}
|
|
66
|
+
|
|
67
|
+
if (enabledServices.has('permission')) {
|
|
68
|
+
provide({
|
|
69
|
+
name: 'permission',
|
|
70
|
+
description: '权限管理',
|
|
71
|
+
value: new PermissionService(),
|
|
72
|
+
});
|
|
73
|
+
}
|
|
74
|
+
|
|
75
|
+
if (enabledServices.has('cron')) {
|
|
76
|
+
provide(createCronService());
|
|
77
|
+
}
|
|
78
|
+
|
|
79
|
+
// 3. 配置生效:日志、数据库、插件加载
|
|
80
|
+
setLevel(appConfig.log_level || LogLevel.INFO);
|
|
81
|
+
|
|
82
|
+
if (appConfig.database) {
|
|
83
|
+
provide(defineDatabaseService(appConfig.database));
|
|
84
|
+
const logTransport = new DatabaseLogTransport(plugin);
|
|
85
|
+
// 直接访问 logger 实例的 transports 数组,或使用非递归方式
|
|
86
|
+
// 检查是否已存在 DatabaseLogTransport(避免重复添加)
|
|
87
|
+
if (!logger['transports'].some((t: any) => t instanceof DatabaseLogTransport)) {
|
|
88
|
+
logger['transports'].push(logTransport);
|
|
89
|
+
}
|
|
90
|
+
}
|
|
91
|
+
|
|
92
|
+
// 4. 启动核心上下文(确保扩展方法可用,比如 addCommand)
|
|
93
|
+
await start();
|
|
94
|
+
|
|
95
|
+
// 5. 加载插件(父插件已启动,会自动 start 子插件)
|
|
96
|
+
// 先去重插件列表,避免重复加载
|
|
97
|
+
const pluginNames = new Set(appConfig.plugins || []);
|
|
98
|
+
logger.debug(`Plugin list: ${Array.from(pluginNames).join(', ')}`);
|
|
99
|
+
for (const pluginName of pluginNames) {
|
|
100
|
+
const dir = appConfig.plugin_dirs?.find((dir: string) => resolveEntry(path.join(dir, pluginName)));
|
|
101
|
+
if (dir) {
|
|
102
|
+
const pluginPath = path.join(process.cwd(), dir, pluginName);
|
|
103
|
+
logger.debug(`Importing plugin: ${pluginName} from ${pluginPath}`);
|
|
104
|
+
await importPlugin(pluginPath);
|
|
105
|
+
}
|
|
106
|
+
}
|
|
107
|
+
logger.debug(`${children.length} plugins loaded`);
|
|
108
|
+
|
|
109
|
+
// 6. 优雅关闭(使用 once 防止重复注册)
|
|
110
|
+
const handleSIGTERM = () => {
|
|
111
|
+
logger.info('Received SIGTERM, shutting down gracefully...');
|
|
112
|
+
stop();
|
|
113
|
+
process.exit(0);
|
|
114
|
+
};
|
|
115
|
+
|
|
116
|
+
const handleSIGINT = () => {
|
|
117
|
+
logger.info('Received SIGINT, shutting down gracefully...');
|
|
118
|
+
stop();
|
|
119
|
+
process.exit(0);
|
|
120
|
+
};
|
|
121
|
+
|
|
122
|
+
// 7. 异常处理
|
|
123
|
+
const handleUncaughtException = (error: Error) => {
|
|
124
|
+
logger.error('Uncaught exception:', error);
|
|
125
|
+
stop();
|
|
126
|
+
process.exit(1);
|
|
127
|
+
};
|
|
128
|
+
|
|
129
|
+
const handleUnhandledRejection = (reason: unknown) => {
|
|
130
|
+
logger.error('Unhandled rejection:', reason);
|
|
131
|
+
stop();
|
|
132
|
+
process.exit(1);
|
|
133
|
+
};
|
|
134
|
+
|
|
135
|
+
// 移除可能存在的旧监听器(防止热重载时累积)
|
|
136
|
+
process.removeListener('SIGTERM', handleSIGTERM);
|
|
137
|
+
process.removeListener('SIGINT', handleSIGINT);
|
|
138
|
+
process.removeListener('uncaughtException', handleUncaughtException);
|
|
139
|
+
process.removeListener('unhandledRejection', handleUnhandledRejection);
|
|
140
|
+
|
|
141
|
+
// 注册新监听器
|
|
142
|
+
process.once('SIGTERM', handleSIGTERM);
|
|
143
|
+
process.once('SIGINT', handleSIGINT);
|
|
144
|
+
process.on('uncaughtException', handleUncaughtException);
|
|
145
|
+
process.on('unhandledRejection', handleUnhandledRejection);
|
package/src/types.ts
ADDED
|
@@ -0,0 +1,34 @@
|
|
|
1
|
+
import { Bot, DatabaseConfig, Databases } from "@zhin.js/core";
|
|
2
|
+
import { LogLevel } from "@zhin.js/logger";
|
|
3
|
+
|
|
4
|
+
/**
|
|
5
|
+
* App配置类型,涵盖机器人、数据库、插件、调试等
|
|
6
|
+
*/
|
|
7
|
+
export interface AppConfig<T extends keyof Databases = keyof Databases> {
|
|
8
|
+
bots?: Bot.Config[];
|
|
9
|
+
log_level: LogLevel;
|
|
10
|
+
/** 数据库配置列表 */
|
|
11
|
+
database?: DatabaseConfig<T>;
|
|
12
|
+
/** 插件目录列表,默认为 ['./plugins', 'node_modules'] */
|
|
13
|
+
plugin_dirs?: string[];
|
|
14
|
+
/** 需要加载的插件列表 */
|
|
15
|
+
plugins?: string[];
|
|
16
|
+
/** 启用的内置服务列表,例如 ['process','config','command','component','permission','cron'] */
|
|
17
|
+
services?: ('process' | 'config' | 'command' | 'component' | 'permission' | 'cron')[];
|
|
18
|
+
/** 禁用的依赖列表 */
|
|
19
|
+
disable_dependencies?: string[];
|
|
20
|
+
/** 是否启用调试模式 */
|
|
21
|
+
debug?: boolean;
|
|
22
|
+
/** 日志配置 */
|
|
23
|
+
log?: {
|
|
24
|
+
/** 最大日志保留天数,默认 7 天 */
|
|
25
|
+
maxDays?: number;
|
|
26
|
+
/** 最大日志条数,默认 10000 条 */
|
|
27
|
+
maxRecords?: number;
|
|
28
|
+
/** 自动清理间隔(小时),默认 24 小时 */
|
|
29
|
+
cleanupInterval?: number;
|
|
30
|
+
};
|
|
31
|
+
/** 插件配置(键为插件名,值为配置对象) */
|
|
32
|
+
[key: string]: any;
|
|
33
|
+
}
|
|
34
|
+
|