Compare commits

...

13 Commits

Author SHA1 Message Date
shenmo7192 e16acbd0a5 refactor(installer): 调整下载重试超时配置和次数
更新了下载重试的超时时间列表和总重试次数,从原3次调整为10次,优化下载成功率
2026-05-13 21:13:25 +08:00
shenmo7192 8a5f8d154f feat: 添加APM安装确认弹窗并重构APM检查流程
1. 新增全局状态控制APM安装弹窗显示
2. 新建ApmInstallConfirmModal弹窗组件
3. 将主进程的APM安装弹窗逻辑迁移到前端Vue组件
4. 更新package.json版本到5.1.0
5. 简化安装和升级流程中的APM检查逻辑
2026-05-12 21:54:47 +08:00
shenmo7192 8c8b53fc29 update tool/apt-fast/ss-apt-fast.
Signed-off-by: shenmo <jifengshenmo@outlook.com>
2026-05-09 15:04:46 +00:00
shenmo7192 c50655c106 !389 dark下的效果还是怪怪的重新改一下,还有统一应用管理和软件更新的按钮样式
Merge pull request !389 from zeqi/Erotica
2026-05-03 04:02:34 +00:00
zeqi 4b37aa4da4 dark下的效果还是怪怪的重新改一下,还有统一应用管理和软件更新的按钮样式
Signed-off-by: zeqi <a202128502@163.com>
2026-05-03 02:35:28 +00:00
shenmo7192 ce5de692f7 修复 Ubuntu 26.04 上无法正常安装的问题
Signed-off-by: shenmo <jifengshenmo@outlook.com>
2026-04-30 15:29:13 +00:00
shenmo7192 3d4af0c492 改为5.0.1 2026-04-25 14:40:37 +08:00
shenmo7192 c39b25e393 5.1 2026-04-25 14:33:21 +08:00
shenmo7192 2086152aa5 refactor: 移除缓存清除函数并直接使用原始URL
移除cacheBuster函数及其所有调用,改为直接使用原始URL进行请求
2026-04-25 14:25:52 +08:00
shenmo7192 f8f112a782 !388 feat(build): add loong64 to build
Merge pull request !388 from AAA Elysia 猫猫侠 ⁧~喵/elysia/add-loong64
2026-04-21 14:04:55 +00:00
Elysia 6a9091b2ec feat(build): add loong64
- Downgrad electron for the sake of loong64
- Add my project to CREDIT.md

