mirror of
https://gitee.com/spark-store-project/spark-store
synced 2026-06-22 22:23:49 +08:00
Compare commits
12 Commits
| Author | SHA1 | Date | |
|---|---|---|---|
| 9e8758b5f2 | |||
| e84c1d86bf | |||
| e39525901e | |||
| 8e8617218a | |||
| 97f49201b7 | |||
| f62665cd73 | |||
| e16acbd0a5 | |||
| 8a5f8d154f | |||
| 8c8b53fc29 | |||
| c50655c106 | |||
| 4b37aa4da4 | |||
| ce5de692f7 |
@@ -26,6 +26,7 @@ linux:
|
|||||||
Categories: "System;"
|
Categories: "System;"
|
||||||
mimeTypes:
|
mimeTypes:
|
||||||
- "x-scheme-handler/spk"
|
- "x-scheme-handler/spk"
|
||||||
|
- "x-scheme-handler/apt"
|
||||||
target:
|
target:
|
||||||
- "AppImage"
|
- "AppImage"
|
||||||
- "deb"
|
- "deb"
|
||||||
|
|||||||
@@ -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") {
|
||||||
@@ -447,6 +390,7 @@ async function processNextInQueue() {
|
|||||||
const aria2Args = [
|
const aria2Args = [
|
||||||
`--dir=${downloadDir}`,
|
`--dir=${downloadDir}`,
|
||||||
"--allow-overwrite=true",
|
"--allow-overwrite=true",
|
||||||
|
"--async-dns=false",
|
||||||
"--summary-interval=1",
|
"--summary-interval=1",
|
||||||
"--connect-timeout=10",
|
"--connect-timeout=10",
|
||||||
"--timeout=15",
|
"--timeout=15",
|
||||||
@@ -462,8 +406,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 +1033,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
|
||||||
|
|||||||
@@ -89,6 +89,7 @@ export const downloadPackage = async ({
|
|||||||
const aria2Args = [
|
const aria2Args = [
|
||||||
`--dir=${downloadDir}`,
|
`--dir=${downloadDir}`,
|
||||||
"--allow-overwrite=true",
|
"--allow-overwrite=true",
|
||||||
|
"--async-dns=false",
|
||||||
"--summary-interval=1",
|
"--summary-interval=1",
|
||||||
"--connect-timeout=10",
|
"--connect-timeout=10",
|
||||||
"--timeout=15",
|
"--timeout=15",
|
||||||
@@ -104,8 +105,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;
|
||||||
|
|
||||||
|
|||||||
@@ -45,7 +45,7 @@ class ListenersMap {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
const protocols = ["spk"];
|
const protocols = ["spk", "apt"];
|
||||||
const listeners = new ListenersMap();
|
const listeners = new ListenersMap();
|
||||||
|
|
||||||
export const deepLink = {
|
export const deepLink = {
|
||||||
@@ -81,6 +81,22 @@ export function handleCommandLine(commandLine: string[]) {
|
|||||||
try {
|
try {
|
||||||
const url = new URL(target);
|
const url = new URL(target);
|
||||||
|
|
||||||
|
// Handle apt:// protocol: convert to spk://search/pkgname
|
||||||
|
if (url.protocol === "apt:") {
|
||||||
|
// Format: apt://pkgname
|
||||||
|
const pkgname = url.hostname || url.pathname.split("/").filter(Boolean)[0];
|
||||||
|
if (pkgname) {
|
||||||
|
const query: Query = { pkgname };
|
||||||
|
logger.info(`Deep link: apt protocol converted to search: ${pkgname}`);
|
||||||
|
listeners.emit("search", query);
|
||||||
|
} else {
|
||||||
|
logger.warn(
|
||||||
|
`Deep link: invalid apt format, expected //pkgname, got ${target}`,
|
||||||
|
);
|
||||||
|
}
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
const action = url.hostname; // 'search'
|
const action = url.hostname; // 'search'
|
||||||
logger.info(`Deep link: action found: ${action}`);
|
logger.info(`Deep link: action found: ${action}`);
|
||||||
|
|
||||||
|
|||||||
@@ -318,6 +318,21 @@ ipcMain.handle("check-for-updates", async () => {
|
|||||||
}
|
}
|
||||||
});
|
});
|
||||||
|
|
||||||
|
// Register custom protocol handlers
|
||||||
|
if (process.defaultApp) {
|
||||||
|
if (process.argv.length >= 2) {
|
||||||
|
app.setAsDefaultProtocolClient("spk", process.execPath, [
|
||||||
|
path.resolve(process.argv[1]),
|
||||||
|
]);
|
||||||
|
app.setAsDefaultProtocolClient("apt", process.execPath, [
|
||||||
|
path.resolve(process.argv[1]),
|
||||||
|
]);
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
app.setAsDefaultProtocolClient("spk");
|
||||||
|
app.setAsDefaultProtocolClient("apt");
|
||||||
|
}
|
||||||
|
|
||||||
app.whenReady().then(() => {
|
app.whenReady().then(() => {
|
||||||
// Set User-Agent for client
|
// Set User-Agent for client
|
||||||
session.defaultSession.webRequest.onBeforeSendHeaders((details, callback) => {
|
session.defaultSession.webRequest.onBeforeSendHeaders((details, callback) => {
|
||||||
|
|||||||
@@ -23,6 +23,12 @@ if ! command -v apt >/dev/null 2>&1; then
|
|||||||
ARGS="$ARGS --no-spark"
|
ARGS="$ARGS --no-spark"
|
||||||
fi
|
fi
|
||||||
|
|
||||||
|
# 检查是否是AOSC OS
|
||||||
|
if grep -q "ID=aosc" /etc/os-release; then
|
||||||
|
echo "检测到 AOSC OS"
|
||||||
|
ARGS="$ARGS --no-spark"
|
||||||
|
fi
|
||||||
|
|
||||||
# 注意:已移除原先针对 arm64 + wayland 添加 --disable-gpu 的逻辑,
|
# 注意:已移除原先针对 arm64 + wayland 添加 --disable-gpu 的逻辑,
|
||||||
# 现在 arm64 设备无论是否使用 wayland 均不再添加此参数。
|
# 现在 arm64 设备无论是否使用 wayland 均不再添加此参数。
|
||||||
|
|
||||||
|
|||||||
+1
-1
@@ -1,6 +1,6 @@
|
|||||||
{
|
{
|
||||||
"name": "spark-store",
|
"name": "spark-store",
|
||||||
"version": "5.0.1",
|
"version": "5.1.1",
|
||||||
"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>",
|
||||||
|
|||||||
@@ -10,4 +10,4 @@ Keywords=appstore;
|
|||||||
Terminal=false
|
Terminal=false
|
||||||
StartupNotify=true
|
StartupNotify=true
|
||||||
StartupWMClass=spark-store
|
StartupWMClass=spark-store
|
||||||
MimeType=x-scheme-handler/spk
|
MimeType=x-scheme-handler/spk;x-scheme-handler/apt
|
||||||
|
|||||||
@@ -37,6 +37,7 @@ void DownloadManager::startDownload(const QString &packageName, const QString &u
|
|||||||
QStringList arguments = {
|
QStringList arguments = {
|
||||||
"--enable-rpc=false",
|
"--enable-rpc=false",
|
||||||
"--console-log-level=warn",
|
"--console-log-level=warn",
|
||||||
|
"--async-dns=false",
|
||||||
"--summary-interval=1",
|
"--summary-interval=1",
|
||||||
"--allow-overwrite=true",
|
"--allow-overwrite=true",
|
||||||
"--connect-timeout=30",
|
"--connect-timeout=30",
|
||||||
|
|||||||
+28
@@ -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";
|
||||||
@@ -964,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);
|
||||||
@@ -1276,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) => {
|
||||||
|
|||||||
@@ -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>
|
||||||
@@ -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 dark:hover:text-white"
|
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')"
|
||||||
>
|
>
|
||||||
|
|||||||
@@ -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:hover:text-white 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>
|
||||||
|
|||||||
@@ -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");
|
||||||
|
|
||||||
|
|||||||
@@ -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(
|
||||||
|
|||||||
@@ -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";
|
||||||
|
|
||||||
|
|||||||
@@ -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[@]}" "$@"
|
||||||
|
|||||||
Reference in New Issue
Block a user