9 Commits

Author SHA1 Message Date
Minoricew
c84aaef994 [ Feat] Add settings for Aikari Telemetry 2025-11-22 19:21:52 +08:00
Minoricew
8656fd0334 [🛠️ Fix] Unexpected infinite reconn for ACM 2025-11-21 02:00:30 +08:00
Minoricew
80354cc310 [🛠️ Fix] Add auto clean for prev Py-PLS installation 2025-11-18 20:12:00 +08:00
Minoricew
cd946a60fb [🛠️ Fix] Add vcredist inst check & Improve Aikari lifecycle mgmt
1. [+] 添加了对于 VC Redist 是否安装的状态检查 & 引导
2. [*] 略微调整和改进了 Aikari 的生命周期管理, 避免彻底卡死等情况, 添加更多的错误说明
2025-11-18 19:37:52 +08:00
Minorice
a6c68782a0 [🔄 Chore] Add rickroll to README (bushi 2025-11-18 02:19:14 +08:00
Minoricew
d6db3e398a [🛠️ Fix] Settings renderer 'auraDisable' bug 2025-11-18 01:00:30 +08:00
Minoricew
7afeb39275 [️ BREAKING / Feat] Bump version to v0.2.0-RC1
- [+] 完成了下载 & 安装 Aikari 的步骤
- [*] 修复了部分状态管理错误, 但没修全
2025-11-17 00:40:21 +08:00
Minoricew
b277a61923 [🔄 Chore] Prepare for Aikari (2/2) 2025-11-17 00:40:21 +08:00
Minoricew
08290301a3 [🔄 Chore] Prepare for Aikari (1/2)
1. [/] 一些无意义的重命名工作
2. [*] 修改了 WebSocket 对接逻辑
2025-11-17 00:40:21 +08:00
43 changed files with 2843 additions and 1964 deletions

View File

@@ -1,8 +1,6 @@
name: Pack source code & Create CI/CD Release name: Pack source code & Create CI/CD Release
on: on:
schedule:
- cron: '0 5 * * *'
push: push:
branches: [dev, stable] branches: [dev, stable]
pull_request: pull_request:

1
.gitignore vendored
View File

@@ -15,6 +15,7 @@ autoConfiguration.json
# NPM Packages # NPM Packages
node_modules/ node_modules/
package-lock.json
# OS Files # OS Files
desktop.ini desktop.ini

View File

@@ -3,7 +3,7 @@
<h1 align="center">HugoAura</h1> <h1 align="center">HugoAura</h1>
<h4 align="center">下一代希沃管家注入式修改方案</h4> <h4 align="center">下一代希沃管家注入式修改方案</h4>
<div align="center"> <div align="center">
<a href="https://github.com/HugoAura/HugoAura">首页</a> · <a href="https://hugo.aurax.cc/about">关于</a> · <a href="https://github.com/HugoAura/HugoAura/wiki">文档</a> · <a href="https://github.com/HugoAura/HugoAura/issues">反馈</a> · <a href="https://forum.aurax.cc/">社区</a> <a href="https://github.com/HugoAura/HugoAura">首页</a> · <a href="https://hugo.aurax.cc/about">关于 (WIP)</a> · <a href="https://docs.aurax.cc/HugoAura-Main/">文档 (WIP)</a> · <a href="https://github.com/HugoAura/HugoAura/issues">反馈</a> · <a href="https://forum.aurax.cc/">社区</a>
</div> </div>
<br /> <br />
@@ -14,17 +14,12 @@
> [!IMPORTANT] > [!IMPORTANT]
> 已经过测试的希沃管家版本: v1.5.5.3878 > 已经过测试的希沃管家版本: v1.5.5.3878
> [!CAUTION] > [!NOTE]
> 由于作者高三学业繁忙, 项目正处于缓慢更新阶段。 > **社群信息**
> >
> 作者将尽可能抽出时间维护项目, 但新功能的实现速度将大幅降低。 > - [QQ 群 (用户自治)](https://qm.qq.com/q/buo7m9oHBK)
> - [Telegram 群组](https://www.bilibili.com/video/BV1GJ411x7h7/) (暂时还没有...)
> [!WARNING] > - [STCN 论坛讨论版块](https://www.bilibili.com/video/BV1GJ411x7h7/) (暂时还没有...)
> 我们正在使用 C++ 重构 PLS, 新的子项目代号为 [Aikari](https://github.com/HugoAura/HugoAura-Aikari)。其将集成 MQTT 消息中转、HugoAura 自动更新、特权操作辅助等功能。
>
> 在 Aikari 的首个 Alpha 版本发布前, HugoAura 主项目与 [PLS](https://github.com/HugoAura/HugoAura-PLS) 的新功能更新频率将减弱。
>
> 您依然可以提出 Feature Request 或 Bug Report。开发者将在能力 / 时间限度内尽可能处理。
![Repobeats](https://repobeats.axiom.co/api/embed/69b5be5daacef624b8f5e4b8966a0b5898439a22.svg "Repobeats analytics image") ![Repobeats](https://repobeats.axiom.co/api/embed/69b5be5daacef624b8f5e4b8966a0b5898439a22.svg "Repobeats analytics image")

View File

@@ -1,6 +1,6 @@
{ {
"name": "HugoAura", "name": "HugoAura",
"version": "0.1.1-beta-patch-1", "version": "0.2.0-rc1-p2",
"description": "Aura for SeewoHugo", "description": "Aura for SeewoHugo",
"main": "app.asar/main.js", "main": "app.asar/main.js",
"dependencies": {}, "dependencies": {},

View File

@@ -80,7 +80,7 @@ const buildIpcMain = (electron) => {
const { applyDebugIpcHandler } = require("./ipcModules/debugIpcHandler"); const { applyDebugIpcHandler } = require("./ipcModules/debugIpcHandler");
const { applyConfigIpcHandler } = require("./ipcModules/configIpcHandler"); const { applyConfigIpcHandler } = require("./ipcModules/configIpcHandler");
const { applyFsIpcHandler } = require("./ipcModules/fsIpcHandler"); const { applyFsIpcHandler } = require("./ipcModules/fsIpcHandler");
const { applyPlsIpcHandler } = require("./ipcModules/plsIpcHandler"); const { applyAikariIpcHandler } = require("./ipcModules/aikariIpcHandler");
ipcMain.handle("$aura.base.restartApplication", async () => { ipcMain.handle("$aura.base.restartApplication", async () => {
app.relaunch(); app.relaunch();
@@ -91,7 +91,7 @@ const buildIpcMain = (electron) => {
applyDebugIpcHandler(ipcMain); applyDebugIpcHandler(ipcMain);
applyConfigIpcHandler(ipcMain); applyConfigIpcHandler(ipcMain);
applyFsIpcHandler(ipcMain); applyFsIpcHandler(ipcMain);
applyPlsIpcHandler(ipcMain); applyAikariIpcHandler(ipcMain);
}; };
module.exports = { buildIpcMain }; module.exports = { buildIpcMain };

View File

@@ -2,7 +2,7 @@
const __SCOPE = "main"; const __SCOPE = "main";
const { exec } = require("child_process"); const { exec, execSync } = require("child_process");
const fs = require("fs"); const fs = require("fs");
const path = require("path"); const path = require("path");
const nodeHttps = require("https"); const nodeHttps = require("https");
@@ -68,7 +68,7 @@ const functions = {
* @param {(arg: DownloadTask) => any} callbackFn * @param {(arg: DownloadTask) => any} callbackFn
* @param {string} binPath * @param {string} binPath
*/ */
handlePLSDownload: async (channel, callbackFn, binPath) => { handleAikariDlAndInstall: async (channel, callbackFn, binPath) => {
// TODO: Channel selection // TODO: Channel selection
const apiInfo = global.__HUGO_AURA_API__; const apiInfo = global.__HUGO_AURA_API__;
@@ -78,7 +78,7 @@ const functions = {
const reqPromise = new Promise((resolveHttpRequest) => { const reqPromise = new Promise((resolveHttpRequest) => {
nodeHttps nodeHttps
.get( .get(
`${apiDomain}${apiInfo.plsUpdate}?channel=${channel}`, `${apiDomain}${apiInfo.aikariUpdate}?channel=${channel}`,
(rep) => { (rep) => {
let dataChunk = ""; let dataChunk = "";
rep.on("data", (chunk) => { rep.on("data", (chunk) => {
@@ -167,12 +167,13 @@ const functions = {
status: "failed", status: "failed",
dlUrl: null, dlUrl: null,
savePath: null, savePath: null,
message: "未能获取 PLS 版本信息, 所有 API 域名均无法连接", message:
"未能获取 Aikari 版本信息, 所有 API 域名均无法连接, 建议前往 GitHub 下载安装包并自行安装",
}); });
return false; return false;
} }
const plsVersionInfo = rawResInfo.data; const aikariVersionInfo = rawResInfo.data;
let deviceArch = process.env.PROCESSOR_ARCHITEW6432 let deviceArch = process.env.PROCESSOR_ARCHITEW6432
? process.env.PROCESSOR_ARCHITEW6432 ? process.env.PROCESSOR_ARCHITEW6432
@@ -180,32 +181,92 @@ const functions = {
// @ts-expect-error // @ts-expect-error
deviceArch = deviceArch.toLowerCase(); deviceArch = deviceArch.toLowerCase();
if (!Object.keys(plsVersionInfo.data.downloadUrl).includes(deviceArch)) { if (!Object.keys(aikariVersionInfo.data.downloadUrl).includes(deviceArch)) {
callbackFn({ callbackFn({
id: "", id: "",
progress: 0, progress: 0,
status: "failed", status: "failed",
dlUrl: null, dlUrl: null,
savePath: null, savePath: null,
message: `处理器架构识别失败, 检测到的架构: ${deviceArch}`, message: `不支持的处理器架构, 检测到的架构: "${deviceArch}"`,
}); });
return false; return false;
} }
fsComposables.downloadFile( const downloadFilePromise = new Promise((resolve) => {
plsVersionInfo.data.downloadUrl[deviceArch], fsComposables.downloadFile(
binPath, aikariVersionInfo.data.downloadUrl[deviceArch],
(...args) => { binPath,
if (args[0].status === "done") { (...args) => {
if (global.__HUGO_AURA__.plsStats) { if (args[0].status === "done") {
global.__HUGO_AURA__.plsStats.installed = true; resolve(true);
global.__HUGO_AURA__.plsStats.status = "dead"; } else if (args[0].status === "failed") {
resolve(false);
} }
}
callbackFn(...args); callbackFn(...args);
}
);
});
const dlResult = await downloadFilePromise;
if (dlResult) {
callbackFn({
id: "INSTALL_STAGE",
progress: 5,
status: "waiting",
dlUrl: null,
savePath: null,
message: "准备运行 Aikari 安装程序...",
});
const runInstPromise = new Promise((resolve) => {
exec(
`"${binPath}" /VERYSILENT /SUPPRESSMSGBOXES /LOG="${binPath}\\..\\Aikari-Install.log"`,
(err, stdout, stderr) => {
if (err) {
console.error(
`[HugoAura / Main / Aikari IPC Handler / Install Aikari] Error running installer: ${stderr}`
);
resolve({ success: false });
}
resolve({ success: true });
}
);
});
callbackFn({
id: "INSTALL_STAGE",
progress: 15,
status: "progressing",
dlUrl: null,
savePath: null,
message: "正在安装 Aikari...",
});
const instResult = await runInstPromise;
callbackFn({
id: "INSTALL_STAGE",
progress: 100,
status: instResult.success ? "done" : "failed",
dlUrl: null,
savePath: null,
message: instResult.success
? "Aikari 安装成功"
: "安装失败, 请检查 %TEMP%/Aikari-Install-Temp 下的安装日志",
});
if (instResult.success) {
if (global.__HUGO_AURA__.aikariStats) {
global.__HUGO_AURA__.aikariStats.installed = true;
global.__HUGO_AURA__.aikariStats.status = "dead";
}
} }
);
fs.unlinkSync(binPath);
}
}, },
}; };
@@ -213,37 +274,65 @@ const functions = {
* *
* @param {import("../../../types/main/electron").AuraIPCMain} ipcMain * @param {import("../../../types/main/electron").AuraIPCMain} ipcMain
*/ */
const applyPlsIpcHandler = (ipcMain) => { const applyAikariIpcHandler = (ipcMain) => {
const methodBase = "$aura.pls"; const methodBase = "$aura.aikari";
const PLS_INSTALL_DIR = path.join("C:\\Program Files", "HugoAura PLS", "bin"); const AIKARI_DEFAULT_INSTALL_DIR = path.join(
const PLS_BIN_PATH = path.join(PLS_INSTALL_DIR, "HugoAura-PLS.exe"); "C:\\Program Files",
const PLS_SVC_NAME = "HugoAuraPLS"; "HugoAura",
"Aikari"
);
const AIKARI_LAUNCHER_PATH = path.join(
AIKARI_DEFAULT_INSTALL_DIR,
"Aikari-Launcher.exe"
);
const AIKARI_UNINSTALLER_PATH = path.join(
AIKARI_DEFAULT_INSTALL_DIR,
"unins000.exe"
);
const AIKARI_SVC_NAME = "HugoAuraAikari";
const isPlsDetached = process.argv.includes("--pls-detach"); const AIKARI_TEMP_DL_DIR = path.join(
require("os").tmpdir(),
"Aikari-Install-Temp"
);
const AIKARI_TEMP_INSTALLER_FILENAME = path.join(
AIKARI_TEMP_DL_DIR,
"Aikari-Installer.exe"
);
global.__HUGO_AURA__.plsStats = { // Prev PLS Cfg
const OLD_PLS_INSTALL_DIR = path.join(
"C:\\Program Files",
"HugoAura PLS",
"bin"
);
const OLD_PLS_SVC_NAME = "HugoAuraPLS";
const isAikariDetached = process.argv.includes("--aikari-detach");
global.__HUGO_AURA__.aikariStats = {
installed: false, installed: false,
launched: false, launched: false,
detached: isPlsDetached, detached: isAikariDetached,
connected: false, connected: false,
version: "unknown", version: "unknown",
status: "dead", status: "dead",
authToken: global.__HUGO_AURA_CONFIG__.plsToken, authToken: global.__HUGO_AURA_CONFIG__.aikariToken,
}; };
ipcMain.handle( ipcMain.handle(
`${methodBase}.getPlsBinExists`, `${methodBase}.getIfAikariBinExists`,
/** /**
* *
* @returns {{ success: boolean; data: { isExists: boolean }; error?: Error }} * @returns {{ success: boolean; data: { isExists: boolean }; error?: Error }}
*/ */
(_event, _arg) => { (_event, _arg) => {
try { try {
const result = fs.existsSync(PLS_BIN_PATH); const result = fs.existsSync(AIKARI_LAUNCHER_PATH);
if (global.__HUGO_AURA__.plsStats?.status === "notInstalled") { if (global.__HUGO_AURA__.aikariStats?.status === "notInstalled") {
global.__HUGO_AURA__.plsStats.status = "dead"; global.__HUGO_AURA__.aikariStats.status = "dead";
} }
return { return {
@@ -252,7 +341,7 @@ const applyPlsIpcHandler = (ipcMain) => {
}; };
} catch (e) { } catch (e) {
// @ts-expect-error // @ts-expect-error
global.__HUGO_AURA__.plsStats.status = "notInstalled"; global.__HUGO_AURA__.aikariStats.status = "notInstalled";
return { return {
success: false, success: false,
data: { isExists: false }, data: { isExists: false },
@@ -263,7 +352,7 @@ const applyPlsIpcHandler = (ipcMain) => {
); );
ipcMain.handle( ipcMain.handle(
`${methodBase}.ensurePlsInstallDir`, `${methodBase}.ensureAikariInstallDir`,
/** /**
* *
* @param {import("electron").IpcMainInvokeEvent} _event * @param {import("electron").IpcMainInvokeEvent} _event
@@ -271,24 +360,24 @@ const applyPlsIpcHandler = (ipcMain) => {
* @returns {{ success: boolean; data: { alreadyExists: boolean; createdDir: string; }; error?: Error }} * @returns {{ success: boolean; data: { alreadyExists: boolean; createdDir: string; }; error?: Error }}
*/ */
(_event, _arg) => { (_event, _arg) => {
const alreadyExists = fs.existsSync(PLS_INSTALL_DIR); const alreadyExists = fs.existsSync(AIKARI_DEFAULT_INSTALL_DIR);
if (alreadyExists) { if (alreadyExists) {
return { return {
success: true, success: true,
data: { data: {
alreadyExists: true, alreadyExists: true,
createdDir: PLS_INSTALL_DIR, createdDir: AIKARI_DEFAULT_INSTALL_DIR,
}, },
}; };
} }
try { try {
fs.mkdirSync(PLS_INSTALL_DIR, { recursive: true }); fs.mkdirSync(AIKARI_DEFAULT_INSTALL_DIR, { recursive: true });
return { return {
success: true, success: true,
data: { data: {
alreadyExists: false, alreadyExists: false,
createdDir: PLS_INSTALL_DIR, createdDir: AIKARI_DEFAULT_INSTALL_DIR,
}, },
}; };
} catch (error) { } catch (error) {
@@ -296,7 +385,7 @@ const applyPlsIpcHandler = (ipcMain) => {
success: false, success: false,
data: { data: {
alreadyExists: false, alreadyExists: false,
createdDir: PLS_INSTALL_DIR, createdDir: AIKARI_DEFAULT_INSTALL_DIR,
}, },
error: error, error: error,
}; };
@@ -305,30 +394,30 @@ const applyPlsIpcHandler = (ipcMain) => {
); );
ipcMain.handle( ipcMain.handle(
`${methodBase}.getPlsStats`, `${methodBase}.getAikariStatus`,
/** /**
* *
* @returns {{ success: boolean; data: import("../../../types/shared/pls/status").PLSStatus | null | undefined; }} * @returns {{ success: boolean; data: import("../../../types/shared/aikari/status").AikariStatus | null | undefined; }}
*/ */
(_event, _arg) => { (_event, _arg) => {
return { return {
success: true, success: true,
data: global.__HUGO_AURA__.plsStats, data: global.__HUGO_AURA__.aikariStats,
}; };
} }
); );
ipcMain.handle( ipcMain.handle(
`${methodBase}.updatePlsStats`, `${methodBase}.updateAikariStatus`,
/** /**
* *
* @param {import("electron").IpcMainInvokeEvent} _event * @param {import("electron").IpcMainInvokeEvent} _event
* @param {import("../../../types/shared/pls/status").PLSStatus} arg * @param {import("../../../types/shared/aikari/status").AikariStatus} arg
* @returns * @returns
*/ */
(_event, arg) => { (_event, arg) => {
global.__HUGO_AURA__.plsStats = arg; global.__HUGO_AURA__.aikariStats = arg;
ipcMain.send("assistant", `${methodBase}.post.onPlsStatsUpdate`, arg); ipcMain.send("assistant", `${methodBase}.post.onAikariStatsUpdate`, arg);
return { return {
success: true, success: true,
}; };
@@ -336,7 +425,7 @@ const applyPlsIpcHandler = (ipcMain) => {
); );
ipcMain.handle( ipcMain.handle(
`${methodBase}.getPlsSettings`, `${methodBase}.getAikariSettings`,
/** /**
* *
* @returns {{ success: boolean; data: Record<any, any> | null | undefined }} * @returns {{ success: boolean; data: Record<any, any> | null | undefined }}
@@ -344,13 +433,13 @@ const applyPlsIpcHandler = (ipcMain) => {
(_event, _arg) => { (_event, _arg) => {
return { return {
success: true, success: true,
data: global.__HUGO_AURA__.plsSettings, data: global.__HUGO_AURA__.aikariSettings,
}; };
} }
); );
ipcMain.handle( ipcMain.handle(
`${methodBase}.updatePlsSettings`, `${methodBase}.updateAikariSettings`,
/** /**
* *
* @param {import("electron").IpcMainInvokeEvent} _event * @param {import("electron").IpcMainInvokeEvent} _event
@@ -358,8 +447,12 @@ const applyPlsIpcHandler = (ipcMain) => {
* @returns * @returns
*/ */
(_event, arg) => { (_event, arg) => {
global.__HUGO_AURA__.plsSettings = arg; global.__HUGO_AURA__.aikariSettings = arg;
ipcMain.send("assistant", `${methodBase}.post.onPlsSettingsUpdate`, arg); ipcMain.send(
"assistant",
`${methodBase}.post.onAikariSettingsUpdate`,
arg
);
return { return {
success: true, success: true,
}; };
@@ -367,7 +460,7 @@ const applyPlsIpcHandler = (ipcMain) => {
); );
ipcMain.handle( ipcMain.handle(
`${methodBase}.getPlsRules`, `${methodBase}.getAikariRules`,
/** /**
* *
* @returns {{ success: boolean; data: Record<any, any> | null | undefined }} * @returns {{ success: boolean; data: Record<any, any> | null | undefined }}
@@ -375,13 +468,13 @@ const applyPlsIpcHandler = (ipcMain) => {
(_event, _arg) => { (_event, _arg) => {
return { return {
success: true, success: true,
data: global.__HUGO_AURA__.plsRules, data: global.__HUGO_AURA__.aikariRules,
}; };
} }
); );
ipcMain.handle( ipcMain.handle(
`${methodBase}.updatePlsRules`, `${methodBase}.updateAikariRules`,
/** /**
* *
* @param {import("electron").IpcMainInvokeEvent} _event * @param {import("electron").IpcMainInvokeEvent} _event
@@ -389,8 +482,8 @@ const applyPlsIpcHandler = (ipcMain) => {
* @returns * @returns
*/ */
(_event, arg) => { (_event, arg) => {
global.__HUGO_AURA__.plsRules = arg; global.__HUGO_AURA__.aikariRules = arg;
ipcMain.send("assistant", `${methodBase}.post.onPlsRulesUpdate`, arg); ipcMain.send("assistant", `${methodBase}.post.onAikariRulesUpdate`, arg);
return { return {
success: true, success: true,
}; };
@@ -402,7 +495,7 @@ const applyPlsIpcHandler = (ipcMain) => {
/** /**
* *
* @param {import("electron").IpcMainEvent} _event * @param {import("electron").IpcMainEvent} _event
* @param {PLSResponse} arg * @param {AikariResponse} arg
*/ */
(_event, arg) => { (_event, arg) => {
ipcMain.send("assistant", `${methodBase}.ws.post.onNewMsgRecv`, arg); ipcMain.send("assistant", `${methodBase}.ws.post.onNewMsgRecv`, arg);
@@ -414,7 +507,7 @@ const applyPlsIpcHandler = (ipcMain) => {
/** /**
* *
* @param {import("electron").IpcMainInvokeEvent} _event * @param {import("electron").IpcMainInvokeEvent} _event
* @param {ClientPLSRequest} arg * @param {ClientAikariRequest} arg
*/ */
(_event, arg) => { (_event, arg) => {
ipcMain.send( ipcMain.send(
@@ -426,17 +519,17 @@ const applyPlsIpcHandler = (ipcMain) => {
); );
ipcMain.handle( ipcMain.handle(
`${methodBase}.syncPlsConfig`, `${methodBase}.syncAikariConfig`,
/** /**
* *
* @param {import("electron").IpcMainInvokeEvent} event * @param {import("electron").IpcMainInvokeEvent} event
* @param {{ basic: Record<any, any>, rules: Record<any, any> }} arg * @param {{ basic: Record<any, any>, rules: Record<any, any> }} arg
*/ */
(event, arg) => { (event, arg) => {
global.__HUGO_AURA__.plsRules = arg.rules; global.__HUGO_AURA__.aikariRules = arg.rules;
global.__HUGO_AURA__.plsSettings = arg.basic; global.__HUGO_AURA__.aikariSettings = arg.basic;
ipcMain.send("*", `${methodBase}.syncPlsConfig`, arg, event.sender); ipcMain.send("*", `${methodBase}.syncAikariConfig`, arg, event.sender);
return { return {
success: true, success: true,
@@ -445,24 +538,27 @@ const applyPlsIpcHandler = (ipcMain) => {
); );
ipcMain.handle( ipcMain.handle(
`${methodBase}.plsLifecycleQuery`, `${methodBase}.aikariLifecycleQuery`,
/** /**
* *
* @param {import("electron").IpcMainInvokeEvent} _event * @param {import("electron").IpcMainInvokeEvent} _event
* @param {{ target: import("../../../types/shared/pls/status").PLSLifecycleType }} arg * @param {{ target: import("../../../types/shared/aikari/status").AikariLifecycleType }} arg
* @returns {Promise<{ success: boolean, result: boolean }>} * @returns {Promise<{ success: boolean, result: boolean }>}
*/ */
async (_event, arg) => { async (_event, arg) => {
switch (arg.target) { switch (arg.target) {
case "isDetached": case "isDetached":
return { success: true, result: isPlsDetached }; return { success: true, result: isAikariDetached };
case "isSvcInstalled": case "isSvcInstalled":
return await functions.querySvcDetail(PLS_SVC_NAME, "SERVICE_NAME"); return await functions.querySvcDetail(
AIKARI_SVC_NAME,
"SERVICE_NAME"
);
case "isSvcStart": case "isSvcStart":
return await functions.querySvcDetail(PLS_SVC_NAME, "RUNNING"); return await functions.querySvcDetail(AIKARI_SVC_NAME, "RUNNING");
default: default:
console.warn( console.warn(
`[HugoAura / IPC / PLS] <plsLifecycleQuery> Invalid arg.target: ${arg.target}` `[HugoAura / IPC / Aikari] <AikariLifecycleQuery> Invalid arg.target: ${arg.target}`
); );
return { return {
success: false, success: false,
@@ -473,98 +569,72 @@ const applyPlsIpcHandler = (ipcMain) => {
); );
ipcMain.handle( ipcMain.handle(
`${methodBase}.plsLifecycleControl`, `${methodBase}.aikariLifecycleControl`,
/** /**
* *
* @param {*} _event * @param {*} _event
* @param {{ target: import("../../../types/shared/pls/status").PLSLifecycleControlType }} arg * @param {{ target: import("../../../types/shared/aikari/status").AikariLifecycleControlType }} arg
* @returns {Promise<{ success: boolean, errorObj?: Error }>} * @returns {Promise<{ success: boolean, errorObj?: Error }>}
*/ */
async (_event, arg) => { async (_event, arg) => {
const logHeader = "[HugoAura / IPC / PLS] <plsLifecycleControl>"; const logHeader = "[HugoAura / IPC / Aikari] <AikariLifecycleControl>";
// TODO: Impl this
switch (arg.target) { switch (arg.target) {
case "instSvc": case "instSvc":
return await functions.execCommand( return await functions.execCommand(
logHeader, logHeader,
PLS_BIN_PATH, AIKARI_LAUNCHER_PATH,
"--startup auto install" "--service install"
); );
case "rmSvc": { case "uninstSvc": {
const result = await functions.execCommand( const result = await functions.execCommand(
logHeader, logHeader,
PLS_BIN_PATH, AIKARI_LAUNCHER_PATH,
"remove" "--service uninstall"
); );
return result; return result;
} }
case "startSvc": case "startSvc":
return await functions.execCommand(logHeader, PLS_BIN_PATH, "start"); return await functions.execCommand(
logHeader,
"sc.exe",
`start ${AIKARI_SVC_NAME}`
);
case "stopSvc": { case "stopSvc": {
const result = await functions.execCommand( const result = await functions.execCommand(
logHeader, logHeader,
PLS_BIN_PATH, "sc.exe",
"stop" `stop ${AIKARI_SVC_NAME}`
); );
if (result.success && global.__HUGO_AURA__.plsStats) { if (result.success && global.__HUGO_AURA__.aikariStats) {
global.__HUGO_AURA__.plsStats.connected = false; global.__HUGO_AURA__.aikariStats.connected = false;
global.__HUGO_AURA__.plsStats.launched = false; global.__HUGO_AURA__.aikariStats.launched = false;
global.__HUGO_AURA__.plsStats.version = "unknown"; global.__HUGO_AURA__.aikariStats.version = "unknown";
global.__HUGO_AURA__.plsStats.status = "dead"; global.__HUGO_AURA__.aikariStats.status = "dead";
ipcMain.send( ipcMain.send(
"assistant", "assistant",
`${methodBase}.post.onPlsStatsUpdate`, `${methodBase}.post.onAikariStatsUpdate`,
global.__HUGO_AURA__.plsStats global.__HUGO_AURA__.aikariStats
); );
ipcMain.send( ipcMain.send(
"auraWsKeepAlive", "auraWsKeepAlive",
`${methodBase}.post.plsStopped`, `${methodBase}.post.aikariStopped`,
{} {}
); );
} }
return result; return result;
} }
case "rmBin": case "inst":
const unlinkPromise = new Promise((resolve) => { // TODO: Impl Aikari INST
fs.unlink(PLS_BIN_PATH, (error) => { case "uninst":
if (error) { return await functions.execCommand(
resolve({ logHeader,
success: false, AIKARI_UNINSTALLER_PATH,
errorObj: error, ""
}); );
console.error(
`${logHeader} Failed to remove PLS bin, error:`,
error
);
return false;
}
resolve({
success: true,
errorObj: null,
});
return true;
});
});
const unlinkRet = await unlinkPromise;
if (unlinkRet.success && global.__HUGO_AURA__.plsStats) {
global.__HUGO_AURA__.plsStats.connected = false;
global.__HUGO_AURA__.plsStats.launched = false;
global.__HUGO_AURA__.plsStats.installed = false;
global.__HUGO_AURA__.plsStats.version = "unknown";
ipcMain.send(
"assistant",
`${methodBase}.post.onPlsStatsUpdate`,
global.__HUGO_AURA__.plsStats
);
}
return unlinkRet;
default: default:
return { success: false, errorObj: new Error("Method not found") }; return { success: false, errorObj: new Error("Method not found") };
} }
@@ -572,32 +642,54 @@ const applyPlsIpcHandler = (ipcMain) => {
); );
ipcMain.handle( ipcMain.handle(
`${methodBase}.downloadPls`, `${methodBase}.downloadAndInstallAikari`,
/** /**
* *
* @param {import("electron").IpcMainInvokeEvent} _evt * @param {import("electron").IpcMainInvokeEvent} _evt
* @param {{channel?: "stable" | "alpha", reportTo?: import("../../../types/main/core").WindowName}} arg * @param {{channel?: "stable" | "alpha", reportTo?: import("../../../types/main/core").WindowName}} arg
* @returns {void} * @returns {Promise<void>}
*/ */
(_evt, arg) => { async (_evt, arg) => {
if (fs.existsSync(AIKARI_TEMP_DL_DIR)) {
try {
fs.unlinkSync(AIKARI_TEMP_DL_DIR);
} catch (err) {
console.error(err);
}
} else {
fs.mkdirSync(AIKARI_TEMP_DL_DIR);
}
if (fs.existsSync(OLD_PLS_INSTALL_DIR)) {
try {
execSync(`sc stop ${OLD_PLS_SVC_NAME}`);
execSync(`sc delete ${OLD_PLS_SVC_NAME}`);
} catch (err) {
// ...
}
try {
fs.unlinkSync(OLD_PLS_INSTALL_DIR);
} catch (err) {
// ...
}
}
const channel = arg.channel ? arg.channel : "stable"; const channel = arg.channel ? arg.channel : "stable";
const reportWin = arg.reportTo ? arg.reportTo : "assistant"; const reportWin = arg.reportTo ? arg.reportTo : "assistant";
functions.handlePLSDownload( functions.handleAikariDlAndInstall(
channel, channel,
(status) => { (status) => {
ipcMain.send( ipcMain.send(
reportWin, reportWin,
`${methodBase}.post.reportPlsDownloadStatus`, `${methodBase}.post.reportAikariInstallStep`,
status status
); );
}, },
PLS_BIN_PATH AIKARI_TEMP_INSTALLER_FILENAME
); );
} }
); );
ipcMain.handle( ipcMain.handle(
`${methodBase}.retryPlsConnect`, `${methodBase}.retryAikariConnect`,
/** /**
* *
* @param {import("electron").IpcMainInvokeEvent} _event * @param {import("electron").IpcMainInvokeEvent} _event
@@ -605,13 +697,17 @@ const applyPlsIpcHandler = (ipcMain) => {
* @returns {{ success: boolean, status: "Already" | "Retrying" }} * @returns {{ success: boolean, status: "Already" | "Retrying" }}
*/ */
(_event, arg) => { (_event, arg) => {
if (global.__HUGO_AURA__.plsStats?.connected) { if (global.__HUGO_AURA__.aikariStats?.connected) {
return { return {
success: true, success: true,
status: "Already", status: "Already",
}; };
} else { } else {
ipcMain.send("auraWsKeepAlive", `${methodBase}.retryPlsConnect`, arg); ipcMain.send(
"auraWsKeepAlive",
`${methodBase}.retryAikariConnect`,
arg
);
return { return {
success: true, success: true,
@@ -621,13 +717,31 @@ const applyPlsIpcHandler = (ipcMain) => {
} }
); );
ipcMain.handle(
`${methodBase}.forceReloadKeepAliveWin`,
/**
*
* @param {import("electron").IpcMainInvokeEvent} _event
* @param {any} arg
*/
(_event, arg) => {
ipcMain.send(
"auraWsKeepAlive",
`${methodBase}.post.onForceReloadRequested`,
arg
);
return { success: true };
}
);
ipcMain.handle( ipcMain.handle(
`${methodBase}.post.updateRetryStatus`, `${methodBase}.post.updateRetryStatus`,
/** /**
* *
* @param {import("electron").IpcMainInvokeEvent} _event * @param {import("electron").IpcMainInvokeEvent} _event
* @param {{ success: boolean }} arg * @param {{ success: boolean; message: string }} arg
*/ */
(_event, arg) => { (_event, arg) => {
ipcMain.send("assistant", `${methodBase}.post.updateRetryStatus`, arg); ipcMain.send("assistant", `${methodBase}.post.updateRetryStatus`, arg);
@@ -639,4 +753,4 @@ const applyPlsIpcHandler = (ipcMain) => {
); );
}; };
module.exports = { applyPlsIpcHandler }; module.exports = { applyAikariIpcHandler };

View File

@@ -3,14 +3,14 @@ interface HugoAuraGlobal {
} }
interface AssistantHugoAuraGlobal extends HugoAuraGlobal { interface AssistantHugoAuraGlobal extends HugoAuraGlobal {
plsStatus: PLSStatus; plsStatus: AikariStatus;
plsRules: Record<any, any>; plsRules: Record<any, any>;
plsSettings: Record<any, any>; plsSettings: Record<any, any>;
} }
interface AuraWSKeepAliveWindowHugoAuraGlobal extends HugoAuraGlobal { interface AuraWSKeepAliveWindowHugoAuraGlobal extends HugoAuraGlobal {
plsWs: WebSocket | null; plsWs: WebSocket | null;
plsStats: PLSStatus; plsStats: AikariStatus;
} }
type UIFunctionsObject = Record<string, any>; type UIFunctionsObject = Record<string, any>;

View File

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

View File

@@ -1,17 +1,18 @@
interface ClientPLSRequest { interface ClientAikariRequest {
method: string; method: string;
data: Record<any, any>; data: Record<any, any>;
eventId: string; eventId: string;
module: string;
} }
interface PLSResponse { interface AikariResponse {
success: boolean; success: boolean;
code: number; code: number;
data: Record<any, any>; data: Record<any, any>;
eventId: string; eventId: string;
} }
interface PLSPush { interface AikariPush {
success: boolean; success: boolean;
type: string; type: string;
data: Record<any, any>; data: Record<any, any>;

View File

@@ -3,7 +3,7 @@ import type EventBus from "../../utils/eventBus";
import { HookedWindowsMap, UIHooksMap, WindowHooksMap } from "../main/core"; import { HookedWindowsMap, UIHooksMap, WindowHooksMap } from "../main/core";
import { UIHooksObject } from "../render/uiHook"; import { UIHooksObject } from "../render/uiHook";
import ConfigManager from "../../init/shared/configManager"; import ConfigManager from "../../init/shared/configManager";
import { PLSStatus } from "./pls/status"; import { AikariStatus } from "./aikari/status";
type MainProcessOnlyVal<T> = T; type MainProcessOnlyVal<T> = T;
type RendererProcessOnlyVal<T> = T; type RendererProcessOnlyVal<T> = T;
@@ -15,10 +15,10 @@ interface GlobalHugoAuraInfo {
hookedWindows?: MainProcessOnlyVal<HookedWindowsMap>; hookedWindows?: MainProcessOnlyVal<HookedWindowsMap>;
ipcInit?: MainProcessOnlyVal<boolean>; ipcInit?: MainProcessOnlyVal<boolean>;
auraDir: MainProcessOnlyVal<string>; auraDir: MainProcessOnlyVal<string>;
plsRules?: Record<any, any> | null; aikariRules?: Record<any, any> | null;
plsSettings?: Record<any, any> | null; aikariSettings?: Record<any, any> | null;
plsStats?: PLSStatus | null; aikariStats?: AikariStatus | null;
plsWs?: RendererProcessOnlyVal<WebSocket>; aikariWs?: RendererProcessOnlyVal<WebSocket>;
uiHooks?: MainProcessOnlyVal<UIHooksMap>; uiHooks?: MainProcessOnlyVal<UIHooksMap>;
windowHooks?: MainProcessOnlyVal<WindowHooksMap>; windowHooks?: MainProcessOnlyVal<WindowHooksMap>;
version: RendererProcessOnlyVal<string>; version: RendererProcessOnlyVal<string>;
@@ -26,7 +26,7 @@ interface GlobalHugoAuraInfo {
interface GlobalHugoAuraApiInfo { interface GlobalHugoAuraApiInfo {
domains: string[]; domains: string[];
plsUpdate: string; aikariUpdate: string;
auraUpdate: string; auraUpdate: string;
} }

View File

@@ -0,0 +1,129 @@
// @ts-check
const { genRandomHex } = require("../../utils/crypto");
const IPC_METHOD_BASE = "$aura.aikari";
/** @type {Map<string, any>} */
const wsGetCallbacks = new Map();
const actions = {
getAikariVersion: async (originalAikariStates, wsObj) => {
const eventId = genRandomHex();
wsObj.send(
JSON.stringify({
module: "launcher",
eventId: eventId,
method: "basic.props.getVersion",
data: {},
})
);
const promise = new Promise((resolve) => {
wsGetCallbacks.set(eventId, resolve);
});
const data = await promise;
if (data.success) {
originalAikariStates.version = data.data.version;
console.debug(
"[HugoAura / UI / Aikari OCMS] Updated Aikari version info: " +
data.data.version
);
}
},
getAikariLauncherConfig: async (wsObj) => {
const eventId = genRandomHex();
wsObj.send(
JSON.stringify({
module: "launcher",
eventId,
method: "config.actions.getConfig",
data: {},
})
);
const promiseForConfig = new Promise((resolve) => {
wsGetCallbacks.set(eventId, resolve);
});
const data = await promiseForConfig;
if (data.success) {
console.debug(
"[HugoAura / UI / Aikari OCMS] Received Aikari launcher config: ",
data
);
} else {
return null;
}
wsObj.send(
JSON.stringify({
module: "launcher",
eventId,
method: "config.actions.getTelemetryIsEnabled",
data: {},
})
);
const promiseForTelemetry = new Promise((resolve) => {
wsGetCallbacks.set(eventId, resolve);
});
const telemetryConfig = await promiseForTelemetry;
if (telemetryConfig.success) {
data.data.telemetryEnabled = telemetryConfig.data.isEnabled;
} else {
data.data.telemetryEnabled = false;
}
return data.data;
},
getAikariPLSRules: async (wsObj) => {
const eventId = genRandomHex();
wsObj.send(
JSON.stringify({
module: "pls",
eventId,
method: "config.rules.getConfig",
})
);
const promise = new Promise((resolve) => {
wsGetCallbacks.set(eventId, resolve);
});
const data = await promise;
if (data.success) {
console.debug(
"[HugoAura / UI / Aikari OCMS] Received Aikari PLS rules: ",
data
);
return data.data;
} else {
return null;
}
},
};
const onAikariConnectedMsgSeq = async ({ curAikariStates, wsObj }) => {
const updatedAikariStates = { ...curAikariStates };
const onMsgRecvListener = (data) => {
if (wsGetCallbacks.has(data.detail.eventId)) {
wsGetCallbacks.get(data.detail.eventId)(data.detail);
}
};
document.addEventListener("onAikariMessageRecv", onMsgRecvListener);
// Get Aikari Version
await actions.getAikariVersion(updatedAikariStates, wsObj);
// Get Aikari Launcher Config
const aikariLauncherConfig = await actions.getAikariLauncherConfig(wsObj);
if (aikariLauncherConfig) {
global.ipcRenderer.invoke(
`${IPC_METHOD_BASE}.updateAikariSettings`,
aikariLauncherConfig
);
}
// Get Aikari PLS Rules
const aikariPLSRules = await actions.getAikariPLSRules(wsObj);
if (aikariPLSRules) {
global.ipcRenderer.invoke(
`${IPC_METHOD_BASE}.updateAikariRules`,
aikariPLSRules
);
}
return updatedAikariStates;
};
module.exports = { onAikariConnectedMsgSeq };

View File

@@ -7,7 +7,7 @@ const { configRouteHandler } = require(`${REQUIRE_BASE}/routes/config`);
/** /**
* *
* @param {PLSPush} parsedWsMsg * @param {AikariPush} parsedWsMsg
* @returns * @returns
*/ */
const pushMsgHandler = (parsedWsMsg) => { const pushMsgHandler = (parsedWsMsg) => {

View File

@@ -0,0 +1,49 @@
// @ts-check
const IPC_METHOD_BASE = "$aura.aikari";
// TODO: REFACTOR
/**
*
* @param {AikariPush} parsedWsMsg
* @returns
*/
const basicRouteHandler = (parsedWsMsg) => {
const target = parsedWsMsg.type.split(".").slice(-1)[0];
switch (target) {
case "pushPlsInfo":
if (global.__HUGO_AURA__.aikariStats) {
global.__HUGO_AURA__.aikariStats.status = parsedWsMsg.data.status;
global.__HUGO_AURA__.aikariStats.version = parsedWsMsg.data.version;
}
global.ipcRenderer.invoke(
`${IPC_METHOD_BASE}.updateAikariStatus`,
global.__HUGO_AURA__.aikariStats
);
console.debug(
"[HugoAura / UI / Aikari Routes / DEBUG] Updated aikariStats basic info:",
global.__HUGO_AURA__.aikariStats
);
break;
case "plsNotReadyError":
if (global.__HUGO_AURA__.aikariStats) {
global.__HUGO_AURA__.aikariStats.launched = true;
global.__HUGO_AURA__.aikariStats.connected = false;
global.__HUGO_AURA__.aikariStats.status = "notReady";
}
global.ipcRenderer.invoke(
`${IPC_METHOD_BASE}.updateAikariStatus`,
global.__HUGO_AURA__.aikariStats
);
break;
default:
return false;
}
return true;
};
module.exports = { basicRouteHandler };

View File

@@ -1,10 +1,10 @@
// @ts-check // @ts-check
const IPC_METHOD_BASE = "$aura.pls"; const IPC_METHOD_BASE = "$aura.aikari";
/** /**
* *
* @param {PLSPush} parsedWsMsg * @param {AikariPush} parsedWsMsg
* @returns * @returns
*/ */
const configRouteHandler = (parsedWsMsg) => { const configRouteHandler = (parsedWsMsg) => {
@@ -12,13 +12,13 @@ const configRouteHandler = (parsedWsMsg) => {
switch (target) { switch (target) {
case "pushBasicConfig": case "pushBasicConfig":
global.ipcRenderer.invoke( global.ipcRenderer.invoke(
`${IPC_METHOD_BASE}.updatePlsSettings`, `${IPC_METHOD_BASE}.updateAikariSettings`,
parsedWsMsg.data parsedWsMsg.data
); );
break; break;
case "pushRuleSettings": case "pushRuleSettings":
global.ipcRenderer.invoke( global.ipcRenderer.invoke(
`${IPC_METHOD_BASE}.updatePlsRules`, `${IPC_METHOD_BASE}.updateAikariRules`,
parsedWsMsg.data parsedWsMsg.data
); );
break; break;

View File

@@ -0,0 +1,177 @@
// @ts-check
const __SCOPE = "assistant / rendererCommon";
const IPC_METHOD_BASE = "$aura.aikari";
const updateAikariStatusFromLocal = async () => {
const aikariStatus = (
await global.ipcRenderer.invoke(`${IPC_METHOD_BASE}.getAikariStatus`)
).data;
global.__HUGO_AURA__.aikariStats = aikariStatus;
return aikariStatus;
};
const updateAikariSettingsFromLocal = async () => {
const aikariSettings = (
await global.ipcRenderer.invoke(`${IPC_METHOD_BASE}.getAikariSettings`)
).data;
global.__HUGO_AURA__.aikariSettings = aikariSettings;
return aikariSettings;
};
const updateAikariRulesFromLocal = async () => {
const aikariRules = (
await global.ipcRenderer.invoke(`${IPC_METHOD_BASE}.getAikariRules`)
).data;
global.__HUGO_AURA__.aikariRules = aikariRules;
return aikariRules;
};
const { genRandomHex } = require("../../utils/crypto");
/**
*
* @param {string} configKey
* @param {any} configValue
*/
const updateAikariConfigToRemote = async (
configKey,
configValue,
module = "launcher",
writeToDisk = true
) => {
const configLevels = configKey.split(".");
const aikariConfigUpdateEvent = new CustomEvent("onAikariConfigUpdate", {
detail: {
path: configLevels,
value: configValue,
},
});
document.dispatchEvent(aikariConfigUpdateEvent);
const settingsEntries = document.getElementsByClassName(
"aura-settings-entry"
);
if (settingsEntries.length > 0) {
Array.from(settingsEntries).forEach((entry) => {
entry.dispatchEvent(aikariConfigUpdateEvent);
});
}
/**
* @type {ClientAikariRequest}
*/
const data = {
method: "config.actions.updateConfig",
data: {
key: configKey,
value: configValue,
write: writeToDisk,
},
eventId: genRandomHex(), // 不用 crypto, 因为会带来不必要的性能开销
module: module,
};
global.ipcRenderer.invoke(`${IPC_METHOD_BASE}.ws.sendWsMessage`, data);
global.ipcRenderer.invoke(`${IPC_METHOD_BASE}.syncAikariConfig`, {
basic: global.__HUGO_AURA__.aikariSettings,
rules: global.__HUGO_AURA__.aikariRules,
});
};
/**
*
* @param {boolean} newValue
*/
const updateAikariTelemetryConfigToRemote = async (newValue) => {
const aikariConfigUpdateEvent = new CustomEvent("onAikariConfigUpdate", {
detail: {
path: ["telemetry"],
value: newValue,
},
});
document.dispatchEvent(aikariConfigUpdateEvent);
const settingsEntries = document.getElementsByClassName(
"aura-settings-entry"
);
if (settingsEntries.length > 0) {
Array.from(settingsEntries).forEach((entry) => {
entry.dispatchEvent(aikariConfigUpdateEvent);
});
}
/**
* @type {ClientAikariRequest}
*/
const data = {
method: "config.actions.setTelemetryIsEnabled",
data: {
isEnabled: newValue,
},
eventId: genRandomHex(),
module: "launcher",
};
global.ipcRenderer.invoke(`${IPC_METHOD_BASE}.ws.sendWsMessage`, data);
global.ipcRenderer.invoke(`${IPC_METHOD_BASE}.syncAikariConfig`, {
basic: global.__HUGO_AURA__.aikariSettings,
rules: global.__HUGO_AURA__.aikariRules,
});
};
// [!] Will be deprecated
const updateAikariPLSRulesToRemote = async (
configKey,
configValue,
affiliated,
writeToDisk = true
) => {
const configLevels = configKey.split(".");
const aikariRuleConfigUpdateEvent = new CustomEvent("onAikariConfigUpdate", {
detail: {
path: configLevels,
value: configValue,
},
});
document.dispatchEvent(aikariRuleConfigUpdateEvent);
const settingsEntries = document.getElementsByClassName(
"aura-settings-entry"
);
if (settingsEntries.length > 0) {
Array.from(settingsEntries).forEach((entry) => {
entry.dispatchEvent(aikariRuleConfigUpdateEvent);
});
}
/**
* @type {ClientAikariRequest}
*/
const data = {
method: "config.rules.updateConfig",
data: {
key: configKey,
value: configValue,
write: writeToDisk,
affiliated,
},
eventId: genRandomHex(),
module: "pls",
};
global.ipcRenderer.invoke(`${IPC_METHOD_BASE}.ws.sendWsMessage`, data);
global.ipcRenderer.invoke(`${IPC_METHOD_BASE}.syncAikariConfig`, {
basic: global.__HUGO_AURA__.aikariSettings,
rules: global.__HUGO_AURA__.aikariRules,
});
};
module.exports = {
updateAikariRulesFromLocal,
updateAikariStatusFromLocal,
updateAikariSettingsFromLocal,
updateAikariTelemetryConfigToRemote,
updateAikariConfigToRemote,
updateAikariPLSRulesToRemote,
};

View File

@@ -1,81 +0,0 @@
// @ts-check
const __SCOPE = "assistant / rendererCommon";
const IPC_METHOD_BASE = "$aura.pls";
const updatePlsStatusFromLocal = async () => {
const plsStatus = (
await global.ipcRenderer.invoke(`${IPC_METHOD_BASE}.getPlsStats`)
).data;
global.__HUGO_AURA__.plsStats = plsStatus;
return plsStatus;
};
const updatePlsSettingsFromLocal = async () => {
const plsSettings = (
await global.ipcRenderer.invoke(`${IPC_METHOD_BASE}.getPlsSettings`)
).data;
global.__HUGO_AURA__.plsSettings = plsSettings;
return plsSettings;
};
const updatePlsRulesFromLocal = async () => {
const plsRules = (
await global.ipcRenderer.invoke(`${IPC_METHOD_BASE}.getPlsRules`)
).data;
global.__HUGO_AURA__.plsRules = plsRules;
return plsRules;
};
const { genRandomHex } = require("../../utils/crypto");
/**
*
* @param {string} configKey
* @param {any} configValue
*/
const updatePlsConfigToRemote = async (configKey, configValue) => {
const configLevels = configKey.split(".");
const plsConfigUpdateEvent = new CustomEvent("onPLSConfigUpdate", {
detail: {
path: configLevels,
value: configValue,
},
});
document.dispatchEvent(plsConfigUpdateEvent);
const settingsEntries = document.getElementsByClassName(
"aura-settings-entry"
);
if (settingsEntries.length > 0) {
Array.from(settingsEntries).forEach((entry) => {
entry.dispatchEvent(plsConfigUpdateEvent);
});
}
/**
* @type {ClientPLSRequest}
*/
const data = {
method: "config.action.updateConfig",
data: {
key: configKey,
value: configValue,
},
eventId: genRandomHex(), // 不用 crypto, 因为会带来不必要的性能开销
};
global.ipcRenderer.invoke(`${IPC_METHOD_BASE}.ws.sendWsMessage`, data);
global.ipcRenderer.invoke(`${IPC_METHOD_BASE}.syncPlsConfig`, {
basic: global.__HUGO_AURA__.plsSettings,
rules: global.__HUGO_AURA__.plsRules,
});
};
module.exports = {
updatePlsRulesFromLocal,
updatePlsStatusFromLocal,
updatePlsSettingsFromLocal,
updatePlsConfigToRemote,
};

View File

@@ -0,0 +1,34 @@
const childProc = require("child_process");
const appRawCmds = {
checkVcRedistInst: async () => {
let deviceArch = process.env.PROCESSOR_ARCHITEW6432
? process.env.PROCESSOR_ARCHITEW6432
: process.env.PROCESSOR_ARCHITECTURE;
// @ts-expect-error
deviceArch = deviceArch.toLowerCase();
if (deviceArch === "amd64") {
deviceArch = "x64";
} else if (deviceArch === "arm64") {
// do nothing
}
const waitForCmd = new Promise((resolve) => {
childProc.exec(
`reg query "HKLM\\SOFTWARE\\Microsoft\\VisualStudio\\14.0\\VC\\Runtimes\\${deviceArch}" /v Installed`,
(error, stdout, stderr) => {
if (error) {
console.warn(
`[HugoAura / UI / Composables / Raw CMD / APP] Detected VS Redist not installed: ${stderr}`
);
resolve({ installed: false, arch: deviceArch });
} else {
resolve({ installed: true, arch: deviceArch });
}
}
);
});
return waitForCmd;
},
};
module.exports = appRawCmds;

View File

@@ -0,0 +1,28 @@
const childProc = require("child_process");
const fileSystemRawCmds = {
getDiskCaptions: async () => {
const waitForCmd = new Promise((resolve) => {
childProc.exec("fsutil fsinfo drives", (error, stdout, stderr) => {
if (error) {
console.error(
`[HugoAura / UI / Composables / Raw CMD / FS] Failed to exec fsutil getCaption: ${error}`
);
resolve([]);
}
const finResult = [];
stdout
.trim()
.split(": ")[1]
.split(":\\")
.forEach((line) => {
if (line !== "") finResult.push(line.trim() + ":");
});
resolve(finResult);
});
});
return waitForCmd;
},
};
module.exports = fileSystemRawCmds;

View File

@@ -18,12 +18,12 @@ const showRelaunchToast = () => {
if (!toastBs.isShown()) toastBs.show(); if (!toastBs.isShown()) toastBs.show();
}; };
const showRelaunchPLSToast = () => { const showRelaunchAikariToast = () => {
const toast = document.getElementById("relaunchPlsNotifyToast"); const toast = document.getElementById("relaunchAikariNotifyToast");
const toastBs = bootstrap.Toast.getOrCreateInstance(toast); const toastBs = bootstrap.Toast.getOrCreateInstance(toast);
if (global.__HUGO_AURA__.plsStats.detached) { if (global.__HUGO_AURA__.aikariStats.detached) {
const relaunchBtn = document.getElementById("plsRelaunchBtn"); const relaunchBtn = document.getElementById("aikariRelaunchBtn");
relaunchBtn.disabled = true; relaunchBtn.disabled = true;
relaunchBtn.textContent = "分离模式下无法执行"; relaunchBtn.textContent = "分离模式下无法执行";
} }
@@ -37,12 +37,6 @@ const showToast = (entry) => {
} else if (entry.restart) { } else if (entry.restart) {
showRelaunchToast(); showRelaunchToast();
} }
/*
else if (entry.restartPLS) {
showRelaunchPLSToast();
}
*/
}; };
const setDisableStatus = (el, isDisable, hint = null) => { const setDisableStatus = (el, isDisable, hint = null) => {
@@ -56,9 +50,9 @@ const setDisableStatus = (el, isDisable, hint = null) => {
tooltipIns.enable(); tooltipIns.enable();
} }
} else { } else {
el.setAttribute("data-bs-toggle", "tooltip"); el.removeAttribute("data-bs-toggle");
el.setAttribute("data-bs-placement", "top"); el.removeAttribute("data-bs-placement");
el.setAttribute("data-bs-title", "None"); el.removeAttribute("data-bs-title");
el.classList.remove("ase-operation-area-disabled"); el.classList.remove("ase-operation-area-disabled");
const tooltipIns = bootstrap.Tooltip.getInstance(el); const tooltipIns = bootstrap.Tooltip.getInstance(el);
if (tooltipIns) { if (tooltipIns) {
@@ -106,7 +100,18 @@ const renderInputArea = (entry, operationArea, descriptionArea) => {
case "radio": { case "radio": {
const elValue = entry.valueGetter(); const elValue = entry.valueGetter();
const elArr = []; const elArr = [];
for (const template of entry.templates) { let targetTemplatesArr = [];
let targetTemplateLablesArr = [];
targetTemplatesArr =
typeof entry.templates === "function"
? entry.templates()
: entry.templates;
targetTemplateLablesArr =
typeof entry.templateLabels === "function"
? entry.templateLabels()
: entry.templateLabels;
for (const template of targetTemplatesArr) {
const inlineContainerEl = document.createElement("div"); const inlineContainerEl = document.createElement("div");
inlineContainerEl.classList.add("form-check", "form-check-inline"); inlineContainerEl.classList.add("form-check", "form-check-inline");
const radioEl = document.createElement("input"); const radioEl = document.createElement("input");
@@ -114,7 +119,7 @@ const renderInputArea = (entry, operationArea, descriptionArea) => {
radioEl.classList.add("form-check-input"); radioEl.classList.add("form-check-input");
radioEl.type = "radio"; radioEl.type = "radio";
radioEl.name = `${entry.id}Radios`; radioEl.name = `${entry.id}Radios`;
radioEl.id = `${entry.id}Radio${entry.templates.indexOf(template)}`; radioEl.id = `${entry.id}Radio${targetTemplatesArr.indexOf(template)}`;
radioEl.checked = template === elValue ? true : false; radioEl.checked = template === elValue ? true : false;
radioEl.addEventListener("change", async (event) => { radioEl.addEventListener("change", async (event) => {
if (event.target.checked) { if (event.target.checked) {
@@ -132,7 +137,53 @@ const renderInputArea = (entry, operationArea, descriptionArea) => {
labelEl.classList.add("form-check-label"); labelEl.classList.add("form-check-label");
labelEl.setAttribute("for", radioEl.id); labelEl.setAttribute("for", radioEl.id);
labelEl.textContent = labelEl.textContent =
entry.templateLabels[entry.templates.indexOf(template)]; targetTemplateLablesArr[targetTemplatesArr.indexOf(template)];
inlineContainerEl.appendChild(labelEl);
elArr.push(inlineContainerEl);
}
return elArr;
}
case "checkbox": {
const elValue = entry.valueGetter();
const elArr = [];
let targetTemplatesArr = [];
let targetTemplateLablesArr = [];
targetTemplatesArr =
typeof entry.templates === "function"
? entry.templates()
: entry.templates;
targetTemplateLablesArr =
typeof entry.templateLabels === "function"
? entry.templateLabels()
: entry.templateLabels;
for (const templateName of targetTemplatesArr) {
const inlineContainerEl = document.createElement("div");
inlineContainerEl.classList.add("form-check", "form-check-inline");
const chkBoxEl = document.createElement("input");
chkBoxEl.value = templateName;
chkBoxEl.classList.add("form-check-input");
chkBoxEl.type = "checkbox";
chkBoxEl.name = `${entry.id}Checkbox`;
chkBoxEl.id = `${entry.id}Checkbox${targetTemplatesArr.indexOf(
templateName
)}`;
chkBoxEl.checked = elValue.includes(templateName) ? true : false;
chkBoxEl.addEventListener("change", async (event) => {
showToast(entry);
await entry.callbackFn(
event.target.value,
chkBoxEl,
operationArea,
descriptionArea
);
});
inlineContainerEl.appendChild(chkBoxEl);
const labelEl = document.createElement("label");
labelEl.classList.add("form-check-label");
labelEl.setAttribute("for", chkBoxEl.id);
labelEl.textContent =
targetTemplateLablesArr[targetTemplatesArr.indexOf(templateName)];
inlineContainerEl.appendChild(labelEl); inlineContainerEl.appendChild(labelEl);
elArr.push(inlineContainerEl); elArr.push(inlineContainerEl);
} }
@@ -225,29 +276,29 @@ const renderNormalSettingsItem = (entry, formEl) => {
powerIcon.setAttribute("data-bs-title", "需要重启 Electron 进程"); powerIcon.setAttribute("data-bs-title", "需要重启 Electron 进程");
entryTitle.appendChild(powerIcon); entryTitle.appendChild(powerIcon);
} }
if (entry.PLSRequired) { if (entry.aikariRequired) {
const plsIcon = document.createElement("i"); const aikariIcon = document.createElement("i");
plsIcon.classList.add( aikariIcon.classList.add(
"layui-icon", "layui-icon",
"layui-icon-component", "layui-icon-component",
"aura-settings-entry-property-icon" "aura-settings-entry-property-icon"
); );
plsIcon.setAttribute("data-bs-toggle", "tooltip"); aikariIcon.setAttribute("data-bs-toggle", "tooltip");
plsIcon.setAttribute("data-bs-placement", "top"); aikariIcon.setAttribute("data-bs-placement", "top");
plsIcon.setAttribute("data-bs-title", "需要 PLS 支持"); aikariIcon.setAttribute("data-bs-title", "需要 Aikari 支持");
entryTitle.appendChild(plsIcon); entryTitle.appendChild(aikariIcon);
} }
if (entry.restartPLS) { if (entry.restartAikari) {
const plsIcon = document.createElement("i"); const aikariIcon = document.createElement("i");
plsIcon.classList.add( aikariIcon.classList.add(
"layui-icon", "layui-icon",
"layui-icon-logout", "layui-icon-logout",
"aura-settings-entry-property-icon" "aura-settings-entry-property-icon"
); );
plsIcon.setAttribute("data-bs-toggle", "tooltip"); aikariIcon.setAttribute("data-bs-toggle", "tooltip");
plsIcon.setAttribute("data-bs-placement", "top"); aikariIcon.setAttribute("data-bs-placement", "top");
plsIcon.setAttribute("data-bs-title", "需要重启 PLS 进程"); aikariIcon.setAttribute("data-bs-title", "需要重启 Aikari 进程");
entryTitle.appendChild(plsIcon); entryTitle.appendChild(aikariIcon);
} }
if (entry.reload) { if (entry.reload) {
const reloadIcon = document.createElement("i"); const reloadIcon = document.createElement("i");
@@ -308,33 +359,14 @@ const renderNormalSettingsItem = (entry, formEl) => {
insertOrRemoveEl(entryOperationArea, targetEl, true); insertOrRemoveEl(entryOperationArea, targetEl, true);
} }
}; };
const channel = entry.PLSRequired const channel = entry.aikariRequired
? "onPLSConfigUpdate" ? "onAikariConfigUpdate"
: "onHugoAuraConfigUpdate"; : "onHugoAuraConfigUpdate";
entryContainerEl.addEventListener(channel, evtListener); entryContainerEl.addEventListener(channel, evtListener);
// createOnLeaveEvtListener(channel, evtListener); // createOnLeaveEvtListener(channel, evtListener);
} }
if (entry.PLSRequired) { const updateDisableStatus = (entry) => {
if (!global.__HUGO_AURA__.plsStats.connected) {
setDisableStatus(entryOperationArea, true, "连接至 PLS 以继续");
}
const evtListener = (event) => {
if (event.detail.connected) {
setDisableStatus(entryOperationArea, false);
} else {
setDisableStatus(entryOperationArea, true, "连接至 PLS 以继续");
}
};
entryContainerEl.addEventListener("onPLSStatsUpdate", evtListener);
// createOnLeaveEvtListener("onPLSStatsUpdate", evtListener);
}
entryContainerEl.appendChild(entryOperationArea);
const isShow = entry.auraIf();
if (!isShow) entryContainerEl.classList.add("aura-settings-entry-hidden");
const updateDisableStatus = () => {
const isDisabledRet = entry.auraDisable(); const isDisabledRet = entry.auraDisable();
setDisableStatus( setDisableStatus(
entryOperationArea, entryOperationArea,
@@ -344,9 +376,32 @@ const renderNormalSettingsItem = (entry, formEl) => {
}; };
if (entry.auraDisable) { if (entry.auraDisable) {
updateDisableStatus(); updateDisableStatus(entry);
} }
if (entry.aikariRequired) {
if (!global.__HUGO_AURA__.aikariStats.connected && !entry.alwaysEnable) {
setDisableStatus(entryOperationArea, true, "连接至 Aikari 以继续");
}
const evtListener = (event) => {
if (event.detail.connected) {
try {
updateDisableStatus(entry);
} catch {
setDisableStatus(entryOperationArea, false);
}
} else {
setDisableStatus(entryOperationArea, true, "连接至 Aikari 以继续");
}
};
entryContainerEl.addEventListener("onAikariStatsUpdate", evtListener);
// createOnLeaveEvtListener("onAikariStatsUpdate", evtListener);
}
entryContainerEl.appendChild(entryOperationArea);
const isShow = entry.auraIf();
if (!isShow) entryContainerEl.classList.add("aura-settings-entry-hidden");
if (entry.associateVal) { if (entry.associateVal) {
const evtListener = (event) => { const evtListener = (event) => {
if (!entry.associateVal.includes(event.detail.path.join("."))) return; if (!entry.associateVal.includes(event.detail.path.join("."))) return;
@@ -357,11 +412,11 @@ const renderNormalSettingsItem = (entry, formEl) => {
: cls.add("aura-settings-entry-hidden"); : cls.add("aura-settings-entry-hidden");
if (entry.auraDisable) { if (entry.auraDisable) {
updateDisableStatus(); updateDisableStatus(entry);
} }
}; };
const channel = entry.PLSRequired const channel = entry.aikariRequired
? "onPLSConfigUpdate" ? "onAikariConfigUpdate"
: "onHugoAuraConfigUpdate"; : "onHugoAuraConfigUpdate";
entryContainerEl.addEventListener(channel, evtListener); entryContainerEl.addEventListener(channel, evtListener);
// createOnLeaveEvtListener(channel, evtListener); // createOnLeaveEvtListener(channel, evtListener);
@@ -403,7 +458,9 @@ const renderPreviewItem = (entry, formEl) => {
}; };
document.addEventListener( document.addEventListener(
eventChannel === "pls" ? "onPLSConfigUpdate" : "onHugoAuraConfigUpdate", eventChannel === "aikari"
? "onAikariConfigUpdate"
: "onHugoAuraConfigUpdate",
eventListener eventListener
); );
createOnLeaveEvtListener(eventListener); // Clean up createOnLeaveEvtListener(eventListener); // Clean up

View File

@@ -41,13 +41,13 @@ const def = {
selectorMode: "appendChild", selectorMode: "appendChild",
pageCSS: "ui/pages/configSubPages/behaviourCtrl/behaviourCtrl.css", pageCSS: "ui/pages/configSubPages/behaviourCtrl/behaviourCtrl.css",
childs: { childs: {
PlsStatus: { AikariStatus: {
active: false, active: false,
pageURI: "ui/pages/configSubPages/behaviourCtrl/plsStatus.html", pageURI: "ui/pages/configSubPages/behaviourCtrl/aikariStatus.html",
pageScript: "ui/pages/configSubPages/behaviourCtrl/plsStatus.js", pageScript: "ui/pages/configSubPages/behaviourCtrl/aikariStatus.js",
pageSelector: "#status-subpage", pageSelector: "#status-subpage",
selectorMode: "appendChild", selectorMode: "appendChild",
pageCSS: "ui/pages/configSubPages/behaviourCtrl/plsStatus.css", pageCSS: "ui/pages/configSubPages/behaviourCtrl/aikariStatus.css",
}, },
DeviceSecurity: { DeviceSecurity: {
childs: { childs: {
@@ -86,7 +86,7 @@ const def = {
], ],
globalJS: [ globalJS: [
"ui/js/global.js", "ui/js/global.js",
"ui/js/plsListener.js", "ui/js/aikariListener.js",
"ui/bootstrap/bootstrap.bundle.min.js", "ui/bootstrap/bootstrap.bundle.min.js",
], ],
onLoaded: ` onLoaded: `

View File

@@ -0,0 +1,386 @@
// @ts-check
(() => {
if (!global.__HUGO_AURA__)
global.__HUGO_AURA__ = {
configInit: true,
auraDir: "",
version: "",
};
if (!global.__HUGO_AURA__.aikariStats)
global.__HUGO_AURA__.aikariStats = {
installed: false,
detached: false,
connected: false,
launched: false,
status: "dead",
version: "unknown",
authToken: "",
};
const IPC_METHOD_BASE = "$aura.aikari";
const REQUIRE_BASE = "../../..";
const __SCOPE = "auraWsKeepAlive";
const AIKARI_RPC_CONFIG_REG_PATH = "Aikari\\RPC";
const { pushMsgHandler } = require(`${REQUIRE_BASE}/aikari/pushHandler`);
const {
onAikariConnectedMsgSeq,
} = require(`${REQUIRE_BASE}/aikari/onConnectedSeq`);
const RegistryManager = require(`${REQUIRE_BASE}/../init/shared/registryManager`);
const registryManager = new RegistryManager();
/** @type {number} */
let failedCounter = 0;
/** @type {boolean} */
let isErrorOccurred = false;
/** @type {number} */
let aikariPort = 22077;
/** @type {"wss" | "ws"} */
let aikariProtocol = "wss";
/** @type {boolean} */
let isRetrying = false;
/** @type {any} */
let curSendListener = null;
let curSendGetListener = null;
const sendRetryStatusToMain = (
/** @type {Boolean} */ status,
/** @type {string?} */ message = null
) => {
global.ipcRenderer.invoke(`${IPC_METHOD_BASE}.post.updateRetryStatus`, {
success: status,
message: message,
});
};
const clearReqSendIpcListener = () => {
if (curSendListener) {
global.ipcRenderer.off(
`${IPC_METHOD_BASE}.ws.post.onReqSendMsg`,
curSendListener
);
curSendListener = null;
}
};
const clearSendGetIpcListener = () => {
if (curSendGetListener) {
global.ipcRenderer.off(
`${IPC_METHOD_BASE}.ws.post.onSendGetMsg`,
curSendListener
);
curSendGetListener = null;
}
};
const startConnAikariProc = async (updatedAikariStats) => {
const authTokenRet = await registryManager.readRegKey(
AIKARI_RPC_CONFIG_REG_PATH,
"authToken",
true
);
if (authTokenRet.success) {
updatedAikariStats.authToken = authTokenRet.data;
// @ts-expect-error
global.__HUGO_AURA__.aikariStats.authToken = authTokenRet.data;
} else {
sendRetryStatusToMain(false, "E_AUTH_TOKEN_GET_FAILED");
return;
}
const portRet = await registryManager.readRegKey(
AIKARI_RPC_CONFIG_REG_PATH,
"wsPort",
true
);
if (portRet.success) {
try {
aikariPort = Number(portRet.data);
} catch {
console.warn(
`[HugoAura / UI / Aikari Conn Manager] Invalid Aikari port: ${portRet.data}`
);
}
}
// TODO: wsHost
createAikariConnection(
updatedAikariStats.authToken,
connectionResultCallback
);
};
/**
*
* @param {string} authToken
* @param {any} callback
* @returns
*/
const createAikariConnection = (authToken, callback) => {
if (failedCounter >= 3) {
console.error(
`[HugoAura / UI / Aikari Conn Manager / ERROR] Failed connecting to Aikari WebSocket server, please check the status of Aikari process.`
);
sendRetryStatusToMain(false, "E_WS_CONN_FAILED_AFT_MULTIPLE_TRIES");
return;
}
/** @type {WebSocket} */
const aikariWs = new WebSocket(
`${aikariProtocol}://aikari.hugoaura.local:${aikariPort}/?auth=${authToken}`
);
aikariWs.onopen = () => {
callback(true, aikariWs);
};
aikariWs.onerror = () => {
isErrorOccurred = true;
failedCounter += 1;
callback(false, aikariWs);
};
aikariWs.onclose = () => {
clearReqSendIpcListener();
if (global.__HUGO_AURA__.aikariStats) {
if (global.__HUGO_AURA__.aikariStats.status === "notReady") {
if (isRetrying) {
sendRetryStatusToMain(false, "E_IS_LOADING");
return;
}
console.warn(
"[HugoAura / UI / Aikari Conn Manager / WARN] Aikari not ready, try again after 10s..."
);
isRetrying = true;
setTimeout(async () => {
isRetrying = false;
startConnAikariProc(global.__HUGO_AURA__.aikariStats);
}, 10000);
sendRetryStatusToMain(false, "E_START_WAIT_FOR_LOADING");
return;
}
if (global.__HUGO_AURA__.aikariStats.launched === false) {
console.warn(
"[HugoAura / UI / Aikari Conn Manager / WARN] Aikari stopped, closing WebSocket connection."
);
return;
}
}
console.error(
"[HugoAura / UI / Aikari Conn Manager / ERROR] WebSocket connection closed."
);
if (isErrorOccurred) return;
failedCounter += 1;
callback(false, aikariWs);
};
};
/**
*
* @param {WebSocket} wsObj
*/
const registerSendReqListener = (wsObj) => {
clearReqSendIpcListener();
/**
*
* @param {import("electron").IpcRendererEvent} _evt
* @param {any} arg
*/
curSendListener = (_evt, arg) => {
wsObj.send(JSON.stringify(arg));
};
global.ipcRenderer.on(
`${IPC_METHOD_BASE}.ws.post.onReqSendMsg`,
curSendListener
);
};
/**
*
* @param {boolean} result
* @param {WebSocket} wsObj
* @returns
*/
const connectionResultCallback = async (result, wsObj) => {
if (!global.__HUGO_AURA__.aikariStats) return; // 😅 typescript
global.__HUGO_AURA__.aikariStats.launched = result;
global.__HUGO_AURA__.aikariStats.connected = result;
global.ipcRenderer.invoke(
`${IPC_METHOD_BASE}.updateAikariStatus`,
global.__HUGO_AURA__.aikariStats
);
if (!result) {
console.error(
`[HugoAura / UI / Aikari Conn Manager / ERROR] Failed connecting to Aikari WebSocket server, retrying ...`
);
createAikariConnection(
global.__HUGO_AURA__.aikariStats.authToken,
connectionResultCallback
);
return;
}
wsObj.onmessage = aikariPushHandler;
registerSendReqListener(wsObj);
global.__HUGO_AURA__.aikariWs = wsObj;
global.__HUGO_AURA__.aikariStats = await onAikariConnectedMsgSeq({
curAikariStates: global.__HUGO_AURA__.aikariStats,
wsObj,
});
global.ipcRenderer.invoke(
`${IPC_METHOD_BASE}.updateAikariStatus`,
global.__HUGO_AURA__.aikariStats
);
sendRetryStatusToMain(true, "SUCCESS");
};
/**
*
* @param {MessageEvent} event
*/
const aikariPushHandler = (event) => {
try {
/** @type {Record<any, any>} */
const parsedEvent = JSON.parse(event.data);
console.debug(
"[HugoAura / UI / Aikari Conn Manager / DEBUG] Received new server message: "
);
if (!parsedEvent.eventId || parsedEvent.eventId === "N/A") {
// Push
pushMsgHandler(parsedEvent);
} else {
// Not push
global.ipcRenderer.send(
`${IPC_METHOD_BASE}.ws.broadcastMessageRecv`,
parsedEvent
);
const msgRecvEvent = new CustomEvent("onAikariMessageRecv", {
detail: parsedEvent,
});
document.dispatchEvent(msgRecvEvent);
}
} catch {
console.error(
"[HugoAura / UI / Aikari Conn Manager / ERROR] Failed to resolve server message: ",
event.data
);
}
};
const initAikariWebSocketConnection = async () => {
if (!global.__HUGO_AURA__.aikariStats) return;
if (isRetrying) {
sendRetryStatusToMain(false, "E_RETRY_PENDING");
return;
}
failedCounter = 0;
isErrorOccurred = false;
const curAikariStats = await global.ipcRenderer.invoke(
`${IPC_METHOD_BASE}.getAikariStatus`
);
let updatedAikariStats = {};
if (
(curAikariStats === null || !curAikariStats.success) &&
curAikariStats.status !== "downloading" &&
curAikariStats.status !== "installing"
) {
updatedAikariStats = {
installed: false,
launched: false,
detached: false,
connected: false,
version: "unknown",
status: "dead",
authToken: "",
};
} else {
updatedAikariStats = curAikariStats.data;
}
const isAikariBinExists = (
await global.ipcRenderer.invoke(`${IPC_METHOD_BASE}.getIfAikariBinExists`)
).data.isExists;
updatedAikariStats.installed = isAikariBinExists;
// @ts-expect-error
global.__HUGO_AURA__.aikariStats = updatedAikariStats;
console.debug(
"[HugoAura / UI / Aikari Conn Manager / DEBUG] Updated early aikariStats:",
global.__HUGO_AURA__.aikariStats
);
global.ipcRenderer.invoke(
`${IPC_METHOD_BASE}.updateAikariStatus`,
updatedAikariStats
);
/*
if (updatedPlsStats.detached && updatedPlsStats.installed) {
*/
if (updatedAikariStats.installed || updatedAikariStats.detached) {
await startConnAikariProc(updatedAikariStats);
} else {
sendRetryStatusToMain(false, "E_NOT_INSTALLED");
}
/*
global.ipcRenderer.on(`${IPC_METHOD_BASE}.post.onPlsLaunched`, (_event) => {
setTimeout(() => {
startConnPls();
}, 5000);
});
*/
};
const onSetup = () => {
if (!global.ipcRenderer) {
// @ts-ignore
global.ipcRenderer = require("electron").ipcRenderer;
}
initAikariWebSocketConnection();
global.ipcRenderer.on(
`${IPC_METHOD_BASE}.retryAikariConnect`,
(_evt, _arg) => {
if (!global.__HUGO_AURA__.aikariStats) return;
if (global.__HUGO_AURA__.aikariStats.connected) return;
initAikariWebSocketConnection();
}
);
global.ipcRenderer.on(
`${IPC_METHOD_BASE}.post.onForceReloadRequested`,
(_evt, _arg) => {
window.location.reload();
}
);
global.ipcRenderer.on(
`${IPC_METHOD_BASE}.post.aikariStopped`,
(_evt, _arg) => {
if (!global.__HUGO_AURA__.aikariStats) return;
global.__HUGO_AURA__.aikariStats.launched = false;
global.__HUGO_AURA__.aikariStats.connected = false;
global.__HUGO_AURA__.aikariStats.version = "unknown";
}
);
};
setTimeout(() => {
onSetup();
}, 1500);
})();

View File

@@ -1,21 +1,21 @@
(() => { (() => {
const IPC_METHOD_BASE = "$aura.pls"; const IPC_METHOD_BASE = "$aura.aikari";
const REQUIRE_BASE = "../../aura/ui"; const REQUIRE_BASE = "../../aura/ui";
const __SCOPE = "assistant"; const __SCOPE = "assistant";
const { const {
updatePlsStatusFromLocal, updateAikariStatusFromLocal,
} = require(`${REQUIRE_BASE}/composables/plsConfigManager`); } = require(`${REQUIRE_BASE}/composables/aikariConfigManager`);
const setupListeners = () => { const setupListeners = () => {
if (!global.ipcRenderer) if (!global.ipcRenderer)
global.ipcRenderer = require("electron").ipcRenderer; global.ipcRenderer = require("electron").ipcRenderer;
ipcRenderer.on( ipcRenderer.on(
`${IPC_METHOD_BASE}.post.onPlsStatsUpdate`, `${IPC_METHOD_BASE}.post.onAikariStatsUpdate`,
(_event, arg) => { (_event, arg) => {
global.__HUGO_AURA__.plsStats = arg; global.__HUGO_AURA__.aikariStats = arg;
const event = new CustomEvent("onPLSStatsUpdate", { const event = new CustomEvent("onAikariStatsUpdate", {
detail: { detail: {
connected: arg.connected, connected: arg.connected,
}, },
@@ -35,11 +35,11 @@
); );
ipcRenderer.on( ipcRenderer.on(
`${IPC_METHOD_BASE}.post.onPlsSettingsUpdate`, `${IPC_METHOD_BASE}.post.onAikariSettingsUpdate`,
(_event, arg) => { (_event, arg) => {
global.__HUGO_AURA__.plsSettings = arg; global.__HUGO_AURA__.aikariSettings = arg;
const event = new CustomEvent("onPLSConfigUpdate", { const event = new CustomEvent("onAikariConfigUpdate", {
detail: { detail: {
path: ["root", "settings"], path: ["root", "settings"],
value: arg, value: arg,
@@ -60,11 +60,11 @@
); );
ipcRenderer.on( ipcRenderer.on(
`${IPC_METHOD_BASE}.post.onPlsRulesUpdate`, `${IPC_METHOD_BASE}.post.onAikariRulesUpdate`,
(_event, arg) => { (_event, arg) => {
global.__HUGO_AURA__.plsRules = arg; global.__HUGO_AURA__.aikariRules = arg;
const event = new CustomEvent("onPLSConfigUpdate", { const event = new CustomEvent("onAikariConfigUpdate", {
detail: { detail: {
path: ["root", "ruleSettings"], path: ["root", "ruleSettings"],
value: arg, value: arg,
@@ -87,7 +87,7 @@
ipcRenderer.on( ipcRenderer.on(
`${IPC_METHOD_BASE}.post.updateRetryStatus`, `${IPC_METHOD_BASE}.post.updateRetryStatus`,
(_event, arg) => { (_event, arg) => {
const event = new CustomEvent("onPLSStatsUpdate", { const event = new CustomEvent("onAikariStatsUpdate", {
detail: { detail: {
connected: arg.success, connected: arg.success,
}, },
@@ -107,6 +107,6 @@
); );
}; };
updatePlsStatusFromLocal(); updateAikariStatusFromLocal();
setupListeners(); setupListeners();
})(); })();

View File

@@ -6,9 +6,11 @@
let tooltipTriggerCache = null; let tooltipTriggerCache = null;
const refreshBsTooltip = (selector = '[data-bs-toggle="tooltip"]') => { const refreshBsTooltip = (selector = '[data-bs-toggle="tooltip"]') => {
if (tooltipTriggerCache) { if (tooltipTriggerCache) {
[...tooltipTriggerCache].map((el) => [...tooltipTriggerCache].map((el) => {
bootstrap.Tooltip.getInstance(el).disable() if (bootstrap.Tooltip.getInstance(el)) {
); bootstrap.Tooltip.getInstance(el).disable();
}
});
} }
const tooltipTriggerList = document.querySelectorAll(selector); const tooltipTriggerList = document.querySelectorAll(selector);

View File

@@ -1,357 +0,0 @@
// @ts-check
(() => {
if (!global.__HUGO_AURA__)
global.__HUGO_AURA__ = {
configInit: true,
auraDir: "",
version: "",
};
if (!global.__HUGO_AURA__.plsStats)
global.__HUGO_AURA__.plsStats = {
installed: false,
detached: false,
connected: false,
launched: false,
status: "dead",
version: "unknown",
authToken: "",
};
const IPC_METHOD_BASE = "$aura.pls";
const REQUIRE_BASE = "../../..";
const __SCOPE = "auraWsKeepAlive";
const PLS_REG_PATH = "ProxyLayerServices";
const { pushMsgHandler } = require(`${REQUIRE_BASE}/pls/pushHandler`);
const RegistryManager = require(`${REQUIRE_BASE}/../init/shared/registryManager`);
const registryManager = new RegistryManager();
/** @type {number} */
let failedCounter = 0;
/** @type {boolean} */
let isErrorOccurred = false;
/** @type {number} */
let plsPort = 22077;
/** @type {"wss" | "ws"} */
let plsProtocol = "wss";
/** @type {boolean} */
let isRetrying = false;
/** @type {any} */
let curSendListener = null;
const sendRetryStatusToMain = (/** @type {Boolean} */ status) => {
global.ipcRenderer.invoke(`${IPC_METHOD_BASE}.post.updateRetryStatus`, {
success: status,
});
};
const calcFullAuthToken = (/** @type {string} */ authToken) => {
const conjToken = authToken + "AuraXAuth 0xFFFFFF NeverEnds";
const crypto = require("crypto");
return crypto.createHash("sha512").update(conjToken).digest("hex");
};
const clearIpcListener = () => {
if (curSendListener) {
global.ipcRenderer.off(
`${IPC_METHOD_BASE}.ws.post.onReqSendMsg`,
curSendListener
);
curSendListener = null;
}
};
const startConnPlsProc = async (updatedPlsStats) => {
const authTokenRet = await registryManager.readRegKey(
PLS_REG_PATH,
"AuthToken",
true
);
if (authTokenRet.success) {
updatedPlsStats.authToken = authTokenRet.data;
// @ts-expect-error
global.__HUGO_AURA__.plsStats.authToken = authTokenRet.data;
} else {
sendRetryStatusToMain(false);
return;
}
const portRet = await registryManager.readRegKey(
PLS_REG_PATH,
"WsPort",
true
);
if (portRet.success) {
try {
plsPort = Number(portRet.data);
} catch {
console.warn(
`[HugoAura / UI / PLS Manager] Invalid PLS port: ${portRet.data}`
);
}
}
const protoRet = await registryManager.readRegKey(
PLS_REG_PATH,
"Protocol",
true
);
if (protoRet.success) {
plsProtocol = protoRet.data;
}
createPlsConnection(updatedPlsStats.authToken, connectionResultCallback);
};
/**
*
* @param {string} authToken
* @param {any} callback
* @returns
*/
const createPlsConnection = (authToken, callback) => {
if (failedCounter >= 3) {
console.error(
`[HugoAura / UI / PLS Manager / ERROR] Failed connecting to PLS WebSocket server, please check the status of PLS process.`
);
sendRetryStatusToMain(false);
return;
}
const fullAuthToken = calcFullAuthToken(authToken);
/** @type {WebSocket} */
const plsWs = new WebSocket(
`${plsProtocol}://pls.hugoaura.local:${plsPort}/?auth=${fullAuthToken}`
);
plsWs.onopen = () => {
callback(true, plsWs);
};
plsWs.onerror = () => {
isErrorOccurred = true;
failedCounter += 1;
callback(false, plsWs);
};
plsWs.onclose = () => {
clearIpcListener();
if (global.__HUGO_AURA__.plsStats) {
if (global.__HUGO_AURA__.plsStats.status === "notReady") {
if (isRetrying) {
sendRetryStatusToMain(false);
return;
}
console.warn(
"[HugoAura / UI / PLS Manager / WARN] PLS not ready, try again after 10s..."
);
isRetrying = true;
setTimeout(async () => {
isRetrying = false;
startConnPlsProc(global.__HUGO_AURA__.plsStats);
}, 10000);
sendRetryStatusToMain(false);
return;
}
if (global.__HUGO_AURA__.plsStats.launched === false) {
console.warn(
"[HugoAura / UI / PLS Manager / WARN] PLS stopped, closing WebSocket connection."
);
return;
}
}
console.error(
"[HugoAura / UI / PLS Manager / ERROR] WebSocket connection closed."
);
if (isErrorOccurred) return;
failedCounter += 1;
callback(false, plsWs);
};
};
/**
*
* @param {WebSocket} wsObj
*/
const registerSendReqListener = (wsObj) => {
clearIpcListener();
/**
*
* @param {import("electron").IpcRendererEvent} _evt
* @param {any} arg
*/
curSendListener = (_evt, arg) => {
wsObj.send(JSON.stringify(arg));
};
global.ipcRenderer.on(
`${IPC_METHOD_BASE}.ws.post.onReqSendMsg`,
curSendListener
);
};
/**
*
* @param {boolean} result
* @param {WebSocket} wsObj
* @returns
*/
const connectionResultCallback = (result, wsObj) => {
if (!global.__HUGO_AURA__.plsStats) return; // 😅 typescript
global.__HUGO_AURA__.plsStats.launched = result;
global.__HUGO_AURA__.plsStats.connected = result;
global.ipcRenderer.invoke(
`${IPC_METHOD_BASE}.updatePlsStats`,
global.__HUGO_AURA__.plsStats
);
if (!result) {
console.error(
`[HugoAura / UI / PLS Manager / ERROR] Failed connecting to PLS WebSocket server, retrying ...`
);
createPlsConnection(
global.__HUGO_AURA__.plsStats.authToken,
connectionResultCallback
);
return;
}
sendRetryStatusToMain(true);
global.__HUGO_AURA__.plsWs = wsObj;
registerSendReqListener(wsObj);
wsObj.onmessage = plsPushHandler;
};
/**
*
* @param {MessageEvent} event
*/
const plsPushHandler = (event) => {
try {
/** @type {Record<any, any>} */
const parsedEvent = JSON.parse(event.data);
console.debug(
"[HugoAura / UI / PLS Manager / DEBUG] Received new server message: "
);
if (!parsedEvent.eventId) {
// Push
pushMsgHandler(parsedEvent);
} else {
// Not push
global.ipcRenderer.send(
`${IPC_METHOD_BASE}.ws.broadcastMessageRecv`,
parsedEvent
);
}
} catch {
console.error(
"[HugoAura / UI / PLS Manager / ERROR] Failed to resolve server message: ",
event.data
);
}
};
const initPlsConnection = async () => {
if (!global.__HUGO_AURA__.plsStats) return;
if (isRetrying) {
sendRetryStatusToMain(false);
return;
}
failedCounter = 0;
isErrorOccurred = false;
const curPlsStats = await global.ipcRenderer.invoke(
`${IPC_METHOD_BASE}.getPlsStats`
);
let updatedPlsStats = {};
if (
(curPlsStats === null || !curPlsStats.success) &&
curPlsStats.status !== "downloading"
) {
updatedPlsStats = {
installed: false,
launched: false,
detached: false,
connected: false,
version: "unknown",
status: "dead",
authToken: "66ccff0d000721114514191981023333",
};
} else {
updatedPlsStats = curPlsStats.data;
}
const isPlsFolderExists = (
await global.ipcRenderer.invoke(`${IPC_METHOD_BASE}.getPlsBinExists`)
).data.isExists;
updatedPlsStats.installed = isPlsFolderExists;
// @ts-expect-error
global.__HUGO_AURA__.plsStats = updatedPlsStats;
console.debug(
"[HugoAura / UI / PLS Manager / DEBUG] Updated early plsStats:",
global.__HUGO_AURA__.plsStats
);
global.ipcRenderer.invoke(
`${IPC_METHOD_BASE}.updatePlsStats`,
updatedPlsStats
);
/*
if (updatedPlsStats.detached && updatedPlsStats.installed) {
*/
if (updatedPlsStats.installed) {
await startConnPlsProc(updatedPlsStats);
} else {
sendRetryStatusToMain(false);
}
/*
global.ipcRenderer.on(`${IPC_METHOD_BASE}.post.onPlsLaunched`, (_event) => {
setTimeout(() => {
startConnPls();
}, 5000);
});
*/
};
const onSetup = () => {
if (!global.ipcRenderer) {
// @ts-ignore
global.ipcRenderer = require("electron").ipcRenderer;
}
initPlsConnection();
global.ipcRenderer.on(
`${IPC_METHOD_BASE}.retryPlsConnect`,
(_evt, _arg) => {
if (!global.__HUGO_AURA__.plsStats) return;
if (global.__HUGO_AURA__.plsStats.connected) return;
initPlsConnection();
}
);
global.ipcRenderer.on(
`${IPC_METHOD_BASE}.post.plsStopped`,
(_evt, _arg) => {
if (!global.__HUGO_AURA__.plsStats) return;
global.__HUGO_AURA__.plsStats.launched = false;
global.__HUGO_AURA__.plsStats.connected = false;
global.__HUGO_AURA__.plsStats.version = "unknown";
}
);
};
setTimeout(() => {
onSetup();
}, 1500);
})();

View File

@@ -293,14 +293,14 @@
<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 <div
id="relaunchPlsNotifyToast" id="relaunchAikariNotifyToast"
class="toast acp-toast-emerg" class="toast acp-toast-emerg"
aria-atomic="true" aria-atomic="true"
data-bs-autohide="false" data-bs-autohide="false"
> >
<div class="toast-header"> <div class="toast-header">
<i class="layui-icon layui-icon-tips"></i> <i class="layui-icon layui-icon-tips"></i>
<strong class="me-auto">重启 PLS 进程以应用设置</strong> <strong class="me-auto">重启 Aikari 进程以应用设置</strong>
<button <button
type="button" type="button"
class="btn-close" class="btn-close"
@@ -308,13 +308,13 @@
></button> ></button>
</div> </div>
<div class="toast-body"> <div class="toast-body">
<p>请重启 PLS 进程以应用修改的设置</p> <p>请重启 Aikari 进程以应用修改的设置</p>
<p>已修改的配置将自动保存</p> <p>已修改的配置将自动保存</p>
<button <button
type="button" type="button"
class="btn btn-primary btn-sm" class="btn btn-primary btn-sm"
id="plsRelaunchBtn" id="aikariRelaunchBtn"
onclick="ipcRenderer.invoke('$aura.pls.relaunchPls')" onclick="ipcRenderer.invoke('$aura.aikari.relaunchPls')"
> >
重启进程 重启进程
</button> </button>

View File

@@ -106,7 +106,7 @@ global.__HUGO_AURA_UI_FUNCTIONS__.config = {
if (!side) { if (!side) {
setTimeout(() => { setTimeout(() => {
global.__HUGO_AURA_LOADER__[ global.__HUGO_AURA_LOADER__[
"Aura.UI.Assistant.Config.BehaviourCtrl.PlsStatus" "Aura.UI.Assistant.Config.BehaviourCtrl.AikariStatus"
].active = false; ].active = false;
}, 500); }, 500);
} }

View File

@@ -1,15 +1,15 @@
.acs-behaviour-control-pls-status-page { .acs-behaviour-control-aikari-status-page {
display: flex; display: flex;
flex-direction: column; flex-direction: column;
justify-content: center; justify-content: center;
align-items: center; align-items: center;
} }
.acs-behaviour-control-pls-status-page p { .acs-behaviour-control-aikari-status-page p {
font-family: sans-serif; font-family: sans-serif;
} }
.acs-bc-pls-status-page-pls-description { .acs-bc-aikari-status-page-aikari-description {
margin-top: 0.5rem; margin-top: 0.5rem;
max-width: 80%; max-width: 80%;
opacity: 0.35; opacity: 0.35;
@@ -17,11 +17,11 @@
text-align: center; text-align: center;
} }
.acs-bc-pls-status-page-main-logo { .acs-bc-aikari-status-page-main-logo {
max-width: 11%; max-width: 15%;
opacity: 0.45; opacity: 0.45;
margin-top: 1.5rem; margin-top: 1rem;
margin-bottom: 1.5rem; margin-bottom: 1rem;
} }
.acs-bc-psp-operations-container { .acs-bc-psp-operations-container {
@@ -133,7 +133,7 @@
margin-top: 1px; margin-top: 1px;
} }
.acs-bc-pls-status-page-status-el { .acs-bc-aikari-status-page-status-el {
display: flex; display: flex;
align-items: center; align-items: center;
width: 40%; width: 40%;
@@ -141,17 +141,17 @@
border-bottom: 1px solid rgba(0, 0, 0, 0.25); border-bottom: 1px solid rgba(0, 0, 0, 0.25);
} }
.acs-bc-pls-status-page-status-el div { .acs-bc-aikari-status-page-status-el div {
display: flex; display: flex;
align-items: center; align-items: center;
} }
.acs-bc-pls-status-page-status-area { .acs-bc-aikari-status-page-status-area {
flex-grow: 1; flex-grow: 1;
justify-content: flex-end; justify-content: flex-end;
} }
.acs-bc-pls-status-page-status-area-circle { .acs-bc-aikari-status-page-status-area-circle {
height: 10px; height: 10px;
width: 10px; width: 10px;
border-radius: 100%; border-radius: 100%;
@@ -159,48 +159,48 @@
margin-top: 2px; margin-top: 2px;
} }
.acs-bc-pls-status-page-status-area.pending .acs-bc-aikari-status-page-status-area.pending
.acs-bc-pls-status-page-status-area-circle { .acs-bc-aikari-status-page-status-area-circle {
background-color: rgba(0, 0, 0, 0.375); background-color: rgba(0, 0, 0, 0.375);
} }
.acs-bc-pls-status-page-status-area.pending p { .acs-bc-aikari-status-page-status-area.pending p {
opacity: 0.5; opacity: 0.5;
} }
.acs-bc-pls-status-page-status-area.success .acs-bc-aikari-status-page-status-area.success
.acs-bc-pls-status-page-status-area-circle { .acs-bc-aikari-status-page-status-area-circle {
background-color: rgb(0, 175, 38); background-color: rgb(0, 175, 38);
} }
.acs-bc-pls-status-page-status-area.success p { .acs-bc-aikari-status-page-status-area.success p {
color: rgb(0, 150, 33); color: rgb(0, 150, 33);
} }
.acs-bc-pls-status-page-status-area.failed .acs-bc-aikari-status-page-status-area.failed
.acs-bc-pls-status-page-status-area-circle { .acs-bc-aikari-status-page-status-area-circle {
background-color: rgb(175, 0, 0); background-color: rgb(175, 0, 0);
} }
.acs-bc-pls-status-page-status-area.failed p { .acs-bc-aikari-status-page-status-area.failed p {
color: rgb(175, 0, 0); color: rgb(175, 0, 0);
} }
.acs-bc-pls-status-page-status-area.warning .acs-bc-aikari-status-page-status-area.warning
.acs-bc-pls-status-page-status-area-circle { .acs-bc-aikari-status-page-status-area-circle {
background-color: rgb(212, 127, 0); background-color: rgb(212, 127, 0);
} }
.acs-bc-pls-status-page-status-area.warning p { .acs-bc-aikari-status-page-status-area.warning p {
color: rgb(212, 127, 0); color: rgb(212, 127, 0);
} }
.acs-bc-pls-status-page-status-area.info .acs-bc-aikari-status-page-status-area.info
.acs-bc-pls-status-page-status-area-circle { .acs-bc-aikari-status-page-status-area-circle {
background-color: #3d78ff; background-color: #3d78ff;
} }
.acs-bc-pls-status-page-status-area.info p { .acs-bc-aikari-status-page-status-area.info p {
color: #3d78ff; color: #3d78ff;
} }

View File

@@ -1,12 +1,11 @@
<div class="acs-behaviour-control-pls-status-page"> <div class="acs-behaviour-control-aikari-status-page">
<p class="acs-bc-pls-status-page-pls-description"> <p class="acs-bc-aikari-status-page-aikari-description">
HugoAura ProxyLayerServices (Aura-PLS) 是基于 Python + MITMProxy HugoAura Aikari 是基于 C++ 实现的 HugoAura 特权访问服务, 集成篡改 MQTT
实现的代理服务, 用于解密并修改希沃基础服务 (SeewoCore) 的 MQTT 数据包, 数据包、冰点穿透增强等功能, 实现行为监控、伪造上报
实现行为监控、伪造上报等功能
</p> </p>
<img <img
src="../../aura/ui/static/aura_pls.png" src="../../aura/ui/static/aikari_color_reversed.png"
class="acs-bc-pls-status-page-main-logo" class="acs-bc-aikari-status-page-main-logo"
/> />
<div class="acs-bc-psp-operations-container"> <div class="acs-bc-psp-operations-container">
@@ -28,7 +27,7 @@
<div <div
class="acs-bc-psp-operation-btn" class="acs-bc-psp-operation-btn"
aura-disabled="true" aura-disabled="true"
id="acsBcPsp-operBtn-Download" id="acsBcPsp-operBtn-Install"
> >
<svg <svg
xmlns="http://www.w3.org/2000/svg" xmlns="http://www.w3.org/2000/svg"
@@ -45,10 +44,10 @@
d="M17 26.17V14h-2v12.17l-2.59-2.58L11 25l5 5l5-5l-1.41-1.41z" d="M17 26.17V14h-2v12.17l-2.59-2.58L11 25l5 5l5-5l-1.41-1.41z"
/> />
</svg> </svg>
<p>下载内核</p> <p>下载应用</p>
</div> </div>
<div class="acs-bc-psp-operation-btn" id="acsBcPsp-operBtn-Install"> <div class="acs-bc-psp-operation-btn" id="acsBcPsp-operBtn-InstallSvc">
<svg <svg
xmlns="http://www.w3.org/2000/svg" xmlns="http://www.w3.org/2000/svg"
width="32" width="32"
@@ -73,7 +72,7 @@
<div <div
class="acs-bc-psp-operation-btn acs-bc-psp-o-btn-dangerous" class="acs-bc-psp-operation-btn acs-bc-psp-o-btn-dangerous"
id="acsBcPsp-operBtn-Uninstall" id="acsBcPsp-operBtn-UninstallSvc"
> >
<svg <svg
xmlns="http://www.w3.org/2000/svg" xmlns="http://www.w3.org/2000/svg"
@@ -155,61 +154,61 @@
</div> </div>
</div> </div>
<div class="acs-bc-pls-status-page-status-el"> <div class="acs-bc-aikari-status-page-status-el">
<p>安装状态</p> <p>安装状态</p>
<div <div
class="acs-bc-pls-status-page-status-area pending" class="acs-bc-aikari-status-page-status-area pending"
id="acs-bc-psp-installStatus-container" id="acs-bc-psp-installStatus-container"
> >
<span class="acs-bc-pls-status-page-status-area-circle"></span> <span class="acs-bc-aikari-status-page-status-area-circle"></span>
<p id="acs-bc-psp-installStatus-text">未安装</p> <p id="acs-bc-psp-installStatus-text">未安装</p>
</div> </div>
</div> </div>
<div class="acs-bc-pls-status-page-status-el"> <div class="acs-bc-aikari-status-page-status-el">
<p>启动状态</p> <p>启动状态</p>
<div <div
class="acs-bc-pls-status-page-status-area pending" class="acs-bc-aikari-status-page-status-area pending"
id="acs-bc-psp-launchStatus-container" id="acs-bc-psp-launchStatus-container"
> >
<span class="acs-bc-pls-status-page-status-area-circle"></span> <span class="acs-bc-aikari-status-page-status-area-circle"></span>
<p id="acs-bc-psp-launchStatus-text">未启动</p> <p id="acs-bc-psp-launchStatus-text">未启动</p>
</div> </div>
</div> </div>
<div class="acs-bc-pls-status-page-status-el"> <div class="acs-bc-aikari-status-page-status-el">
<p>连接状态</p> <p>连接状态</p>
<div <div
class="acs-bc-pls-status-page-status-area pending" class="acs-bc-aikari-status-page-status-area pending"
id="acs-bc-psp-connStatus-container" id="acs-bc-psp-connStatus-container"
> >
<span class="acs-bc-pls-status-page-status-area-circle"></span> <span class="acs-bc-aikari-status-page-status-area-circle"></span>
<p id="acs-bc-psp-connStatus-text">已断开</p> <p id="acs-bc-psp-connStatus-text">已断开</p>
</div> </div>
</div> </div>
<div class="acs-bc-pls-status-page-status-el" style="border-bottom: none"> <div class="acs-bc-aikari-status-page-status-el" style="border-bottom: none">
<p>版本</p> <p>版本</p>
<div class="acs-bc-pls-status-page-status-area"> <div class="acs-bc-aikari-status-page-status-area">
<p id="acs-bc-psp-version-text">不可用</p> <p id="acs-bc-psp-version-text">不可用</p>
</div> </div>
</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 <div
id="plsStatusNotifyToast" id="aikariStatusNotifyToast"
class="acs-bc-psp-toast toast" class="acs-bc-psp-toast toast"
data-bs-autohide="false" data-bs-autohide="false"
> >
<div class="toast-header"> <div class="toast-header">
<strong class="me-auto" id="plsStatusNotifyToastTitle"></strong> <strong class="me-auto" id="aikariStatusNotifyToastTitle"></strong>
<button <button
type="button" type="button"
class="btn-close" class="btn-close"
data-bs-dismiss="toast" data-bs-dismiss="toast"
></button> ></button>
</div> </div>
<div class="toast-body" id="plsStatusNotifyToastBody"></div> <div class="toast-body" id="aikariStatusNotifyToastBody"></div>
</div> </div>
</div> </div>
</div> </div>

File diff suppressed because it is too large Load Diff

View File

@@ -36,13 +36,13 @@
class="nav-link" class="nav-link"
id="security-config-tab" id="security-config-tab"
data-bs-toggle="pill" data-bs-toggle="pill"
data-bs-target="#security-config-subpage" data-bs-target="#device-info-post-config-subpage"
type="button" type="button"
role="tab" role="tab"
aria-controls="security-config-subpage" aria-controls="device-info-post-config-subpage"
aria-selected="false" aria-selected="false"
> >
设备安全 信息上报
</button> </button>
</li> </li>
</ul> </ul>
@@ -61,7 +61,7 @@
></div> ></div>
<div <div
class="tab-pane fade" class="tab-pane fade"
id="security-config-subpage" id="device-info-post-config-subpage"
role="tabpanel" role="tabpanel"
aria-labelledby="security-config-tab" aria-labelledby="security-config-tab"
></div> ></div>

View File

@@ -7,45 +7,58 @@
} = require(`${REQUIRE_BASE}/../../../../composables/settingsRenderer`); } = require(`${REQUIRE_BASE}/../../../../composables/settingsRenderer`);
const { basicSettings } = require(`${REQUIRE_BASE}/basic`); const { basicSettings } = require(`${REQUIRE_BASE}/basic`);
const { deviceSecuritySettings } = require(`${REQUIRE_BASE}/deviceSecurity`); const { deviceInfoPostSettings } = require(`${REQUIRE_BASE}/deviceInfoPost`);
const { const {
updatePlsSettingsFromLocal, updateAikariSettingsFromLocal,
updatePlsRulesFromLocal, updateAikariRulesFromLocal,
} = require(`${REQUIRE_BASE}/../../../../composables/plsConfigManager`); } = require(`${REQUIRE_BASE}/../../../../composables/aikariConfigManager`);
const fileSystemRawCmds = require(`${REQUIRE_BASE}/../../../../composables/rawCmdExec/fs`);
const initStatusPage = () => { const initStatusPage = () => {
global.__HUGO_AURA_LOADER__[ global.__HUGO_AURA_LOADER__[
"Aura.UI.Assistant.Config.BehaviourCtrl.PlsStatus" "Aura.UI.Assistant.Config.BehaviourCtrl.AikariStatus"
].active = true; ].active = true;
}; };
const preInitUIReactives = async () => {
if (!global.__HUGO_AURA_UI_REACTIVES__.subConfig)
global.__HUGO_AURA_UI_REACTIVES__.subConfig = {};
if (!global.__HUGO_AURA_UI_REACTIVES__.subConfig.behaviourCtrlShared)
global.__HUGO_AURA_UI_REACTIVES__.subConfig.behaviourCtrlShared = {};
global.__HUGO_AURA_UI_REACTIVES__.subConfig.behaviourCtrlShared.diskCaptions =
await fileSystemRawCmds.getDiskCaptions();
};
const initBasicSettingsPage = () => { const initBasicSettingsPage = () => {
const basicSubPageEl = document.getElementById("basic-config-subpage"); const basicSubPageEl = document.getElementById("basic-config-subpage");
settingsRenderer(basicSubPageEl, basicSettings); settingsRenderer(basicSubPageEl, basicSettings);
}; };
const initDeviceSecuritySettingsPage = () => { const initDeviceInfoPostSettingsPage = () => {
const deviceSecuritySubPageEl = document.getElementById( const deviceInfoPostSubPageEl = document.getElementById(
"security-config-subpage" "device-info-post-config-subpage"
); );
settingsRenderer(deviceSecuritySubPageEl, deviceSecuritySettings); settingsRenderer(deviceInfoPostSubPageEl, deviceInfoPostSettings);
}; };
const renderSubPages = async () => { const renderSubPages = async () => {
await updatePlsSettingsFromLocal(); await updateAikariSettingsFromLocal();
await updatePlsRulesFromLocal(); await updateAikariRulesFromLocal();
initBasicSettingsPage(); initBasicSettingsPage();
initDeviceSecuritySettingsPage(); initDeviceInfoPostSettingsPage();
}; };
const onMounted = () => { const onMounted = () => {
const rootEl = document.getElementById("acs-behaviour-control-el"); const rootEl = document.getElementById("acs-behaviour-control-el");
preInitUIReactives();
initStatusPage(); initStatusPage();
setTimeout(() => { setTimeout(() => {
rootEl.classList.remove("acs-behaviour-control-hidden"); rootEl.classList.remove("acs-behaviour-control-hidden");
renderSubPages(); // 如果立即渲染子页面, 此时 plsRules 还未初始化, 会导致子页面 auraIf 失效 renderSubPages();
}, 500); }, 500);
}; };

View File

@@ -1,887 +0,0 @@
if (!global.__HUGO_AURA_UI_FUNCTIONS__.subConfig)
global.__HUGO_AURA_UI_FUNCTIONS__.subConfig = {};
if (!global.__HUGO_AURA_UI_REACTIVES__.subConfig)
global.__HUGO_AURA_UI_REACTIVES__.subConfig = {};
(() => {
const REQUIRE_BASE = "../../aura/ui/pages/configSubPages/behaviourCtrl";
const IPC_METHOD_BASE = "$aura.pls";
const lifecycleStatus = {
installed: false,
detached: false,
svcInstalled: false,
svcRunning: false,
};
global.__HUGO_AURA_UI_REACTIVES__.subConfig.plsStatus = {
toastAutoHideTimeout: null,
curDlTaskId: null,
};
global.__HUGO_AURA_UI_FUNCTIONS__.subConfig.plsStatus = {
updateToast: async (
variant,
title,
body = null,
closable = true,
autoHide = true,
hideAfter = 3000
) => {
const toastRootEl = document.getElementById("plsStatusNotifyToast");
const toastHeaderEl = document.getElementById(
"plsStatusNotifyToastTitle"
);
const toastBodyEl = document.getElementById("plsStatusNotifyToastBody");
const bsToastIns = bootstrap.Toast.getOrCreateInstance(toastRootEl);
if (bsToastIns.isShown) {
bsToastIns.hide();
const timeout =
global.__HUGO_AURA_UI_REACTIVES__.subConfig.plsStatus
.toastAutoHideTimeout;
if (timeout) {
clearTimeout(timeout);
}
await global.__HUGO_AURA_GLOBAL__.utils.sleep(160);
}
toastRootEl.setAttribute("variant", variant);
toastHeaderEl.innerHTML = title;
if (body) {
toastBodyEl.innerHTML = body;
toastRootEl.classList.remove("body-display-none");
} else {
toastRootEl.classList.add("body-display-none");
}
toastRootEl.setAttribute("closable", closable.toString());
bsToastIns.show();
if (autoHide && hideAfter) {
global.__HUGO_AURA_UI_REACTIVES__.subConfig.plsStatus.toastAutoHideTimeout =
setTimeout(() => {
bsToastIns.hide();
}, hideAfter);
}
},
updateOperationBtnStatus: async (
btnName,
isDisabled,
btnContent = null
) => {
const btnEl = document.getElementById(`acsBcPsp-operBtn-${btnName}`);
if (!btnEl) return false;
btnEl.setAttribute("aura-disabled", isDisabled ? "true" : "false");
if (btnContent) {
const btnPEl = btnEl.getElementsByTagName("p")[0];
btnPEl.textContent = btnContent;
}
if (isDisabled) {
btnEl.onclick = () => {};
} else {
switch (btnName) {
case "Refresh":
btnEl.onclick = () => {
global.__HUGO_AURA_UI_FUNCTIONS__.subConfig.plsStatus.updateToast(
"warning",
"正在更新",
null,
false,
false,
null
);
global.__HUGO_AURA_UI_FUNCTIONS__.subConfig.plsStatus.refreshPlsStatus();
};
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",
"正在请求安装",
null,
false,
false,
null
);
const ret = await ipcRenderer.invoke(
`${IPC_METHOD_BASE}.plsLifecycleControl`,
{ target: "instSvc" }
);
if (ret.success) {
lifecycleStatus.svcInstalled = true;
global.__HUGO_AURA_UI_FUNCTIONS__.subConfig.plsStatus.updateToast(
"success",
"服务安装成功",
null,
true,
true,
2000
);
} else {
global.__HUGO_AURA_UI_FUNCTIONS__.subConfig.plsStatus.updateToast(
"error",
"服务安装失败",
`<p>${ret.errorObj}</p>`,
true,
false,
null
);
}
global.__HUGO_AURA_UI_FUNCTIONS__.subConfig.plsStatus.updateStatusContent();
};
break;
case "Uninstall":
if (btnContent === "删除内核") {
btnEl.onclick = async () => {
global.__HUGO_AURA_UI_FUNCTIONS__.subConfig.plsStatus.updateOperationBtnStatus(
"Uninstall",
true
);
global.__HUGO_AURA_UI_FUNCTIONS__.subConfig.plsStatus.updateToast(
"warning",
"正在删除内核",
null,
false,
false,
null
);
const ret = await ipcRenderer.invoke(
`${IPC_METHOD_BASE}.plsLifecycleControl`,
{ target: "rmBin" }
);
if (ret.success) {
lifecycleStatus.installed = 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(
"success",
"内核已删除",
null,
true,
true,
2000
);
} else {
global.__HUGO_AURA_UI_FUNCTIONS__.subConfig.plsStatus.updateToast(
"error",
"内核删除失败",
`<p>
${ret.errorObj ? ret.errorObj : "检查日志以获取详细信息"}
</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>${
stopRet.errorObj
? stopRet.errorObj
: "检查日志以获取详细信息"
}</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",
"正在请求启动",
null,
false,
false,
null
);
const ret = await ipcRenderer.invoke(
`${IPC_METHOD_BASE}.plsLifecycleControl`,
{ target: "startSvc" }
);
if (ret.success) {
lifecycleStatus.svcRunning = true;
global.__HUGO_AURA_UI_FUNCTIONS__.subConfig.plsStatus.updateStatusContent();
global.__HUGO_AURA_UI_FUNCTIONS__.subConfig.plsStatus.updateToast(
"success",
"PLS 已启动",
null,
true,
true,
2000
);
await global.__HUGO_AURA_GLOBAL__.utils.sleep(100);
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 启动失败",
"<p>检查 PLS 日志目录以获取详细信息</p>",
true,
false,
null
);
}
};
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",
"正在请求停止",
null,
false,
false,
null
);
const ret = await ipcRenderer.invoke(
`${IPC_METHOD_BASE}.plsLifecycleControl`,
{ target: "stopSvc" }
);
if (ret.success) {
lifecycleStatus.svcRunning = false;
global.__HUGO_AURA__.plsStats.launched = false;
global.__HUGO_AURA__.plsStats.version = "unknown";
global.__HUGO_AURA__.plsStats.status = "dead";
global.__HUGO_AURA_UI_FUNCTIONS__.subConfig.plsStatus.updateToast(
"success",
"PLS 已停止",
null,
true,
true,
2000
);
} else {
global.__HUGO_AURA_UI_FUNCTIONS__.subConfig.plsStatus.updateToast(
"error",
"PLS 停止失败",
null,
true,
false,
null
);
global.__HUGO_AURA_UI_FUNCTIONS__.subConfig.plsStatus.updateStatusContent();
}
};
break;
default:
break;
}
}
return true;
},
updateStatusContent: async () => {
const curPlsStats = await updatePlsStatusFromLocal();
if (curPlsStats.status === "downloading") {
GLOBAL_FUNCTIONS.downloadPLSBin(true);
}
const acIdInst = "acs-bc-psp-installStatus-container";
const atIdInst = "acs-bc-psp-installStatus-text";
switch (lifecycleStatus.installed) {
case true:
if (!lifecycleStatus.svcInstalled) {
updateStatusEl(acIdInst, atIdInst, "WARNING", "已下载, 服务未安装");
GLOBAL_FUNCTIONS.updateOperationBtnStatus("Install", false);
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,
"卸载服务"
);
}
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);
GLOBAL_FUNCTIONS.updateOperationBtnStatus("Stop", true);
}
const acIdLaunch = "acs-bc-psp-launchStatus-container";
const atIdLaunch = "acs-bc-psp-launchStatus-text";
if (lifecycleStatus.detached) {
updateStatusEl(acIdLaunch, atIdLaunch, "INFO", "已分离");
GLOBAL_FUNCTIONS.updateOperationBtnStatus("Start", true);
GLOBAL_FUNCTIONS.updateOperationBtnStatus("Stop", true);
} else if (lifecycleStatus.svcInstalled && lifecycleStatus.installed) {
switch (lifecycleStatus.svcRunning || curPlsStats.launched) {
case true:
if (curPlsStats.status !== "notReady") {
updateStatusEl(acIdLaunch, atIdLaunch, "SUCCESS", "已启动");
GLOBAL_FUNCTIONS.updateOperationBtnStatus("Start", true);
GLOBAL_FUNCTIONS.updateOperationBtnStatus("Stop", false);
} else {
updateStatusEl(acIdLaunch, atIdLaunch, "WARNING", "启动中");
GLOBAL_FUNCTIONS.updateOperationBtnStatus("Start", true);
GLOBAL_FUNCTIONS.updateOperationBtnStatus("Stop", false);
}
break;
case false:
updateStatusEl(acIdLaunch, atIdLaunch, "PENDING", "未启动");
GLOBAL_FUNCTIONS.updateOperationBtnStatus("Start", false);
GLOBAL_FUNCTIONS.updateOperationBtnStatus("Stop", true);
break;
}
}
const acIdConn = "acs-bc-psp-connStatus-container";
const atIdConn = "acs-bc-psp-connStatus-text";
switch (curPlsStats.connected) {
case true:
updateStatusEl(acIdConn, atIdConn, "SUCCESS", "已连接");
break;
case false:
if (curPlsStats.status !== "notReady") {
updateStatusEl(acIdConn, atIdConn, "FAILED", "连接失败");
} else {
updateStatusEl(acIdConn, atIdConn, "PENDING", "等待启动");
}
break;
}
const versionTextEl = document.getElementById("acs-bc-psp-version-text");
if (curPlsStats.version && curPlsStats.version !== "unknown") {
versionTextEl.textContent = "v" + curPlsStats.version;
} else {
versionTextEl.textContent = "不可用"
}
},
refreshPlsStatus: async (init = false) => {
const binExistsRet = await ipcRenderer.invoke(
`${IPC_METHOD_BASE}.getPlsBinExists`
);
if (binExistsRet.success && binExistsRet.data.isExists) {
lifecycleStatus.installed = true;
global.__HUGO_AURA__.plsStats.installed = true;
ipcRenderer.invoke(
`${IPC_METHOD_BASE}.updatePlsStats`,
global.__HUGO_AURA__.plsStats
);
} else {
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(
"error",
"请下载 PLS 内核以继续",
null,
true,
true,
3000
);
global.__HUGO_AURA_UI_FUNCTIONS__.subConfig.plsStatus.updateStatusContent();
return;
}
const isDetachedRet = await ipcRenderer.invoke(
`${IPC_METHOD_BASE}.plsLifecycleQuery`,
{ target: "isDetached" }
);
if (isDetachedRet.success && isDetachedRet.result) {
lifecycleStatus.detached = true;
} else {
lifecycleStatus.detached = false;
}
const isSvcInstalledRet = await ipcRenderer.invoke(
`${IPC_METHOD_BASE}.plsLifecycleQuery`,
{ target: "isSvcInstalled" }
);
if (isSvcInstalledRet.success && isSvcInstalledRet.result) {
lifecycleStatus.svcInstalled = true;
} else {
lifecycleStatus.svcInstalled = false;
}
const isSvcRunningRet = await ipcRenderer.invoke(
`${IPC_METHOD_BASE}.plsLifecycleQuery`,
{ target: "isSvcStart" }
);
if (isSvcRunningRet.success && isSvcRunningRet.result) {
lifecycleStatus.svcRunning = true;
} else {
lifecycleStatus.svcRunning = false;
}
if (init) {
global.__HUGO_AURA_UI_FUNCTIONS__.subConfig.plsStatus.updateStatusContent();
return;
}
const updateOperationBtnStatus =
global.__HUGO_AURA_UI_FUNCTIONS__.subConfig.plsStatus
.updateOperationBtnStatus;
updateOperationBtnStatus("Refresh", true);
const result = await ipcRenderer.invoke(
`${IPC_METHOD_BASE}.retryPlsConnect`
);
if (result.success && result.status === "Retrying") {
updateOperationBtnStatus("Refresh", true, "正在重连");
ipcRenderer.once(
`${IPC_METHOD_BASE}.post.updateRetryStatus`,
async (_evt, arg) => {
await global.__HUGO_AURA_GLOBAL__.utils.sleep(50);
updateOperationBtnStatus("Refresh", false, "刷新状态");
global.__HUGO_AURA_UI_FUNCTIONS__.subConfig.plsStatus.updateToast(
arg.success ? "success" : "error",
arg.success ? "更新成功" : "连接失败",
null,
true,
true,
3000
);
global.__HUGO_AURA_UI_FUNCTIONS__.subConfig.plsStatus.updateStatusContent();
}
);
} else if (result.success && result.status === "Already") {
updateOperationBtnStatus("Refresh", false, "刷新状态");
global.__HUGO_AURA_UI_FUNCTIONS__.subConfig.plsStatus.updateToast(
"success",
"更新成功",
null,
true,
true,
3000
);
}
},
/**
*
* @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 =
global.__HUGO_AURA_UI_FUNCTIONS__.subConfig.plsStatus;
const CUR_CHANNEL = `${IPC_METHOD_BASE}.post.reportPlsDownloadStatus`;
if (!retrieveMode) {
GLOBAL_FUNCTIONS.updateOperationBtnStatus("Download", true, "正在检查");
GLOBAL_FUNCTIONS.updateOperationBtnStatus("Refresh", true);
await ipcRenderer.invoke(`${IPC_METHOD_BASE}.ensurePlsInstallDir`);
GLOBAL_FUNCTIONS.updateToast(
"info",
"准备开始下载...",
null,
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) => {
switch (info.status) {
case "failed":
GLOBAL_FUNCTIONS.updateToast(
"error",
"下载失败",
`<p>${
info.message ? info.message : "检查日志以获取错误信息"
}</p><p>
${info.errorObj ? info.errorObj : ""}
</p>`,
true,
true,
5000
);
GLOBAL_FUNCTIONS.updatePBarStatus(
100,
"下载时发生错误",
"danger",
false
);
setTimeout(() => {
GLOBAL_FUNCTIONS.switchPBarShowStatus(false);
}, 1000);
ipcRenderer.off(CUR_CHANNEL, callbackFn);
GLOBAL_FUNCTIONS.updateOperationBtnStatus("Refresh", false);
GLOBAL_FUNCTIONS.updateOperationBtnStatus(
"Download",
false,
"下载内核"
);
break;
case "done":
GLOBAL_FUNCTIONS.updateToast(
"success",
"下载成功",
null,
true,
true,
2500
);
GLOBAL_FUNCTIONS.updatePBarStatus(
100,
"下载成功",
"success",
false
);
setTimeout(() => {
GLOBAL_FUNCTIONS.switchPBarShowStatus(false);
}, 1000);
ipcRenderer.off(CUR_CHANNEL, callbackFn);
GLOBAL_FUNCTIONS.updateOperationBtnStatus("Refresh", false);
GLOBAL_FUNCTIONS.updateOperationBtnStatus(
"Download",
true,
"下载内核"
);
lifecycleStatus.installed = true;
global.__HUGO_AURA__.plsStats.installed = true;
ipcRenderer.invoke(
`${IPC_METHOD_BASE}.updatePlsStats`,
global.__HUGO_AURA__.plsStats
);
GLOBAL_FUNCTIONS.updateStatusContent();
break;
case "waiting":
GLOBAL_FUNCTIONS.updatePBarStatus(0, "正在连接", "normal");
if (
!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;
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(
"Download",
false,
"下载内核"
);
break;
}
};
ipcRenderer.on(CUR_CHANNEL, callbackFn);
ipcRenderer.invoke(`${IPC_METHOD_BASE}.downloadPls`, {
channel: "stable",
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 =
global.__HUGO_AURA_UI_FUNCTIONS__.subConfig.plsStatus;
const {
updatePlsStatusFromLocal,
} = require(`${REQUIRE_BASE}/../../../composables/plsConfigManager`);
const initBsTooltip = () => {
const tooltipTriggerList = document.querySelectorAll(
'[data-bs-toggle="tooltip"]'
);
const _tooltipList = [...tooltipTriggerList].map(
(tooltipTriggerEl) => new bootstrap.Tooltip(tooltipTriggerEl)
);
};
const updateStatusEl = (
areaContainerId,
areaTextId,
target,
text = false
) => {
const areaContainerEl = document.getElementById(areaContainerId);
const areaContainerText = document.getElementById(areaTextId);
switch (target) {
case "PENDING":
areaContainerEl.className =
"acs-bc-pls-status-page-status-area pending";
break;
case "SUCCESS":
areaContainerEl.className =
"acs-bc-pls-status-page-status-area success";
break;
case "FAILED":
areaContainerEl.className = "acs-bc-pls-status-page-status-area failed";
break;
case "WARNING":
areaContainerEl.className =
"acs-bc-pls-status-page-status-area warning";
break;
case "INFO":
areaContainerEl.className = "acs-bc-pls-status-page-status-area info";
break;
default:
return false;
}
areaContainerText.textContent = text ? text : "";
return true;
};
const onMounted = () => {
initBsTooltip();
GLOBAL_FUNCTIONS.updateOperationBtnStatus("Refresh", false);
GLOBAL_FUNCTIONS.refreshPlsStatus(true);
const eventListener = () => {
GLOBAL_FUNCTIONS.updateStatusContent();
};
document.addEventListener("onPLSStatsUpdate", eventListener);
global.__HUGO_AURA_GLOBAL__.utils.createOnLeaveEvtListener(
"onPLSStatsUpdate",
eventListener
);
};
onMounted();
})();

View File

@@ -1,30 +1,13 @@
const REQUIRE_BASE = "."; const REQUIRE_BASE = ".";
const path = require("path");
const AIKARI_ROOT_DIR = path.join("C:\\ProgramData", "HugoAura", "Aikari");
const { const {
updatePlsConfigToRemote, updateAikariConfigToRemote,
} = require(`${REQUIRE_BASE}/../../../../composables/plsConfigManager`); updateAikariTelemetryConfigToRemote,
} = require(`${REQUIRE_BASE}/../../../../composables/aikariConfigManager`);
const reusableChkFn = {
checkRelativePath: () => {
if (newVal === "" || !newVal)
return { valid: false, hint: "请输入证书路径" };
if (newVal.includes(":/") || newVal.includes(":\\")) {
return { valid: false, hint: "请输入相对路径, 而非绝对路径" };
}
if (newVal.includes("\\")) {
return {
valid: false,
hint: '请输入正确的路径, 使用 "/" 作为路径符',
};
}
return {
valid: true,
};
},
};
const basicSettings = [ const basicSettings = [
{ {
@@ -33,142 +16,202 @@ const basicSettings = [
child: [ child: [
{ {
index: 0, index: 0,
id: "plsListenPort", id: "aikarWsPreferPort",
type: "input", type: "input",
subType: "number", subType: "number",
name: "PLS WS 默认监听端口", name: "Aikari WS 默认监听端口",
description: "PLS 的 WebSocket 服务器默认监听指定的端口", description: "Aikari WebSocket 服务器默认监听的端口",
reactive: true, reactive: true,
reactiveVal: ["root.settings"], reactiveVal: ["root.settings"],
restart: false, restart: false,
reload: false, reload: false,
PLSRequired: true, aikariRequired: true,
restartPLS: false, restartAikari: false,
warning: true, warning: true,
warningContent: "PLS 仍会在默认端口被占用时, 自动随机端口重试", warningContent: "Aikari 仍会在默认端口被占用时, 自动随机端口重试",
associateVal: null, associateVal: null,
auraIf: () => true, auraIf: () => true,
defaultValue: "", defaultValue: "",
placeHolder: "输入端口号 (10000 ~ 65535)", placeHolder: "输入端口号 (10000 ~ 65535)",
valueGetter: () => { valueGetter: () => {
if (!global.__HUGO_AURA__.plsSettings) return ""; if (!global.__HUGO_AURA__.aikariSettings) return "";
return global.__HUGO_AURA__.plsSettings.wsPort; return global.__HUGO_AURA__.aikariSettings.wsPreferPort;
}, },
callbackFn: (newVal) => { callbackFn: (newVal) => {
if (newVal === "" || !newVal) if (newVal === "" || !newVal)
return { valid: false, hint: "请输入端口号" }; return { valid: false, hint: "请输入端口号" };
const numberNewVal = Number(newVal); const numberNewVal = Number(newVal);
if (numberNewVal === NaN || !(10000 <= numberNewVal) || !(newVal <= 65535)) { if (
numberNewVal === NaN ||
!(10000 <= numberNewVal) ||
!(newVal <= 65535)
) {
return { valid: false, hint: "请输入合法的端口号 (10000 ~ 65535)" }; return { valid: false, hint: "请输入合法的端口号 (10000 ~ 65535)" };
} }
global.__HUGO_AURA__.plsSettings.wsPort = numberNewVal; global.__HUGO_AURA__.aikariSettings.wsPreferPort = numberNewVal;
updatePlsConfigToRemote("wsPort", numberNewVal); updateAikariConfigToRemote("wsPreferPort", numberNewVal);
return { valid: true }; return { valid: true };
}, },
}, },
{ {
index: 1, index: 1,
id: "plsCertPath", id: "aikariForceRegenWsTlsCert",
type: "input",
subType: "text",
name: "WSS TLS 证书相对路径",
description: "PLS 将使用指定路径下的证书启动 WSS 服务器",
reactive: true,
reactiveVal: ["root.settings"],
restart: false,
reload: false,
PLSRequired: true,
restartPLS: true,
tip: true,
tipTitle:
'路径相对于 "%PROGRAMDATA%\\HugoAura\\Aura-PLS\\", 使用 "/" 作为路径符',
associateVal: null,
auraIf: () => true,
defaultValue: "",
placeHolder: "输入相对路径, 例如: config/vme50/cert.crt",
valueGetter: () => {
if (!global.__HUGO_AURA__.plsSettings) return "";
return global.__HUGO_AURA__.plsSettings.certPath;
},
callbackFn: (newVal) => {
const validate = reusableChkFn.checkRelativePath();
if (!validate.valid) {
return validate;
}
global.__HUGO_AURA__.plsSettings.certPath = newVal;
updatePlsConfigToRemote("certPath", newVal);
return { valid: true };
},
},
{
index: 2,
id: "plsCertPath",
type: "input",
subType: "text",
name: "WSS TLS 证书私钥相对路径",
description: "PLS 将使用指定路径下的私钥启动 WSS 服务器",
reactive: true,
reactiveVal: ["root.settings"],
restart: false,
reload: false,
PLSRequired: true,
restartPLS: true,
tip: true,
tipTitle:
'路径相对于 "%PROGRAMDATA%\\HugoAura\\Aura-PLS\\", 使用 "/" 作为路径符',
warning: true,
warningContent: "请使用 PEM 格式的密钥",
associateVal: null,
auraIf: () => true,
defaultValue: "",
placeHolder: "输入相对路径, 例如: config/vme50/cert.key",
valueGetter: () => {
if (!global.__HUGO_AURA__.plsSettings) return "";
return global.__HUGO_AURA__.plsSettings.keyPath;
},
callbackFn: (newVal) => {
const validate = reusableChkFn.checkRelativePath();
if (!validate.valid) {
return validate;
}
global.__HUGO_AURA__.plsSettings.keyPath = newVal;
updatePlsConfigToRemote("keyPath", newVal);
return { valid: true };
},
},
{
index: 3,
id: "plsRegenCertAftRelaunch",
type: "switch", type: "switch",
name: "重新生成 TLS 证书", name: "重新生成 WS TLS 证书",
description: "PLS 将在下次启动时重新生成 TLS 证书", description: "Aikari 将在下次启动时重新生成用于 WebSocket 的 TLS 证书",
reactive: true, reactive: true,
reactiveVal: ["root.settings"], reactiveVal: ["root.settings"],
restart: false, restart: false,
reload: false, reload: false,
PLSRequired: true, aikariRequired: true,
restartPLS: true, restartAikari: true,
associateVal: null, associateVal: null,
auraIf: () => true, auraIf: () => true,
defaultValue: false, defaultValue: false,
valueGetter: () => { valueGetter: () => {
if (!global.__HUGO_AURA__.plsSettings) return ""; if (!global.__HUGO_AURA__.aikariSettings) return "";
return global.__HUGO_AURA__.plsSettings.regenCert; return global.__HUGO_AURA__.aikariSettings.tls.regenWsCertNextLaunch;
}, },
callbackFn: (newVal) => { callbackFn: (newVal) => {
if (typeof newVal !== "boolean") return false; if (typeof newVal !== "boolean") return false;
global.__HUGO_AURA__.plsSettings.regenCert = newVal; global.__HUGO_AURA__.aikariSettings.tls.regenWsCertNextLaunch =
updatePlsConfigToRemote("regenCert", newVal); newVal;
updateAikariConfigToRemote("tls.regenWsCertNextLaunch", newVal);
return true; return true;
}, },
}, },
], ],
}, },
{
id: 1,
categoryName: "分析",
child: [
{
index: 0,
id: "aikariTelemetryCtrl",
type: "switch",
name: "启用错误收集与分析",
description: "启用后, Aikari 将在发生错误时上报自托管 Sentry",
reactive: true,
reactiveVal: ["root.settings"],
restart: false,
reload: false,
aikariRequired: false,
restartAikari: false,
warning: true,
warningContent:
"我们不会收集您的设备用户名、管家内的学校名等信息, 也不会保存您的 IP 地址, 所有上传的数据仅供调试使用, 不会与任何第三方共享",
associateVal: null,
auraIf: () => true,
defaultValue: false,
valueGetter: () => {
if (
!global.__HUGO_AURA__.aikariSettings ||
!global.__HUGO_AURA__.aikariStats.connected
) {
const fs = require("fs");
return fs.existsSync(
path.join(AIKARI_ROOT_DIR, ".telemetryEnabled")
);
} else {
return global.__HUGO_AURA__.aikariSettings.telemetryEnabled;
}
},
callbackFn: (newVal) => {
if (
!global.__HUGO_AURA__.aikariSettings ||
!global.__HUGO_AURA__.aikariStats.connected
) {
if (newVal) {
const fs = require("fs");
fs.appendFile(
path.join(AIKARI_ROOT_DIR, ".telemetryEnabled"),
"",
(err) => {
if (err) console.warn(err);
}
);
return true;
} else {
const fs = require("fs");
try {
fs.unlinkSync(path.join(AIKARI_ROOT_DIR, ".telemetryEnabled"));
return true;
} catch (err) {
console.error("Error removing telemetry flag: ", err);
}
}
} else {
global.__HUGO_AURA__.aikariSettings.telemetryEnabled = newVal;
updateAikariTelemetryConfigToRemote(newVal);
return true;
}
},
},
{
index: 1,
id: "aikariTelemetryId",
type: "button",
style: "outline",
name: "Aikari Telemetry ID",
reactive: true,
reactiveVal: ["telemetry"],
restart: false,
reload: false,
aikariRequired: true,
restartAikari: false,
warning: true,
warningContent: "此标识符完全在初始化时随机生成, 与设备特征无关",
associateVal: ["telemetry"],
auraIf: () => true,
alwaysEnable: true,
buttonContent: "复制",
valueGetter: async () => {
if (!global.__HUGO_AURA_UI_REACTIVES__.subConfig.behaviourCtrl)
global.__HUGO_AURA_UI_REACTIVES__.subConfig.behaviourCtrl = {};
const getIdPromise = new Promise((resolve) => {
setTimeout(() => {
const fs = require("fs");
const telemetryIdPath = path.join(
AIKARI_ROOT_DIR,
".telemetryId"
);
if (fs.existsSync(telemetryIdPath)) {
const fileContent = fs
.readFileSync(telemetryIdPath, { encoding: "utf-8" })
.trim();
global.__HUGO_AURA_UI_REACTIVES__.subConfig.behaviourCtrl.telemetryId =
fileContent;
resolve("标识符: " + fileContent);
}
global.__HUGO_AURA_UI_REACTIVES__.subConfig.behaviourCtrl.telemetryId =
null;
resolve("未能获取标识符, Aikari 未安装或未初始化");
}, 1000);
});
return await getIdPromise;
},
callbackFn: async (event) => {
if (
global.__HUGO_AURA_UI_REACTIVES__.subConfig.behaviourCtrl
.telemetryId
) {
await navigator.clipboard.writeText(
global.__HUGO_AURA_UI_REACTIVES__.subConfig.behaviourCtrl
.telemetryId
);
event.target.textContent = "已复制";
} else {
event.target.textContent = "复制失败";
}
},
},
],
},
]; ];
module.exports = { basicSettings }; module.exports = { basicSettings };

View File

@@ -0,0 +1,259 @@
// [!] Will be deprecated
const REQUIRE_BASE = ".";
const {
updateAikariPLSRulesToRemote,
} = require(`${REQUIRE_BASE}/../../../../composables/aikariConfigManager`);
const composables = {};
const deviceInfoPostSettings = [
{
id: 0,
categoryName: "冰点管理",
child: [
{
index: 0,
id: "enableFreezeInfoReportOverride",
type: "switch",
name: "启用冰冻状态篡改",
description: "篡改上报的冰冻数据, 可自定义集控端显示的状态",
reactive: true,
reactiveVal: ["root.ruleSettings"],
restart: false,
reload: false,
aikariRequired: true,
restartAikari: false,
associateVal: null,
auraIf: () => true,
defaultValue: false,
valueGetter: () => {
if (!global.__HUGO_AURA__.aikariRules) return "";
return global.__HUGO_AURA__.aikariRules.ssaFeatures.securityPolicies
.freezeManagement.freezeDiskInfoPost.enabled;
},
callbackFn: (newVal) => {
if (typeof newVal !== "boolean") return;
if (!global.__HUGO_AURA__.aikariRules) return;
global.__HUGO_AURA__.aikariRules.ssaFeatures.securityPolicies.freezeManagement.freezeDiskInfoPost.enabled =
newVal;
updateAikariPLSRulesToRemote(
"ssaFeatures.securityPolicies.freezeManagement.freezeDiskInfoPost.enabled",
newVal,
"ssaFeatures.securityPolicies.freezeManagement.freezeDiskInfoPost"
);
return true;
},
},
{
index: 1,
id: "freezeInfoReportFrozenDisks",
type: "checkbox",
name: "被冻结的磁盘",
description: "选中的磁盘会<b>被上报</b>为冻结 (不是实际行为)",
restart: false,
reload: false,
aikariRequired: true,
restartAikari: false,
warning: true,
warningContent:
"如果可选的磁盘盘符与下方预览不一致, 则多出的盘符可能为 DVD 驱动器 / 软盘 / 可移动磁盘, 忽略即可",
reactive: true,
reactiveVal: ["root.ruleSettings"],
associateVal: [
"ssaFeatures.securityPolicies.freezeManagement.freezeDiskInfoPost.enabled",
],
auraIf: () => true,
auraDisable: () => {
if (!global.__HUGO_AURA__.aikariRules) return { value: true };
if (
!global.__HUGO_AURA_UI_REACTIVES__.subConfig.behaviourCtrlShared
.diskCaptions
)
return {
value: true,
tooltip: "发生错误, 请上报至 HugoAura GitHub Issues",
};
if (
!global.__HUGO_AURA_UI_REACTIVES__.subConfig.behaviourCtrlShared
.diskCaptions.length === 0
)
return {
value: true,
tooltip: "发生错误, 请上报至 HugoAura GitHub Issues",
};
return {
value:
!global.__HUGO_AURA__.aikariRules.ssaFeatures.securityPolicies
.freezeManagement.freezeDiskInfoPost.enabled,
tooltip: "启用冰冻状态篡改以继续",
};
},
defaultValue: [],
templates: () => {
try {
if (
global.__HUGO_AURA_UI_REACTIVES__.subConfig.behaviourCtrlShared
.diskCaptions.length === 0
) {
return ["error"];
} else {
return global.__HUGO_AURA_UI_REACTIVES__.subConfig.behaviourCtrlShared.diskCaptions.map(
(element) => {
return element.toLowerCase().replace(/:/g, "");
}
);
}
} catch (err) {
console.error(err);
return ["error"];
}
},
templateLabels: () => {
try {
if (
global.__HUGO_AURA_UI_REACTIVES__.subConfig.behaviourCtrlShared
.diskCaptions.length === 0
) {
return ["获取盘符时发生错误, 请上报至 GitHub Issues"];
} else {
return global.__HUGO_AURA_UI_REACTIVES__.subConfig.behaviourCtrlShared.diskCaptions.map(
(element) => {
return element.replace(/:/g, " 盘");
}
);
}
} catch (err) {
console.error(err);
return ["发生未知错误"];
}
},
valueGetter: () => {
if (!global.__HUGO_AURA__.aikariRules) return [];
return global.__HUGO_AURA__.aikariRules.ssaFeatures.securityPolicies
.freezeManagement.freezeDiskInfoPost.frozenDisks;
},
callbackFn: (affectedData, affectedEl) => {
const targetArr =
global.__HUGO_AURA__.aikariRules.ssaFeatures.securityPolicies
.freezeManagement.freezeDiskInfoPost.frozenDisks;
if (affectedEl.checked) {
targetArr.push(affectedData);
} else {
targetArr.splice(targetArr.indexOf(affectedData), 1);
}
updateAikariPLSRulesToRemote(
"ssaFeatures.securityPolicies.freezeManagement.freezeDiskInfoPost.frozenDisks",
targetArr,
"ssaFeatures.securityPolicies.freezeManagement.freezeDiskInfoPost"
);
return true;
},
},
{
index: 2,
id: "freezeInfoReportOverridePreview",
type: "preview",
loaderTarget:
"Aura.UI.Assistant.Config.BehaviourCtrl.DeviceSecurity.FreezeOverridePreview",
associateVal: [
"ssaFeatures.securityPolicies.freezeManagement.freezeDiskInfoPost.frozenDisks",
],
listenerType: "aikari",
},
],
},
{
id: 1,
categoryName: "软件信息",
child: [
{
index: 0,
id: "enableSoftwareReportPostOverride",
type: "switch",
name: "启用软件信息上报覆写",
description:
'覆写上报的软件信息, 可自定义集控端 "设备管控" - <设备名> - "软件列表" 下的信息显示',
reactive: true,
reactiveVal: ["root.ruleSettings"],
restart: false,
reload: false,
aikariRequired: true,
restartAikari: false,
warning: true,
warningContent: '此功能与 "弹窗拦截" 等无关',
associateVal: null,
auraIf: () => true,
defaultValue: false,
valueGetter: () => {
if (!global.__HUGO_AURA__.aikariRules) return false;
return global.__HUGO_AURA__.aikariRules.deviceInfo.software
.softwareReportPost.enabled;
},
callbackFn: (newVal) => {
if (typeof newVal !== "boolean") return;
if (!global.__HUGO_AURA__.aikariRules) return;
global.__HUGO_AURA__.aikariRules.deviceInfo.software.softwareReportPost.enabled =
newVal;
updateAikariPLSRulesToRemote(
"deviceInfo.software.softwareReportPost.enabled",
newVal,
"deviceInfo.software.softwareReportPost"
);
return true;
},
},
{
index: 1,
id: "enableSoftwareReportPostSetAsEmpty",
type: "switch",
name: "清空软件上报列表",
description: "将上报列表置空, 集控端将无法看到任何已安装应用",
reactive: true,
reactiveVal: ["root.ruleSettings"],
restart: false,
reload: false,
aikariRequired: true,
restartAikari: false,
associateVal: ["deviceInfo.software.softwareReportPost.enabled"],
auraIf: () => true,
auraDisable: () => {
if (!global.__HUGO_AURA__.aikariRules) return { value: true };
return {
value:
!global.__HUGO_AURA__.aikariRules.deviceInfo.software
.softwareReportPost.enabled,
tooltip: "启用软件信息上报覆写以继续",
};
},
defaultValue: true,
valueGetter: () => {
if (!global.__HUGO_AURA__.aikariRules) return true;
return global.__HUGO_AURA__.aikariRules.deviceInfo.software
.softwareReportPost.setAsEmpty;
},
callbackFn: (newVal) => {
if (typeof newVal !== "boolean") return;
if (!global.__HUGO_AURA__.aikariRules) return;
global.__HUGO_AURA__.aikariRules.deviceInfo.software.softwareReportPost.setAsEmpty =
newVal;
updateAikariPLSRulesToRemote(
"deviceInfo.software.softwareReportPost.setAsEmpty",
newVal,
"deviceInfo.software.softwareReportPost"
);
return true;
},
},
],
},
];
module.exports = { deviceInfoPostSettings };

View File

@@ -1,99 +0,0 @@
const REQUIRE_BASE = ".";
const {
updatePlsConfigToRemote,
} = require(`${REQUIRE_BASE}/../../../../composables/plsConfigManager`);
const composables = {};
const deviceSecuritySettings = [
{
id: 0,
categoryName: "冰点管理",
child: [
{
index: 0,
id: "enableFreezeInfoReportOverride",
type: "switch",
name: "启用冰冻状态篡改",
description: "篡改上报的冰冻数据, 可自定义集控端显示的状态",
reactive: true,
reactiveVal: ["root.ruleSettings"],
restart: false,
reload: false,
PLSRequired: true,
restartPLS: false,
associateVal: null,
auraIf: () => true,
defaultValue: false,
valueGetter: () => {
if (!global.__HUGO_AURA__.plsRules) return "";
return global.__HUGO_AURA__.plsRules.client.security.uploadFreezeInfo
.enable;
},
callbackFn: (newVal) => {
if (typeof newVal !== "boolean") return;
if (!global.__HUGO_AURA__.plsRules) return;
global.__HUGO_AURA__.plsRules.client.security.uploadFreezeInfo.enable =
newVal;
updatePlsConfigToRemote(
"ruleSettings.client.security.uploadFreezeInfo.enable",
newVal
);
return true;
},
},
{
index: 1,
id: "freezeInfoReportOverrideType",
type: "radio",
name: "篡改模式",
description:
"选择一种篡改模式, 选中的磁盘范围会<b>被上报</b>为冻结 (不是实际行为)",
restart: false,
reload: false,
PLSRequired: true,
restartPLS: false,
reactive: true,
reactiveVal: ["root.ruleSettings"],
associateVal: ["ruleSettings.client.security.uploadFreezeInfo.enable"],
auraIf: () => {
if (!global.__HUGO_AURA__.plsRules) return true;
return global.__HUGO_AURA__.plsRules.client.security.uploadFreezeInfo
.enable;
},
defaultValue: "allFreeze",
templates: ["allFreeze", "systemOnly", "exceptSecondDisk"],
templateLabels: ["全部冻结", "仅系统盘", "第二磁盘除外"],
valueGetter: () => {
if (!global.__HUGO_AURA__.plsRules) return;
return global.__HUGO_AURA__.plsRules.client.security.uploadFreezeInfo
.rewriteMode;
},
callbackFn: (newVal) => {
global.__HUGO_AURA__.plsRules.client.security.uploadFreezeInfo.rewriteMode =
newVal;
updatePlsConfigToRemote(
"ruleSettings.client.security.uploadFreezeInfo.rewriteMode",
newVal
);
return true;
},
},
{
index: 2,
id: "freezeInfoReportOverridePreview",
type: "preview",
loaderTarget:
"Aura.UI.Assistant.Config.BehaviourCtrl.DeviceSecurity.FreezeOverridePreview",
associateVal: ["ruleSettings.client.security.uploadFreezeInfo"],
listenerType: "pls",
},
],
},
];
module.exports = { deviceSecuritySettings };

View File

@@ -13,7 +13,7 @@
<path fill="currentColor" d="M20.59 22L15 16.41V7h2v8.58l5 5.01z" /> <path fill="currentColor" d="M20.59 22L15 16.41V7h2v8.58l5 5.01z" />
</svg> </svg>
<p>请稍候...</p> <p>无数据可用, 检查 Aikari 连接</p>
</div> </div>
<div class="acs-bc-dsc-fop-on-req-error" auraIf="false"> <div class="acs-bc-dsc-fop-on-req-error" auraIf="false">

View File

@@ -36,8 +36,8 @@
resolve({ resolve({
success: true, success: true,
data: null, data: null,
status: response.status status: response.status,
}) });
} }
const parsedData = await response.json(); const parsedData = await response.json();
@@ -84,7 +84,7 @@
const diskElTemplate = document.createElement("p"); const diskElTemplate = document.createElement("p");
diskElTemplate.classList.add("acs-bc-dsc-fop-disk-el"); diskElTemplate.classList.add("acs-bc-dsc-fop-disk-el");
if (!curConfig.enable) { if (!curConfig.enabled) {
for (const disk of curDisks) { for (const disk of curDisks) {
const curDiskEl = diskElTemplate.cloneNode(); const curDiskEl = diskElTemplate.cloneNode();
if (disk.status !== 0) { if (disk.status !== 0) {
@@ -95,44 +95,16 @@
diskContainerEl.appendChild(curDiskEl); diskContainerEl.appendChild(curDiskEl);
} }
} else { } else {
switch (curConfig.rewriteMode) { let idx = 0;
case "allFreeze": for (const disk of curDisks) {
{ const curDiskEl = diskElTemplate.cloneNode();
for (const disk of curDisks) { if (curConfig.frozenDisks.includes(disk.name.toLowerCase())) {
const curDiskEl = diskElTemplate.cloneNode(); // @ts-expect-error
// @ts-expect-error curDiskEl.classList.add("active");
curDiskEl.classList.add("active"); }
curDiskEl.textContent = `${disk.name.toUpperCase()}`; curDiskEl.textContent = `${disk.name.toUpperCase()}`;
diskContainerEl.appendChild(curDiskEl); diskContainerEl.appendChild(curDiskEl);
} idx += 1;
}
break;
case "systemOnly":
{
let idx = 0;
for (const disk of curDisks) {
const curDiskEl = diskElTemplate.cloneNode();
// @ts-expect-error
if (idx === 0) curDiskEl.classList.add("active");
curDiskEl.textContent = `${disk.name.toUpperCase()}`;
diskContainerEl.appendChild(curDiskEl);
idx += 1;
}
}
break;
case "exceptSecondDisk":
{
let idx = 0;
for (const disk of curDisks) {
const curDiskEl = diskElTemplate.cloneNode();
// @ts-expect-error
if (idx === 0) curDiskEl.classList.add("active");
curDiskEl.textContent = `${disk.name.toUpperCase()}`;
diskContainerEl.appendChild(curDiskEl);
idx += 1;
}
}
break;
} }
} }
@@ -148,9 +120,10 @@
)[0]; )[0];
const eventListener = (_event) => { const eventListener = (_event) => {
if (!global.__HUGO_AURA__.plsRules) return; if (!global.__HUGO_AURA__.aikariRules) return;
composables.getAndUpdateDiskInfo( composables.getAndUpdateDiskInfo(
global.__HUGO_AURA__.plsRules.client.security.uploadFreezeInfo global.__HUGO_AURA__.aikariRules.ssaFeatures.securityPolicies
.freezeManagement.freezeDiskInfoPost
); );
}; };
rootEl.addEventListener("onAssociateValueUpdated", eventListener); rootEl.addEventListener("onAssociateValueUpdated", eventListener);

View File

@@ -10,7 +10,7 @@
<script> <script>
let global = window; let global = window;
</script> </script>
<script src="../../../js/plsConnectionManager.js"></script> <script src="../../../js/aikariConnectionManager.js"></script>
</head> </head>
<body></body> <body></body>
</html> </html>

View File

@@ -1,48 +0,0 @@
// @ts-check
const IPC_METHOD_BASE = "$aura.pls";
/**
*
* @param {PLSPush} parsedWsMsg
* @returns
*/
const basicRouteHandler = (parsedWsMsg) => {
const target = parsedWsMsg.type.split(".").slice(-1)[0];
switch (target) {
case "pushPlsInfo":
if (global.__HUGO_AURA__.plsStats) {
global.__HUGO_AURA__.plsStats.status = parsedWsMsg.data.status;
global.__HUGO_AURA__.plsStats.version = parsedWsMsg.data.version;
}
global.ipcRenderer.invoke(
`${IPC_METHOD_BASE}.updatePlsStats`,
global.__HUGO_AURA__.plsStats
);
console.debug(
"[HugoAura / UI / PLS Routes / DEBUG] Updated plsStats basic info:",
global.__HUGO_AURA__.plsStats
);
break;
case "plsNotReadyError":
if (global.__HUGO_AURA__.plsStats) {
global.__HUGO_AURA__.plsStats.launched = true;
global.__HUGO_AURA__.plsStats.connected = false;
global.__HUGO_AURA__.plsStats.status = "notReady";
}
global.ipcRenderer.invoke(
`${IPC_METHOD_BASE}.updatePlsStats`,
global.__HUGO_AURA__.plsStats
);
break;
default:
return false;
}
return true;
};
module.exports = { basicRouteHandler };

Binary file not shown.

After

Width:  |  Height:  |  Size: 39 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 21 KiB

View File

@@ -27,7 +27,7 @@ if (!global.__HUGO_AURA_API__) {
"https://api-aura-projekts.minorice.moe", "https://api-aura-projekts.minorice.moe",
"https://api.aura.vim.moe", "https://api.aura.vim.moe",
], ],
plsUpdate: "/api/getPLSLatestVersion", aikariUpdate: "/api/getAikariLatestVersion",
auraUpdate: "/api/getAuraLatestVersion", auraUpdate: "/api/getAuraLatestVersion",
}; };
global.__HUGO_AURA_API__ = __HUGO_AURA_API__; global.__HUGO_AURA_API__ = __HUGO_AURA_API__;

View File

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