Signed-off-by: Elysia <a.elysia@proton.me>
2026-04-19 09:37:21 +08:00
shenmo7192 994dbaf9b9 !387 优化dark模式下应用管理的关闭按钮和刷新按钮、软件更新的关闭按钮的hover效果
Merge pull request !387 from zeqi/Erotica
2026-04-16 22:59:56 +00:00
zeqi 9c9f0b6076 优化dark模式下应用管理的关闭按钮和刷新按钮、软件更新的关闭按钮的hover效果
Signed-off-by: zeqi <a202128502@163.com>
2026-04-16 15:27:55 +00:00
19 changed files with 385 additions and 310 deletions
+20
View File
@@ -23,3 +23,23 @@
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
SOFTWARE. SOFTWARE.
2. https://github.com/elysia-best/apm-app-store MulanPSL-2.0
Copyright (c) 2026-present The Spark Project Contributors
apm-store is licensed under Mulan PSL v2.
You can use this software according to the terms and conditions of the Mulan PSL v2.
You may obtain a copy of Mulan PSL v2 at:
http://license.coscl.org.cn/MulanPSL2
THIS SOFTWARE IS PROVIDED ON AN "AS IS" BASIS, WITHOUT WARRANTIES OF ANY KIND,
EITHER EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO NON-INFRINGEMENT,
MERCHANTABILITY OR FIT FOR A PARTICULAR PURPOSE.
See the Mulan PSL v2 for more details.
+8 -105
View File
@@ -1,4 +1,4 @@
import { BrowserWindow, dialog, ipcMain, WebContents } from "electron"; import { ipcMain, WebContents } from "electron";
import { spawn, ChildProcess, exec } from "node:child_process"; import { spawn, ChildProcess, exec } from "node:child_process";
import { promisify } from "node:util"; import { promisify } from "node:util";
import fs from "node:fs"; import fs from "node:fs";
@@ -114,25 +114,6 @@ const checkSparkAvailable = async (): Promise<boolean> => {
return found; return found;
}; };
/** 提权执行 shell-caller aptss install apm 安装 APM,安装后需用户重启电脑 */
const runInstallApm = async (superUserCmd: string): Promise<boolean> => {
const execCommand = superUserCmd || SHELL_CALLER_PATH;
const execParams = superUserCmd
? [SHELL_CALLER_PATH, "aptss", "install", "apm"]
: [SHELL_CALLER_PATH, "aptss", "install", "apm"];
logger.info(`执行安装 APM: ${execCommand} ${execParams.join(" ")}`);
const { code, stdout, stderr } = await runCommandCapture(
execCommand,
execParams,
);
if (code !== 0) {
logger.error({ code, stdout, stderr }, "安装 APM 失败");
return false;
}
logger.info("安装 APM 完成");
return true;
};
const parseUpgradableList = (output: string) => { const parseUpgradableList = (output: string) => {
const apps: Array<{ const apps: Array<{
pkgname: string; pkgname: string;
@@ -215,62 +196,24 @@ ipcMain.on("queue-install", async (event, download_json) => {
const execParams = []; const execParams = [];
const downloadDir = `/tmp/spark-store/download/${pkgname}`; const downloadDir = `/tmp/spark-store/download/${pkgname}`;
// APM 应用:若本机没有 apm 命令,弹窗提示并可选提权安装 APM(安装后需重启电脑) // APM 应用:若本机没有 apm 命令,通知前端弹窗引导安装 APM
if (origin === "apm") { if (origin === "apm") {
const hasApm = await checkApmAvailable(); const hasApm = await checkApmAvailable();
if (!hasApm) { if (!hasApm) {
const win = BrowserWindow.fromWebContents(webContents); webContents.send("trigger-apm-install-dialog");
const { response } = await dialog.showMessageBox(win ?? undefined, {
type: "question",
title: "需要安装 APM",
message: "此应用需要使用 APM 安装。",
detail:
"APM是星火应用商店的容器包管理器,安装APM后方可安装此应用,是否确认安装?",
buttons: ["确认", "取消"],
defaultId: 0,
cancelId: 1,
});
if (response !== 0) {
webContents.send("install-complete", { webContents.send("install-complete", {
id, id,
success: false, success: false,
time: Date.now(), time: Date.now(),
exitCode: -1, exitCode: -1,
message: JSON.stringify({ message: JSON.stringify({
message: "用户取消安装 APM,无法继续安装此应用", message: "安装 APM,无法继续安装此应用",
stdout: "", stdout: "",
stderr: "", stderr: "",
}), }),
}); });
return; return;
} }
const installApmOk = await runInstallApm(superUserCmd);
if (!installApmOk) {
webContents.send("install-complete", {
id,
success: false,
time: Date.now(),
exitCode: -1,
message: JSON.stringify({
message: "安装 APM 失败,请检查网络或权限后重试",
stdout: "",
stderr: "",
}),
});
return;
} else {
// 安装APM成功,提示用户已安装成功,需要重启后方可展示应用
await dialog.showMessageBox(win ?? undefined, {
type: "info",
title: "APM 安装成功",
message: "恭喜您,APM 已成功安装",
detail:
"恭喜您,APM 已成功安装!您的应用已在安装中~\n首次安装APM后,需要重启电脑后方可在启动器展示应用。您可在应用安装完毕后择机重启电脑\n若您需要立即使用应用,可在应用安装后先在应用商店中打开您的应用。",
buttons: ["确定"],
defaultId: 0,
});
}
}
} }
if (origin === "spark") { if (origin === "spark") {
@@ -462,8 +405,8 @@ async function processNextInQueue() {
sendStatus("downloading"); sendStatus("downloading");
// 下载重试逻辑:每次超时时间递增,最多3次 // 下载重试逻辑:共10次,5次3秒,3次5秒,2次10秒
const timeoutList = [3000, 5000, 15000]; // 第一次3秒,第二次5秒,第三次15秒 const timeoutList = [3000, 3000, 3000, 3000, 3000, 5000, 5000, 5000, 10000, 10000];
let retryCount = 0; let retryCount = 0;
let downloadSuccess = false; let downloadSuccess = false;
@@ -1089,51 +1032,11 @@ ipcMain.handle("check-spark-available", async () => {
}); });
// 显示 APM 安装对话框(在点击安装按钮时提前检查) // 显示 APM 安装对话框(在点击安装按钮时提前检查)
// 前端已改为 Vue 弹窗,此后端处理仅作为兜底
ipcMain.handle("show-apm-install-dialog", async (event) => { ipcMain.handle("show-apm-install-dialog", async (event) => {
const webContents = event.sender; const webContents = event.sender;
const win = BrowserWindow.fromWebContents(webContents); webContents.send("trigger-apm-install-dialog");
const superUserCmd = await checkSuperUserCommand();
const { response } = await dialog.showMessageBox(win ?? undefined, {
type: "question",
title: "需要安装 APM",
message: "此应用需要使用 APM 安装。",
detail:
"APM 是星火应用商店的软件包兼容工具,此应用使用星火 APM 提供支持,安装APM后方可安装此应用,是否确认安装?",
buttons: ["确认", "取消"],
defaultId: 0,
cancelId: 1,
});
if (response !== 0) {
return { success: false, cancelled: true }; return { success: false, cancelled: true };
}
const installApmOk = await runInstallApm(superUserCmd);
if (!installApmOk) {
await dialog.showMessageBox(win ?? undefined, {
type: "error",
title: "安装失败",
message: "安装 APM 失败",
detail: "请检查网络或权限后重试",
buttons: ["确定"],
defaultId: 0,
});
return { success: false, cancelled: false };
}
// 安装APM成功,提示用户已安装成功,需要重启后方可展示应用
await dialog.showMessageBox(win ?? undefined, {
type: "info",
title: "APM 安装成功",
message: "恭喜您,APM 已成功安装",
detail:
"恭喜您,APM 已成功安装!\n首次安装APM后,需要重启电脑后方可使用全部功能。您可在应用安装完毕后择机重启电脑。",
buttons: ["确定"],
defaultId: 0,
});
return { success: true, cancelled: false };
}); });
// eslint-disable-next-line @typescript-eslint/no-explicit-any // eslint-disable-next-line @typescript-eslint/no-explicit-any
+2 -2
View File
@@ -104,8 +104,8 @@ export const downloadPackage = async ({
onStatus?.("downloading"); onStatus?.("downloading");
// 下载重试逻辑:每次超时时间递增,最多3次 // 下载重试逻辑:共10次,5次3秒,3次5秒,2次10秒
const timeoutList = [3000, 5000, 15000]; const timeoutList = [3000, 3000, 3000, 3000, 3000, 5000, 5000, 5000, 10000, 10000];
let retryCount = 0; let retryCount = 0;
let downloadSuccess = false; let downloadSuccess = false;
+40 -11
View File
@@ -376,7 +376,9 @@ export const loadUpdateCenterItems = async (
storeFilter: StoreFilter = "both", storeFilter: StoreFilter = "both",
runCommand: UpdateCenterCommandRunner = runCommandCapture, runCommand: UpdateCenterCommandRunner = runCommandCapture,
): Promise<UpdateCenterLoadItemsResult> => { ): Promise<UpdateCenterLoadItemsResult> => {
console.log(`[UpdateCenter] loadUpdateCenterItems called with storeFilter=${storeFilter}`); console.log(
`[UpdateCenter] loadUpdateCenterItems called with storeFilter=${storeFilter}`,
);
const [sparkEnabled, apmEnabled] = await Promise.all([ const [sparkEnabled, apmEnabled] = await Promise.all([
isSourceEnabled(storeFilter, "spark") isSourceEnabled(storeFilter, "spark")
? isCommandAvailable(runCommand, "aptss") ? isCommandAvailable(runCommand, "aptss")
@@ -385,7 +387,9 @@ export const loadUpdateCenterItems = async (
? isCommandAvailable(runCommand, "apm") ? isCommandAvailable(runCommand, "apm")
: Promise.resolve(false), : Promise.resolve(false),
]); ]);
console.log(`[UpdateCenter] sparkEnabled=${sparkEnabled}, apmEnabled=${apmEnabled}`); console.log(
`[UpdateCenter] sparkEnabled=${sparkEnabled}, apmEnabled=${apmEnabled}`,
);
const [aptssResult, apmResult, aptssInstalledResult, apmInstalledResult] = const [aptssResult, apmResult, aptssInstalledResult, apmInstalledResult] =
await Promise.all([ await Promise.all([
@@ -409,10 +413,18 @@ export const loadUpdateCenterItems = async (
: Promise.resolve({ code: 0, stdout: "", stderr: "" }), : Promise.resolve({ code: 0, stdout: "", stderr: "" }),
]); ]);
console.log(`[UpdateCenter] aptssResult: code=${aptssResult.code}, stdout=${aptssResult.stdout.substring(0, 500)}, stderr=${aptssResult.stderr.substring(0, 500)}`); console.log(
console.log(`[UpdateCenter] apmResult: code=${apmResult.code}, stdout=${apmResult.stdout.substring(0, 500)}, stderr=${apmResult.stderr.substring(0, 500)}`); `[UpdateCenter] aptssResult: code=${aptssResult.code}, stdout=${aptssResult.stdout.substring(0, 500)}, stderr=${aptssResult.stderr.substring(0, 500)}`,
console.log(`[UpdateCenter] aptssInstalledResult: code=${aptssInstalledResult.code}, stdout=${aptssInstalledResult.stdout.substring(0, 500)}`); );
console.log(`[UpdateCenter] apmInstalledResult: code=${apmInstalledResult.code}, stdout=${apmInstalledResult.stdout.substring(0, 500)}`); console.log(
`[UpdateCenter] apmResult: code=${apmResult.code}, stdout=${apmResult.stdout.substring(0, 500)}, stderr=${apmResult.stderr.substring(0, 500)}`,
);
console.log(
`[UpdateCenter] aptssInstalledResult: code=${aptssInstalledResult.code}, stdout=${aptssInstalledResult.stdout.substring(0, 500)}`,
);
console.log(
`[UpdateCenter] apmInstalledResult: code=${apmInstalledResult.code}, stdout=${apmInstalledResult.stdout.substring(0, 500)}`,
);
const aptssAvailable = const aptssAvailable =
sparkEnabled && (aptssResult.code === 0 || aptssInstalledResult.code === 0); sparkEnabled && (aptssResult.code === 0 || aptssInstalledResult.code === 0);
@@ -438,8 +450,14 @@ export const loadUpdateCenterItems = async (
apmEnabled && apmResult.code === 0 apmEnabled && apmResult.code === 0
? parseApmUpgradableOutput(apmResult.stdout) ? parseApmUpgradableOutput(apmResult.stdout)
: []; : [];
console.log(`[UpdateCenter] parsed aptssItems count=${aptssItems.length}`, aptssItems.map((i) => `${i.pkgname} ${i.currentVersion}->${i.nextVersion}`)); console.log(
console.log(`[UpdateCenter] parsed apmItems count=${apmItems.length}`, apmItems.map((i) => `${i.pkgname} ${i.currentVersion}->${i.nextVersion}`)); `[UpdateCenter] parsed aptssItems count=${aptssItems.length}`,
aptssItems.map((i) => `${i.pkgname} ${i.currentVersion}->${i.nextVersion}`),
);
console.log(
`[UpdateCenter] parsed apmItems count=${apmItems.length}`,
apmItems.map((i) => `${i.pkgname} ${i.currentVersion}->${i.nextVersion}`),
);
const installedSources = buildInstalledSourceMap( const installedSources = buildInstalledSourceMap(
aptssAvailable && aptssInstalledResult.code === 0 aptssAvailable && aptssInstalledResult.code === 0
@@ -461,15 +479,26 @@ export const loadUpdateCenterItems = async (
? enrichApmItems(categorizedApmItems, runCommand) ? enrichApmItems(categorizedApmItems, runCommand)
: Promise.resolve({ items: [], warnings: [] }), : Promise.resolve({ items: [], warnings: [] }),
]); ]);
console.log(`[UpdateCenter] enrichedAptssItems: count=${enrichedAptssItems.items.length}, warnings=${enrichedAptssItems.warnings.length}`, enrichedAptssItems.warnings); console.log(
console.log(`[UpdateCenter] enrichedApmItems: count=${enrichedApmItems.items.length}, warnings=${enrichedApmItems.warnings.length}`, enrichedApmItems.warnings); `[UpdateCenter] enrichedAptssItems: count=${enrichedAptssItems.items.length}, warnings=${enrichedAptssItems.warnings.length}`,
enrichedAptssItems.warnings,
);
console.log(
`[UpdateCenter] enrichedApmItems: count=${enrichedApmItems.items.length}, warnings=${enrichedApmItems.warnings.length}`,
enrichedApmItems.warnings,
);
const mergedItems = mergeUpdateSources( const mergedItems = mergeUpdateSources(
enrichItemIcons(enrichedAptssItems.items), enrichItemIcons(enrichedAptssItems.items),
enrichItemIcons(enrichedApmItems.items), enrichItemIcons(enrichedApmItems.items),
installedSources, installedSources,
); );
console.log(`[UpdateCenter] mergedItems count=${mergedItems.length}`, mergedItems.map((i) => `${i.pkgname} (${i.source}) ${i.currentVersion}->${i.nextVersion}`)); console.log(
`[UpdateCenter] mergedItems count=${mergedItems.length}`,
mergedItems.map(
(i) => `${i.pkgname} (${i.source}) ${i.currentVersion}->${i.nextVersion}`,
),
);
return { return {
items: mergedItems, items: mergedItems,
+21 -7
View File
@@ -263,16 +263,26 @@ const compareVersions = (left: string, right: string): number => {
export const parseAptssUpgradableOutput = ( export const parseAptssUpgradableOutput = (
output: string, output: string,
): UpdateCenterItem[] => { ): UpdateCenterItem[] => {
console.log(`[UpdateCenter] parseAptssUpgradableOutput input (first 1000 chars): ${output.substring(0, 1000)}`); console.log(
`[UpdateCenter] parseAptssUpgradableOutput input (first 1000 chars): ${output.substring(0, 1000)}`,
);
const result = parseUpgradableOutput(output, "aptss"); const result = parseUpgradableOutput(output, "aptss");
console.log(`[UpdateCenter] parseAptssUpgradableOutput result count=${result.length}`); console.log(
`[UpdateCenter] parseAptssUpgradableOutput result count=${result.length}`,
);
return result; return result;
}; };
export const parseApmUpgradableOutput = (output: string): UpdateCenterItem[] => { export const parseApmUpgradableOutput = (
console.log(`[UpdateCenter] parseApmUpgradableOutput input (first 1000 chars): ${output.substring(0, 1000)}`); output: string,
): UpdateCenterItem[] => {
console.log(
`[UpdateCenter] parseApmUpgradableOutput input (first 1000 chars): ${output.substring(0, 1000)}`,
);
const result = parseUpgradableOutput(output, "apm"); const result = parseUpgradableOutput(output, "apm");
console.log(`[UpdateCenter] parseApmUpgradableOutput result count=${result.length}`); console.log(
`[UpdateCenter] parseApmUpgradableOutput result count=${result.length}`,
);
return result; return result;
}; };
@@ -283,10 +293,14 @@ export const parsePrintUrisOutput = (
"downloadUrl" | "fileName" | "size" | "sha512" "downloadUrl" | "fileName" | "size" | "sha512"
> | null => { > | null => {
const trimmed = output.trim(); const trimmed = output.trim();
console.log(`[UpdateCenter] parsePrintUrisOutput input (first 500 chars): ${trimmed.substring(0, 500)}`); console.log(
`[UpdateCenter] parsePrintUrisOutput input (first 500 chars): ${trimmed.substring(0, 500)}`,
);
const match = trimmed.match(PRINT_URIS_PATTERN); const match = trimmed.match(PRINT_URIS_PATTERN);
if (!match) { if (!match) {
console.log(`[UpdateCenter] parsePrintUrisOutput: no match found for pattern ${PRINT_URIS_PATTERN}`); console.log(
`[UpdateCenter] parsePrintUrisOutput: no match found for pattern ${PRINT_URIS_PATTERN}`,
);
return null; return null;
} }
+10 -3
View File
@@ -166,7 +166,9 @@ export const createUpdateCenterService = (
storeFilter: StoreFilter = currentStoreFilter, storeFilter: StoreFilter = currentStoreFilter,
): Promise<UpdateCenterServiceState> => { ): Promise<UpdateCenterServiceState> => {
currentStoreFilter = storeFilter; currentStoreFilter = storeFilter;
console.log(`[UpdateCenter] service.refresh called with storeFilter=${storeFilter}`); console.log(
`[UpdateCenter] service.refresh called with storeFilter=${storeFilter}`,
);
queue.startRefresh(); queue.startRefresh();
emit(); emit();
@@ -176,11 +178,16 @@ export const createUpdateCenterService = (
const loadedItems = normalizeLoadedItems( const loadedItems = normalizeLoadedItems(
await options.loadItems(currentStoreFilter), await options.loadItems(currentStoreFilter),
); );
console.log(`[UpdateCenter] loadItems returned: items=${loadedItems.items.length}, warnings=${loadedItems.warnings.length}`, loadedItems.warnings); console.log(
`[UpdateCenter] loadItems returned: items=${loadedItems.items.length}, warnings=${loadedItems.warnings.length}`,
loadedItems.warnings,
);
const items = sortIgnoredItems( const items = sortIgnoredItems(
applyIgnoredEntries(loadedItems.items, ignoredEntries), applyIgnoredEntries(loadedItems.items, ignoredEntries),
); );
console.log(`[UpdateCenter] after applying ignored: items=${items.length}`); console.log(
`[UpdateCenter] after applying ignored: items=${items.length}`,
);
queue.setItems(items); queue.setItems(items);
queue.finishRefresh(loadedItems.warnings); queue.finishRefresh(loadedItems.warnings);
return emit(); return emit();
+5
View File
@@ -98,6 +98,10 @@ logger.info("User Agent: " + getUserAgent());
/** 根据启动参数 --no-apm / --no-spark 决定只展示的来源 */ /** 根据启动参数 --no-apm / --no-spark 决定只展示的来源 */
function getStoreFilterFromArgv(): "spark" | "apm" | "both" { function getStoreFilterFromArgv(): "spark" | "apm" | "both" {
if (process.arch === "loong64") {
// Currently loong64 only have spark support
return "spark";
} else {
const argv = process.argv; const argv = process.argv;
const noApm = argv.includes("--no-apm"); const noApm = argv.includes("--no-apm");
const noSpark = argv.includes("--no-spark"); const noSpark = argv.includes("--no-spark");
@@ -105,6 +109,7 @@ function getStoreFilterFromArgv(): "spark" | "apm" | "both" {
if (noApm) return "spark"; if (noApm) return "spark";
if (noSpark) return "apm"; if (noSpark) return "apm";
return "both"; return "both";
}
} }
ipcMain.handle("get-store-filter", (): "spark" | "apm" | "both" => ipcMain.handle("get-store-filter", (): "spark" | "apm" | "both" =>
+17 -15
View File
@@ -1,12 +1,12 @@
{ {
"name": "spark-store", "name": "spark-store",
"version": "5.0.0beta4", "version": "5.0.0",
"lockfileVersion": 3, "lockfileVersion": 3,
"requires": true, "requires": true,
"packages": { "packages": {
"": { "": {
"name": "spark-store", "name": "spark-store",
"version": "5.0.0beta4", "version": "5.0.0",
"license": "GPL-3.0", "license": "GPL-3.0",
"dependencies": { "dependencies": {
"@tailwindcss/vite": "^4.1.18", "@tailwindcss/vite": "^4.1.18",
@@ -28,7 +28,7 @@
"@vue/test-utils": "^2.4.3", "@vue/test-utils": "^2.4.3",
"conventional-changelog": "^7.1.1", "conventional-changelog": "^7.1.1",
"conventional-changelog-angular": "^8.1.0", "conventional-changelog-angular": "^8.1.0",
"electron": "^40.0.0", "electron": "^39.2.7",
"eslint": "^9.39.2", "eslint": "^9.39.2",
"eslint-config-prettier": "^10.1.8", "eslint-config-prettier": "^10.1.8",
"eslint-plugin-prettier": "^5.5.5", "eslint-plugin-prettier": "^5.5.5",
@@ -4870,15 +4870,15 @@
} }
}, },
"node_modules/electron": { "node_modules/electron": {
"version": "40.8.5", "version": "39.2.7",
"resolved": "https://registry.npmjs.org/electron/-/electron-40.8.5.tgz", "resolved": "https://registry.npmjs.org/electron/-/electron-39.2.7.tgz",
"integrity": "sha512-pgTY/VPQKaiU4sTjfU96iyxCXrFm4htVPCMRT4b7q9ijNTRgtLmLvcmzp2G4e7xDrq9p7OLHSmu1rBKFf6Y1/A==", "integrity": "sha512-KU0uFS6LSTh4aOIC3miolcbizOFP7N1M46VTYVfqIgFiuA2ilfNaOHLDS9tCMvwwHRowAsvqBrh9NgMXcTOHCQ==",
"dev": true, "dev": true,
"hasInstallScript": true, "hasInstallScript": true,
"license": "MIT", "license": "MIT",
"dependencies": { "dependencies": {
"@electron/get": "^2.0.0", "@electron/get": "^2.0.0",
"@types/node": "^24.9.0", "@types/node": "^22.7.7",
"extract-zip": "^2.0.1" "extract-zip": "^2.0.1"
}, },
"bin": { "bin": {
@@ -4889,19 +4889,21 @@
} }
}, },
"node_modules/electron/node_modules/@types/node": { "node_modules/electron/node_modules/@types/node": {
"version": "24.12.0", "version": "22.19.17",
"resolved": "https://registry.npmjs.org/@types/node/-/node-24.12.0.tgz", "resolved": "https://registry.npmjs.org/@types/node/-/node-22.19.17.tgz",
"integrity": "sha512-GYDxsZi3ChgmckRT9HPU0WEhKLP08ev/Yfcq2AstjrDASOYCSXeyjDsHg4v5t4jOj7cyDX3vmprafKlWIG9MXQ==", "integrity": "sha512-wGdMcf+vPYM6jikpS/qhg6WiqSV/OhG+jeeHT/KlVqxYfD40iYJf9/AE1uQxVWFvU7MipKRkRv8NSHiCGgPr8Q==",
"dev": true, "dev": true,
"license": "MIT",
"dependencies": { "dependencies": {
"undici-types": "~7.16.0" "undici-types": "~6.21.0"
} }
}, },
"node_modules/electron/node_modules/undici-types": { "node_modules/electron/node_modules/undici-types": {
"version": "7.16.0", "version": "6.21.0",
"resolved": "https://registry.npmjs.org/undici-types/-/undici-types-7.16.0.tgz", "resolved": "https://registry.npmjs.org/undici-types/-/undici-types-6.21.0.tgz",
"integrity": "sha512-Zz+aZWSj8LE6zoxD+xrjh4VfkIG8Ya6LvYkZqtUQGJPZjYl53ypCaUwWqo7eI0x66KBGeRo+mlBEkMSeSZ38Nw==", "integrity": "sha512-iwDZqg0QAGrg9Rav5H4n0M64c3mkR59cJ6wQp+7C4nI0gsmExaedaYLNO44eT4AtBBwjbTiGPMlt2Md0T9H9JQ==",
"dev": true "dev": true,
"license": "MIT"
}, },
"node_modules/emoji-regex": { "node_modules/emoji-regex": {
"version": "8.0.0", "version": "8.0.0",
+3 -2
View File
@@ -1,6 +1,6 @@
{ {
"name": "spark-store", "name": "spark-store",
"version": "5.0.0", "version": "5.1.0",
"main": "dist-electron/main/index.js", "main": "dist-electron/main/index.js",
"description": "Client for Spark App Store", "description": "Client for Spark App Store",
"author": "elysia-best <elysia-best@simplelinux.cn.eu.org>", "author": "elysia-best <elysia-best@simplelinux.cn.eu.org>",
@@ -30,6 +30,7 @@
"build:vite": "vue-tsc --noEmit && vite build --mode production", "build:vite": "vue-tsc --noEmit && vite build --mode production",
"build:rpm": "vue-tsc --noEmit && vite build --mode production && electron-builder --config electron-builder.yml --linux rpm", "build:rpm": "vue-tsc --noEmit && vite build --mode production && electron-builder --config electron-builder.yml --linux rpm",
"build:deb": "vue-tsc --noEmit && vite build --mode production && electron-builder --config electron-builder.yml --linux deb", "build:deb": "vue-tsc --noEmit && vite build --mode production && electron-builder --config electron-builder.yml --linux deb",
"build:deb-loong64": "vue-tsc --noEmit && vite build --mode production && env ELECTRON_MIRROR=https://github.com/darkyzhou/electron-loong64/releases/download/ electron_use_remote_checksums=1 electron-builder --config electron-builder.yml --loong64 --linux deb",
"preview": "vite preview --mode debug", "preview": "vite preview --mode debug",
"lint": "eslint --ext .ts,.vue src electron", "lint": "eslint --ext .ts,.vue src electron",
"lint:fix": "eslint --ext .ts,.vue src electron --fix", "lint:fix": "eslint --ext .ts,.vue src electron --fix",
@@ -56,7 +57,7 @@
"@vue/test-utils": "^2.4.3", "@vue/test-utils": "^2.4.3",
"conventional-changelog": "^7.1.1", "conventional-changelog": "^7.1.1",
"conventional-changelog-angular": "^8.1.0", "conventional-changelog-angular": "^8.1.0",
"electron": "^40.0.0", "electron": "^39.2.7",
"eslint": "^9.39.2", "eslint": "^9.39.2",
"eslint-config-prettier": "^10.1.8", "eslint-config-prettier": "^10.1.8",
"eslint-plugin-prettier": "^5.5.5", "eslint-plugin-prettier": "^5.5.5",
+35 -9
View File
@@ -152,6 +152,12 @@
@success="onUninstallSuccess" @success="onUninstallSuccess"
/> />
<ApmInstallConfirmModal
:show="showApmInstallDialog"
@close="closeApmInstallDialog"
@confirm="confirmApmInstall"
/>
<AboutModal :show="showAboutModal" @close="closeAboutModal" /> <AboutModal :show="showAboutModal" @close="closeAboutModal" />
<SettingsModal :show="showSettingsModal" @close="closeSettingsModal" /> <SettingsModal :show="showSettingsModal" @close="closeSettingsModal" />
@@ -173,6 +179,7 @@ import DownloadDetail from "./components/DownloadDetail.vue";
import InstalledAppsModal from "./components/InstalledAppsModal.vue"; import InstalledAppsModal from "./components/InstalledAppsModal.vue";
import UpdateCenterModal from "./components/UpdateCenterModal.vue"; import UpdateCenterModal from "./components/UpdateCenterModal.vue";
import UninstallConfirmModal from "./components/UninstallConfirmModal.vue"; import UninstallConfirmModal from "./components/UninstallConfirmModal.vue";
import ApmInstallConfirmModal from "./components/ApmInstallConfirmModal.vue";
import AboutModal from "./components/AboutModal.vue"; import AboutModal from "./components/AboutModal.vue";
import SettingsModal from "./components/SettingsModal.vue"; import SettingsModal from "./components/SettingsModal.vue";
import { import {
@@ -181,6 +188,7 @@ import {
currentAppSparkInstalled, currentAppSparkInstalled,
currentAppApmInstalled, currentAppApmInstalled,
currentStoreMode, currentStoreMode,
showApmInstallDialog,
getHybridDefaultOrigin, getHybridDefaultOrigin,
loadPriorityConfig, loadPriorityConfig,
} from "./global/storeConfig"; } from "./global/storeConfig";
@@ -238,8 +246,6 @@ const fetchWithRetry = async <T,>(
} }
}; };
const cacheBuster = (url: string) => `${url}?cb=${Date.now()}`;
// 响应式状态 // 响应式状态
const themeMode = ref<"light" | "dark" | "auto">("auto"); const themeMode = ref<"light" | "dark" | "auto">("auto");
const systemIsDark = ref( const systemIsDark = ref(
@@ -401,7 +407,7 @@ const fetchAppFromStore = async (
const arch = window.apm_store.arch || "amd64"; const arch = window.apm_store.arch || "amd64";
const finalArch = origin === "spark" ? `${arch}-store` : `${arch}-apm`; const finalArch = origin === "spark" ? `${arch}-store` : `${arch}-apm`;
const appJsonUrl = `${APM_STORE_BASE_URL}/${finalArch}/${category}/${pkgname}/app.json`; const appJsonUrl = `${APM_STORE_BASE_URL}/${finalArch}/${category}/${pkgname}/app.json`;
const response = await fetch(cacheBuster(appJsonUrl)); const response = await fetch(appJsonUrl);
if (!response.ok) return null; if (!response.ok) return null;
const appJson = await response.json(); const appJson = await response.json();
return { return {
@@ -672,7 +678,7 @@ const loadHome = async () => {
// homelinks.json // homelinks.json
try { try {
const res = await fetch(cacheBuster(`${base}/homelinks.json`)); const res = await fetch(`${base}/homelinks.json`);
if (res.ok) { if (res.ok) {
const links = await res.json(); const links = await res.json();
const taggedLinks = links.map((l: HomeLink) => ({ const taggedLinks = links.map((l: HomeLink) => ({
@@ -687,14 +693,14 @@ const loadHome = async () => {
// homelist.json // homelist.json
try { try {
const res2 = await fetch(cacheBuster(`${base}/homelist.json`)); const res2 = await fetch(`${base}/homelist.json`);
if (res2.ok) { if (res2.ok) {
const lists = await res2.json(); const lists = await res2.json();
for (const item of lists) { for (const item of lists) {
if (item.type === "appList" && item.jsonUrl) { if (item.type === "appList" && item.jsonUrl) {
try { try {
const url = `${APM_STORE_BASE_URL}/${finalArch}${item.jsonUrl}`; const url = `${APM_STORE_BASE_URL}/${finalArch}${item.jsonUrl}`;
const r = await fetch(cacheBuster(url)); const r = await fetch(url);
if (r.ok) { if (r.ok) {
const appsJson = await r.json(); const appsJson = await r.json();
const rawApps = appsJson || []; const rawApps = appsJson || [];
@@ -712,7 +718,7 @@ const loadHome = async () => {
try { try {
const realAppUrl = `${APM_STORE_BASE_URL}/${finalArch}/${baseApp.category}/${baseApp.pkgname}/app.json`; const realAppUrl = `${APM_STORE_BASE_URL}/${finalArch}/${baseApp.category}/${baseApp.pkgname}/app.json`;
const realRes = await fetch(cacheBuster(realAppUrl)); const realRes = await fetch(realAppUrl);
if (realRes.ok) { if (realRes.ok) {
const realApp = await realRes.json(); const realApp = await realRes.json();
if (realApp.Filename) if (realApp.Filename)
@@ -966,6 +972,22 @@ const onUninstallSuccess = () => {
} }
}; };
const closeApmInstallDialog = () => {
showApmInstallDialog.value = false;
};
const confirmApmInstall = async () => {
showApmInstallDialog.value = false;
closeDetail();
await nextTick();
const apmApp = apps.value.find((a) => a.pkgname === "apm");
if (apmApp) {
openDetail(apmApp);
} else {
searchQuery.value = "apm";
}
};
const installCompleteCallback = (pkgname?: string) => { const installCompleteCallback = (pkgname?: string) => {
if (currentApp.value && (!pkgname || currentApp.value.pkgname === pkgname)) { if (currentApp.value && (!pkgname || currentApp.value.pkgname === pkgname)) {
checkAppInstalled(currentApp.value); checkAppInstalled(currentApp.value);
@@ -1081,7 +1103,7 @@ const loadCategories = async () => {
const path = `/${finalArch}/categories.json`; const path = `/${finalArch}/categories.json`;
try { try {
const response = await axiosInstance.get(cacheBuster(path)); const response = await axiosInstance.get(path);
const data = response.data; const data = response.data;
Object.keys(data).forEach((key) => { Object.keys(data).forEach((key) => {
if (categoryData[key]) { if (categoryData[key]) {
@@ -1134,7 +1156,7 @@ const loadApps = async (onFirstBatch?: () => void) => {
logger.info(`加载分类: ${category} (来源: ${mode})`); logger.info(`加载分类: ${category} (来源: ${mode})`);
const categoryApps = await fetchWithRetry<AppJson[]>( const categoryApps = await fetchWithRetry<AppJson[]>(
cacheBuster(path), path,
); );
const normalizedApps = (categoryApps || []).map((appJson) => ({ const normalizedApps = (categoryApps || []).map((appJson) => ({
@@ -1278,6 +1300,10 @@ onMounted(async () => {
} }
}); });
window.ipcRenderer.on("trigger-apm-install-dialog", () => {
showApmInstallDialog.value = true;
});
window.ipcRenderer.on( window.ipcRenderer.on(
"deep-link-install", "deep-link-install",
(_event: IpcRendererEvent, pkgname: string) => { (_event: IpcRendererEvent, pkgname: string) => {
@@ -191,7 +191,9 @@ describe("update-center load items", () => {
], ],
}); });
const result = await loadUpdateCenterItems("both", async (command, args) => { const result = await loadUpdateCenterItems(
"both",
async (command, args) => {
const key = `${command} ${args.join(" ")}`; const key = `${command} ${args.join(" ")}`;
const match = commandResults.get(key); const match = commandResults.get(key);
if (!match) { if (!match) {
@@ -199,7 +201,8 @@ describe("update-center load items", () => {
} }
return match; return match;
}); },
);
expect(result.warnings).toEqual([]); expect(result.warnings).toEqual([]);
expect(result.items).toContainEqual({ expect(result.items).toContainEqual({
@@ -233,7 +236,9 @@ describe("update-center load items", () => {
], ],
}); });
const result = await loadUpdateCenterItems("both", async (command, args) => { const result = await loadUpdateCenterItems(
"both",
async (command, args) => {
const key = `${command} ${args.join(" ")}`; const key = `${command} ${args.join(" ")}`;
if (key === WHICH_APTSS_KEY) { if (key === WHICH_APTSS_KEY) {
@@ -287,7 +292,8 @@ describe("update-center load items", () => {
} }
throw new Error(`Unexpected command ${key}`); throw new Error(`Unexpected command ${key}`);
}); },
);
expect(result.items).toEqual([ expect(result.items).toEqual([
{ {
@@ -416,7 +422,9 @@ describe("update-center load items", () => {
], ],
}); });
const result = await loadUpdateCenterItems("both", async (command, args) => { const result = await loadUpdateCenterItems(
"both",
async (command, args) => {
const key = `${command} ${args.join(" ")}`; const key = `${command} ${args.join(" ")}`;
if (key === WHICH_APTSS_KEY) { if (key === WHICH_APTSS_KEY) {
@@ -461,7 +469,8 @@ describe("update-center load items", () => {
} }
throw new Error(`Unexpected command ${key}`); throw new Error(`Unexpected command ${key}`);
}); },
);
expect(result.items).toEqual([ expect(result.items).toEqual([
{ {
+81
View File
@@ -0,0 +1,81 @@
<template>
<Transition
enter-active-class="duration-200 ease-out"
enter-from-class="opacity-0 scale-95"
enter-to-class="opacity-100 scale-100"
leave-active-class="duration-150 ease-in"
leave-from-class="opacity-100 scale-100"
leave-to-class="opacity-0 scale-95"
>
<div
v-if="show"
class="fixed inset-0 z-[80] flex items-center justify-center bg-slate-900/70 p-4"
@click.self="handleClose"
>
<div
class="relative w-full max-w-lg overflow-hidden rounded-3xl border border-white/10 bg-white/95 p-6 shadow-2xl dark:border-slate-800 dark:bg-slate-900"
>
<div class="mb-6 flex items-center gap-4">
<div
class="flex h-16 w-16 items-center justify-center rounded-2xl bg-gradient-to-br from-brand/20 to-brand/10 shadow-inner dark:from-brand/20 dark:to-brand/10"
>
<i class="fas fa-box-open text-2xl text-brand"></i>
</div>
<div>
<h3 class="text-xl font-bold text-slate-900 dark:text-white">
需要安装 APM
</h3>
<p class="text-sm text-slate-500 dark:text-slate-400">
APM 是星火应用商店的软件包兼容工具此应用使用星火 APM 提供支持
</p>
<p class="mt-1 text-sm text-slate-500 dark:text-slate-400">
是否前往商店安装
<span class="font-semibold text-slate-700 dark:text-slate-200"
>APM</span
>
</p>
</div>
</div>
<div class="flex items-center justify-end gap-3">
<button
type="button"
class="rounded-xl border border-slate-200 bg-white px-4 py-2 text-sm font-medium text-slate-600 transition hover:bg-slate-50 dark:border-slate-700 dark:bg-slate-800 dark:text-slate-300 dark:hover:bg-slate-700"
@click="handleClose"
>
取消
</button>
<button
type="button"
class="inline-flex items-center gap-2 rounded-xl bg-gradient-to-r from-brand to-brand-dark px-4 py-2 text-sm font-semibold text-white shadow-lg shadow-brand/30 transition hover:-translate-y-0.5"
@click="handleConfirm"
>
<i class="fas fa-download"></i>
前往安装
</button>
</div>
</div>
</div>
</Transition>
</template>
<script setup lang="ts">
defineProps<{
show: boolean;
}>();
const emit = defineEmits<{
(e: "close"): void;
(e: "confirm"): void;
}>();
const handleClose = () => {
emit("close");
};
const handleConfirm = () => {
emit("confirm");
};
</script>
+2 -2
View File
@@ -62,7 +62,7 @@
</div> </div>
<button <button
type="button" type="button"
class="inline-flex items-center gap-2 rounded-2xl border border-slate-200/70 px-4 py-2 text-sm font-semibold text-slate-600 transition hover:bg-slate-50 disabled:opacity-40 dark:border-slate-700 dark:text-slate-200" class="inline-flex items-center gap-2 rounded-2xl border border-slate-200/70 px-4 py-2 text-sm font-semibold text-slate-600 transition hover:bg-slate-50 disabled:opacity-40 dark:border-slate-700 dark:text-slate-200 dark:hover:bg-slate-800"
:disabled="loading" :disabled="loading"
@click="$emit('refresh')" @click="$emit('refresh')"
> >
@@ -71,7 +71,7 @@
</button> </button>
<button <button
type="button" type="button"
class="inline-flex h-10 w-10 items-center justify-center rounded-full border border-slate-200/70 text-slate-500 transition hover:text-slate-900 dark:border-slate-700" class="inline-flex h-10 w-10 items-center justify-center rounded-full border border-slate-200/70 text-slate-500 transition hover:text-slate-900 dark:hover:text-white dark:border-slate-700 dark:hover:bg-slate-800"
@click="$emit('close')" @click="$emit('close')"
aria-label="关闭" aria-label="关闭"
> >
+3 -1
View File
@@ -47,7 +47,9 @@
v-if="store.loading.value && store.filteredItems.value.length === 0" v-if="store.loading.value && store.filteredItems.value.length === 0"
class="flex min-h-0 flex-1 items-center justify-center p-6" class="flex min-h-0 flex-1 items-center justify-center p-6"
> >
<div class="flex flex-col items-center gap-3 text-slate-500 dark:text-slate-400"> <div
class="flex flex-col items-center gap-3 text-slate-500 dark:text-slate-400"
>
<i class="fas fa-circle-notch fa-spin text-3xl"></i> <i class="fas fa-circle-notch fa-spin text-3xl"></i>
<p class="text-sm">正在检查更新</p> <p class="text-sm">正在检查更新</p>
</div> </div>
@@ -33,7 +33,7 @@
<button <button
type="button" type="button"
aria-label="关闭" aria-label="关闭"
class="inline-flex h-11 w-11 items-center justify-center rounded-full border border-slate-200/70 text-slate-500 transition hover:text-slate-900 dark:border-slate-700 dark:text-slate-300" class="inline-flex h-10 w-10 items-center justify-center rounded-full border border-slate-200/70 text-slate-500 transition hover:text-slate-900 dark:hover:text-white dark:border-slate-700 dark:hover:bg-slate-800"
@click="$emit('request-close')" @click="$emit('request-close')"
> >
<i class="fas fa-xmark"></i> <i class="fas fa-xmark"></i>
+1
View File
@@ -11,6 +11,7 @@ export const APM_STORE_STATS_BASE_URL: string =
export const currentApp = ref<App | null>(null); export const currentApp = ref<App | null>(null);
export const currentAppSparkInstalled = ref(false); export const currentAppSparkInstalled = ref(false);
export const currentAppApmInstalled = ref(false); export const currentAppApmInstalled = ref(false);
export const showApmInstallDialog = ref(false);
export const currentStoreMode = ref<StoreMode>("hybrid"); export const currentStoreMode = ref<StoreMode>("hybrid");
+3 -14
View File
@@ -5,6 +5,7 @@ import {
currentApp, currentApp,
currentAppSparkInstalled, currentAppSparkInstalled,
currentAppApmInstalled, currentAppApmInstalled,
showApmInstallDialog,
} from "../global/storeConfig"; } from "../global/storeConfig";
import { APM_STORE_BASE_URL } from "../global/storeConfig"; import { APM_STORE_BASE_URL } from "../global/storeConfig";
import { downloads, getNextDownloadId } from "../global/downloadStatus"; import { downloads, getNextDownloadId } from "../global/downloadStatus";
@@ -28,16 +29,10 @@ export const handleInstall = async (appObj?: App) => {
if (targetApp.origin === "apm") { if (targetApp.origin === "apm") {
const hasApm = await window.ipcRenderer.invoke("check-apm-available"); const hasApm = await window.ipcRenderer.invoke("check-apm-available");
if (!hasApm) { if (!hasApm) {
// 发送事件到主进程显示 APM 安装对话框 showApmInstallDialog.value = true;
const { success, cancelled } = await window.ipcRenderer.invoke(
"show-apm-install-dialog",
);
if (!success || cancelled) {
// 用户取消或未安装成功,不继续安装应用
return; return;
} }
} }
}
if ( if (
downloads.value.find( downloads.value.find(
@@ -119,16 +114,10 @@ export const handleUpgrade = async (app: App) => {
if (app.origin === "apm") { if (app.origin === "apm") {
const hasApm = await window.ipcRenderer.invoke("check-apm-available"); const hasApm = await window.ipcRenderer.invoke("check-apm-available");
if (!hasApm) { if (!hasApm) {
// 发送事件到主进程显示 APM 安装对话框 showApmInstallDialog.value = true;
const { success, cancelled } = await window.ipcRenderer.invoke(
"show-apm-install-dialog",
);
if (!success || cancelled) {
// 用户取消或未安装成功,不继续更新应用
return; return;
} }
} }
}
if ( if (
downloads.value.find( downloads.value.find(
+1 -1
View File
@@ -3,7 +3,7 @@ Dir::Cache::archives "/var/cache/apt/archives";
Dir::Cache "/var/lib/aptss/"; Dir::Cache "/var/lib/aptss/";
Dir::Etc::SourceParts "/opt/durapps/spark-store/bin/apt-fast-conf/sources.list.d/"; Dir::Etc::SourceParts "/opt/durapps/spark-store/bin/apt-fast-conf/sources.list.d/";
Dir::State::lists "/var/lib/aptss/lists/"; Dir::State::lists "/var/lib/aptss/lists/";
APT::Sandbox::User "root";
APT::Get::Fix-Broken true; APT::Get::Fix-Broken true;
APT::Get::List-Cleanup="0"; APT::Get::List-Cleanup="0";
+6 -20
View File
@@ -83,6 +83,9 @@ cleanup_aptfast()
if [ -n "$LISTTEMP" ] && [ -d "$LISTTEMP" ]; then if [ -n "$LISTTEMP" ] && [ -d "$LISTTEMP" ]; then
rm -rf "$LISTTEMP" rm -rf "$LISTTEMP"
fi fi
if [ -n "$tmpdir" ] && [ -d "$tmpdir" ]; then
rm -rf "$tmpdir"
fi
} }
exit_cleanup_state() exit_cleanup_state()
{ {
@@ -120,11 +123,8 @@ _create_lock()
# unlock and remove the lock file # unlock and remove the lock file
_remove_lock() _remove_lock()
{ {
# Only unlock if lock file exists (was created by _create_lock)
if [ -f "$LCK_FILE.lock" ]; then
flock -u "$LCK_FD" 2>/dev/null flock -u "$LCK_FD" 2>/dev/null
rm -f "$LCK_FILE.lock" rm -f "$LCK_FILE.lock"
fi
} }
# Search for known options and decide if root privileges are needed. # Search for known options and decide if root privileges are needed.
@@ -134,7 +134,6 @@ for argument in "$@"; do
case "$argument" in case "$argument" in
upgrade | full-upgrade | install | dist-upgrade | build-dep) upgrade | full-upgrade | install | dist-upgrade | build-dep)
option="install" option="install"
_create_lock
;; ;;
clean | autoclean) clean | autoclean)
option="clean" option="clean"
@@ -313,6 +312,9 @@ https_proxy=
[ "$TMP_http_proxy" = "$TMP_RANDOM" ] || http_proxy="$TMP_http_proxy" [ "$TMP_http_proxy" = "$TMP_RANDOM" ] || http_proxy="$TMP_http_proxy"
[ "$TMP_https_proxy" = "$TMP_RANDOM" ] || https_proxy="$TMP_https_proxy" [ "$TMP_https_proxy" = "$TMP_RANDOM" ] || https_proxy="$TMP_https_proxy"
if [ "$option" == "install" ]; then
_create_lock
fi
# Disable colors if not executed in terminal. # Disable colors if not executed in terminal.
if [ ! -t 1 ]; then if [ ! -t 1 ]; then
@@ -456,22 +458,12 @@ get_uris(){
exit "$CLEANUP_STATE" exit "$CLEANUP_STATE"
fi fi
prepare_auth prepare_auth
local tmpdir
tmpdir=$(mktemp -d) || { tmpdir=$(mktemp -d) || {
msg "Failed to create tmp dir" "warning" msg "Failed to create tmp dir" "warning"
msg "无法创建临时目录" "warning" msg "无法创建临时目录" "warning"
exit 1 exit 1
} }
cleanup_tmpdir() {
if [ -n "$tmpdir" ] && [ -d "$tmpdir" ]; then
rm -rf "$tmpdir"
fi
}
trap cleanup_tmpdir EXIT
## --print-uris format is: ## --print-uris format is:
# 'fileurl' filename filesize checksum_hint:filechecksum # 'fileurl' filename filesize checksum_hint:filechecksum
# 修改:process_package函数增加第二个参数表示当前线程的临时输出文件 # 修改:process_package函数增加第二个参数表示当前线程的临时输出文件
@@ -824,9 +816,6 @@ elif [ "$option" == "download" ]; then
"${_APTMGR}" "$@" "${_APTMGR}" "$@"
fi fi
# Clean up temporary directory for download command
cleanup_aptfast
elif [ "$option" == "source" ]; then elif [ "$option" == "source" ]; then
msg msg
msg "Working... this may take a while." "normal" msg "Working... this may take a while." "normal"
@@ -853,9 +842,6 @@ elif [ "$option" == "source" ]; then
# dpkg-source -x "$(basename "$srcfile")" # dpkg-source -x "$(basename "$srcfile")"
#done < "$DLLIST" #done < "$DLLIST"
# Clean up temporary directory for source command
cleanup_aptfast
# Execute package manager directly if unknown options are passed. # Execute package manager directly if unknown options are passed.
else else
"${_APTMGR}" "${APT_SCRIPT_WARNING[@]}" "$@" "${_APTMGR}" "${APT_SCRIPT_WARNING[@]}" "$@"