refactor: improve code formatting and consistency across components

- Updated button and span elements in ThemeToggle.vue and TopActions.vue for better readability.
- Enhanced UninstallConfirmModal.vue and UpdateAppsModal.vue with consistent indentation and spacing.
- Refactored downloadStatus.ts and storeConfig.ts for improved code clarity.
- Standardized string quotes and spacing in typedefinition.ts and processInstall.ts.
- Ensured consistent use of arrow functions and improved variable declarations throughout the codebase.
This commit is contained in:
Elysia
2026-02-12 18:32:41 +08:00
parent e11740ad4c
commit 6622e70033
29 changed files with 1681 additions and 1042 deletions

View File

@@ -1,11 +1,11 @@
import { ipcMain, WebContents } from 'electron';
import { spawn, ChildProcess, exec } from 'node:child_process';
import { promisify } from 'node:util';
import pino from 'pino';
import { ipcMain, WebContents } from "electron";
import { spawn, ChildProcess, exec } from "node:child_process";
import { promisify } from "node:util";
import pino from "pino";
import { ChannelPayload, InstalledAppInfo } from '../../typedefinition';
import { ChannelPayload, InstalledAppInfo } from "../../typedefinition";
const logger = pino({ 'name': 'install-manager' });
const logger = pino({ name: "install-manager" });
type InstallTask = {
id: number;
@@ -16,67 +16,69 @@ type InstallTask = {
webContents: WebContents | null;
};
const SHELL_CALLER_PATH = '/opt/apm-store/extras/shell-caller.sh';
const SHELL_CALLER_PATH = "/opt/apm-store/extras/shell-caller.sh";
export const tasks = new Map<number, InstallTask>();
let idle = true; // Indicates if the installation manager is idle
const checkSuperUserCommand = async (): Promise<string> => {
let superUserCmd = '';
let superUserCmd = "";
const execAsync = promisify(exec);
if (process.getuid && process.getuid() !== 0) {
const { stdout, stderr } = await execAsync('which /usr/bin/pkexec');
const { stdout, stderr } = await execAsync("which /usr/bin/pkexec");
if (stderr) {
logger.error('没有找到 pkexec 命令');
logger.error("没有找到 pkexec 命令");
return;
}
logger.info(`找到提升权限命令: ${stdout.trim()}`);
superUserCmd = stdout.trim();
if (superUserCmd.length === 0) {
logger.error('没有找到提升权限的命令 pkexec!');
logger.error("没有找到提升权限的命令 pkexec!");
}
}
return superUserCmd;
}
};
const runCommandCapture = async (execCommand: string, execParams: string[]) => {
return await new Promise<{ code: number; stdout: string; stderr: string }>((resolve) => {
const child = spawn(execCommand, execParams, {
shell: true,
env: process.env
});
return await new Promise<{ code: number; stdout: string; stderr: string }>(
(resolve) => {
const child = spawn(execCommand, execParams, {
shell: true,
env: process.env,
});
let stdout = '';
let stderr = '';
let stdout = "";
let stderr = "";
child.stdout?.on('data', (data) => {
stdout += data.toString();
});
child.stdout?.on("data", (data) => {
stdout += data.toString();
});
child.stderr?.on('data', (data) => {
stderr += data.toString();
});
child.stderr?.on("data", (data) => {
stderr += data.toString();
});
child.on('error', (err) => {
resolve({ code: -1, stdout, stderr: err.message });
});
child.on("error", (err) => {
resolve({ code: -1, stdout, stderr: err.message });
});
child.on('close', (code) => {
resolve({ code: typeof code === 'number' ? code : -1, stdout, stderr });
});
});
child.on("close", (code) => {
resolve({ code: typeof code === "number" ? code : -1, stdout, stderr });
});
},
);
};
const parseInstalledList = (output: string) => {
const apps: Array<InstalledAppInfo> = [];
const lines = output.split('\n');
const lines = output.split("\n");
for (const line of lines) {
const trimmed = line.trim();
if (!trimmed) continue;
if (trimmed.startsWith('Listing')) continue;
if (trimmed.startsWith('[INFO]')) continue;
if (trimmed.startsWith("Listing")) continue;
if (trimmed.startsWith("[INFO]")) continue;
const match = trimmed.match(/^(\S+)\/\S+,\S+\s+(\S+)\s+(\S+)\s+\[(.+)\]$/);
if (!match) continue;
@@ -85,32 +87,40 @@ const parseInstalledList = (output: string) => {
version: match[2],
arch: match[3],
flags: match[4],
raw: trimmed
raw: trimmed,
});
}
return apps;
};
const parseUpgradableList = (output: string) => {
const apps: Array<{ pkgname: string; newVersion: string; currentVersion: string; raw: string }> = [];
const lines = output.split('\n');
const apps: Array<{
pkgname: string;
newVersion: string;
currentVersion: string;
raw: string;
}> = [];
const lines = output.split("\n");
for (const line of lines) {
const trimmed = line.trim();
if (!trimmed) continue;
if (trimmed.startsWith('Listing')) continue;
if (trimmed.startsWith('[INFO]')) continue;
if (trimmed.includes('=') && !trimmed.includes('/')) continue;
if (trimmed.startsWith("Listing")) continue;
if (trimmed.startsWith("[INFO]")) continue;
if (trimmed.includes("=") && !trimmed.includes("/")) continue;
if (!trimmed.includes('/')) continue;
if (!trimmed.includes("/")) continue;
const tokens = trimmed.split(/\s+/);
if (tokens.length < 2) continue;
const pkgToken = tokens[0];
const pkgname = pkgToken.split('/')[0];
const newVersion = tokens[1] || '';
const currentMatch = trimmed.match(/\[(?:upgradable from|from):\s*([^\]\s]+)\]/i);
const currentToken = tokens[5] || '';
const currentVersion = currentMatch?.[1] || currentToken.replace('[', '').replace(']', '');
const pkgname = pkgToken.split("/")[0];
const newVersion = tokens[1] || "";
const currentMatch = trimmed.match(
/\[(?:upgradable from|from):\s*([^\]\s]+)\]/i,
);
const currentToken = tokens[5] || "";
const currentVersion =
currentMatch?.[1] || currentToken.replace("[", "").replace("]", "");
if (!pkgname) continue;
apps.push({ pkgname, newVersion, currentVersion, raw: trimmed });
@@ -119,30 +129,30 @@ const parseUpgradableList = (output: string) => {
};
// 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 { id, pkgname } = download || {};
if (!id || !pkgname) {
logger.warn('passed arguments missing id or pkgname');
logger.warn("passed arguments missing id or pkgname");
return;
}
logger.info(`收到下载任务: ${id}, 软件包名称: ${pkgname}`);
// 避免重复添加同一任务,但允许重试下载
if (tasks.has(id) && !download.retry) {
tasks.get(id)?.webContents.send('install-log', {
tasks.get(id)?.webContents.send("install-log", {
id,
time: Date.now(),
message: `任务id ${id} 已在列表中,忽略重复添加`
message: `任务id ${id} 已在列表中,忽略重复添加`,
});
tasks.get(id)?.webContents.send('install-complete', {
tasks.get(id)?.webContents.send("install-complete", {
id: id,
success: false,
time: Date.now(),
exitCode: -1,
message: `{"message":"任务id ${id} 已在列表中,忽略重复添加","stdout":"","stderr":""}`
message: `{"message":"任务id ${id} 已在列表中,忽略重复添加","stdout":"","stderr":""}`,
});
return;
}
@@ -151,7 +161,7 @@ ipcMain.on('queue-install', async (event, download_json) => {
// 开始组装安装命令
const superUserCmd = await checkSuperUserCommand();
let execCommand = '';
let execCommand = "";
const execParams = [];
if (superUserCmd.length > 0) {
execCommand = superUserCmd;
@@ -159,7 +169,7 @@ ipcMain.on('queue-install', async (event, download_json) => {
} else {
execCommand = SHELL_CALLER_PATH;
}
execParams.push('apm', 'install', '-y', pkgname);
execParams.push("apm", "install", "-y", pkgname);
const task: InstallTask = {
id,
@@ -167,7 +177,7 @@ ipcMain.on('queue-install', async (event, download_json) => {
execCommand,
execParams,
process: null,
webContents
webContents,
};
tasks.set(id, task);
if (idle) processNextInQueue(0);
@@ -179,53 +189,53 @@ function processNextInQueue(index: number) {
idle = false;
const task = Array.from(tasks.values())[index];
const webContents = task.webContents;
let stdoutData = '';
let stderrData = '';
let stdoutData = "";
let stderrData = "";
webContents.send('install-status', {
webContents.send("install-status", {
id: task.id,
time: Date.now(),
message: 'installing'
})
webContents.send('install-log', {
id: task.id,
time: Date.now(),
message: `开始执行: ${task.execCommand} ${task.execParams.join(' ')}`
message: "installing",
});
logger.info(`启动安装命令: ${task.execCommand} ${task.execParams.join(' ')}`);
webContents.send("install-log", {
id: task.id,
time: Date.now(),
message: `开始执行: ${task.execCommand} ${task.execParams.join(" ")}`,
});
logger.info(`启动安装命令: ${task.execCommand} ${task.execParams.join(" ")}`);
const child = spawn(task.execCommand, task.execParams, {
shell: true,
env: process.env
env: process.env,
});
task.process = child;
// 监听 stdout
child.stdout.on('data', (data) => {
child.stdout.on("data", (data) => {
stdoutData += data.toString();
webContents.send('install-log', {
webContents.send("install-log", {
id: task.id,
time: Date.now(),
message: data.toString()
message: data.toString(),
});
});
// 监听 stderr
child.stderr.on('data', (data) => {
child.stderr.on("data", (data) => {
stderrData += data.toString();
webContents.send('install-log', {
webContents.send("install-log", {
id: task.id,
time: Date.now(),
message: data.toString()
message: data.toString(),
});
});
child.on('close', (code) => {
child.on("close", (code) => {
const success = code === 0;
// 拼接json消息
const messageJSONObj = {
message: success ? '安装完成' : `安装失败,退出码 ${code}`,
message: success ? "安装完成" : `安装失败,退出码 ${code}`,
stdout: stdoutData,
stderr: stderrData
stderr: stderrData,
};
if (success) {
@@ -234,42 +244,45 @@ function processNextInQueue(index: number) {
logger.error(messageJSONObj);
}
webContents.send('install-complete', {
webContents.send("install-complete", {
id: task.id,
success: success,
time: Date.now(),
exitCode: code,
message: JSON.stringify(messageJSONObj)
message: JSON.stringify(messageJSONObj),
});
tasks.delete(task.id);
idle = true;
if (tasks.size > 0)
processNextInQueue(0);
if (tasks.size > 0) processNextInQueue(0);
});
}
ipcMain.handle('check-installed', async (_event, pkgname: string) => {
ipcMain.handle("check-installed", async (_event, pkgname: string) => {
if (!pkgname) {
logger.warn('check-installed missing pkgname');
logger.warn("check-installed missing pkgname");
return false;
}
let isInstalled = false;
logger.info(`检查应用是否已安装: ${pkgname}`);
const child = spawn(SHELL_CALLER_PATH, ['apm', 'list', '--installed', pkgname], {
shell: true,
env: process.env
});
const child = spawn(
SHELL_CALLER_PATH,
["apm", "list", "--installed", pkgname],
{
shell: true,
env: process.env,
},
);
let output = '';
child.stdout.on('data', (data) => {
let output = "";
child.stdout.on("data", (data) => {
output += data.toString();
});
await new Promise<void>((resolve) => {
child.on('close', (code) => {
child.on("close", (code) => {
if (code === 0 && output.includes(pkgname)) {
isInstalled = true;
logger.info(`应用已安装: ${pkgname}`);
@@ -282,16 +295,16 @@ ipcMain.handle('check-installed', async (_event, pkgname: string) => {
return isInstalled;
});
ipcMain.on('remove-installed', async (_event, pkgname: string) => {
ipcMain.on("remove-installed", async (_event, pkgname: string) => {
const webContents = _event.sender;
if (!pkgname) {
logger.warn('remove-installed missing pkgname');
logger.warn("remove-installed missing pkgname");
return;
}
logger.info(`卸载已安装应用: ${pkgname}`);
const superUserCmd = await checkSuperUserCommand();
let execCommand = '';
let execCommand = "";
const execParams = [];
if (superUserCmd.length > 0) {
execCommand = superUserCmd;
@@ -299,25 +312,29 @@ ipcMain.on('remove-installed', async (_event, pkgname: string) => {
} else {
execCommand = SHELL_CALLER_PATH;
}
const child = spawn(execCommand, [...execParams, 'apm', 'remove', '-y', pkgname], {
shell: true,
env: process.env
});
let output = '';
child.stdout.on('data', (data) => {
const child = spawn(
execCommand,
[...execParams, "apm", "remove", "-y", pkgname],
{
shell: true,
env: process.env,
},
);
let output = "";
child.stdout.on("data", (data) => {
const chunk = data.toString();
output += chunk;
webContents.send('remove-progress', chunk);
webContents.send("remove-progress", chunk);
});
child.on('close', (code) => {
child.on("close", (code) => {
const success = code === 0;
// 拼接json消息
const messageJSONObj = {
message: success ? '卸载完成' : `卸载失败,退出码 ${code}`,
message: success ? "卸载完成" : `卸载失败,退出码 ${code}`,
stdout: output,
stderr: ''
stderr: "",
};
if (success) {
@@ -326,26 +343,28 @@ ipcMain.on('remove-installed', async (_event, pkgname: string) => {
logger.error(messageJSONObj);
}
webContents.send('remove-complete', {
webContents.send("remove-complete", {
id: 0,
success: success,
time: Date.now(),
exitCode: code,
message: JSON.stringify(messageJSONObj)
message: JSON.stringify(messageJSONObj),
} satisfies ChannelPayload);
});
});
ipcMain.handle('list-upgradable', async () => {
const { code, stdout, stderr } = await runCommandCapture(
SHELL_CALLER_PATH,
['apm', 'list', '--upgradable']);
ipcMain.handle("list-upgradable", async () => {
const { code, stdout, stderr } = await runCommandCapture(SHELL_CALLER_PATH, [
"apm",
"list",
"--upgradable",
]);
if (code !== 0) {
logger.error(`list-upgradable failed: ${stderr || stdout}`);
return {
success: false,
message: stderr || stdout || `list-upgradable failed with code ${code}`,
apps: []
apps: [],
};
}
@@ -353,20 +372,25 @@ ipcMain.handle('list-upgradable', async () => {
return { success: true, apps };
});
ipcMain.handle('list-installed', async () => {
ipcMain.handle("list-installed", async () => {
const superUserCmd = await checkSuperUserCommand();
const execCommand = superUserCmd.length > 0 ? superUserCmd : SHELL_CALLER_PATH;
const execParams = superUserCmd.length > 0
? [SHELL_CALLER_PATH, 'apm', 'list', '--installed']
: ['apm', 'list', '--installed'];
const execCommand =
superUserCmd.length > 0 ? superUserCmd : SHELL_CALLER_PATH;
const execParams =
superUserCmd.length > 0
? [SHELL_CALLER_PATH, "apm", "list", "--installed"]
: ["apm", "list", "--installed"];
const { code, stdout, stderr } = await runCommandCapture(execCommand, execParams);
const { code, stdout, stderr } = await runCommandCapture(
execCommand,
execParams,
);
if (code !== 0) {
logger.error(`list-installed failed: ${stderr || stdout}`);
return {
success: false,
message: stderr || stdout || `list-installed failed with code ${code}`,
apps: []
apps: [],
};
}
@@ -374,19 +398,24 @@ ipcMain.handle('list-installed', async () => {
return { success: true, apps };
});
ipcMain.handle('uninstall-installed', async (_event, pkgname: string) => {
ipcMain.handle("uninstall-installed", async (_event, pkgname: string) => {
if (!pkgname) {
logger.warn('uninstall-installed missing pkgname');
return { success: false, message: 'missing pkgname' };
logger.warn("uninstall-installed missing pkgname");
return { success: false, message: "missing pkgname" };
}
const superUserCmd = await checkSuperUserCommand();
const execCommand = superUserCmd.length > 0 ? superUserCmd : SHELL_CALLER_PATH;
const execParams = superUserCmd.length > 0
? [SHELL_CALLER_PATH, 'apm', 'remove', '-y', pkgname]
: ['apm', 'remove', '-y', pkgname];
const execCommand =
superUserCmd.length > 0 ? superUserCmd : SHELL_CALLER_PATH;
const execParams =
superUserCmd.length > 0
? [SHELL_CALLER_PATH, "apm", "remove", "-y", pkgname]
: ["apm", "remove", "-y", pkgname];
const { code, stdout, stderr } = await runCommandCapture(execCommand, execParams);
const { code, stdout, stderr } = await runCommandCapture(
execCommand,
execParams,
);
const success = code === 0;
if (success) {
@@ -397,24 +426,28 @@ ipcMain.handle('uninstall-installed', async (_event, pkgname: string) => {
return {
success,
message: success ? '卸载完成' : (stderr || stdout || `卸载失败,退出码 ${code}`)
message: success
? "卸载完成"
: stderr || stdout || `卸载失败,退出码 ${code}`,
};
});
ipcMain.handle('launch-app', async (_event, pkgname: string) => {
ipcMain.handle("launch-app", async (_event, pkgname: string) => {
if (!pkgname) {
logger.warn('No pkgname provided for launch-app');
logger.warn("No pkgname provided for launch-app");
}
const execCommand = "/opt/apm-store/extras/host-spawn";
const execParams = ['/opt/apm-store/extras/apm-launcher', 'launch', pkgname ];
const execParams = ["/opt/apm-store/extras/apm-launcher", "launch", pkgname];
logger.info(`Launching app: ${pkgname} with command: ${execCommand} ${execParams.join(' ')}`);
logger.info(
`Launching app: ${pkgname} with command: ${execCommand} ${execParams.join(" ")}`,
);
spawn(execCommand, execParams, {
shell: false,
env: process.env,
detached: true,
stdio: 'ignore'
stdio: "ignore",
}).unref();
});
});