[Feat] Download PLS from API

This commit is contained in:
Minoricew
2025-06-09 18:02:11 +08:00
parent 8ab55bc93c
commit ed29f1b86f
13 changed files with 662 additions and 55 deletions

View File

@@ -1,6 +1,6 @@
{
"name": "HugoAura",
"version": "0.1.1-pre-III",
"version": "0.1.1-pre-IV",
"description": "Aura for SeewoHugo",
"main": "app.asar/main.js",
"dependencies": {},

View File

@@ -74,6 +74,7 @@ const buildIpcMain = (electron) => {
};
const { applyConfigIpcHandler } = require("./ipcModules/configIpcHandler");
const { applyFsIpcHandler } = require("./ipcModules/fsIpcHandler");
const { applyPlsIpcHandler } = require("./ipcModules/plsIpcHandler");
ipcMain.handle("$aura.base.restartApplication", async () => {
@@ -82,6 +83,7 @@ const buildIpcMain = (electron) => {
});
applyConfigIpcHandler(ipcMain);
applyFsIpcHandler(ipcMain);
applyPlsIpcHandler(ipcMain);
};

View File

@@ -0,0 +1,186 @@
// @ts-check
const __SCOPE = "main";
const { exec } = require("child_process");
const nodeHttp = require("http");
const nodeHttps = require("https");
const fs = require("fs");
const path = require("path");
const { genRandomHex } = require("../../../utils/crypto");
const composableFunctions = {
/**
*
* @param {string} url
* @param {string} targetPath
* @param {((arg: DownloadTask) => any)} progressCallback
*/
downloadFile: async (url, targetPath, progressCallback) => {
if (!progressCallback) return false;
const taskId = genRandomHex();
/**
* @type {DownloadTask}
*/
const failedTemplate = {
id: taskId,
progress: 100,
status: "failed",
dlUrl: url,
savePath: targetPath,
message: "",
};
if (!url || !targetPath) {
failedTemplate.message = "Invalid arg";
progressCallback(failedTemplate);
return false;
}
if (!fs.existsSync(path.dirname(targetPath))) {
failedTemplate.message = "Path not exists";
progressCallback(failedTemplate);
}
const httpModuleIns = url.startsWith("https") ? nodeHttps : nodeHttp;
global.__HUGO_AURA__.fsTasks?.downloadTasks.set(taskId, {
status: "waiting",
cancelReq: null,
});
const fsStream = fs.createWriteStream(targetPath);
const dlReq = httpModuleIns.get(url, (response) => {
if (response.statusCode !== 200) {
fsStream.close();
failedTemplate.message = `Request error: HTTP ${response.statusCode}`;
progressCallback(failedTemplate);
return false;
}
const contentLength = response.headers["content-length"];
// @ts-expect-error
const totalBytes = parseInt(contentLength, 10) || 0; // No error handling 😆
let curRecvBytes = 0;
global.__HUGO_AURA__.fsTasks?.downloadTasks.set(taskId, {
status: "progressing",
cancelReq: () => {
dlReq.destroy();
fsStream.close();
fs.unlink(targetPath, () => {});
global.__HUGO_AURA__.fsTasks?.downloadTasks.delete(taskId);
progressCallback({
id: taskId,
progress: 100,
curBytes: curRecvBytes,
totalBytes: totalBytes,
status: "cancelled",
dlUrl: url,
savePath: targetPath,
});
},
});
response.on("data", (chunk) => {
curRecvBytes += chunk.length;
const curProgress =
totalBytes > 0 ? (curRecvBytes / totalBytes) * 100 : 0;
progressCallback({
id: taskId,
progress: curProgress.toFixed(2),
curBytes: curRecvBytes,
totalBytes: totalBytes,
status: "progressing",
dlUrl: url,
savePath: targetPath,
});
});
response.pipe(fsStream);
fsStream.on("finish", () => {
fsStream.close();
progressCallback({
id: taskId,
progress: (100).toFixed(2),
curBytes: curRecvBytes,
totalBytes: totalBytes,
status: "done",
dlUrl: url,
savePath: targetPath,
});
});
global.__HUGO_AURA__.fsTasks?.downloadTasks.delete(taskId);
return true;
});
dlReq.on("error", (e) => {
fsStream.close();
fs.unlink(targetPath, () => {});
failedTemplate.message =
"Request error: Unexpected error while downloading file";
failedTemplate.errorObj = e;
progressCallback(failedTemplate);
global.__HUGO_AURA__.fsTasks?.downloadTasks.delete(taskId);
return false;
});
},
};
/**
*
* @param {import("electron").IpcMain} ipcMain
*/
const applyFsIpcHandler = (ipcMain) => {
const methodBase = "$aura.fs";
global.__HUGO_AURA__.fsTasks = {
downloadTasks: new Map(),
};
ipcMain.handle(
`${methodBase}.dl.cancelDownloadTask`,
/**
*
* @param {import("electron").IpcMainInvokeEvent} _evt
* @param {{ targetTaskId: string }} arg
* @returns {{ success: boolean, error: string | null }}
*/
(_evt, arg) => {
if (!arg.targetTaskId) {
return {
success: false,
error: "ARG_INVALID",
};
}
if (!global.__HUGO_AURA__.fsTasks?.downloadTasks.has(arg.targetTaskId)) {
return {
success: false,
error: "TASK_ID_NOT_FOUND",
};
}
const taskObj = global.__HUGO_AURA__.fsTasks.downloadTasks.get(
arg.targetTaskId
);
if (!taskObj?.cancelReq) {
return {
success: false,
error: "TASK_NOT_STARTED",
};
}
taskObj.cancelReq();
return {
success: true,
error: null,
};
}
);
};
module.exports = { fsComposables: composableFunctions, applyFsIpcHandler };

View File

@@ -5,6 +5,8 @@ const __SCOPE = "main";
const { exec } = require("child_process");
const fs = require("fs");
const path = require("path");
const nodeHttps = require("https");
const { fsComposables } = require("./fsIpcHandler");
const functions = {
querySvcDetail: (
@@ -47,8 +49,9 @@ const functions = {
* @returns {Promise<{ success: boolean, errorObj?: Error }>}
*/
execCommand: (logHeader, binPath, command) => {
const processedPath = binPath.includes(" ") ? `"${binPath}"` : binPath;
return new Promise((resolve) => {
exec(`"${binPath}" ${command}`, (error, stdout, stderr) => {
exec(`${processedPath} ${command}`, (error, stdout, stderr) => {
if (error) {
console.error(`${logHeader} Failed to execute command:`, error);
resolve({ success: false, errorObj: error });
@@ -58,6 +61,103 @@ const functions = {
});
});
},
/**
*
* @param {"stable" | "alpha"} channel
* @param {(arg: DownloadTask) => any} callbackFn
* @param {string} binPath
*/
handlePLSDownload: async (channel, callbackFn, binPath) => {
// TODO: Channel selection
const apiInfo = global.__HUGO_AURA_API__;
let plsVersionInfo = {};
const getVerPromise = new Promise((resolve) => {
// ↓ 目前 channel param 没有什么用处
nodeHttps
.get(
`${apiInfo.baseUrl}${apiInfo.plsUpdate}?channel=${channel}`,
(rep) => {
let dataChunk = "";
rep.on("data", (chunk) => {
dataChunk += chunk;
});
rep.on("end", () => {
resolve({
success: true,
data: dataChunk,
});
});
}
)
.on("error", (e) => {
resolve({
success: false,
data: null,
errorObj: e,
});
});
});
const rawResInfo = await getVerPromise;
if (!rawResInfo.success) {
callbackFn({
id: "",
progress: 0,
status: "failed",
dlUrl: null,
savePath: null,
message: "未能获取 PLS 版本信息",
errorObj: rawResInfo.errorObj ? rawResInfo.errorObj : null,
});
return false;
}
try {
plsVersionInfo = JSON.parse(rawResInfo.data);
} catch (e) {
callbackFn({
id: "",
progress: 0,
status: "failed",
dlUrl: null,
savePath: null,
message: "PLS 版本信息解析失败",
errorObj: e,
});
console.error(
"[HugoAura / IPC / PLS] Error querying PLS version info:",
e
);
return false;
}
let deviceArch = process.env.PROCESSOR_ARCHITEW6432
? process.env.PROCESSOR_ARCHITEW6432
: process.env.PROCESSOR_ARCHITECTURE;
// @ts-expect-error
deviceArch = deviceArch.toLowerCase();
if (!Object.keys(plsVersionInfo.data.downloadUrl).includes(deviceArch)) {
callbackFn({
id: "",
progress: 0,
status: "failed",
dlUrl: null,
savePath: null,
message: `处理器架构识别失败, 检测到的架构: ${deviceArch}`,
});
return false;
}
fsComposables.downloadFile(
plsVersionInfo.data.downloadUrl[deviceArch],
binPath,
callbackFn
);
},
};
/**
@@ -67,8 +167,8 @@ const functions = {
const applyPlsIpcHandler = (ipcMain) => {
const methodBase = "$aura.pls";
const PLS_INSTALL_DIR = path.join("C:\\Program Files", "HugoAura PLS");
const PLS_BIN_PATH = path.join(PLS_INSTALL_DIR, "bin", "HugoAura-PLS.exe");
const PLS_INSTALL_DIR = path.join("C:\\Program Files", "HugoAura PLS", "bin");
const PLS_BIN_PATH = path.join(PLS_INSTALL_DIR, "HugoAura-PLS.exe");
const PLS_SVC_NAME = "HugoAuraPLS";
const isPlsDetached = process.argv.includes("--pls-detach");
@@ -106,6 +206,48 @@ const applyPlsIpcHandler = (ipcMain) => {
}
);
ipcMain.handle(
`${methodBase}.ensurePlsInstallDir`,
/**
*
* @param {import("electron").IpcMainInvokeEvent} _event
* @param {any} _arg
* @returns {{ success: boolean; data: { alreadyExists: boolean; createdDir: string; }; error?: Error }}
*/
(_event, _arg) => {
const alreadyExists = fs.existsSync(PLS_INSTALL_DIR);
if (alreadyExists) {
return {
success: true,
data: {
alreadyExists: true,
createdDir: PLS_INSTALL_DIR,
},
};
}
try {
fs.mkdirSync(PLS_INSTALL_DIR);
return {
success: true,
data: {
alreadyExists: false,
createdDir: PLS_INSTALL_DIR,
},
};
} catch (error) {
return {
success: false,
data: {
alreadyExists: false,
createdDir: PLS_INSTALL_DIR,
},
error: error,
};
}
}
);
ipcMain.handle(
`${methodBase}.getPlsStats`,
/**
@@ -294,7 +436,7 @@ const applyPlsIpcHandler = (ipcMain) => {
return await functions.execCommand(
logHeader,
PLS_BIN_PATH,
"install"
"--startup auto install"
);
case "rmSvc":
return await functions.execCommand(logHeader, PLS_BIN_PATH, "remove");
@@ -302,12 +444,59 @@ const applyPlsIpcHandler = (ipcMain) => {
return await functions.execCommand(logHeader, PLS_BIN_PATH, "start");
case "stopSvc":
return await functions.execCommand(logHeader, PLS_BIN_PATH, "stop");
case "rmBin":
const unlinkPromise = new Promise((resolve) => {
fs.unlink(PLS_BIN_PATH, (error) => {
if (error) {
resolve({
success: false,
errorObj: error,
});
return false;
}
resolve({
success: true,
errorObj: null,
});
return true;
});
});
const unlinkRet = await unlinkPromise;
return unlinkRet;
default:
return { success: false, errorObj: new Error("Method not found") };
}
}
);
ipcMain.handle(
`${methodBase}.downloadPls`,
/**
*
* @param {import("electron").IpcMainInvokeEvent} _evt
* @param {{channel?: "stable" | "alpha", reportTo?: import("../../../types/main/core").WindowName}} arg
* @returns {void}
*/
(_evt, arg) => {
const channel = arg.channel ? arg.channel : "stable";
const reportWin = arg.reportTo ? arg.reportTo : "assistant";
functions.handlePLSDownload(
channel,
(status) => {
ipcMain.send(
reportWin,
`${methodBase}.post.reportPlsDownloadStatus`,
status
);
},
PLS_BIN_PATH
);
}
);
ipcMain.handle(
`${methodBase}.retryPlsConnect`,
/**

21
src/aura/types/main/ipc/fs.d.ts vendored Normal file
View File

@@ -0,0 +1,21 @@
type DownloadTaskID = string;
type DownloadTaskStatus = "waiting" | "progressing" | "done" | "failed" | "cancelled";
interface DownloadTask {
id: DownloadTaskID;
progress: float;
curBytes?: number;
totalBytes?: number;
status: DownloadTaskStatus;
dlUrl: string | null;
savePath: string | null;
message?: string;
errorObj?: Error;
}
interface FSTasks {
downloadTasks: Map<
DownloadTaskID,
{ status: DownloadTaskStatus; cancelReq: any }
>;
}

View File

@@ -11,6 +11,7 @@ type RendererProcessOnlyVal<T> = T;
interface GlobalHugoAuraInfo {
central?: MainProcessOnlyVal<(...args: any) => any>;
configInit: boolean;
fsTasks?: MainProcessOnlyVal<FSTasks>;
hookedWindows?: MainProcessOnlyVal<HookedWindowsMap>;
ipcInit?: MainProcessOnlyVal<boolean>;
plsRules?: Record<any, any> | null;
@@ -22,12 +23,19 @@ interface GlobalHugoAuraInfo {
version: RendererProcessOnlyVal<string>;
}
interface GlobalHugoAuraApiInfo {
baseUrl: string;
plsUpdate: string;
auraUpdate: string;
}
type GlobalHugoAuraConfig = AuraConfig;
declare global {
var ipcRenderer: RendererProcessOnlyVal<IpcRenderer>;
var _ACCEPT_DATA: any;
var __HUGO_AURA__: GlobalHugoAuraInfo;
var __HUGO_AURA_API__: GlobalHugoAuraApiInfo;
var __HUGO_AURA_CONFIG__: GlobalHugoAuraConfig;
var __HUGO_AURA_CONFIG_MGR__: ConfigManager;
var __HUGO_AURA_EVENT_BUS__: EventBus;

View File

@@ -12,4 +12,4 @@ interface PLSStatus {
type PLSLifecycleType = "isDetached" | "isSvcInstalled" | "isSvcStart";
type PLSLifecycleControlType = "instSvc" | "rmSvc" | "startSvc" | "stopSvc";
type PLSLifecycleControlType = "instSvc" | "rmSvc" | "startSvc" | "stopSvc" | "rmBin" | "dlBin";

View File

@@ -28,18 +28,7 @@ const updatePlsRulesFromLocal = async () => {
return plsRules;
};
/**
*
* @returns {string}
*/
const genRandomHex = () => {
let result = "";
for (let i = 0; i < 8; i++) {
const randomNum = Math.floor(Math.random() * 0x10000);
result += randomNum.toString(16).padStart(4, "0");
}
return result;
};
const { genRandomHex } = require("../../utils/crypto");
/**
*

View File

@@ -96,11 +96,19 @@ if (!global.__HUGO_AURA_UI_REACTIVES__.subConfig)
};
break;
case "Download":
btnEl.onclick = async () => {
global.__HUGO_AURA_UI_FUNCTIONS__.subConfig.plsStatus.downloadPLSBin();
};
break;
// ↓ 这边的确可以把这些全都合并到一个可复用 fn 里去, 但没必要
// 如果后续要引入错误视觉反馈, 合并到单个 fn 反而会增加实现复杂度
case "Install":
btnEl.onclick = async () => {
global.__HUGO_AURA_UI_FUNCTIONS__.subConfig.plsStatus.updateOperationBtnStatus(
"Install",
true
);
global.__HUGO_AURA_UI_FUNCTIONS__.subConfig.plsStatus.updateToast(
"info",
"正在请求安装",
@@ -137,44 +145,116 @@ if (!global.__HUGO_AURA_UI_REACTIVES__.subConfig)
};
break;
case "Uninstall":
btnEl.onclick = async () => {
global.__HUGO_AURA_UI_FUNCTIONS__.subConfig.plsStatus.updateToast(
"info",
"正在请求卸载",
null,
false,
false,
null
);
const ret = await ipcRenderer.invoke(
`${IPC_METHOD_BASE}.plsLifecycleControl`,
{ target: "rmSvc" }
);
if (ret.success) {
lifecycleStatus.svcInstalled = false;
global.__HUGO_AURA_UI_FUNCTIONS__.subConfig.plsStatus.updateToast(
"success",
"服务卸载成功",
null,
true,
true,
2000
if (btnContent === "删除内核") {
btnEl.onclick = async () => {
global.__HUGO_AURA_UI_FUNCTIONS__.subConfig.plsStatus.updateOperationBtnStatus(
"Uninstall",
true
);
} else {
global.__HUGO_AURA_UI_FUNCTIONS__.subConfig.plsStatus.updateToast(
"error",
"服务卸载失败",
"<p>检查日志以获取详细信息</p>",
true,
"warning",
"正在删除内核",
null,
false,
false,
null
);
}
global.__HUGO_AURA_UI_FUNCTIONS__.subConfig.plsStatus.updateStatusContent();
};
const ret = await ipcRenderer.invoke(
`${IPC_METHOD_BASE}.plsLifecycleControl`,
{ target: "rmBin" }
);
if (ret.success) {
lifecycleStatus.installed = false;
lifecycleStatus.svcInstalled = false;
global.__HUGO_AURA_UI_FUNCTIONS__.subConfig.plsStatus.updateToast(
"success",
"内核已删除",
null,
true,
true,
2000
);
} else {
global.__HUGO_AURA_UI_FUNCTIONS__.subConfig.plsStatus.updateToast(
"error",
"内核删除失败",
"<p>检查日志以获取详细信息</p>",
true,
false,
null
);
}
global.__HUGO_AURA_UI_FUNCTIONS__.subConfig.plsStatus.updateStatusContent();
};
} else {
btnEl.onclick = async () => {
global.__HUGO_AURA_UI_FUNCTIONS__.subConfig.plsStatus.updateOperationBtnStatus(
"Uninstall",
true
);
global.__HUGO_AURA_UI_FUNCTIONS__.subConfig.plsStatus.updateToast(
"info",
lifecycleStatus.svcRunning
? "正在停止服务并卸载"
: "正在请求卸载",
null,
false,
false,
null
);
if (lifecycleStatus.svcRunning) {
const stopRet = await ipcRenderer.invoke(
`${IPC_METHOD_BASE}.plsLifecycleControl`,
{ target: "stopSvc" }
);
if (!stopRet.success) {
global.__HUGO_AURA_UI_FUNCTIONS__.subConfig.plsStatus.updateToast(
"error",
"服务卸载失败: 无法停止服务",
"<p>检查日志以获取详细信息</p><p>您可以尝试手动停止 PLS 服务</p>",
true,
false,
null
);
} else {
lifecycleStatus.svcRunning = false;
}
}
const ret = await ipcRenderer.invoke(
`${IPC_METHOD_BASE}.plsLifecycleControl`,
{ target: "rmSvc" }
);
if (ret.success) {
lifecycleStatus.svcInstalled = false;
global.__HUGO_AURA_UI_FUNCTIONS__.subConfig.plsStatus.updateToast(
"success",
"服务卸载成功",
null,
true,
true,
2000
);
} else {
global.__HUGO_AURA_UI_FUNCTIONS__.subConfig.plsStatus.updateToast(
"error",
"服务卸载失败",
"<p>检查日志以获取详细信息</p>",
true,
false,
null
);
}
global.__HUGO_AURA_UI_FUNCTIONS__.subConfig.plsStatus.updateStatusContent();
};
}
break;
case "Start":
btnEl.onclick = async () => {
global.__HUGO_AURA_UI_FUNCTIONS__.subConfig.plsStatus.updateOperationBtnStatus(
"Start",
true
);
global.__HUGO_AURA_UI_FUNCTIONS__.subConfig.plsStatus.updateToast(
"info",
"正在请求启动",
@@ -202,6 +282,7 @@ if (!global.__HUGO_AURA_UI_REACTIVES__.subConfig)
await ipcRenderer.invoke(`${IPC_METHOD_BASE}.retryPlsConnect`);
global.__HUGO_AURA_UI_FUNCTIONS__.subConfig.plsStatus.updateStatusContent();
} else {
global.__HUGO_AURA_UI_FUNCTIONS__.subConfig.plsStatus.updateStatusContent();
global.__HUGO_AURA_UI_FUNCTIONS__.subConfig.plsStatus.updateToast(
"error",
"PLS 启动失败",
@@ -215,6 +296,10 @@ if (!global.__HUGO_AURA_UI_REACTIVES__.subConfig)
break;
case "Stop":
btnEl.onclick = async () => {
global.__HUGO_AURA_UI_FUNCTIONS__.subConfig.plsStatus.updateOperationBtnStatus(
"Stop",
true
);
global.__HUGO_AURA_UI_FUNCTIONS__.subConfig.plsStatus.updateToast(
"info",
"正在请求停止",
@@ -229,7 +314,6 @@ if (!global.__HUGO_AURA_UI_REACTIVES__.subConfig)
);
if (ret.success) {
lifecycleStatus.svcRunning = false;
global.__HUGO_AURA_UI_FUNCTIONS__.subConfig.plsStatus.updateStatusContent();
global.__HUGO_AURA_UI_FUNCTIONS__.subConfig.plsStatus.updateToast(
"success",
"PLS 已停止",
@@ -247,6 +331,7 @@ if (!global.__HUGO_AURA_UI_REACTIVES__.subConfig)
false,
null
);
global.__HUGO_AURA_UI_FUNCTIONS__.subConfig.plsStatus.updateStatusContent();
}
};
break;
@@ -267,17 +352,26 @@ if (!global.__HUGO_AURA_UI_REACTIVES__.subConfig)
if (!lifecycleStatus.svcInstalled) {
updateStatusEl(acIdInst, atIdInst, "WARNING", "已下载, 服务未安装");
GLOBAL_FUNCTIONS.updateOperationBtnStatus("Install", false);
GLOBAL_FUNCTIONS.updateOperationBtnStatus("Uninstall", true);
GLOBAL_FUNCTIONS.updateOperationBtnStatus(
"Uninstall",
false,
"删除内核"
);
GLOBAL_FUNCTIONS.updateOperationBtnStatus("Start", true);
GLOBAL_FUNCTIONS.updateOperationBtnStatus("Stop", true);
} else {
updateStatusEl(acIdInst, atIdInst, "SUCCESS", "已安装");
GLOBAL_FUNCTIONS.updateOperationBtnStatus("Install", true);
GLOBAL_FUNCTIONS.updateOperationBtnStatus("Uninstall", false);
GLOBAL_FUNCTIONS.updateOperationBtnStatus(
"Uninstall",
false,
"卸载服务"
);
}
break;
case false:
updateStatusEl(acIdInst, atIdInst, "PENDING", "未下载");
GLOBAL_FUNCTIONS.updateOperationBtnStatus("Download", false);
GLOBAL_FUNCTIONS.updateOperationBtnStatus("Install", true);
GLOBAL_FUNCTIONS.updateOperationBtnStatus("Uninstall", true);
GLOBAL_FUNCTIONS.updateOperationBtnStatus("Start", true);
@@ -343,6 +437,14 @@ if (!global.__HUGO_AURA_UI_REACTIVES__.subConfig)
lifecycleStatus.installed = true;
} else {
lifecycleStatus.installed = false;
global.__HUGO_AURA_UI_FUNCTIONS__.subConfig.plsStatus.updateToast(
"error",
"请下载 PLS 内核以继续",
null,
true,
true,
3000
);
global.__HUGO_AURA_UI_FUNCTIONS__.subConfig.plsStatus.updateStatusContent();
return;
}
@@ -410,8 +512,93 @@ if (!global.__HUGO_AURA_UI_REACTIVES__.subConfig)
);
} else if (result.success && result.status === "Already") {
updateOperationBtnStatus("Refresh", false, "刷新状态");
global.__HUGO_AURA_UI_FUNCTIONS__.subConfig.plsStatus.updateToast(
"success",
"更新成功",
null,
true,
true,
3000
);
}
},
downloadPLSBin: async () => {
const GLOBAL_FUNCTIONS =
global.__HUGO_AURA_UI_FUNCTIONS__.subConfig.plsStatus;
GLOBAL_FUNCTIONS.updateOperationBtnStatus("Download", true, "正在检查");
GLOBAL_FUNCTIONS.updateOperationBtnStatus("Refresh", true);
const CUR_CHANNEL = `${IPC_METHOD_BASE}.post.reportPlsDownloadStatus`;
await ipcRenderer.invoke(`${IPC_METHOD_BASE}.ensurePlsInstallDir`);
GLOBAL_FUNCTIONS.updateToast(
"info",
"准备开始下载...",
null,
true,
true,
2000
);
GLOBAL_FUNCTIONS.updateOperationBtnStatus("Download", true, "等待下载");
const callbackFn = (_evt, info) => {
switch (info.status) {
case "failed":
GLOBAL_FUNCTIONS.updateToast(
"error",
"下载失败",
`<p>${
info.message ? info.message : "检查日志以获取错误信息"
}</p>`,
true,
true,
5000
);
ipcRenderer.off(CUR_CHANNEL, callbackFn);
GLOBAL_FUNCTIONS.updateOperationBtnStatus("Refresh", false);
break;
case "done":
GLOBAL_FUNCTIONS.updateToast(
"success",
"下载成功",
null,
true,
true,
2500
);
ipcRenderer.off(CUR_CHANNEL, callbackFn);
GLOBAL_FUNCTIONS.updateOperationBtnStatus("Refresh", false);
GLOBAL_FUNCTIONS.updateOperationBtnStatus(
"Download",
true,
"下载内核"
);
lifecycleStatus.installed = true;
GLOBAL_FUNCTIONS.updateStatusContent();
break;
case "waiting":
GLOBAL_FUNCTIONS.updateOperationBtnStatus(
"Download",
true,
"等待中..."
);
break;
case "progressing":
GLOBAL_FUNCTIONS.updateOperationBtnStatus(
"Download",
true,
`下载 ${Math.round(info.progress)}%`
);
break;
}
};
ipcRenderer.on(CUR_CHANNEL, callbackFn);
ipcRenderer.invoke(`$aura.pls.downloadPls`, {
channel: "stable",
reportTo: "assistant",
});
},
};
const GLOBAL_FUNCTIONS =

View File

@@ -83,7 +83,7 @@ const basicSettings = [
restartPLS: true,
tip: true,
tipTitle:
'路径相对于 "%USERPROFILE%\\Documents\\HugoAura\\Aura-PLS\\", 使用 "/" 作为路径符',
'路径相对于 "%PROGRAMDATA%\\HugoAura\\Aura-PLS\\", 使用 "/" 作为路径符',
associateVal: null,
auraIf: () => true,
defaultValue: "",
@@ -118,7 +118,7 @@ const basicSettings = [
restartPLS: true,
tip: true,
tipTitle:
'路径相对于 "%USERPROFILE%\\Documents\\HugoAura\\Aura-PLS\\", 使用 "/" 作为路径符',
'路径相对于 "%PROGRAMDATA%\\HugoAura\\Aura-PLS\\", 使用 "/" 作为路径符',
warning: true,
warningContent: "请使用 PEM 格式的密钥",
associateVal: null,

14
src/aura/utils/crypto.js Normal file
View File

@@ -0,0 +1,14 @@
/**
*
* @returns {string}
*/
const genRandomHex = () => {
let result = "";
for (let i = 0; i < 8; i++) {
const randomNum = Math.floor(Math.random() * 0x10000);
result += randomNum.toString(16).padStart(4, "0");
}
return result;
};
module.exports = { genRandomHex };

View File

@@ -16,6 +16,16 @@ if (!global.__HUGO_AURA__) {
global.__HUGO_AURA__ = __HUGO_AURA__;
}
if (!global.__HUGO_AURA_API__) {
/** @type {import("../aura/types/shared/global").GlobalHugoAuraApiInfo} */
const __HUGO_AURA_API__ = {
baseUrl: "https://api-aura.xwx.li",
plsUpdate: "/api/v1/getPLSLatestVersion",
auraUpdate: "/api/v1/getAuraLatestVersion",
};
global.__HUGO_AURA_API__ = __HUGO_AURA_API__;
}
if (!global.__HUGO_AURA_CONFIG__) {
global.__HUGO_AURA_CONFIG__ = {};
}
@@ -134,7 +144,8 @@ const launcher = ({ central, windowName, config }) => {
configManager.ensureConfigExists();
const loadedConfig = configManager.loadConfig();
if (!global.__HUGO_AURA__.configInit) global.__HUGO_AURA__.configInit = true;
if (!global.__HUGO_AURA_CONFIG_MGR__) global.__HUGO_AURA_CONFIG_MGR__ = configManager;
if (!global.__HUGO_AURA_CONFIG_MGR__)
global.__HUGO_AURA_CONFIG_MGR__ = configManager;
global.__HUGO_AURA_CONFIG__ = loadedConfig;

View File

@@ -1,6 +1,6 @@
// @ts-check
const __AURA_VERSION__ = "0.1.1-pre-III";
const __AURA_VERSION__ = "0.1.1-pre-IV";
(() => {
if (require.main) return; // 如果只是导入 Aura Version, 不运行闭包逻辑