Skip to content

Commit f319fee

Browse files
为备份与重载插件补充资源清理逻辑
Ultraworked with [Sisyphus](https://github.com/code-yeongyu/oh-my-openagent) Co-authored-by: Sisyphus <clio-agent@sisyphuslabs.ai>
1 parent 3f43cf2 commit f319fee

File tree

2 files changed

+54
-12
lines changed

2 files changed

+54
-12
lines changed

src/plugin/bf.ts

Lines changed: 31 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -6,13 +6,26 @@ import * as fs from "fs";
66
import * as path from "path";
77
import * as crypto from "crypto";
88
import * as os from "os";
9-
import { spawn } from "child_process";
9+
import { spawn, type ChildProcess } from "child_process";
1010
import { Low } from "lowdb";
1111
import { JSONFile } from "lowdb/node";
1212
import { getPrefixes } from "@utils/pluginManager";
1313

1414
const prefixes = getPrefixes();
1515
const mainPrefix = prefixes[0];
16+
const activeChildProcesses = new Set<ChildProcess>();
17+
18+
function trackChildProcess<T extends ChildProcess>(child: T): T {
19+
const release = () => {
20+
activeChildProcesses.delete(child);
21+
};
22+
23+
activeChildProcesses.add(child);
24+
child.once("close", release);
25+
child.once("exit", release);
26+
child.once("error", release);
27+
return child;
28+
}
1629

1730
// 时区设置
1831
const CN_TIME_ZONE = "Asia/Shanghai";
@@ -128,6 +141,10 @@ class ConfigManager {
128141
await this.setTargets(filtered);
129142
return filtered;
130143
}
144+
145+
static cleanup(): void {
146+
this.db = null;
147+
}
131148
}
132149

133150
// 工具函数
@@ -169,13 +186,13 @@ async function createBackup(dirs: string[], outputPath: string): Promise<void> {
169186

170187
// 创建tar.gz
171188
await new Promise<void>((resolve, reject) => {
172-
const tar = spawn("tar", [
189+
const tar = trackChildProcess(spawn("tar", [
173190
"-czf",
174191
outputPath,
175192
"-C",
176193
tempDir,
177194
"telebox_backup",
178-
]);
195+
]));
179196

180197
tar.on("close", (code) => {
181198
if (code === 0) resolve();
@@ -216,7 +233,7 @@ async function extractBackup(archivePath: string): Promise<string> {
216233
fs.mkdirSync(extractDir, { recursive: true });
217234

218235
await new Promise<void>((resolve, reject) => {
219-
const tar = spawn("tar", ["-xzf", archivePath, "-C", extractDir]);
236+
const tar = trackChildProcess(spawn("tar", ["-xzf", archivePath, "-C", extractDir]));
220237

221238
tar.on("close", (code) => {
222239
if (code === 0) resolve();
@@ -279,6 +296,13 @@ const help_text = `<code>${mainPrefix}bf</code> 备份 plugins + assets 目录
279296
// 插件类
280297
class BfPlugin extends Plugin {
281298
cleanup(): void {
299+
for (const child of activeChildProcesses) {
300+
try {
301+
child.kill("SIGTERM");
302+
} catch {}
303+
}
304+
activeChildProcesses.clear();
305+
ConfigManager.cleanup();
282306
}
283307

284308
description = `\n📦 备份插件\n\n${help_text}
@@ -404,7 +428,7 @@ class BfPlugin extends Plugin {
404428
const dirName = path.basename(programDir);
405429

406430
await new Promise<void>((resolve, reject) => {
407-
const tar = spawn("tar", [
431+
const tar = trackChildProcess(spawn("tar", [
408432
"-cf",
409433
"-",
410434
"-C",
@@ -415,9 +439,9 @@ class BfPlugin extends Plugin {
415439
"--exclude=temp",
416440
"--exclude=logs",
417441
dirName,
418-
], { stdio: ["pipe", "pipe", "pipe"] });
442+
], { stdio: ["pipe", "pipe", "pipe"] }));
419443

420-
const gzip = spawn("gzip", ["-1"], { stdio: ["pipe", "pipe", "pipe"] });
444+
const gzip = trackChildProcess(spawn("gzip", ["-1"], { stdio: ["pipe", "pipe", "pipe"] }));
421445

422446
const output = fs.createWriteStream(backupPath);
423447

src/plugin/reload.ts

Lines changed: 23 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -27,6 +27,7 @@ const exitDir = createDirectoryInTemp("exit");
2727
const exitFile = path.join(exitDir, "msg.json");
2828
const assetsDir = createDirectoryInAssets("reload");
2929
const configPath = path.join(assetsDir, "config.json");
30+
const pendingExitTimers = new Set<ReturnType<typeof setTimeout>>();
3031

3132
interface ReloadConfig {
3233
leakfixEnabled: boolean;
@@ -43,6 +44,19 @@ async function initConfig() {
4344
return db;
4445
}
4546

47+
function scheduleTrackedTimeout(
48+
callback: () => void | Promise<void>,
49+
delay: number
50+
): ReturnType<typeof setTimeout> {
51+
let timer: ReturnType<typeof setTimeout>;
52+
timer = setTimeout(() => {
53+
pendingExitTimers.delete(timer);
54+
void Promise.resolve(callback());
55+
}, delay);
56+
pendingExitTimers.add(timer);
57+
return timer;
58+
}
59+
4660
const editExitMsg = async () => {
4761
try {
4862
const data = fs.readFileSync(exitFile, "utf-8");
@@ -134,9 +148,9 @@ async function memoryMonitorTask() {
134148
`正在重启 TeleBox...`,
135149
parseMode: "html"
136150
});
137-
setTimeout(() => process.exit(0), 1000);
151+
scheduleTrackedTimeout(() => process.exit(0), 1000);
138152
} else if (client) {
139-
setTimeout(() => process.exit(0), 1000);
153+
scheduleTrackedTimeout(() => process.exit(0), 1000);
140154
}
141155
} else {
142156
console.log(`[Memory Monitor] 内存使用 ${memory.heapUsed.toFixed(2)}MB / ${threshold}MB,正常`);
@@ -166,7 +180,11 @@ const HELP_TEXT = `🔄 Reload - 插件重载与内存管理
166180

167181
class ReloadPlugin extends Plugin {
168182
cleanup(): void {
169-
// 当前插件不持有需要在 reload 时额外释放的长期资源。
183+
for (const timer of pendingExitTimers) {
184+
clearTimeout(timer);
185+
}
186+
pendingExitTimers.clear();
187+
this.lastReloadMemory = null;
170188
}
171189

172190
description = HELP_TEXT;
@@ -218,7 +236,7 @@ class ReloadPlugin extends Plugin {
218236

219237
pmr: async (msg) => {
220238
await msg.delete();
221-
setTimeout(async () => {
239+
scheduleTrackedTimeout(async () => {
222240
try {
223241
await execAsync("pm2 restart telebox");
224242
} catch (error) {
@@ -348,4 +366,4 @@ class ReloadPlugin extends Plugin {
348366
};
349367
}
350368

351-
export default new ReloadPlugin();
369+
export default new ReloadPlugin();

0 commit comments

Comments
 (0)