mirror of
https://gitee.com/spark-store-project/spark-store
synced 2026-04-26 09:20:18 +08:00
新增更新中心模块,支持管理 APM 和传统 deb 软件更新任务 - 添加更新任务队列管理、状态跟踪和日志记录功能 - 实现更新项忽略配置持久化存储 - 新增更新确认对话框和迁移提示 - 优化主窗口关闭时的任务保护机制 - 添加单元测试覆盖核心逻辑
88 lines
2.0 KiB
TypeScript
88 lines
2.0 KiB
TypeScript
import { mkdir } from "node:fs/promises";
|
|
import { join } from "node:path";
|
|
import { spawn } from "node:child_process";
|
|
|
|
import type { UpdateCenterItem } from "./types";
|
|
|
|
export interface Aria2DownloadResult {
|
|
filePath: string;
|
|
}
|
|
|
|
export interface RunAria2DownloadOptions {
|
|
item: UpdateCenterItem;
|
|
downloadDir: string;
|
|
onProgress?: (progress: number) => void;
|
|
onLog?: (message: string) => void;
|
|
signal?: AbortSignal;
|
|
}
|
|
|
|
const PROGRESS_PATTERN = /(\d{1,3}(?:\.\d+)?)%/;
|
|
|
|
export const runAria2Download = async ({
|
|
item,
|
|
downloadDir,
|
|
onProgress,
|
|
onLog,
|
|
signal,
|
|
}: RunAria2DownloadOptions): Promise<Aria2DownloadResult> => {
|
|
if (!item.downloadUrl || !item.fileName) {
|
|
throw new Error(`Missing download metadata for ${item.pkgname}`);
|
|
}
|
|
|
|
await mkdir(downloadDir, { recursive: true });
|
|
|
|
const filePath = join(downloadDir, item.fileName);
|
|
|
|
await new Promise<void>((resolve, reject) => {
|
|
const child = spawn("aria2c", [
|
|
"--dir",
|
|
downloadDir,
|
|
"--out",
|
|
item.fileName,
|
|
item.downloadUrl,
|
|
]);
|
|
|
|
const abortDownload = () => {
|
|
child.kill();
|
|
reject(new Error(`Update task cancelled: ${item.pkgname}`));
|
|
};
|
|
|
|
if (signal?.aborted) {
|
|
abortDownload();
|
|
return;
|
|
}
|
|
|
|
signal?.addEventListener("abort", abortDownload, { once: true });
|
|
|
|
const handleOutput = (chunk: Buffer) => {
|
|
const message = chunk.toString().trim();
|
|
if (!message) {
|
|
return;
|
|
}
|
|
|
|
onLog?.(message);
|
|
const progressMatch = message.match(PROGRESS_PATTERN);
|
|
if (progressMatch) {
|
|
onProgress?.(Number(progressMatch[1]));
|
|
}
|
|
};
|
|
|
|
child.stdout?.on("data", handleOutput);
|
|
child.stderr?.on("data", handleOutput);
|
|
child.on("error", reject);
|
|
child.on("close", (code) => {
|
|
signal?.removeEventListener("abort", abortDownload);
|
|
if (code === 0) {
|
|
resolve();
|
|
return;
|
|
}
|
|
|
|
reject(new Error(`aria2c exited with code ${code ?? -1}`));
|
|
});
|
|
});
|
|
|
|
onProgress?.(100);
|
|
|
|
return { filePath };
|
|
};
|