mirror of
https://github.com/HugoAura/Seewo-HugoAura.git
synced 2026-06-20 23:14:28 +08:00
[✨ Feat] Impl file-based IPC for Aikari installation
This commit is contained in:
@@ -9,11 +9,12 @@
|
|||||||
<br />
|
<br />
|
||||||
|
|
||||||
<center>
|
<center>
|
||||||
<a href="https://campus.seewo.com/iot-public/file/?key=iot_doc_seewoServiceUpdateLog">
|
<a href="https://forum.smart-teach.cn/d/898-xi-wo-guan-jia-zhe-kuai-zhen-de-shi-zhao-xiao-si-liao">
|
||||||
<img src="https://docs.aurax.cc/static/img/emg_announcement_banner.png" />
|
<img src="https://docs.aurax.cc/static/img/emg_announcement_banner.png" />
|
||||||
</a>
|
</a>
|
||||||
</center>
|
</center>
|
||||||
|
|
||||||
|
<br />
|
||||||
<br />
|
<br />
|
||||||
|
|
||||||
> [!TIP]
|
> [!TIP]
|
||||||
|
|||||||
@@ -68,149 +68,166 @@ const functions = {
|
|||||||
* @param {"stable" | "alpha"} channel
|
* @param {"stable" | "alpha"} channel
|
||||||
* @param {(arg: DownloadTask) => any} callbackFn
|
* @param {(arg: DownloadTask) => any} callbackFn
|
||||||
* @param {string} binPath
|
* @param {string} binPath
|
||||||
|
* @param {string} logPath
|
||||||
|
* @param {string} progressFilePath
|
||||||
*/
|
*/
|
||||||
handleAikariDlAndInstall: async (channel, callbackFn, binPath) => {
|
handleAikariDlAndInstall: async (
|
||||||
// TODO: Channel selection
|
channel,
|
||||||
const apiInfo = global.__HUGO_AURA_API__;
|
callbackFn,
|
||||||
|
binPath,
|
||||||
|
logPath,
|
||||||
|
progressFilePath
|
||||||
|
) => {
|
||||||
|
let dlResult = false;
|
||||||
|
if (fs.existsSync(path.join(path.dirname(binPath), ".force"))) {
|
||||||
|
dlResult = true;
|
||||||
|
} else {
|
||||||
|
// TODO: Channel selection
|
||||||
|
const apiInfo = global.__HUGO_AURA_API__;
|
||||||
|
|
||||||
const getVerPromise = new Promise(async (resolveGetVerReq) => {
|
const getVerPromise = new Promise(async (resolveGetVerReq) => {
|
||||||
// ↓ 目前 channel param 没有什么用处
|
// ↓ 目前 channel param 没有什么用处
|
||||||
for (const apiDomain of apiInfo.domains) {
|
for (const apiDomain of apiInfo.domains) {
|
||||||
const reqPromise = new Promise((resolveHttpRequest) => {
|
const reqPromise = new Promise((resolveHttpRequest) => {
|
||||||
nodeHttps
|
nodeHttps
|
||||||
.get(
|
.get(
|
||||||
`${apiDomain}${apiInfo.aikariUpdate}?channel=${channel}`,
|
`${apiDomain}${apiInfo.aikariUpdate}?channel=${channel}`,
|
||||||
(rep) => {
|
(rep) => {
|
||||||
let dataChunk = "";
|
let dataChunk = "";
|
||||||
rep.on("data", (chunk) => {
|
rep.on("data", (chunk) => {
|
||||||
dataChunk += chunk;
|
dataChunk += chunk;
|
||||||
});
|
});
|
||||||
|
|
||||||
rep.on("end", () => {
|
rep.on("end", () => {
|
||||||
let parsedData = {};
|
let parsedData = {};
|
||||||
try {
|
try {
|
||||||
parsedData = JSON.parse(dataChunk);
|
parsedData = JSON.parse(dataChunk);
|
||||||
} catch (e) {
|
} catch (e) {
|
||||||
callbackFn({
|
callbackFn({
|
||||||
id: "",
|
id: "",
|
||||||
progress: 0,
|
progress: 0,
|
||||||
status: "struggling",
|
status: "struggling",
|
||||||
dlUrl: null,
|
dlUrl: null,
|
||||||
savePath: null,
|
savePath: null,
|
||||||
message: `数据解析失败, 正在尝试 API 域名 ${
|
message: `数据解析失败, 正在尝试 API 域名 ${
|
||||||
apiInfo.domains[apiInfo.domains.indexOf(apiDomain) + 1]
|
apiInfo.domains[
|
||||||
} ...`,
|
apiInfo.domains.indexOf(apiDomain) + 1
|
||||||
errorObj: e,
|
]
|
||||||
});
|
} ...`,
|
||||||
|
|
||||||
setTimeout(() => {
|
|
||||||
resolveHttpRequest({
|
|
||||||
success: false,
|
|
||||||
errorObj: e,
|
errorObj: e,
|
||||||
});
|
});
|
||||||
}, 1000);
|
|
||||||
|
setTimeout(() => {
|
||||||
|
resolveHttpRequest({
|
||||||
|
success: false,
|
||||||
|
errorObj: e,
|
||||||
|
});
|
||||||
|
}, 1000);
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
resolveHttpRequest({
|
||||||
|
success: true,
|
||||||
|
data: parsedData,
|
||||||
|
});
|
||||||
return;
|
return;
|
||||||
}
|
|
||||||
|
|
||||||
resolveHttpRequest({
|
|
||||||
success: true,
|
|
||||||
data: parsedData,
|
|
||||||
});
|
});
|
||||||
return;
|
}
|
||||||
});
|
)
|
||||||
}
|
.on("error", (e) => {
|
||||||
)
|
callbackFn({
|
||||||
.on("error", (e) => {
|
id: "",
|
||||||
callbackFn({
|
progress: 0,
|
||||||
id: "",
|
status: "struggling",
|
||||||
progress: 0,
|
dlUrl: null,
|
||||||
status: "struggling",
|
savePath: null,
|
||||||
dlUrl: null,
|
message: `连接失败, 正在尝试 API 域名 ${
|
||||||
savePath: null,
|
apiInfo.domains[apiInfo.domains.indexOf(apiDomain) + 1]
|
||||||
message: `连接失败, 正在尝试 API 域名 ${
|
} ...`,
|
||||||
apiInfo.domains[apiInfo.domains.indexOf(apiDomain) + 1]
|
|
||||||
} ...`,
|
|
||||||
errorObj: e,
|
|
||||||
});
|
|
||||||
|
|
||||||
setTimeout(() => {
|
|
||||||
resolveHttpRequest({
|
|
||||||
success: false,
|
|
||||||
errorObj: e,
|
errorObj: e,
|
||||||
});
|
});
|
||||||
}, 1000);
|
|
||||||
});
|
|
||||||
});
|
|
||||||
|
|
||||||
const requestResult = await reqPromise;
|
setTimeout(() => {
|
||||||
if (requestResult.success) {
|
resolveHttpRequest({
|
||||||
resolveGetVerReq({
|
success: false,
|
||||||
success: true,
|
errorObj: e,
|
||||||
data: requestResult.data,
|
});
|
||||||
|
}, 1000);
|
||||||
|
});
|
||||||
});
|
});
|
||||||
break;
|
|
||||||
} else {
|
const requestResult = await reqPromise;
|
||||||
continue;
|
if (requestResult.success) {
|
||||||
|
resolveGetVerReq({
|
||||||
|
success: true,
|
||||||
|
data: requestResult.data,
|
||||||
|
});
|
||||||
|
break;
|
||||||
|
} else {
|
||||||
|
continue;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
resolveGetVerReq({
|
||||||
|
success: false,
|
||||||
|
data: null,
|
||||||
|
});
|
||||||
|
});
|
||||||
|
|
||||||
|
const rawResInfo = await getVerPromise;
|
||||||
|
if (!rawResInfo.success) {
|
||||||
|
callbackFn({
|
||||||
|
id: "",
|
||||||
|
progress: 0,
|
||||||
|
status: "failed",
|
||||||
|
dlUrl: null,
|
||||||
|
savePath: null,
|
||||||
|
message:
|
||||||
|
"未能获取 Aikari 版本信息, 所有 API 域名均无法连接, 建议前往 GitHub 下载安装包并自行安装",
|
||||||
|
});
|
||||||
|
return false;
|
||||||
}
|
}
|
||||||
|
|
||||||
resolveGetVerReq({
|
const aikariVersionInfo = rawResInfo.data;
|
||||||
success: false,
|
|
||||||
data: null,
|
|
||||||
});
|
|
||||||
});
|
|
||||||
|
|
||||||
const rawResInfo = await getVerPromise;
|
let deviceArch = process.env.PROCESSOR_ARCHITEW6432
|
||||||
if (!rawResInfo.success) {
|
? process.env.PROCESSOR_ARCHITEW6432
|
||||||
callbackFn({
|
: process.env.PROCESSOR_ARCHITECTURE;
|
||||||
id: "",
|
// @ts-expect-error
|
||||||
progress: 0,
|
deviceArch = deviceArch.toLowerCase();
|
||||||
status: "failed",
|
|
||||||
dlUrl: null,
|
|
||||||
savePath: null,
|
|
||||||
message:
|
|
||||||
"未能获取 Aikari 版本信息, 所有 API 域名均无法连接, 建议前往 GitHub 下载安装包并自行安装",
|
|
||||||
});
|
|
||||||
return false;
|
|
||||||
}
|
|
||||||
|
|
||||||
const aikariVersionInfo = rawResInfo.data;
|
if (
|
||||||
|
!Object.keys(aikariVersionInfo.data.downloadUrl).includes(deviceArch)
|
||||||
|
) {
|
||||||
|
callbackFn({
|
||||||
|
id: "",
|
||||||
|
progress: 0,
|
||||||
|
status: "failed",
|
||||||
|
dlUrl: null,
|
||||||
|
savePath: null,
|
||||||
|
message: `不支持的处理器架构, 检测到的架构: "${deviceArch}"`,
|
||||||
|
});
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
let deviceArch = process.env.PROCESSOR_ARCHITEW6432
|
const downloadFilePromise = new Promise((resolve) => {
|
||||||
? process.env.PROCESSOR_ARCHITEW6432
|
fsComposables.downloadFile(
|
||||||
: process.env.PROCESSOR_ARCHITECTURE;
|
aikariVersionInfo.data.downloadUrl[deviceArch],
|
||||||
// @ts-expect-error
|
binPath,
|
||||||
deviceArch = deviceArch.toLowerCase();
|
(...args) => {
|
||||||
|
if (args[0].status === "done") {
|
||||||
|
resolve(true);
|
||||||
|
} else if (args[0].status === "failed") {
|
||||||
|
resolve(false);
|
||||||
|
}
|
||||||
|
|
||||||
if (!Object.keys(aikariVersionInfo.data.downloadUrl).includes(deviceArch)) {
|
callbackFn(...args);
|
||||||
callbackFn({
|
|
||||||
id: "",
|
|
||||||
progress: 0,
|
|
||||||
status: "failed",
|
|
||||||
dlUrl: null,
|
|
||||||
savePath: null,
|
|
||||||
message: `不支持的处理器架构, 检测到的架构: "${deviceArch}"`,
|
|
||||||
});
|
|
||||||
return false;
|
|
||||||
}
|
|
||||||
|
|
||||||
const downloadFilePromise = new Promise((resolve) => {
|
|
||||||
fsComposables.downloadFile(
|
|
||||||
aikariVersionInfo.data.downloadUrl[deviceArch],
|
|
||||||
binPath,
|
|
||||||
(...args) => {
|
|
||||||
if (args[0].status === "done") {
|
|
||||||
resolve(true);
|
|
||||||
} else if (args[0].status === "failed") {
|
|
||||||
resolve(false);
|
|
||||||
}
|
}
|
||||||
|
);
|
||||||
|
});
|
||||||
|
|
||||||
callbackFn(...args);
|
dlResult = await downloadFilePromise;
|
||||||
}
|
}
|
||||||
);
|
|
||||||
});
|
|
||||||
|
|
||||||
const dlResult = await downloadFilePromise;
|
|
||||||
|
|
||||||
if (dlResult) {
|
if (dlResult) {
|
||||||
callbackFn({
|
callbackFn({
|
||||||
@@ -224,7 +241,7 @@ const functions = {
|
|||||||
|
|
||||||
const runInstPromise = new Promise((resolve) => {
|
const runInstPromise = new Promise((resolve) => {
|
||||||
exec(
|
exec(
|
||||||
`"${binPath}" /VERYSILENT /SUPPRESSMSGBOXES /LOG="${binPath}\\..\\Aikari-Install.log"`,
|
`"${binPath}" /VERYSILENT /SUPPRESSMSGBOXES /LOG="${logPath}" /PROGRESS_FILE="${progressFilePath}"`,
|
||||||
(err, stdout, stderr) => {
|
(err, stdout, stderr) => {
|
||||||
if (err) {
|
if (err) {
|
||||||
console.error(
|
console.error(
|
||||||
@@ -237,6 +254,13 @@ const functions = {
|
|||||||
);
|
);
|
||||||
});
|
});
|
||||||
|
|
||||||
|
if (!fs.existsSync(path.dirname(progressFilePath))) {
|
||||||
|
fs.mkdirSync(path.dirname(progressFilePath));
|
||||||
|
}
|
||||||
|
if (!fs.existsSync(progressFilePath)) {
|
||||||
|
fs.writeFileSync(progressFilePath, "");
|
||||||
|
}
|
||||||
|
|
||||||
callbackFn({
|
callbackFn({
|
||||||
id: "INSTALL_STAGE",
|
id: "INSTALL_STAGE",
|
||||||
progress: 15,
|
progress: 15,
|
||||||
@@ -246,8 +270,87 @@ const functions = {
|
|||||||
message: "正在安装 Aikari...",
|
message: "正在安装 Aikari...",
|
||||||
});
|
});
|
||||||
|
|
||||||
|
let instFilesStageLastFakeLoadProgressPercent = -1;
|
||||||
|
|
||||||
|
const fileProgressWatcher = fs.watch(progressFilePath, (eventType) => {
|
||||||
|
if (eventType === "change") {
|
||||||
|
try {
|
||||||
|
const content = fs.readFileSync(progressFilePath, "utf-8").trim();
|
||||||
|
if (content === "") {
|
||||||
|
callbackFn({
|
||||||
|
id: "INSTALL_STAGE",
|
||||||
|
progress: 20,
|
||||||
|
status: "progressing",
|
||||||
|
dlUrl: null,
|
||||||
|
savePath: null,
|
||||||
|
message: "正在准备安装...",
|
||||||
|
});
|
||||||
|
} else {
|
||||||
|
const contentArr = content.split(";");
|
||||||
|
switch (contentArr[0]) {
|
||||||
|
case "DL_VC_REDIST":
|
||||||
|
callbackFn({
|
||||||
|
id: "INSTALL_STAGE",
|
||||||
|
progress:
|
||||||
|
20 +
|
||||||
|
Math.round((parseFloat(contentArr[1]) / 10) * 2), // 20 + [0, 20] === 20 ~ 40
|
||||||
|
status: "progressing",
|
||||||
|
dlUrl: null,
|
||||||
|
savePath: null,
|
||||||
|
message: `正在下载 VC++ 运行时... (${contentArr[1]}%)`,
|
||||||
|
});
|
||||||
|
break;
|
||||||
|
|
||||||
|
case "INST_FILES":
|
||||||
|
if (instFilesStageLastFakeLoadProgressPercent === -1) {
|
||||||
|
instFilesStageLastFakeLoadProgressPercent = 60;
|
||||||
|
} else if (instFilesStageLastFakeLoadProgressPercent < 85) {
|
||||||
|
instFilesStageLastFakeLoadProgressPercent +=
|
||||||
|
Math.random() * (5 - 1) + 1;
|
||||||
|
}
|
||||||
|
callbackFn({
|
||||||
|
id: "INSTALL_STAGE",
|
||||||
|
progress: instFilesStageLastFakeLoadProgressPercent,
|
||||||
|
status: "progressing",
|
||||||
|
dlUrl: null,
|
||||||
|
savePath: null,
|
||||||
|
message: `正在解压文件...`,
|
||||||
|
});
|
||||||
|
break;
|
||||||
|
|
||||||
|
case "INST_VC_REDIST":
|
||||||
|
callbackFn({
|
||||||
|
id: "INSTALL_STAGE",
|
||||||
|
progress: 90,
|
||||||
|
status: "progressing",
|
||||||
|
dlUrl: null,
|
||||||
|
savePath: null,
|
||||||
|
message: `正在安装 VC++ 运行时...`,
|
||||||
|
});
|
||||||
|
break;
|
||||||
|
|
||||||
|
case "DONE":
|
||||||
|
callbackFn({
|
||||||
|
id: "INSTALL_STAGE",
|
||||||
|
progress: 99,
|
||||||
|
status: "progressing",
|
||||||
|
dlUrl: null,
|
||||||
|
savePath: null,
|
||||||
|
message: `即将完成`,
|
||||||
|
});
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
} catch (e) {
|
||||||
|
// Ignore
|
||||||
|
}
|
||||||
|
}
|
||||||
|
});
|
||||||
|
|
||||||
const instResult = await runInstPromise;
|
const instResult = await runInstPromise;
|
||||||
|
|
||||||
|
fileProgressWatcher.close();
|
||||||
|
|
||||||
callbackFn({
|
callbackFn({
|
||||||
id: "INSTALL_STAGE",
|
id: "INSTALL_STAGE",
|
||||||
progress: 100,
|
progress: 100,
|
||||||
@@ -267,6 +370,8 @@ const functions = {
|
|||||||
}
|
}
|
||||||
|
|
||||||
fs.unlinkSync(binPath);
|
fs.unlinkSync(binPath);
|
||||||
|
fs.unlinkSync(logPath);
|
||||||
|
fs.unlinkSync(progressFilePath);
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
clearHostFileItem: async () => {
|
clearHostFileItem: async () => {
|
||||||
@@ -341,6 +446,14 @@ const applyAikariIpcHandler = (ipcMain) => {
|
|||||||
AIKARI_TEMP_DL_DIR,
|
AIKARI_TEMP_DL_DIR,
|
||||||
"Aikari-Installer.exe"
|
"Aikari-Installer.exe"
|
||||||
);
|
);
|
||||||
|
const AIKARI_INSTALLER_LOG_FILENAME = path.join(
|
||||||
|
AIKARI_TEMP_DL_DIR,
|
||||||
|
"install.log"
|
||||||
|
);
|
||||||
|
const AIKARI_INSTALLER_PROGRESS_FIPC_FILENAME = path.join(
|
||||||
|
AIKARI_TEMP_DL_DIR,
|
||||||
|
"PROGRESS"
|
||||||
|
);
|
||||||
|
|
||||||
// Prev PLS Cfg
|
// Prev PLS Cfg
|
||||||
const OLD_PLS_INSTALL_DIR = path.join(
|
const OLD_PLS_INSTALL_DIR = path.join(
|
||||||
@@ -694,6 +807,8 @@ const applyAikariIpcHandler = (ipcMain) => {
|
|||||||
async (_evt, arg) => {
|
async (_evt, arg) => {
|
||||||
if (fs.existsSync(AIKARI_TEMP_DL_DIR)) {
|
if (fs.existsSync(AIKARI_TEMP_DL_DIR)) {
|
||||||
try {
|
try {
|
||||||
|
fs.unlinkSync(AIKARI_INSTALLER_LOG_FILENAME);
|
||||||
|
fs.unlinkSync(AIKARI_INSTALLER_PROGRESS_FIPC_FILENAME);
|
||||||
fs.unlinkSync(AIKARI_TEMP_DL_DIR);
|
fs.unlinkSync(AIKARI_TEMP_DL_DIR);
|
||||||
} catch (err) {
|
} catch (err) {
|
||||||
console.error(err);
|
console.error(err);
|
||||||
@@ -725,7 +840,9 @@ const applyAikariIpcHandler = (ipcMain) => {
|
|||||||
status
|
status
|
||||||
);
|
);
|
||||||
},
|
},
|
||||||
AIKARI_TEMP_INSTALLER_FILENAME
|
AIKARI_TEMP_INSTALLER_FILENAME,
|
||||||
|
AIKARI_INSTALLER_LOG_FILENAME,
|
||||||
|
AIKARI_INSTALLER_PROGRESS_FIPC_FILENAME
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
);
|
);
|
||||||
|
|||||||
@@ -31,8 +31,8 @@ if (!global.__HUGO_AURA_UI_REACTIVES__.subConfig)
|
|||||||
getRetryStatusDescByErrId: (errIdString) => {
|
getRetryStatusDescByErrId: (errIdString) => {
|
||||||
switch (errIdString) {
|
switch (errIdString) {
|
||||||
case "E_AUTH_TOKEN_GET_FAILED":
|
case "E_AUTH_TOKEN_GET_FAILED":
|
||||||
return `<p>Aikari 注册表访问失败, 这是一个极为罕见的问题</p>
|
return `<p>Aikari 注册表访问失败, 这可能代表 Aikari 在启动后立即发生了崩溃</p>
|
||||||
<p>请检查 HKEY_USERS\\.DEFAULT 是否存在, 并反馈至 GitHub Issues</p>`;
|
<p>请将此情况反馈至 GitHub Issues, 并附上您的系统版本信息</p>`;
|
||||||
case "E_WS_CONN_FAILED_AFT_MULTIPLE_TRIES":
|
case "E_WS_CONN_FAILED_AFT_MULTIPLE_TRIES":
|
||||||
return `<p>在多次尝试连接后仍然失败, 请检查服务是否已启动</p>`;
|
return `<p>在多次尝试连接后仍然失败, 请检查服务是否已启动</p>`;
|
||||||
case "E_IS_LOADING":
|
case "E_IS_LOADING":
|
||||||
@@ -921,7 +921,7 @@ if (!global.__HUGO_AURA_UI_REACTIVES__.subConfig)
|
|||||||
} else {
|
} else {
|
||||||
GLOBAL_FUNCTIONS.updatePBarStatus(
|
GLOBAL_FUNCTIONS.updatePBarStatus(
|
||||||
info.progress,
|
info.progress,
|
||||||
`正在安装 Aikari...`,
|
info.message,
|
||||||
"normal",
|
"normal",
|
||||||
false
|
false
|
||||||
);
|
);
|
||||||
|
|||||||
Reference in New Issue
Block a user