4 Commits

Author SHA1 Message Date
Minoricew
ca5d94ebd8 [🚧 Fix] <PLS & FS> Improve PLS download logic & UX
1. [+] 增加了 PLS 下载操作的取消功能
2. [/] 修复了 FS IPC 中 `downloadFile` 时, 过早地从 downloadTasks 中删除任务的逻辑错误。
3. [↑] 改进了 PLS IPC 中 `handlePLSDownload` 获取版本信息时的逻辑, 现在该函数会从全局 API 信息中逐个尝试 API 域名。减少了极端网络环境下, 版本信息获取失败的可能性。
4. [/] 修复了下载失败后, 下载按钮依然保持灰显的问题。
5. [+] 为 PLS 下载增加了进度条显示。
6. [/] 优化了 `plsConnectionManager` 中一些不必要的 IPC 状态同步 (有些时候还会导致逻辑错误)。
2025-06-13 11:49:22 +08:00
Minoricew
a9d3772b51 [Fix] <PLS> More verbose logging & Change API domain 2025-06-12 20:30:13 +08:00
Minoricew
9e63a9374f [Feat] Log file auto cleanup (#15) & Improve logger experience
Co-authored-by: TianMiao <tianmiao.work@foxmail.com>
2025-06-11 17:59:57 +08:00
Minoricew
c3a70ece88 [Fix] IPC plsStats not sync 2025-06-11 17:29:28 +08:00
12 changed files with 602 additions and 162 deletions

View File

@@ -64,9 +64,12 @@ const buildIpcMain = (electron) => {
} else { } else {
const isWindowValid = global.__HUGO_AURA__.hookedWindows.has(windowKey); const isWindowValid = global.__HUGO_AURA__.hookedWindows.has(windowKey);
if (!isWindowValid) { if (!isWindowValid) {
throw new Error( console.warn(
`[HugoAura / Main / IPC / ERROR] Unknown windowKey: ${windowKey}` `[HugoAura / Main / IPC / WARN] Unknown windowKey: ${windowKey}, window may not have started yet.`
); );
return {
success: false,
};
} }
sendDataToWebContents(windowKey, channel, data); sendDataToWebContents(windowKey, channel, data);

View File

@@ -38,10 +38,13 @@ const composableFunctions = {
progressCallback(failedTemplate); progressCallback(failedTemplate);
return false; return false;
} }
if (!fs.existsSync(path.dirname(targetPath))) {
failedTemplate.message = "Path not exists"; const dirName = path.dirname(targetPath);
progressCallback(failedTemplate);
if (!fs.existsSync(dirName)) {
fs.mkdirSync(dirName);
} }
const httpModuleIns = url.startsWith("https") ? nodeHttps : nodeHttp; const httpModuleIns = url.startsWith("https") ? nodeHttps : nodeHttp;
global.__HUGO_AURA__.fsTasks?.downloadTasks.set(taskId, { global.__HUGO_AURA__.fsTasks?.downloadTasks.set(taskId, {
@@ -49,6 +52,14 @@ const composableFunctions = {
cancelReq: null, cancelReq: null,
}); });
progressCallback({
id: taskId,
progress: 0,
status: "waiting",
dlUrl: url,
savePath: targetPath,
});
const fsStream = fs.createWriteStream(targetPath); const fsStream = fs.createWriteStream(targetPath);
const dlReq = httpModuleIns.get(url, (response) => { const dlReq = httpModuleIns.get(url, (response) => {
@@ -64,9 +75,12 @@ const composableFunctions = {
const totalBytes = parseInt(contentLength, 10) || 0; // No error handling 😆 const totalBytes = parseInt(contentLength, 10) || 0; // No error handling 😆
let curRecvBytes = 0; let curRecvBytes = 0;
let hasCancelled = false;
global.__HUGO_AURA__.fsTasks?.downloadTasks.set(taskId, { global.__HUGO_AURA__.fsTasks?.downloadTasks.set(taskId, {
status: "progressing", status: "progressing",
cancelReq: () => { cancelReq: () => {
hasCancelled = true;
dlReq.destroy(); dlReq.destroy();
fsStream.close(); fsStream.close();
fs.unlink(targetPath, () => {}); fs.unlink(targetPath, () => {});
@@ -102,6 +116,9 @@ const composableFunctions = {
fsStream.on("finish", () => { fsStream.on("finish", () => {
fsStream.close(); fsStream.close();
if (hasCancelled) {
return;
}
progressCallback({ progressCallback({
id: taskId, id: taskId,
progress: (100).toFixed(2), progress: (100).toFixed(2),
@@ -111,9 +128,9 @@ const composableFunctions = {
dlUrl: url, dlUrl: url,
savePath: targetPath, savePath: targetPath,
}); });
global.__HUGO_AURA__.fsTasks?.downloadTasks.delete(taskId);
}); });
global.__HUGO_AURA__.fsTasks?.downloadTasks.delete(taskId);
return true; return true;
}); });
@@ -123,6 +140,10 @@ const composableFunctions = {
failedTemplate.message = failedTemplate.message =
"Request error: Unexpected error while downloading file"; "Request error: Unexpected error while downloading file";
failedTemplate.errorObj = e; failedTemplate.errorObj = e;
console.error(
`[HugoAura / IPC / FS / ERROR] Error downloading file from ${url}, errorObj:`,
e
);
progressCallback(failedTemplate); progressCallback(failedTemplate);
global.__HUGO_AURA__.fsTasks?.downloadTasks.delete(taskId); global.__HUGO_AURA__.fsTasks?.downloadTasks.delete(taskId);
return false; return false;

View File

@@ -71,34 +71,92 @@ const functions = {
handlePLSDownload: async (channel, callbackFn, binPath) => { handlePLSDownload: async (channel, callbackFn, binPath) => {
// TODO: Channel selection // TODO: Channel selection
const apiInfo = global.__HUGO_AURA_API__; const apiInfo = global.__HUGO_AURA_API__;
let plsVersionInfo = {};
const getVerPromise = new Promise((resolve) => { const getVerPromise = new Promise(async (resolveGetVerReq) => {
// ↓ 目前 channel param 没有什么用处 // ↓ 目前 channel param 没有什么用处
nodeHttps for (const apiDomain of apiInfo.domains) {
.get( const reqPromise = new Promise((resolveHttpRequest) => {
`${apiInfo.baseUrl}${apiInfo.plsUpdate}?channel=${channel}`, nodeHttps
(rep) => { .get(
let dataChunk = ""; `${apiDomain}${apiInfo.plsUpdate}?channel=${channel}`,
rep.on("data", (chunk) => { (rep) => {
dataChunk += chunk; let dataChunk = "";
}); rep.on("data", (chunk) => {
dataChunk += chunk;
});
rep.on("end", () => { rep.on("end", () => {
resolve({ let parsedData = {};
success: true, try {
data: dataChunk, parsedData = JSON.parse(dataChunk);
} catch (e) {
callbackFn({
id: "",
progress: 0,
status: "struggling",
dlUrl: null,
savePath: null,
message: `数据解析失败, 正在尝试 API 域名 ${
apiInfo.domains[apiInfo.domains.indexOf(apiDomain) + 1]
} ...`,
errorObj: e,
});
setTimeout(() => {
resolveHttpRequest({
success: false,
errorObj: e,
});
}, 1000);
return;
}
resolveHttpRequest({
success: true,
data: parsedData,
});
return;
});
}
)
.on("error", (e) => {
callbackFn({
id: "",
progress: 0,
status: "struggling",
dlUrl: null,
savePath: null,
message: `连接失败, 正在尝试 API 域名 ${
apiInfo.domains[apiInfo.domains.indexOf(apiDomain) + 1]
} ...`,
errorObj: e,
}); });
setTimeout(() => {
resolveHttpRequest({
success: false,
errorObj: e,
});
}, 1000);
}); });
}
)
.on("error", (e) => {
resolve({
success: false,
data: null,
errorObj: e,
});
}); });
const requestResult = await reqPromise;
if (requestResult.success) {
resolveGetVerReq({
success: true,
data: requestResult.data,
});
break;
} else {
continue;
}
}
resolveGetVerReq({
success: false,
data: null,
});
}); });
const rawResInfo = await getVerPromise; const rawResInfo = await getVerPromise;
@@ -109,30 +167,12 @@ const functions = {
status: "failed", status: "failed",
dlUrl: null, dlUrl: null,
savePath: null, savePath: null,
message: "未能获取 PLS 版本信息", message: "未能获取 PLS 版本信息, 所有 API 域名均无法连接",
errorObj: rawResInfo.errorObj ? rawResInfo.errorObj : null,
}); });
return false; return false;
} }
try { const plsVersionInfo = rawResInfo.data;
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 let deviceArch = process.env.PROCESSOR_ARCHITEW6432
? process.env.PROCESSOR_ARCHITEW6432 ? process.env.PROCESSOR_ARCHITEW6432
@@ -155,7 +195,16 @@ const functions = {
fsComposables.downloadFile( fsComposables.downloadFile(
plsVersionInfo.data.downloadUrl[deviceArch], plsVersionInfo.data.downloadUrl[deviceArch],
binPath, binPath,
callbackFn (...args) => {
if (args[0].status === "done") {
if (global.__HUGO_AURA__.plsStats) {
global.__HUGO_AURA__.plsStats.installed = true;
global.__HUGO_AURA__.plsStats.status = "dead";
}
}
callbackFn(...args);
}
); );
}, },
}; };
@@ -192,11 +241,18 @@ const applyPlsIpcHandler = (ipcMain) => {
(_event, _arg) => { (_event, _arg) => {
try { try {
const result = fs.existsSync(PLS_BIN_PATH); const result = fs.existsSync(PLS_BIN_PATH);
if (global.__HUGO_AURA__.plsStats?.status === "notInstalled") {
global.__HUGO_AURA__.plsStats.status = "dead";
}
return { return {
success: true, success: true,
data: { isExists: result }, data: { isExists: result },
}; };
} catch (e) { } catch (e) {
// @ts-expect-error
global.__HUGO_AURA__.plsStats.status = "notInstalled";
return { return {
success: false, success: false,
data: { isExists: false }, data: { isExists: false },
@@ -427,10 +483,6 @@ const applyPlsIpcHandler = (ipcMain) => {
async (_event, arg) => { async (_event, arg) => {
const logHeader = "[HugoAura / IPC / PLS] <plsLifecycleControl>"; const logHeader = "[HugoAura / IPC / PLS] <plsLifecycleControl>";
if (!global.__HUGO_AURA__.plsStats?.installed) {
return { success: false, errorObj: new Error("PLS not installed") };
}
switch (arg.target) { switch (arg.target) {
case "instSvc": case "instSvc":
return await functions.execCommand( return await functions.execCommand(
@@ -452,6 +504,10 @@ const applyPlsIpcHandler = (ipcMain) => {
success: false, success: false,
errorObj: error, errorObj: error,
}); });
console.error(
`${logHeader} Failed to remove PLS bin, error:`,
error
);
return false; return false;
} }

View File

@@ -0,0 +1,111 @@
const path = require("path");
const fs = require("fs");
const os = require("os");
const util = require("util");
/**
*
* @param {import("../aura/types/main/core").WindowName} windowName
*/
const initLogger = (windowName) => {
const logDir = path.join(os.homedir(), "Documents", "HugoAura", "logs");
if (!fs.existsSync(logDir)) {
fs.mkdirSync(logDir, { recursive: true });
}
cleanupOldLogs(logDir);
const logFile = getLogFileName(logDir);
const logStream = fs.createWriteStream(logFile, { flags: "a" });
const timestamp = new Date().toISOString();
const startupMsg = `\n=== [${timestamp}] HugoAura 窗口启动: ${windowName} ===\n\n`;
logStream.write(startupMsg);
const originalConsole = {
log: console.log,
error: console.error,
warn: console.warn,
info: console.info,
debug: console.debug,
};
console.log = function (...args) {
const msg = util.format(`[LOG] <${windowName}>`, ...args) + "\n";
logStream.write(msg);
originalConsole.log.apply(console, args);
};
console.error = function (...args) {
const msg = util.format(`[ERROR] <${windowName}>`, ...args) + "\n";
logStream.write(msg);
originalConsole.error.apply(console, args);
};
console.warn = function (...args) {
const msg = util.format(`[WARN] <${windowName}>`, ...args) + "\n";
logStream.write(msg);
originalConsole.warn.apply(console, args);
};
console.info = function (...args) {
const msg = util.format(`[INFO] <${windowName}>`, ...args) + "\n";
logStream.write(msg);
originalConsole.info.apply(console, args);
};
console.debug = function (...args) {
if (!process.argv.includes("--aura-debug")) return;
const msg = util.format(`[DEBUG] <${windowName}>`, ...args) + "\n";
logStream.write(msg);
originalConsole.debug.apply(console, args);
};
process.on("uncaughtException", (err) => {
console.error("[CRITICAL] UNCAUGHT EXCEPTION:", err);
});
console.log(
"[HugoAura / Logger] Logger initialized. Log file:",
logFile
);
};
const cleanupOldLogs = (logDir) => {
try {
const files = fs.readdirSync(logDir);
const now = new Date();
const sevenDaysAgo = new Date(now.getTime() - 30 * 24 * 60 * 60 * 1000);
files.forEach((file) => {
if (file.endsWith(".log")) {
const filePath = path.join(logDir, file);
const stats = fs.statSync(filePath);
// 如果文件创建时间超过 30 天, 则删除
if (stats.birthtime < sevenDaysAgo) {
fs.unlinkSync(filePath);
console.log(
`[HugoAura / Logger / Cleanup] Cleaned log file: ${file}`
);
}
}
});
} catch (error) {
console.error("[HugoAura / Logger / Cleanup] Unexpected error occurred cleaning log file:", error);
}
};
/**
* 生成每日 Log 文件路径
* @param {string} logDir 日志目录路径
* @param {string} _windowName 窗口名称 (暂时无用)
* @returns {string} 日志文件路径
*/
const getLogFileName = (logDir, _windowName) => {
const today = new Date();
const dateStr = today.toISOString().split("T")[0]; // YYYY-MM-DD 格式
const logFileName = `HugoAura-SSA-${dateStr}.log`;
return path.join(logDir, logFileName);
};
module.exports = { initLogger };

View File

@@ -1,5 +1,5 @@
type DownloadTaskID = string; type DownloadTaskID = string;
type DownloadTaskStatus = "waiting" | "progressing" | "done" | "failed" | "cancelled"; type DownloadTaskStatus = "waiting" | "progressing" | "done" | "failed" | "cancelled" | "struggling";
interface DownloadTask { interface DownloadTask {
id: DownloadTaskID; id: DownloadTaskID;

View File

@@ -24,7 +24,7 @@ interface GlobalHugoAuraInfo {
} }
interface GlobalHugoAuraApiInfo { interface GlobalHugoAuraApiInfo {
baseUrl: string; domains: string[];
plsUpdate: string; plsUpdate: string;
auraUpdate: string; auraUpdate: string;
} }

View File

@@ -1,15 +1,28 @@
import { RendererProcessOnlyVal } from "../global"; import { RendererProcessOnlyVal } from "../global";
type PLSStatusDesc =
| "dead"
| "running"
| "notReady"
| "downloading"
| "notInstalled";
interface PLSStatus { interface PLSStatus {
installed: boolean; installed: boolean;
detached: boolean; detached: boolean;
connected: boolean; connected: boolean;
launched: boolean; launched: boolean;
status: string; status: PLSStatusDesc;
version: string; version: string;
authToken: string; authToken: string;
} }
type PLSLifecycleType = "isDetached" | "isSvcInstalled" | "isSvcStart"; type PLSLifecycleType = "isDetached" | "isSvcInstalled" | "isSvcStart";
type PLSLifecycleControlType = "instSvc" | "rmSvc" | "startSvc" | "stopSvc" | "rmBin" | "dlBin"; type PLSLifecycleControlType =
| "instSvc"
| "rmSvc"
| "startSvc"
| "stopSvc"
| "rmBin"
| "dlBin";

View File

@@ -6,7 +6,7 @@
detached: false, detached: false,
connected: false, connected: false,
launched: false, launched: false,
status: "unknown", status: "dead",
version: "未知", version: "未知",
authToken: "", authToken: "",
}; };
@@ -259,7 +259,10 @@
`${IPC_METHOD_BASE}.getPlsStats` `${IPC_METHOD_BASE}.getPlsStats`
); );
let updatedPlsStats = {}; let updatedPlsStats = {};
if (curPlsStats === null || !curPlsStats.success) { if (
(curPlsStats === null || !curPlsStats.success) &&
curPlsStats.status !== "downloading"
) {
updatedPlsStats = { updatedPlsStats = {
installed: false, installed: false,
launched: false, launched: false,
@@ -269,15 +272,14 @@
status: "dead", status: "dead",
authToken: "66ccff0d000721114514191981023333", authToken: "66ccff0d000721114514191981023333",
}; };
const isPlsFolderExists = (
await global.ipcRenderer.invoke(`${IPC_METHOD_BASE}.getPlsBinExists`)
).data.isExists;
updatedPlsStats.installed = isPlsFolderExists;
} else { } else {
updatedPlsStats = curPlsStats.data; updatedPlsStats = curPlsStats.data;
} }
const isPlsFolderExists = (
await global.ipcRenderer.invoke(`${IPC_METHOD_BASE}.getPlsBinExists`)
).data.isExists;
updatedPlsStats.installed = isPlsFolderExists;
// @ts-expect-error // @ts-expect-error
global.__HUGO_AURA__.plsStats = updatedPlsStats; global.__HUGO_AURA__.plsStats = updatedPlsStats;
console.debug( console.debug(

View File

@@ -33,6 +33,10 @@
margin-bottom: 1rem; margin-bottom: 1rem;
} }
.acs-bc-psp-operations-container.acs-bc-psp-oper-ctnr-hidden {
display: none;
}
.acs-bc-psp-operation-btn { .acs-bc-psp-operation-btn {
--svg-color: rgb(17, 140, 255); --svg-color: rgb(17, 140, 255);
@@ -85,6 +89,50 @@
opacity: 0.3; opacity: 0.3;
} }
.acs-bc-psp-download-progress-area {
display: flex;
flex-direction: column;
width: 80%;
justify-content: center;
align-items: center;
}
.acs-bc-psp-download-progress-area.acs-bc-psp-dl-pbar-hidden {
display: none;
}
.acs-bc-psp-download-progress-area .progress {
height: 2px;
width: 80%;
margin-bottom: 0.6rem;
}
.acs-bc-psp-download-progress-area .progress-bar {
transition: all 0.15s;
}
.acs-bc-psp-download-progress-area #acsBcPspDownloadPbarDesc {
opacity: 0.6;
margin-bottom: 0.25rem;
}
.acs-bc-psp-download-progress-info-area {
display: flex;
}
.acs-bc-psp-download-progress-info-area .acs-bc-psp-operation-btn {
margin-left: 1rem;
margin-bottom: 0.25rem;
}
.acs-bc-psp-download-progress-info-area .acs-bc-psp-operation-btn.hidden {
display: none;
}
.acs-bc-psp-download-progress-info-area .acs-bc-psp-operation-btn svg {
margin-top: 1px;
}
.acs-bc-pls-status-page-status-el { .acs-bc-pls-status-page-status-el {
display: flex; display: flex;
align-items: center; align-items: center;

View File

@@ -124,6 +124,37 @@
</div> </div>
</div> </div>
<div class="acs-bc-psp-download-progress-area acs-bc-psp-dl-pbar-hidden">
<div class="progress" role="progressbar">
<div class="progress-bar" id="acsBcPspDownloadPbarEl"></div>
</div>
<div class="acs-bc-psp-download-progress-info-area">
<p id="acsBcPspDownloadPbarDesc">等待中...</p>
<div
class="acs-bc-psp-operation-btn hidden"
id="acsBcPspDownloadPbarCancelBtn"
onclick="global.__HUGO_AURA_UI_FUNCTIONS__.subConfig.plsStatus.cancelDownloadTask()"
>
<svg
xmlns="http://www.w3.org/2000/svg"
width="32"
height="32"
viewBox="0 0 32 32"
>
<path
fill="var(--svg-color)"
d="M9 10.555L10.555 9L23 21.444L21.444 23z"
/>
<path
fill="var(--svg-color)"
d="M16 2A13.914 13.914 0 0 0 2 16a13.914 13.914 0 0 0 14 14a13.914 13.914 0 0 0 14-14A13.914 13.914 0 0 0 16 2m0 26a12 12 0 1 1 12-12a12.035 12.035 0 0 1-12 12"
/>
</svg>
<p>取消操作</p>
</div>
</div>
</div>
<div class="acs-bc-pls-status-page-status-el"> <div class="acs-bc-pls-status-page-status-el">
<p>安装状态</p> <p>安装状态</p>
<div <div
@@ -165,7 +196,11 @@
</div> </div>
<div class="toast-container position-fixed bottom-0 end-0 p-3"> <div class="toast-container position-fixed bottom-0 end-0 p-3">
<div id="plsStatusNotifyToast" class="acs-bc-psp-toast toast" data-bs-autohide="false"> <div
id="plsStatusNotifyToast"
class="acs-bc-psp-toast toast"
data-bs-autohide="false"
>
<div class="toast-header"> <div class="toast-header">
<strong class="me-auto" id="plsStatusNotifyToastTitle"></strong> <strong class="me-auto" id="plsStatusNotifyToastTitle"></strong>
<button <button

View File

@@ -17,6 +17,7 @@ if (!global.__HUGO_AURA_UI_REACTIVES__.subConfig)
global.__HUGO_AURA_UI_REACTIVES__.subConfig.plsStatus = { global.__HUGO_AURA_UI_REACTIVES__.subConfig.plsStatus = {
toastAutoHideTimeout: null, toastAutoHideTimeout: null,
curDlTaskId: null,
}; };
global.__HUGO_AURA_UI_FUNCTIONS__.subConfig.plsStatus = { global.__HUGO_AURA_UI_FUNCTIONS__.subConfig.plsStatus = {
@@ -135,7 +136,7 @@ if (!global.__HUGO_AURA_UI_REACTIVES__.subConfig)
global.__HUGO_AURA_UI_FUNCTIONS__.subConfig.plsStatus.updateToast( global.__HUGO_AURA_UI_FUNCTIONS__.subConfig.plsStatus.updateToast(
"error", "error",
"服务安装失败", "服务安装失败",
"<p>检查日志以获取详细信息</p>", `<p>${ret.errorObj}</p>`,
true, true,
false, false,
null null
@@ -166,6 +167,13 @@ if (!global.__HUGO_AURA_UI_REACTIVES__.subConfig)
if (ret.success) { if (ret.success) {
lifecycleStatus.installed = false; lifecycleStatus.installed = false;
lifecycleStatus.svcInstalled = false; lifecycleStatus.svcInstalled = false;
global.__HUGO_AURA__.plsStats.installed = false;
global.__HUGO_AURA__.plsStats.connected = false;
global.__HUGO_AURA__.plsStats.launched = false;
ipcRenderer.invoke(
`${IPC_METHOD_BASE}.updatePlsStats`,
global.__HUGO_AURA__.plsStats
);
global.__HUGO_AURA_UI_FUNCTIONS__.subConfig.plsStatus.updateToast( global.__HUGO_AURA_UI_FUNCTIONS__.subConfig.plsStatus.updateToast(
"success", "success",
"内核已删除", "内核已删除",
@@ -178,7 +186,9 @@ if (!global.__HUGO_AURA_UI_REACTIVES__.subConfig)
global.__HUGO_AURA_UI_FUNCTIONS__.subConfig.plsStatus.updateToast( global.__HUGO_AURA_UI_FUNCTIONS__.subConfig.plsStatus.updateToast(
"error", "error",
"内核删除失败", "内核删除失败",
"<p>检查日志以获取详细信息</p>", `<p>
${ret.errorObj ? ret.errorObj : "检查日志以获取详细信息"}
</p>`,
true, true,
false, false,
null null
@@ -211,7 +221,14 @@ if (!global.__HUGO_AURA_UI_REACTIVES__.subConfig)
global.__HUGO_AURA_UI_FUNCTIONS__.subConfig.plsStatus.updateToast( global.__HUGO_AURA_UI_FUNCTIONS__.subConfig.plsStatus.updateToast(
"error", "error",
"服务卸载失败: 无法停止服务", "服务卸载失败: 无法停止服务",
"<p>检查日志以获取详细信息</p><p>您可以尝试手动停止 PLS 服务</p>", `<p>${
stopRet.errorObj
? stopRet.errorObj
: "检查日志以获取详细信息"
}</p>
<p>
您可以尝试手动停止 PLS 服务
</p>`,
true, true,
false, false,
null null
@@ -345,6 +362,10 @@ if (!global.__HUGO_AURA_UI_REACTIVES__.subConfig)
updateStatusContent: async () => { updateStatusContent: async () => {
const curPlsStats = await updatePlsStatusFromLocal(); const curPlsStats = await updatePlsStatusFromLocal();
if (curPlsStats.status === "downloading") {
GLOBAL_FUNCTIONS.downloadPLSBin(true);
}
const acIdInst = "acs-bc-psp-installStatus-container"; const acIdInst = "acs-bc-psp-installStatus-container";
const atIdInst = "acs-bc-psp-installStatus-text"; const atIdInst = "acs-bc-psp-installStatus-text";
switch (lifecycleStatus.installed) { switch (lifecycleStatus.installed) {
@@ -435,8 +456,18 @@ if (!global.__HUGO_AURA_UI_REACTIVES__.subConfig)
); );
if (binExistsRet.success && binExistsRet.data.isExists) { if (binExistsRet.success && binExistsRet.data.isExists) {
lifecycleStatus.installed = true; lifecycleStatus.installed = true;
global.__HUGO_AURA__.plsStats.installed = true;
ipcRenderer.invoke(
`${IPC_METHOD_BASE}.updatePlsStats`,
global.__HUGO_AURA__.plsStats
);
} else { } else {
lifecycleStatus.installed = false; lifecycleStatus.installed = false;
global.__HUGO_AURA__.plsStats.installed = false;
ipcRenderer.invoke(
`${IPC_METHOD_BASE}.updatePlsStats`,
global.__HUGO_AURA__.plsStats
);
global.__HUGO_AURA_UI_FUNCTIONS__.subConfig.plsStatus.updateToast( global.__HUGO_AURA_UI_FUNCTIONS__.subConfig.plsStatus.updateToast(
"error", "error",
"请下载 PLS 内核以继续", "请下载 PLS 内核以继续",
@@ -523,22 +554,101 @@ if (!global.__HUGO_AURA_UI_REACTIVES__.subConfig)
} }
}, },
downloadPLSBin: async () => { /**
*
* @param {boolean} isShow
*/
switchPBarShowStatus: (isShow) => {
const operAreaEl = document.getElementsByClassName(
"acs-bc-psp-operations-container"
)[0];
const pBarAreaEl = document.getElementsByClassName(
"acs-bc-psp-download-progress-area"
)[0];
const pBarDescEl = document.getElementById("acsBcPspDownloadPbarDesc");
const pBarSelfEl = document.getElementById("acsBcPspDownloadPbarEl");
if (isShow) {
pBarAreaEl.classList.remove("acs-bc-psp-dl-pbar-hidden");
operAreaEl.classList.add("acs-bc-psp-oper-ctnr-hidden");
pBarDescEl.textContent = "等待中...";
pBarSelfEl.style["width"] = "0";
} else {
pBarAreaEl.classList.add("acs-bc-psp-dl-pbar-hidden");
operAreaEl.classList.remove("acs-bc-psp-oper-ctnr-hidden");
}
return true;
},
updatePBarStatus: async (
progress = null,
desc = null,
type = null,
isCancelShown = null
) => {
const pBarDescEl = document.getElementById("acsBcPspDownloadPbarDesc");
const pBarSelfEl = document.getElementById("acsBcPspDownloadPbarEl");
const pBarBtnEl = document.getElementById(
"acsBcPspDownloadPbarCancelBtn"
);
if (progress) {
pBarSelfEl.style["width"] = `${progress}%`;
}
if (type) {
pBarSelfEl.classList.remove("bg-success");
pBarSelfEl.classList.remove("bg-warning");
pBarSelfEl.classList.remove("bg-danger");
if (type !== "normal") {
pBarSelfEl.classList.add(`bg-${type}`);
}
}
if (desc) {
pBarDescEl.innerHTML = desc;
}
if (isCancelShown !== null) {
if (isCancelShown) {
pBarBtnEl.classList.remove("hidden");
} else {
pBarBtnEl.classList.add("hidden");
}
}
},
downloadPLSBin: async (retrieveMode = false) => {
const GLOBAL_FUNCTIONS = const GLOBAL_FUNCTIONS =
global.__HUGO_AURA_UI_FUNCTIONS__.subConfig.plsStatus; 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`; const CUR_CHANNEL = `${IPC_METHOD_BASE}.post.reportPlsDownloadStatus`;
await ipcRenderer.invoke(`${IPC_METHOD_BASE}.ensurePlsInstallDir`);
GLOBAL_FUNCTIONS.updateToast( if (!retrieveMode) {
"info", GLOBAL_FUNCTIONS.updateOperationBtnStatus("Download", true, "正在检查");
"准备开始下载...", GLOBAL_FUNCTIONS.updateOperationBtnStatus("Refresh", true);
null, await ipcRenderer.invoke(`${IPC_METHOD_BASE}.ensurePlsInstallDir`);
true, GLOBAL_FUNCTIONS.updateToast(
true, "info",
2000 "准备开始下载...",
); null,
GLOBAL_FUNCTIONS.updateOperationBtnStatus("Download", true, "等待下载"); true,
true,
2000
);
GLOBAL_FUNCTIONS.updateOperationBtnStatus("Download", true);
} else {
GLOBAL_FUNCTIONS.updateToast(
"info",
"正在恢复下载状态",
null,
true,
true,
2000
);
}
GLOBAL_FUNCTIONS.switchPBarShowStatus(true);
GLOBAL_FUNCTIONS.updatePBarStatus(0, "等待中...", "normal", false);
const callbackFn = (_evt, info) => { const callbackFn = (_evt, info) => {
switch (info.status) { switch (info.status) {
@@ -548,13 +658,31 @@ if (!global.__HUGO_AURA_UI_REACTIVES__.subConfig)
"下载失败", "下载失败",
`<p>${ `<p>${
info.message ? info.message : "检查日志以获取错误信息" info.message ? info.message : "检查日志以获取错误信息"
}</p>`, }</p><p>
${info.errorObj ? info.errorObj : ""}
</p>`,
true, true,
true, true,
5000 5000
); );
GLOBAL_FUNCTIONS.updatePBarStatus(
100,
"下载时发生错误",
"danger",
false
);
setTimeout(() => {
GLOBAL_FUNCTIONS.switchPBarShowStatus(false);
}, 1000);
ipcRenderer.off(CUR_CHANNEL, callbackFn); ipcRenderer.off(CUR_CHANNEL, callbackFn);
GLOBAL_FUNCTIONS.updateOperationBtnStatus("Refresh", false); GLOBAL_FUNCTIONS.updateOperationBtnStatus("Refresh", false);
GLOBAL_FUNCTIONS.updateOperationBtnStatus(
"Download",
false,
"下载内核"
);
break; break;
case "done": case "done":
GLOBAL_FUNCTIONS.updateToast( GLOBAL_FUNCTIONS.updateToast(
@@ -565,6 +693,17 @@ if (!global.__HUGO_AURA_UI_REACTIVES__.subConfig)
true, true,
2500 2500
); );
GLOBAL_FUNCTIONS.updatePBarStatus(
100,
"下载成功",
"success",
false
);
setTimeout(() => {
GLOBAL_FUNCTIONS.switchPBarShowStatus(false);
}, 1000);
ipcRenderer.off(CUR_CHANNEL, callbackFn); ipcRenderer.off(CUR_CHANNEL, callbackFn);
GLOBAL_FUNCTIONS.updateOperationBtnStatus("Refresh", false); GLOBAL_FUNCTIONS.updateOperationBtnStatus("Refresh", false);
GLOBAL_FUNCTIONS.updateOperationBtnStatus( GLOBAL_FUNCTIONS.updateOperationBtnStatus(
@@ -573,20 +712,49 @@ if (!global.__HUGO_AURA_UI_REACTIVES__.subConfig)
"下载内核" "下载内核"
); );
lifecycleStatus.installed = true; lifecycleStatus.installed = true;
global.__HUGO_AURA__.plsStats.installed = true;
ipcRenderer.invoke(
`${IPC_METHOD_BASE}.updatePlsStats`,
global.__HUGO_AURA__.plsStats
);
GLOBAL_FUNCTIONS.updateStatusContent(); GLOBAL_FUNCTIONS.updateStatusContent();
break; break;
case "waiting": case "waiting":
GLOBAL_FUNCTIONS.updateOperationBtnStatus( GLOBAL_FUNCTIONS.updatePBarStatus(0, "正在连接", "normal");
"Download", if (
true, !global.__HUGO_AURA_UI_REACTIVES__.subConfig.plsStatus
"等待中..." .curDlTaskId ||
); global.__HUGO_AURA_UI_REACTIVES__.subConfig.plsStatus
.curDlTaskId !== info.id
) {
global.__HUGO_AURA_UI_REACTIVES__.subConfig.plsStatus.curDlTaskId =
info.id;
}
break; break;
case "progressing": case "progressing":
const roundProgress = Math.round(info.progress);
GLOBAL_FUNCTIONS.updatePBarStatus(
roundProgress,
`正在下载中... ${roundProgress}% (${(
info.curBytes /
1024 /
1024
).toFixed(2)}MB / ${(info.totalBytes / 1024 / 1024).toFixed(
2
)}MB)`, // POWERED BY PRETTIER
"normal",
true
);
break;
case "struggling":
GLOBAL_FUNCTIONS.updatePBarStatus(100, info.message, "warning");
break;
case "cancelled":
GLOBAL_FUNCTIONS.updateOperationBtnStatus("Refresh", false);
GLOBAL_FUNCTIONS.updateOperationBtnStatus( GLOBAL_FUNCTIONS.updateOperationBtnStatus(
"Download", "Download",
true, false,
`下载 ${Math.round(info.progress)}%` "下载内核"
); );
break; break;
} }
@@ -594,11 +762,56 @@ if (!global.__HUGO_AURA_UI_REACTIVES__.subConfig)
ipcRenderer.on(CUR_CHANNEL, callbackFn); ipcRenderer.on(CUR_CHANNEL, callbackFn);
ipcRenderer.invoke(`$aura.pls.downloadPls`, { ipcRenderer.invoke(`${IPC_METHOD_BASE}.downloadPls`, {
channel: "stable", channel: "stable",
reportTo: "assistant", reportTo: "assistant",
}); });
}, },
cancelDownloadTask: async () => {
const taskId =
global.__HUGO_AURA_UI_REACTIVES__.subConfig.plsStatus.curDlTaskId;
if (!taskId) {
GLOBAL_FUNCTIONS.updateToast(
"error",
"操作取消失败",
"<p>未能获取当前的下载任务 ID</p>",
true,
true,
3000
);
return false;
}
const result = await ipcRenderer.invoke(
"$aura.fs.dl.cancelDownloadTask",
{ targetTaskId: taskId }
);
if (result.success) {
GLOBAL_FUNCTIONS.updateToast(
"success",
"操作取消成功",
null,
true,
true,
2000
);
GLOBAL_FUNCTIONS.switchPBarShowStatus(false);
return true;
} else {
GLOBAL_FUNCTIONS.updateToast(
"error",
"操作取消失败",
`<p>错误代码: ${result.error}</p>`,
true,
true,
3000
);
return false;
}
},
}; };
const GLOBAL_FUNCTIONS = const GLOBAL_FUNCTIONS =

View File

@@ -19,9 +19,15 @@ if (!global.__HUGO_AURA__) {
if (!global.__HUGO_AURA_API__) { if (!global.__HUGO_AURA_API__) {
/** @type {import("../aura/types/shared/global").GlobalHugoAuraApiInfo} */ /** @type {import("../aura/types/shared/global").GlobalHugoAuraApiInfo} */
const __HUGO_AURA_API__ = { const __HUGO_AURA_API__ = {
baseUrl: "https://api-aura.xwx.li", domains: [
plsUpdate: "/api/v1/getPLSLatestVersion", "https://api-aura-projekts.delta.ooo",
auraUpdate: "/api/v1/getAuraLatestVersion", "https://api-aura.asaka.site",
"https://api.hugoaura.dpdns.org",
"https://api-aura-projekts.minorice.moe",
"https://api.aura.vim.moe"
],
plsUpdate: "/api/getPLSLatestVersion",
auraUpdate: "/api/getAuraLatestVersion",
}; };
global.__HUGO_AURA_API__ = __HUGO_AURA_API__; global.__HUGO_AURA_API__ = __HUGO_AURA_API__;
} }
@@ -30,11 +36,6 @@ if (!global.__HUGO_AURA_CONFIG__) {
global.__HUGO_AURA_CONFIG__ = {}; global.__HUGO_AURA_CONFIG__ = {};
} }
const fs = require("fs");
const util = require("util");
const path = require("path");
const os = require("os");
const MainProcessHooksManager = require("../aura/init/main/windowHooksManager"); const MainProcessHooksManager = require("../aura/init/main/windowHooksManager");
const RendererHooksManager = require("../aura/init/rendererHook/uiHooksManager"); const RendererHooksManager = require("../aura/init/rendererHook/uiHooksManager");
const EventBus = require("../aura/utils/eventBus"); const EventBus = require("../aura/utils/eventBus");
@@ -42,70 +43,7 @@ const NetworkHook = require("../aura/init/rendererHook/networkHook");
const ConfigManager = require("../aura/init/shared/configManager"); const ConfigManager = require("../aura/init/shared/configManager");
const { buildIpcMain } = require("../aura/init/main/ipcHandler"); const { buildIpcMain } = require("../aura/init/main/ipcHandler");
/** const { initLogger } = require("../aura/init/main/logger");
*
* @param {import("../aura/types/main/core").WindowName} windowName
*/
const initLogger = (windowName) => {
const logDir = path.join(os.homedir(), "Documents", "HugoAura", "logs");
if (!fs.existsSync(logDir)) {
fs.mkdirSync(logDir, { recursive: true });
}
const logFile = path.join(
logDir,
`main-${windowName}-${new Date().toISOString().replace(/:/g, "-")}.log`
);
const logStream = fs.createWriteStream(logFile, { flags: "a" });
const originalConsole = {
log: console.log,
error: console.error,
warn: console.warn,
info: console.info,
debug: console.debug,
};
console.log = function (...args) {
const msg = util.format("[LOG] ", ...args) + "\n";
logStream.write(msg);
originalConsole.log.apply(console, args);
};
console.error = function (...args) {
const msg = util.format("[ERROR] ", ...args) + "\n";
logStream.write(msg);
originalConsole.error.apply(console, args);
};
console.warn = function (...args) {
const msg = util.format("[WARN] ", ...args) + "\n";
logStream.write(msg);
originalConsole.warn.apply(console, args);
};
console.info = function (...args) {
const msg = util.format("[INFO] ", ...args) + "\n";
logStream.write(msg);
originalConsole.info.apply(console, args);
};
console.debug = function (...args) {
if (!process.argv.includes("--aura-debug")) return;
const msg = util.format("[DEBUG] ", ...args) + "\n";
logStream.write(msg);
originalConsole.debug.apply(console, args);
};
process.on("uncaughtException", (err) => {
console.error("UNCAUGHT EXCEPTION:", err);
});
console.log(
"[HugoAura / Init / Logger] Logger initialized. Log file:",
logFile
);
};
/** /**
* *