mirror of
https://gitee.com/spark-store-project/spark-store
synced 2026-04-26 09:20:18 +08:00
feat(install): add app uninstall functionality
This commit is contained in:
@@ -22,9 +22,10 @@
|
|||||||
- [x] 可以展示应用列表及其详细信息
|
- [x] 可以展示应用列表及其详细信息
|
||||||
- [ ] 实现应用下载&下载列表管理
|
- [ ] 实现应用下载&下载列表管理
|
||||||
- [x] 实现应用安装&重试安装
|
- [x] 实现应用安装&重试安装
|
||||||
- [ ] 实现应用卸载
|
- [x] 实现应用卸载
|
||||||
- [ ] 实现应用更新
|
- [ ] 实现应用更新
|
||||||
- [ ] 支持显示本地是否已经安装
|
- [ ] 显示本地已安装app
|
||||||
|
- [x] 支持显示本地是否已经安装
|
||||||
- [x] 实现应用搜索
|
- [x] 实现应用搜索
|
||||||
|
|
||||||
|
|
||||||
|
|||||||
@@ -18,6 +18,25 @@ const tasks = new Map<number, InstallTask>();
|
|||||||
|
|
||||||
let idle = true; // Indicates if the installation manager is idle
|
let idle = true; // Indicates if the installation manager is idle
|
||||||
|
|
||||||
|
const checkSuperUserCommand = async (): Promise<string> => {
|
||||||
|
let superUserCmd = '';
|
||||||
|
const execAsync = promisify(exec);
|
||||||
|
if (process.getuid && process.getuid() !== 0) {
|
||||||
|
const { stdout, stderr } = await execAsync('which pkexec');
|
||||||
|
if (stderr) {
|
||||||
|
logger.error('没有找到 pkexec 命令');
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
logger.info(`找到提升权限命令: ${stdout.trim()}`);
|
||||||
|
superUserCmd = stdout.trim();
|
||||||
|
|
||||||
|
if (superUserCmd.length === 0) {
|
||||||
|
logger.error('没有找到提升权限的命令 pkexec!');
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return superUserCmd;
|
||||||
|
}
|
||||||
|
|
||||||
// Listen for download requests from renderer process
|
// Listen for download requests from renderer process
|
||||||
ipcMain.on('queue-install', async (event, download_json) => {
|
ipcMain.on('queue-install', async (event, download_json) => {
|
||||||
const download = JSON.parse(download_json);
|
const download = JSON.parse(download_json);
|
||||||
@@ -50,28 +69,7 @@ ipcMain.on('queue-install', async (event, download_json) => {
|
|||||||
const webContents = event.sender;
|
const webContents = event.sender;
|
||||||
|
|
||||||
// 开始组装安装命令
|
// 开始组装安装命令
|
||||||
const execAsync = promisify(exec);
|
let superUserCmd = await checkSuperUserCommand();
|
||||||
let superUserCmd = '';
|
|
||||||
if (process.getuid && process.getuid() !== 0) {
|
|
||||||
const { stdout, stderr } = await execAsync('which pkexec');
|
|
||||||
if (stderr) {
|
|
||||||
logger.error('没有找到 pkexec 命令');
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
logger.info(`找到提升权限命令: ${stdout.trim()}`);
|
|
||||||
superUserCmd = stdout.trim();
|
|
||||||
|
|
||||||
if (superUserCmd.length === 0) {
|
|
||||||
logger.error('没有找到提升权限的命令 pkexec, 无法继续安装');
|
|
||||||
webContents.send('install-error', {
|
|
||||||
id,
|
|
||||||
time: Date.now(),
|
|
||||||
message: '无法找到提升权限的命令 pkexec,请手动安装'
|
|
||||||
});
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
let execCommand = '';
|
let execCommand = '';
|
||||||
let execParams = [];
|
let execParams = [];
|
||||||
if (superUserCmd.length > 0) {
|
if (superUserCmd.length > 0) {
|
||||||
@@ -201,3 +199,55 @@ ipcMain.handle('check-installed', async (_event, pkgname: string) => {
|
|||||||
});
|
});
|
||||||
return isInstalled;
|
return isInstalled;
|
||||||
});
|
});
|
||||||
|
|
||||||
|
ipcMain.on('remove-installed', async (_event, pkgname: string) => {
|
||||||
|
const webContents = _event.sender;
|
||||||
|
if (!pkgname) {
|
||||||
|
logger.warn('remove-installed missing pkgname');
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
logger.info(`卸载已安装应用: ${pkgname}`);
|
||||||
|
|
||||||
|
let superUserCmd = await checkSuperUserCommand();
|
||||||
|
let execCommand = '';
|
||||||
|
let execParams = [];
|
||||||
|
if (superUserCmd.length > 0) {
|
||||||
|
execCommand = superUserCmd;
|
||||||
|
execParams.push('/usr/bin/apm');
|
||||||
|
} else {
|
||||||
|
execCommand = '/usr/bin/apm';
|
||||||
|
}
|
||||||
|
let child = spawn(execCommand, [...execParams, 'remove', '-y', pkgname], {
|
||||||
|
shell: true,
|
||||||
|
env: process.env
|
||||||
|
});
|
||||||
|
let output = '';
|
||||||
|
|
||||||
|
child.stdout.on('data', (data) => {
|
||||||
|
output += data.toString();
|
||||||
|
});
|
||||||
|
|
||||||
|
child.on('close', (code) => {
|
||||||
|
const success = code === 0;
|
||||||
|
// 拼接json消息
|
||||||
|
const messageJSONObj = {
|
||||||
|
message: success ? '卸载完成' : `卸载失败,退出码 ${code}`,
|
||||||
|
stdout: output,
|
||||||
|
stderr: ''
|
||||||
|
};
|
||||||
|
|
||||||
|
if (success) {
|
||||||
|
logger.info(messageJSONObj);
|
||||||
|
} else {
|
||||||
|
logger.error(messageJSONObj);
|
||||||
|
}
|
||||||
|
|
||||||
|
webContents.send('remove-complete', {
|
||||||
|
id: 0,
|
||||||
|
success: success,
|
||||||
|
time: Date.now(),
|
||||||
|
exitCode: code,
|
||||||
|
message: JSON.stringify(messageJSONObj)
|
||||||
|
});
|
||||||
|
});
|
||||||
|
});
|
||||||
@@ -38,7 +38,7 @@ import AppDetailModal from './components/AppDetailModal.vue';
|
|||||||
import ScreenPreview from './components/ScreenPreview.vue';
|
import ScreenPreview from './components/ScreenPreview.vue';
|
||||||
import DownloadQueue from './components/DownloadQueue.vue';
|
import DownloadQueue from './components/DownloadQueue.vue';
|
||||||
import DownloadDetail from './components/DownloadDetail.vue';
|
import DownloadDetail from './components/DownloadDetail.vue';
|
||||||
import { APM_STORE_ARCHITECTURE, APM_STORE_BASE_URL, currentApp } from './global/storeConfig';
|
import { APM_STORE_ARCHITECTURE, APM_STORE_BASE_URL, currentApp, currentAppIsInstalled } from './global/storeConfig';
|
||||||
import { downloads } from './global/downloadStatus';
|
import { downloads } from './global/downloadStatus';
|
||||||
import { handleInstall, handleRetry, handleRemove } from './modeuls/processInstall';
|
import { handleInstall, handleRetry, handleRemove } from './modeuls/processInstall';
|
||||||
|
|
||||||
@@ -60,7 +60,6 @@ const showModal = ref(false);
|
|||||||
const showPreview = ref(false);
|
const showPreview = ref(false);
|
||||||
const currentScreenIndex = ref(0);
|
const currentScreenIndex = ref(0);
|
||||||
const screenshots = ref([]);
|
const screenshots = ref([]);
|
||||||
const currentAppIsInstalled = ref(false);
|
|
||||||
const loading = ref(true);
|
const loading = ref(true);
|
||||||
const showDownloadDetailModal = ref(false);
|
const showDownloadDetailModal = ref(false);
|
||||||
const currentDownload = ref(null);
|
const currentDownload = ref(null);
|
||||||
|
|||||||
@@ -2,4 +2,7 @@ import { ref } from "vue";
|
|||||||
|
|
||||||
export const APM_STORE_BASE_URL=import.meta.env.VITE_APM_STORE_BASE_URL;
|
export const APM_STORE_BASE_URL=import.meta.env.VITE_APM_STORE_BASE_URL;
|
||||||
export const APM_STORE_ARCHITECTURE='amd64-apm';
|
export const APM_STORE_ARCHITECTURE='amd64-apm';
|
||||||
|
|
||||||
|
// 下面的变量用于存储当前应用的信息,其实用在多个组件中
|
||||||
export const currentApp = ref<any>(null);
|
export const currentApp = ref<any>(null);
|
||||||
|
export const currentAppIsInstalled = ref(false);
|
||||||
|
|||||||
@@ -2,7 +2,7 @@
|
|||||||
// console.log('[Receive Main-process message]:', ...args)
|
// console.log('[Receive Main-process message]:', ...args)
|
||||||
// })
|
// })
|
||||||
|
|
||||||
import { currentApp } from "../global/storeConfig";
|
import { currentApp, currentAppIsInstalled } from "../global/storeConfig";
|
||||||
import { APM_STORE_BASE_URL, APM_STORE_ARCHITECTURE } from "../global/storeConfig";
|
import { APM_STORE_BASE_URL, APM_STORE_ARCHITECTURE } from "../global/storeConfig";
|
||||||
import { downloads } from "../global/downloadStatus";
|
import { downloads } from "../global/downloadStatus";
|
||||||
|
|
||||||
@@ -53,11 +53,20 @@ export const handleRetry = (download_: DownloadItem) => {
|
|||||||
window.ipcRenderer.send('queue-install', JSON.stringify(download_));
|
window.ipcRenderer.send('queue-install', JSON.stringify(download_));
|
||||||
};
|
};
|
||||||
|
|
||||||
export const handleRemove = (download_: DownloadItem) => {
|
export const handleRemove = () => {
|
||||||
if (!currentApp.value?.Pkgname) return;
|
if (!currentApp.value?.Pkgname) return;
|
||||||
console.log('请求卸载: ', currentApp.value.Pkgname);
|
window.ipcRenderer.send('remove-installed', currentApp.value.Pkgname);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
window.ipcRenderer.on('remove-complete', (_event, log: DownloadResult) => {
|
||||||
|
if (log.success) {
|
||||||
|
currentAppIsInstalled.value = false;
|
||||||
|
} else {
|
||||||
|
currentAppIsInstalled.value = true;
|
||||||
|
console.error('卸载失败:', log.message);
|
||||||
|
}
|
||||||
|
});
|
||||||
|
|
||||||
window.ipcRenderer.on('install-status', (_event, log: InstallLog) => {
|
window.ipcRenderer.on('install-status', (_event, log: InstallLog) => {
|
||||||
const downloadObj: any = downloads.value.find(d => d.id === log.id);
|
const downloadObj: any = downloads.value.find(d => d.id === log.id);
|
||||||
downloadObj.status = log.message;
|
downloadObj.status = log.message;
|
||||||
|
|||||||
Reference in New Issue
Block a user